diff --git a/app/.settings/org.eclipse.jdt.core.prefs b/app/.settings/org.eclipse.jdt.core.prefs index da7176c57..bd0342e50 100644 --- a/app/.settings/org.eclipse.jdt.core.prefs +++ b/app/.settings/org.eclipse.jdt.core.prefs @@ -5,9 +5,9 @@ org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annota 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 @@ -92,7 +92,7 @@ org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disa org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning 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/build.xml b/app/build.xml old mode 100644 new mode 100755 index c8d8ef937..ad89dbf8c --- a/app/build.xml +++ b/app/build.xml @@ -92,9 +92,12 @@ encoding="UTF-8" includeAntRuntime="false" classpath="../core/library/core.jar; lib/ant.jar; lib/ant-launcher.jar; lib/antlr.jar; lib/apple.jar; lib/jdt-core.jar; lib/jna.jar; 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" - debug="on"> + 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 old mode 100755 new mode 100644 index 1f192aa4b..50230531b --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -6,9 +6,9 @@ Copyright (c) 2004-13 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology - 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 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 @@ -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 = 219; + // updating the values, due to javac inlining the static final values. + static private final int REVISION = 224; /** This might be replaced by main() if there's a lib/version.txt file. */ - static private String VERSION_NAME = "0219"; //$NON-NLS-1$ + static private String VERSION_NAME = "0224"; //$NON-NLS-1$ /** Set true if this a proper release rather than a numbered revision. */ // static private boolean RELEASE = false; @@ -198,9 +198,19 @@ public class Base { } log("about to create base..."); //$NON-NLS-1$ - Base base = new Base(args); - // Prevent more than one copy of the PDE from running. - SingleInstance.startServer(base); + try { + Base base = new Base(args); + // Prevent more than one copy of the PDE from running. + SingleInstance.startServer(base); + + } catch (Exception e) { + // 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); + } log("done creating base..."); //$NON-NLS-1$ } } @@ -220,11 +230,11 @@ public class Base { try { Class platformClass = Class.forName("processing.app.Platform"); //$NON-NLS-1$ if (Base.isMacOS()) { - platformClass = Class.forName("processing.app.macosx.Platform"); //$NON-NLS-1$ + platformClass = Class.forName("processing.app.platform.MacPlatform"); //$NON-NLS-1$ } else if (Base.isWindows()) { - platformClass = Class.forName("processing.app.windows.Platform"); //$NON-NLS-1$ + platformClass = Class.forName("processing.app.platform.WindowsPlatform"); //$NON-NLS-1$ } else if (Base.isLinux()) { - platformClass = Class.forName("processing.app.linux.Platform"); //$NON-NLS-1$ + platformClass = Class.forName("processing.app.platform.LinuxPlatform"); //$NON-NLS-1$ } platform = (Platform) platformClass.newInstance(); } catch (Exception e) { @@ -324,7 +334,7 @@ public class Base { } - public Base(String[] args) { + public Base(String[] args) throws Exception { // // Get the sketchbook path, and make sure it's set properly // determineSketchbookFolder(); @@ -338,7 +348,7 @@ public class Base { // removeDir(contrib.getFolder()); // } // } - ContributionManager.deleteFlagged(); + ContributionManager.cleanup(); buildCoreModes(); rebuildContribModes(); @@ -354,7 +364,7 @@ public class Base { } else { for (Mode m : getModeList()) { if (m.getIdentifier().equals(lastModeIdentifier)) { - log("Setting next mode to " + lastModeIdentifier); //$NON-NLS-1$ + logf("Setting next mode to {0}.", lastModeIdentifier); //$NON-NLS-1$ nextMode = m; } } @@ -1028,6 +1038,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. @@ -1067,6 +1078,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 @@ -1384,7 +1396,7 @@ public class Base { } } } - return found; // actually ignored, but.. + return found; } @@ -1580,18 +1592,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"); @@ -1642,6 +1665,29 @@ public class Base { } + static private Boolean usableOracleJava; + + // Make sure this is Oracle Java 7u40 or later. This is temporary. + static public boolean isUsableOracleJava() { + if (usableOracleJava == null) { + usableOracleJava = false; + + if (Base.isMacOS() && + System.getProperty("java.vendor").contains("Oracle")) { + String version = System.getProperty("java.version"); // 1.7.0_40 + String[] m = PApplet.match(version, "1.(\\d).*_(\\d+)"); + + if (m != null && + PApplet.parseInt(m[1]) >= 7 && + PApplet.parseInt(m[2]) >= 40) { + usableOracleJava = true; + } + } + } + return usableOracleJava; + } + + /** * returns true if running on windows. */ @@ -2047,7 +2093,7 @@ public class Base { /** * 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) { @@ -2066,7 +2112,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; @@ -2315,15 +2361,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 libFolder = new File(decodedPath).getParentFile(); - if (libFolder.getName().equals("lib")) { - // The main Processing installation directory - processingRoot = libFolder.getParentFile(); + + 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 { - Base.log("Could not find lib in " + - libFolder.getAbsolutePath() + ", switching to user.dir"); - processingRoot = new File(System.getProperty("user.dir")); + // 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")); + } } } /* @@ -2344,6 +2412,57 @@ 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 name.endsWith(".jdk") && dir.isDirectory(); + } + }); + 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 @@ -2429,12 +2548,21 @@ public class Base { } - static public void readSettings(String filename, String lines[], + /** + * Parse a String array that contains attribute/value pairs separated + * by = (the equals sign). The # (hash) symbol is used to denote comments. + * Comments can be anywhere on a line. Blank lines are ignored. + */ + static public void readSettings(String filename, String[] lines, HashMap settings) { - for (int i = 0; i < lines.length; i++) { - int hash = lines[i].indexOf('#'); - String line = (hash == -1) ? - lines[i].trim() : lines[i].substring(0, hash).trim(); + for (String line : lines) { + // Remove comments + int commentMarker = line.indexOf('#'); + if (commentMarker != -1) { + line = line.substring(0, commentMarker); + } + // Remove extra whitespace + line = line.trim(); if (line.length() != 0) { int equals = line.indexOf('='); @@ -2472,6 +2600,7 @@ public class Base { to = null; targetFile.setLastModified(sourceFile.lastModified()); + targetFile.setExecutable(sourceFile.canExecute()); } @@ -2544,10 +2673,50 @@ 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 + * or Recycle Bin (if available) or simply deleting it (if not). + * + * When the file/folder is on another file system, it may simply be removed + * immediately, without additional warning. So only use this if you want to, + * you know, "delete" the subject in question. + * + * NOTE: Not yet tested nor ready for prime-time. + * + * @param file the victim (a directory or individual file) + * @return true if all ends well + * @throws IOException what went wrong + */ + static public boolean platformDelete(File file) throws IOException { + return platform.deleteFile(file); + } + + /** * Remove all files in a directory and the directory itself. - * TODO implement cross-platform "move to trash" instead of deleting, - * since this is potentially scary if there's a bug. */ static public void removeDir(File dir) { if (dir.exists()) { @@ -2600,7 +2769,8 @@ public class Base { if (files == null) return -1; for (int i = 0; i < files.length; i++) { - if (files[i].equals(".") || (files[i].equals("..")) || + if (files[i].equals(".") || + files[i].equals("..") || files[i].equals(".DS_Store")) continue; File fella = new File(folder, files[i]); if (fella.isDirectory()) { @@ -2917,7 +3087,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/Editor.java b/app/src/processing/app/Editor.java index cfbe5931e..3463555e0 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,92 @@ 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("This method no longer exists: " + nsme.getMessage()); + 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 (Exception ex) { + System.err.println("An error occurred inside \"" + tool.getMenuTitle() + "\""); + ex.printStackTrace(); + } } ArrayList toolList = new ArrayList(toolItems.keySet()); @@ -984,7 +1060,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 +1085,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"); } @@ -1409,6 +1482,8 @@ public abstract class Editor extends JFrame implements RunnerListener { if (compoundEdit != null) { compoundEdit.end(); undo.addEdit(compoundEdit); + caretUndoStack.push(textarea.getCaretPosition()); + caretRedoStack.clear(); undoAction.updateUndoState(); redoAction.updateRedoState(); compoundEdit = null; @@ -2008,6 +2083,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()) { @@ -2327,9 +2405,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()); 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/EditorHeader.java b/app/src/processing/app/EditorHeader.java old mode 100755 new mode 100644 index 432a47179..334d0e91d --- a/app/src/processing/app/EditorHeader.java +++ b/app/src/processing/app/EditorHeader.java @@ -55,7 +55,10 @@ public class EditorHeader extends JComponent { // (total tab width will be this plus TEXT_MARGIN*2) static final int NO_TEXT_WIDTH = 10; - Color backgroundColor; + Color bgColor; + boolean hiding; + Color hideColor; + Color textColor[] = new Color[2]; Color tabColor[] = new Color[2]; Color modifiedColor; @@ -194,7 +197,11 @@ public class EditorHeader extends JComponent { tabArrow = Toolkit.getLibImage("tab-arrow" + suffix); } - backgroundColor = mode.getColor("header.bgcolor"); + bgColor = mode.getColor("header.bgcolor"); + + hiding = Preferences.getBoolean("buttons.hide.image"); + hideColor = mode.getColor("buttons.hide.color"); + textColor[SELECTED] = mode.getColor("header.text.selected.color"); textColor[UNSELECTED] = mode.getColor("header.text.unselected.color"); font = mode.getFont("header.text.font"); @@ -252,31 +259,25 @@ public class EditorHeader extends JComponent { if (Toolkit.highResDisplay()) { // scale everything 2x, will be scaled down when drawn to the screen 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 { - // don't anti-alias text in retina mode + // don't anti-alias text in retina mode w/ Apple Java g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); } // set the background for the offscreen - g.setColor(backgroundColor); + g.setColor(hiding ? hideColor : bgColor); g.fillRect(0, 0, imageW, imageH); -// EditorToolbar toolbar = editor.toolbar; -// if (toolbar != null && toolbar.backgroundImage != null) { -// g.drawImage(toolbar.backgroundImage, -// 0, -toolbar.getHeight(), -// EditorToolbar.BACKGROUND_WIDTH, -// EditorToolbar.BACKGROUND_HEIGHT, null); -// } - //editor.getMode().drawBackground(g, EditorToolbar.BUTTON_HEIGHT); - editor.getMode().drawBackground(g, Preferences.GRID_SIZE); + if (!hiding) { + editor.getMode().drawBackground(g, Preferences.GRID_SIZE); + } -// int codeCount = sketch.getCodeCount(); -// if ((tabLeft == null) || (tabLeft.length < codeCount)) { -// tabLeft = new int[codeCount]; -// tabRight = new int[codeCount]; -// } if (tabs.length != sketch.getCodeCount()) { tabs = new Tab[sketch.getCodeCount()]; for (int i = 0; i < tabs.length; i++) { @@ -285,9 +286,6 @@ public class EditorHeader extends JComponent { visitOrder = new Tab[sketch.getCodeCount() - 1]; } -// menuRight = sizeW - 16; -// menuLeft = menuRight - pieces[0][MENU].getWidth(this); -// menuLeft = menuRight - 50; // FIXME!! int leftover = ARROW_GAP_WIDTH + ARROW_WIDTH + MARGIN_WIDTH; // + SCROLLBAR_WIDTH; int tabMax = getWidth() - leftover; @@ -536,16 +534,14 @@ public class EditorHeader extends JComponent { item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Sketch sketch = editor.getSketch(); - if (!sketch.isUntitled()) { // don't bother if untitled - if (!Base.isMacOS() && // ok on OS X - editor.base.editors.size() == 1 && // mmm! accessor - sketch.getCurrentCodeIndex() == 0) { + if (!Base.isMacOS() && // ok on OS X + editor.base.editors.size() == 1 && // mmm! accessor + sketch.getCurrentCodeIndex() == 0) { Base.showWarning("Yeah, no." , "You can't delete the last tab " + "of the last open sketch.", null); - } else { - editor.getSketch().handleDeleteCode(); - } + } else { + editor.getSketch().handleDeleteCode(); } } }); 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..4f006d580 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.*; @@ -196,6 +197,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/EditorToolbar.java b/app/src/processing/app/EditorToolbar.java index 7981cb27e..8e19de102 100644 --- a/app/src/processing/app/EditorToolbar.java +++ b/app/src/processing/app/EditorToolbar.java @@ -56,7 +56,9 @@ public abstract class EditorToolbar extends JComponent implements MouseInputList Image offscreen; int width, height; - Color bgcolor; + Color bgColor; + boolean hiding; + Color hideColor; protected Button rollover; @@ -92,13 +94,16 @@ public abstract class EditorToolbar extends JComponent implements MouseInputList rollover = null; mode = editor.getMode(); - bgcolor = mode.getColor("buttons.bgcolor"); + bgColor = mode.getColor("buttons.bgcolor"); statusFont = mode.getFont("buttons.status.font"); statusColor = mode.getColor("buttons.status.color"); // modeTitle = mode.getTitle().toUpperCase(); modeTitle = mode.getTitle(); modeTextFont = mode.getFont("mode.button.font"); modeButtonColor = mode.getColor("mode.button.color"); + + hiding = Preferences.getBoolean("buttons.hide.image"); + hideColor = mode.getColor("buttons.hide.color"); if (modeArrow == null) { String suffix = Toolkit.highResDisplay() ? "-2x.png" : ".png"; @@ -206,18 +211,25 @@ public abstract class EditorToolbar extends JComponent implements MouseInputList if (Toolkit.highResDisplay()) { // scale everything 2x, will be scaled down when drawn to the screen 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 { - // don't anti-alias text in retina mode + // don't anti-alias text in retina mode w/ Apple Java g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); } - g.setColor(bgcolor); //getBackground()); + g.setColor(hiding ? hideColor : bgColor); g.fillRect(0, 0, width, height); // if (backgroundImage != null) { // g.drawImage(backgroundImage, 0, 0, BACKGROUND_WIDTH, BACKGROUND_HEIGHT, null); // } - mode.drawBackground(g, 0); + if (!hiding) { + mode.drawBackground(g, 0); + } // for (int i = 0; i < buttonCount; i++) { // g.drawImage(stateImage[i], x1[i], y1, null); diff --git a/app/src/processing/app/Library.java b/app/src/processing/app/Library.java index 2520bea15..7b8adc556 100644 --- a/app/src/processing/app/Library.java +++ b/app/src/processing/app/Library.java @@ -15,9 +15,10 @@ public class Library extends LocalContribution { protected File examplesFolder; // shortname/examples protected File referenceFile; // shortname/reference/index.html - /** - * Subfolder for grouping libraries in a menu. Basic subfolder support - * is provided so that basic organization can be done in the import menu. + /** + * Subfolder for grouping libraries in a menu. Basic subfolder support + * is provided so that some organization can be done in the import menu. + * (This is the replacement for the "library compilation" type.) */ protected String group; @@ -83,12 +84,27 @@ public class Library extends LocalContribution { }; + static public Library load(File folder) { + try { + return new Library(folder); +// } catch (IgnorableException ig) { +// Base.log(ig.getMessage()); + } catch (Error err) { + // Handles UnsupportedClassVersionError and others + err.printStackTrace(); + } catch (Exception ex) { + ex.printStackTrace(); + } + return null; + } + + public Library(File folder) { this(folder, null); } - public Library(File folder, String groupName) { + private Library(File folder, String groupName) { super(folder); this.group = groupName; @@ -196,7 +212,6 @@ public class Library extends LocalContribution { // get the path for all .jar files in this code folder packageList = Base.packageListFromClassPath(getClassPath()); - } @@ -420,14 +435,14 @@ public class Library extends LocalContribution { } }; - + static public ArrayList discover(File folder) { ArrayList libraries = new ArrayList(); discover(folder, libraries); return libraries; } - + static public void discover(File folder, ArrayList libraries) { String[] list = folder.list(junkFolderFilter); @@ -462,14 +477,14 @@ public class Library extends LocalContribution { } } - + static protected ArrayList list(File folder) { ArrayList libraries = new ArrayList(); list(folder, libraries); return libraries; } - + static protected void list(File folder, ArrayList libraries) { ArrayList librariesFolders = new ArrayList(); discover(folder, librariesFolders); @@ -495,7 +510,7 @@ public class Library extends LocalContribution { } } - + public ContributionType getType() { return ContributionType.LIBRARY; } diff --git a/app/src/processing/app/Mode.java b/app/src/processing/app/Mode.java index f45a6523b..0efa9c72e 100644 --- a/app/src/processing/app/Mode.java +++ b/app/src/processing/app/Mode.java @@ -959,9 +959,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); } diff --git a/app/src/processing/app/Platform.java b/app/src/processing/app/Platform.java index 2cf380e66..5b16bd5ff 100644 --- a/app/src/processing/app/Platform.java +++ b/app/src/processing/app/Platform.java @@ -24,12 +24,14 @@ package processing.app; import java.awt.Desktop; import java.io.File; +import java.io.IOException; import java.net.URI; import javax.swing.UIManager; import com.sun.jna.Library; import com.sun.jna.Native; +import com.sun.jna.platform.FileUtils; /** @@ -142,6 +144,30 @@ public class Platform { } */ } + + + /** + * Attempts to move to the Trash on OS X, or the Recycle Bin on Windows. + * Also tries to find a suitable Trash location on Linux. + * If not possible, just deletes the file or folder instead. + * @param file the folder or file to be removed/deleted + * @return true if the folder was successfully removed + * @throws IOException + */ + final public boolean deleteFile(File file) throws IOException { + FileUtils fu = FileUtils.getInstance(); + if (fu.hasTrash()) { + fu.moveToTrash(new File[] { file }); + return true; + + } else if (file.isDirectory()) { + Base.removeDir(file); + return true; + + } else { + return file.delete(); + } + } // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . diff --git a/app/src/processing/app/Preferences.java b/app/src/processing/app/Preferences.java index 348b678c1..8c5d6dcde 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.*; @@ -50,18 +49,18 @@ import processing.core.*; * being lectured by strangers who feel that it doesn't look like what they * learned in CS class. *

- * Would also be possible to change this to use the Java Preferences API. - * Some useful articles - * here and - * here. - * However, haven't implemented this yet for lack of time, but more - * importantly, because it would entail writing to the registry (on Windows), - * or an obscure file location (on Mac OS X) and make it far more difficult to - * find the preferences to tweak them by hand (no! stay out of regedit!) - * or to reset the preferences by simply deleting the preferences.txt file. + * We don't use the Java Preferences API because it would entail writing to + * the registry (on Windows), or an obscure file location (on Mac OS X) and + * make it far more difficult (impossible) to remove the preferences.txt to + * reset them (when they become corrupt), or to find the the file to make + * edits for numerous obscure preferences that are not part of the preferences + * window. If we added a generic editor (e.g. about:config in Mozilla) for + * such things, we could start using the Java Preferences API. But wow, that + * sounds like a lot of work. Not unlike writing this paragraph. */ 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$ @@ -109,24 +108,28 @@ public class Preferences { JTextField sketchbookLocationField; JCheckBox editorAntialiasBox; -// JCheckBox exportSeparateBox; JCheckBox deletePreviousBox; -// JCheckBox externalEditorBox; + JCheckBox whinyBox; 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; - 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; @@ -142,7 +145,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); @@ -218,7 +223,18 @@ public class Preferences { // } } - PApplet.useNativeSelect = Preferences.getBoolean("chooser.files.native"); //$NON-NLS-1$ + PApplet.useNativeSelect = + Preferences.getBoolean("chooser.files.native"); //$NON-NLS-1$ + + // Set http proxy for folks that require it. + // http://docs.oracle.com/javase/6/docs/technotes/guides/net/proxies.html + String proxyHost = get("proxy.host"); + String proxyPort = get("proxy.port"); + if (proxyHost != null && proxyHost.trim().length() != 0 && + proxyPort != null && proxyPort.trim().length() != 0) { + System.setProperty("http.proxyHost", proxyHost); + System.setProperty("http.proxyPort", proxyPort); + } } @@ -229,6 +245,11 @@ public class Preferences { dialog = new JFrame("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); @@ -285,22 +306,7 @@ public class Preferences { top += vmax + GUI_BETWEEN; - // Editor font size [ ] - - Container box = Box.createHorizontalBox(); - label = new JLabel("Editor font size: "); - box.add(label); - fontSizeField = new JTextField(4); - box.add(fontSizeField); - label = new JLabel(" (requires restart of Processing)"); - 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 @@ -309,20 +315,65 @@ 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("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("Editor font size: "); + box.add(label); + //fontSizeField = new JTextField(4); + fontSizeField = new JComboBox(FONT_SIZES); + fontSizeField.setEditable(true); + box.add(fontSizeField); +// label = new JLabel(" (requires restart of Processing)"); +// box.add(label); + box.add(Box.createHorizontalStrut(GUI_BETWEEN)); + label = new JLabel("Console font size: "); + box.add(label); + 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("Use smooth text in editor window " + - "(requires restart of Processing)"); + editorAntialiasBox = new JCheckBox("Use smooth text in editor window"); +// 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 @@ -370,10 +421,10 @@ public class Preferences { // top += d.height + GUI_BETWEEN; - // [ ] Delete previous folder on export + // [ ] Delete previous application folder on export deletePreviousBox = - new JCheckBox("Delete previous folder on export"); + new JCheckBox("Delete previous application folder on export"); pain.add(deletePreviousBox); d = deletePreviousBox.getPreferredSize(); deletePreviousBox.setBounds(left, top, d.width + 10, d.height); @@ -390,6 +441,16 @@ public class Preferences { // right = Math.max(right, left + d.width); // top += d.height + GUI_BETWEEN; + + // [ ] Use external editor + + whinyBox = new JCheckBox("Hide tab/toolbar background image (requires restart)"); + pain.add(whinyBox); + d = whinyBox.getPreferredSize(); + whinyBox.setBounds(left, top, d.width + 10, d.height); + right = Math.max(right, left + d.width); + top += d.height + GUI_BETWEEN; + // [ ] Check for updates on startup @@ -436,6 +497,7 @@ public class Preferences { // Launch programs as [ ] 32-bit [ ] 64-bit (Mac OS X only) + /* if (Base.isMacOS()) { box = Box.createHorizontalBox(); label = new JLabel("Launch programs in "); @@ -454,6 +516,7 @@ public class Preferences { box.setBounds(left, top, d.width, d.height); top += d.height + GUI_BETWEEN; } + */ // More preferences are in the ... @@ -600,17 +663,18 @@ 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.applet.separate_jar_files", -// exportSeparateBox.isSelected()); setBoolean("export.delete_target_folder", //$NON-NLS-1$ deletePreviousBox.isSelected()); -// setBoolean("sketchbook.closing_last_window_quits", -// closingLastQuitsBox.isSelected()); - //setBoolean("sketchbook.prompt", sketchPromptBox.isSelected()); - //setBoolean("sketchbook.auto_clean", sketchCleanBox.isSelected()); + boolean wine = whinyBox.isSelected(); + setBoolean("header.hide.image", wine); //$NON-NLS-1$ + setBoolean("buttons.hide.image", wine); //$NON-NLS-1$ + // Could iterate through editors here and repaint them all, but probably + // requires a doLayout() call, and that may have different effects on + // each platform, and nobody wants to debug/support that. // if the sketchbook path has changed, rebuild the menus String oldPath = get("sketchbook.path"); //$NON-NLS-1$ @@ -660,6 +724,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()) { @@ -672,16 +737,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$ @@ -698,7 +798,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 @@ -721,6 +821,9 @@ public class Preferences { checkUpdatesBox. setSelected(getBoolean("update.check")); //$NON-NLS-1$ + whinyBox.setSelected(getBoolean("header.hide.image") || //$NON-NLS-1$ + getBoolean("buttons.hide.image")); //$NON-NLS-1$ + updateDisplayList(); int displayNum = getInteger("run.display"); //$NON-NLS-1$ // System.out.println("display is " + displayNum + ", d count is " + displayCount); @@ -728,12 +831,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$ @@ -747,6 +861,7 @@ public class Preferences { bitsThirtyTwoButton.setEnabled(false); } } + */ if (autoAssociateBox != null) { autoAssociateBox. @@ -757,6 +872,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; @@ -999,6 +1171,7 @@ public class Preferences { } + /* static public SyntaxStyle getStyle(String what) { String str = get("editor." + what + ".style"); //, dflt); //$NON-NLS-1$ //$NON-NLS-2$ @@ -1013,9 +1186,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/Sketch.java b/app/src/processing/app/Sketch.java index 4b941413a..669452b8c 100644 --- a/app/src/processing/app/Sketch.java +++ b/app/src/processing/app/Sketch.java @@ -32,7 +32,7 @@ import javax.swing.*; /** - * Stores information about files in the current sketch + * Stores information about files in the current sketch. */ public class Sketch { private Editor editor; @@ -70,7 +70,7 @@ public class Sketch { */ private int codeCount; private SketchCode[] code; - + /** Moved out of Editor and into here for cleaner access. */ private boolean untitled; @@ -536,6 +536,13 @@ public class Sketch { return; } + // don't allow if untitled + if (currentIndex == 0 && isUntitled()) { + Base.showMessage("Cannot Delete", + "You can't delete a sketch that has not been saved."); + return; + } + // confirm deletion with user, yes/no Object[] options = { "OK", "Cancel" }; String prompt = (currentIndex == 0) ? @@ -1420,7 +1427,7 @@ public class Sketch { String msg = "The sketch name had to be modified. Sketch names can only consist\n" + "of ASCII characters and numbers (but cannot start with a number).\n" + - "They should also be less less than 64 characters long."; + "They should also be less than 64 characters long."; System.out.println(msg); } return newName; @@ -1441,8 +1448,8 @@ public class Sketch { static final boolean asciiLetter(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } - - + + /** * Produce a sanitized name that fits our standards for likely to work. *

@@ -1457,14 +1464,14 @@ public class Sketch { * underscores. Also disallows starting the sketch name with a digit * or underscore. *

- * In Processing 2.0, sketches can no longer begin with an underscore, + * In Processing 2.0, sketches can no longer begin with an underscore, * because these aren't valid class names on Android. */ static public String sanitizeName(String origName) { char orig[] = origName.toCharArray(); StringBuffer buffer = new StringBuffer(); - // Can't lead with a digit (or anything besides a letter), so prefix with + // Can't lead with a digit (or anything besides a letter), so prefix with // "sketch_". In 1.x this prefixed with an underscore, but those get shaved // off later, since you can't start a sketch name with underscore anymore. if (!asciiLetter(orig[0])) { @@ -1476,8 +1483,8 @@ public class Sketch { buffer.append(c); } else { - // Tempting to only add if prev char is not underscore, but that - // might be more confusing if lots of chars are converted and the + // Tempting to only add if prev char is not underscore, but that + // might be more confusing if lots of chars are converted and the // result is a very short string thats nothing like the original. buffer.append('_'); } diff --git a/app/src/processing/app/SketchCode.java b/app/src/processing/app/SketchCode.java index 0a37efd20..8deaedbe7 100644 --- a/app/src/processing/app/SketchCode.java +++ b/app/src/processing/app/SketchCode.java @@ -31,7 +31,7 @@ import javax.swing.undo.*; /** - * Represents a single tab of a sketch. + * Represents a single tab of a sketch. */ public class SketchCode { /** Pretty name (no extension), not the full file name */ @@ -40,12 +40,12 @@ public class SketchCode { /** File object for where this code is located */ private File file; - /** Extension for this file (no dots, and in lowercase). */ + /** Extension for this file (no dots, and in lowercase). */ private String extension; /** Text of the program text for this tab */ private String program; - + /** Last version of the program on disk. */ private String savedProgram; @@ -54,15 +54,15 @@ public class SketchCode { /** Last time this tab was visited */ long visited; - + /** * Undo Manager for this tab, each tab keeps track of their own * Editor.undo will be set to this object when this code is the tab * that's currently the front. */ private UndoManager undo = new UndoManager(); - - /** What was on top of the undo stack when last saved. */ + + /** What was on top of the undo stack when last saved. */ // private UndoableEdit lastEdit; // saved positions from last time this tab was used @@ -73,9 +73,9 @@ public class SketchCode { private boolean modified; /** name of .java file after preproc */ -// private String preprocName; +// private String preprocName; /** where this code starts relative to the concat'd code */ - private int preprocOffset; + private int preprocOffset; public SketchCode(File file, String extension) { @@ -102,23 +102,23 @@ public class SketchCode { public File getFile() { return file; } - - + + protected boolean fileExists() { return file.exists(); } - - + + protected boolean fileReadOnly() { return !file.canWrite(); } - - + + protected boolean deleteFile() { return file.delete(); } - - + + protected boolean renameTo(File what, String ext) { // System.out.println("renaming " + file); // System.out.println(" to " + what); @@ -130,32 +130,32 @@ public class SketchCode { } return success; } - - + + public void copyTo(File dest) throws IOException { Base.saveFile(program, dest); } - + public String getFileName() { return file.getName(); } - - + + public String getPrettyName() { return prettyName; } - - + + public String getExtension() { return extension; } - - + + public boolean isExtension(String what) { return extension.equals(what); } - + /** get the current text for this tab */ public String getProgram() { @@ -174,12 +174,12 @@ public class SketchCode { return savedProgram; } - + public int getLineCount() { return Base.countLines(program); } - - + + public void setModified(boolean modified) { this.modified = modified; } @@ -208,8 +208,8 @@ public class SketchCode { public int getPreprocOffset() { return preprocOffset; } - - + + public void addPreprocOffset(int extra) { preprocOffset += extra; } @@ -218,72 +218,81 @@ public class SketchCode { public Document getDocument() { return document; } - - + + public void setDocument(Document d) { document = d; } - - + + public UndoManager getUndo() { return undo; } - - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + // TODO these could probably be handled better, since it's a general state // issue that's read/write from only one location in Editor (on tab switch.) - - + + public int getSelectionStart() { return selectionStart; } - - + + public int getSelectionStop() { return selectionStop; } - - + + public int getScrollPosition() { return scrollPosition; } - - + + protected void setState(String p, int start, int stop, int pos) { program = p; selectionStart = start; selectionStop = stop; scrollPosition = pos; } - + public long lastVisited() { return visited; } - - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + /** * Load this piece of code from a file. */ public void load() throws IOException { program = Base.loadFile(file); + + // Remove NUL characters because they'll cause problems, + // and their presence is very difficult to debug. + // https://github.com/processing/processing/issues/1973 + if (program.indexOf('\0') != -1) { + program = program.replaceAll("\0", ""); + } savedProgram = program; + // This used to be the "Fix Encoding and Reload" warning, but since that + // tool has been removed, it just rambles about text editors and encodings. if (program.indexOf('\uFFFD') != -1) { - System.err.println(file.getName() + " contains unrecognized characters."); - System.err.println("If this code was created with an older version of Processing,"); - System.err.println("you may need to use Tools -> Fix Encoding & Reload to update"); - System.err.println("the sketch to use UTF-8 encoding. If not, you may need to"); + System.err.println(file.getName() + " contains unrecognized characters."); + System.err.println("You should re-open " + file.getName() + + " with a text editor,"); + System.err.println("and re-save it in UTF-8 format. Otherwise, you can"); System.err.println("delete the bad characters to get rid of this warning."); System.err.println(); } - + setModified(false); } @@ -312,11 +321,11 @@ public class SketchCode { makePrettyName(); setModified(false); } - - + + /** - * Called when the sketch folder name/location has changed. Called when - * renaming tab 0, the main code. + * Called when the sketch folder name/location has changed. Called when + * renaming tab 0, the main code. */ public void setFolder(File sketchFolder) { file = new File(sketchFolder, file.getName()); diff --git a/app/src/processing/app/Toolkit.java b/app/src/processing/app/Toolkit.java old mode 100755 new mode 100644 index 34c68eed9..e549371af --- a/app/src/processing/app/Toolkit.java +++ b/app/src/processing/app/Toolkit.java @@ -27,6 +27,8 @@ import java.awt.FontFormatException; import java.awt.Frame; import java.awt.Graphics; import java.awt.Graphics2D; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; import java.awt.Image; import java.awt.datatransfer.Clipboard; import java.awt.event.ActionEvent; @@ -34,11 +36,15 @@ 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; @@ -216,24 +222,58 @@ public class Toolkit { return awtToolkit.getSystemClipboard(); } + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + static Boolean highResProp; - static Boolean retinaProp; static public boolean highResDisplay() { + if (highResProp == null) { + highResProp = checkRetina(); + } + return highResProp; + } + + + static private boolean checkRetina() { if (Base.isMacOS()) { - // This should probably be reset each time there's a display change. - // A 5-minute search didn't turn up any such event in the Java API. - // Also, should we use the Toolkit associated with the editor window? - if (retinaProp == null) { + // This should probably be reset each time there's a display change. + // A 5-minute search didn't turn up any such event in the Java API. + // Also, should we use the Toolkit associated with the editor window? +// String javaVendor = System.getProperty("java.vendor"); +// if (javaVendor.contains("Apple")) { + if (System.getProperty("java.vendor").contains("Apple")) { Float prop = (Float) awtToolkit.getDesktopProperty("apple.awt.contentScaleFactor"); if (prop != null) { - retinaProp = prop == 2; - } else { - retinaProp = false; + return prop == 2; } +// } else if (javaVendor.contains("Oracle")) { +// String version = System.getProperty("java.version"); // 1.7.0_40 +// String[] m = PApplet.match(version, "1.(\\d).*_(\\d+)"); +// +// // Make sure this is Oracle Java 7u40 or later +// if (m != null && +// PApplet.parseInt(m[1]) >= 7 && +// PApplet.parseInt(m[1]) >= 40) { + } else if (Base.isUsableOracleJava()) { + GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice device = env.getDefaultScreenDevice(); + + try { + Field field = device.getClass().getDeclaredField("scale"); + if (field != null) { + field.setAccessible(true); + Object scale = field.get(device); + + if (scale instanceof Integer && ((Integer)scale).intValue() == 2) { + return true; + } + } + } catch (Exception ignore) { } } - return retinaProp; } return false; } @@ -283,17 +323,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); @@ -304,20 +408,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); } @@ -328,26 +427,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/contrib/AvailableContribution.java b/app/src/processing/app/contrib/AvailableContribution.java index e387e527a..b4cb57cd4 100644 --- a/app/src/processing/app/contrib/AvailableContribution.java +++ b/app/src/processing/app/contrib/AvailableContribution.java @@ -41,7 +41,8 @@ class AvailableContribution extends Contribution { this.type = type; this.link = params.get("download"); - category = ContributionListing.getCategory(params.get("category")); + //category = ContributionListing.getCategory(params.get("category")); + categories = parseCategories(params.get("category")); name = params.get("name"); authorList = params.get("authorList"); url = params.get("url"); @@ -65,12 +66,11 @@ class AvailableContribution extends Contribution { // Unzip the file into the modes, tools, or libraries folder inside the // sketchbook. Unzipping to /tmp is problematic because it may be on // another file system, so move/rename operations will break. - File sketchbookContribFolder = type.getSketchbookFolder(); +// File sketchbookContribFolder = type.getSketchbookFolder(); File tempFolder = null; try { - tempFolder = - Base.createTempFolder(type.toString(), "tmp", sketchbookContribFolder); + tempFolder = type.createTempFolder(); } catch (IOException e) { status.setErrorMessage("Could not create a temporary folder to install."); return null; @@ -121,10 +121,36 @@ class AvailableContribution extends Contribution { LocalContribution newContrib = type.load(editor.getBase(), contribFolder); + // 1.1. get info we need to delete the newContrib folder later + File newContribFolder = newContrib.getFolder(); + // 2. Check to make sure nothing has the same name already, // backup old if needed, then move things into place and reload. installedContrib = - newContrib.moveAndLoad(editor, confirmReplace, status); + newContrib.copyAndLoad(editor, confirmReplace, status); + if (newContrib != null && type.requiresRestart()) { + installedContrib.setRestartFlag(); + //status.setMessage("Restart Processing to finish the installation."); + } + + // 3. Delete the newContrib, do a garbage collection, hope and pray + // that Java will unlock the temp folder on Windows now + newContrib = null; + System.gc(); + + + if (Base.isWindows()) { + // we'll even give it a second to finish up ... because file ops are + // just that flaky on Windows. + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + // 4. Okay, now actually delete that temp folder + Base.removeDir(newContribFolder); } else { status.setErrorMessage("Error overwriting .properties file."); @@ -163,7 +189,7 @@ class AvailableContribution extends Contribution { PrintWriter writer = PApplet.createWriter(propFile); writer.println("name=" + getName()); - writer.println("category=" + getCategory()); + writer.println("category=" + getCategoryStr()); writer.println("authorList=" + getAuthorList()); writer.println("url=" + getUrl()); writer.println("sentence=" + getSentence()); diff --git a/app/src/processing/app/contrib/Contribution.java b/app/src/processing/app/contrib/Contribution.java index 759b85537..8367b98d3 100644 --- a/app/src/processing/app/contrib/Contribution.java +++ b/app/src/processing/app/contrib/Contribution.java @@ -21,9 +21,21 @@ */ package processing.app.contrib; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import processing.core.PApplet; + abstract public class Contribution { - protected String category; // "Sound" + static final List validCategories = + Arrays.asList("3D", "Animation", "Data", "Geometry", "GUI", "Hardware", + "I/O", "Math", "Simulation", "Sound", "Typography", + "Utilities", "Video & Vision", "Other"); + + //protected String category; // "Sound" + protected List categories; // "Sound", "Typography" protected String name; // "pdf" or "PDF Export" protected String authorList; // Ben Fry protected String url; // http://processing.org @@ -34,8 +46,37 @@ abstract public class Contribution { // "Sound" - public String getCategory() { - return category; +// public String getCategory() { +// return category; +// } + + + // "Sound", "Utilities"... see valid list in ContributionListing + protected List getCategories() { + return categories; + } + + + protected String getCategoryStr() { + StringBuilder sb = new StringBuilder(); + for (String category : categories) { + sb.append(category); + sb.append(','); + } + sb.deleteCharAt(sb.length()-1); // delete last comma + return sb.toString(); + } + + + protected boolean hasCategory(String category) { + if (category != null) { + for (String c : categories) { + if (category.equalsIgnoreCase(c)) { + return true; + } + } + } + return false; } @@ -92,17 +133,54 @@ abstract public class Contribution { abstract public boolean isInstalled(); - /** - * Returns true if the type of contribution requires the PDE to restart - * when being added or removed. - */ - public boolean requiresRestart() { - return getType() == ContributionType.TOOL || getType() == ContributionType.MODE; - } +// /** +// * Returns true if the type of contribution requires the PDE to restart +// * when being added or removed. +// */ +// public boolean requiresRestart() { +// return getType() == ContributionType.TOOL || getType() == ContributionType.MODE; +// } - /** Overridden by InstalledContribution. */ + boolean isRestartFlagged() { + return false; + } + + + /** Overridden by LocalContribution. */ boolean isDeletionFlagged() { 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 List parseCategories(String categoryStr) { + List outgoing = new ArrayList(); + + if (categoryStr != null) { + String[] listing = PApplet.trim(PApplet.split(categoryStr, ',')); + for (String category : listing) { + if (validCategories.contains(category)) { + outgoing.add(category); + } + } + } + if (outgoing.size() == 0) { + return defaultCategory(); + } + return outgoing; + } } diff --git a/app/src/processing/app/contrib/ContributionListPanel.java b/app/src/processing/app/contrib/ContributionListPanel.java index e25537cf7..2fc5922fa 100644 --- a/app/src/processing/app/contrib/ContributionListPanel.java +++ b/app/src/processing/app/contrib/ContributionListPanel.java @@ -36,14 +36,11 @@ import processing.app.Base; // The "Scrollable" implementation and its methods here take care of preventing // the scrolling area from running exceptionally slowly. Not sure why they're -// necessary in the first place, however; seems like odd behavior. +// necessary in the first place, however; seems like odd behavior. +// It also allows the description text in the panels to wrap properly. public class ContributionListPanel extends JPanel implements Scrollable, ContributionChangeListener { - static public final String DELETION_MESSAGE = - "This tool has been flagged for deletion. " + - "Restart Proessing to finalize the removal process."; - static public final String INSTALL_FAILURE_TITLE = "Install Failed"; static public final String MALFORMED_URL_MESSAGE = diff --git a/app/src/processing/app/contrib/ContributionListing.java b/app/src/processing/app/contrib/ContributionListing.java index aa05a72c5..05531f63d 100644 --- a/app/src/processing/app/contrib/ContributionListing.java +++ b/app/src/processing/app/contrib/ContributionListing.java @@ -15,7 +15,7 @@ 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 + 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 */ @@ -31,11 +31,12 @@ import processing.core.PApplet; public class ContributionListing { - static final String LISTING_URL = - "https://raw.github.com/processing/processing-web/master/contrib_generate/contributions.txt"; + // Stable URL that will redirect to wherever we're hosting the file + static final String LISTING_URL = + "http://download.processing.org/contributions.txt"; static ContributionListing singleInstance; - + File listingFile; ArrayList listeners; ArrayList advertisedContributions; @@ -44,12 +45,6 @@ public class ContributionListing { boolean hasDownloadedLatestList; ReentrantLock downloadingListingLock; - static final String[] validCategories = { - "3D", "Animation", "Compilations", "Data", "Geometry", "GUI", - "Hardware", "I/O", "Math", "Simulation", "Sound", "Typography", - "Utilities", "Video & Vision" - }; - private ContributionListing() { listeners = new ArrayList(); @@ -85,16 +80,17 @@ public class ContributionListing { Collections.sort(allContributions, nameComparator); } - + /** * Adds the installed libraries to the listing of libraries, replacing any * pre-existing libraries by the same name as one in the list. */ 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); } @@ -103,59 +99,63 @@ public class ContributionListing { protected void replaceContribution(Contribution oldLib, Contribution newLib) { - if (oldLib == null || newLib == null) { - return; - } + if (oldLib != null && newLib != null) { + for (String category : oldLib.getCategories()) { + if (librariesByCategory.containsKey(category)) { + List list = librariesByCategory.get(category); - if (librariesByCategory.containsKey(oldLib.getCategory())) { - List list = librariesByCategory.get(oldLib.getCategory()); - - for (int i = 0; i < list.size(); i++) { - if (list.get(i) == oldLib) { - list.set(i, newLib); + for (int i = 0; i < list.size(); i++) { + if (list.get(i) == oldLib) { + list.set(i, newLib); + } + } } } - } - for (int i = 0; i < allContributions.size(); i++) { - if (allContributions.get(i) == oldLib) { - allContributions.set(i, newLib); + for (int i = 0; i < allContributions.size(); i++) { + if (allContributions.get(i) == oldLib) { + allContributions.set(i, newLib); + } } - } - notifyChange(oldLib, newLib); + notifyChange(oldLib, newLib); + } } private void addContribution(Contribution contribution) { - if (librariesByCategory.containsKey(contribution.getCategory())) { - List list = librariesByCategory.get(contribution.getCategory()); - list.add(contribution); - Collections.sort(list, nameComparator); - - } else { - ArrayList list = new ArrayList(); - list.add(contribution); - librariesByCategory.put(contribution.getCategory(), list); + for (String category : contribution.getCategories()) { + if (librariesByCategory.containsKey(category)) { + List list = librariesByCategory.get(category); + list.add(contribution); + Collections.sort(list, nameComparator); + + } else { + ArrayList list = new ArrayList(); + list.add(contribution); + librariesByCategory.put(category, list); + } + allContributions.add(contribution); + notifyAdd(contribution); + Collections.sort(allContributions, nameComparator); } - allContributions.add(contribution); - notifyAdd(contribution); - Collections.sort(allContributions, nameComparator); } - protected void removeContribution(Contribution info) { - if (librariesByCategory.containsKey(info.getCategory())) { - librariesByCategory.get(info.getCategory()).remove(info); + protected void removeContribution(Contribution contribution) { + for (String category : contribution.getCategories()) { + if (librariesByCategory.containsKey(category)) { + librariesByCategory.get(category).remove(contribution); + } } - allContributions.remove(info); - notifyRemove(info); + allContributions.remove(contribution); + notifyRemove(contribution); } private Contribution getContribution(Contribution contribution) { for (Contribution c : allContributions) { - if (c.getName().equals(contribution.getName()) && + if (c.getName().equals(contribution.getName()) && c.getType() == contribution.getType()) { return c; } @@ -166,7 +166,7 @@ public class ContributionListing { protected AvailableContribution getAvailableContribution(Contribution info) { for (AvailableContribution advertised : advertisedContributions) { - if (advertised.getType() == info.getType() && + if (advertised.getType() == info.getType() && advertised.getName().equals(info.getName())) { return advertised; } @@ -174,7 +174,7 @@ public class ContributionListing { return null; } - + protected Set getCategories(ContributionFilter filter) { Set outgoing = new HashSet(); @@ -194,12 +194,12 @@ public class ContributionListing { return outgoing; } - + // public List getAllContributions() { // return new ArrayList(allContributions); // } - + // public List getLibararies(String category) { // ArrayList libinfos = // new ArrayList(librariesByCategory.get(category)); @@ -207,14 +207,16 @@ public class ContributionListing { // return libinfos; // } - + protected List getFilteredLibraryList(String category, List filters) { - ArrayList filteredList = new ArrayList(allContributions); + ArrayList filteredList = + new ArrayList(allContributions); Iterator it = filteredList.iterator(); while (it.hasNext()) { Contribution libInfo = it.next(); - if (category != null && !category.equals(libInfo.getCategory())) { + //if (category != null && !category.equals(libInfo.getCategory())) { + if (category != null && !libInfo.hasCategory(category)) { it.remove(); } else { for (String filter : filters) { @@ -254,7 +256,7 @@ public class ContributionListing { return contrib.getAuthorList() != null && contrib.getAuthorList().toLowerCase().matches(filter) || contrib.getSentence() != null && contrib.getSentence().toLowerCase().matches(filter) || contrib.getParagraph() != null && contrib.getParagraph().toLowerCase().matches(filter) - || contrib.getCategory() != null && contrib.getCategory().toLowerCase().matches(filter) + || contrib.hasCategory(filter) || contrib.getName() != null && contrib.getName().toLowerCase().matches(filter); } @@ -267,9 +269,9 @@ public class ContributionListing { } - /** + /** * Returns true if the contribution fits the given property, false otherwise. - * If the property is invalid, returns false. + * If the property is invalid, returns false. */ private boolean hasProperty(Contribution contrib, String property) { // update, updates, updatable, upgrade @@ -318,7 +320,7 @@ public class ContributionListing { } } - + protected void addContributionListener(ContributionChangeListener listener) { for (Contribution contrib : allContributions) { listener.contributionAdded(contrib); @@ -326,21 +328,21 @@ public class ContributionListing { listeners.add(listener); } - + /* private void removeContributionListener(ContributionChangeListener listener) { listeners.remove(listener); } - + private ArrayList getContributionListeners() { return new ArrayList(listeners); } */ - + /** - * Starts a new thread to download the advertised list of contributions. + * Starts a new thread to download the advertised list of contributions. * Only one instance will run at a time. */ protected void downloadAvailableList(final ProgressMonitor progress) { @@ -367,8 +369,8 @@ public class ContributionListing { } }).start(); } - - + + boolean hasUpdates() { for (Contribution info : allContributions) { if (hasUpdates(info)) { @@ -390,45 +392,45 @@ public class ContributionListing { return false; } - + boolean hasDownloadedLatestList() { return hasDownloadedLatestList; } - - /** - * @return a lowercase string with all non-alphabetic characters removed - */ - static protected String normalize(String s) { - return s.toLowerCase().replaceAll("^\\p{Lower}", ""); - } + +// /** +// * @return a lowercase string with all non-alphabetic characters removed +// */ +// static protected String normalize(String s) { +// return s.toLowerCase().replaceAll("^\\p{Lower}", ""); +// } - /** - * @return the proper, valid name of this category to be displayed in the UI - * (e.g. "Typography / Geometry"). "Unknown" if the category null. - */ - static public String getCategory(String category) { - if (category == null) { - return "Unknown"; - } - String normCatName = normalize(category); +// /** +// * @return the proper, valid name of this category to be displayed in the UI +// * (e.g. "Typography / Geometry"). "Unknown" if the category null. +// */ +// static public String getCategory(String category) { +// if (category == null) { +// return "Unknown"; +// } +// String normCatName = normalize(category); +// +// for (String validCatName : validCategories) { +// String normValidCatName = normalize(validCatName); +// if (normValidCatName.equals(normCatName)) { +// return validCatName; +// } +// } +// return category; +// } - for (String validCatName : validCategories) { - String normValidCatName = normalize(validCatName); - if (normValidCatName.equals(normCatName)) { - return validCatName; - } - } - return category; - } - ArrayList parseContribList(File file) { ArrayList outgoing = new ArrayList(); if (file != null && file.exists()) { - String lines[] = PApplet.loadStrings(file); + String[] lines = PApplet.loadStrings(file); int start = 0; while (start < lines.length) { @@ -454,7 +456,7 @@ public class ContributionListing { HashMap contribParams = new HashMap(); Base.readSettings(file.getName(), contribLines, contribParams); - + outgoing.add(new AvailableContribution(contribType, contribParams)); start = end + 1; // } else { @@ -465,17 +467,17 @@ public class ContributionListing { return outgoing; } - + // boolean isDownloadingListing() { // return downloadingListingLock.isLocked(); // } - + public Comparator getComparator() { return nameComparator; } - + static Comparator nameComparator = new Comparator() { public int compare(Contribution o1, Contribution o2) { return o1.getName().toLowerCase().compareTo(o2.getName().toLowerCase()); diff --git a/app/src/processing/app/contrib/ContributionManager.java b/app/src/processing/app/contrib/ContributionManager.java index 7659a1dfc..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(); @@ -147,6 +149,7 @@ public class ContributionManager { static public void refreshInstalled(Editor editor) { editor.getMode().rebuildImportMenu(); + editor.getMode().resetExamples(); editor.rebuildToolMenu(); } @@ -202,15 +205,21 @@ public class ContributionManager { } - /** Called by Base to clean up entries previously marked for deletion. */ - static public void deleteFlagged() { + /** + * Called by Base to clean up entries previously marked for deletion + * and remove any "requires restart" flags. + */ + static public void cleanup() throws Exception { deleteFlagged(Base.getSketchbookLibrariesFolder()); deleteFlagged(Base.getSketchbookModesFolder()); deleteFlagged(Base.getSketchbookToolsFolder()); + + clearRestartFlags(Base.getSketchbookModesFolder()); + clearRestartFlags(Base.getSketchbookToolsFolder()); } - static private void deleteFlagged(File root) { + static private void deleteFlagged(File root) throws Exception { File[] markedForDeletion = root.listFiles(new FileFilter() { public boolean accept(File folder) { return (folder.isDirectory() && @@ -221,4 +230,16 @@ public class ContributionManager { Base.removeDir(folder); } } + + + static private void clearRestartFlags(File root) throws Exception { + File[] folderList = root.listFiles(new FileFilter() { + public boolean accept(File folder) { + return folder.isDirectory(); + } + }); + for (File folder : folderList) { + LocalContribution.clearRestartFlags(folder); + } + } } diff --git a/app/src/processing/app/contrib/ContributionManagerDialog.java b/app/src/processing/app/contrib/ContributionManagerDialog.java index 19e5eb3b4..6cbe3b888 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(); } } }); @@ -274,11 +272,15 @@ public class ContributionManagerDialog { // } Collections.sort(categories); // categories.add(0, ContributionManagerDialog.ANY_CATEGORY); + boolean categoriesFound = false; categoryChooser.addItem(ContributionManagerDialog.ANY_CATEGORY); for (String s : categories) { categoryChooser.addItem(s); + if (!s.equals("Unknown")) { + categoriesFound = true; + } } - categoryChooser.setEnabled(categories.size() != 0); + categoryChooser.setEnabled(categoriesFound); } } @@ -318,7 +320,18 @@ public class ContributionManagerDialog { protected void updateContributionListing() { if (editor != null) { - ArrayList libraries = new ArrayList(editor.getMode().contribLibraries); + ArrayList contributions = new ArrayList(); + + ArrayList libraries = + new ArrayList(editor.getMode().contribLibraries); + contributions.addAll(libraries); + + ArrayList tools = editor.contribTools; + contributions.addAll(tools); + + ArrayList modes = editor.getBase().getModeContribs(); + contributions.addAll(modes); + // ArrayList compilations = LibraryCompilation.list(libraries); // // // Remove libraries from the list that are part of a compilations @@ -332,11 +345,6 @@ public class ContributionManagerDialog { // } // } - ArrayList contributions = new ArrayList(); - contributions.addAll(editor.contribTools); - contributions.addAll(libraries); -// contributions.addAll(compilations); - contribListing.updateInstalledList(contributions); } } diff --git a/app/src/processing/app/contrib/ContributionPanel.java b/app/src/processing/app/contrib/ContributionPanel.java index 9f0ff9a60..09ba24faf 100644 --- a/app/src/processing/app/contrib/ContributionPanel.java +++ b/app/src/processing/app/contrib/ContributionPanel.java @@ -44,6 +44,12 @@ import processing.app.Base; * Panel that expands and gives a brief overview of a library when clicked. */ class ContributionPanel extends JPanel { + static public final String REMOVE_RESTART_MESSAGE = + "Please restart Processing to finish removing this item."; + + static public final String INSTALL_RESTART_MESSAGE = + "Please restart Processing to finish installing this item."; + private final ContributionListPanel listPanel; private final ContributionListing contribListing = ContributionListing.getInstance(); @@ -95,6 +101,7 @@ class ContributionPanel extends JPanel { public void actionPerformed(ActionEvent e) { if (contrib instanceof AvailableContribution) { installContribution((AvailableContribution) contrib); + contribListing.replaceContribution(contrib, contrib); } } }; @@ -103,7 +110,7 @@ class ContributionPanel extends JPanel { public void actionPerformed(ActionEvent e) { if (contrib instanceof LocalContribution) { LocalContribution installed = (LocalContribution) contrib; - installed.unsetDeletionFlag(); + installed.setDeletionFlag(false); contribListing.replaceContribution(contrib, contrib); // ?? } } @@ -336,9 +343,11 @@ class ContributionPanel extends JPanel { } description.append("
"); - boolean isFlagged = contrib.isDeletionFlagged(); - if (isFlagged) { - description.append(ContributionListPanel.DELETION_MESSAGE); + //System.out.println("checking restart flag for " + contrib + " " + contrib.getName() + " and it's " + contrib.isRestartFlagged()); + if (contrib.isDeletionFlagged()) { + description.append(REMOVE_RESTART_MESSAGE); + } else if (contrib.isRestartFlagged()) { + description.append(INSTALL_RESTART_MESSAGE); } else { String sentence = contrib.getSentence(); if (sentence == null || sentence.isEmpty()) { @@ -357,11 +366,14 @@ class ContributionPanel extends JPanel { if (contribListing.hasUpdates(contrib)) { StringBuilder versionText = new StringBuilder(); versionText.append(""); - if (isFlagged) { - versionText.append("To finish an update, reinstall this contribution after the restart."); + if (contrib.isDeletionFlagged()) { + // Already marked for deletion, see requiresRestart() notes below. + versionText.append("To finish an update, reinstall this contribution after restarting."); } else { versionText.append("New version available!"); - if (contrib.requiresRestart()) { + if (contrib.getType().requiresRestart()) { + // If a contribution can't be reinstalled in-place, the user may need + // to remove the current version, restart Processing, then install. versionText.append(" To update, first remove the current version."); } } @@ -374,7 +386,7 @@ class ContributionPanel extends JPanel { } updateButton.setEnabled(true); - if (contrib != null && !contrib.requiresRestart()) { + if (contrib != null && !contrib.getType().requiresRestart()) { updateButton.setVisible(isSelected() && contribListing.hasUpdates(contrib)); } @@ -382,7 +394,7 @@ class ContributionPanel extends JPanel { installRemoveButton.removeActionListener(removeActionListener); installRemoveButton.removeActionListener(undoActionListener); - if (isFlagged) { + if (contrib.isDeletionFlagged()) { installRemoveButton.addActionListener(undoActionListener); installRemoveButton.setText("Undo"); } else if (contrib.isInstalled()) { @@ -449,7 +461,8 @@ class ContributionPanel extends JPanel { } catch (MalformedURLException e) { Base.showWarning(ContributionListPanel.INSTALL_FAILURE_TITLE, ContributionListPanel.MALFORMED_URL_MESSAGE, e); - installRemoveButton.setEnabled(true); + // not sure why we'd re-enable the button if it had an error... +// installRemoveButton.setEnabled(true); } } @@ -487,7 +500,7 @@ class ContributionPanel extends JPanel { // now a hyperlink, it will be opened as the mouse is released. enableHyperlinks = alreadySelected; - if (contrib != null && !contrib.requiresRestart()) { + if (contrib != null && !contrib.getType().requiresRestart()) { updateButton.setVisible(isSelected() && contribListing.hasUpdates(contrib)); } installRemoveButton.setVisible(isSelected() || installRemoveButton.getText().equals("Remove")); diff --git a/app/src/processing/app/contrib/ContributionType.java b/app/src/processing/app/contrib/ContributionType.java index 4c3b69fee..8b882ee59 100644 --- a/app/src/processing/app/contrib/ContributionType.java +++ b/app/src/processing/app/contrib/ContributionType.java @@ -23,6 +23,7 @@ package processing.app.contrib; import java.io.File; import java.io.FileFilter; +import java.io.IOException; import java.util.ArrayList; import processing.app.Base; @@ -30,7 +31,6 @@ import processing.app.Editor; import processing.app.Library; public enum ContributionType { -// LIBRARY, LIBRARY_COMPILATION, TOOL, MODE; LIBRARY, TOOL, MODE; @@ -38,8 +38,6 @@ public enum ContributionType { switch (this) { case LIBRARY: return "library"; -// case LIBRARY_COMPILATION: -// return "compilation"; case TOOL: return "tool"; case MODE: @@ -49,7 +47,10 @@ public enum ContributionType { }; - /** Return Mode for mode, Tool for tool, etc. */ + /** + * Get this type name as a purtied up, capitalized version. + * @return Mode for mode, Tool for tool, etc. + */ public String getTitle() { String s = toString(); return Character.toUpperCase(s.charAt(0)) + s.substring(1); @@ -60,8 +61,6 @@ public enum ContributionType { switch (this) { case LIBRARY: return "libraries"; -// case LIBRARY_COMPILATION: -// return "libraries"; case TOOL: return "tools"; case MODE: @@ -69,6 +68,26 @@ public enum ContributionType { } return null; // should be unreachable } + + + public File createTempFolder() throws IOException { + return Base.createTempFolder(toString(), "tmp", getSketchbookFolder()); + } + + + public boolean isTempFolderName(String name) { + return name.startsWith(toString()) && name.endsWith("tmp"); + } + + +// public String getTempPrefix() { +// return toString(); +// } +// +// +// public String getTempSuffix() { +// return "tmp"; +// } // public String getPropertiesName() { @@ -78,16 +97,13 @@ public enum ContributionType { static public ContributionType fromName(String s) { if (s != null) { - if ("library".equals(s.toLowerCase())) { + if ("library".equalsIgnoreCase(s)) { return LIBRARY; } -// if ("compilation".equals(s.toLowerCase())) { -// return LIBRARY_COMPILATION; -// } - if ("tool".equals(s.toLowerCase())) { + if ("tool".equalsIgnoreCase(s)) { return TOOL; } - if ("mode".equals(s.toLowerCase())) { + if ("mode".equalsIgnoreCase(s)) { return MODE; } } @@ -109,7 +125,9 @@ public enum ContributionType { boolean isCandidate(File potential) { - return (potential.isDirectory() && new File(potential, toString()).exists()); + return (potential.isDirectory() && + new File(potential, toString()).exists() && + !isTempFolderName(potential.getName())); } @@ -145,10 +163,20 @@ public enum ContributionType { } + /** + * Returns true if the type of contribution requires the PDE to restart + * when being added or removed. + */ + boolean requiresRestart() { + return this == ContributionType.TOOL || this == ContributionType.MODE; + } + + LocalContribution load(Base base, File folder) { switch (this) { case LIBRARY: - return new Library(folder); + //return new Library(folder); + return Library.load(folder); case TOOL: return ToolContribution.load(folder); case MODE: @@ -174,15 +202,20 @@ public enum ContributionType { return contribs; } + + File getBackupFolder() { + return new File(getSketchbookFolder(), "old"); + } + File createBackupFolder(StatusPanel status) { - File backupFolder = new File(getSketchbookFolder(), "old"); - if (backupFolder.isDirectory()) { - status.setErrorMessage("First remove the folder named \"old\" from the " + - getFolderName() + " folder in the sketchbook."); - return null; - } - if (!backupFolder.mkdirs()) { + File backupFolder = getBackupFolder(); +// if (backupFolder.isDirectory()) { +// status.setErrorMessage("First remove the folder named \"old\" from the " + +// getFolderName() + " folder in the sketchbook."); +// return null; +// } + if (!backupFolder.exists() && !backupFolder.mkdirs()) { status.setErrorMessage("Could not create a backup folder in the " + "sketchbook " + toString() + " folder."); return null; diff --git a/app/src/processing/app/contrib/LocalContribution.java b/app/src/processing/app/contrib/LocalContribution.java index 621448000..03a6e0f31 100644 --- a/app/src/processing/app/contrib/LocalContribution.java +++ b/app/src/processing/app/contrib/LocalContribution.java @@ -38,14 +38,15 @@ import processing.app.*; * be installed. */ public abstract class LocalContribution extends Contribution { - static public final String DELETION_FLAG = "flagged_for_deletion"; + static public final String DELETION_FLAG = "marked_for_deletion"; + static public final String RESTART_FLAG = "requires_restart"; protected String id; // 1 (unique id for this library) protected int latestVersion; // 103 protected File folder; protected HashMap properties; protected ClassLoader loader; - + public LocalContribution(File folder) { this.folder = folder; @@ -57,7 +58,7 @@ public abstract class LocalContribution extends Contribution { name = properties.get("name"); id = properties.get("id"); - category = ContributionListing.getCategory(properties.get("category")); + categories = parseCategories(properties.get("category")); if (name == null) { name = folder.getName(); } @@ -71,7 +72,6 @@ public abstract class LocalContribution extends Contribution { } catch (NumberFormatException e) { System.err.println("The version number for the “" + name + "” library is not set properly."); System.err.println("Please contact the library author to fix it according to the guidelines."); - //e.printStackTrace(); } prettyVersion = properties.get("prettyVersion"); @@ -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(); } } @@ -184,9 +185,9 @@ public abstract class LocalContribution extends Contribution { // } - LocalContribution moveAndLoad(Editor editor, - boolean confirmReplace, - StatusPanel status) { + LocalContribution copyAndLoad(Editor editor, + boolean confirmReplace, + StatusPanel status) { ArrayList oldContribs = getType().listContributions(editor); @@ -199,7 +200,7 @@ public abstract class LocalContribution extends Contribution { if ((oldContrib.getFolder().exists() && oldContrib.getFolder().equals(contribFolder)) || (oldContrib.getId() != null && oldContrib.getId().equals(getId()))) { - if (oldContrib.requiresRestart()) { + if (oldContrib.getType().requiresRestart()) { // XXX: We can't replace stuff, soooooo.... do something different if (!oldContrib.backup(editor, false, status)) { return null; @@ -214,7 +215,7 @@ public abstract class LocalContribution extends Contribution { "A pre-existing copy of the \"" + oldContrib.getName() + "\" library
"+ "has been found in your sketchbook. Clicking “Yes”
"+ "will move the existing library to a backup folder
" + - " in libraries/old before replacing it."); + "in libraries/old before replacing it."); if (result != JOptionPane.YES_OPTION || !oldContrib.backup(editor, true, status)) { return null; } @@ -243,12 +244,28 @@ public abstract class LocalContribution extends Contribution { if (contribFolder.exists()) { Base.removeDir(contribFolder); } + + File oldFolder = getFolder(); + + try { + Base.copyDir(oldFolder, contribFolder); + } catch (IOException e) { + status.setErrorMessage("Could not copy " + getTypeName() + + " \"" + getName() + "\" to the sketchbook."); + e.printStackTrace(); + return null; + } + + + /* if (!getFolder().renameTo(contribFolder)) { status.setErrorMessage("Could not move " + getTypeName() + " \"" + getName() + "\" to the sketchbook."); return null; } + */ + return getType().load(editor.getBase(), contribFolder); } @@ -260,15 +277,15 @@ public abstract class LocalContribution extends Contribution { * should instead be copied, leaving the original in place */ boolean backup(Editor editor, boolean deleteOriginal, StatusPanel status) { - - boolean success = false; File backupFolder = getType().createBackupFolder(status); + boolean success = false; if (backupFolder != null) { String libFolderName = getFolder().getName(); String prefix = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); - final String backupName = prefix + "_" + libFolderName; - File backupSubFolder = ContributionManager.getUniqueName(backupFolder, backupName); + final String backupName = prefix + " " + libFolderName; + File backupSubFolder = + ContributionManager.getUniqueName(backupFolder, backupName); if (deleteOriginal) { success = getFolder().renameTo(backupSubFolder); @@ -310,9 +327,9 @@ public abstract class LocalContribution extends Contribution { pm.startTask("Removing", ProgressMonitor.UNKNOWN); boolean doBackup = Preferences.getBoolean("contribution.backup.on_remove"); - if (requiresRestart()) { + if (getType().requiresRestart()) { if (!doBackup || (doBackup && backup(editor, false, status))) { - if (setDeletionFlag()) { + if (setDeletionFlag(true)) { contribListing.replaceContribution(this, this); } } @@ -355,33 +372,6 @@ public abstract class LocalContribution extends Contribution { return folder != null; } - - boolean setDeletionFlag() { - // Only returns false if the file already exists, so we can - // ignore the return value. - try { - new File(getFolder(), DELETION_FLAG).createNewFile(); - return true; - } catch (IOException e) { - return false; - } - } - - - boolean unsetDeletionFlag() { - return new File(getFolder(), DELETION_FLAG).delete(); - } - - - boolean isDeletionFlagged() { - return isDeletionFlagged(getFolder()); - } - - - static boolean isDeletionFlagged(File folder) { - return new File(folder, DELETION_FLAG).exists(); - } - // public String getCategory() { // return category; @@ -449,8 +439,77 @@ public abstract class LocalContribution extends Contribution { return null; } */ + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + boolean setDeletionFlag(boolean flag) { + return setFlag(DELETION_FLAG, flag); + } + + + boolean isDeletionFlagged() { + return isDeletionFlagged(getFolder()); + } + static boolean isDeletionFlagged(File folder) { + return isFlagged(folder, DELETION_FLAG); + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + boolean setRestartFlag() { + //System.out.println("setting restart flag for " + folder); + return setFlag(RESTART_FLAG, true); + } + + + @Override + boolean isRestartFlagged() { + //System.out.println("checking for restart inside LocalContribution for " + getName()); + return isFlagged(getFolder(), RESTART_FLAG); + } + + + static void clearRestartFlags(File folder) { + File restartFlag = new File(folder, RESTART_FLAG); + if (restartFlag.exists()) { + restartFlag.delete(); + } + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + private boolean setFlag(String flagFilename, boolean flag) { + if (flag) { + // Only returns false if the file already exists, so we can + // ignore the return value. + try { + new File(getFolder(), flagFilename).createNewFile(); + return true; + } catch (IOException e) { + return false; + } + } else { + return new File(getFolder(), flagFilename).delete(); + } + } + + + static private boolean isFlagged(File folder, String flagFilename) { + return new File(folder, flagFilename).exists(); + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + /** * * @param base name of the class, with or without the package @@ -489,7 +548,7 @@ public abstract class LocalContribution extends Contribution { } - class IgnorableException extends Exception { + static protected class IgnorableException extends Exception { public IgnorableException(String msg) { super(msg); } diff --git a/app/src/processing/app/contrib/ModeContribution.java b/app/src/processing/app/contrib/ModeContribution.java index 44290d006..9fdac9f0d 100644 --- a/app/src/processing/app/contrib/ModeContribution.java +++ b/app/src/processing/app/contrib/ModeContribution.java @@ -15,7 +15,7 @@ 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 + 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 */ @@ -39,20 +39,23 @@ public class ModeContribution extends LocalContribution { } - static public ModeContribution load(Base base, File folder, + static public ModeContribution load(Base base, File folder, String searchName) { try { return new ModeContribution(base, folder, searchName); + } catch (IgnorableException ig) { Base.log(ig.getMessage()); - } 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, + // 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; @@ -92,15 +95,19 @@ public class ModeContribution extends LocalContribution { existing.put(contrib.getFolder(), contrib); } File[] potential = ContributionType.MODE.listCandidates(modesFolder); - for (File folder : potential) { - if (!existing.containsKey(folder)) { + // 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 (Exception e) { + } catch (Throwable e) { e.printStackTrace(); } + } } } } diff --git a/app/src/processing/app/contrib/ToolContribution.java b/app/src/processing/app/contrib/ToolContribution.java index 94715497e..bccbda1bb 100644 --- a/app/src/processing/app/contrib/ToolContribution.java +++ b/app/src/processing/app/contrib/ToolContribution.java @@ -15,7 +15,7 @@ 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 + 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 */ @@ -40,8 +40,11 @@ public class ToolContribution extends LocalContribution implements Tool { return new ToolContribution(folder); } catch (IgnorableException ig) { Base.log(ig.getMessage()); - } catch (Exception e) { - e.printStackTrace(); + } catch (Error err) { + // Handles UnsupportedClassVersionError and others + err.printStackTrace(); + } catch (Exception ex) { + ex.printStackTrace(); } return null; } @@ -71,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/linux/Platform.java b/app/src/processing/app/platform/LinuxPlatform.java similarity index 89% rename from app/src/processing/app/linux/Platform.java rename to app/src/processing/app/platform/LinuxPlatform.java index 2a5415b82..8e9ee7c71 100644 --- a/app/src/processing/app/linux/Platform.java +++ b/app/src/processing/app/platform/LinuxPlatform.java @@ -3,12 +3,12 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2008 Ben Fry and Casey Reas + Copyright (c) 2012-2013 The Processing Foundation + Copyright (c) 2008-2012 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 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 @@ -20,15 +20,16 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -package processing.app.linux; +package processing.app.platform; import java.io.File; import processing.app.Base; +import processing.app.Platform; import processing.app.Preferences; -public class Platform extends processing.app.Platform { +public class LinuxPlatform extends Platform { public void init(Base base) { super.init(base); diff --git a/app/src/processing/app/macosx/Platform.java b/app/src/processing/app/platform/MacPlatform.java similarity index 91% rename from app/src/processing/app/macosx/Platform.java rename to app/src/processing/app/platform/MacPlatform.java index 406e65987..0407fdc6f 100644 --- a/app/src/processing/app/macosx/Platform.java +++ b/app/src/processing/app/platform/MacPlatform.java @@ -3,12 +3,12 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2008 Ben Fry and Casey Reas + Copyright (c) 2012-2013 The Processing Foundation + Copyright (c) 2008-2012 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 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 @@ -20,7 +20,7 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -package processing.app.macosx; +package processing.app.platform; import java.io.File; import java.io.FileNotFoundException; @@ -28,12 +28,13 @@ import java.io.FileNotFoundException; import com.apple.eio.FileManager; import processing.app.Base; +import processing.app.Platform; /** * Platform handler for Mac OS X. */ -public class Platform extends processing.app.Platform { +public class MacPlatform extends Platform { // Removing for 2.0b8 because Quaqua doesn't have OS X 10.8 version. /* @@ -79,8 +80,8 @@ public class Platform extends processing.app.Platform { } */ } - - + + public File getSettingsFolder() throws Exception { return new File(getLibraryFolder(), "Processing"); } @@ -101,6 +102,15 @@ public class Platform extends processing.app.Platform { } */ } + + +// /** +// * Moves the specified File object (which might be a file or folder) +// * to the trash. +// */ +// public boolean deleteFile(File file) throws IOException { +// return FileManager.moveToTrash(file); +// } /* diff --git a/app/src/processing/app/macosx/ThinkDifferent.java b/app/src/processing/app/platform/ThinkDifferent.java similarity index 75% rename from app/src/processing/app/macosx/ThinkDifferent.java rename to app/src/processing/app/platform/ThinkDifferent.java index 9a328d500..aba1ed4ba 100644 --- a/app/src/processing/app/macosx/ThinkDifferent.java +++ b/app/src/processing/app/platform/ThinkDifferent.java @@ -3,11 +3,12 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2007-2010 Ben Fry and Casey Reas + Copyright (c) 2012-2013 The Processing Foundation + Copyright (c) 2007-2012 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 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 @@ -19,15 +20,12 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -package processing.app.macosx; +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; @@ -76,35 +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 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.out.println("Skipping default menu bar due to apparent Oracle Java bug."); + // 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/platform/WindowsPlatform.java b/app/src/processing/app/platform/WindowsPlatform.java new file mode 100644 index 000000000..0c14b5ead --- /dev/null +++ b/app/src/processing/app/platform/WindowsPlatform.java @@ -0,0 +1,584 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-2013 The Processing Foundation + Copyright (c) 2008-2012 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.platform; + +import java.io.File; +import java.io.UnsupportedEncodingException; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.platform.win32.Kernel32Util; +import com.sun.jna.platform.win32.Shell32; +import com.sun.jna.platform.win32.ShlObj; +import com.sun.jna.platform.win32.WinDef; +import com.sun.jna.platform.win32.WinError; +import com.sun.jna.platform.win32.WinNT.HRESULT; + +import processing.app.Base; +import processing.app.Platform; +import processing.app.Preferences; +import processing.app.platform.WindowsRegistry.REGISTRY_ROOT_KEY; +import processing.core.PApplet; + + +/** + * Platform-specific glue for Windows. + */ +public class WindowsPlatform extends Platform { + + static final String APP_NAME = "Processing"; + static final String REG_OPEN_COMMAND = + System.getProperty("user.dir").replace('/', '\\') + + "\\" + APP_NAME.toLowerCase() + ".exe \"%1\""; + static final String REG_DOC = APP_NAME + ".Document"; + + + public void init(Base base) { + super.init(base); + checkAssociations(); + //checkQuickTime(); + checkPath(); + + /* + File f = new File(System.getProperty("user.dir"), "recycle-test.txt"); + //File f = new File("C:\\recycle-test.txt"); + System.out.println(f.getAbsolutePath()); + java.io.PrintWriter writer = PApplet.createWriter(f); + writer.println("blah"); + writer.flush(); + writer.close(); + try { + deleteFile(f); + } catch (IOException e) { + e.printStackTrace(); + } + */ + + //findJDK(); + /* + new Thread(new Runnable() { + public void run() { + try { + Thread.sleep(2000); + } catch (InterruptedException ie) { } + findJDK(); + } + }).start(); + */ + } + + +// HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit\CurrentVersion -> 1.6 (String) +// HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit\CurrentVersion\1.6\JavaHome -> c:\jdk-1.6.0_05 + + /* + static public void findJDK() { + try { + String jcpo = System.getProperty("java.home"); + String jv = System.getProperty("java.version"); + System.out.println("home and version = " + jcpo + " and " + jv); + + // the last parameter will be anything appearing on the right-hand + // side of regedit. + final String JDK_KEY = "SOFTWARE\\JavaSoft\\Java Development Kit"; + String currentVersion = + Registry.getStringValue(REGISTRY_ROOT_KEY.LOCAL_MACHINE, + JDK_KEY, + "CurrentVersion"); + System.out.println("current version is " + currentVersion); + if (currentVersion != null) { + String javaHome = + Registry.getStringValue(REGISTRY_ROOT_KEY.LOCAL_MACHINE, + JDK_KEY + "\\" + currentVersion, + "JavaHome"); + System.out.println("home is where the " + javaHome + " is"); + if (javaHome != null) { + String jcp = System.getProperty("java.class.path"); + String toolsJar = javaHome + "\\lib\\tools.jar"; + System.setProperty("java.class.path", + jcp + File.pathSeparator + toolsJar); + System.out.println("set jcp to " + + System.getProperty("java.class.path")); + } + } + } catch (UnsupportedEncodingException uee) { + uee.printStackTrace(); + } + } + */ + + + /** + * Make sure that .pde files are associated with processing.exe. + */ + protected void checkAssociations() { + try { + if (Preferences.getBoolean("platform.auto_file_type_associations")) { + // Check the key that should be set by a previous run of Processing + String knownCommand = + WindowsRegistry.getStringValue(REGISTRY_ROOT_KEY.CURRENT_USER, + "Software\\Classes\\" + REG_DOC + "\\shell\\open\\command", ""); + // If the association hasn't been set, or it's not correct, set it. + if (knownCommand == null || !knownCommand.equals(REG_OPEN_COMMAND)) { + setAssociations(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + + /** + * Associate .pde files with this version of Processing. After 2.0.1, + * this was changed to only set the values for the current user, so that + * it would no longer silently fail on systems that have UAC turned on. + */ + protected void setAssociations() throws UnsupportedEncodingException { + // http://support.microsoft.com/kb/184082 + // http://msdn.microsoft.com/en-us/library/cc144175%28v=VS.85%29.aspx + // http://msdn.microsoft.com/en-us/library/cc144104%28v=VS.85%29.aspx + // http://msdn.microsoft.com/en-us/library/cc144067%28v=VS.85%29.aspx + // msdn.microsoft.com/en-us/library/windows/desktop/ms724475(v=vs.85).aspx + +// HKEY_CLASSES_ROOT +// MyProgram.exe +// shell +// open +// command +// (Default) = C:\MyDir\MyProgram.exe "%1" + +/* + REGISTRY_ROOT_KEY rootKey = REGISTRY_ROOT_KEY.CLASSES_ROOT; + if (Registry.createKey(rootKey, + "", ".pde") && + Registry.setStringValue(rootKey, + ".pde", "", DOC) && + + Registry.createKey(rootKey, "", DOC) && + Registry.setStringValue(rootKey, DOC, "", + "Processing Source Code") && + + Registry.createKey(rootKey, + DOC, "shell") && + Registry.createKey(rootKey, + DOC + "\\shell", "open") && + Registry.createKey(rootKey, + DOC + "\\shell\\open", "command") && + Registry.setStringValue(rootKey, + DOC + "\\shell\\open\\command", "", + openCommand)) { +*/ + + // "To change the settings for the interactive user, store the changes + // under HKEY_CURRENT_USER\Software\Classes rather than HKEY_CLASSES_ROOT." + // msdn.microsoft.com/en-us/library/windows/desktop/ms724475(v=vs.85).aspx + final REGISTRY_ROOT_KEY rootKey = REGISTRY_ROOT_KEY.CURRENT_USER; + final String docPrefix = "Software\\Classes\\" + REG_DOC; + + // First create the .pde association + if (WindowsRegistry.createKey(rootKey, "Software\\Classes", ".pde") && + WindowsRegistry.setStringValue(rootKey, "Software\\Classes\\.pde", "", REG_DOC) && + + // Now give files with a .pde extension a name for the explorer + WindowsRegistry.createKey(rootKey, "Software\\Classes", REG_DOC) && + WindowsRegistry.setStringValue(rootKey, docPrefix, "", APP_NAME + " Source Code") && + + // Now associate the 'open' command with the current processing.exe + WindowsRegistry.createKey(rootKey, docPrefix, "shell") && + WindowsRegistry.createKey(rootKey, docPrefix + "\\shell", "open") && + WindowsRegistry.createKey(rootKey, docPrefix + "\\shell\\open", "command") && + WindowsRegistry.setStringValue(rootKey, docPrefix + "\\shell\\open\\command", "", REG_OPEN_COMMAND)) { + + // everything ok + // hooray! + + } else { + Base.log("Could not associate files, turning off auto-associate pref."); + Preferences.setBoolean("platform.auto_file_type_associations", false); + } + } + + + /** + * Remove extra quotes, slashes, and garbage from the Windows PATH. + */ + protected void checkPath() { + String path = System.getProperty("java.library.path"); + String[] pieces = PApplet.split(path, File.pathSeparatorChar); + String[] legit = new String[pieces.length]; + int legitCount = 0; + for (String item : pieces) { + if (item.startsWith("\"")) { + item = item.substring(1); + } + if (item.endsWith("\"")) { + item = item.substring(0, item.length() - 1); + } + if (item.endsWith(File.separator)) { + item = item.substring(0, item.length() - File.separator.length()); + } + File directory = new File(item); + if (!directory.exists()) { + continue; + } + if (item.trim().length() == 0) { + continue; + } + legit[legitCount++] = item; + } + legit = PApplet.subset(legit, 0, legitCount); + String newPath = PApplet.join(legit, File.pathSeparator); + if (!newPath.equals(path)) { + System.setProperty("java.library.path", newPath); + } + } + + + // looking for Documents and Settings/blah/Application Data/Processing + public File getSettingsFolder() throws Exception { + String appData = getAppDataPath(); + if (appData != null) { + return new File(appData, APP_NAME); + } + return null; + } + + + static private String getAppDataPath() throws Exception { + // HKEY_CURRENT_USER\Software\Microsoft + // \Windows\CurrentVersion\Explorer\Shell Folders + // Value Name: AppData + // Value Type: REG_SZ + // Value Data: path + + //String keyPath = + // "Software\\Microsoft\\Windows\\CurrentVersion" + + // "\\Explorer\\Shell Folders"; + //String appDataPath = + // Registry.getStringValue(REGISTRY_ROOT_KEY.CURRENT_USER, keyPath, "AppData"); + + // Fix for Issue 410 + // Java 1.6 doesn't provide a good workaround (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6519127) + // Using JNA and SHGetFolderPath instead. + + // this will be contain the path if SHGetFolderPath is successful + char[] pszPath = new char[WinDef.MAX_PATH]; + HRESULT hResult = + Shell32.INSTANCE.SHGetFolderPath(null, ShlObj.CSIDL_APPDATA, + null, ShlObj.SHGFP_TYPE_CURRENT, + pszPath); + + if (!hResult.equals(WinError.S_OK)) { + System.err.println(Kernel32Util.formatMessageFromHR(hResult)); + throw new Exception("Problem city, population: your computer."); + } + + String appDataPath = new String(pszPath); + int len = appDataPath.indexOf("\0"); +// appDataPath = appDataPath.substring(0, len); +// return new File(appDataPath, "Processing"); + return appDataPath.substring(0, len); + } + + + // looking for Documents and Settings/blah/My Documents/Processing + public File getDefaultSketchbookFolder() throws Exception { + String documentsPath = getDocumentsPath(); + if (documentsPath != null) { + return new File(documentsPath, APP_NAME); + } + return null; + } + + + static private String getDocumentsPath() throws Exception { + // http://support.microsoft.com/?kbid=221837&sd=RMVP + // http://support.microsoft.com/kb/242557/en-us + + // The path to the My Documents folder is stored in the following + // registry key, where path is the complete path to your storage location + + // HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders + // Value Name: Personal + // Value Type: REG_SZ + // Value Data: path + + // in some instances, this may be overridden by a policy, in which case check: + // HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders + + //String keyPath = + // "Software\\Microsoft\\Windows\\CurrentVersion" + + // "\\Explorer\\Shell Folders"; + //String personalPath = + // Registry.getStringValue(REGISTRY_ROOT_KEY.CURRENT_USER, keyPath, "Personal"); + + // "The "Shell Folders" key exists solely to permit four programs written + // in 1994 to continue running on the RTM version of Windows 95." -- Raymond Chen, MSDN + + char[] pszPath = new char[WinDef.MAX_PATH]; // this will be contain the path if SHGetFolderPath is successful + HRESULT hResult = Shell32.INSTANCE.SHGetFolderPath(null, ShlObj.CSIDL_PERSONAL, null, ShlObj.SHGFP_TYPE_CURRENT, pszPath); + + if (!hResult.equals(WinError.S_OK)) { + System.err.println(Kernel32Util.formatMessageFromHR(hResult)); + throw new Exception("Problem city, population: your computer."); + } + + String personalPath = new String(pszPath); + int len = personalPath.indexOf("\0"); +// personalPath = personalPath.substring(0, len); +// return new File(personalPath, "Processing"); + return personalPath.substring(0, len); + } + + +// @Override +// public boolean deleteFile(File file) { +// try { +// moveToTrash(new File[] { file }); +// } catch (IOException e) { +// e.printStackTrace(); +// Base.log("Could not move " + file.getAbsolutePath() + " to the trash.", e); +// return false; +// } +// return true; +// } + + +// /** +// * Move files/folders to the trash. If this file is on another file system +// * or on a shared network directory, it will simply be deleted without any +// * additional confirmation. Take that. +// *

+// * Based on JNA source for com.sun.jna.platform.win32.W32FileUtils +// * +// * @param files array of File objects to be removed +// * @return true if no error codes returned +// * @throws IOException if something bad happened along the way +// */ +// static private boolean moveToTrash(File[] files) throws IOException { +// Shell32 shell = Shell32.INSTANCE; +// SHFILEOPSTRUCT fileop = new SHFILEOPSTRUCT(); +// fileop.wFunc = ShellAPI.FO_DELETE; +// String[] paths = new String[files.length]; +// for (int i = 0; i < paths.length; i++) { +// paths[i] = files[i].getAbsolutePath(); +// System.out.println(paths[i]); +// } +// fileop.pFrom = new WString(fileop.encodePaths(paths)); +// fileop.fFlags = ShellAPI.FOF_ALLOWUNDO | ShellAPI.FOF_NO_UI; +// int ret = shell.SHFileOperation(fileop); +// if (ret != 0) { +// throw new IOException("Move to trash failed: " + +// fileop.pFrom + ": error code " + ret); +//// throw new IOException("Move to trash failed: " + fileop.pFrom + ": " + +//// Kernel32Util.formatMessageFromLastErrorCode(ret)); +// } +// if (fileop.fAnyOperationsAborted) { +// throw new IOException("Move to trash aborted"); +// } +// return true; +// } + + +// /** +// * Ported from ShellAPI.h in the Microsoft Windows SDK 6.0A. +// * Modified (bastardized) version from the JNA "platform" classes. +// * @author dblock[at]dblock.org +// */ +// public interface ShellAPI extends StdCallLibrary { +// +// int STRUCTURE_ALIGNMENT = com.sun.jna.Platform.is64Bit() ? +// Structure.ALIGN_DEFAULT : Structure.ALIGN_NONE; +// +// int FO_MOVE = 0x0001; +// int FO_COPY = 0x0002; +// int FO_DELETE = 0x0003; +// int FO_RENAME = 0x0004; +// +// int FOF_MULTIDESTFILES = 0x0001; +// int FOF_CONFIRMMOUSE = 0x0002; +// int FOF_SILENT = 0x0004; // don't display progress UI (confirm prompts may be displayed still) +// int FOF_RENAMEONCOLLISION = 0x0008; // automatically rename the source files to avoid the collisions +// int FOF_NOCONFIRMATION = 0x0010; // don't display confirmation UI, assume "yes" for cases that can be bypassed, "no" for those that can not +// int FOF_WANTMAPPINGHANDLE = 0x0020; // Fill in SHFILEOPSTRUCT.hNameMappings +// int FOF_ALLOWUNDO = 0x0040; // enable undo including Recycle behavior for IFileOperation::Delete() +// int FOF_FILESONLY = 0x0080; // only operate on the files (non folders), both files and folders are assumed without this +// int FOF_SIMPLEPROGRESS = 0x0100; // means don't show names of files +// int FOF_NOCONFIRMMKDIR = 0x0200; // don't dispplay confirmatino UI before making any needed directories, assume "Yes" in these cases +// int FOF_NOERRORUI = 0x0400; // don't put up error UI, other UI may be displayed, progress, confirmations +// int FOF_NOCOPYSECURITYATTRIBS = 0x0800; // dont copy file security attributes (ACLs) +// int FOF_NORECURSION = 0x1000; // don't recurse into directories for operations that would recurse +// int FOF_NO_CONNECTED_ELEMENTS = 0x2000; // don't operate on connected elements ("xxx_files" folders that go with .htm files) +// int FOF_WANTNUKEWARNING = 0x4000; // during delete operation, warn if nuking instead of recycling (partially overrides FOF_NOCONFIRMATION) +// int FOF_NORECURSEREPARSE = 0x8000; // deprecated; the operations engine always does the right thing on FolderLink objects (symlinks, reparse points, folder shortcuts) +// int FOF_NO_UI = (FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR); // don't display any UI at all +// +// int PO_DELETE = 0x0013; // printer is being deleted +// int PO_RENAME = 0x0014; // printer is being renamed +// int PO_PORTCHANGE = 0x0020; // port this printer connected to is being changed +// int PO_REN_PORT = 0x0034; // PO_RENAME and PO_PORTCHANGE at same time. +// } + + + /* + public void openURL(String url) throws Exception { + // this is not guaranteed to work, because who knows if the + // path will always be c:\progra~1 et al. also if the user has + // a different browser set as their default (which would + // include me) it'd be annoying to be dropped into ie. + //Runtime.getRuntime().exec("c:\\progra~1\\intern~1\\iexplore " + // + currentDir + + // the following uses a shell execute to launch the .html file + // note that under cygwin, the .html files have to be chmodded +x + // after they're unpacked from the zip file. i don't know why, + // and don't understand what this does in terms of windows + // permissions. without the chmod, the command prompt says + // "Access is denied" in both cygwin and the "dos" prompt. + //Runtime.getRuntime().exec("cmd /c " + currentDir + "\\reference\\" + + // referenceFile + ".html"); + if (url.startsWith("http://")) { + // open dos prompt, give it 'start' command, which will + // open the url properly. start by itself won't work since + // it appears to need cmd + Runtime.getRuntime().exec("cmd /c start " + url); + } else { + // just launching the .html file via the shell works + // but make sure to chmod +x the .html files first + // also place quotes around it in case there's a space + // in the user.dir part of the url + Runtime.getRuntime().exec("cmd /c \"" + url + "\""); + } + } + + + public boolean openFolderAvailable() { + return true; + } + + + public void openFolder(File file) throws Exception { + String folder = file.getAbsolutePath(); + + // doesn't work + //Runtime.getRuntime().exec("cmd /c \"" + folder + "\""); + + // works fine on winxp, prolly win2k as well + Runtime.getRuntime().exec("explorer \"" + folder + "\""); + + // not tested + //Runtime.getRuntime().exec("start explorer \"" + folder + "\""); + } + */ + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + // Code partially thanks to Richard Quirk from: + // http://quirkygba.blogspot.com/2009/11/setting-environment-variables-in-java.html + + static WinLibC clib = (WinLibC) Native.loadLibrary("msvcrt", WinLibC.class); + + public interface WinLibC extends Library { + //WinLibC INSTANCE = (WinLibC) Native.loadLibrary("msvcrt", WinLibC.class); + //libc = Native.loadLibrary("msvcrt", WinLibC.class); + public int _putenv(String name); + } + + + public void setenv(String variable, String value) { + //WinLibC clib = WinLibC.INSTANCE; + clib._putenv(variable + "=" + value); + } + + + public String getenv(String variable) { + return System.getenv(variable); + } + + + public int unsetenv(String variable) { + //WinLibC clib = WinLibC.INSTANCE; + //clib._putenv(variable + "="); + //return 0; + return clib._putenv(variable + "="); + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + // JNA code for using SHGetFolderPath to fix Issue 410 + // https://code.google.com/p/processing/issues/detail?id=410 + // Based on answer provided by McDowell at + // http://stackoverflow.com/questions/585534/what-is-the-best-way-to-find-the-users-home-directory-in-java/586917#586917 + +// private static Map OPTIONS = new HashMap(); +// +// static { +// OPTIONS.put(Library.OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE); +// OPTIONS.put(Library.OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE); +// } +// +// +// static class HANDLE extends PointerType implements NativeMapped { +// public HANDLE() { } +// } +// +// static class HWND extends HANDLE { } +// +// +// public interface Shell32 extends Library { +// +// public static final int MAX_PATH = 260; +// public static final int SHGFP_TYPE_CURRENT = 0; +// public static final int SHGFP_TYPE_DEFAULT = 1; +// public static final int S_OK = 0; +// +// // KNOWNFOLDERIDs are preferred to CSDIL values +// // but Windows XP only supports CSDIL so thats what we have to use +// public static final int CSIDL_APPDATA = 0x001a; // "Application Data" +// public static final int CSIDL_PERSONAL = 0x0005; // "My Documents" +// +// static Shell32 INSTANCE = (Shell32) Native.loadLibrary("shell32", Shell32.class, OPTIONS); +// +// /** +// * see http://msdn.microsoft.com/en-us/library/bb762181(VS.85).aspx +// * +// * HRESULT SHGetFolderPath( HWND hwndOwner, int nFolder, HANDLE hToken, +// * DWORD dwFlags, LPTSTR pszPath); +// */ +// public int SHGetFolderPath(HWND hwndOwner, int nFolder, HANDLE hToken, +// int dwFlags, char[] pszPath); +// +// /** +// * This function can be used to copy, move, rename, +// * or delete a file system object. +// * @param fileop Address of an SHFILEOPSTRUCT structure that contains +// * information this function needs to carry out the specified operation. +// * @return Returns zero if successful, or nonzero otherwise. +// */ +// public int SHFileOperation(SHFILEOPSTRUCT fileop); +// } +} diff --git a/app/src/processing/app/platform/WindowsRegistry.java b/app/src/processing/app/platform/WindowsRegistry.java new file mode 100644 index 000000000..dd2581cd5 --- /dev/null +++ b/app/src/processing/app/platform/WindowsRegistry.java @@ -0,0 +1,486 @@ +package processing.app.platform; + +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.TreeMap; +import java.util.TreeSet; + +import com.sun.jna.platform.win32.Advapi32; +import com.sun.jna.platform.win32.WinBase; +import com.sun.jna.platform.win32.WinError; +import com.sun.jna.platform.win32.WinNT; +import com.sun.jna.platform.win32.WinReg; +import com.sun.jna.platform.win32.WinReg.HKEY; +import com.sun.jna.platform.win32.WinReg.HKEYByReference; +import com.sun.jna.ptr.IntByReference; + + +/** + * Methods for accessing the Windows Registry. Only String and DWORD values + * supported at the moment. + *

+ * Not sure where this code came from originally (if you know the reference, + * please get in touch so that we can add a proper citation). Several changes + * were made to update it for JNA 3.5.2's platform classes and clean up the + * syntax to make it less like a C program. [fry 130720] + */ +public class WindowsRegistry { + static public enum REGISTRY_ROOT_KEY { + CLASSES_ROOT, CURRENT_USER, LOCAL_MACHINE, USERS + }; + //private final static HashMap rootKeyMap = new HashMap(); + private final static HashMap rootKeyMap = + new HashMap(); + + static { + rootKeyMap.put(REGISTRY_ROOT_KEY.CLASSES_ROOT, WinReg.HKEY_CLASSES_ROOT); + rootKeyMap.put(REGISTRY_ROOT_KEY.CURRENT_USER, WinReg.HKEY_CURRENT_USER); + rootKeyMap.put(REGISTRY_ROOT_KEY.LOCAL_MACHINE, WinReg.HKEY_LOCAL_MACHINE); + rootKeyMap.put(REGISTRY_ROOT_KEY.USERS, WinReg.HKEY_USERS); + } + + + /** + * Gets one of the root keys. + * + * @param key key type + * @return root key + */ + private static HKEY getRegistryRootKey(REGISTRY_ROOT_KEY key) { + //Advapi32 advapi32; + //IntByReference pHandle; + //int handle = 0; + + Advapi32 advapi32 = Advapi32.INSTANCE; +// pHandle = new IntByReference(); + HKEYByReference pHandle = new WinReg.HKEYByReference(); + + HKEY handle = null; + if (advapi32.RegOpenKeyEx(rootKeyMap.get(key), null, 0, 0, pHandle) == WinError.ERROR_SUCCESS) { + handle = pHandle.getValue(); + } + return handle; + } + + + /** + * Opens a key. + * + * @param rootKey root key + * @param subKeyName name of the key + * @param access access mode + * @return handle to the key or 0 + */ + private static HKEY openKey(REGISTRY_ROOT_KEY rootKey, String subKeyName, int access) { + //Advapi32 advapi32; + //IntByReference pHandle; + //int rootKeyHandle; + + Advapi32 advapi32 = Advapi32.INSTANCE; + HKEY rootKeyHandle = getRegistryRootKey(rootKey); + //pHandle = new IntByReference(); + HKEYByReference pHandle = new HKEYByReference(); + + if (advapi32.RegOpenKeyEx(rootKeyHandle, subKeyName, 0, access, pHandle) == WinError.ERROR_SUCCESS) { + return pHandle.getValue(); + } else { + return null; + } + } + + + /** + * Converts a Windows buffer to a Java String. + * + * @param buf buffer + * @throws java.io.UnsupportedEncodingException on error + * @return String + */ + private static String convertBufferToString(byte[] buf) throws UnsupportedEncodingException { + return new String(buf, 0, buf.length - 2, "UTF-16LE"); + } + + + /** + * Converts a Windows buffer to an int. + * + * @param buf buffer + * @return int + */ + private static int convertBufferToInt(byte[] buf) { + return ((buf[0] & 0xff) + + ((buf[1] & 0xff) << 8) + + ((buf[2] & 0xff) << 16) + + ((buf[3] & 0xff) << 24)); + } + + + /** + * Read a String value. + * + * @param rootKey root key + * @param subKeyName key name + * @param name value name + * @throws java.io.UnsupportedEncodingException on error + * @return String or null + */ + static public String getStringValue(REGISTRY_ROOT_KEY rootKey, String subKeyName, String name) throws UnsupportedEncodingException { + //Advapi32 advapi32; + //IntByReference pType, lpcbData; + byte[] lpData = new byte[1]; + //int handle = 0; + + Advapi32 advapi32 = Advapi32.INSTANCE; + IntByReference pType = new IntByReference(); + IntByReference lpcbData = new IntByReference(); + HKEY handle = openKey(rootKey, subKeyName, WinNT.KEY_READ); + + String ret = null; + //if (handle != 0) { + if (handle != null) { + //if (advapi32.RegQueryValueEx(handle, name, null, pType, lpData, lpcbData) == WinError.ERROR_MORE_DATA) { + if (advapi32.RegQueryValueEx(handle, name, 0, pType, lpData, lpcbData) == WinError.ERROR_MORE_DATA) { + lpData = new byte[lpcbData.getValue()]; + + //if (advapi32.RegQueryValueEx(handle, name, null, pType, lpData, lpcbData) == WinError.ERROR_SUCCESS) { + if (advapi32.RegQueryValueEx(handle, name, 0, pType, lpData, lpcbData) == WinError.ERROR_SUCCESS) { + ret = convertBufferToString(lpData); + } + } + advapi32.RegCloseKey(handle); + } + return ret; + } + + + /** + * Read an int value. + * + * @return int or 0 + * @param rootKey root key + * @param subKeyName key name + * @param name value name + */ + static public int getIntValue(REGISTRY_ROOT_KEY rootKey, String subKeyName, String name) { + Advapi32 advapi32 = Advapi32.INSTANCE; + IntByReference pType = new IntByReference(); + IntByReference lpcbData = new IntByReference(); + HKEY handle = openKey(rootKey, subKeyName, WinNT.KEY_READ); + + int ret = 0; + byte[] lpData = new byte[1]; + //if(handle != 0) { + if (handle != null) { + //if (advapi32.RegQueryValueEx(handle, name, null, pType, lpData, lpcbData) == WinError.ERROR_MORE_DATA) { + if (advapi32.RegQueryValueEx(handle, name, 0, pType, lpData, lpcbData) == WinError.ERROR_MORE_DATA) { + lpData = new byte[lpcbData.getValue()]; + + //if(advapi32.RegQueryValueEx(handle, name, null, pType, lpData, lpcbData) == WinError.ERROR_SUCCESS) { + if (advapi32.RegQueryValueEx(handle, name, 0, pType, lpData, lpcbData) == WinError.ERROR_SUCCESS) { + ret = convertBufferToInt(lpData); + } + } + advapi32.RegCloseKey(handle); + } + return ret; + } + + + /** + * Delete a value. + * + * @param rootKey root key + * @param subKeyName key name + * @param name value name + * @return true on success + */ + static public boolean deleteValue(REGISTRY_ROOT_KEY rootKey, String subKeyName, String name) { + Advapi32 advapi32 = Advapi32.INSTANCE; + //int handle; + + HKEY handle = openKey(rootKey, subKeyName, WinNT.KEY_READ | WinNT.KEY_WRITE); + + boolean ret = true; + //if(handle != 0) { + if (handle != null) { + if (advapi32.RegDeleteValue(handle, name) == WinError.ERROR_SUCCESS) { + ret = true; + } + advapi32.RegCloseKey(handle); + } + return ret; + } + + + /** + * Writes a String value. + * + * @param rootKey root key + * @param subKeyName key name + * @param name value name + * @param value value + * @throws java.io.UnsupportedEncodingException on error + * @return true on success + */ + static public boolean setStringValue(REGISTRY_ROOT_KEY rootKey, String subKeyName, String name, String value) throws UnsupportedEncodingException { + //int handle; + //byte[] data; + + // appears to be Java 1.6 syntax, removing [fry] + //data = Arrays.copyOf(value.getBytes("UTF-16LE"), value.length() * 2 + 2); + byte[] data = new byte[value.length() * 2 + 2]; + byte[] src = value.getBytes("UTF-16LE"); + System.arraycopy(src, 0, data, 0, src.length); + + Advapi32 advapi32 = Advapi32.INSTANCE; + HKEY handle = openKey(rootKey, subKeyName, WinNT.KEY_READ | WinNT.KEY_WRITE); + + boolean ret = false; + //if(handle != 0) { + if (handle != null) { + if (advapi32.RegSetValueEx(handle, name, 0, WinNT.REG_SZ, data, data.length) == WinError.ERROR_SUCCESS) { + ret = true; + } + advapi32.RegCloseKey(handle); + } + return ret; + } + + + /** + * Writes an int value. + * + * @return true on success + * @param rootKey root key + * @param subKeyName key name + * @param name value name + * @param value value + */ + static public boolean setIntValue(REGISTRY_ROOT_KEY rootKey, String subKeyName, String name, int value) { + //Advapi32 advapi32; + //int handle; + //byte[] data; + + byte[] data = new byte[4]; + data[0] = (byte)(value & 0xff); + data[1] = (byte)((value >> 8) & 0xff); + data[2] = (byte)((value >> 16) & 0xff); + data[3] = (byte)((value >> 24) & 0xff); + Advapi32 advapi32 = Advapi32.INSTANCE; + HKEY handle = openKey(rootKey, subKeyName, WinNT.KEY_READ | WinNT.KEY_WRITE); + + boolean ret = false; + //if(handle != 0) { + if (handle != null) { + if (advapi32.RegSetValueEx(handle, name, 0, WinNT.REG_DWORD, data, data.length) == WinError.ERROR_SUCCESS) { + ret = true; + } + advapi32.RegCloseKey(handle); + } + return ret; + } + + + /** + * Check for existence of a value. + * + * @param rootKey root key + * @param subKeyName key name + * @param name value name + * @return true if exists + */ + static public boolean valueExists(REGISTRY_ROOT_KEY rootKey, String subKeyName, String name) { + //Advapi32 advapi32; + //IntByReference pType, lpcbData; + //int handle = 0; + + Advapi32 advapi32 = Advapi32.INSTANCE; + IntByReference pType = new IntByReference(); + IntByReference lpcbData = new IntByReference(); + HKEY handle = openKey(rootKey, subKeyName, WinNT.KEY_READ); + + byte[] lpData = new byte[1]; + boolean ret = false; + //if(handle != 0) { + if (handle != null) { + //if (advapi32.RegQueryValueEx(handle, name, null, pType, lpData, lpcbData) != WinError.ERROR_FILE_NOT_FOUND) { + if (advapi32.RegQueryValueEx(handle, name, 0, pType, lpData, lpcbData) != WinError.ERROR_FILE_NOT_FOUND) { + ret = true; + + } else { + ret = false; + } + advapi32.RegCloseKey(handle); + } + return ret; + } + + + /** + * Create a new key. + * + * @param rootKey root key + * @param parent name of parent key + * @param name key name + * @return true on success + */ + static public boolean createKey(REGISTRY_ROOT_KEY rootKey, String parent, String name) { + //Advapi32 advapi32; + //IntByReference hkResult, dwDisposition; + //int handle = 0; + + Advapi32 advapi32 = Advapi32.INSTANCE; + //IntByReference hkResult = new IntByReference(); + HKEYByReference hkResult = new HKEYByReference(); + IntByReference dwDisposition = new IntByReference(); + HKEY handle = openKey(rootKey, parent, WinNT.KEY_READ); + + boolean ret = false; + //if(handle != 0) { + if (handle != null) { + if (advapi32.RegCreateKeyEx(handle, name, 0, null, WinNT.REG_OPTION_NON_VOLATILE, WinNT.KEY_READ, null, + hkResult, dwDisposition) == WinError.ERROR_SUCCESS) { + ret = true; + advapi32.RegCloseKey(hkResult.getValue()); + + } else { + ret = false; + } + advapi32.RegCloseKey(handle); + } + return ret; + } + + + /** + * Delete a key. + * + * @param rootKey root key + * @param parent name of parent key + * @param name key name + * @return true on success + */ + static public boolean deleteKey(REGISTRY_ROOT_KEY rootKey, String parent, String name) { + //Advapi32 advapi32; + //int handle = 0; + + Advapi32 advapi32 = Advapi32.INSTANCE; + HKEY handle = openKey(rootKey, parent, WinNT.KEY_READ); + + boolean ret = false; + //if(handle != 0) { + if (handle != null) { + if (advapi32.RegDeleteKey(handle, name) == WinError.ERROR_SUCCESS) { + ret = true; + + } else { + ret = false; + } + advapi32.RegCloseKey(handle); + } + return ret; + } + + + /** + * Get all sub keys of a key. + * + * @param rootKey root key + * @param parent key name + * @return array with all sub key names + */ + static public String[] getSubKeys(REGISTRY_ROOT_KEY rootKey, String parent) { + //Advapi32 advapi32; + //int handle = 0, dwIndex; + //char[] lpName; + //IntByReference lpcName; + //WinBase.FILETIME lpftLastWriteTime; + TreeSet subKeys = new TreeSet(); + Advapi32 advapi32 = Advapi32.INSTANCE; + HKEY handle = openKey(rootKey, parent, WinNT.KEY_READ); + char[] lpName = new char[256]; + IntByReference lpcName = new IntByReference(256); + WinBase.FILETIME lpftLastWriteTime = new WinBase.FILETIME(); + + //if(handle != 0) { + if (handle != null) { + int dwIndex = 0; + + while (advapi32.RegEnumKeyEx(handle, dwIndex, lpName, lpcName, null, + null, null, lpftLastWriteTime) == WinError.ERROR_SUCCESS) { + subKeys.add(new String(lpName, 0, lpcName.getValue())); + lpcName.setValue(256); + dwIndex++; + } + advapi32.RegCloseKey(handle); + } + return subKeys.toArray(new String[] { }); + } + + + /** + * Get all values under a key. + * + * @param rootKey root key + * @param key jey name + * @throws java.io.UnsupportedEncodingException on error + * @return TreeMap with name and value pairs + */ + static public TreeMap getValues(REGISTRY_ROOT_KEY rootKey, String key) throws UnsupportedEncodingException { + //Advapi32 advapi32; + //int handle = 0, dwIndex, result = 0; + //char[] lpValueName; + //byte[] lpData; + //IntByReference lpcchValueName, lpType, lpcbData; + //String name; + TreeMap values = + new TreeMap(String.CASE_INSENSITIVE_ORDER); + + Advapi32 advapi32 = Advapi32.INSTANCE; + HKEY handle = openKey(rootKey, key, WinNT.KEY_READ); + char[] lpValueName = new char[16384]; + IntByReference lpcchValueName = new IntByReference(16384); + IntByReference lpType = new IntByReference(); + byte[] lpData = new byte[1]; + IntByReference lpcbData = new IntByReference(); + + //if(handle != 0) { + if (handle != null) { + int dwIndex = 0; + int result = 0; + String name; + + do { + lpcbData.setValue(0); + result = advapi32.RegEnumValue(handle, dwIndex, lpValueName, lpcchValueName, null, + lpType, lpData, lpcbData); + + if (result == WinError.ERROR_MORE_DATA) { + lpData = new byte[lpcbData.getValue()]; + lpcchValueName = new IntByReference(16384); + result = advapi32.RegEnumValue(handle, dwIndex, lpValueName, lpcchValueName, null, + lpType, lpData, lpcbData); + + if (result == WinError.ERROR_SUCCESS) { + name = new String(lpValueName, 0, lpcchValueName.getValue()); + + switch(lpType.getValue()) { + case WinNT.REG_SZ: + values.put(name, convertBufferToString(lpData)); + break; + case WinNT.REG_DWORD: + values.put(name, convertBufferToInt(lpData)); + break; + default: + break; + } + } + } + dwIndex++; + } while (result == WinError.ERROR_SUCCESS); + + advapi32.RegCloseKey(handle); + } + return values; + } +} 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 361f41def..bc5bb0ed3 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; @@ -78,7 +80,7 @@ public class JEditTextArea extends JComponent private InputMethodSupport inputMethodSupport = null; private Brackets bracketHelper = new Brackets(); - + /** * Creates a new JEditTextArea with the specified settings. @@ -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; @@ -156,8 +158,8 @@ public class JEditTextArea extends JComponent // System.out.println(" mods extext = " + mods + " " + mods.length() + " " + PApplet.hex(mods.charAt(0))); // } // System.out.println(" " + e); - - // inertia scrolling on OS X will fire several shift-wheel events + + // inertia scrolling on OS X will fire several shift-wheel events // that are negative values.. this makes the scrolling area jump. boolean skip = Base.isMacOS() && e.isShiftDown(); //if (ex == 0) { @@ -187,7 +189,7 @@ public class JEditTextArea extends JComponent return null; } - + /** * Get current position of the vertical scroll bar. [fry] */ @@ -210,8 +212,13 @@ public class JEditTextArea extends JComponent public final TextAreaPainter getPainter() { return painter; } - + + public final Printable getPrintable() { + return painter.getPrintable(); + } + + /** * Returns the input handler. */ @@ -219,7 +226,7 @@ public class JEditTextArea extends JComponent return inputHandler; } - + /** * Sets the input handler. * @param inputHandler The new input handler @@ -228,7 +235,7 @@ public class JEditTextArea extends JComponent this.inputHandler = inputHandler; } - + /** * Returns true if the caret is blinking, false otherwise. */ @@ -236,7 +243,7 @@ public class JEditTextArea extends JComponent return caretBlinks; } - + /** * Toggles caret blinking. * @param caretBlinks True if the caret should blink, false otherwise @@ -249,7 +256,7 @@ public class JEditTextArea extends JComponent painter.invalidateSelectedLines(); } - + /** * Returns true if the caret is visible, false otherwise. */ @@ -257,7 +264,7 @@ public class JEditTextArea extends JComponent return (!caretBlinks || blink) && caretVisible; } - + /** * Sets if the caret should be visible. * @param caretVisible True if the caret should be visible, false @@ -270,7 +277,7 @@ public class JEditTextArea extends JComponent painter.invalidateSelectedLines(); } - + /** * Blinks the caret. */ @@ -283,7 +290,7 @@ public class JEditTextArea extends JComponent } } - + /** * Returns the number of lines from the top and button of the * text area that are always visible. @@ -292,7 +299,7 @@ public class JEditTextArea extends JComponent return electricScroll; } - + /** * Sets the number of lines from the top and bottom of the text * area that are always visible @@ -349,7 +356,7 @@ public class JEditTextArea extends JComponent } } - + /** * Returns the line displayed at the text area's origin. */ @@ -357,7 +364,7 @@ public class JEditTextArea extends JComponent return firstLine; } - + /** * Sets the line displayed at the text area's origin without * updating the scroll bars. @@ -371,15 +378,15 @@ public class JEditTextArea extends JComponent } painter.repaint(); } - - - /** - * Convenience for checking what's on-screen. [fry] + + + /** + * Convenience for checking what's on-screen. [fry] */ public final int getLastLine() { return getFirstLine() + getVisibleLines(); } - + /** * Returns the number of lines visible in this text area. @@ -388,7 +395,7 @@ public class JEditTextArea extends JComponent return visibleLines; } - + /** * Recalculates the number of visible lines. This should not * be called directly. @@ -402,7 +409,7 @@ public class JEditTextArea extends JComponent updateScrollBars(); } - + /** * Returns the horizontal offset of drawn lines. */ @@ -410,7 +417,7 @@ public class JEditTextArea extends JComponent return horizontalOffset; } - + /** * Sets the horizontal offset of drawn lines. This can be used to * implement horizontal scrolling. @@ -427,7 +434,7 @@ public class JEditTextArea extends JComponent painter.repaint(); } - + /** * A fast way of changing both the first line and horizontal * offset. @@ -442,12 +449,12 @@ public class JEditTextArea extends JComponent this.horizontalOffset = horizontalOffset; changed = true; } - + if (firstLine != this.firstLine) { this.firstLine = firstLine; changed = true; } - + if (changed) { updateScrollBars(); painter.repaint(); @@ -455,7 +462,7 @@ public class JEditTextArea extends JComponent return changed; } - + /** * Ensures that the caret is visible by scrolling the text area if * necessary. @@ -471,7 +478,7 @@ public class JEditTextArea extends JComponent return scrollTo(line,offset); } - + /** * Ensures that the specified line and offset is visible by scrolling * the text area if necessary. @@ -516,7 +523,7 @@ public class JEditTextArea extends JComponent return setOrigin(newFirstLine,newHorizontalOffset); } - + /** * Converts a line index to a y co-ordinate. * @param line The line @@ -527,7 +534,7 @@ public class JEditTextArea extends JComponent - (fm.getLeading() + fm.getMaxDescent()); } - + /** * Converts a y co-ordinate to a line index. * @param y The y co-ordinate @@ -539,7 +546,7 @@ public class JEditTextArea extends JComponent y / height + firstLine)); } - + /** * Converts an offset in a line into an x co-ordinate. This is a * slow version that can be used any time. @@ -552,7 +559,7 @@ public class JEditTextArea extends JComponent return _offsetToX(line,offset); } - + /** * Converts an offset in a line into an x coordinate. This is a * fast version that should only be used if no changes were made @@ -575,9 +582,9 @@ public class JEditTextArea extends JComponent if (tokenMarker == null) { lineSegment.count = offset; return x + Utilities.getTabbedTextWidth(lineSegment, fm, x, painter, 0); - + } else { - // If syntax coloring is enabled, we have to do this + // If syntax coloring is enabled, we have to do this // because tokens can vary in width Token tokens; if (painter.currentLineIndex == line && painter.currentLineTokens != null) { @@ -587,7 +594,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 +606,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 +682,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; @@ -1395,30 +1405,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 +1430,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. @@ -1880,11 +1862,21 @@ public class JEditTextArea extends JComponent selection = selection.replaceAll("\t", tabString); } - // particularly on macosx when pasting from safari, - // replace unicode x00A0 (non-breaking space) - // with just a plain space. [fry 030929] + // Replace unicode x00A0 (non-breaking space) with just a plain space. + // Seen often on Mac OS X when pasting from Safari. [fry 030929] selection = selection.replace('\u00A0', ' '); + // Remove ASCII NUL characters. Reported when pasting from + // Acrobat Reader and PDF documents. [fry 130719] + // https://github.com/processing/processing/issues/1973 + if (selection.indexOf('\0') != -1) { + //System.out.println("found NUL charaacters"); + //int before = selection.length(); + selection = selection.replaceAll("\0", ""); + //int after = selection.length(); + //System.out.println(before + " " + after); + } + int repeatCount = inputHandler.getRepeatCount(); StringBuffer buf = new StringBuffer(); for (int i = 0; i < repeatCount; i++) { @@ -1973,7 +1965,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; @@ -2323,8 +2315,8 @@ public class JEditTextArea extends JComponent } } - - class DragHandler implements MouseMotionListener + + class DragHandler implements MouseMotionListener { public void mouseDragged(MouseEvent evt) { if (popup != null && popup.isVisible()) return; @@ -2359,28 +2351,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? @@ -2393,48 +2378,41 @@ 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()); + 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) { @@ -2445,8 +2423,17 @@ 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); @@ -2456,35 +2443,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); @@ -2495,11 +2479,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; @@ -2507,61 +2491,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 9b5ea6838..977d6f4b7 100644 --- a/app/src/processing/app/syntax/PdeKeywords.java +++ b/app/src/processing/app/syntax/PdeKeywords.java @@ -38,11 +38,11 @@ public class PdeKeywords extends TokenMarker { private int lastOffset; private int lastKeyword; - + /** - * Add a keyword, and the associated coloring. KEYWORD2 and KEYWORD3 - * should only be used with functions (where parens are present). + * Add a keyword, and the associated coloring. KEYWORD2 and KEYWORD3 + * should only be used with functions (where parens are present). * This is done for the extra paren handling. * @param coloring one of KEYWORD1, KEYWORD2, LITERAL1, etc. */ @@ -57,7 +57,7 @@ public class PdeKeywords extends TokenMarker { boolean paren = false; switch (coloring.charAt(0)) { case 'K': id = Token.KEYWORD1 + num; break; - case 'L': id = Token.LITERAL1 + num; break; + case 'L': id = Token.LITERAL1 + num; break; case 'F': id = Token.FUNCTION1 + num; paren = true; break; } keywordColoring.add(keyword, (byte) id, paren); @@ -137,7 +137,10 @@ public class PdeKeywords extends TokenMarker { lastOffset = lastKeyword = mlength; break loop; } - i++; // http://processing.org/bugs/bugzilla/609.html [jdf] + // https://github.com/processing/processing/issues/1681 + if (array[i1] != ' ') { + i++; // http://processing.org/bugs/bugzilla/609.html [jdf] + } } break; default: @@ -216,24 +219,24 @@ public class PdeKeywords extends TokenMarker { } return token; } - - + + private boolean doKeyword(Segment line, int i, char c) { // return doKeyword(line, i, false); // } -// -// +// +// // //private boolean doKeyword(Segment line, int i, char c) { // private boolean doKeyword(Segment line, int i, boolean paren) { int i1 = i + 1; int len = i - lastKeyword; - + boolean paren = Editor.checkParen(line.array, i, line.array.length); // String s = new String(line.array, lastKeyword, len); // if (s.equals("mousePressed")) { // System.out.println("found mousePressed" + (paren ? "()" : "")); // //new Exception().printStackTrace(System.out); -//// System.out.println(" " + i + " " + line.count + " " + +//// System.out.println(" " + i + " " + line.count + " " + //// //new String(line.array, line.offset + i, line.offset + line.count - i)); //// new String(line.array, i, line.array.length - i)); // } 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..d1679ddaa 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,41 @@ 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; } +// }; + - /** - * 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 +591,7 @@ implements TabExpander, Printable { public int getCurrentLineIndex() { return currentLineIndex; } + /** * Accessor used by tools that want to hook in and grab the formatting. @@ -528,6 +599,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 +608,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 +625,175 @@ 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 += w; + } - 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 += w; + } + //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 +806,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 +855,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 +874,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 +882,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 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 old mode 100755 new mode 100644 diff --git a/app/src/processing/app/tools/CreateFont.java b/app/src/processing/app/tools/CreateFont.java index 217af0bfa..17d6cbc73 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 461a80dba..c3133de1b 100644 --- a/app/src/processing/app/tools/InstallCommander.java +++ b/app/src/processing/app/tools/InstallCommander.java @@ -80,30 +80,28 @@ 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() + + " -cp \"" + classPath + "\"" + " processing.mode.java.Commander \"$@\""); writer.flush(); writer.close(); diff --git a/app/src/processing/app/tools/SerialFixer.java b/app/src/processing/app/tools/SerialFixer.java deleted file mode 100644 index 7065b53bc..000000000 --- a/app/src/processing/app/tools/SerialFixer.java +++ /dev/null @@ -1,89 +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 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.File; - -import javax.swing.JOptionPane; - -import processing.app.Base; -import processing.app.Editor; -import processing.core.PApplet; - - -public class SerialFixer implements Tool { - Editor editor; - - - public String getMenuTitle() { - return "Fix the Serial Library"; - } - - - public void init(Editor editor) { - this.editor = editor; - } - - - public void run() { - final String primary = - "Attempt to fix common serial port problems?"; - final String secondary = - "Click “OK” to perform additional installation steps to enable " + - "the Serial library. An administrator password will be required."; - - int result = - JOptionPane.showConfirmDialog(editor, - " " + - " " + - "" + primary + "" + - "

" + secondary + "

", - "Commander", - JOptionPane.OK_CANCEL_OPTION, - JOptionPane.QUESTION_MESSAGE); - - if (result == JOptionPane.OK_OPTION) { - String shellScript = "mkdir -p /var/lock && chmod 777 /var/lock"; - String appleScript = - "do shell script \"" + shellScript + "\" with administrator privileges"; - PApplet.exec(new String[] { "osascript", "-e", appleScript }); - } - editor.statusNotice("Finished."); - } - - - static public boolean isNeeded() { - if (Base.isMacOS()) { - File lockFolder = new File("/var/lock"); - if (!lockFolder.exists() || - !lockFolder.canRead() || - !lockFolder.canWrite() || - !lockFolder.canExecute()) { - return true; - } - } - return false; - } -} \ No newline at end of file diff --git a/app/src/processing/app/windows/Advapi32.java b/app/src/processing/app/windows/Advapi32.java deleted file mode 100644 index 716983ea3..000000000 --- a/app/src/processing/app/windows/Advapi32.java +++ /dev/null @@ -1,335 +0,0 @@ -package processing.app.windows; - -/* - * Advapi32.java - * - * Created on 6. August 2007, 11:24 - * - * To change this template, choose Tools | Template Manager - * and open the template in the editor. - */ - -import com.sun.jna.*; -import com.sun.jna.ptr.*; -import com.sun.jna.win32.*; - -/** - * - * @author TB - */ -public interface Advapi32 extends StdCallLibrary { - Advapi32 INSTANCE = (Advapi32) Native.loadLibrary("Advapi32", Advapi32.class, Options.UNICODE_OPTIONS); //$NON-NLS-1$ - -/* -BOOL WINAPI LookupAccountName( - LPCTSTR lpSystemName, - LPCTSTR lpAccountName, - PSID Sid, - LPDWORD cbSid, - LPTSTR ReferencedDomainName, - LPDWORD cchReferencedDomainName, - PSID_NAME_USE peUse -);*/ - public boolean LookupAccountName(String lpSystemName, String lpAccountName, - byte[] Sid, IntByReference cbSid, char[] ReferencedDomainName, - IntByReference cchReferencedDomainName, PointerByReference peUse); - -/* -BOOL WINAPI LookupAccountSid( - LPCTSTR lpSystemName, - PSID lpSid, - LPTSTR lpName, - LPDWORD cchName, - LPTSTR lpReferencedDomainName, - LPDWORD cchReferencedDomainName, - PSID_NAME_USE peUse -);*/ - public boolean LookupAccountSid(String lpSystemName, byte[] Sid, - char[] lpName, IntByReference cchName, char[] ReferencedDomainName, - IntByReference cchReferencedDomainName, PointerByReference peUse); - -/* -BOOL ConvertSidToStringSid( - PSID Sid, - LPTSTR* StringSid -);*/ - public boolean ConvertSidToStringSid(byte[] Sid, PointerByReference StringSid); - -/* -BOOL WINAPI ConvertStringSidToSid( - LPCTSTR StringSid, - PSID* Sid -);*/ - public boolean ConvertStringSidToSid(String StringSid, PointerByReference Sid); - -/* -SC_HANDLE WINAPI OpenSCManager( - LPCTSTR lpMachineName, - LPCTSTR lpDatabaseName, - DWORD dwDesiredAccess -);*/ - public Pointer OpenSCManager(String lpMachineName, WString lpDatabaseName, int dwDesiredAccess); - -/* -BOOL WINAPI CloseServiceHandle( - SC_HANDLE hSCObject -);*/ - public boolean CloseServiceHandle(Pointer hSCObject); - -/* -SC_HANDLE WINAPI OpenService( - SC_HANDLE hSCManager, - LPCTSTR lpServiceName, - DWORD dwDesiredAccess -);*/ - public Pointer OpenService(Pointer hSCManager, String lpServiceName, int dwDesiredAccess); - -/* -BOOL WINAPI StartService( - SC_HANDLE hService, - DWORD dwNumServiceArgs, - LPCTSTR* lpServiceArgVectors -);*/ - public boolean StartService(Pointer hService, int dwNumServiceArgs, char[] lpServiceArgVectors); - -/* -BOOL WINAPI ControlService( - SC_HANDLE hService, - DWORD dwControl, - LPSERVICE_STATUS lpServiceStatus -);*/ - public boolean ControlService(Pointer hService, int dwControl, SERVICE_STATUS lpServiceStatus); - -/* -BOOL WINAPI StartServiceCtrlDispatcher( - const SERVICE_TABLE_ENTRY* lpServiceTable -);*/ - public boolean StartServiceCtrlDispatcher(Structure[] lpServiceTable); - -/* -SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandler( - LPCTSTR lpServiceName, - LPHANDLER_FUNCTION lpHandlerProc -);*/ - public Pointer RegisterServiceCtrlHandler(String lpServiceName, Handler lpHandlerProc); - -/* -SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerEx( - LPCTSTR lpServiceName, - LPHANDLER_FUNCTION_EX lpHandlerProc, - LPVOID lpContext -);*/ - public Pointer RegisterServiceCtrlHandlerEx(String lpServiceName, HandlerEx lpHandlerProc, Pointer lpContext); - -/* -BOOL WINAPI SetServiceStatus( - SERVICE_STATUS_HANDLE hServiceStatus, - LPSERVICE_STATUS lpServiceStatus -);*/ - public boolean SetServiceStatus(Pointer hServiceStatus, SERVICE_STATUS lpServiceStatus); - -/* -SC_HANDLE WINAPI CreateService( - SC_HANDLE hSCManager, - LPCTSTR lpServiceName, - LPCTSTR lpDisplayName, - DWORD dwDesiredAccess, - DWORD dwServiceType, - DWORD dwStartType, - DWORD dwErrorControl, - LPCTSTR lpBinaryPathName, - LPCTSTR lpLoadOrderGroup, - LPDWORD lpdwTagId, - LPCTSTR lpDependencies, - LPCTSTR lpServiceStartName, - LPCTSTR lpPassword -);*/ - public Pointer CreateService(Pointer hSCManager, String lpServiceName, String lpDisplayName, - int dwDesiredAccess, int dwServiceType, int dwStartType, int dwErrorControl, - String lpBinaryPathName, String lpLoadOrderGroup, IntByReference lpdwTagId, - String lpDependencies, String lpServiceStartName, String lpPassword); - -/* -BOOL WINAPI DeleteService( - SC_HANDLE hService -);*/ - public boolean DeleteService(Pointer hService); - -/* -BOOL WINAPI ChangeServiceConfig2( - SC_HANDLE hService, - DWORD dwInfoLevel, - LPVOID lpInfo -);*/ - public boolean ChangeServiceConfig2(Pointer hService, int dwInfoLevel, ChangeServiceConfig2Info lpInfo); - -/* -LONG WINAPI RegOpenKeyEx( - HKEY hKey, - LPCTSTR lpSubKey, - DWORD ulOptions, - REGSAM samDesired, - PHKEY phkResult -);*/ - public int RegOpenKeyEx(int hKey, String lpSubKey, int ulOptions, int samDesired, IntByReference phkResult); - -/* -LONG WINAPI RegQueryValueEx( - HKEY hKey, - LPCTSTR lpValueName, - LPDWORD lpReserved, - LPDWORD lpType, - LPBYTE lpData, - LPDWORD lpcbData -);*/ - public int RegQueryValueEx(int hKey, String lpValueName, IntByReference lpReserved, IntByReference lpType, byte[] lpData, IntByReference lpcbData); - -/* -LONG WINAPI RegCloseKey( - HKEY hKey -);*/ - public int RegCloseKey(int hKey); - -/* -LONG WINAPI RegDeleteValue( - HKEY hKey, - LPCTSTR lpValueName -);*/ - public int RegDeleteValue(int hKey, String lpValueName); - -/* -LONG WINAPI RegSetValueEx( - HKEY hKey, - LPCTSTR lpValueName, - DWORD Reserved, - DWORD dwType, - const BYTE* lpData, - DWORD cbData -);*/ - public int RegSetValueEx(int hKey, String lpValueName, int Reserved, int dwType, byte[] lpData, int cbData); - -/* -LONG WINAPI RegCreateKeyEx( - HKEY hKey, - LPCTSTR lpSubKey, - DWORD Reserved, - LPTSTR lpClass, - DWORD dwOptions, - REGSAM samDesired, - LPSECURITY_ATTRIBUTES lpSecurityAttributes, - PHKEY phkResult, - LPDWORD lpdwDisposition -);*/ - public int RegCreateKeyEx(int hKey, String lpSubKey, int Reserved, String lpClass, int dwOptions, - int samDesired, WINBASE.SECURITY_ATTRIBUTES lpSecurityAttributes, IntByReference phkResult, - IntByReference lpdwDisposition); - -/* -LONG WINAPI RegDeleteKey( - HKEY hKey, - LPCTSTR lpSubKey -);*/ - public int RegDeleteKey(int hKey, String name); - -/* -LONG WINAPI RegEnumKeyEx( - HKEY hKey, - DWORD dwIndex, - LPTSTR lpName, - LPDWORD lpcName, - LPDWORD lpReserved, - LPTSTR lpClass, - LPDWORD lpcClass, - PFILETIME lpftLastWriteTime -);*/ - public int RegEnumKeyEx(int hKey, int dwIndex, char[] lpName, IntByReference lpcName, IntByReference reserved, - char[] lpClass, IntByReference lpcClass, WINBASE.FILETIME lpftLastWriteTime); - -/* -LONG WINAPI RegEnumValue( - HKEY hKey, - DWORD dwIndex, - LPTSTR lpValueName, - LPDWORD lpcchValueName, - LPDWORD lpReserved, - LPDWORD lpType, - LPBYTE lpData, - LPDWORD lpcbData -);*/ - public int RegEnumValue(int hKey, int dwIndex, char[] lpValueName, IntByReference lpcchValueName, IntByReference reserved, - IntByReference lpType, byte[] lpData, IntByReference lpcbData); - - interface SERVICE_MAIN_FUNCTION extends StdCallCallback { - /* - VOID WINAPI ServiceMain( - DWORD dwArgc, - LPTSTR* lpszArgv - );*/ - public void callback(int dwArgc, Pointer lpszArgv); - } - - interface Handler extends StdCallCallback { - /* - VOID WINAPI Handler( - DWORD fdwControl - );*/ - public void callback(int fdwControl); - } - - interface HandlerEx extends StdCallCallback { - /* - DWORD WINAPI HandlerEx( - DWORD dwControl, - DWORD dwEventType, - LPVOID lpEventData, - LPVOID lpContext - );*/ - public void callback(int dwControl, int dwEventType, Pointer lpEventData, Pointer lpContext); - } - -/* -typedef struct _SERVICE_STATUS { - DWORD dwServiceType; - DWORD dwCurrentState; - DWORD dwControlsAccepted; - DWORD dwWin32ExitCode; - DWORD dwServiceSpecificExitCode; - DWORD dwCheckPoint; - DWORD dwWaitHint; -} SERVICE_STATUS, - *LPSERVICE_STATUS;*/ - public static class SERVICE_STATUS extends Structure { - public int dwServiceType; - public int dwCurrentState; - public int dwControlsAccepted; - public int dwWin32ExitCode; - public int dwServiceSpecificExitCode; - public int dwCheckPoint; - public int dwWaitHint; - } - -/* -typedef struct _SERVICE_TABLE_ENTRY { - LPTSTR lpServiceName; - LPSERVICE_MAIN_FUNCTION lpServiceProc; -} SERVICE_TABLE_ENTRY, - *LPSERVICE_TABLE_ENTRY;*/ - public static class SERVICE_TABLE_ENTRY extends Structure { - public String lpServiceName; - public SERVICE_MAIN_FUNCTION lpServiceProc; - } - - public static class ChangeServiceConfig2Info extends Structure { - } - -/* - typedef struct _SERVICE_DESCRIPTION { - LPTSTR lpDescription; -} SERVICE_DESCRIPTION, - *LPSERVICE_DESCRIPTION;*/ - public static class SERVICE_DESCRIPTION extends ChangeServiceConfig2Info { - public String lpDescription; - } -} - - diff --git a/app/src/processing/app/windows/Options.java b/app/src/processing/app/windows/Options.java deleted file mode 100644 index f5cff2888..000000000 --- a/app/src/processing/app/windows/Options.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Options.java - * - * Created on 8. August 2007, 17:07 - * - * To change this template, choose Tools | Template Manager - * and open the template in the editor. - */ - -package processing.app.windows; - -import static com.sun.jna.Library.*; -import com.sun.jna.win32.*; -import java.util.*; - -/** - * - * @author TB - */ -public interface Options { - Map UNICODE_OPTIONS = new HashMap() { - { - put(OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE); - put(OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE); - } - }; -} diff --git a/app/src/processing/app/windows/Platform.java b/app/src/processing/app/windows/Platform.java deleted file mode 100644 index a6c228f17..000000000 --- a/app/src/processing/app/windows/Platform.java +++ /dev/null @@ -1,444 +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 - Copyright (c) 2008-2012 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 processing.app.windows; - -import java.io.File; -import java.io.UnsupportedEncodingException; -import java.util.HashMap; -import java.util.Map; - -import com.sun.jna.Library; -import com.sun.jna.Native; -import com.sun.jna.NativeMapped; -import com.sun.jna.PointerType; -import com.sun.jna.win32.W32APIFunctionMapper; -import com.sun.jna.win32.W32APITypeMapper; - -import processing.app.Base; -import processing.app.Preferences; -import processing.app.windows.Registry.REGISTRY_ROOT_KEY; -import processing.core.PApplet; - - -public class Platform extends processing.app.Platform { - - static final String openCommand = - System.getProperty("user.dir").replace('/', '\\') + - "\\processing.exe \"%1\""; - static final String DOC = "Processing.Document"; -// static final String DOC = "Processing.exe"; - - - public void init(Base base) { - super.init(base); - checkAssociations(); - //checkQuickTime(); - checkPath(); - - //findJDK(); - /* - new Thread(new Runnable() { - public void run() { - try { - Thread.sleep(2000); - } catch (InterruptedException ie) { } - findJDK(); - } - }).start(); - */ - } - - -// HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit\CurrentVersion -> 1.6 (String) -// HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit\CurrentVersion\1.6\JavaHome -> c:\jdk-1.6.0_05 - - /* - static public void findJDK() { - try { - String jcpo = System.getProperty("java.home"); - String jv = System.getProperty("java.version"); - System.out.println("home and version = " + jcpo + " and " + jv); - - // the last parameter will be anything appearing on the right-hand - // side of regedit. - final String JDK_KEY = "SOFTWARE\\JavaSoft\\Java Development Kit"; - String currentVersion = - Registry.getStringValue(REGISTRY_ROOT_KEY.LOCAL_MACHINE, - JDK_KEY, - "CurrentVersion"); - System.out.println("current version is " + currentVersion); - if (currentVersion != null) { - String javaHome = - Registry.getStringValue(REGISTRY_ROOT_KEY.LOCAL_MACHINE, - JDK_KEY + "\\" + currentVersion, - "JavaHome"); - System.out.println("home is where the " + javaHome + " is"); - if (javaHome != null) { - String jcp = System.getProperty("java.class.path"); - String toolsJar = javaHome + "\\lib\\tools.jar"; - System.setProperty("java.class.path", - jcp + File.pathSeparator + toolsJar); - System.out.println("set jcp to " + - System.getProperty("java.class.path")); - } - } - } catch (UnsupportedEncodingException uee) { - uee.printStackTrace(); - } - } - */ - - - /** - * Make sure that .pde files are associated with processing.exe. - */ - protected void checkAssociations() { -// HKEY_CLASSES_ROOT -// MyProgram.exe -// shell -// open -// command -// (Default) = C:\MyDir\MyProgram.exe "%1" - - try { - String knownCommand = - Registry.getStringValue(REGISTRY_ROOT_KEY.CLASSES_ROOT, - DOC + "\\shell\\open\\command", ""); - if (knownCommand == null) { - if (Preferences.getBoolean("platform.auto_file_type_associations")) { - setAssociations(); - } - - } else if (!knownCommand.equals(openCommand)) { - // If the value is set differently, just change the registry setting. - if (Preferences.getBoolean("platform.auto_file_type_associations")) { - setAssociations(); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - - /** - * Associate .pde files with this version of Processing. - */ - protected void setAssociations() throws UnsupportedEncodingException { - if (Registry.createKey(REGISTRY_ROOT_KEY.CLASSES_ROOT, - "", ".pde") && - Registry.setStringValue(REGISTRY_ROOT_KEY.CLASSES_ROOT, - ".pde", "", DOC) && - - Registry.createKey(REGISTRY_ROOT_KEY.CLASSES_ROOT, "", DOC) && - Registry.setStringValue(REGISTRY_ROOT_KEY.CLASSES_ROOT, DOC, "", - "Processing Source Code") && - - Registry.createKey(REGISTRY_ROOT_KEY.CLASSES_ROOT, - DOC, "shell") && - Registry.createKey(REGISTRY_ROOT_KEY.CLASSES_ROOT, - DOC + "\\shell", "open") && - Registry.createKey(REGISTRY_ROOT_KEY.CLASSES_ROOT, - DOC + "\\shell\\open", "command") && - Registry.setStringValue(REGISTRY_ROOT_KEY.CLASSES_ROOT, - DOC + "\\shell\\open\\command", "", - openCommand)) { - // everything ok - // hooray! - - } else { - Preferences.setBoolean("platform.auto_file_type_associations", false); - } - } - - - /** - * Find QuickTime for Java installation. - */ -// protected void checkQuickTime() { -// // http://developer.apple.com/documentation/QuickTime/Conceptual/QT7Win_Update_Guide/Chapter03/chapter_3_section_1.html -// // HKEY_LOCAL_MACHINE\SOFTWARE\Apple Computer, Inc.\QuickTime\QTSysDir -// try { -// String qtsystemPath = -// Registry.getStringValue(REGISTRY_ROOT_KEY.LOCAL_MACHINE, -// "Software\\Apple Computer, Inc.\\QuickTime", -// "QTSysDir"); -// // Could show a warning message here if QT not installed, but that -// // would annoy people who don't want anything to do with QuickTime. -// if (qtsystemPath != null) { -// File qtjavaZip = new File(qtsystemPath, "QTJava.zip"); -// if (qtjavaZip.exists()) { -// String qtjavaZipPath = qtjavaZip.getAbsolutePath(); -// String cp = System.getProperty("java.class.path"); -// System.setProperty("java.class.path", -// cp + File.pathSeparator + qtjavaZipPath); -// } -// } -// } catch (UnsupportedEncodingException e) { -// e.printStackTrace(); -// } -// } - - - /** - * Remove extra quotes, slashes, and garbage from the Windows PATH. - */ - protected void checkPath() { - String path = System.getProperty("java.library.path"); - String[] pieces = PApplet.split(path, File.pathSeparatorChar); - String[] legit = new String[pieces.length]; - int legitCount = 0; - for (String item : pieces) { - if (item.startsWith("\"")) { - item = item.substring(1); - } - if (item.endsWith("\"")) { - item = item.substring(0, item.length() - 1); - } - if (item.endsWith(File.separator)) { - item = item.substring(0, item.length() - File.separator.length()); - } - File directory = new File(item); - if (!directory.exists()) { - continue; - } - if (item.trim().length() == 0) { - continue; - } - legit[legitCount++] = item; - } - legit = PApplet.subset(legit, 0, legitCount); - String newPath = PApplet.join(legit, File.pathSeparator); - if (!newPath.equals(path)) { - System.setProperty("java.library.path", newPath); - } - } - - - // looking for Documents and Settings/blah/Application Data/Processing - public File getSettingsFolder() throws Exception { - // HKEY_CURRENT_USER\Software\Microsoft - // \Windows\CurrentVersion\Explorer\Shell Folders - // Value Name: AppData - // Value Type: REG_SZ - // Value Data: path - - //String keyPath = - // "Software\\Microsoft\\Windows\\CurrentVersion" + - // "\\Explorer\\Shell Folders"; - //String appDataPath = - // Registry.getStringValue(REGISTRY_ROOT_KEY.CURRENT_USER, keyPath, "AppData"); - - // Fix for Issue 410 - // Java 1.6 doesn't provide a good workaround (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6519127) - // Using JNA and SHGetFolderPath instead. - - char[] pszPath = new char[Shell32.MAX_PATH]; // this will be contain the path if SHGetFolderPath is successful - int hResult = Shell32.INSTANCE.SHGetFolderPath(null, Shell32.CSIDL_APPDATA, null, Shell32.SHGFP_TYPE_CURRENT, pszPath); - - if (Shell32.S_OK != hResult){ - throw new Exception("Problem city, population your computer"); - } - - String appDataPath = new String(pszPath); - int len = appDataPath.indexOf("\0"); - appDataPath = appDataPath.substring(0, len); - - // DEBUG - //throw new Exception("win: " + appDataPath); - return new File(appDataPath, "Processing"); - } - - - // looking for Documents and Settings/blah/My Documents/Processing - public File getDefaultSketchbookFolder() throws Exception { - - // http://support.microsoft.com/?kbid=221837&sd=RMVP - // http://support.microsoft.com/kb/242557/en-us - - // The path to the My Documents folder is stored in the following - // registry key, where path is the complete path to your storage location - - // HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders - // Value Name: Personal - // Value Type: REG_SZ - // Value Data: path - - // in some instances, this may be overridden by a policy, in which case check: - // HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders - - //String keyPath = - // "Software\\Microsoft\\Windows\\CurrentVersion" + - // "\\Explorer\\Shell Folders"; - //String personalPath = - // Registry.getStringValue(REGISTRY_ROOT_KEY.CURRENT_USER, keyPath, "Personal"); - - // "The "Shell Folders" key exists solely to permit four programs written - // in 1994 to continue running on the RTM version of Windows 95." -- Raymond Chen, MSDN - - char[] pszPath = new char[Shell32.MAX_PATH]; // this will be contain the path if SHGetFolderPath is successful - int hResult = Shell32.INSTANCE.SHGetFolderPath(null, Shell32.CSIDL_PERSONAL, null, Shell32.SHGFP_TYPE_CURRENT, pszPath); - - if (Shell32.S_OK != hResult){ - throw new Exception("Problem city, population your computer"); - } - - String personalPath = new String(pszPath); - int len = personalPath.indexOf("\0"); - personalPath = personalPath.substring(0, len); - - // DEBUG - //throw new Exception("win: " + personalPath); - return new File(personalPath, "Processing"); - } - - - /* - public void openURL(String url) throws Exception { - // this is not guaranteed to work, because who knows if the - // path will always be c:\progra~1 et al. also if the user has - // a different browser set as their default (which would - // include me) it'd be annoying to be dropped into ie. - //Runtime.getRuntime().exec("c:\\progra~1\\intern~1\\iexplore " - // + currentDir - - // the following uses a shell execute to launch the .html file - // note that under cygwin, the .html files have to be chmodded +x - // after they're unpacked from the zip file. i don't know why, - // and don't understand what this does in terms of windows - // permissions. without the chmod, the command prompt says - // "Access is denied" in both cygwin and the "dos" prompt. - //Runtime.getRuntime().exec("cmd /c " + currentDir + "\\reference\\" + - // referenceFile + ".html"); - if (url.startsWith("http://")) { - // open dos prompt, give it 'start' command, which will - // open the url properly. start by itself won't work since - // it appears to need cmd - Runtime.getRuntime().exec("cmd /c start " + url); - } else { - // just launching the .html file via the shell works - // but make sure to chmod +x the .html files first - // also place quotes around it in case there's a space - // in the user.dir part of the url - Runtime.getRuntime().exec("cmd /c \"" + url + "\""); - } - } - - - public boolean openFolderAvailable() { - return true; - } - - - public void openFolder(File file) throws Exception { - String folder = file.getAbsolutePath(); - - // doesn't work - //Runtime.getRuntime().exec("cmd /c \"" + folder + "\""); - - // works fine on winxp, prolly win2k as well - Runtime.getRuntime().exec("explorer \"" + folder + "\""); - - // not tested - //Runtime.getRuntime().exec("start explorer \"" + folder + "\""); - } - */ - - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - - // Code partially thanks to Richard Quirk from: - // http://quirkygba.blogspot.com/2009/11/setting-environment-variables-in-java.html - - static WinLibC clib = (WinLibC) Native.loadLibrary("msvcrt", WinLibC.class); - - public interface WinLibC extends Library { - //WinLibC INSTANCE = (WinLibC) Native.loadLibrary("msvcrt", WinLibC.class); - //libc = Native.loadLibrary("msvcrt", WinLibC.class); - public int _putenv(String name); - } - - - public void setenv(String variable, String value) { - //WinLibC clib = WinLibC.INSTANCE; - clib._putenv(variable + "=" + value); - } - - - public String getenv(String variable) { - return System.getenv(variable); - } - - - public int unsetenv(String variable) { - //WinLibC clib = WinLibC.INSTANCE; - //clib._putenv(variable + "="); - //return 0; - return clib._putenv(variable + "="); - } - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - // JNA code for using SHGetFolderPath to fix Issue 410 - // Based on answer provided by McDowell at - // http://stackoverflow.com/questions/585534/ - // what-is-the-best-way-to-find-the-users-home-directory-in-java/586917#586917 - - private static Map OPTIONS = new HashMap(); - - static{ - OPTIONS.put(Library.OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE); - OPTIONS.put(Library.OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE); - } - - static class HANDLE extends PointerType implements NativeMapped{} - - static class HWND extends HANDLE {} - - public interface Shell32 extends Library{ - - public static final int MAX_PATH = 260; - public static final int SHGFP_TYPE_CURRENT = 0; - public static final int SHGFP_TYPE_DEFAULT = 1; - public static final int S_OK = 0; - - // KNOWNFOLDERIDs are preferred to CSDIL values - // but Windows XP only supports CSDIL so thats what we have to use - public static final int CSIDL_APPDATA = 0x001a; // "Application Data" - public static final int CSIDL_PERSONAL = 0x0005; // "My Documents" - - static Shell32 INSTANCE = (Shell32) Native.loadLibrary("shell32", Shell32.class, OPTIONS); - - /** - * see http://msdn.microsoft.com/en-us/library/bb762181(VS.85).aspx - * - * HRESULT SHGetFolderPath( HWND hwndOwner, int nFolder, HANDLE hToken, - * DWORD dwFlags, LPTSTR pszPath); - */ - public int SHGetFolderPath(HWND hwndOwner, int nFolder, HANDLE hToken, - int dwFlags, char[] pszPath); - } - -} diff --git a/app/src/processing/app/windows/Registry.java b/app/src/processing/app/windows/Registry.java deleted file mode 100644 index 06d0e0ed5..000000000 --- a/app/src/processing/app/windows/Registry.java +++ /dev/null @@ -1,456 +0,0 @@ -package processing.app.windows; - -import java.io.UnsupportedEncodingException; -import java.util.HashMap; -import java.util.TreeMap; -import java.util.TreeSet; - -import com.sun.jna.ptr.IntByReference; - -/** - * Methods for accessing the Windows Registry. Only String and DWORD values supported at the moment. - */ -public class Registry { - public static enum REGISTRY_ROOT_KEY{CLASSES_ROOT, CURRENT_USER, LOCAL_MACHINE, USERS}; - private final static HashMap rootKeyMap = new HashMap(); - - static { - rootKeyMap.put(REGISTRY_ROOT_KEY.CLASSES_ROOT, WINREG.HKEY_CLASSES_ROOT); - rootKeyMap.put(REGISTRY_ROOT_KEY.CURRENT_USER, WINREG.HKEY_CURRENT_USER); - rootKeyMap.put(REGISTRY_ROOT_KEY.LOCAL_MACHINE, WINREG.HKEY_LOCAL_MACHINE); - rootKeyMap.put(REGISTRY_ROOT_KEY.USERS, WINREG.HKEY_USERS); - } - - /** - * Testing. - * - * @param args arguments - * @throws java.lang.Exception on error - */ - public static void main(String[] args) throws Exception { - } - - /** - * Gets one of the root keys. - * - * @param key key type - * @return root key - */ - private static int getRegistryRootKey(REGISTRY_ROOT_KEY key) { - Advapi32 advapi32; - IntByReference pHandle; - int handle = 0; - - advapi32 = Advapi32.INSTANCE; - pHandle = new IntByReference(); - - if(advapi32.RegOpenKeyEx(rootKeyMap.get(key), null, 0, 0, pHandle) == WINERROR.ERROR_SUCCESS) { - handle = pHandle.getValue(); - } - return(handle); - } - - /** - * Opens a key. - * - * @param rootKey root key - * @param subKeyName name of the key - * @param access access mode - * @return handle to the key or 0 - */ - private static int openKey(REGISTRY_ROOT_KEY rootKey, String subKeyName, int access) { - Advapi32 advapi32; - IntByReference pHandle; - int rootKeyHandle; - - advapi32 = Advapi32.INSTANCE; - rootKeyHandle = getRegistryRootKey(rootKey); - pHandle = new IntByReference(); - - if(advapi32.RegOpenKeyEx(rootKeyHandle, subKeyName, 0, access, pHandle) == WINERROR.ERROR_SUCCESS) { - return(pHandle.getValue()); - - } else { - return(0); - } - } - - /** - * Converts a Windows buffer to a Java String. - * - * @param buf buffer - * @throws java.io.UnsupportedEncodingException on error - * @return String - */ - private static String convertBufferToString(byte[] buf) throws UnsupportedEncodingException { - return(new String(buf, 0, buf.length - 2, "UTF-16LE")); - } - - /** - * Converts a Windows buffer to an int. - * - * @param buf buffer - * @return int - */ - private static int convertBufferToInt(byte[] buf) { - return((buf[0] & 0xff) + ((buf[1] & 0xff) << 8) + ((buf[2] & 0xff) << 16) + ((buf[3] & 0xff) << 24)); - } - - /** - * Read a String value. - * - * @param rootKey root key - * @param subKeyName key name - * @param name value name - * @throws java.io.UnsupportedEncodingException on error - * @return String or null - */ - public static String getStringValue(REGISTRY_ROOT_KEY rootKey, String subKeyName, String name) throws UnsupportedEncodingException { - Advapi32 advapi32; - IntByReference pType, lpcbData; - byte[] lpData = new byte[1]; - int handle = 0; - String ret = null; - - advapi32 = Advapi32.INSTANCE; - pType = new IntByReference(); - lpcbData = new IntByReference(); - handle = openKey(rootKey, subKeyName, WINNT.KEY_READ); - - if(handle != 0) { - - if(advapi32.RegQueryValueEx(handle, name, null, pType, lpData, lpcbData) == WINERROR.ERROR_MORE_DATA) { - lpData = new byte[lpcbData.getValue()]; - - if(advapi32.RegQueryValueEx(handle, name, null, pType, lpData, lpcbData) == WINERROR.ERROR_SUCCESS) { - ret = convertBufferToString(lpData); - } - } - advapi32.RegCloseKey(handle); - } - return(ret); - } - - /** - * Read an int value. - * - * - * @return int or 0 - * @param rootKey root key - * @param subKeyName key name - * @param name value name - */ - public static int getIntValue(REGISTRY_ROOT_KEY rootKey, String subKeyName, String name) { - Advapi32 advapi32; - IntByReference pType, lpcbData; - byte[] lpData = new byte[1]; - int handle = 0; - int ret = 0; - - advapi32 = Advapi32.INSTANCE; - pType = new IntByReference(); - lpcbData = new IntByReference(); - handle = openKey(rootKey, subKeyName, WINNT.KEY_READ); - - if(handle != 0) { - - if(advapi32.RegQueryValueEx(handle, name, null, pType, lpData, lpcbData) == WINERROR.ERROR_MORE_DATA) { - lpData = new byte[lpcbData.getValue()]; - - if(advapi32.RegQueryValueEx(handle, name, null, pType, lpData, lpcbData) == WINERROR.ERROR_SUCCESS) { - ret = convertBufferToInt(lpData); - } - } - advapi32.RegCloseKey(handle); - } - return(ret); - } - - /** - * Delete a value. - * - * @param rootKey root key - * @param subKeyName key name - * @param name value name - * @return true on success - */ - public static boolean deleteValue(REGISTRY_ROOT_KEY rootKey, String subKeyName, String name) { - Advapi32 advapi32; - int handle; - boolean ret = true; - - advapi32 = Advapi32.INSTANCE; - - handle = openKey(rootKey, subKeyName, WINNT.KEY_READ | WINNT.KEY_WRITE); - - if(handle != 0) { - if(advapi32.RegDeleteValue(handle, name) == WINERROR.ERROR_SUCCESS) { - ret = true; - } - advapi32.RegCloseKey(handle); - } - return(ret); - } - - /** - * Writes a String value. - * - * @param rootKey root key - * @param subKeyName key name - * @param name value name - * @param value value - * @throws java.io.UnsupportedEncodingException on error - * @return true on success - */ - public static boolean setStringValue(REGISTRY_ROOT_KEY rootKey, String subKeyName, String name, String value) throws UnsupportedEncodingException { - Advapi32 advapi32; - int handle; - byte[] data; - boolean ret = false; - - // appears to be Java 1.6 syntax, removing [fry] - //data = Arrays.copyOf(value.getBytes("UTF-16LE"), value.length() * 2 + 2); - data = new byte[value.length() * 2 + 2]; - byte[] src = value.getBytes("UTF-16LE"); - System.arraycopy(src, 0, data, 0, src.length); - - advapi32 = Advapi32.INSTANCE; - handle = openKey(rootKey, subKeyName, WINNT.KEY_READ | WINNT.KEY_WRITE); - - if(handle != 0) { - if(advapi32.RegSetValueEx(handle, name, 0, WINNT.REG_SZ, data, data.length) == WINERROR.ERROR_SUCCESS) { - ret = true; - } - advapi32.RegCloseKey(handle); - } - return(ret); - } - - /** - * Writes an int value. - * - * - * @return true on success - * @param rootKey root key - * @param subKeyName key name - * @param name value name - * @param value value - */ - public static boolean setIntValue(REGISTRY_ROOT_KEY rootKey, String subKeyName, String name, int value) { - Advapi32 advapi32; - int handle; - byte[] data; - boolean ret = false; - - data = new byte[4]; - data[0] = (byte)(value & 0xff); - data[1] = (byte)((value >> 8) & 0xff); - data[2] = (byte)((value >> 16) & 0xff); - data[3] = (byte)((value >> 24) & 0xff); - advapi32 = Advapi32.INSTANCE; - handle = openKey(rootKey, subKeyName, WINNT.KEY_READ | WINNT.KEY_WRITE); - - if(handle != 0) { - - if(advapi32.RegSetValueEx(handle, name, 0, WINNT.REG_DWORD, data, data.length) == WINERROR.ERROR_SUCCESS) { - ret = true; - } - advapi32.RegCloseKey(handle); - } - return(ret); - } - - /** - * Check for existence of a value. - * - * @param rootKey root key - * @param subKeyName key name - * @param name value name - * @return true if exists - */ - public static boolean valueExists(REGISTRY_ROOT_KEY rootKey, String subKeyName, String name) { - Advapi32 advapi32; - IntByReference pType, lpcbData; - byte[] lpData = new byte[1]; - int handle = 0; - boolean ret = false; - - advapi32 = Advapi32.INSTANCE; - pType = new IntByReference(); - lpcbData = new IntByReference(); - handle = openKey(rootKey, subKeyName, WINNT.KEY_READ); - - if(handle != 0) { - - if(advapi32.RegQueryValueEx(handle, name, null, pType, lpData, lpcbData) != WINERROR.ERROR_FILE_NOT_FOUND) { - ret = true; - - } else { - ret = false; - } - advapi32.RegCloseKey(handle); - } - return(ret); - } - - /** - * Create a new key. - * - * @param rootKey root key - * @param parent name of parent key - * @param name key name - * @return true on success - */ - public static boolean createKey(REGISTRY_ROOT_KEY rootKey, String parent, String name) { - Advapi32 advapi32; - IntByReference hkResult, dwDisposition; - int handle = 0; - boolean ret = false; - - advapi32 = Advapi32.INSTANCE; - hkResult = new IntByReference(); - dwDisposition = new IntByReference(); - handle = openKey(rootKey, parent, WINNT.KEY_READ); - - if(handle != 0) { - - if(advapi32.RegCreateKeyEx(handle, name, 0, null, WINNT.REG_OPTION_NON_VOLATILE, WINNT.KEY_READ, null, - hkResult, dwDisposition) == WINERROR.ERROR_SUCCESS) { - ret = true; - advapi32.RegCloseKey(hkResult.getValue()); - - } else { - ret = false; - } - advapi32.RegCloseKey(handle); - } - return(ret); - } - - /** - * Delete a key. - * - * @param rootKey root key - * @param parent name of parent key - * @param name key name - * @return true on success - */ - public static boolean deleteKey(REGISTRY_ROOT_KEY rootKey, String parent, String name) { - Advapi32 advapi32; - int handle = 0; - boolean ret = false; - - advapi32 = Advapi32.INSTANCE; - handle = openKey(rootKey, parent, WINNT.KEY_READ); - - if(handle != 0) { - - if(advapi32.RegDeleteKey(handle, name) == WINERROR.ERROR_SUCCESS) { - ret = true; - - } else { - ret = false; - } - advapi32.RegCloseKey(handle); - } - return(ret); - } - - /** - * Get all sub keys of a key. - * - * @param rootKey root key - * @param parent key name - * @return array with all sub key names - */ - public static String[] getSubKeys(REGISTRY_ROOT_KEY rootKey, String parent) { - Advapi32 advapi32; - int handle = 0, dwIndex; - char[] lpName; - IntByReference lpcName; - WINBASE.FILETIME lpftLastWriteTime; - TreeSet subKeys = new TreeSet(); - - advapi32 = Advapi32.INSTANCE; - handle = openKey(rootKey, parent, WINNT.KEY_READ); - lpName = new char[256]; - lpcName = new IntByReference(256); - lpftLastWriteTime = new WINBASE.FILETIME(); - - if(handle != 0) { - dwIndex = 0; - - while(advapi32.RegEnumKeyEx(handle, dwIndex, lpName, lpcName, null, - null, null, lpftLastWriteTime) == WINERROR.ERROR_SUCCESS) { - subKeys.add(new String(lpName, 0, lpcName.getValue())); - lpcName.setValue(256); - dwIndex++; - } - advapi32.RegCloseKey(handle); - } - - return(subKeys.toArray(new String[]{})); - } - - /** - * Get all values under a key. - * - * @param rootKey root key - * @param key jey name - * @throws java.io.UnsupportedEncodingException on error - * @return TreeMap with name and value pairs - */ - public static TreeMap getValues(REGISTRY_ROOT_KEY rootKey, String key) throws UnsupportedEncodingException { - Advapi32 advapi32; - int handle = 0, dwIndex, result = 0; - char[] lpValueName; - byte[] lpData; - IntByReference lpcchValueName, lpType, lpcbData; - String name; - TreeMap values = new TreeMap(String.CASE_INSENSITIVE_ORDER); - - advapi32 = Advapi32.INSTANCE; - handle = openKey(rootKey, key, WINNT.KEY_READ); - lpValueName = new char[16384]; - lpcchValueName = new IntByReference(16384); - lpType = new IntByReference(); - lpData = new byte[1]; - lpcbData = new IntByReference(); - - if(handle != 0) { - dwIndex = 0; - - do { - lpcbData.setValue(0); - result = advapi32.RegEnumValue(handle, dwIndex, lpValueName, lpcchValueName, null, - lpType, lpData, lpcbData); - - if(result == WINERROR.ERROR_MORE_DATA) { - lpData = new byte[lpcbData.getValue()]; - lpcchValueName = new IntByReference(16384); - result = advapi32.RegEnumValue(handle, dwIndex, lpValueName, lpcchValueName, null, - lpType, lpData, lpcbData); - - if(result == WINERROR.ERROR_SUCCESS) { - name = new String(lpValueName, 0, lpcchValueName.getValue()); - - switch(lpType.getValue()) { - case WINNT.REG_SZ: - values.put(name, convertBufferToString(lpData)); - break; - case WINNT.REG_DWORD: - values.put(name, convertBufferToInt(lpData)); - break; - default: - break; - } - } - } - dwIndex++; - } while(result == WINERROR.ERROR_SUCCESS); - - advapi32.RegCloseKey(handle); - } - return(values); - } -} diff --git a/app/src/processing/app/windows/WINBASE.java b/app/src/processing/app/windows/WINBASE.java deleted file mode 100644 index c4807cc90..000000000 --- a/app/src/processing/app/windows/WINBASE.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * WINBASE.java - * - * Created on 5. September 2007, 11:24 - * - * To change this template, choose Tools | Template Manager - * and open the template in the editor. - */ - -package processing.app.windows; - -import com.sun.jna.Pointer; -import com.sun.jna.Structure; - -/** - * - * @author TB - */ -public interface WINBASE { -/* -typedef struct _SECURITY_ATTRIBUTES { - DWORD nLength; - LPVOID lpSecurityDescriptor; - BOOL bInheritHandle; -} SECURITY_ATTRIBUTES, - *PSECURITY_ATTRIBUTES, - *LPSECURITY_ATTRIBUTES;*/ - public static class SECURITY_ATTRIBUTES extends Structure { - public int nLength; - public Pointer lpSecurityDescriptor; - public boolean bInheritHandle; - } - -/* -typedef struct _FILETIME { - DWORD dwLowDateTime; - DWORD dwHighDateTime; -} FILETIME, *PFILETIME, *LPFILETIME;*/ - public static class FILETIME extends Structure { - public int dwLowDateTime; - public int dwHighDateTime; - } -} diff --git a/app/src/processing/app/windows/WINERROR.java b/app/src/processing/app/windows/WINERROR.java deleted file mode 100644 index 3e1146e93..000000000 --- a/app/src/processing/app/windows/WINERROR.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * WINERROR.java - * - * Created on 7. August 2007, 08:09 - * - * To change this template, choose Tools | Template Manager - * and open the template in the editor. - */ - -package processing.app.windows; - - -/** - * - * @author TB - */ -public interface WINERROR { - public final static int ERROR_SUCCESS = 0; - public final static int NO_ERROR = 0; - public final static int ERROR_FILE_NOT_FOUND = 2; - public final static int ERROR_MORE_DATA = 234; -} diff --git a/app/src/processing/app/windows/WINNT.java b/app/src/processing/app/windows/WINNT.java deleted file mode 100644 index 89aa36168..000000000 --- a/app/src/processing/app/windows/WINNT.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * WINNT.java - * - * Created on 8. August 2007, 13:41 - * - * To change this template, choose Tools | Template Manager - * and open the template in the editor. - */ - -package processing.app.windows; - -/** - * - * @author TB - */ -public interface WINNT { - public final static int DELETE = 0x00010000; - public final static int READ_CONTROL = 0x00020000; - public final static int WRITE_DAC = 0x00040000; - public final static int WRITE_OWNER = 0x00080000; - public final static int SYNCHRONIZE = 0x00100000; - - public final static int STANDARD_RIGHTS_REQUIRED = 0x000F0000; - - public final static int STANDARD_RIGHTS_READ = READ_CONTROL; - public final static int STANDARD_RIGHTS_WRITE = READ_CONTROL; - public final static int STANDARD_RIGHTS_EXECUTE = READ_CONTROL; - - public final static int STANDARD_RIGHTS_ALL = 0x001F0000; - - public final static int SPECIFIC_RIGHTS_ALL = 0x0000FFFF; - - public final static int GENERIC_EXECUTE = 0x20000000; - - public final static int SERVICE_WIN32_OWN_PROCESS = 0x00000010; - - public final static int KEY_QUERY_VALUE = 0x0001; - public final static int KEY_SET_VALUE = 0x0002; - public final static int KEY_CREATE_SUB_KEY = 0x0004; - public final static int KEY_ENUMERATE_SUB_KEYS = 0x0008; - public final static int KEY_NOTIFY = 0x0010; - public final static int KEY_CREATE_LINK = 0x0020; - - public final static int KEY_READ = ((STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY) & (~SYNCHRONIZE)); - public final static int KEY_WRITE = ((STANDARD_RIGHTS_WRITE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY) & (~SYNCHRONIZE)); - - public final static int REG_NONE = 0; // No value type - public final static int REG_SZ = 1; // Unicode nul terminated string - public final static int REG_EXPAND_SZ = 2; // Unicode nul terminated string - // (with environment variable references) - public final static int REG_BINARY = 3; // Free form binary - public final static int REG_DWORD = 4; // 32-bit number - public final static int REG_DWORD_LITTLE_ENDIAN = 4; // 32-bit number (same as REG_DWORD) - public final static int REG_DWORD_BIG_ENDIAN = 5; // 32-bit number - public final static int REG_LINK = 6; // Symbolic Link (unicode) - public final static int REG_MULTI_SZ = 7; // Multiple Unicode strings - public final static int REG_RESOURCE_LIST = 8; // Resource list in the resource map - public final static int REG_FULL_RESOURCE_DESCRIPTOR = 9; // Resource list in the hardware description - public final static int REG_RESOURCE_REQUIREMENTS_LIST = 10; - - public final static int REG_OPTION_RESERVED = 0x00000000; // Parameter is reserved - public final static int REG_OPTION_NON_VOLATILE = 0x00000000; // Key is preserved - // when system is rebooted - public final static int REG_OPTION_VOLATILE = 0x00000001; // Key is not preserved - // when system is rebooted - public final static int REG_OPTION_CREATE_LINK = 0x00000002; // Created key is a - // symbolic link - public final static int REG_OPTION_BACKUP_RESTORE = 0x00000004; // open for backup or restore - // special access rules - // privilege required - public final static int REG_OPTION_OPEN_LINK = 0x00000008; // Open symbolic link - -} diff --git a/app/src/processing/app/windows/WINREG.java b/app/src/processing/app/windows/WINREG.java deleted file mode 100644 index 988f7ef36..000000000 --- a/app/src/processing/app/windows/WINREG.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * WINREG.java - * - * Created on 17. August 2007, 14:32 - * - * To change this template, choose Tools | Template Manager - * and open the template in the editor. - */ - -package processing.app.windows; - -/** - * - * @author TB - */ -public interface WINREG { - public final static int HKEY_CLASSES_ROOT = 0x80000000; - public final static int HKEY_CURRENT_USER = 0x80000001; - public final static int HKEY_LOCAL_MACHINE = 0x80000002; - public final static int HKEY_USERS = 0x80000003; -} diff --git a/app/src/processing/mode/java/Commander.java b/app/src/processing/mode/java/Commander.java index 55c63c351..ba0d83cd6 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 = "--export"; static final String platformArg = "--platform="; static final String bitsArg = "--bits="; // static final String preferencesArg = "--preferences="; @@ -66,26 +67,7 @@ public class Commander implements RunnerListener { 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" - }; - } - */ - + static public void main(String[] args) { // 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 @@ -108,8 +90,10 @@ 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 @@ -122,6 +106,9 @@ public class Commander implements RunnerListener { 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,24 +133,29 @@ 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()); @@ -286,11 +278,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); } } } @@ -302,6 +294,7 @@ public class Commander implements RunnerListener { } catch (SketchException re) { statusError(re); + System.exit(1); } catch (IOException e) { e.printStackTrace(); @@ -329,13 +322,16 @@ 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 + ":" + line + ":" + column + ":" + " " + re.getMessage()); } else { // no line number, pass the trace along to the user @@ -376,11 +372,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(); } diff --git a/app/src/processing/mode/java/Compiler.java b/app/src/processing/mode/java/Compiler.java index ba98e68fe..417e9f45f 100644 --- a/app/src/processing/mode/java/Compiler.java +++ b/app/src/processing/mode/java/Compiler.java @@ -100,7 +100,7 @@ public class Compiler { // System.arraycopy(sourceFiles, 0, command, baseCommand.length, sourceCount); String[] command = PApplet.concat(baseCommand, sourceFiles); - //PApplet.println(command); +// PApplet.println(command); try { // Load errors into a local StringBuffer diff --git a/app/src/processing/mode/java/JavaBuild.java b/app/src/processing/mode/java/JavaBuild.java index e132c09cf..f42d0930d 100644 --- a/app/src/processing/mode/java/JavaBuild.java +++ b/app/src/processing/mode/java/JavaBuild.java @@ -454,6 +454,12 @@ public class JavaBuild { javaClassPath = javaClassPath.substring(1, javaClassPath.length() - 1); } classPath += File.pathSeparator + javaClassPath; + + // But make sure that there isn't anything in there that's missing, + // otherwise ECJ will complain and die. For instance, Java 1.7 (or maybe + // it's appbundler?) adds Java/Classes to the path, which kills us. + //String[] classPieces = PApplet.split(classPath, File.pathSeparator); + // Nah, nevermind... we'll just create the @!#$! folder until they fix it. // 3. then loop over the code[] and save each .java file @@ -1084,6 +1090,7 @@ public class JavaBuild { return false; } + /* File folder = null; for (String platformName : PConstants.platformNames) { int platform = Base.getPlatformIndex(platformName); @@ -1107,23 +1114,42 @@ public class JavaBuild { } } } + */ + + 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 @@ -1154,13 +1180,54 @@ 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 jdkFolderName = null; + String jvmRuntime = ""; 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 + ""; + } + +// File dotAppSkeleton = mode.getContentFile("application/template.app"); +// Base.copyDir(dotAppSkeleton, dotAppFolder); + 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.. @@ -1183,13 +1250,20 @@ 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(); @@ -1374,13 +1448,22 @@ public class JavaBuild { /// figure out run options for the VM - String runOptions = Preferences.get("run.options"); + // this is too vague. if anyone is using it, we can bring it back +// 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"); } + + StringBuilder jvmOptionsList = new StringBuilder(); + for (String opt : runOptions) { + jvmOptionsList.append(" "); + jvmOptionsList.append(opt); + jvmOptionsList.append(""); + jvmOptionsList.append('\n'); + } + // 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". @@ -1395,10 +1478,12 @@ public class JavaBuild { /// macosx: write out Info.plist (template for classpath, etc) if (exportPlatform == PConstants.MACOSX) { - String PLIST_TEMPLATE = "template.plist"; + //String PLIST_TEMPLATE = "template.plist"; + 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/template.plist"); + plistTemplate = mode.getContentFile("application/Info.plist.tmpl"); } File plistFile = new File(dotAppFolder, "Contents/Info.plist"); PrintWriter pw = PApplet.createWriter(plistFile); @@ -1408,33 +1493,37 @@ 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(), + jvmOptionsList.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("@@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); - } +// 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(); } @@ -1448,10 +1537,10 @@ public class JavaBuild { File argsFile = new File(destFolder + "/lib/args.txt"); PrintWriter pw = PApplet.createWriter(argsFile); - pw.println(runOptions); - - pw.println(sketch.getName()); - pw.println(exportClassPath); + // 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); pw.flush(); pw.close(); diff --git a/app/src/processing/mode/java/JavaEditor.java b/app/src/processing/mode/java/JavaEditor.java index 2eb4d28f9..676db1272 100644 --- a/app/src/processing/mode/java/JavaEditor.java +++ b/app/src/processing/mode/java/JavaEditor.java @@ -164,6 +164,14 @@ public class JavaEditor extends Editor { } }); menu.add(item); + + item = new JMenuItem("The Processing Foundation"); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Base.openURL("http://processing.org/foundation/"); + } + }); + menu.add(item); item = new JMenuItem("Visit Processing.org"); item.addActionListener(new ActionListener() { @@ -249,27 +257,21 @@ public class JavaEditor extends Editor { panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); panel.add(Box.createVerticalStrut(6)); - //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 = "Export to Application creates double-clickable,"; - String line2 = "standalone applications for the selected plaforms."; + //String line2 = "standalone applications for the selected plaforms."; + 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"); //windowsButton.setMnemonic(KeyEvent.VK_W); windowsButton.setSelected(Preferences.getBoolean("export.application.platform.windows")); @@ -310,11 +312,12 @@ public class JavaEditor extends Editor { 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 indent = new JCheckBox().getPreferredSize().width; + int indent = 0; + final JCheckBox showStopButton = new JCheckBox("Show a Stop button"); - //showStopButton.setMnemonic(KeyEvent.VK_S); showStopButton.setSelected(Preferences.getBoolean("export.application.stop")); showStopButton.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { @@ -322,12 +325,9 @@ 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("Full Screen (Present mode)"); - //fullscreenButton.setMnemonic(KeyEvent.VK_F); fullScreenButton.setSelected(Preferences.getBoolean("export.application.fullscreen")); fullScreenButton.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { @@ -338,16 +338,36 @@ public class JavaEditor extends Editor { }); fullScreenButton.setBorder(new EmptyBorder(3, 13, 3, 13)); + boolean embed = Preferences.getBoolean("export.application.embed_java"); + final String embedWarning = "Embedding Java makes larger applications"; + final String nopeWarning = "Users will have to install the latest Java 7"; + final JLabel warningLabel = new JLabel(embed ? embedWarning : nopeWarning); + warningLabel.setBorder(new EmptyBorder(3, 13 + indent, 3, 13)); + + final JCheckBox embedJavaButton = new JCheckBox("Embed Java"); + 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)); + JPanel optionPanel = new JPanel(); optionPanel.setLayout(new BoxLayout(optionPanel, BoxLayout.Y_AXIS)); optionPanel.add(fullScreenButton); optionPanel.add(showStopButton); -// optionPanel.add(indentPanel); + optionPanel.add(embedJavaButton); + optionPanel.add(warningLabel); optionPanel.setBorder(new TitledBorder("Options")); - wide = Math.max(wide, platformPanel.getPreferredSize().width); - //goodIdea = new Dimension(wide, optionPanel.getPreferredSize().height); +// wide = Math.max(wide, platformPanel.getPreferredSize().width); optionPanel.setAlignmentX(Component.LEFT_ALIGNMENT); - //optionPanel.setMaximumSize(goodIdea); panel.add(optionPanel); Dimension good; @@ -356,43 +376,20 @@ public class JavaEditor extends Editor { 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, platformPanel.getPreferredSize().height); +// platformPanel.setMaximumSize(good); good = new Dimension(wide, optionPanel.getPreferredSize().height); - optionPanel.setMaximumSize(good); +// 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; -// } -// }); - - // 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); -// } String[] options = { "Export", "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, "Export Options", true); + final JDialog dialog = new JDialog(this, "Export Application", true); dialog.setContentPane(optionPane); optionPane.addPropertyChangeListener(new PropertyChangeListener() { @@ -402,9 +399,8 @@ 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); } } diff --git a/app/src/processing/mode/java/JavaMode.java b/app/src/processing/mode/java/JavaMode.java index 4e7aff444..0753404b5 100644 --- a/app/src/processing/mode/java/JavaMode.java +++ b/app/src/processing/mode/java/JavaMode.java @@ -101,6 +101,12 @@ public class JavaMode extends Mode { if (coreLibrary == null) { File coreFolder = Base.getContentFile("core"); coreLibrary = new Library(coreFolder); +// try { +// coreLibrary = getLibrary("processing.core"); +// System.out.println("core found at " + coreLibrary.getLibraryPath()); +// } catch (SketchException e) { +// Base.log("Serious problem while locating processing.core", e); +// } } return coreLibrary; } diff --git a/app/src/processing/mode/java/runner/Runner.java b/app/src/processing/mode/java/runner/Runner.java index 3a4d32dfe..118376dd3 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,56 +161,42 @@ 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 (System.getProperty("os.version").startsWith("10.6")) { - commandArgs = new String[] { - "/usr/libexec/java_home", - "--version", "1.6", - "--exec", "java", - "-d" + Base.getNativeBits(), - jdwpArg - }; - } else { // for 10.7, 10.8, etc - commandArgs = new String[] { - "/usr/libexec/java_home", - "--request", // install on-demand - "--version", "1.6", - "--exec", "java", - "-d" + Base.getNativeBits(), + if (false) { + if (System.getProperty("os.version").startsWith("10.6")) { + commandArgs = new String[] { + "/usr/libexec/java_home", + "--version", "1.6", + "--exec", "java", + "-d" + Base.getNativeBits(), + jdwpArg + }; + } else { // for 10.7, 10.8, etc + commandArgs = new String[] { + "/usr/libexec/java_home", + "--request", // install on-demand + "--version", "1.6", + "--exec", "java", + "-d" + Base.getNativeBits(), // debugArg, + jdwpArg + }; + } + } else { + // testing jdk-7u40 + commandArgs = new String[] { + //"/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"); @@ -459,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) { @@ -806,18 +793,27 @@ public class Runner implements MessageConsumer { // System.out.println("mess type " + messageValue.type()); //StringReference messageReference = (StringReference) messageValue.type(); -// System.out.println(or.referenceType().fields()); -// if (name.startsWith("java.lang.")) { -// name = name.substring(10); - if (!handleCommonErrors(exceptionName, message, listener)) { - reportException(message, or, event.thread()); - } + // First just report the exception and its placement + reportException(message, or, event.thread()); + // Then try to pretty it up with a better message + handleCommonErrors(exceptionName, message, listener); + if (editor != null) { editor.deactivateRun(); } } + /** + * Provide more useful explanations of common error messages, perhaps with + * a short message in the status area, and (if necessary) a longer message + * in the console. + * + * @param exceptionClass Class name causing the error (with full package name) + * @param message The message from the exception + * @param listener The Editor or command line interface that's listening for errors + * @return true if the error was purtified, false otherwise + */ public static boolean handleCommonErrors(final String exceptionClass, final String message, final RunnerListener listener) { @@ -837,6 +833,11 @@ public class Runner implements MessageConsumer { System.err.println("If your sketch uses a lot of memory (for instance if it loads a lot of data files)"); System.err.println("you can increase the memory available to your sketch using the Preferences window."); } + } else if (exceptionClass.equals("java.lang.UnsatisfiedLinkError")) { + listener.statusError("A library used by this sketch is not installed properly."); + System.err.println("A library relies on native code that's not available."); + System.err.println("Or only works properly when the sketch is run as a " + + ((Base.getNativeBits() == 32) ? "64-bit " : "32-bit ") + " application."); } else if (exceptionClass.equals("java.lang.StackOverflowError")) { listener.statusError("StackOverflowError: This sketch is attempting too much recursion."); diff --git a/app/test/resources/annotations.expected b/app/test/resources/annotations.expected index 8a45dbc62..76d6b79a8 100755 --- a/app/test/resources/annotations.expected +++ b/app/test/resources/annotations.expected @@ -1,21 +1,23 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.io.Serializable; + +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class annotations extends PApplet { + + public void setup() { size(200,200); } @@ -40,7 +42,12 @@ class Apple implements Serializable { comments="Shazam!", date="2001-07-04T12:08:56.235-0700") class Pear {} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "annotations" }); + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "annotations" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/annotations.pde b/app/test/resources/annotations.pde index 1381d95e0..7df4e2a9b 100755 --- a/app/test/resources/annotations.pde +++ b/app/test/resources/annotations.pde @@ -1,3 +1,5 @@ +import java.io.Serializable; + void setup() { size(200,200); } diff --git a/app/test/resources/bug1064.expected b/app/test/resources/bug1064.expected index 2cbeaca7b..2fd1f3ef0 100644 --- a/app/test/resources/bug1064.expected +++ b/app/test/resources/bug1064.expected @@ -1,25 +1,29 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug1064 extends PApplet { public void setup() { -// import "; - noLoop(); -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug1064" }); +// import "; + noLoop(); + } + + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug1064" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug136.expected b/app/test/resources/bug136.expected index 81328df74..3ed7a5fc5 100644 --- a/app/test/resources/bug136.expected +++ b/app/test/resources/bug136.expected @@ -1,35 +1,41 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.Collections; + +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug136 extends PApplet { -java.util.List alist = Collections.synchronizedList(new ArrayList()); - -public void setup() { -size(400, 200); -alist.add("hello"); -} - -public void draw() { -rect(width/4, height/4, width/2, height/2); -synchronized(alist) { -alist.get(0); -} -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug136" }); + +java.util.List alist = Collections.synchronizedList(new ArrayList()); + +public void setup() { +size(400, 200); +alist.add("hello"); +} + +public void draw() { +rect(width/4, height/4, width/2, height/2); +synchronized(alist) { +alist.get(0); +} +} + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug136" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug136.pde b/app/test/resources/bug136.pde index fe00c17a8..58f078e22 100644 --- a/app/test/resources/bug136.pde +++ b/app/test/resources/bug136.pde @@ -1,3 +1,4 @@ +import java.util.Collections; java.util.List alist = Collections.synchronizedList(new ArrayList()); void setup() { diff --git a/app/test/resources/bug1362.expected b/app/test/resources/bug1362.expected index ddcfb0962..479feefb6 100644 --- a/app/test/resources/bug1362.expected +++ b/app/test/resources/bug1362.expected @@ -1,25 +1,29 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug1362 extends PApplet { public void setup() { -if (true) {} else { new String(); } - noLoop(); -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug1362" }); +if (true) {} else { new String(); } + noLoop(); + } + + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug1362" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug1442.expected b/app/test/resources/bug1442.expected index f04c55eb3..1a22ef53a 100644 --- a/app/test/resources/bug1442.expected +++ b/app/test/resources/bug1442.expected @@ -1,25 +1,28 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug1442 extends PApplet { -public float a() { - return 1.0f; -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug1442" }); +public float a() { + return 1.0f; +} + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug1442" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug1511.expected b/app/test/resources/bug1511.expected index f0b7611cc..10a0d155a 100644 --- a/app/test/resources/bug1511.expected +++ b/app/test/resources/bug1511.expected @@ -1,33 +1,37 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; import java.io.StringWriter; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug1511 extends PApplet { public void setup() { -// \u00df - -/** -* a -*/ - - - noLoop(); -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug1511" }); +// \u00df + +/** +* a +*/ + + + noLoop(); + } + + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug1511" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug1512.expected b/app/test/resources/bug1512.expected index ef4ef3cd3..511615729 100644 --- a/app/test/resources/bug1512.expected +++ b/app/test/resources/bug1512.expected @@ -1,25 +1,29 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug1512 extends PApplet { public void setup() { -println("oi/*"); - noLoop(); -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug1512" }); +println("oi/*"); + noLoop(); + } + + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug1512" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug1514a.expected b/app/test/resources/bug1514a.expected index b7cdc32aa..7596c2745 100644 --- a/app/test/resources/bug1514a.expected +++ b/app/test/resources/bug1514a.expected @@ -1,31 +1,35 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; import java.io.StringWriter; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug1514a extends PApplet { public void setup() { -//----- \u00e8\u00e9\u00e9\u00e8\u00e8\u00e9\u00e9\u00e9\u00e0\u00e9\u00e9\u00e8\u00e9''\u00e9\u00e9\u00e9 -//---------------------------------------------------------------- -//--- IMPORTS -//---------------------------------------------------------------- - - noLoop(); -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug1514a" }); +//----- \u00e8\u00e9\u00e9\u00e8\u00e8\u00e9\u00e9\u00e9\u00e0\u00e9\u00e9\u00e8\u00e9''\u00e9\u00e9\u00e9 +//---------------------------------------------------------------- +//--- IMPORTS +//---------------------------------------------------------------- + + noLoop(); + } + + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug1514a" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug1514b.expected b/app/test/resources/bug1514b.expected index 8e7c95b93..b7628ef32 100644 --- a/app/test/resources/bug1514b.expected +++ b/app/test/resources/bug1514b.expected @@ -1,31 +1,35 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; import java.io.StringWriter; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug1514b extends PApplet { public void setup() { -//----- \u00e8\u00e9\u00e9\u00e8\u00e8\u00e9\u00e9\u00e9\u00e0\u00e9\u00e9\u00e8\u00e9''\u00e9\u00e9 -//---------------------------------------------------------------- -//--- IMPORTS -//---------------------------------------------------------------- - - noLoop(); -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug1514b" }); +//----- \u00e8\u00e9\u00e9\u00e8\u00e8\u00e9\u00e9\u00e9\u00e0\u00e9\u00e9\u00e8\u00e9''\u00e9\u00e9 +//---------------------------------------------------------------- +//--- IMPORTS +//---------------------------------------------------------------- + + noLoop(); + } + + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug1514b" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug1515.expected b/app/test/resources/bug1515.expected index 0401a4ef2..f2d0ae2c4 100644 --- a/app/test/resources/bug1515.expected +++ b/app/test/resources/bug1515.expected @@ -1,29 +1,32 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug1515 extends PApplet { -// a class definition that does something with things that extend PApplet -class Heythere{ -} - -// method definition which can do things with papplet -public void doSomething( T thing ){ -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug1515" }); +// a class definition that does something with things that extend PApplet +class Heythere{ +} + +// method definition which can do things with papplet +public void doSomething( T thing ){ +} + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug1515" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug1516.expected b/app/test/resources/bug1516.expected index 70faf1b6b..15eb348d3 100644 --- a/app/test/resources/bug1516.expected +++ b/app/test/resources/bug1516.expected @@ -1,49 +1,52 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; import java.util.ArrayList; import java.util.List; import java.util.Collections; import java.util.Comparator; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug1516 extends PApplet { - - - - - -public void setup() -{ -List list = new ArrayList(); -list.add("foo"); -list.add("bar"); -list.add("baz"); - -Comparator comparator = new Comparator() -{ -public int compare(final String value0, final String value1) -{ -return value0.compareTo(value1); -} -}; - -Collections.sort(list, comparator); -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug1516" }); + + + + + +public void setup() +{ +List list = new ArrayList(); +list.add("foo"); +list.add("bar"); +list.add("baz"); + +Comparator comparator = new Comparator() +{ +public int compare(final String value0, final String value1) +{ +return value0.compareTo(value1); +} +}; + +Collections.sort(list, comparator); +} + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug1516" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug1517.expected b/app/test/resources/bug1517.expected index b6533a310..b8bac1af2 100644 --- a/app/test/resources/bug1517.expected +++ b/app/test/resources/bug1517.expected @@ -1,49 +1,52 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; import java.util.ArrayList; import java.util.List; import java.util.Collections; import java.util.Comparator; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug1517 extends PApplet { - - - - - -Comparator comparator = new Comparator() -{ -public int compare(final String value0, final String value1) -{ -return value0.compareTo(value1); -} -}; - -public void setup() -{ -List list = new ArrayList(); -list.add("foo"); -list.add("bar"); -list.add("baz"); - -Collections.sort(list, comparator); -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug1517" }); + + + + + +Comparator comparator = new Comparator() +{ +public int compare(final String value0, final String value1) +{ +return value0.compareTo(value1); +} +}; + +public void setup() +{ +List list = new ArrayList(); +list.add("foo"); +list.add("bar"); +list.add("baz"); + +Collections.sort(list, comparator); +} + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug1517" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug1518a.expected b/app/test/resources/bug1518a.expected index 3ba413d18..55288cc3c 100644 --- a/app/test/resources/bug1518a.expected +++ b/app/test/resources/bug1518a.expected @@ -1,42 +1,45 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; import java.util.ArrayList; import java.util.List; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug1518a extends PApplet { - - - -public void setup() -{ -List list = new ArrayList(); -list.add("foo"); -list.add("bar"); -list.add("baz"); - -binarySearch(list, "bar"); -} - -static int binarySearch(List> list, T -key) { -return 0; -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug1518a" }); + + + +public void setup() +{ +List list = new ArrayList(); +list.add("foo"); +list.add("bar"); +list.add("baz"); + +binarySearch(list, "bar"); +} + +static int binarySearch(List> list, T +key) { +return 0; +} + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug1518a" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug1518b.expected b/app/test/resources/bug1518b.expected index 156e9cfe0..f4144e7b1 100644 --- a/app/test/resources/bug1518b.expected +++ b/app/test/resources/bug1518b.expected @@ -1,40 +1,43 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; import java.util.ArrayList; import java.util.List; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug1518b extends PApplet { - - - -public void setup() -{ -List list = new ArrayList(); -list.add("foo"); -list.add("bar"); -list.add("baz"); -} - -static int binarySearch(List> list, T -key) { -return 0; -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug1518b" }); + + + +public void setup() +{ +List list = new ArrayList(); +list.add("foo"); +list.add("bar"); +list.add("baz"); +} + +static int binarySearch(List> list, T +key) { +return 0; +} + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug1518b" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug1525.expected b/app/test/resources/bug1525.expected index 813f98f77..c1337f7b2 100644 --- a/app/test/resources/bug1525.expected +++ b/app/test/resources/bug1525.expected @@ -1,27 +1,31 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug1525 extends PApplet { public void setup() { if (frameCount > (frameRate - 1)) { println("My head asplode!"); } - noLoop(); -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug1525" }); + noLoop(); + } + + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug1525" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug1534.expected b/app/test/resources/bug1534.expected index 2132c573f..1b61ca49c 100644 --- a/app/test/resources/bug1534.expected +++ b/app/test/resources/bug1534.expected @@ -1,25 +1,29 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug1534 extends PApplet { public void setup() { char c = '\"'; - noLoop(); -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug1534" }); + noLoop(); + } + + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug1534" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug281.expected b/app/test/resources/bug281.expected index aca36a9b3..d6e7f4154 100644 --- a/app/test/resources/bug281.expected +++ b/app/test/resources/bug281.expected @@ -1,28 +1,32 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug281 extends PApplet { public void setup() { -if ( "monopoly".charAt( 3 ) == '(' ) -{ - println("parcheesi"); -} - noLoop(); -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug281" }); +if ( "monopoly".charAt( 3 ) == '(' ) +{ + println("parcheesi"); +} + noLoop(); + } + + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug281" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug315g.expected b/app/test/resources/bug315g.expected index cba428de7..ef5cdffa6 100644 --- a/app/test/resources/bug315g.expected +++ b/app/test/resources/bug315g.expected @@ -1,18 +1,16 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug315g extends PApplet { public void setup() { @@ -23,9 +21,15 @@ y = 60; int d; d = 80; ellipse(75, y, d, d); - noLoop(); -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug315g" }); + noLoop(); + } + + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug315g" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug4.expected b/app/test/resources/bug4.expected index a54d206c3..661de7b80 100644 --- a/app/test/resources/bug4.expected +++ b/app/test/resources/bug4.expected @@ -1,26 +1,30 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug4 extends PApplet { public void setup() { int x = 12; float u = (PApplet.parseFloat(x)/width); - noLoop(); -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug4" }); + noLoop(); + } + + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug4" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug400g.expected b/app/test/resources/bug400g.expected index 3383c22cf..92dc4f577 100644 --- a/app/test/resources/bug400g.expected +++ b/app/test/resources/bug400g.expected @@ -1,18 +1,16 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug400g extends PApplet { @@ -25,7 +23,12 @@ public void setup(){ else{ // Syntax error on token "else", } expected } } - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug400g" }); + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug400g" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug427g.expected b/app/test/resources/bug427g.expected index b02e3d780..aebd13672 100755 --- a/app/test/resources/bug427g.expected +++ b/app/test/resources/bug427g.expected @@ -1,18 +1,16 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug427g extends PApplet { @@ -29,7 +27,12 @@ public class MyClass { } - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug427g" }); + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug427g" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug481.expected b/app/test/resources/bug481.expected index 467881e7a..ef2848da3 100644 --- a/app/test/resources/bug481.expected +++ b/app/test/resources/bug481.expected @@ -1,25 +1,32 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.applet.Applet; + +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug481 extends PApplet { public void setup() { -Class[] abc = new Class[]{Applet.class}; - noLoop(); -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug481" }); + +Class[] abc = new Class[]{Applet.class}; + noLoop(); + } + + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug481" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug481.pde b/app/test/resources/bug481.pde index c9aa594d5..38aead2ad 100644 --- a/app/test/resources/bug481.pde +++ b/app/test/resources/bug481.pde @@ -1 +1,2 @@ +import java.applet.Applet; Class[] abc = new Class[]{Applet.class}; \ No newline at end of file diff --git a/app/test/resources/bug598.expected b/app/test/resources/bug598.expected index 629c172c7..988c1125b 100644 --- a/app/test/resources/bug598.expected +++ b/app/test/resources/bug598.expected @@ -1,54 +1,70 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; import static java.lang.Math.tanh; import java.util.concurrent.Callable; +import java.util.List; +import java.util.Comparator; +import java.util.Map; +import java.util.Collection; +import java.util.Arrays; +import java.util.HashSet; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug598 extends PApplet { -// java 5 torture test - - - - -private static Comparator rotarapmoc = new Comparator() { - public int compare(final String o1, final String o2) - { - return o1.charAt(o1.length() - 1) - o2.charAt(o2.length() - 1); - } -}; - -final void printClass(T t) { - println(t.getClass()); -} -public final List sortem(final String... strings) { - Arrays.sort(strings, rotarapmoc); - return Arrays.asList(strings); -} - -final Map> -charlesDeGaulle = new HashMap>(); - -public void setup() { - charlesDeGaulle.put("banana", new HashSet()); - charlesDeGaulle.get("banana").add(0); - System.out.println(sortem("aztec", "maya", "spanish", "portuguese")); - printClass(12.d); -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug598" }); +// java 5 torture test + + + + + + + + + + + +private static Comparator rotarapmoc = new Comparator() { + public int compare(final String o1, final String o2) + { + return o1.charAt(o1.length() - 1) - o2.charAt(o2.length() - 1); + } +}; + +final void printClass(T t) { + println(t.getClass()); +} +public final List sortem(final String... strings) { + Arrays.sort(strings, rotarapmoc); + return Arrays.asList(strings); +} + +final Map> +charlesDeGaulle = new HashMap>(); + +public void setup() { + charlesDeGaulle.put("banana", new HashSet()); + charlesDeGaulle.get("banana").add(0); + System.out.println(sortem("aztec", "maya", "spanish", "portuguese")); + printClass(12.d); +} + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug598" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug598.pde b/app/test/resources/bug598.pde index c8efc6655..e0863d6f9 100644 --- a/app/test/resources/bug598.pde +++ b/app/test/resources/bug598.pde @@ -2,6 +2,13 @@ import static java.lang.Math.tanh; import java.util.concurrent.Callable; +import java.util.List; +import java.util.Comparator; +import java.util.Map; +import java.util.Collection; +import java.util.Arrays; +import java.util.HashSet; + private static Comparator rotarapmoc = new Comparator() { public int compare(final String o1, final String o2) diff --git a/app/test/resources/bug5a.expected b/app/test/resources/bug5a.expected index b5722a425..326bd783e 100644 --- a/app/test/resources/bug5a.expected +++ b/app/test/resources/bug5a.expected @@ -1,26 +1,30 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug5a extends PApplet { public void setup() { -println("The next line should not cause a failure."); -// no newline after me - noLoop(); -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug5a" }); +println("The next line should not cause a failure."); +// no newline after me + noLoop(); + } + + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug5a" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug5b.expected b/app/test/resources/bug5b.expected index 91a50b045..3731e6723 100644 --- a/app/test/resources/bug5b.expected +++ b/app/test/resources/bug5b.expected @@ -1,26 +1,30 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug5b extends PApplet { public void setup() { -println("The next line should not cause a failure."); -/* no newline after me */ - noLoop(); -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug5b" }); +println("The next line should not cause a failure."); +/* no newline after me */ + noLoop(); + } + + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug5b" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/resources/bug631.expected b/app/test/resources/bug631.expected index d8f217e6a..ae5c3b7ba 100644 --- a/app/test/resources/bug631.expected +++ b/app/test/resources/bug631.expected @@ -1,18 +1,16 @@ import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; -import java.applet.*; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.event.MouseEvent; -import java.awt.event.KeyEvent; -import java.awt.event.FocusEvent; -import java.awt.Image; -import java.io.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; -import java.util.regex.*; +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; public class bug631 extends PApplet { public void setup() { @@ -23,9 +21,15 @@ firstLoop: System.out.println(i + " " + j); } } - noLoop(); -} - static public void main(String args[]) { - PApplet.main(new String[] { "--bgcolor=null", "bug631" }); + noLoop(); + } + + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "bug631" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } } } diff --git a/app/test/src/test/processing/mode/java/ParserTests.java b/app/test/src/test/processing/mode/java/ParserTests.java index 965b63ac5..eca3de658 100644 --- a/app/test/src/test/processing/mode/java/ParserTests.java +++ b/app/test/src/test/processing/mode/java/ParserTests.java @@ -184,7 +184,7 @@ public class ParserTests { @Test public void bug820() { - expectCompilerException("bug820", "x1 is already defined in setup()", 21); + expectCompilerException("bug820", "error: variable x1 is already defined in method setup()", 18); } @Test @@ -194,7 +194,7 @@ public class ParserTests { @Test public void bug1145() { - expectCompilerException("bug1145", "'.' expected", 4); + expectCompilerException("bug1145", "error: '.' expected", 6); } @Test diff --git a/build/build.xml b/build/build.xml index 6f5b7707f..736f95f26 100755 --- a/build/build.xml +++ b/build/build.xml @@ -19,6 +19,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -34,28 +151,22 @@ - - - + + + - - - - - - + + + + value="macosx/work/Processing.app/Contents/Java"> @@ -68,6 +179,7 @@ + @@ -97,18 +209,6 @@ - - - - @@ -128,7 +228,6 @@ - @@ -140,7 +239,6 @@ - @@ -148,19 +246,8 @@ - - - - - - - - - - - - - + + @@ -372,8 +459,6 @@ - - @@ -391,30 +476,115 @@ - - - - - - + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - + - + + + + + - + + @@ -468,10 +644,13 @@ + + @@ -488,57 +667,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ======================================================= - Processing for Mac OS X was built. Grab the image from - - macosx/processing-${version}.dmg - ======================================================= - - - @@ -597,6 +725,11 @@ + + + + + @@ -604,8 +737,10 @@ + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -841,6 +1066,11 @@ + + + + @@ -861,9 +1091,11 @@ - + + + diff --git a/build/howto.txt b/build/howto.txt index a3f9661ad..5f6ab9fa6 100755 --- a/build/howto.txt +++ b/build/howto.txt @@ -1,5 +1,5 @@ As part of a grand plan for Casey and I to rid ourselves of as much of the duct tape and chewing gum that's holding together processing.org, -this document has been moved to Google Code: +this document has been moved to Github: -http://code.google.com/p/processing/wiki/BuildInstructions +https://github.com/processing/processing/wiki/Build-Instructions 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 dcd1c0017..14708c1ed 100755 --- a/build/linux/processing +++ b/build/linux/processing @@ -108,7 +108,7 @@ then else # Start Processing in the same directory as this script if [ "$1" ]; then - SKETCH=`readlink -f $1` + SKETCH=`readlink -f "$1"` else 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..4a365e304 --- /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/MacOS", 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/lib/preferences.txt b/build/shared/lib/defaults.txt similarity index 91% rename from build/shared/lib/preferences.txt rename to build/shared/lib/defaults.txt index c8f40630e..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 @@ -157,10 +159,16 @@ editor.divider.size = 0 # but keeps it from being annoyingly obtrusive editor.divider.size.windows = 2 +# Hide the background image. Gross because this is a pref that +# really lives over in theme.txt but it's split here. +buttons.hide.image = false +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 @@ -259,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 @@ -269,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 @@ -323,3 +331,11 @@ run.present.stop.color = #cccccc #run.present.exclusive = false # use this by default to hide the menu bar and dock on osx #run.present.exclusive.macosx = true + +# HTTP PROXY +# Set a proxy server for folks that require it. This will allow the update +# checker and the contrib manager to run properly in those environments. +#proxy.host=proxy.example.com +#proxy.port=8080 +proxy.host= +proxy.port= 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 29b08320f..70f11e26f 100755 --- a/build/shared/lib/theme.txt +++ b/build/shared/lib/theme.txt @@ -6,13 +6,13 @@ 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. header.bgcolor = #000000 +#header.hide.image = false # in preferences.txt +header.hide.color = #0E1B25 header.text.selected.color = #000000 header.text.unselected.color = #ffffff header.text.font = processing.sans,plain,14 @@ -26,15 +26,17 @@ console.output.color = #cccccc console.error.color = #ff3000 # TOOLBAR BUTTONS -#buttons.bgcolor = #4a545e buttons.bgcolor = #000000 +#buttons.hide.image = false # in preferences.txt +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 @@ -42,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 135b8a990..1bb92c515 100644 --- a/build/shared/revisions.txt +++ b/build/shared/revisions.txt @@ -1,3 +1,587 @@ +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 ] + ++ blendMode() change causes OpenGL renderer to be very slow + https://github.com/processing/processing/issues/2021 + ++ Serious OpenGL performance issues on OS X, this was fixed + with the JOGL update in 2.0.2, but we neglected to note it. + https://github.com/processing/processing/issues/1714 + ++ P2D low quality text rendering + https://github.com/processing/processing/issues/1972 + ++ Fix issues with slow text rendering and OpenGL + https://github.com/processing/processing/issues/2025 + ++ Corrupted text with large font and OpenGL + https://github.com/processing/processing/issues/1869 + ++ loadFont hangs on Processing 2.0 with any OpenGL renderer + https://github.com/processing/processing/issues/1854 + ++ Rendering artifacts on the diagonal line (topleft to bottomright) in P2D + https://github.com/processing/processing/issues/1964 + ++ loadShape doesn't load OBJ files in subdirectories properly + https://github.com/processing/processing/issues/2003 + ++ more OpenGL issues fixed by JOGL or newer drivers + https://github.com/processing/processing/issues/1986 + ++ Vertical offset when sketch height is indivisible by 2 + https://github.com/processing/processing/issues/1985 + ++ ellipse() causes RuntimeException: java.lang.OutOfMemoryError + https://github.com/processing/processing/issues/1941 + ++ beginShape()...endShape() lines look wrong at joins/caps with P2D + https://github.com/processing/processing/issues/1927 + ++ Problem with bit shifting and video + https://github.com/processing/processing/pull/2023 + 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 + + +. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + +PROCESSING 2.0.2 (REV 0220) - 14 August 2013 + +Many small bug fixes and lots of work on the Library/Tool/Mode Manager. +Full screen is back for Windows, and lots more. + + +[ bug fixes ] + ++ Fix Windows issues with associating .pde files + https://github.com/processing/processing/issues/286 + http://code.google.com/p/processing/issues/detail?id=247 + ++ Present Mode does not work properly on Windows + https://github.com/processing/processing/issues/1955 + ++ pixels[] array not updated with Capture and P2D/P3D + https://github.com/processing/processing/issues/1852 + ++ Unable to get TAB key event with P2D/P3D renderer + https://github.com/processing/processing/issues/1967 + ++ Setting an INT4 uniform in PShader causes an out of bounds exception + https://github.com/processing/processing/issues/1994 + ++ Fix "less less" typo + https://github.com/processing/processing/issues/1928 + ++ Slash breaks syntax highlighting when spaces are involved + https://github.com/processing/processing/issues/1681 + Fix from Github user hamzaissa - thanks! + ++ selectInput() in exported OS X sketch treats .app package as a folder + https://github.com/processing/processing/issues/1959 + ++ Code with a NUL character causes an error + https://github.com/processing/processing/issues/1973 + Also remove NUL characters when loading PDE files + ++ Allow delete of files in unsaved sketches + https://github.com/processing/processing/issues/1942 + https://github.com/processing/processing/pull/1945 + ++ UnsatisfiedLinkError was causing huge/unreadable message + ++ selectInput() in exported OS X sketch treats .app package as a folder + https://github.com/processing/processing/issues/1959 + ++ Retain blendMode() between frames + https://github.com/processing/processing/issues/1962 + + +[ contribution managers ] + ++ Support multiple categories for libraries + https://github.com/processing/processing/issues/1970 + ++ Notify users that Modes and Tools require restart. + https://github.com/processing/processing/issues/1782 + https://github.com/processing/processing/issues/1504 + http://code.google.com/p/processing/issues/detail?id=1466 + ++ Change the location of the manager download to something more stable. + ++ Restrict library categories to the ones in the document. If it's not + correct, shows up as 'other'. + ++ Catch Errors (not just Exceptions) when loading libraries, modes, + and tools. Handles UnsupportedClassVersionError and other quirks. + ++ Redo handling of "old" versions of contributions. + ++ Update example list when library is installed + https://github.com/processing/processing/issues/1909 + https://github.com/processing/processing/pull/1925 + + +[ data, data, data ] + ++ Error in IntList and FloatList insert() + https://github.com/processing/processing/issues/1929 + ++ Add a sort() method to Table. + ++ Implement version of Table that takes a dictionary file. Syntax is: + Table t = loadTable("bigdata.tsv", "dictionary=bigdata-dict.tsv") + This allows you to set the data type of columns before loading, which + provides much better performance and lower memory usage. + ++ Constructing table from an Iterator is missing + https://github.com/processing/processing/issues/1956 + ++ Add sum() to IntList and FloatList + https://github.com/processing/processing/issues/1893 + ++ When using increment() on IntList, make sure the index exists and + automatically resize the list if necessary. This is more in keeping + with increment() in the Dict classes. + ++ getSubset() broken in IntList, StringList, and missing from FloatList + https://github.com/processing/processing/issues/1979 + ++ Add join() method to Int/Float/StringList + ++ Add getContent(defaultValue) to XML + ++ Add isNull() (returns boolean) to JSONObject/Array + https://github.com/processing/processing/issues/2009 + ++ Add getXxxx(xxx, defaultValue) methods to JSONObject/Array + https://github.com/processing/processing/issues/2007 + + +[ internal changes you'll never notice... unless I broke something ] + ++ Add an exception wrapper for startup, hopefully we can catch/debug + more "Processing can't start!" issues with this. + ++ Update JNA from 3.2.4 to 3.5.2. + ++ Updated JOGL to 2.0.2. + ++ Added methods to move files to Trash/Recycle Bin where available. + ++ Implement basic proxy server support for update checking and the + contribution managers. + https://github.com/processing/processing/issues/1476 + ++ Basic getShape(ch) implementation for font glyph shapes. Not finished + and (therefore) not yet documented. + ++ Change QUAD_BEZIER_VERTEX to QUADRATIC_VERTEX to match the API call + (because this lives inside PConstants). + ++ Add retina switch for PApplet to set useActive with OS X and 7u40. + Prevents speed/performance issues with old sketches on retina macs. + ++ Add error message for raspberry pi (and others?) about int buffers + https://github.com/processing/processing/issues/2010 + + +[ changes ] + ++ Experimental Mode has been removed from the default download, + so that it can be updated more frequently. Install it and help us + test what will become the 3.0 release of Processing! + ++ Add "Processing Foundation" to the Help menu. + https://github.com/processing/processing/issues/1908 + + +. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + +PROCESSING 2.0.1 (REV 0219) - 21 June 2013 + +Bug fixes for some of what ailed the Processing 2.0 release, including +two contributed from Josh Giesbrecht. Thanks Josh! + +[ bug fixes ] + ++ Modes, Tools, Libraries not moving properly on Windows + https://github.com/processing/processing/issues/1781 + ++ Undo seems to not be going to the right location (now with example) + https://github.com/processing/processing/issues/707 + http://code.google.com/p/processing/issues/detail?id=668 + ++ Fix a problem with exporting Windows applications from OS X and Linux. + https://github.com/processing/processing/issues/1890 + ++ getVertex() trying to get three values when no Z-coord is available + "PShape getVertex() not implemented properly for SVG files" + https://github.com/processing/processing/issues/1596 + ++ Fix typo in default printProjection() method + https://github.com/processing/processing/issues/1863 + +[ additions ] + ++ Add error message for that reports what line was bad while parsing a table. + (Otherwise confusing ArrayIndexOutOfBoundsException while parsing bad CSV.) + ++ Added option to remove the background image at the top of the window. + + +. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + PROCESSING 2.0 (REV 0218) - 3 June 2013 And just like that, here we are at 2.0. 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/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..eba834ee3 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,20 +1126,223 @@ 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; 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/core/build.xml b/core/build.xml old mode 100644 new mode 100755 index 00d1cbbd6..0c2628197 --- a/core/build.xml +++ b/core/build.xml @@ -13,13 +13,17 @@ - + classpath="library/jogl-all.jar; library/gluegen-rt.jar" + nowarn="true" + compiler="org.eclipse.jdt.core.JDTCompilerAdapter"> + + + 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 old mode 100644 new mode 100755 index 00d2e49dd..bbde1fcef --- a/core/src/processing/core/PApplet.java +++ b/core/src/processing/core/PApplet.java @@ -54,7 +54,9 @@ import java.util.regex.*; import java.util.zip.*; import javax.imageio.ImageIO; +import javax.swing.JFrame; import javax.swing.JFileChooser; +import javax.swing.filechooser.FileSystemView; /** @@ -857,18 +859,11 @@ public class PApplet extends Applet // screenWidth = screen.width; // screenHeight = screen.height; - if (platform == MACOSX) { - Float prop = (Float) - getToolkit().getDesktopProperty("apple.awt.contentScaleFactor"); - if (prop != null) { - retina = prop == 2; - if (retina) { - // The active-mode rendering seems to be 2x slower, so disable it - // with retina. On a non-retina machine, however, useActive seems - // the only (or best?) way to handle the rendering. - useActive = false; - } - } + if (checkRetina()) { + // The active-mode rendering seems to be 2x slower, so disable it + // with retina. On a non-retina machine, however, useActive seems + // the only (or best) way to handle the rendering. + useActive = false; } // send tab keys through to the PApplet @@ -960,6 +955,47 @@ public class PApplet extends Applet } + private boolean checkRetina() { + if (platform == MACOSX) { + // This should probably be reset each time there's a display change. + // A 5-minute search didn't turn up any such event in the Java API. + // Also, should we use the Toolkit associated with the editor window? + final String javaVendor = System.getProperty("java.vendor"); + if (javaVendor.contains("Apple")) { + Float prop = (Float) + getToolkit().getDesktopProperty("apple.awt.contentScaleFactor"); + if (prop != null) { + return prop == 2; + } + } else if (javaVendor.contains("Oracle")) { + String version = System.getProperty("java.version"); // 1.7.0_40 + String[] m = match(version, "1.(\\d).*_(\\d+)"); + + // Make sure this is Oracle Java 7u40 or later + if (m != null && + PApplet.parseInt(m[1]) >= 7 && + PApplet.parseInt(m[1]) >= 40) { + GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice device = env.getDefaultScreenDevice(); + + try { + Field field = device.getClass().getDeclaredField("scale"); + if (field != null) { + field.setAccessible(true); + Object scale = field.get(device); + + if (scale instanceof Integer && ((Integer)scale).intValue() == 2) { + return true; + } + } + } catch (Exception ignore) { } + } + } + } + return false; + } + + public int sketchQuality() { return 2; } @@ -4300,8 +4336,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) { @@ -4344,6 +4381,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 @@ -4352,9 +4409,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 @@ -4373,14 +4431,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); @@ -4422,7 +4481,58 @@ 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 && 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"); @@ -5828,6 +5938,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); @@ -5845,7 +5962,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) @@ -6068,6 +6185,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; @@ -6121,6 +6239,7 @@ public class PApplet extends Applet return new JSONObject(new StringReader(input)); } + /** * @webref input:files * @param filename name of a file in the data folder or a URL @@ -6134,6 +6253,12 @@ public class PApplet extends Applet return new JSONObject(createReader(filename)); } + + static public JSONObject loadJSONObject(File file) { + return new JSONObject(createReader(file)); + } + + /** * @webref output:files * @see JSONObject @@ -6156,6 +6281,7 @@ public class PApplet extends Applet return new JSONArray(new StringReader(input)); } + /** * @webref input:files * @param filename name of a file in the data folder or a URL @@ -6169,6 +6295,12 @@ public class PApplet extends Applet return new JSONArray(createReader(filename)); } + + static public JSONArray loadJSONArray(File file) { + return new JSONArray(createReader(file)); + } + + /** * @webref output:files * @see JSONObject @@ -6214,22 +6346,28 @@ public class PApplet extends Applet /** - * @param options may contain "header", "tsv", "csv", or "bin" separated by commas + * Options may contain "header", "tsv", "csv", or "bin" separated by commas. + * + * Another option is "dictionary=filename.tsv", which allows users to + * specify a "dictionary" file that contains a mapping of the column titles + * and the data types used in the table file. This can be far more efficient + * (in terms of speed and memory usage) for loading and parsing tables. The + * dictionary file can only be tab separated values (.tsv) and its extension + * will be ignored. This option was added in Processing 2.0.2. */ public Table loadTable(String filename, String options) { try { -// String ext = checkExtension(filename); -// if (ext != null) { -// if (ext.equals("csv") || ext.equals("tsv") || ext.equals("bin")) { -// if (options == null) { -// options = ext; -// } else { -// options = ext + "," + options; -// } -// } -// } - return new Table(createInput(filename), - Table.extensionOptions(true, filename, options)); + String optionStr = Table.extensionOptions(true, filename, options); + String[] optionList = trim(split(optionStr, ',')); + + Table dictionary = null; + for (String opt : optionList) { + if (opt.startsWith("dictionary=")) { + dictionary = loadTable(opt.substring(opt.indexOf('=') + 1), "tsv"); + return dictionary.typedParse(createInput(filename), optionStr); + } + } + return new Table(createInput(filename), optionStr); } catch (IOException e) { e.printStackTrace(); @@ -7674,13 +7812,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. @@ -7707,11 +7871,12 @@ 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(); File dataFolder = new File(containingFolder, "data"); + System.out.println(dataFolder); return new File(dataFolder, where); } // Windows, Linux, or when not using a Mac OS X .app file @@ -7721,7 +7886,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) { // } @@ -10074,15 +10239,21 @@ public class PApplet extends Applet if (farm.isVisible()) { Insets insets = farm.getInsets(); Dimension windowSize = farm.getSize(); - Rectangle newBounds = - new Rectangle(insets.left, insets.top, - windowSize.width - insets.left - insets.right, - windowSize.height - insets.top - insets.bottom); - Rectangle oldBounds = getBounds(); - if (!newBounds.equals(oldBounds)) { - // the ComponentListener in PApplet will handle calling size() - setBounds(newBounds); - } + // JFrame (unlike java.awt.Frame) doesn't include the left/top + // insets for placement (though it does seem to need them for + // overall size of the window. Perhaps JFrame sets its coord + // system so that (0, 0) is always the upper-left of the content + // area. Which seems nice, but breaks any f*ing AWT-based code. + Rectangle newBounds = + new Rectangle(0, 0, //insets.left, insets.top, + windowSize.width - insets.left - insets.right, + windowSize.height - insets.top - insets.bottom); + Rectangle oldBounds = getBounds(); + if (!newBounds.equals(oldBounds)) { + // the ComponentListener in PApplet will handle calling size() + setBounds(newBounds); + revalidate(); // let the layout manager do its work + } } } } @@ -10378,30 +10549,24 @@ public class PApplet extends Applet displayDevice = environment.getDefaultScreenDevice(); } - Frame frame = new Frame(displayDevice.getDefaultConfiguration()); - frame.setBackground(new Color(0xCC, 0xCC, 0xCC)); // default Processing gray -// JFrame frame = new JFrame(displayDevice.getDefaultConfiguration()); - /* - Frame frame = null; - if (displayDevice != null) { - frame = new Frame(displayDevice.getDefaultConfiguration()); - } else { - frame = new Frame(); - } - */ - //Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); - - // remove the grow box by default - // users who want it back can call frame.setResizable(true) -// frame.setResizable(false); - // moved later (issue #467) + // Using a JFrame fixes a Windows problem with Present mode. This might + // be our error, but usually this is the sort of crap we usually get from + // OS X. It's time for a turnaround: Redmond is thinking different too! + // https://github.com/processing/processing/issues/1955 + 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). + final Color defaultGray = new Color(0xCC, 0xCC, 0xCC); + ((JFrame) frame).getContentPane().setBackground(defaultGray); + // Cannot call setResizable(false) until later due to OS X (issue #467) final PApplet applet; if (constructedApplet != null) { applet = constructedApplet; } else { try { - Class c = Thread.currentThread().getContextClassLoader().loadClass(name); + Class c = + Thread.currentThread().getContextClassLoader().loadClass(name); applet = (PApplet) c.newInstance(); } catch (Exception e) { throw new RuntimeException(e); @@ -10499,9 +10664,12 @@ public class PApplet extends Applet // japplemenubar.JAppleMenuBar.hide(); // } + // Tried to use this to fix the 'present' mode issue. + // Did not help, and the screenRect setup seems to work fine. + //frame.setExtendedState(Frame.MAXIMIZED_BOTH); frame.setUndecorated(true); if (backgroundColor != null) { - frame.setBackground(backgroundColor); + ((JFrame) frame).getContentPane().setBackground(backgroundColor); } // if (exclusive) { // displayDevice.setFullScreenWindow(frame); @@ -10520,6 +10688,7 @@ public class PApplet extends Applet } else { frame.pack(); } + // insufficient, places the 100x100 sketches offset strangely //frame.validate(); @@ -10597,14 +10766,18 @@ public class PApplet extends Applet } else { // if not presenting // can't do pack earlier cuz present mode don't like it // (can't go full screen with a frame after calling pack) - // frame.pack(); // get insets. get more. - Insets insets = frame.getInsets(); + // frame.pack(); + // get insets. get more. + Insets insets = frame.getInsets(); int windowW = Math.max(applet.width, MIN_WINDOW_WIDTH) + insets.left + insets.right; int windowH = Math.max(applet.height, MIN_WINDOW_HEIGHT) + insets.top + insets.bottom; + int contentW = Math.max(applet.width, MIN_WINDOW_WIDTH); + int contentH = Math.max(applet.height, MIN_WINDOW_HEIGHT); + frame.setSize(windowW, windowH); if (location != null) { @@ -10653,12 +10826,15 @@ 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; - applet.setBounds((windowW - applet.width)/2, - insets.top + (usableWindowH - applet.height)/2, +// int usableWindowH = windowH - insets.top - insets.bottom; +// applet.setBounds((windowW - applet.width)/2, +// insets.top + (usableWindowH - applet.height)/2, +// applet.width, applet.height); + applet.setBounds((contentW - applet.width)/2, + (contentH - applet.height)/2, applet.width, applet.height); if (external) { @@ -10679,6 +10855,21 @@ public class PApplet extends Applet // all set for rockin if (applet.displayable()) { frame.setVisible(true); + + // Linux doesn't deal with insets the same way. We get fake insets + // earlier, and then the window manager will slap its own insets + // onto things once the frame is realized on the screen. Awzm. + if (platform == LINUX) { + Insets irlInsets = frame.getInsets(); + if (!irlInsets.equals(insets)) { + insets = irlInsets; + windowW = Math.max(applet.width, MIN_WINDOW_WIDTH) + + insets.left + insets.right; + windowH = Math.max(applet.height, MIN_WINDOW_HEIGHT) + + insets.top + insets.bottom; + frame.setSize(windowW, windowH); + } + } } } diff --git a/core/src/processing/core/PConstants.java b/core/src/processing/core/PConstants.java index f4030bb7e..162f936b8 100644 --- a/core/src/processing/core/PConstants.java +++ b/core/src/processing/core/PConstants.java @@ -92,10 +92,13 @@ public interface PConstants { static public final int VERTEX = 0; static public final int BEZIER_VERTEX = 1; - static public final int QUAD_BEZIER_VERTEX = 2; + static public final int QUADRATIC_VERTEX = 2; static public final int CURVE_VERTEX = 3; static public final int BREAK = 4; + @Deprecated + static public final int QUAD_BEZIER_VERTEX = 2; // should not have been exposed + // useful goodness /** diff --git a/core/src/processing/core/PFont.java b/core/src/processing/core/PFont.java index 6060fcfb9..cba1bf4d0 100644 --- a/core/src/processing/core/PFont.java +++ b/core/src/processing/core/PFont.java @@ -24,6 +24,9 @@ package processing.core; import java.awt.*; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.PathIterator; import java.awt.image.*; import java.io.*; import java.util.Arrays; @@ -683,6 +686,95 @@ public class PFont implements PConstants { } + public PShape getShape(char ch) { + return getShape(ch, 0); + } + + + public PShape getShape(char ch, float detail) { + Font font = (Font) getNative(); + if (font == null) { + throw new IllegalArgumentException("getShape() only works on fonts loaded with createFont()"); + } + + PShape s = new PShape(PShape.PATH); + + // six element array received from the Java2D path iterator + float[] iterPoints = new float[6]; + // array passed to createGylphVector + char[] textArray = new char[] { ch }; + + //Graphics2D graphics = (Graphics2D) this.getGraphics(); + //FontRenderContext frc = graphics.getFontRenderContext(); + @SuppressWarnings("deprecation") + FontRenderContext frc = + Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext(); + GlyphVector gv = font.createGlyphVector(frc, textArray); + Shape shp = gv.getOutline(); + // make everything into moveto and lineto + PathIterator iter = (detail == 0) ? + shp.getPathIterator(null) : // maintain curves + shp.getPathIterator(null, detail); // convert to line segments + + int contours = 0; + //boolean outer = true; +// boolean contour = false; + while (!iter.isDone()) { + int type = iter.currentSegment(iterPoints); + switch (type) { + case PathIterator.SEG_MOVETO: // 1 point (2 vars) in textPoints +// System.out.println("moveto"); +// if (!contour) { + if (contours == 0) { + s.beginShape(); + } else { + s.beginContour(); +// contour = true; + } + contours++; + s.vertex(iterPoints[0], iterPoints[1]); + break; + + case PathIterator.SEG_LINETO: // 1 point +// System.out.println("lineto"); +// PApplet.println(PApplet.subset(iterPoints, 0, 2)); + s.vertex(iterPoints[0], iterPoints[1]); + break; + + case PathIterator.SEG_QUADTO: // 2 points +// System.out.println("quadto"); +// PApplet.println(PApplet.subset(iterPoints, 0, 4)); + s.quadraticVertex(iterPoints[0], iterPoints[1], + iterPoints[2], iterPoints[3]); + break; + + case PathIterator.SEG_CUBICTO: // 3 points +// System.out.println("cubicto"); +// PApplet.println(iterPoints); + s.quadraticVertex(iterPoints[0], iterPoints[1], + iterPoints[2], iterPoints[3], + iterPoints[4], iterPoints[5]); + break; + + case PathIterator.SEG_CLOSE: +// System.out.println("close"); + if (contours > 1) { +// contours--; +// if (contours == 0) { +//// s.endShape(); +// } else { + s.endContour(); + } + break; + } +// PApplet.println(iterPoints); + iter.next(); + } + s.endShape(CLOSE); + return s; + } + + ////////////////////////////////////////////////////////////// diff --git a/core/src/processing/core/PGraphics.java b/core/src/processing/core/PGraphics.java index 5edf6b826..8ec72c89d 100644 --- a/core/src/processing/core/PGraphics.java +++ b/core/src/processing/core/PGraphics.java @@ -473,6 +473,11 @@ public class PGraphics extends PImage implements PConstants { protected float backgroundR, backgroundG, backgroundB, backgroundA; protected int backgroundRi, backgroundGi, backgroundBi, backgroundAi; + + /** The current blending mode. */ + protected int blendMode; + + // ........................................................ /** @@ -944,6 +949,8 @@ public class PGraphics extends PImage implements PConstants { background(backgroundColor); } + blendMode(BLEND); + settingsInited = true; // defaultSettings() overlaps reapplySettings(), don't do both //reapplySettings = false; @@ -961,12 +968,11 @@ public class PGraphics extends PImage implements PConstants { * called before defaultSettings(), so we should be safe. */ protected void reapplySettings() { -// System.out.println("attempting reapplySettings()"); + // This might be called by allocate... So if beginDraw() has never run, + // we don't want to reapply here, we actually just need to let + // defaultSettings() get called a little from inside beginDraw(). if (!settingsInited) return; // if this is the initial setup, no need to reapply -// System.out.println(" doing reapplySettings"); -// new Exception().printStackTrace(System.out); - colorMode(colorMode, colorModeX, colorModeY, colorModeZ); if (fill) { // PApplet.println(" fill " + PApplet.hex(fillColor)); @@ -1013,6 +1019,8 @@ public class PGraphics extends PImage implements PConstants { textAlign(textAlign, textAlignY); background(backgroundColor); + blendMode(blendMode); + reapplySettings = false; } @@ -1216,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; } @@ -1827,7 +1838,15 @@ public class PGraphics extends PImage implements PConstants { * @param mode the blending mode to use */ public void blendMode(int mode) { - showMissingWarning("blendMode"); + this.blendMode = mode; + blendModeImpl(); + } + + + protected void blendModeImpl() { + if (blendMode != BLEND) { + showMissingWarning("blendMode"); + } } @@ -5539,7 +5558,7 @@ public class PGraphics extends PImage implements PConstants { * @see PGraphics#camera(float, float, float, float, float, float, float, float, float) */ public void printProjection() { - showMethodWarning("printCamera"); + showMethodWarning("printProjection"); } @@ -7619,6 +7638,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 e0095728a..62c58a659 100644 --- a/core/src/processing/core/PGraphicsJava2D.java +++ b/core/src/processing/core/PGraphicsJava2D.java @@ -52,7 +52,7 @@ import processing.data.XML; * any way shape or form. Which just means "have fun, but don't complain * if it breaks."

*/ -public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { +public class PGraphicsJava2D extends PGraphics { BufferStrategy strategy; BufferedImage bimage; VolatileImage vimage; @@ -263,13 +263,15 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { if (image == null || ((VolatileImage) image).validate(gc) == VolatileImage.IMAGE_INCOMPATIBLE) { image = gc.createCompatibleVolatileImage(width, height); g2 = (Graphics2D) image.getGraphics(); + reapplySettings = true; } } else { if (image == null) { GraphicsConfiguration gc = parent.getGraphicsConfiguration(); image = gc.createCompatibleImage(width, height); - System.out.println("created new image, type is " + image); + PApplet.debug("created new image, type is " + image); g2 = (Graphics2D) image.getGraphics(); + reapplySettings = true; } } //g2 = (Graphics2D) image.getGraphics(); @@ -305,6 +307,7 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { // image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); g2 = bimage.createGraphics(); defaultComposite = g2.getComposite(); + reapplySettings = true; } } @@ -373,7 +376,7 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { 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); @@ -691,8 +694,8 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { * @param mode the blending mode to use */ @Override - public void blendMode(final int mode) { - if (mode == BLEND) { + protected void blendModeImpl() { + if (blendMode == BLEND) { g2.setComposite(defaultComposite); } else { @@ -702,7 +705,7 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { public CompositeContext createContext(ColorModel srcColorModel, ColorModel dstColorModel, RenderingHints hints) { - return new BlendingContext(mode); + return new BlendingContext(blendMode); } }); } @@ -1244,15 +1247,25 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { // 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)) || @@ -1270,8 +1283,8 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { (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(); @@ -1297,17 +1310,17 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { class ImageCache { boolean tinted; int tintedColor; - int tintedPixels[]; // one row of tinted pixels + int[] tintedTemp; // one row of tinted pixels BufferedImage image; - 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 @@ -1315,29 +1328,41 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { * 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; @@ -1348,12 +1373,15 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { 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 }; @@ -1361,16 +1389,17 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { // 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; @@ -1382,7 +1411,7 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { 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); @@ -1394,7 +1423,7 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { 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) | @@ -1404,11 +1433,11 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { 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... @@ -1417,7 +1446,17 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { // 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; @@ -2288,18 +2327,32 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { protected WritableRaster getRaster() { + WritableRaster raster = null; if (primarySurface) { // 'offscreen' will probably be removed in the next release if (useOffscreen) { - return ((BufferedImage) offscreen).getRaster(); - } - // when possible, we'll try VolatileImage - if (image instanceof VolatileImage) { - return ((VolatileImage) image).getSnapshot().getRaster(); + raster = ((BufferedImage) offscreen).getRaster(); + } else if (image instanceof VolatileImage) { + // when possible, we'll try VolatileImage + raster = ((VolatileImage) image).getSnapshot().getRaster(); } } - return ((BufferedImage) image).getRaster(); - //((BufferedImage) (useOffscreen && primarySurface ? offscreen : image)).getRaster(); + if (raster == null) { + raster = ((BufferedImage) image).getRaster(); + } + + // On Raspberry Pi (and perhaps other platforms, the color buffer won't + // necessarily be the int array that we'd like. Need to convert it here. + // Not that this would probably mean getRaster() would need to work more + // like loadRaster/updateRaster because the pixels will need to be + // temporarily moved to (and later from) a buffer that's understood by + // the rest of the Processing source. + // https://github.com/processing/processing/issues/2010 + if (raster.getTransferType() != DataBuffer.TYPE_INT) { + System.err.println("See https://github.com/processing/processing/issues/2010"); + throw new RuntimeException("Pixel operations are not supported on this device."); + } + return raster; } @@ -2308,10 +2361,19 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { if ((pixels == null) || (pixels.length != width * height)) { pixels = new int[width * height]; } - //((BufferedImage) image).getRGB(0, 0, width, height, pixels, 0, width); + + 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(); - getRaster().getDataElements(0, 0, width, height, pixels); } @@ -2377,8 +2439,12 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { 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]; } @@ -2404,6 +2470,10 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { 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 @@ -2414,7 +2484,15 @@ public class PGraphicsJava2D extends PGraphics /*PGraphics2D*/ { 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/PImage.java b/core/src/processing/core/PImage.java index 153c5fe47..e3994c91e 100644 --- a/core/src/processing/core/PImage.java +++ b/core/src/processing/core/PImage.java @@ -54,7 +54,7 @@ import javax.imageio.metadata.*; * * @webref image * @usage Web & Application - * @instanceName img any object of type PImage + * @instanceName pimg any object of type PImage * @see PApplet#loadImage(String) * @see PApplet#imageMode(int) * @see PApplet#createImage(int, int, int) 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 e5eaa28fd..8420f72f6 100644 --- a/core/src/processing/core/PShape.java +++ b/core/src/processing/core/PShape.java @@ -166,6 +166,10 @@ public class PShape implements PConstants { protected int emissiveColor; 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; @@ -510,10 +514,12 @@ public class PShape implements PConstants { image = null; } + // TODO unapproved protected void solid(boolean solid) { } + /** * @webref shape:vertex * @brief Starts a new contour @@ -535,8 +541,18 @@ public class PShape implements PConstants { return; } openContour = true; + beginContourImpl(); } + + protected void beginContourImpl() { + if (vertexCodes.length == vertexCodeCount) { + vertexCodes = PApplet.expand(vertexCodes); + } + vertexCodes[vertexCodeCount++] = BREAK; + } + + /** * @webref shape:vertex * @brief Ends a contour @@ -557,28 +573,64 @@ public class PShape implements PConstants { PGraphics.showWarning("Need to call beginContour() first."); return; } + endContourImpl(); openContour = false; } - public void vertex(float x, float y) { + + protected void endContourImpl() { } + + public void vertex(float x, float y) { + if (vertices == null) { + vertices = new float[10][2]; + } else if (vertices.length == vertexCount) { + vertices = (float[][]) PApplet.expand(vertices); + } + vertices[vertexCount++] = new float[] { x, y }; + + if (vertexCodes == null) { + vertexCodes = new int[10]; + } else if (vertexCodes.length == vertexCodeCount) { + vertexCodes = PApplet.expand(vertexCodes); + } + vertexCodes[vertexCodeCount++] = VERTEX; + + if (x > width) { + width = x; + } + if (y > height) { + height = y; + } + } + + public void vertex(float x, float y, float u, float v) { } + public void vertex(float x, float y, float z) { } + public void vertex(float x, float y, float z, float u, float v) { } + public void normal(float nx, float ny, float nz) { } + /** + * @webref pshape:method + * @brief Starts the creation of a new PShape + * @see PApplet#endShape() + */ public void beginShape() { beginShape(POLYGON); } + public void beginShape(int kind) { this.kind = kind; openShape = true; @@ -587,12 +639,13 @@ public class PShape implements PConstants { /** * @webref pshape:method * @brief Finishes the creation of a new PShape - * @see PApplet#createShape() + * @see PApplet#beginShape() */ public void endShape() { endShape(OPEN); } + public void endShape(int mode) { if (family == GROUP) { PGraphics.showWarning("Cannot end GROUP shape"); @@ -1073,24 +1126,70 @@ public class PShape implements PConstants { public void bezierDetail(int detail) { } + public void bezierVertex(float x2, float y2, float x3, float y3, float x4, float y4) { + if (vertices == null) { + vertices = new float[10][]; + } else if (vertexCount + 2 >= vertices.length) { + vertices = (float[][]) PApplet.expand(vertices); + } + vertices[vertexCount++] = new float[] { x2, y2 }; + vertices[vertexCount++] = new float[] { x3, y3 }; + vertices[vertexCount++] = new float[] { x4, y4 }; + + // vertexCodes must be allocated because a vertex() call is required + if (vertexCodes.length == vertexCodeCount) { + vertexCodes = PApplet.expand(vertexCodes); + } + vertexCodes[vertexCodeCount++] = BEZIER_VERTEX; + + if (x4 > width) { + width = x4; + } + if (y4 > height) { + height = y4; + } } + public void bezierVertex(float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4) { } + public void quadraticVertex(float cx, float cy, float x3, float y3) { + if (vertices == null) { + vertices = new float[10][]; + } else if (vertexCount + 1 >= vertices.length) { + vertices = (float[][]) PApplet.expand(vertices); + } + vertices[vertexCount++] = new float[] { cx, cy }; + vertices[vertexCount++] = new float[] { x3, y3 }; + + // vertexCodes must be allocated because a vertex() call is required + if (vertexCodes.length == vertexCodeCount) { + vertexCodes = PApplet.expand(vertexCodes); + } + vertexCodes[vertexCodeCount++] = QUADRATIC_VERTEX; + + if (x3 > width) { + width = x3; + } + if (y3 > height) { + height = y3; + } } + public void quadraticVertex(float cx, float cy, float cz, float x3, float y3, float z3) { } + /////////////////////////////////////////////////////////// // @@ -1550,23 +1649,12 @@ public class PShape implements PConstants { case VERTEX: g.vertex(vertices[index][X], vertices[index][Y]); -// cx = vertices[index][X]; -// cy = vertices[index][Y]; index++; break; - case QUAD_BEZIER_VERTEX: + case QUADRATIC_VERTEX: g.quadraticVertex(vertices[index+0][X], vertices[index+0][Y], - vertices[index+1][X], vertices[index+1][Y]); -// float x1 = vertices[index+0][X]; -// float y1 = vertices[index+0][Y]; -// float x2 = vertices[index+1][X]; -// float y2 = vertices[index+1][Y]; -// g.bezierVertex(x1 + ((cx-x1)*2/3.0f), y1 + ((cy-y1)*2/3.0f), -// x2 + ((cx-x2)*2/3.0f), y2 + ((cy-y2)*2/3.0f), -// x2, y2); -// cx = vertices[index+1][X]; -// cy = vertices[index+1][Y]; + vertices[index+1][X], vertices[index+1][Y]); index += 2; break; @@ -1574,8 +1662,6 @@ public class PShape implements PConstants { g.bezierVertex(vertices[index+0][X], vertices[index+0][Y], vertices[index+1][X], vertices[index+1][Y], vertices[index+2][X], vertices[index+2][Y]); -// cx = vertices[index+2][X]; -// cy = vertices[index+2][Y]; index += 3; break; @@ -1598,13 +1684,10 @@ public class PShape implements PConstants { case VERTEX: g.vertex(vertices[index][X], vertices[index][Y], vertices[index][Z]); -// cx = vertices[index][X]; -// cy = vertices[index][Y]; -// cz = vertices[index][Z]; index++; break; - case QUAD_BEZIER_VERTEX: + case QUADRATIC_VERTEX: g.quadraticVertex(vertices[index+0][X], vertices[index+0][Y], vertices[index+0][Z], vertices[index+1][X], vertices[index+1][Y], vertices[index+0][Z]); index += 2; @@ -1904,6 +1987,7 @@ public class PShape implements PConstants { return getVertex(index, null); } + /** * @param vec PVector to assign the data to */ @@ -1911,9 +1995,14 @@ public class PShape implements PConstants { if (vec == null) { vec = new PVector(); } - vec.x = vertices[index][X]; - vec.y = vertices[index][Y]; - vec.z = vertices[index][Z]; + float[] vert = vertices[index]; + vec.x = vert[X]; + vec.y = vert[Y]; + if (vert.length > 2) { + vec.z = vert[Z]; + } else { + vec.z = 0; // in case this isn't a new vector + } return vec; } diff --git a/core/src/processing/core/PShapeOBJ.java b/core/src/processing/core/PShapeOBJ.java index 0296bc1ba..c64031cc5 100644 --- a/core/src/processing/core/PShapeOBJ.java +++ b/core/src/processing/core/PShapeOBJ.java @@ -1,6 +1,7 @@ package processing.core; import java.io.BufferedReader; +import java.io.File; import java.util.ArrayList; import java.util.Hashtable; @@ -22,17 +23,21 @@ public class PShapeOBJ extends PShape { * Initializes a new OBJ Object with the given filename. */ public PShapeOBJ(PApplet parent, String filename) { - this(parent, parent.createReader(filename)); + this(parent, parent.createReader(filename), getBasePath(parent, filename)); } - public PShapeOBJ(PApplet parent, BufferedReader reader) { + this(parent, reader, ""); + } + + public PShapeOBJ(PApplet parent, BufferedReader reader, String basePath) { ArrayList faces = new ArrayList(); ArrayList materials = new ArrayList(); ArrayList coords = new ArrayList(); ArrayList normals = new ArrayList(); ArrayList texcoords = new ArrayList(); - parseOBJ(parent, reader, faces, materials, coords, normals, texcoords); + parseOBJ(parent, basePath, reader, + faces, materials, coords, normals, texcoords); // The OBJ geometry is stored with each face in a separate child shape. parent = null; @@ -147,7 +152,7 @@ public class PShapeOBJ extends PShape { } - static protected void parseOBJ(PApplet parent, + static protected void parseOBJ(PApplet parent, String path, BufferedReader reader, ArrayList faces, ArrayList materials, @@ -218,9 +223,14 @@ public class PShapeOBJ extends PShape { // Object name is ignored, for now. } else if (parts[0].equals("mtllib")) { if (parts[1] != null) { - BufferedReader mreader = parent.createReader(parts[1]); + String fn = parts[1]; + if (fn.indexOf(File.separator) == -1 && !path.equals("")) { + // Relative file name, adding the base path. + fn = path + File.separator + fn; + } + BufferedReader mreader = parent.createReader(fn); if (mreader != null) { - parseMTL(parent, mreader, materials, mtlTable); + parseMTL(parent, path, mreader, materials, mtlTable); } } } else if (parts[0].equals("g")) { @@ -308,7 +318,7 @@ public class PShapeOBJ extends PShape { } - static protected void parseMTL(PApplet parent, + static protected void parseMTL(PApplet parent, String path, BufferedReader reader, ArrayList materials, Hashtable materialsHash) { @@ -330,6 +340,10 @@ public class PShapeOBJ extends PShape { } else if (parts[0].equals("map_Kd") && parts.length > 1) { // Loading texture map. String texname = parts[1]; + if (texname.indexOf(File.separator) == -1 && !path.equals("")) { + // Relative file name, adding the base path. + texname = path + File.separator + texname; + } currentMtl.kdMap = parent.loadImage(texname); } else if (parts[0].equals("Ka") && parts.length > 3) { // The ambient color of the material @@ -395,6 +409,18 @@ public class PShapeOBJ extends PShape { } + static protected String getBasePath(PApplet parent, String filename) { + // Obtaining the path + File file = new File(parent.dataPath(filename)); + if (!file.exists()) { + file = parent.sketchFile(filename); + } + String absolutePath = file.getAbsolutePath(); + return absolutePath.substring(0, + absolutePath.lastIndexOf(File.separator)); + } + + // Stores a material defined in an MTL file. static protected class OBJMaterial { String name; diff --git a/core/src/processing/core/PShapeSVG.java b/core/src/processing/core/PShapeSVG.java index 671114ee5..1be0b30d6 100644 --- a/core/src/processing/core/PShapeSVG.java +++ b/core/src/processing/core/PShapeSVG.java @@ -915,7 +915,7 @@ public class PShapeSVG extends PShape { float x2, float y2) { //System.out.println("quadto: " + x1 + "," + y1 + " " + cx + "," + cy + " " + x2 + "," + y2); // parsePathCode(BEZIER_VERTEX); - parsePathCode(QUAD_BEZIER_VERTEX); + parsePathCode(QUADRATIC_VERTEX); // x1/y1 already covered by last moveto, lineto, or curveto parsePathVertex(cx, cy); parsePathVertex(x2, y2); diff --git a/core/src/processing/core/PVector.java b/core/src/processing/core/PVector.java index 725c208b1..ca25eb1db 100644 --- a/core/src/processing/core/PVector.java +++ b/core/src/processing/core/PVector.java @@ -926,9 +926,9 @@ public class PVector implements Serializable { static public float angleBetween(PVector v1, PVector v2) { // We get NaN if we pass in a zero vector which can cause problems - // Zero seems like a reasonable angle between a (0,0) vector and something else - if (v1.x == 0 && v1.y == 0) return 0.0f; - if (v2.x == 0 && v2.y == 0) return 0.0f; + // Zero seems like a reasonable angle between a (0,0,0) vector and something else + if (v1.x == 0 && v1.y == 0 && v1.z == 0 ) return 0.0f; + if (v2.x == 0 && v2.y == 0 && v2.z == 0 ) return 0.0f; double dot = v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; double v1mag = Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z); @@ -994,4 +994,4 @@ public class PVector implements Serializable { result = 31 * result + Float.floatToIntBits(z); return result; } -} \ No newline at end of file +} diff --git a/core/src/processing/data/FloatDict.java b/core/src/processing/data/FloatDict.java index 7207a179a..794ca2713 100644 --- a/core/src/processing/data/FloatDict.java +++ b/core/src/processing/data/FloatDict.java @@ -427,6 +427,7 @@ public class FloatDict { * @webref floatlist:method * @brief Return the largest value */ + // The index of the entry that has the max value. Reference above is incorrect. public int maxIndex() { checkMinMax("maxIndex"); // Will still return NaN if there is 1 or more entries, and they're all NaN @@ -453,12 +454,14 @@ public class FloatDict { } + /** The key for a max value. */ public String maxKey() { checkMinMax("maxKey"); return keys[maxIndex()]; } + /** The max value. */ public float maxValue() { checkMinMax("maxValue"); return values[maxIndex()]; @@ -515,7 +518,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]; @@ -680,6 +683,25 @@ public class FloatDict { } + /** + * Sum all of the values in this dictionary, then return a new FloatDict of + * each key, divided by the total sum. The total for all values will be ~1.0. + * @return a Dict with the original keys, mapped to their pct of the total + */ + public FloatDict getPercent() { + double sum = 0; + for (float value : valueArray()) { + sum += value; + } + FloatDict outgoing = new FloatDict(); + for (int i = 0; i < size(); i++) { + double percent = value(i) / sum; + outgoing.set(key(i), (float) percent); + } + return outgoing; + } + + /** Returns a duplicate copy of this object. */ public FloatDict copy() { FloatDict outgoing = new FloatDict(count); diff --git a/core/src/processing/data/FloatList.java b/core/src/processing/data/FloatList.java index e386c9303..9921cb326 100644 --- a/core/src/processing/data/FloatList.java +++ b/core/src/processing/data/FloatList.java @@ -16,6 +16,8 @@ import processing.core.PApplet; * a sorted copy, use list.copy().sort(). * * @webref data:composite + * @see IntList + * @see StringList */ public class FloatList implements Iterable { int count; @@ -299,7 +301,7 @@ public class FloatList implements Iterable { if (index < 0) { throw new IllegalArgumentException("insert() index cannot be negative: it was " + index); } - if (index >= values.length) { + if (index >= data.length) { throw new IllegalArgumentException("insert() index " + index + " is past the end of this list"); } @@ -531,6 +533,15 @@ public class FloatList implements Iterable { } + public float sum() { + double outgoing = 0; + for (int i = 0; i < count; i++) { + outgoing += data[i]; + } + return (float) outgoing; + } + + /** * Sorts the array in place. * @@ -575,17 +586,18 @@ public class FloatList implements Iterable { // } - public void subset(int start) { - subset(start, count - start); - } +// public void subset(int start) { +// subset(start, count - start); +// } - public void subset(int start, int num) { - for (int i = 0; i < num; i++) { - data[i] = data[i+start]; - } - count = num; - } +// public void subset(int start, int num) { +// for (int i = 0; i < num; i++) { +// data[i] = data[i+start]; +// } +// count = num; +// } + /** * @webref floatlist:method @@ -705,6 +717,52 @@ public class FloatList implements Iterable { } + /** + * Returns a normalized version of this array. Called getPercent() for + * consistency with the Dict classes. It's a getter method because it needs + * to returns a new list (because IntList/Dict can't do percentages or + * normalization in place on int values). + */ + public FloatList getPercent() { + double sum = 0; + for (float value : array()) { + sum += value; + } + FloatList outgoing = new FloatList(count); + for (int i = 0; i < count; i++) { + double percent = data[i] / sum; + outgoing.set(i, (float) percent); + } + return outgoing; + } + + + public FloatList getSubset(int start) { + return getSubset(start, count - start); + } + + + public FloatList getSubset(int start, int num) { + float[] subset = new float[num]; + System.arraycopy(data, start, subset, 0, num); + return new FloatList(subset); + } + + + public String join(String separator) { + if (count == 0) { + return ""; + } + StringBuilder sb = new StringBuilder(); + sb.append(data[0]); + for (int i = 1; i < count; i++) { + sb.append(separator); + sb.append(data[i]); + } + return sb.toString(); + } + + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -718,4 +776,4 @@ public class FloatList implements Iterable { sb.append(" ]"); return sb.toString(); } -} \ No newline at end of file +} diff --git a/core/src/processing/data/IntDict.java b/core/src/processing/data/IntDict.java index 23e4fc48f..44efea215 100644 --- a/core/src/processing/data/IntDict.java +++ b/core/src/processing/data/IntDict.java @@ -88,6 +88,7 @@ public class IntDict { if (pieces.length == 2) { keys[count] = pieces[0]; values[count] = PApplet.parseInt(pieces[1]); + indices.put(pieces[0], count); count++; } } @@ -470,7 +471,7 @@ public class IntDict { } - // return the key for the maximum value + // return the key corresponding to the maximum value public String maxKey() { checkMinMax("maxKey"); return keys[maxIndex()]; @@ -526,7 +527,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]; @@ -621,6 +622,25 @@ public class IntDict { } + /** + * Sum all of the values in this dictionary, then return a new FloatDict of + * each key, divided by the total sum. The total for all values will be ~1.0. + * @return a Dict with the original keys, mapped to their pct of the total + */ + public FloatDict getPercent() { + double sum = 0; + for (int value : valueArray()) { + sum += value; + } + FloatDict outgoing = new FloatDict(); + for (int i = 0; i < size(); i++) { + double percent = value(i) / sum; + outgoing.set(key(i), (float) percent); + } + return outgoing; + } + + /** Returns a duplicate copy of this object. */ public IntDict copy() { IntDict outgoing = new IntDict(count); diff --git a/core/src/processing/data/IntList.java b/core/src/processing/data/IntList.java index 1c1e81569..1b8860fcc 100644 --- a/core/src/processing/data/IntList.java +++ b/core/src/processing/data/IntList.java @@ -21,6 +21,8 @@ import processing.core.PApplet; * a sorted copy, use list.copy().sort(). * * @webref data:composite + * @see FloatList + * @see StringList */ public class IntList implements Iterable { protected int count; @@ -58,6 +60,21 @@ public class IntList implements Iterable { } + static public IntList fromRange(int stop) { + return fromRange(0, stop); + } + + + static public IntList fromRange(int start, int stop) { + int count = stop - start; + IntList newbie = new IntList(count); + for (int i = 0; i < count; i++) { + newbie.set(i, start+i); + } + return newbie; + } + + /** * Improve efficiency by removing allocated but unused entries from the * internal array used to store the data. Set to private, though it could @@ -74,7 +91,7 @@ public class IntList implements Iterable { /** * Get the length of the list. * - * @webref floatlist:method + * @webref intlist:method * @brief Get the length of the list */ public int size() { @@ -98,7 +115,7 @@ public class IntList implements Iterable { /** * Remove all entries from the list. * - * @webref floatlist:method + * @webref intlist:method * @brief Remove all entries from the list */ public void clear() { @@ -109,7 +126,7 @@ public class IntList implements Iterable { /** * Get an entry at a particular index. * - * @webref floatlist:method + * @webref intlist:method * @brief Get an entry at a particular index */ public int get(int index) { @@ -122,7 +139,7 @@ public class IntList implements Iterable { * the list, it'll expand the list to accommodate, and fill the intermediate * entries with 0s. * - * @webref floatlist:method + * @webref intlist:method * @brief Set the entry at a particular index */ public void set(int index, int what) { @@ -140,7 +157,7 @@ public class IntList implements Iterable { /** * Remove an element from the specified index * - * @webref floatlist:method + * @webref intlist:method * @brief Remove an element from the specified index */ public int remove(int index) { @@ -193,7 +210,7 @@ public class IntList implements Iterable { /** * Add a new entry to the list. * - * @webref floatlist:method + * @webref intlist:method * @brief Add a new entry to the list */ public void append(int value) { @@ -253,7 +270,7 @@ public class IntList implements Iterable { if (index < 0) { throw new IllegalArgumentException("insert() index cannot be negative: it was " + index); } - if (index >= values.length) { + if (index >= data.length) { throw new IllegalArgumentException("insert() index " + index + " is past the end of this list"); } @@ -355,7 +372,7 @@ public class IntList implements Iterable { // } /** - * @webref floatlist:method + * @webref intlist:method * @brief Check if a number is a part of the list */ public boolean hasValue(int value) { @@ -372,15 +389,18 @@ public class IntList implements Iterable { } /** - * @webref floatlist:method + * @webref intlist:method * @brief Add one to a value */ public void increment(int index) { + if (count <= index) { + resize(index + 1); + } data[index]++; } /** - * @webref floatlist:method + * @webref intlist:method * @brief Add to a value */ public void add(int index, int amount) { @@ -388,7 +408,7 @@ public class IntList implements Iterable { } /** - * @webref floatlist:method + * @webref intlist:method * @brief Subtract from a value */ public void sub(int index, int amount) { @@ -396,7 +416,7 @@ public class IntList implements Iterable { } /** - * @webref floatlist:method + * @webref intlist:method * @brief Multiply a value */ public void mult(int index, int amount) { @@ -404,7 +424,7 @@ public class IntList implements Iterable { } /** - * @webref floatlist:method + * @webref intlist:method * @brief Divide a value */ public void div(int index, int amount) { @@ -423,7 +443,7 @@ public class IntList implements Iterable { /** - * @webref floatlist:method + * @webref intlist:method * @brief Return the smallest value */ public int min() { @@ -453,7 +473,7 @@ public class IntList implements Iterable { /** - * @webref floatlist:method + * @webref intlist:method * @brief Return the largest value */ public int max() { @@ -482,10 +502,19 @@ public class IntList implements Iterable { } + public int sum() { + int outgoing = 0; + for (int i = 0; i < count; i++) { + outgoing += data[i]; + } + return outgoing; + } + + /** * Sorts the array in place. * - * @webref floatlist:method + * @webref intlist:method * @brief Sorts the array, lowest to highest */ public void sort() { @@ -496,7 +525,7 @@ public class IntList implements Iterable { /** * Reverse sort, orders values from highest to lowest. * - * @webref floatlist:method + * @webref intlist:method * @brief Reverse sort, orders values from highest to lowest */ public void sortReverse() { @@ -539,7 +568,7 @@ public class IntList implements Iterable { // } /** - * @webref floatlist:method + * @webref intlist:method * @brief Reverse sort, orders values by first digit */ public void reverse() { @@ -557,7 +586,7 @@ public class IntList implements Iterable { * Randomize the order of the list elements. Note that this does not * obey the randomSeed() function in PApplet. * - * @webref floatlist:method + * @webref intlist:method * @brief Randomize the order of the list elements */ public void shuffle() { @@ -632,7 +661,7 @@ public class IntList implements Iterable { * Create a new array with a copy of all the values. * * @return an array sized by the length of the list with each of the values. - * @webref floatlist:method + * @webref intlist:method * @brief Create a new array with a copy of all the values */ public int[] array() { @@ -696,17 +725,49 @@ public class IntList implements Iterable { // } + /** + * Returns a normalized version of this array. Called getPercent() for + * consistency with the Dict classes. It's a getter method because it needs + * to returns a new list (because IntList/Dict can't do percentages or + * normalization in place on int values). + */ + public FloatList getPercent() { + double sum = 0; + for (float value : array()) { + sum += value; + } + FloatList outgoing = new FloatList(count); + for (int i = 0; i < count; i++) { + double percent = data[i] / sum; + outgoing.set(i, (float) percent); + } + return outgoing; + } + + public IntList getSubset(int start) { return getSubset(start, count - start); } public IntList getSubset(int start, int num) { - IntList outgoing = new IntList(num); - for (int i = 0; i < num; i++) { - System.arraycopy(data, start, outgoing.data, 0, num); + int[] subset = new int[num]; + System.arraycopy(data, start, subset, 0, num); + return new IntList(subset); + } + + + public String join(String separator) { + if (count == 0) { + return ""; } - return outgoing; + StringBuilder sb = new StringBuilder(); + sb.append(data[0]); + for (int i = 1; i < count; i++) { + sb.append(separator); + sb.append(data[i]); + } + return sb.toString(); } @@ -723,4 +784,4 @@ public class IntList implements Iterable { sb.append(" ]"); return sb.toString(); } -} \ No newline at end of file +} diff --git a/core/src/processing/data/JSONArray.java b/core/src/processing/data/JSONArray.java index 75d39eba8..0e665c805 100644 --- a/core/src/processing/data/JSONArray.java +++ b/core/src/processing/data/JSONArray.java @@ -294,6 +294,20 @@ public class JSONArray { } + /** + * Get the optional string associated with an index. + * The defaultValue is returned if the key is not found. + * + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return A String value. + */ + public String getString(int index, String defaultValue) { + Object object = this.opt(index); + return JSONObject.NULL.equals(object) ? defaultValue : object.toString(); + } + + /** * Get the int value associated with an index. * @@ -318,6 +332,23 @@ public class JSONArray { } + /** + * Get the optional int value associated with an index. + * The defaultValue is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return The value. + */ + public int getInt(int index, int defaultValue) { + try { + return getInt(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** * Get the long value associated with an index. * @@ -338,10 +369,27 @@ public class JSONArray { } + /** + * Get the optional long value associated with an index. + * The defaultValue is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return The value. + */ + public long getLong(int index, long defaultValue) { + try { + return this.getLong(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** * Get a value from an index as a float. JSON uses 'double' values * internally, so this is simply getDouble() cast to a float. - * + * * @webref jsonarray:method * @brief Gets the float value associated with an index * @param index must be between 0 and length() - 1 @@ -354,6 +402,15 @@ public class JSONArray { } + public float getFloat(int index, float defaultValue) { + try { + return getFloat(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** * Get the double value associated with an index. * @@ -374,6 +431,24 @@ public class JSONArray { } + /** + * Get the optional double value associated with an index. + * The defaultValue is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * + * @param index subscript + * @param defaultValue The default value. + * @return The value. + */ + public double getDouble(int index, double defaultValue) { + try { + return this.getDouble(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** * Get the boolean value associated with an index. * The string values "true" and "false" are converted to boolean. @@ -403,9 +478,27 @@ public class JSONArray { } + /** + * Get the optional boolean value associated with an index. + * It returns the defaultValue if there is no value at that index or if + * it is not a Boolean or the String "true" or "false" (case insensitive). + * + * @param index The index must be between 0 and length() - 1. + * @param defaultValue A boolean default. + * @return The truth. + */ + public boolean getBoolean(int index, boolean defaultValue) { + try { + return getBoolean(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** * Get the JSONArray associated with an index. - * + * * @webref jsonobject:method * @brief Gets the JSONArray associated with an index value * @param index must be between 0 and length() - 1 @@ -425,9 +518,18 @@ public class JSONArray { } + public JSONArray getJSONArray(int index, JSONArray defaultValue) { + try { + return getJSONArray(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** * Get the JSONObject associated with an index. - * + * * @webref jsonobject:method * @brief Gets the JSONObject associated with an index value * @param index the index value of the object to get @@ -447,9 +549,18 @@ public class JSONArray { } - /** - * Get this entire array as a String array. - * + public JSONObject getJSONObject(int index, JSONObject defaultValue) { + try { + return getJSONObject(index); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get this entire array as a String array. + * * @webref jsonarray:method * @brief Gets the entire array as an array of Strings * @see JSONArray#getIntArray() @@ -463,9 +574,9 @@ public class JSONArray { } - /** - * Get this entire array as an int array. Everything must be an int. - * + /** + * Get this entire array as an int array. Everything must be an int. + * * @webref jsonarray:method * @brief Gets the entire array as array of ints * @see JSONArray#getStringArray() @@ -533,24 +644,6 @@ public class JSONArray { // // // /** -// * Get the optional boolean value associated with an index. -// * It returns the defaultValue if there is no value at that index or if -// * it is not a Boolean or the String "true" or "false" (case insensitive). -// * -// * @param index The index must be between 0 and length() - 1. -// * @param defaultValue A boolean default. -// * @return The truth. -// */ -// public boolean optBoolean(int index, boolean defaultValue) { -// try { -// return this.getBoolean(index); -// } catch (Exception e) { -// return defaultValue; -// } -// } -// -// -// /** // * Get the optional double value associated with an index. // * NaN is returned if there is no value for the index, // * or if the value is not a number and cannot be converted to a number. @@ -564,24 +657,6 @@ public class JSONArray { // // // /** -// * Get the optional double value associated with an index. -// * The defaultValue is returned if there is no value for the index, -// * or if the value is not a number and cannot be converted to a number. -// * -// * @param index subscript -// * @param defaultValue The default value. -// * @return The value. -// */ -// public double optDouble(int index, double defaultValue) { -// try { -// return this.getDouble(index); -// } catch (Exception e) { -// return defaultValue; -// } -// } -// -// -// /** // * Get the optional int value associated with an index. // * Zero is returned if there is no value for the index, // * or if the value is not a number and cannot be converted to a number. @@ -595,49 +670,6 @@ public class JSONArray { // // // /** -// * Get the optional int value associated with an index. -// * The defaultValue is returned if there is no value for the index, -// * or if the value is not a number and cannot be converted to a number. -// * @param index The index must be between 0 and length() - 1. -// * @param defaultValue The default value. -// * @return The value. -// */ -// public int optInt(int index, int defaultValue) { -// try { -// return this.getInt(index); -// } catch (Exception e) { -// return defaultValue; -// } -// } -// -// -// /** -// * Get the optional JSONArray associated with an index. -// * @param index subscript -// * @return A JSONArray value, or null if the index has no value, -// * or if the value is not a JSONArray. -// */ -// public JSONArray optJSONArray(int index) { -// Object o = this.opt(index); -// return o instanceof JSONArray ? (JSONArray)o : null; -// } -// -// -// /** -// * Get the optional JSONObject associated with an index. -// * Null is returned if the key is not found, or null if the index has -// * no value, or if the value is not a JSONObject. -// * -// * @param index The index must be between 0 and length() - 1. -// * @return A JSONObject value. -// */ -// public JSON optJSONObject(int index) { -// Object o = this.opt(index); -// return o instanceof JSON ? (JSON)o : null; -// } -// -// -// /** // * Get the optional long value associated with an index. // * Zero is returned if there is no value for the index, // * or if the value is not a number and cannot be converted to a number. @@ -651,23 +683,6 @@ public class JSONArray { // // // /** -// * Get the optional long value associated with an index. -// * The defaultValue is returned if there is no value for the index, -// * or if the value is not a number and cannot be converted to a number. -// * @param index The index must be between 0 and length() - 1. -// * @param defaultValue The default value. -// * @return The value. -// */ -// public long optLong(int index, long defaultValue) { -// try { -// return this.getLong(index); -// } catch (Exception e) { -// return defaultValue; -// } -// } -// -// -// /** // * Get the optional string value associated with an index. It returns an // * empty string if there is no value at that index. If the value // * is not a string and is not null, then it is coverted to a string. @@ -677,22 +692,6 @@ public class JSONArray { // */ // public String optString(int index) { // return this.optString(index, ""); -// } -// -// -// /** -// * Get the optional string associated with an index. -// * The defaultValue is returned if the key is not found. -// * -// * @param index The index must be between 0 and length() - 1. -// * @param defaultValue The default value. -// * @return A String value. -// */ -// public String optString(int index, String defaultValue) { -// Object object = this.opt(index); -// return JSON.NULL.equals(object) -// ? defaultValue -// : object.toString(); // } @@ -1052,15 +1051,14 @@ public class JSONArray { * @param index must be between 0 and length() - 1 * @return true if the value at the index is null, or if there is no value. */ - // TODO not sure on this one - protected boolean isNull(int index) { + public boolean isNull(int index) { return JSONObject.NULL.equals(this.opt(index)); } /** * Remove an index and close the hole. - * + * * @webref jsonarray:method * @brief Removes an element * @param index the index value of the element to be removed @@ -1145,6 +1143,7 @@ public class JSONArray { } } + /** * Write the contents of the JSONArray as JSON text to a writer. For * compactness, no whitespace is added. @@ -1157,6 +1156,7 @@ public class JSONArray { return this.write(writer, -1, 0); } + /** * Write the contents of the JSONArray as JSON text to a writer. For * compactness, no whitespace is added. @@ -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 156938575..165b099cd 100644 --- a/core/src/processing/data/JSONObject.java +++ b/core/src/processing/data/JSONObject.java @@ -573,6 +573,20 @@ public class JSONObject { } + /** + * Get an optional string associated with a key. + * It returns the defaultValue if there is no such key. + * + * @param key A key string. + * @param defaultValue The default. + * @return A string which is the value. + */ + public String getString(String key, String defaultValue) { + Object object = this.opt(key); + return NULL.equals(object) ? defaultValue : object.toString(); + } + + /** * Gets the int value associated with a key * @@ -598,6 +612,25 @@ public class JSONObject { } + /** + * Get an optional int value associated with a key, + * or the default if there is no such key or if the value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + */ + public int getInt(String key, int defaultValue) { + try { + return this.getInt(key); + } catch (Exception e) { + return defaultValue; + } + } + + /** * Get the long value associated with a key. * @@ -617,6 +650,26 @@ public class JSONObject { } } + + /** + * Get an optional long value associated with a key, + * or the default if there is no such key or if the value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + */ + public long getLong(String key, long defaultValue) { + try { + return this.getLong(key); + } catch (Exception e) { + return defaultValue; + } + } + + /** * @webref jsonobject:method * @brief Gets the float value associated with a key @@ -630,6 +683,15 @@ public class JSONObject { } + public float getFloat(String key, float defaultValue) { + try { + return getFloat(key); + } catch (Exception e) { + return defaultValue; + } + } + + /** * Get the double value associated with a key. * @param key A key string. @@ -649,6 +711,25 @@ public class JSONObject { } + /** + * Get an optional double associated with a key, or the + * defaultValue if there is no such key or if its value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + */ + public double getDouble(String key, double defaultValue) { + try { + return this.getDouble(key); + } catch (Exception e) { + return defaultValue; + } + } + + /** * Get the boolean value associated with a key. * @@ -676,6 +757,24 @@ public class JSONObject { } + /** + * Get an optional boolean associated with a key. + * It returns the defaultValue if there is no such key, or if it is not + * a Boolean or the String "true" or "false" (case insensitive). + * + * @param key A key string. + * @param defaultValue The default. + * @return The truth. + */ + public boolean getBoolean(String key, boolean defaultValue) { + try { + return this.getBoolean(key); + } catch (Exception e) { + return defaultValue; + } + } + + /** * Get the JSONArray value associated with a key. * @@ -807,7 +906,7 @@ public class JSONObject { * @return true if there is no value associated with the key or if * the value is the JSONObject.NULL object. */ - protected boolean isNull(String key) { + public boolean isNull(String key) { return JSONObject.NULL.equals(this.opt(key)); } @@ -910,24 +1009,6 @@ public class JSONObject { // } -// /** -// * Get an optional boolean associated with a key. -// * It returns the defaultValue if there is no such key, or if it is not -// * a Boolean or the String "true" or "false" (case insensitive). -// * -// * @param key A key string. -// * @param defaultValue The default. -// * @return The truth. -// */ -// private boolean optBoolean(String key, boolean defaultValue) { -// try { -// return this.getBoolean(key); -// } catch (Exception e) { -// return defaultValue; -// } -// } - - // /** // * Get an optional double associated with a key, // * or NaN if there is no such key or if its value is not a number. @@ -942,25 +1023,6 @@ public class JSONObject { // } -// /** -// * Get an optional double associated with a key, or the -// * defaultValue if there is no such key or if its value is not a number. -// * If the value is a string, an attempt will be made to evaluate it as -// * a number. -// * -// * @param key A key string. -// * @param defaultValue The default. -// * @return An object which is the value. -// */ -// private double optDouble(String key, double defaultValue) { -// try { -// return this.getDouble(key); -// } catch (Exception e) { -// return defaultValue; -// } -// } - - // /** // * Get an optional int value associated with a key, // * or zero if there is no such key or if the value is not a number. @@ -975,25 +1037,6 @@ public class JSONObject { // } -// /** -// * Get an optional int value associated with a key, -// * or the default if there is no such key or if the value is not a number. -// * If the value is a string, an attempt will be made to evaluate it as -// * a number. -// * -// * @param key A key string. -// * @param defaultValue The default. -// * @return An object which is the value. -// */ -// private int optInt(String key, int defaultValue) { -// try { -// return this.getInt(key); -// } catch (Exception e) { -// return defaultValue; -// } -// } - - // /** // * Get an optional JSONArray associated with a key. // * It returns null if there is no such key, or if its value is not a @@ -1036,25 +1079,6 @@ public class JSONObject { // } -// /** -// * Get an optional long value associated with a key, -// * or the default if there is no such key or if the value is not a number. -// * If the value is a string, an attempt will be made to evaluate it as -// * a number. -// * -// * @param key A key string. -// * @param defaultValue The default. -// * @return An object which is the value. -// */ -// public long optLong(String key, long defaultValue) { -// try { -// return this.getLong(key); -// } catch (Exception e) { -// return defaultValue; -// } -// } - - // /** // * Get an optional string associated with a key. // * It returns an empty string if there is no such key. If the value is not @@ -1068,20 +1092,6 @@ public class JSONObject { // } -// /** -// * Get an optional string associated with a key. -// * It returns the defaultValue if there is no such key. -// * -// * @param key A key string. -// * @param defaultValue The default. -// * @return A string which is the value. -// */ -// public String optString(String key, String defaultValue) { -// Object object = this.opt(key); -// return NULL.equals(object) ? defaultValue : object.toString(); -// } - - private void populateMap(Object bean) { Class klass = bean.getClass(); @@ -1780,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) { @@ -1791,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..ed284b10b 100644 --- a/core/src/processing/data/StringDict.java +++ b/core/src/processing/data/StringDict.java @@ -316,7 +316,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/StringList.java b/core/src/processing/data/StringList.java index bd71a0867..a407265a9 100644 --- a/core/src/processing/data/StringList.java +++ b/core/src/processing/data/StringList.java @@ -15,6 +15,8 @@ import processing.core.PApplet; * a sorted copy, use list.copy().sort(). * * @webref data:composite + * @see IntList + * @see FloatList */ public class StringList implements Iterable { int count; @@ -646,11 +648,9 @@ public class StringList implements Iterable { public StringList getSubset(int start, int num) { - StringList outgoing = new StringList(num); - for (int i = 0; i < num; i++) { - System.arraycopy(data, start, outgoing.data, 0, num); - } - return outgoing; + String[] subset = new String[num]; + System.arraycopy(data, start, subset, 0, num); + return new StringList(subset); } @@ -688,6 +688,26 @@ public class StringList implements Iterable { // } + public String join(String separator) { + if (count == 0) { + return ""; + } + StringBuilder sb = new StringBuilder(); + sb.append(data[0]); + for (int i = 1; i < count; i++) { + sb.append(separator); + sb.append(data[i]); + } + return sb.toString(); + } + + +// static public StringList split(String value, char delim) { +// String[] array = PApplet.split(value, delim); +// return new StringList(array); +// } + + @Override public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/core/src/processing/data/Table.java b/core/src/processing/data/Table.java index 61ae3e3cb..8dd7b73a6 100644 --- a/core/src/processing/data/Table.java +++ b/core/src/processing/data/Table.java @@ -60,6 +60,7 @@ import processing.core.PConstants; */ public class Table { protected int rowCount; + protected int allocCount; // protected boolean skipEmptyRows = true; // protected boolean skipCommentLines = true; @@ -91,6 +92,10 @@ public class Table { protected RowIterator rowIterator; + // 0 for doubling each time, otherwise the number of rows to increment on + // each expansion. + protected int expandIncrement; + /** * Creates a new, empty table. Use addRow() to add additional rows. @@ -115,6 +120,7 @@ public class Table { */ public Table(File file, String options) throws IOException { // uses createInput() to handle .gz (and eventually .bz2) files + init(); parse(PApplet.createInput(file), extensionOptions(true, file.getName(), options)); } @@ -142,9 +148,23 @@ public class Table { * @throws IOException */ public Table(InputStream input, String options) throws IOException { + init(); parse(input, options); } + + public Table(Iterable rows) { + init(); + boolean typed = false; + for (TableRow row : rows) { + if (!typed) { + setColumnTypes(row.getColumnTypes()); + } + addRow(row); + } + } + + /** * @nowebref */ @@ -206,6 +226,14 @@ public class Table { } + public Table typedParse(InputStream input, String options) throws IOException { + Table table = new Table(); + table.setColumnTypes(this); + table.parse(input, options); + return table; + } + + protected void init() { columns = new Object[0]; columnTypes = new int[0]; @@ -268,7 +296,7 @@ public class Table { protected void parse(InputStream input, String options) throws IOException { - init(); + //init(); boolean awfulCSV = false; boolean header = false; @@ -280,7 +308,7 @@ public class Table { String[] opts = null; if (options != null) { - opts = PApplet.splitTokens(options, " ,"); + opts = PApplet.trim(PApplet.split(options, ',')); for (String opt : opts) { if (opt.equals("tsv")) { extension = "tsv"; @@ -298,6 +326,8 @@ public class Table { header = true; } else if (opt.startsWith(sheetParam)) { worksheet = opt.substring(sheetParam.length()); + } else if (opt.startsWith("dictionary=")) { + // ignore option, this is only handled by PApplet } else { throw new IllegalArgumentException("'" + opt + "' is not a valid option for loading a Table"); } @@ -335,21 +365,22 @@ public class Table { setRowCount(10); } //int prev = 0; //-1; - while ((line = reader.readLine()) != null) { - if (row == getRowCount()) { - setRowCount(row << 1); - } - if (row == 0 && header) { - setColumnTitles(tsv ? PApplet.split(line, '\t') : splitLineCSV(line)); - header = false; - } else { - setRow(row, tsv ? PApplet.split(line, '\t') : splitLineCSV(line)); - row++; - } + try { + while ((line = reader.readLine()) != null) { + if (row == getRowCount()) { + setRowCount(row << 1); + } + if (row == 0 && header) { + setColumnTitles(tsv ? PApplet.split(line, '\t') : splitLineCSV(line)); + header = false; + } else { + setRow(row, tsv ? PApplet.split(line, '\t') : splitLineCSV(line)); + row++; + } - /* - // this is problematic unless we're going to calculate rowCount first - if (row % 10000 == 0) { + // this is problematic unless we're going to calculate rowCount first + if (row % 10000 == 0) { + /* if (row < rowCount) { int pct = (100 * row) / rowCount; if (pct != prev) { // also prevents "0%" from showing up @@ -357,13 +388,17 @@ public class Table { prev = pct; } } - try { - Thread.sleep(5); - } catch (InterruptedException e) { - e.printStackTrace(); + */ + try { + // Sleep this thread so that the GC can catch up + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } } } - */ + } catch (Exception e) { + throw new RuntimeException("Error reading table on line " + row, e); } // shorten or lengthen based on what's left if (row != getRowCount()) { @@ -882,7 +917,7 @@ public class Table { throw new IllegalArgumentException("No extension specified for saving this Table"); } - String[] opts = PApplet.splitTokens(options, ", "); + String[] opts = PApplet.trim(PApplet.split(options, ',')); // Only option for save is the extension, so we can safely grab the last extension = opts[opts.length - 1]; boolean found = false; @@ -1524,6 +1559,14 @@ public class Table { } + public void setColumnTypes(int[] types) { + ensureColumn(types.length - 1); + for (int col = 0; col < types.length; col++) { + setColumnType(col, types[col]); + } + } + + /** * Set the titles (and if a second column is present) the data types for * this table based on a file loaded separately. This will look for the @@ -1533,6 +1576,7 @@ public class Table { * @param dictionary */ public void setColumnTypes(final Table dictionary) { + ensureColumn(dictionary.getRowCount() - 1); int titleCol = 0; int typeCol = 1; if (dictionary.hasColumnTitles()) { @@ -1580,6 +1624,11 @@ public class Table { } + public int[] getColumnTypes() { + return columnTypes; + } + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @@ -1703,6 +1752,7 @@ public class Table { return getRowCount() - 1; } + /** * @webref table:method * @brief Removes all rows from a table @@ -1745,6 +1795,7 @@ public class Table { rowCount = newCount; } + /** * @webref table:method * @brief Adds a row to a table @@ -1752,10 +1803,12 @@ public class Table { * @see Table#clearRows() */ public TableRow addRow() { + //if (rowIncrement == 0) { setRowCount(rowCount + 1); return new RowPointer(this, rowCount - 1); } + /** * @param source a reference to the original row to be duplicated */ @@ -1789,6 +1842,7 @@ public class Table { return new RowPointer(this, row); } + /** * @nowebref */ @@ -1805,35 +1859,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; } @@ -2204,6 +2258,10 @@ public class Table { public int getColumnType(int column) { return table.getColumnType(column); } + + public int[] getColumnTypes() { + return table.getColumnTypes(); + } } @@ -3576,7 +3634,119 @@ public class Table { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - // TODO maybe these aren't needed. better to use getStringList().getUnique()? + public void sort(String columnName) { + sort(getColumnIndex(columnName), false); + } + + + public void sort(int column) { + sort(column, false); + } + + + public void sortReverse(String columnName) { + sort(getColumnIndex(columnName), true); + } + + + public void sortReverse(int column) { + sort(column, true); + } + + + protected void sort(final int column, final boolean reverse) { + final int[] order = IntList.fromRange(getRowCount()).array(); + Sort s = new Sort() { + + @Override + public int size() { + return getRowCount(); + } + + @Override + public float compare(int index1, int index2) { + int a = reverse ? order[index2] : order[index1]; + int b = reverse ? order[index1] : order[index2]; + + switch (getColumnType(column)) { + case INT: + return getInt(a, column) - getInt(b, column); + case LONG: + return getLong(a, column) - getLong(b, column); + case FLOAT: + return getFloat(a, column) - getFloat(b, column); + case DOUBLE: + return (float) (getDouble(a, column) - getDouble(b, column)); + case STRING: + return getString(a, column).compareToIgnoreCase(getString(b, column)); + case CATEGORY: + return getInt(a, column) - getInt(b, column); + default: + throw new IllegalArgumentException("Invalid column type: " + getColumnType(column)); + } + } + + @Override + public void swap(int a, int b) { + int temp = order[a]; + order[a] = order[b]; + order[b] = temp; + } + + }; + s.run(); + + //Object[] newColumns = new Object[getColumnCount()]; + for (int col = 0; col < getColumnCount(); col++) { + switch (getColumnType(col)) { + case INT: + case CATEGORY: + int[] oldInt = (int[]) columns[col]; + int[] newInt = new int[rowCount]; + for (int row = 0; row < getRowCount(); row++) { + newInt[row] = oldInt[order[row]]; + } + columns[col] = newInt; + break; + case LONG: + long[] oldLong = (long[]) columns[col]; + long[] newLong = new long[rowCount]; + for (int row = 0; row < getRowCount(); row++) { + newLong[row] = oldLong[order[row]]; + } + columns[col] = newLong; + break; + case FLOAT: + float[] oldFloat = (float[]) columns[col]; + float[] newFloat = new float[rowCount]; + for (int row = 0; row < getRowCount(); row++) { + newFloat[row] = oldFloat[order[row]]; + } + columns[col] = newFloat; + break; + case DOUBLE: + double[] oldDouble = (double[]) columns[col]; + double[] newDouble = new double[rowCount]; + for (int row = 0; row < getRowCount(); row++) { + newDouble[row] = oldDouble[order[row]]; + } + columns[col] = newDouble; + break; + case STRING: + String[] oldString = (String[]) columns[col]; + String[] newString = new String[rowCount]; + for (int row = 0; row < getRowCount(); row++) { + newString[row] = oldString[order[row]]; + } + columns[col] = newString; + break; + } + } + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + public String[] getUnique(String columnName) { return getUnique(getColumnIndex(columnName)); diff --git a/core/src/processing/data/TableRow.java b/core/src/processing/data/TableRow.java index 299d6eea4..7107ee693 100644 --- a/core/src/processing/data/TableRow.java +++ b/core/src/processing/data/TableRow.java @@ -36,7 +36,7 @@ public interface TableRow { * @param columnName title of the column to reference */ public int getInt(String columnName); - + public long getLong(int column); public long getLong(String columnName); @@ -52,7 +52,7 @@ public interface TableRow { * @param columnName title of the column to reference */ public float getFloat(String columnName); - + public double getDouble(int column); public double getDouble(String columnName); @@ -107,4 +107,6 @@ public interface TableRow { public int getColumnCount(); public int getColumnType(String columnName); public int getColumnType(int column); + + public int[] getColumnTypes(); } diff --git a/core/src/processing/data/XML.java b/core/src/processing/data/XML.java index b345465e3..5860eafc2 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); } @@ -810,6 +837,12 @@ public class XML implements Serializable { } + public String getContent(String defaultValue) { + String s = node.getTextContent(); + return (s != null) ? s : defaultValue; + } + + /** * @webref xml:method * @brief Gets the content of an element as an int 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/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/LineStroker.java b/core/src/processing/opengl/LineStroker.java index 75f6374cb..ee6b20910 100644 --- a/core/src/processing/opengl/LineStroker.java +++ b/core/src/processing/opengl/LineStroker.java @@ -312,7 +312,8 @@ public class LineStroker { return ncoords / 2; } - private static final long ROUND_JOIN_THRESHOLD = 1000L; + //private static final long ROUND_JOIN_THRESHOLD = 1000L; + private static final long ROUND_JOIN_THRESHOLD = 100000000L; private static final long ROUND_JOIN_INTERNAL_THRESHOLD = 1000000000L; 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 e5677804d..f1e43fcd1 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,80 @@ 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 static Thread glThread; - /** The rendering context (holds rendering state info) */ - public static GLContext context; + /** ID of the GL context associated to the surface **/ + protected static int glContext; - /** The canvas where OpenGL rendering takes place */ - public static Canvas canvas; - - /** 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 +104,183 @@ 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 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; + + /** Flags used to handle the creation of a separate front texture */ + protected boolean usingFrontTex = false; + protected boolean needSepFrontTex = false; + + // ........................................................ + + // 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 texGeoVBO; + protected static int tex2DVertLoc; + protected static int tex2DTCoordLoc; + protected static int tex2DSamplerLoc; + + 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 int texRectSamplerLoc; + + 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 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 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; + + // ........................................................ + + // 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; + + // ........................................................ + + // 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,202 +303,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 colorBuffer; - protected FloatBuffer depthBuffer; - protected ByteBuffer stencilBuffer; - - protected float[] projMatrix; - protected float[] mvMatrix; - - /////////////////////////////////////////////////////////// - - // Error messages - - protected static final String FRAMEBUFFER_ERROR = - "Framebuffer error (%1$s), rendering will probably not work as expected"; - - 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)"; - - protected static final String TEXUNIT_ERROR = - "Number of texture units not supported by this hardware (or driver)"; - - - /////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////// // 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); @@ -465,151 +330,20 @@ public class PGL { byteBuffer = allocateByteBuffer(1); intBuffer = allocateIntBuffer(1); + viewBuffer = allocateIntBuffer(4); } - 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; - } - } + protected abstract void setFps(float fps); - 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); - } + protected abstract void initSurface(int antialias); - 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 reinitSurface(); - // 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); - - pg.parent.setLayout(new BorderLayout()); - pg.parent.add(canvasAWT, BorderLayout.CENTER); - canvasAWT.requestFocusInWindow(); - - pg.parent.removeListeners(pg.parent); - pg.parent.addListeners(canvasAWT); - - 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); - } - - fboLayerCreated = false; - fboLayerInUse = false; - firstFrame = true; - - setFps = false; - } + protected abstract void registerListeners(); protected void deleteSurface() { @@ -623,75 +357,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(); + return fboLayerInUse; } @@ -705,25 +400,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); } @@ -742,64 +429,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(); + 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(); + 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; } @@ -807,44 +463,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); } } } @@ -852,19 +487,12 @@ 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) { + 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); } } @@ -899,8 +527,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++) { @@ -1027,10 +655,6 @@ public class PGL { protected void beginDraw(boolean clear0) { - if (!setFps) setFps(targetFps); - - if (USE_JOGL_FBOLAYER) return; - if (needFBOLayer(clear0)) { if (!fboLayerCreated) createFBOLayer(); @@ -1081,112 +705,44 @@ public class PGL { 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(); + if (fboLayerInUse) { + syncBackTexture(); - // Draw the contents of the back texture to the screen framebuffer. - bindFramebuffer(FRAMEBUFFER, 0); + // 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); + 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); + // 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; - } - } - - // 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 < targetFps/2) { - finish(); + // Swapping front and back textures. + int temp = frontTex; + frontTex = backTex; + backTex = temp; } } - protected void requestFocus() { - if (canvas != null) { - canvas.requestFocus(); - } - } + protected abstract boolean canDraw(); - protected boolean canDraw() { - return pg.initialized && pg.parent.isDisplayable(); - } + protected abstract void requestFocus(); - 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 abstract void requestDraw(); - protected void swapBuffers() { - if (WINDOW_TOOLKIT == AWT) { - canvasAWT.swapBuffers(); - } else if (WINDOW_TOOLKIT == NEWT) { - window.swapBuffers(); - } - } + protected abstract void swapBuffers(); - protected boolean threadIsCurrent() { + protected boolean threadIsCurrent() { return Thread.currentThread() == glThread; } @@ -1196,55 +752,10 @@ public class PGL { } - 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 beginGL() { } - protected void endGL() { - } + protected void endGL() { } /////////////////////////////////////////////////////////// @@ -1258,144 +769,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; } @@ -1405,7 +779,7 @@ public class PGL { protected boolean contextIsCurrent(int other) { - return other == -1 || other == context.hashCode(); + return other == -1 || other == glContext; } @@ -1459,7 +833,7 @@ public class PGL { protected void initTexture(int target, int format, int width, int height, - int initColor) { + int initColor) { int[] glcolor = new int[16 * 16]; Arrays.fill(glcolor, javaToNativeARGB(initColor)); IntBuffer texels = PGL.allocateDirectIntBuffer(16 * 16); @@ -1492,17 +866,23 @@ public class PGL { } - protected void drawTexture(int target, int id, int width, int height, - int X0, int Y0, int X1, int Y1) { + /** + * Not an approved function, this will change or be removed in the future. + */ + public void drawTexture(int target, int id, int width, int height, + int X0, int Y0, int X1, int Y1) { drawTexture(target, id, width, height, width, height, X0, Y0, X1, Y1, X0, Y0, X1, Y1); } - protected void drawTexture(int target, 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) { + /** + * Not an approved function, this will change or be removed in the future. + */ + public void drawTexture(int target, 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 (target == TEXTURE_2D) { drawTexture2D(id, texW, texH, scrW, scrH, texX0, texY0, texX1, texY1, @@ -1515,27 +895,39 @@ 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); + protected void initTex2DShader() { + if (!loadedTex2DShader || tex2DShaderContext != glContext) { + String vertSource = PApplet.join(texVertShaderSource, "\n"); + String fragSource = PApplet.join(tex2DFragShaderSource, "\n"); + tex2DVertShader = createShader(VERTEX_SHADER, vertSource); + tex2DFragShader = createShader(FRAGMENT_SHADER, fragSource); if (0 < tex2DVertShader && 0 < tex2DFragShader) { tex2DShaderProgram = createProgram(tex2DVertShader, tex2DFragShader); } if (0 < tex2DShaderProgram) { - tex2DVertLoc = getAttribLocation(tex2DShaderProgram, "inVertex"); - tex2DTCoordLoc = getAttribLocation(tex2DShaderProgram, "inTexcoord"); + tex2DVertLoc = getAttribLocation(tex2DShaderProgram, "position"); + tex2DTCoordLoc = getAttribLocation(tex2DShaderProgram, "texCoord"); + tex2DSamplerLoc = getUniformLocation(tex2DShaderProgram, "texMap"); } loadedTex2DShader = true; - tex2DShaderContext = context; + tex2DShaderContext = glContext; + + genBuffers(1, intBuffer); + texGeoVBO = intBuffer.get(0); + bindBuffer(ARRAY_BUFFER, texGeoVBO); + bufferData(ARRAY_BUFFER, 16 * SIZEOF_FLOAT, null, STATIC_DRAW); } if (texData == null) { texData = allocateDirectFloatBuffer(texCoords.length); } + } + + + 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) { + initTex2DShader(); if (0 < tex2DShaderProgram) { // The texture overwrites anything drawn earlier. @@ -1549,6 +941,11 @@ public class PGL { boolean depthMask = getDepthWriteMask(); depthMask(false); + // Making sure that the viewport matches the provided screen dimensions + viewBuffer.rewind(); + getIntegerv(VIEWPORT, viewBuffer); + viewport(0, 0, scrW, scrH); + useProgram(tex2DShaderProgram); enableVertexAttribArray(tex2DVertLoc); @@ -1587,18 +984,19 @@ public class PGL { enabledTex = true; } bindTexture(TEXTURE_2D, id); - - bindBuffer(ARRAY_BUFFER, 0); // Making sure that no VBO is bound at this point. + uniform1i(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(PGL.ARRAY_BUFFER, texGeoVBO); + bufferData(PGL.ARRAY_BUFFER, 16 * SIZEOF_FLOAT, texData, PGL.STATIC_DRAW); + + vertexAttribPointer(tex2DVertLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, 0); + vertexAttribPointer(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); @@ -1615,6 +1013,35 @@ public class PGL { disable(DEPTH_TEST); } depthMask(depthMask); + + viewport(viewBuffer.get(0), viewBuffer.get(1), + viewBuffer.get(2),viewBuffer.get(3)); + } + } + + + protected void initTexRectShader() { + if (!loadedTexRectShader || texRectShaderContext != glContext) { + String vertSource = PApplet.join(texVertShaderSource, "\n"); + String fragSource = PApplet.join(texRectFragShaderSource, "\n"); + texRectVertShader = createShader(VERTEX_SHADER, vertSource); + texRectFragShader = createShader(FRAGMENT_SHADER, fragSource); + if (0 < texRectVertShader && 0 < texRectFragShader) { + texRectShaderProgram = createProgram(texRectVertShader, + texRectFragShader); + } + if (0 < texRectShaderProgram) { + texRectVertLoc = getAttribLocation(texRectShaderProgram, "position"); + texRectTCoordLoc = getAttribLocation(texRectShaderProgram, "texCoord"); + texRectSamplerLoc = getUniformLocation(texRectShaderProgram, "texMap"); + } + loadedTexRectShader = true; + texRectShaderContext = glContext; + + genBuffers(1, intBuffer); + texGeoVBO = intBuffer.get(0); + bindBuffer(ARRAY_BUFFER, texGeoVBO); + bufferData(ARRAY_BUFFER, 16 * SIZEOF_FLOAT, null, STATIC_DRAW); } } @@ -1622,21 +1049,7 @@ public class PGL { 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; - } + initTexRectShader(); if (texData == null) { texData = allocateDirectFloatBuffer(texCoords.length); @@ -1654,6 +1067,11 @@ public class PGL { boolean depthMask = getDepthWriteMask(); depthMask(false); + // Making sure that the viewport matches the provided screen dimensions + viewBuffer.rewind(); + getIntegerv(VIEWPORT, viewBuffer); + viewport(0, 0, scrW, scrH); + useProgram(texRectShaderProgram); enableVertexAttribArray(texRectVertLoc); @@ -1692,18 +1110,19 @@ public class PGL { enabledTex = true; } bindTexture(TEXTURE_RECTANGLE, id); - - bindBuffer(ARRAY_BUFFER, 0); // Making sure that no VBO is bound at this point. + uniform1i(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(PGL.ARRAY_BUFFER, texGeoVBO); + bufferData(PGL.ARRAY_BUFFER, 16 * SIZEOF_FLOAT, texData, PGL.STATIC_DRAW); + + vertexAttribPointer(texRectVertLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, 0); + vertexAttribPointer(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); @@ -1720,6 +1139,9 @@ public class PGL { disable(DEPTH_TEST); } depthMask(depthMask); + + viewport(viewBuffer.get(0), viewBuffer.get(1), + viewBuffer.get(2),viewBuffer.get(3)); } } @@ -1771,14 +1193,13 @@ public class PGL { * endian) to Java ARGB. */ protected static int nativeToJavaARGB(int color) { - if (BIG_ENDIAN) { // RGBA to ARGB - return (color & 0xff000000) | - ((color >> 8) & 0x00ffffff); + if (PGL.BIG_ENDIAN) { // RGBA to ARGB + return (color >>> 8) | ((color << 24) & 0xFF000000); + // equivalent to + // ((color >> 8) & 0x00FFFFFF) | ((color << 24) & 0xFF000000) } else { // ABGR to ARGB - return (color & 0xff000000) | - ((color << 16) & 0xff0000) | - (color & 0xff00) | - ((color >> 16) & 0xff); + return ((color & 0xFF) << 16) | ((color & 0xFF0000) >> 16) | + (color & 0xFF00FF00); } } @@ -1793,51 +1214,35 @@ public class PGL { 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++; + for (int x = 0; x < width; x++) { + int pixy = pixels[yindex]; + int pixi = pixels[index]; + if (BIG_ENDIAN) { // RGBA to ARGB + pixels[index] = (pixy >>> 8) | ((pixy << 24) & 0xFF000000); + pixels[yindex] = (pixi >>> 8) | ((pixi << 24) & 0xFF000000); + } else { // ABGR to ARGB + pixels[index] = ((pixy & 0xFF) << 16) | ((pixy & 0xFF0000) >> 16) | + (pixy & 0xFF00FF00); + pixels[yindex] = ((pixi & 0xFF) << 16) | ((pixi & 0xFF0000) >> 16) | + (pixi & 0xFF00FF00); } + index++; + yindex++; } yindex -= width * 2; } - // Flips image - if ((height % 2) == 1) { + if ((height % 2) == 1) { // Converts center row 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++; + for (int x = 0; x < width; x++) { + int pixi = pixels[index]; + if (BIG_ENDIAN) { // RGBA to ARGB + pixels[index] = (pixi >>> 8) | ((pixi << 24) & 0xFF000000); + } else { // ABGR to ARGB + pixels[index] = ((pixi & 0xFF) << 16) | ((pixi & 0xFF0000) >> 16) | + (pixi & 0xFF00FF00); } + index++; } } } @@ -1850,11 +1255,10 @@ public class PGL { */ protected static int nativeToJavaRGB(int color) { if (BIG_ENDIAN) { // RGBA to ARGB - return ((color << 8) & 0xffffff00) | 0xff; + return (color >>> 8) | 0xFF000000; } else { // ABGR to ARGB - return 0xff000000 | ((color << 16) & 0xff0000) | - (color & 0xff00) | - ((color >> 16) & 0xff); + return ((color & 0xFF) << 16) | ((color & 0xFF0000) >> 16) | + (color & 0xFF00FF00) | 0xFF000000; } } @@ -1870,45 +1274,35 @@ public class PGL { 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++; + for (int x = 0; x < width; x++) { + int pixy = pixels[yindex]; + int pixi = pixels[index]; + if (BIG_ENDIAN) { // RGBA to ARGB + pixels[index] = (pixy >>> 8) | 0xFF000000; + pixels[yindex] = (pixi >>> 8) | 0xFF000000; + } else { // ABGR to ARGB + pixels[index] = ((pixy & 0xFF) << 16) | ((pixy & 0xFF0000) >> 16) | + (pixy & 0xFF00FF00) | 0xFF000000; + pixels[yindex] = ((pixi & 0xFF) << 16) | ((pixi & 0xFF0000) >> 16) | + (pixi & 0xFF00FF00) | 0xFF000000; } + index++; + yindex++; } yindex -= width * 2; } - // Flips image - if ((height % 2) == 1) { + if ((height % 2) == 1) { // Converts center row 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++; + for (int x = 0; x < width; x++) { + int pixi = pixels[index]; + if (BIG_ENDIAN) { // RGBA to ARGB + pixels[index] = (pixi >>> 8) | 0xFF000000; + } else { // ABGR to ARGB + pixels[index] = ((pixi & 0xFF) << 16) | ((pixi & 0xFF0000) >> 16) | + (pixi & 0xFF00FF00) | 0xFF000000; } + index++; } } } @@ -1920,13 +1314,10 @@ public class PGL { */ protected static int javaToNativeARGB(int color) { if (BIG_ENDIAN) { // ARGB to RGBA - return ((color >> 24) & 0xff) | - ((color << 8) & 0xffffff00); + return ((color >> 24) & 0xFF) | ((color << 8) & 0xFFFFFF00); } else { // ARGB to ABGR - return (color & 0xff000000) | - ((color << 16) & 0xff0000) | - (color & 0xff00) | - ((color >> 16) & 0xff); + return (color & 0xFF000000) | ((color << 16) & 0xFF0000) | + (color & 0xFF00) | ((color >> 16) & 0xFF); } } @@ -1941,52 +1332,35 @@ public class PGL { 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] = (temp & 0xff000000) | - ((temp << 16) & 0xff0000) | - (temp & 0xff00) | - ((temp >> 16) & 0xff); - index++; - yindex++; + for (int x = 0; x < width; x++) { + int pixy = pixels[yindex]; + int pixi = pixels[index]; + if (BIG_ENDIAN) { // ARGB to RGBA + pixels[index] = ((pixy >> 24) & 0xFF) | ((pixy << 8) & 0xFFFFFF00); + pixels[yindex] = ((pixi >> 24) & 0xFF) | ((pixi << 8) & 0xFFFFFF00); + } else { // ARGB to ABGR + pixels[index] = (pixy & 0xFF000000) | ((pixy << 16) & 0xFF0000) | + (pixy & 0xFF00) | ((pixy >> 16) & 0xFF); + pixels[yindex] = (pixi & 0xFF000000) | ((pixi << 16) & 0xFF0000) | + (pixi & 0xFF00) | ((pixi >> 16) & 0xFF); } + index++; + yindex++; } yindex -= width * 2; } - // Flips image - if ((height % 2) == 1) { + if ((height % 2) == 1) { // Converts center row 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++; + for (int x = 0; x < width; x++) { + int pixi = pixels[index]; + if (BIG_ENDIAN) { // ARGB to RGBA + pixels[index] = ((pixi >> 24) & 0xFF) | ((pixi << 8) & 0xFFFFFF00); + } else { // ARGB to ABGR + pixels[index] = (pixi & 0xFF000000) | ((pixi << 16) & 0xFF0000) | + (pixi & 0xFF00) | ((pixi >> 16) & 0xFF); } + index++; } } } @@ -1997,12 +1371,11 @@ public class PGL { * 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); + if (BIG_ENDIAN) { // ARGB to RGB + return 0xFF | ((color << 8) & 0xFFFFFF00); + } else { // ARGB to BGR + return 0xFF000000 | ((color << 16) & 0xFF0000) | + (color & 0xFF00) | ((color >> 16) & 0xFF); } } @@ -2018,51 +1391,102 @@ public class PGL { 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++; + for (int x = 0; x < width; x++) { + int pixy = pixels[yindex]; + int pixi = pixels[index]; + if (BIG_ENDIAN) { // ARGB to RGB + pixels[index] = 0xFF | ((pixy << 8) & 0xFFFFFF00); + pixels[yindex] = 0xFF | ((pixi << 8) & 0xFFFFFF00); + } else { // ARGB to BGR + pixels[index] = 0xFF000000 | ((pixy << 16) & 0xFF0000) | + (pixy & 0xFF00) | ((pixy >> 16) & 0xFF); + pixels[yindex] = 0xFF000000 | ((pixi << 16) & 0xFF0000) | + (pixi & 0xFF00) | ((pixi >> 16) & 0xFF); } + index++; + yindex++; } yindex -= width * 2; } - // Flips image - if ((height % 2) == 1) { // ARGB to RGBA + if ((height % 2) == 1) { // Converts center row 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++; + for (int x = 0; x < width; x++) { + int pixi = pixels[index]; + if (BIG_ENDIAN) { // ARGB to RGB + pixels[index] = 0xFF | ((pixi << 8) & 0xFFFFFF00); + } else { // ARGB to BGR + pixels[index] = 0xFF000000 | ((pixi << 16) & 0xFF0000) | + (pixi & 0xFF00) | ((pixi >> 16) & 0xFF); } + index++; } } } + 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 String[] convertFragmentSource(String[] fragSrc0, + int version0, int version1) { + return fragSrc0; + } + + + protected String[] convertVertexSource(String[] vertSrc0, + int version0, int version1) { + return vertSrc0; + } + + protected int createShader(int shaderType, String source) { int shader = createShader(shaderType); if (shader != 0) { @@ -2165,13 +1589,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. @@ -2182,19 +1614,76 @@ 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; + } } protected int maxSamples() { + intBuffer.rewind(); getIntegerv(MAX_SAMPLES, intBuffer); return intBuffer.get(0); } protected int getMaxTexUnits() { + intBuffer.rewind(); getIntegerv(MAX_TEXTURE_IMAGE_UNITS, intBuffer); return intBuffer.get(0); } @@ -2554,269 +2043,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; - } - } - - @SuppressWarnings("deprecation") - int peCount = peAction == MouseEvent.WHEEL ? - (int) nativeEvent.getWheelRotation() : - 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); - } - @Override - 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(); } @@ -2832,595 +2133,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 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 DEPTH_STENCIL; - 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_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 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 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 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 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 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) { - // The beginPixelsOp/endPixelsOp calls are needed to properly setup the - // framebuffers to read from. - PGraphicsOpenGL.pgCurrent.beginPixelsOp(PGraphicsOpenGL.OP_READ); + public void readPixels(int x, int y, int width, int height, int format, int type, Buffer buffer){ + boolean needEndBegin = format != STENCIL_INDEX && + format != DEPTH_COMPONENT && format != DEPTH_STENCIL; + if (needEndBegin) pg.beginReadPixels(); readPixelsImpl(x, y, width, height, format, type, buffer); - PGraphicsOpenGL.pgCurrent.endPixelsOp(); + if (needEndBegin) 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(); @@ -3437,439 +2592,115 @@ 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); - } - - 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 bindFramebuffer(int target, int framebuffer); + 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 a1ad432d8..9c1761cba 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(width/2f, (3f/2f) * width, -height/2f, height/2f, -1, +1); } @@ -156,7 +156,7 @@ public class PGraphics2D extends PGraphicsOpenGL { @Override protected void defaultCamera() { - super.camera(width/2, height/2); + resetMatrix(); } @@ -180,6 +180,12 @@ public class PGraphics2D extends PGraphicsOpenGL { popProjection(); } +// @Override +// public void resetMatrix() { +// super.resetMatrix(); +// defaultCamera(); +// } + ////////////////////////////////////////////////////////////// @@ -307,35 +313,6 @@ public class PGraphics2D extends PGraphicsOpenGL { } else if (type == PShape.GEOMETRY) { shape = new PShapeOpenGL(parent, 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; } @@ -375,14 +352,14 @@ public class PGraphics2D extends PGraphicsOpenGL { shape = new PShapeOpenGL(parent, 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.setKind(RECT); } else if (kind == ELLIPSE) { - if (len != 4) { + if (len != 4 && len != 5) { showWarning("Wrong number of parameters"); return null; } diff --git a/core/src/processing/opengl/PGraphics3D.java b/core/src/processing/opengl/PGraphics3D.java index 0880651cc..43aa52251 100644 --- a/core/src/processing/opengl/PGraphics3D.java +++ b/core/src/processing/opengl/PGraphics3D.java @@ -117,6 +117,9 @@ public class PGraphics3D extends PGraphicsOpenGL { } else if (extension.equals("objz")) { try { + // TODO: The obj file can be read from the gzip, but if it refers to + // a materials file and texture images, those must be contained in the + // data folder, cannot be inside the gzip. InputStream input = new GZIPInputStream(pg.parent.createInput(filename)); obj = new PShapeOBJ(pg.parent, PApplet.createReader(input)); @@ -175,38 +178,6 @@ public class PGraphics3D extends PGraphicsOpenGL { } else if (type == PShape.GEOMETRY) { shape = new PShapeOpenGL(parent, 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; } @@ -246,14 +217,14 @@ public class PGraphics3D extends PGraphicsOpenGL { shape = new PShapeOpenGL(parent, 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.setKind(RECT); } else if (kind == ELLIPSE) { - if (len != 4) { + if (len != 4 && len != 5) { showWarning("Wrong number of parameters"); return null; } @@ -274,7 +245,7 @@ public class PGraphics3D extends PGraphicsOpenGL { shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); shape.setKind(BOX); } else if (kind == SPHERE) { - if (len != 1) { + if (len < 1 || 3 < len) { showWarning("Wrong number of parameters"); return null; } diff --git a/core/src/processing/opengl/PGraphicsOpenGL.java b/core/src/processing/opengl/PGraphicsOpenGL.java index 8242d2b80..f613fc583 100644 --- a/core/src/processing/opengl/PGraphicsOpenGL.java +++ b/core/src/processing/opengl/PGraphicsOpenGL.java @@ -76,12 +76,21 @@ public class PGraphicsOpenGL extends PGraphics { "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 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 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 = @@ -173,8 +182,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; @@ -230,31 +237,20 @@ public class PGraphicsOpenGL extends PGraphics { 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 PShader defColorShader; + static protected PShader defTextureShader; + static protected PShader defLightShader; + static protected PShader defTexlightShader; + static protected PShader defLineShader; + static protected PShader defPointShader; static protected URL maskShaderFragURL = PGraphicsOpenGL.class.getResource("MaskFrag.glsl"); - static protected TextureShader maskShader; + static protected PShader maskShader; - protected ColorShader colorShader; - protected TextureShader textureShader; - protected LightShader lightShader; - protected TexlightShader texlightShader; - protected LineShader lineShader; - protected PointShader pointShader; - - // 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; // ........................................................ @@ -390,12 +386,6 @@ public class PGraphicsOpenGL extends PGraphics { // ........................................................ - // Blending: - - protected int blendMode; - - // ........................................................ - // Clipping protected boolean clip = false; @@ -477,6 +467,10 @@ public class PGraphicsOpenGL extends PGraphics { protected int smoothCallCount = 0; protected int lastSmoothCall = -10; + /** Used to avoid flushing the geometry when blendMode() is called with the + * same blend mode as the last */ + protected int lastBlendMode = -1; + /** Type of pixels operation. */ static protected final int OP_NONE = 0; static protected final int OP_READ = 1; @@ -504,19 +498,21 @@ public class PGraphicsOpenGL extends PGraphics { /** Used in round point and ellipse tessellation. The * number of subdivisions per round point or ellipse is * calculated with the following formula: - * n = max(N, (TWO_PI * size / F)) + * n = min(M, max(N, (TWO_PI * size / F))) * where size is a measure of the dimensions of the circle * when projected on screen coordinates. F just sets the * minimum number of subdivisions, while a smaller F * would allow to have more detailed circles. * N = MIN_POINT_ACCURACY + * M = MAX_POINT_ACCURACY * F = POINT_ACCURACY_FACTOR */ final static protected int MIN_POINT_ACCURACY = 20; + final static protected int MAX_POINT_ACCURACY = 200; 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. */ @@ -530,7 +526,7 @@ public class PGraphicsOpenGL extends PGraphics { public PGraphicsOpenGL() { if (pgl == null) { - pgl = new PGL(this); + pgl = createPGL(this); } if (tessellator == null) { @@ -544,8 +540,8 @@ public class PGraphicsOpenGL extends PGraphics { viewport = PGL.allocateIntBuffer(4); - inGeo = newInGeometry(IMMEDIATE); - tessGeo = newTessGeometry(IMMEDIATE); + inGeo = newInGeometry(this, IMMEDIATE); + tessGeo = newTessGeometry(this, IMMEDIATE); texCache = newTexCache(); initialized = false; @@ -577,7 +573,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 @@ -588,10 +583,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; } @@ -635,13 +626,15 @@ 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(); @@ -1288,7 +1281,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; @@ -1306,11 +1300,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, @@ -1331,8 +1320,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, @@ -1581,7 +1576,7 @@ public class PGraphicsOpenGL extends PGraphics { @Override public void requestFocus() { // ignore - //pgl.requestFocus(); + pgl.requestFocus(); } @@ -1599,6 +1594,7 @@ public class PGraphicsOpenGL extends PGraphics { public void requestDraw() { if (primarySurface) { if (initialized) { + if (sized) pgl.reinitSurface(); pgl.requestDraw(); } else { initPrimary(); @@ -1645,7 +1641,7 @@ public class PGraphicsOpenGL extends PGraphics { } else { beginOffscreenDraw(); } - setDrawDefaults(); + setDrawDefaults(); // TODO: look at using checkSettings() instead... pgCurrent = this; drawing = true; @@ -1696,6 +1692,12 @@ public class PGraphicsOpenGL extends PGraphics { } + // Factory method + protected PGL createPGL(PGraphicsOpenGL pg) { + return new PJOGL(pg); + } + + @Override public PGL beginPGL() { flush(); @@ -1723,7 +1725,7 @@ public class PGraphicsOpenGL extends PGraphics { protected void restoreGL() { - setBlendMode(blendMode); + blendMode(blendMode); // this should be set by reapplySettings... if (hints[DISABLE_DEPTH_TEST]) { pgl.disable(PGL.DEPTH_TEST); @@ -1736,8 +1738,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); } @@ -1765,6 +1765,13 @@ public class PGraphicsOpenGL extends PGraphics { pgl.drawBuffer(currentFramebuffer.getDefaultDrawBuffer()); } + public void beginReadPixels() { + pgCurrent.beginPixelsOp(OP_READ); + } + + public void endReadPixels() { + pgCurrent.endPixelsOp(); + } protected void beginPixelsOp(int op) { FrameBuffer pixfb = null; @@ -2036,10 +2043,10 @@ 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; @@ -2111,6 +2118,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); } @@ -2123,6 +2131,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); } @@ -2165,17 +2174,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; } @@ -2218,12 +2226,13 @@ public class PGraphicsOpenGL extends PGraphics { tessellator.setInGeometry(inGeo); tessellator.setTessGeometry(tessGeo); tessellator.setFill(fill || textureImage != null); + tessellator.setTexCache(texCache, textureImage0, textureImage); tessellator.setStroke(stroke); tessellator.setStrokeColor(strokeColor); tessellator.setStrokeWeight(strokeWeight); tessellator.setStrokeCap(strokeCap); tessellator.setStrokeJoin(strokeJoin); - tessellator.setTexCache(texCache, textureImage0, textureImage); + tessellator.setRenderer(pgCurrent); tessellator.setTransform(modelview); tessellator.set3D(is3D()); @@ -2248,8 +2257,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(); @@ -2258,7 +2265,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); } @@ -2361,14 +2367,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]; @@ -2399,18 +2409,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(); @@ -2528,7 +2538,7 @@ public class PGraphicsOpenGL extends PGraphics { protected void flushLines() { updateLineBuffers(); - LineShader shader = getLineShader(); + PShader shader = getLineShader(); shader.bind(); IndexCache cache = tessGeo.lineIndexCache; @@ -2544,10 +2554,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(); @@ -2632,7 +2639,7 @@ public class PGraphicsOpenGL extends PGraphics { protected void flushPoints() { updatePointBuffers(); - PointShader shader = getPointShader(); + PShader shader = getPointShader(); shader.bind(); IndexCache cache = tessGeo.pointIndexCache; @@ -2648,10 +2655,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(); @@ -2683,8 +2687,8 @@ public class PGraphicsOpenGL extends PGraphics { int perim; if (0 < size) { // round point weight = +size / 0.5f; - perim = PApplet.max(MIN_POINT_ACCURACY, - (int) (TWO_PI * weight / POINT_ACCURACY_FACTOR)) + 1; + perim = PApplet.min(MAX_POINT_ACCURACY, PApplet.max(MIN_POINT_ACCURACY, + (int) (TWO_PI * weight / POINT_ACCURACY_FACTOR))) + 1; } else { // Square point weight = -size / 0.5f; perim = 5; @@ -2755,9 +2759,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()); } @@ -2783,8 +2785,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()); } @@ -2809,8 +2810,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()); } @@ -2901,70 +2901,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); - endShape(CLOSE); + inGeo.addRect(x1, y1, x2, y2, tl, tr, br, bl, stroke); + endShape(); } - // 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) { @@ -3006,6 +2979,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; @@ -3083,7 +3060,11 @@ public class PGraphicsOpenGL extends PGraphics { @Override public void smooth() { - smooth(2); + if (quality < 2) { + smooth(2); + } else { + smooth(quality); + } } @@ -3111,6 +3092,7 @@ public class PGraphicsOpenGL extends PGraphics { lastSmoothCall = parent.frameCount; quality = level; + if (quality == 1) { quality = 0; } @@ -3272,8 +3254,49 @@ public class PGraphicsOpenGL extends PGraphics { // TEXT IMPL - // protected void textLineAlignImpl(char buffer[], int start, int stop, - // float x, float y) + @Override + public float 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"); + 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) { + 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; + } + + + @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); + } + /** * Implementation of actual drawing for a line of text. @@ -3339,7 +3362,6 @@ public class PGraphicsOpenGL extends PGraphics { @Override protected void textCharImpl(char ch, float x, float y) { PFont.Glyph glyph = textFont.getGlyph(ch); - if (glyph != null) { if (textMode == MODEL) { FontTexture.TextureInfo tinfo = textTex.getTexInfo(glyph); @@ -3373,10 +3395,8 @@ public class PGraphicsOpenGL extends PGraphics { if (textTex.currentTex != info.texIndex) { textTex.setTexture(info.texIndex); } - PImage tex = textTex.getCurrentTexture(); - beginShape(QUADS); - texture(tex); + texture(textTex.getCurrentTexture()); vertex(x0, y0, info.u0, info.v0); vertex(x1, y0, info.u1, info.v0); vertex(x1, y1, info.u1, info.v1); @@ -3430,17 +3450,13 @@ public class PGraphicsOpenGL extends PGraphics { 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 (type == PGL.SEG_MOVETO) { // 1 point (2 vars) in textPoints + } else if (type == PGL.SEG_LINETO) { // 1 point + if (type == PGL.SEG_MOVETO) beginContour(); 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, @@ -3454,8 +3470,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], @@ -3465,10 +3480,8 @@ public class PGraphicsOpenGL extends PGraphics { } lastX = textPoints[4]; lastY = textPoints[5]; - break; - case PGL.SEG_CLOSE: + } else if (type == PGL.SEG_CLOSE) { endContour(); - break; } outline.next(); } @@ -4241,10 +4254,10 @@ public class PGraphicsOpenGL extends PGraphics { public void ortho(float left, float right, float bottom, float top, float near, float far) { - left -= width/2; - right -= width/2; - bottom -= height/2; - top -= height/2; + left -= width/2f; + right -= width/2f; + bottom -= height/2f; + top -= height/2f; // Flushing geometry with a different perspective configuration. flush(); @@ -4523,8 +4536,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) @@ -4704,6 +4729,9 @@ public class PGraphicsOpenGL extends PGraphics { public void lights() { enableLighting(); + // reset number of lights + lightCount = 0; + // need to make sure colorMode is RGB 255 here int colorModeSaved = colorMode; colorMode = RGB; @@ -5005,6 +5033,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; } @@ -5199,9 +5232,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; @@ -5237,7 +5270,6 @@ public class PGraphicsOpenGL extends PGraphics { // First, copy the pixels to the texture. We don't need to invert the // pixel copy because the texture will be drawn inverted. - pgl.copyToTexture(texture.glTarget, texture.glFormat, texture.glName, x, y, w, h, nativePixelBuffer); beginPixelsOp(OP_WRITE); @@ -5294,6 +5326,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; +// } +// } } @@ -5302,8 +5345,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) { @@ -5340,12 +5383,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.copy(offscreenFramebuffer, currentFramebuffer); } if (needEndDraw) { @@ -5463,8 +5504,8 @@ public class PGraphicsOpenGL extends PGraphics { } if (maskShader == null) { - maskShader = new TextureShader(parent, defTextureShaderVertURL, - maskShaderFragURL); + maskShader = new PShader(parent, defTextureShaderVertURL, + maskShaderFragURL); } maskShader.set("mask", alpha); filter(maskShader); @@ -5505,7 +5546,7 @@ public class PGraphicsOpenGL extends PGraphics { @Override public void filter(PShader shader) { - if (!(shader instanceof TextureShader)) { + if (!shader.isPolyShader() || !shader.supportsTexturing()) { PGraphics.showWarning(INVALID_FILTER_SHADER_ERROR); return; } @@ -5547,8 +5588,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); @@ -5560,7 +5601,7 @@ public class PGraphicsOpenGL extends PGraphics { end2D(); // Restoring previous configuration. - textureShader = prevTexShader; + polyShader = prevShader; stroke = prevStroke; lights = prevLights; textureMode = prevTextureMode; @@ -5581,101 +5622,100 @@ 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(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); + pgl.drawTexture(tex.glTarget, tex.glName, + tex.glWidth, tex.glHeight, width, height, + sx, tex.height - (sy + sh), + sx + sw, tex.height - sy, + dx, height - (dy + dh), + dx + dw, height - dy); + + 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. */ @Override - public void blendMode(int mode) { - if (blendMode != mode) { + protected void blendModeImpl() { + if (blendMode != lastBlendMode) { // Flush any geometry that uses a different blending mode. flush(); - setBlendMode(mode); } - } - - protected void setBlendMode(int mode) { - blendMode = mode; pgl.enable(PGL.BLEND); - if (mode == REPLACE) { + if (blendMode == REPLACE) { if (blendEqSupported) { pgl.blendEquation(PGL.FUNC_ADD); } pgl.blendFunc(PGL.ONE, PGL.ZERO); - } else if (mode == BLEND) { + } else if (blendMode == BLEND) { if (blendEqSupported) { pgl.blendEquation(PGL.FUNC_ADD); } pgl.blendFunc(PGL.SRC_ALPHA, PGL.ONE_MINUS_SRC_ALPHA); - } else if (mode == ADD) { + } else if (blendMode == ADD) { if (blendEqSupported) { pgl.blendEquation(PGL.FUNC_ADD); } pgl.blendFunc(PGL.SRC_ALPHA, PGL.ONE); - } else if (mode == SUBTRACT) { + } 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 (mode == LIGHTEST) { + } else if (blendMode == LIGHTEST) { if (blendEqSupported) { pgl.blendEquation(PGL.FUNC_MAX); pgl.blendFunc(PGL.SRC_ALPHA, PGL.DST_ALPHA); @@ -5683,7 +5723,7 @@ public class PGraphicsOpenGL extends PGraphics { PGraphics.showWarning(BLEND_DRIVER_ERROR, "LIGHTEST"); } - } else if (mode == DARKEST) { + } else if (blendMode == DARKEST) { if (blendEqSupported) { pgl.blendEquation(PGL.FUNC_MIN); pgl.blendFunc(PGL.SRC_ALPHA, PGL.DST_ALPHA); @@ -5691,47 +5731,43 @@ public class PGraphicsOpenGL extends PGraphics { PGraphics.showWarning(BLEND_DRIVER_ERROR, "DARKEST"); } - } else if (mode == DIFFERENCE) { - if (blendEqSupported) { - pgl.blendEquation(PGL.FUNC_REVERSE_SUBTRACT); - pgl.blendFunc(PGL.ONE, PGL.ONE); - } else { - PGraphics.showWarning(BLEND_DRIVER_ERROR, "DIFFERENCE"); - } - - } else if (mode == EXCLUSION) { + } else if (blendMode == EXCLUSION) { if (blendEqSupported) { pgl.blendEquation(PGL.FUNC_ADD); } pgl.blendFunc(PGL.ONE_MINUS_DST_COLOR, PGL.ONE_MINUS_SRC_COLOR); - } else if (mode == MULTIPLY) { + } else if (blendMode == MULTIPLY) { if (blendEqSupported) { pgl.blendEquation(PGL.FUNC_ADD); } pgl.blendFunc(PGL.DST_COLOR, PGL.SRC_COLOR); - } else if (mode == SCREEN) { + } else if (blendMode == SCREEN) { if (blendEqSupported) { pgl.blendEquation(PGL.FUNC_ADD); } pgl.blendFunc(PGL.ONE_MINUS_DST_COLOR, PGL.ONE); - } else if (mode == OVERLAY) { + } else if (blendMode == DIFFERENCE) { + PGraphics.showWarning(BLEND_RENDERER_ERROR, "DIFFERENCE"); + + } else if (blendMode == OVERLAY) { PGraphics.showWarning(BLEND_RENDERER_ERROR, "OVERLAY"); - } else if (mode == HARD_LIGHT) { + } else if (blendMode == HARD_LIGHT) { PGraphics.showWarning(BLEND_RENDERER_ERROR, "HARD_LIGHT"); - } else if (mode == SOFT_LIGHT) { + } else if (blendMode == SOFT_LIGHT) { PGraphics.showWarning(BLEND_RENDERER_ERROR, "SOFT_LIGHT"); - } else if (mode == DODGE) { + } else if (blendMode == DODGE) { PGraphics.showWarning(BLEND_RENDERER_ERROR, "DODGE"); - } else if (mode == BURN) { + } else if (blendMode == BURN) { PGraphics.showWarning(BLEND_RENDERER_ERROR, "BURN"); } + lastBlendMode = blendMode; } @@ -5748,23 +5784,25 @@ public class PGraphicsOpenGL extends PGraphics { /** + * Not an approved function, this will change or be removed in the future. * This utility method returns the texture associated to the renderer's. * drawing surface, making sure is updated to reflect the current contents * off the screen (or offscreen drawing surface). */ - protected Texture getTexture() { + public Texture getTexture() { loadTexture(); return texture; } /** + * Not an approved function, this will change or be removed in the future. * This utility method returns the texture associated to the image. * creating and/or updating it if needed. * * @param img the image to have a texture metadata associated to it */ - protected Texture getTexture(PImage img) { + public Texture getTexture(PImage img) { Texture tex = (Texture)initCache(img); if (tex == null) return null; @@ -6061,6 +6099,15 @@ public class PGraphicsOpenGL extends PGraphics { protected void endOffscreenDraw() { + // Set alpha channel to opaque in order to match behavior of JAVA2D: + // https://github.com/processing/processing/issues/1844 + // but still not working as expected. Some strange artifacts with multismapled + // surfaces (see second code example in the issue above). +// pgl.colorMask(false, false, false, true); +// pgl.clearColor(0, 0, 0, 1); +// pgl.clear(PGL.COLOR_BUFFER_BIT); +// pgl.colorMask(true, true, true, true); + if (offscreenMultisample) { multisampleFramebuffer.copy(offscreenFramebuffer, currentFramebuffer); } @@ -6088,11 +6135,8 @@ public class PGraphicsOpenGL extends PGraphics { // Each frame starts with textures disabled. super.noTexture(); - // Screen blend is needed for alpha (i.e. fonts) to work. - // Using setBlendMode() instead of blendMode() because - // the latter will set the blend mode only if it is different - // from current. - setBlendMode(BLEND); + // Making sure that OpenGL is using the last blend mode set by the user. + blendModeImpl(); // this is necessary for 3D drawing if (hints[DISABLE_DEPTH_TEST]) { @@ -6121,11 +6165,11 @@ 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); @@ -6176,9 +6220,7 @@ public class PGraphicsOpenGL extends PGraphics { if (restoreSurface) { restoreSurfaceFromPixels(); - //if (1 < parent.frameCount) { restoreSurface = false; - //} } if (hints[DISABLE_DEPTH_MASK]) { @@ -6204,16 +6246,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); @@ -6231,12 +6268,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); @@ -6253,133 +6284,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); } @@ -6388,10 +6363,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) { @@ -6402,35 +6374,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 @@ -6445,114 +6388,84 @@ public class PGraphicsOpenGL extends PGraphics { } - protected BaseShader getPolyShader(boolean lit, boolean tex) { - BaseShader shader; + protected PShader getPolyShader(boolean lit, boolean tex) { + PShader shader; + boolean useDefault = polyShader == null; + if (polyShader != null) { + polyShader.setRenderer(this); + polyShader.loadAttributes(); + polyShader.loadUniforms(); + } if (lit) { if (tex) { - if (texlightShader == null) { + if (useDefault || !polyShader.checkPolyType(PShader.TEXLIGHT)) { if (defTexlightShader == null) { - defTexlightShader = new TexlightShader(parent, - defTexlightShaderVertURL, - defTextureShaderFragURL); + String[] vertSource = pgl.loadVertexShader(defTexlightShaderVertURL, 120); + String[] fragSource = pgl.loadFragmentShader(defTextureShaderFragURL, 120); + defTexlightShader = new PShader(parent, vertSource, fragSource); } shader = defTexlightShader; - texlightShaderCheck(); } else { - shader = texlightShader; + shader = polyShader; } } else { - if (lightShader == null) { + if (useDefault || !polyShader.checkPolyType(PShader.LIGHT)) { if (defLightShader == null) { - defLightShader = new LightShader(parent, - defLightShaderVertURL, - defColorShaderFragURL); + String[] vertSource = pgl.loadVertexShader(defLightShaderVertURL, 120); + String[] fragSource = pgl.loadFragmentShader(defColorShaderFragURL, 120); + defLightShader = new PShader(parent, vertSource, fragSource); } shader = defLightShader; - lightShaderCheck(); } 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 (useDefault || !polyShader.checkPolyType(PShader.TEXTURE)) { if (defTextureShader == null) { - defTextureShader = new TextureShader(parent, - defTextureShaderVertURL, - defTextureShaderFragURL); + String[] vertSource = pgl.loadVertexShader(defTextureShaderVertURL, 120); + String[] fragSource = pgl.loadFragmentShader(defTextureShaderFragURL, 120); + defTextureShader = new PShader(parent, vertSource, fragSource); } shader = defTextureShader; - textureShaderCheck(); } else { - shader = textureShader; + shader = polyShader; } } else { - if (colorShader == null) { + if (useDefault || !polyShader.checkPolyType(PShader.COLOR)) { if (defColorShader == null) { - defColorShader = new ColorShader(parent, - defColorShaderVertURL, - defColorShaderFragURL); + String[] vertSource = pgl.loadVertexShader(defColorShaderVertURL, 120); + String[] fragSource = pgl.loadFragmentShader(defColorShaderFragURL, 120); + defColorShader = new PShader(parent, vertSource, fragSource); } shader = defColorShader; - colorShaderCheck(); } 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; if (lineShader == null) { if (defLineShader == null) { - defLineShader = new LineShader(parent, defLineShaderVertURL, - defLineShaderFragURL); + String[] vertSource = pgl.loadVertexShader(defLineShaderVertURL, 120); + String[] fragSource = pgl.loadFragmentShader(defLineShaderFragURL, 120); + defLineShader = new PShader(parent, vertSource, fragSource); } shader = defLineShader; } else { @@ -6565,12 +6478,13 @@ public class PGraphicsOpenGL extends PGraphics { } - protected PointShader getPointShader() { - PointShader shader; + protected PShader getPointShader() { + PShader shader; if (pointShader == null) { if (defPointShader == null) { - defPointShader = new PointShader(parent, defPointShaderVertURL, - defPointShaderFragURL); + String[] vertSource = pgl.loadVertexShader(defPointShaderVertURL, 120); + String[] fragSource = pgl.loadFragmentShader(defPointShaderFragURL, 120); + defPointShader = new PShader(parent, vertSource, fragSource); } shader = defPointShader; } else { @@ -6583,765 +6497,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 @@ -7359,24 +6514,24 @@ 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() { + static protected TexCache newTexCache() { return new TexCache(); } // Holds an array of textures and the range of vertex // indices each texture applies to. - protected class TexCache { + static protected class TexCache { int size; PImage[] textures; int[] firstIndex; @@ -7494,7 +6649,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; @@ -7599,23 +6754,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; @@ -7623,8 +6769,10 @@ public class PGraphicsOpenGL extends PGraphics { int[] strokeColors; float[] strokeWeights; - // lines - boolean[] breaks; + // vertex codes + int[] codes; + + // Stroke edges int[][] edges; // Material properties @@ -7643,7 +6791,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(); } @@ -7653,12 +6802,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() { @@ -7672,7 +6822,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(); @@ -7692,7 +6841,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); } } @@ -7734,17 +6890,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--; @@ -7756,10 +6912,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--; @@ -7865,10 +7021,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) { @@ -7893,7 +7049,10 @@ public class PGraphicsOpenGL extends PGraphics { trimSpecular(); trimEmissive(); trimShininess(); - trimBreaks(); + } + + if (0 < codeCount && codeCount < codes.length) { + trimCodes(); } if (0 < edgeCount && edgeCount < edges.length) { @@ -7961,10 +7120,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() { @@ -7977,8 +7136,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, @@ -7986,12 +7149,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, @@ -7999,11 +7168,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, @@ -8011,12 +7184,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, @@ -8024,7 +7203,7 @@ public class PGraphicsOpenGL extends PGraphics { strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininessFactor, - code); + code, brk); } int addVertex(float x, float y, float z, @@ -8033,12 +7212,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; @@ -8063,147 +7240,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. @@ -8234,29 +7314,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 @@ -8275,10 +7358,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) { @@ -8287,16 +7369,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; @@ -8309,8 +7390,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; @@ -8322,7 +7403,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) { @@ -8341,7 +7422,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; @@ -8356,11 +7437,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); @@ -8370,50 +7451,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 @@ -8473,7 +7510,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; @@ -8483,8 +7520,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; @@ -8493,7 +7530,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) { @@ -8510,7 +7547,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; @@ -8522,11 +7559,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); @@ -8556,14 +7593,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); } @@ -8571,9 +7608,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); @@ -8586,11 +7623,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); @@ -8601,151 +7638,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; @@ -8753,19 +7684,19 @@ 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.max(MIN_POINT_ACCURACY, + PApplet.min(MAX_POINT_ACCURACY, PApplet.max(MIN_POINT_ACCURACY, (int) (TWO_PI * PApplet.dist(sx1, sy1, sx2, sy2) / - POINT_ACCURACY_FACTOR)); + POINT_ACCURACY_FACTOR))); 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; @@ -8773,7 +7704,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) { @@ -8787,7 +7718,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); @@ -8808,7 +7739,7 @@ public class PGraphicsOpenGL extends PGraphics { int stopLUT = (int) (0.5f + (stop / TWO_PI) * SINCOS_LENGTH); if (fill) { - addVertex(centerX, centerY, VERTEX); + addVertex(centerX, centerY, VERTEX, true); } int increment = 1; // what's a good algorithm? stopLUT - startLUT; @@ -8820,7 +7751,7 @@ public class PGraphicsOpenGL extends PGraphics { 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) { @@ -8837,7 +7768,7 @@ public class PGraphicsOpenGL extends PGraphics { // 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); @@ -8852,7 +7783,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); } @@ -8867,12 +7798,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); @@ -8881,12 +7812,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); @@ -8897,10 +7828,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); @@ -8911,24 +7842,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); @@ -8939,10 +7856,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); @@ -8957,13 +7888,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]; @@ -8983,21 +7907,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++) { @@ -9022,16 +7948,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++) { @@ -9068,7 +7994,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; @@ -9092,8 +8018,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; @@ -9164,7 +8091,8 @@ public class PGraphicsOpenGL extends PGraphics { float[] pointOffsets; short[] pointIndices; - TessGeometry(int mode) { + TessGeometry(PGraphicsOpenGL pg, int mode) { + this.pg = pg; renderMode = mode; allocate(); } @@ -9966,8 +8894,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; @@ -9989,16 +8917,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; @@ -10022,27 +8950,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; @@ -10107,9 +9035,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) { @@ -10155,7 +9083,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) { @@ -10168,9 +9096,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; @@ -10464,8 +9392,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; @@ -10488,6 +9415,7 @@ public class PGraphicsOpenGL extends PGraphics { PMatrix transform; float transformScale; boolean is2D, is3D; + protected PGraphicsOpenGL pg; int[] rawIndices; int rawSize; @@ -10501,6 +9429,21 @@ 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); @@ -10530,6 +9473,13 @@ public class PGraphicsOpenGL extends PGraphics { this.fill = fill; } + void setTexCache(TexCache texCache, PImage prevTexImage, + PImage newTexImage) { + this.texCache = texCache; + this.prevTexImage = prevTexImage; + this.newTexImage = newTexImage; + } + void setStroke(boolean stroke) { this.stroke = stroke; } @@ -10542,23 +9492,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) { @@ -10576,6 +9523,10 @@ public class PGraphicsOpenGL extends PGraphics { transformScale = -1; } + void resetCurveVertexCount() { + pg.curveVertexCount = 0; + } + // ----------------------------------------------------------------- // // Point tessellation @@ -10589,15 +9540,15 @@ 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 // stroke weight of the point. int nPtVert = - PApplet.max(MIN_POINT_ACCURACY, + PApplet.min(MAX_POINT_ACCURACY, PApplet.max(MIN_POINT_ACCURACY, (int) (TWO_PI * strokeWeight / - POINT_ACCURACY_FACTOR)) + 1; + POINT_ACCURACY_FACTOR))) + 1; if (PGL.MAX_VERTEX_INDEX1 <= nPtVert) { throw new RuntimeException("Error in point tessellation."); } @@ -10624,7 +9575,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]; @@ -10684,7 +9635,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. @@ -10727,7 +9678,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. @@ -10757,7 +9708,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) { @@ -10812,7 +9763,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) { @@ -10852,7 +9803,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() { @@ -10864,8 +9815,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) { @@ -10885,15 +9839,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; @@ -10903,7 +9856,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); @@ -10913,16 +9865,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], @@ -10935,10 +9887,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; } @@ -10947,8 +9898,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) { @@ -10971,10 +9925,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 { @@ -10996,21 +9950,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]); } @@ -11021,10 +9973,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; } } @@ -11032,8 +9982,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) { @@ -11056,11 +10009,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); @@ -11069,10 +10022,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; } @@ -11088,23 +10040,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]); } @@ -11116,10 +10065,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; } } @@ -11129,6 +10076,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) { @@ -11151,7 +10101,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]; @@ -11185,7 +10135,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]; @@ -11195,34 +10145,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(); @@ -11236,12 +10186,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; } } @@ -11251,7 +10201,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]; @@ -11267,26 +10217,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); @@ -11294,7 +10246,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); @@ -11342,11 +10294,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); @@ -11381,7 +10333,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) { @@ -11392,15 +10344,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; @@ -11445,8 +10397,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; @@ -11481,7 +10433,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; @@ -11505,7 +10457,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; @@ -11542,19 +10494,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); @@ -11566,7 +10523,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; @@ -11584,7 +10541,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); @@ -11617,14 +10574,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; } @@ -11637,8 +10594,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; @@ -11654,13 +10611,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; @@ -11679,7 +10636,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) { @@ -11702,17 +10659,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; @@ -11732,10 +10689,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); @@ -11747,17 +10704,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; @@ -11777,10 +10734,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); @@ -11812,7 +10769,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 @@ -12018,78 +10975,535 @@ public class PGraphicsOpenGL extends PGraphics { // ----------------------------------------------------------------- // - // 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; 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() { @@ -12167,7 +11581,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 // @@ -12214,17 +11628,9 @@ public class PGraphicsOpenGL extends PGraphics { vertFirst = cache.vertexCount[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() { diff --git a/core/src/processing/opengl/PJOGL.java b/core/src/processing/opengl/PJOGL.java new file mode 100644 index 000000000..597d63ae3 --- /dev/null +++ b/core/src/processing/opengl/PJOGL.java @@ -0,0 +1,2492 @@ +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 static GL gl; + + /** GLU interface **/ + public static GLU glu; + + /** The rendering context (holds rendering state info) */ + public static GLContext context; + + /** The canvas where OpenGL rendering takes place */ + public static 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 static GLCapabilitiesImmutable capabilities; + + /** The rendering surface */ + protected static GLDrawable drawable; + + /** GLES2 functionality (shaders, etc) */ + protected static GL2ES2 gl2; + + /** GL3 interface */ + protected static GL2GL3 gl3; + + /** 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; + + /** 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 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; + + 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); + if (glu == null) glu = new GLU(); + } + + + @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); + } + } 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; + } + + // 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); + 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) { + window = GLWindow.create(caps); + 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(); + + canvas = canvasNEWT; + canvasAWT = null; + } + + registerListeners(); + + fboLayerCreated = false; + fboLayerInUse = false; + firstFrame = true; + setFps = false; + } + + + @Override + protected void reinitSurface() { + sinkFBO = backFBO = frontFBO = null; + fboLayerCreated = false; + fboLayerInUse = false; + firstFrame = true; + } + + + @Override + protected void registerListeners() { + if (WINDOW_TOOLKIT == AWT) { + pg.parent.removeListeners(pg.parent); + pg.parent.addListeners(canvasAWT); + + listener = new PGLListener(); + canvasAWT.addGLEventListener(listener); + } else if (WINDOW_TOOLKIT == NEWT) { + 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); + } + + listener = new PGLListener(); + window.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) { + window.removeGLEventListener(listener); + } + GLProfile.shutdown(); + } + + + @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(); + 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(); + 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.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()) { + // 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 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) { + 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); + } + } + } + } + + + @Override + protected void swapBuffers() { + if (WINDOW_TOOLKIT == AWT) { + canvasAWT.swapBuffers(); + } else if (WINDOW_TOOLKIT == NEWT) { + window.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 { + // 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 ((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 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) { + } + + @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 { + @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 { + @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; + } + + + @Override + protected String[] convertFragmentSource(String[] fragSrc0, + int version0, int version1) { + 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; + } + + + @Override + protected String[] convertVertexSource(String[] vertSrc0, + int version0, int version1) { + 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; + } + + + /////////////////////////////////////////////////////////// + + // 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 + public void bindFramebuffer(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 03eca7517..87c8e311b 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,52 @@ 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 pg; 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 +94,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 +108,51 @@ public class PShader { protected IntBuffer intBuffer; protected FloatBuffer floatBuffer; + // Uniforms common to all shader types + protected int transformMatLoc; + protected int modelviewMatLoc; + protected int projectionMatLoc; + protected int bufferLoc; + protected int bufferUnit; + 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,13 +169,14 @@ 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; context = pgl.createEmptyContext(); } @@ -125,13 +192,14 @@ public class PShader { */ public PShader(PApplet parent, String vertFilename, String fragFilename) { this.parent = parent; - pgMain = (PGraphicsOpenGL) parent.g; pgl = PGraphicsOpenGL.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 +207,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 +230,14 @@ public class PShader { */ public PShader(PApplet parent, URL vertURL, URL fragURL) { this.parent = parent; - pgMain = (PGraphicsOpenGL) parent.g; pgl = PGraphicsOpenGL.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 +245,53 @@ 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; + pgl = PGraphicsOpenGL.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 +315,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 +357,8 @@ public class PShader { consumeUniforms(); bindTextures(); } + + if (hasType()) bindTyped(); } @@ -221,6 +366,8 @@ public class PShader { * Unbinds the shader program. */ public void unbind() { + if (hasType()) unbindTyped(); + if (bound) { unbindTextures(); pgl.useProgram(0); @@ -264,7 +411,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 +443,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 +514,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 +569,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. @@ -563,7 +766,7 @@ public class PShader { pgl.uniform3i(loc, v[0], v[1], v[2]); } else if (val.type == UniformValue.INT4) { int[] v = ((int[])val.value); - pgl.uniform4i(loc, v[0], v[1], v[2], v[4]); + pgl.uniform4i(loc, v[0], v[1], v[2], v[3]); } else if (val.type == UniformValue.FLOAT1) { float[] v = ((float[])val.value); pgl.uniform1f(loc, v[0]); @@ -622,7 +825,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 = PGraphicsOpenGL.pgPrimary.getTexture(img); if (textures == null) textures = new HashMap(); textures.put(loc, tex); @@ -685,54 +888,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!"); - } - 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 +948,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); - pgl.shaderSource(glVertex, vertexShaderSource); + pgl.shaderSource(glVertex, PApplet.join(vertexShaderSource, "\n")); pgl.compileShader(glVertex); pgl.getShaderiv(glVertex, PGL.COMPILE_STATUS, intBuffer); @@ -852,7 +984,7 @@ public class PShader { protected boolean compileFragmentShader() { glFragment = PGraphicsOpenGL.createGLSLFragShaderObject(context); - pgl.shaderSource(glFragment, fragmentShaderSource); + pgl.shaderSource(glFragment, PApplet.join(fragmentShaderSource, "\n")); pgl.compileShader(glFragment); pgl.getShaderiv(glFragment, PGL.COMPILE_STATUS, intBuffer); @@ -867,16 +999,6 @@ 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); @@ -892,6 +1014,411 @@ public class PShader { } } + 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.pg = pg; + } + + boolean loadedAttributes = false; + 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; + } + + + boolean loadedUniforms = false; + 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"); + bufferLoc = getUniformLoc("buffer"); + + 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) { + pg.updateGLProjmodelview(); + setUniformMatrix(transformMatLoc, pg.glProjmodelview); + } + + if (-1 < modelviewMatLoc) { + pg.updateGLModelview(); + setUniformMatrix(modelviewMatLoc, pg.glModelview); + } + + if (-1 < projectionMatLoc) { + pg.updateGLProjection(); + setUniformMatrix(projectionMatLoc, pg.glProjection); + } + + if (-1 < viewportLoc) { + float x = pg.viewport.get(0); + float y = pg.viewport.get(1); + float w = pg.viewport.get(2); + float h = pg.viewport.get(3); + setUniformValue(viewportLoc, x, y, w, h); + } + + if (-1 < bufferLoc) { + bufferUnit = getLastTexUnit() + 1; + setUniformValue(bufferLoc, bufferUnit); + pgl.activeTexture(PGL.TEXTURE0 + bufferUnit); + pg.bindFrontTexture(); + } else { + bufferUnit = -1; + } + } + + protected void bindTyped() { + if (pg == null) { + setRenderer(PGraphicsOpenGL.pgCurrent); + 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) { + pg.updateGLNormal(); + setUniformMatrix(normalMatLoc, pg.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 = pg.lightCount; + setUniformValue(lightCountLoc, count); + if (0 < count) { + setUniformVector(lightPositionLoc, pg.lightPosition, 4, count); + setUniformVector(lightNormalLoc, pg.lightNormal, 3, count); + setUniformVector(lightAmbientLoc, pg.lightAmbient, 3, count); + setUniformVector(lightDiffuseLoc, pg.lightDiffuse, 3, count); + setUniformVector(lightSpecularLoc, pg.lightSpecular, 3, count); + setUniformVector(lightFalloffLoc, pg.lightFalloffCoefficients, + 3, count); + setUniformVector(lightSpotLoc, pg.lightSpotParameters, 2, count); + } + + if (-1 < directionLoc) pgl.enableVertexAttribArray(directionLoc); + + if (-1 < offsetLoc) pgl.enableVertexAttribArray(offsetLoc); + + if (-1 < perspectiveLoc) { + if (pg.getHint(ENABLE_STROKE_PERSPECTIVE) && + pg.nonOrthoProjection()) { + setUniformValue(perspectiveLoc, 1); + } else { + setUniformValue(perspectiveLoc, 0); + } + } + + if (-1 < scaleLoc) { + if (pg.getHint(DISABLE_OPTIMIZED_STROKE)) { + setUniformValue(scaleLoc, 1.0f, 1.0f, 1.0f); + } else { + float f = PGL.STROKE_DISPLACEMENT; + if (pg.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 < bufferLoc) { + pgl.requestFBOLayer(); + pgl.activeTexture(PGL.TEXTURE0 + bufferUnit); + pg.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 < bufferUnit ? bufferUnit + 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 0d9cbd2fe..704bbdea5 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,13 +261,37 @@ 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; + pg = PGraphicsOpenGL.pgCurrent; pgl = PGraphicsOpenGL.pgl; context = pgl.createEmptyContext(); @@ -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. @@ -330,6 +354,18 @@ public class PShapeOpenGL extends PShape { emissiveColor = pg.emissiveColor; shininess = pg.shininess; + 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; @@ -337,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. @@ -948,21 +984,27 @@ public class PShapeOpenGL extends PShape { child.solid(solid); } } else { - isSolid = solid; + this.solid = solid; } } @Override - public void beginContour() { - super.beginContour(); + protected void beginContourImpl() { breakShape = true; } + @Override + protected void endContourImpl() { + } + + @Override public void vertex(float x, float y) { vertexImpl(x, y, 0, 0, 0); + if (image != null) + PGraphics.showWarning(PGraphicsOpenGL.MISSING_UV_TEXCOORDS_ERROR); } @@ -975,6 +1017,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); } @@ -1022,24 +1066,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; } @@ -1079,7 +1122,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; } @@ -1121,7 +1164,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); + } } @@ -1163,13 +1210,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); + } } @@ -1208,46 +1263,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(); @@ -1258,30 +1298,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], @@ -1293,9 +1340,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, @@ -1331,7 +1398,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?? } @@ -1362,9 +1432,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); } @@ -1390,8 +1467,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); } @@ -1405,14 +1486,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(); + } } @@ -1432,8 +1519,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); } @@ -1805,7 +1894,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; @@ -1926,7 +2015,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(); } @@ -1940,7 +2029,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(); } @@ -1976,7 +2065,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(); } @@ -2000,7 +2089,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(); } @@ -2260,6 +2349,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]; + } + /////////////////////////////////////////////////////////// @@ -2267,6 +2400,7 @@ public class PShapeOpenGL extends PShape { // Tessellated geometry getter. + @Override public PShape getTessellation() { updateTessellation(); @@ -2362,6 +2496,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."); + } + } + + /////////////////////////////////////////////////////////// // @@ -2441,7 +2603,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(); @@ -2481,12 +2643,13 @@ public class PShapeOpenGL extends PShape { tessellator.setInGeometry(inGeo); tessellator.setTessGeometry(tessGeo); tessellator.setFill(fill || image != null); + tessellator.setTexCache(null, 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()); @@ -2520,9 +2683,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 @@ -2669,7 +2841,7 @@ public class PShapeOpenGL extends PShape { x2, y2, 0, x3, y3, 0, x4, y4, 0, - fill, stroke); + stroke); tessellator.tessellateQuads(); } @@ -2678,20 +2850,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]; @@ -2700,20 +2870,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(); } } @@ -2721,17 +2931,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(); } @@ -2739,25 +2984,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(); + } + } } @@ -2779,18 +3060,36 @@ public class PShapeOpenGL extends PShape { protected void tessellateSphere() { - // Getting sphere detail from renderer. Is this correct? - int nu = pg.sphereDetailU; - int nv = pg.sphereDetailV; float r = 0; - if (params.length == 1) { + int nu = sphereDetailU; + int nv = sphereDetailV; + if (1 <= params.length) { r = params[0]; + if (params.length == 2) { + nu = nv = (int)params[1]; + } else if (params.length == 3) { + nu = (int)params[1]; + nv = (int)params[2]; + } + } + + 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); + } } @@ -2801,18 +3100,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 @@ -2820,16 +3120,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 QUAD_BEZIER_VERTEX: + 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; @@ -2837,20 +3137,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 @@ -2859,24 +3158,23 @@ 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; - case QUAD_BEZIER_VERTEX: + case QUADRATIC_VERTEX: inGeo.addQuadraticVertex(vertices[idx+0][X], vertices[idx+0][Y], vertices[idx+0][Z], 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], @@ -2887,8 +3185,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; @@ -2896,22 +3194,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); + } + } /////////////////////////////////////////////////////////// @@ -3002,7 +3341,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); @@ -4039,10 +4377,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) { @@ -4216,10 +4607,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 || @@ -4275,19 +4670,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()) { @@ -4392,7 +4786,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; @@ -4408,10 +4802,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(); @@ -4492,7 +4883,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; @@ -4508,10 +4899,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(); @@ -4544,9 +4932,10 @@ public class PShapeOpenGL extends PShape { int perim; if (0 < size) { // round point weight = +size / 0.5f; - perim = PApplet.max(PGraphicsOpenGL.MIN_POINT_ACCURACY, + perim = PApplet.min(PGraphicsOpenGL.MAX_POINT_ACCURACY, + PApplet.max(PGraphicsOpenGL.MIN_POINT_ACCURACY, (int) (TWO_PI * weight / - PGraphicsOpenGL.POINT_ACCURACY_FACTOR)) + 1; + PGraphicsOpenGL.POINT_ACCURACY_FACTOR))) + 1; } else { // Square point weight = -size / 0.5f; perim = 5; 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 82f0a1f19..1c45ef481 100644 --- a/core/src/processing/opengl/Texture.java +++ b/core/src/processing/opengl/Texture.java @@ -1,1632 +1,1644 @@ -/* -*- 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.opengl; - -import processing.core.PApplet; -import processing.core.PConstants; -import processing.core.PGraphics; -//import processing.core.PImage; -import java.lang.reflect.Method; -import java.nio.ByteBuffer; -import java.nio.IntBuffer; -import java.util.LinkedList; -import java.util.NoSuchElementException; - -/** - * This class wraps an OpenGL texture. - * By Andres Colubri - * - */ -public class Texture implements PConstants { - // texture constants - - /** - * Texture with normalized UV. - */ - protected static final int TEX2D = 0; - /** - * Texture with un-normalized UV. - */ - protected static final int TEXRECT = 1; - - /** Point sampling: both magnification and minification filtering are set - * to nearest */ - protected static final int POINT = 2; - /** Linear sampling: magnification filtering is nearest, minification set - * 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 - * 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. */ - protected static final int TRILINEAR = 5; - - public int width, height; - - public int glName; - public int glTarget; - public int glFormat; - public int glMinFilter; - public int glMagFilter; - public int glWrapS; - public int glWrapT; - public int glWidth; - public int glHeight; - - 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 - // FrameBuffer object. - - protected boolean usingMipmaps; - protected boolean usingRepeat; - protected float maxTexcoordU; - protected float maxTexcoordV; - protected boolean bound; - - protected boolean invertedX; - protected boolean invertedY; - - protected int[] rgbaPixels = null; - protected IntBuffer pixelBuffer = null; - protected FrameBuffer tempFbo = null; - - /** Modified portion of the texture */ - protected boolean modified; - protected int mx1, my1, mx2, my2; - - protected Object bufferSource; - protected LinkedList bufferCache = null; - protected Method disposeBufferMethod; - public static final int MAX_BUFFER_CACHE_SIZE = 3; - - //////////////////////////////////////////////////////////// - - // Constructors. - - - public Texture() { - pgl = PGraphicsOpenGL.pgl; - context = pgl.createEmptyContext(); - - colorBuffer = false; - - glName = 0; - } - - - /** - * Creates an instance of PTexture with size width x height. The texture is - * initialized (empty) to that size. - * @param width int - * @param height int - */ - public Texture(int width, int height) { - this(width, height, new Parameters()); - } - - - /** - * Creates an instance of PTexture with size width x height and with the - * specified parameters. The texture is initialized (empty) to that size. - * @param width int - * @param height int - * @param params Parameters - */ - public Texture(int width, int height, Object params) { - pgl = PGraphicsOpenGL.pgl; - context = pgl.createEmptyContext(); - - colorBuffer = false; - - glName = 0; - - init(width, height, (Parameters)params); - } - - - @Override - protected void finalize() throws Throwable { - try { -// PApplet.println("finalize texture"); - if (glName != 0) { - PGraphicsOpenGL.finalizeTextureObject(glName, context); - } - } finally { - super.finalize(); - } - } - - - //////////////////////////////////////////////////////////// - - // Init, resize methods - - - /** - * Sets the size of the image and texture to width x height. If the texture is - * already initialized, it first destroys the current OpenGL texture object - * and then creates a new one with the specified size. - * @param width int - * @param height int - */ - public void init(int width, int height) { - Parameters params; - if (0 < glName) { - // Re-initializing a pre-existing texture. - // We use the current parameters as default: - params = getParameters(); - } else { - // Just built-in default parameters otherwise: - params = new Parameters(); - } - init(width, height, params); - } - - - /** - * Sets the size of the image and texture to width x height, and the - * parameters of the texture to params. If the texture is already initialized, - * it first destroys the current OpenGL texture object and then creates a new - * one with the specified size. - * @param width int - * @param height int - * @param params GLTextureParameters - */ - public void init(int width, int height, Parameters params) { - setParameters(params); - setSize(width, height); - allocate(); - } - - - /** - * Initializes the texture using GL parameters - */ - public void init(int width, int height, - int glName, int glTarget, int glFormat, - int glWidth, int glHeight, - int glMinFilter, int glMagFilter, - int glWrapS, int glWrapT) { - this.width = width; - this.height = height; - - this.glName = glName; - this.glTarget = glTarget; - this.glFormat = glFormat; - this.glWidth = glWidth; - this.glHeight = glHeight; - this.glMinFilter = glMinFilter; - this.glMagFilter = glMagFilter; - this.glWrapS = glWrapS; - this.glWrapT = glWrapT; - - maxTexcoordU = (float)width / glWidth; - maxTexcoordV = (float)height / glHeight; - - usingMipmaps = glMinFilter == PGL.LINEAR_MIPMAP_NEAREST || - glMinFilter == PGL.LINEAR_MIPMAP_LINEAR; - - usingRepeat = glWrapS == PGL.REPEAT || glWrapT == PGL.REPEAT; - } - - - public void resize(int wide, int high) { - // Marking the texture object as finalized so it is deleted - // when creating the new texture. - dispose(); - - // Creating new texture with the appropriate size. - Texture tex = new Texture(wide, high, getParameters()); - - // Copying the contents of this texture into tex. - tex.set(this); - - // Now, overwriting "this" with tex. - copyObject(tex); - - // Nullifying some utility objects so they are recreated with the - // appropriate size when needed. - tempFbo = null; - } - - - /** - * Returns true if the texture has been initialized. - * @return boolean - */ - public boolean available() { - return 0 < glName; - } - - - //////////////////////////////////////////////////////////// - - // Set methods - - - public void set(Texture tex) { - copyTexture(tex, 0, 0, tex.width, tex.height, true); - } - - - public void set(Texture tex, int x, int y, int w, int h) { - copyTexture(tex, x, y, w, h, true); - } - - - public void set(int texTarget, int texName, int texWidth, int texHeight, - int w, int h) { - copyTexture(texTarget, texName, texWidth, texHeight, 0, 0, w, h, true); - } - - - public void set(int texTarget, int texName, int texWidth, int texHeight, - int target, int tex, int x, int y, int w, int h) { - copyTexture(texTarget, texName, texWidth, texHeight, x, y, w, h, true); - } - - - public void set(int[] pixels) { - set(pixels, 0, 0, width, height, ARGB); - } - - - public void set(int[] pixels, int format) { - set(pixels, 0, 0, width, height, format); - } - - - public void set(int[] pixels, int x, int y, int w, int h) { - set(pixels, x, y, w, h, ARGB); - } - - - public void set(int[] pixels, int x, int y, int w, int h, int format) { - if (pixels == null) { - PGraphics.showWarning("The pixels array is null."); - return; - } - if (pixels.length < w * h) { - PGraphics.showWarning("The pixel array has a length of " + - pixels.length + ", but it should be at least " + - w * h); - return; - } - - if (pixels.length == 0) { - // Nothing to do (means that w == h == 0) but not an erroneous situation - return; - } - - boolean enabledTex = false; - if (!pgl.texturingIsEnabled(glTarget)) { - pgl.enableTexturing(glTarget); - enabledTex = true; - } - pgl.bindTexture(glTarget, glName); - - if (usingMipmaps) { - if (PGraphicsOpenGL.autoMipmapGenSupported) { - // Automatic mipmap generation. - loadPixels(w * h); - convertToRGBA(pixels, rgbaPixels, format, w, h); - updatePixelBuffer(rgbaPixels); - pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, - pixelBuffer); - pgl.generateMipmap(glTarget); - } else { - // TODO: finish manual mipmap generation, replacing Bitmap with AWT's BufferedImage, - // making it work in npot textures (embed npot tex into larger pot tex?), subregions, - // and moving GLUtils.texImage2D (originally from Android SDK) into PGL. - // Actually, this whole code should go into PGL, so the Android implementation can - // use Bitmap, and desktop use BufferedImage. - - /* - if (w != width || h != height) { - System.err.println("Sorry but I don't know how to generate mipmaps for a subregion."); - return; - } - - // Code by Mike Miller obtained from here: - // http://insanitydesign.com/wp/2009/08/01/android-opengl-es-mipmaps/ - int w0 = glWidth; - int h0 = glHeight; - int[] argbPixels = new int[w0 * h0]; - convertToARGB(pixels, argbPixels, format); - int level = 0; - int denom = 1; - - // We create a Bitmap because then we use its built-in filtered downsampling - // functionality. - Bitmap bitmap = Bitmap.createBitmap(w0, h0, Config.ARGB_8888); - bitmap.setPixels(argbPixels, 0, w0, 0, 0, w0, h0); - - while (w0 >= 1 || h0 >= 1) { - //First of all, generate the texture from our bitmap and set it to the according level - GLUtils.texImage2D(glTarget, level, bitmap, 0); - - // We are done. - if (w0 == 1 && h0 == 1) { - break; - } - - // Increase the mipmap level - level++; - denom *= 2; - - // Downsampling bitmap. We must eventually arrive to the 1x1 level, - // and if the width and height are different, there will be a few 1D - // texture levels just before. - // This update formula also allows for NPOT resolutions. - w0 = PApplet.max(1, PApplet.floor((float)glWidth / denom)); - h0 = PApplet.max(1, PApplet.floor((float)glHeight / denom)); - // (see getScaledInstance in AWT Image) - Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, w0, h0, true); - - // Clean up - bitmap.recycle(); - bitmap = bitmap2; - } - */ - - loadPixels(w * h); - convertToRGBA(pixels, rgbaPixels, 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); - updatePixelBuffer(rgbaPixels); - pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, - pixelBuffer); - } - - pgl.bindTexture(glTarget, 0); - if (enabledTex) { - pgl.disableTexturing(glTarget); - } - - updateTexels(x, y, w, h); - } - - - //////////////////////////////////////////////////////////// - - // Native set methods - - - public void setNative(int[] pixels) { - setNative(pixels, 0, 0, width, height); - } - - - public void setNative(int[] pixels, int x, int y, int w, int h) { - updatePixelBuffer(pixels); - setNative(pixelBuffer, x, y, w, h); - } - - - public void setNative(IntBuffer pixBuf, int x, int y, int w, int h) { - if (pixBuf == null) { - pixBuf = null; - PGraphics.showWarning("The pixel buffer is null."); - return; - } - if (pixBuf.capacity() < w * h) { - PGraphics.showWarning("The pixel bufer has a length of " + - pixBuf.capacity() + ", but it should be at least " + - w * h); - return; - } - - if (pixBuf.capacity() == 0) { - // Nothing to do (means that w == h == 0) but not an erroneous situation - return; - } - - boolean enabledTex = false; - if (!pgl.texturingIsEnabled(glTarget)) { - pgl.enableTexturing(glTarget); - enabledTex = true; - } - pgl.bindTexture(glTarget, glName); - - if (usingMipmaps) { - if (PGraphicsOpenGL.autoMipmapGenSupported) { - pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, - pixBuf); - pgl.generateMipmap(glTarget); - } else { - pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, - pixBuf); - } - } else { - pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, - pixBuf); - } - - pgl.bindTexture(glTarget, 0); - if (enabledTex) { - pgl.disableTexturing(glTarget); - } - - updateTexels(x, y, w, h); - } - - - //////////////////////////////////////////////////////////// - - // Get methods - - - /** - * Copy texture to pixels. Involves video memory to main memory transfer (slow). - */ - public void get(int[] pixels) { - if (pixels == null) { - throw new RuntimeException("Trying to copy texture to null pixels array"); - } - if (pixels.length != width * height) { - throw new RuntimeException("Trying to copy texture to pixels array of " + - "wrong size"); - } - - if (tempFbo == null) { - tempFbo = new FrameBuffer(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); - tempFbo.readPixels(); - PGraphicsOpenGL.popFramebuffer(); - - tempFbo.getPixels(pixels); - convertToARGB(pixels); - - if (invertedX) flipArrayOnX(pixels, 1); - if (invertedY) flipArrayOnY(pixels, 1); - } - - - /** - * Copies the contents of the texture to the pixels array. - * @param pixels - */ -// public void loadPixels(int[] pixels) { -// if (hasBuffers()) { -// // Updates the texture AND the pixels array of the image at the same time, -// // getting the pixels directly from the buffer data (and thus avoiding -// // expensive transfer between video and main memory). -// bufferUpdate(pixels); -// } -// -// if (isModified()) { -// // Regular pixel copy from texture. -// get(pixels); -// } -// -// setModified(false); -// } - - - //////////////////////////////////////////////////////////// - - // Put methods (the source texture is not resized to cover the entire - // destination). - - - public void put(Texture tex) { - copyTexture(tex, 0, 0, tex.width, tex.height, false); - } - - - public void put(Texture tex, int x, int y, int w, int h) { - copyTexture(tex, x, y, w, h, false); - } - - - public void put(int texTarget, int texName, int texWidth, int texHeight, - int w, int h) { - copyTexture(texTarget, texName, texWidth, texHeight, 0, 0, w, h, false); - } - - - public void put(int texTarget, int texName, int texWidth, int texHeight, - int target, int tex, int x, int y, int w, int h) { - copyTexture(texTarget, texName, texWidth, texHeight, x, y, w, h, false); - } - - - //////////////////////////////////////////////////////////// - - // Get OpenGL parameters - - - /** - * Returns true or false whether or not the texture is using mipmaps. - * @return boolean - */ - public boolean usingMipmaps() { - return usingMipmaps; - } - - - public void usingMipmaps(boolean mipmaps, int sampling) { - if (mipmaps) { - if (glMinFilter != PGL.LINEAR_MIPMAP_NEAREST && - glMinFilter != PGL.LINEAR_MIPMAP_LINEAR) { - if (sampling == POINT) { - glMagFilter = PGL.NEAREST; - glMinFilter = PGL.NEAREST; - } else if (sampling == LINEAR) { - glMagFilter = PGL.NEAREST; - glMinFilter = - PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR; - } else if (sampling == BILINEAR) { - glMagFilter = PGL.LINEAR; - glMinFilter = - PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR; - } else if (sampling == TRILINEAR) { - glMagFilter = PGL.LINEAR; - glMinFilter = - PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_LINEAR : PGL.LINEAR; - } else { - throw new RuntimeException("Unknown texture filtering mode"); - } - } - - usingMipmaps = true; - } else { - if (glMinFilter == PGL.LINEAR_MIPMAP_NEAREST || - glMinFilter == PGL.LINEAR_MIPMAP_LINEAR) { - glMinFilter = PGL.LINEAR; - } - usingMipmaps = false; - } - - bind(); - pgl.texParameteri(glTarget, PGL.TEXTURE_MIN_FILTER, glMinFilter); - pgl.texParameteri(glTarget, PGL.TEXTURE_MAG_FILTER, glMagFilter); - if (usingMipmaps) { - if (PGraphicsOpenGL.autoMipmapGenSupported) { - pgl.generateMipmap(glTarget); - } else { - // TODO: need manual generation here.. - } - } - unbind(); - } - - - /** - * Returns true or false whether or not the texture is using repeat wrap mode - * along either U or V directions. - * @return boolean - */ - public boolean usingRepeat() { - return usingRepeat; - } - - - public void usingRepeat(boolean repeat) { - if (repeat) { - glWrapS = PGL.REPEAT; - glWrapT = PGL.REPEAT; - usingRepeat = true; - } else { - glWrapS = PGL.CLAMP_TO_EDGE; - glWrapT = PGL.CLAMP_TO_EDGE; - usingRepeat = false; - } - - bind(); - pgl.texParameteri(glTarget, PGL.TEXTURE_WRAP_S, glWrapS); - pgl.texParameteri(glTarget, PGL.TEXTURE_WRAP_T, glWrapT); - unbind(); - } - - - /** - * Returns the maximum possible value for the texture coordinate U - * (horizontal). - * @return float - */ - public float maxTexcoordU() { - return maxTexcoordU; - } - - - /** - * Returns the maximum possible value for the texture coordinate V (vertical). - * @return float - */ - public float maxTexcoordV() { - return maxTexcoordV; - } - - - /** - * Returns true if the texture is inverted along the horizontal direction. - * @return boolean; - */ - public boolean invertedX() { - return invertedX; - } - - - /** - * Sets the texture as inverted or not along the horizontal direction. - * @param v boolean; - */ - public void invertedX(boolean v) { - invertedX = v; - } - - - /** - * Returns true if the texture is inverted along the vertical direction. - * @return boolean; - */ - public boolean invertedY() { - return invertedY; - } - - - /** - * Sets the texture as inverted or not along the vertical direction. - * @param v boolean; - */ - public void invertedY(boolean v) { - invertedY = v; - } - - - //////////////////////////////////////////////////////////// - - // Bind/unbind - - - public void bind() { - // Binding a texture automatically enables texturing for the - // texture target from that moment onwards. Unbinding the texture - // won't disable texturing. - if (!pgl.texturingIsEnabled(glTarget)) { - pgl.enableTexturing(glTarget); - } - pgl.bindTexture(glTarget, glName); - bound = true; - } - - - public void unbind() { - if (pgl.textureIsBound(glTarget, glName)) { - // We don't want to unbind another texture - // that might be bound instead of this one. - if (!pgl.texturingIsEnabled(glTarget)) { - pgl.enableTexturing(glTarget); - pgl.bindTexture(glTarget, 0); - pgl.disableTexturing(glTarget); - } else { - pgl.bindTexture(glTarget, 0); - } - } - bound = false; - } - - - public boolean bound() { - // A true result might not necessarily mean that texturing is enabled - // (a texture can be bound to the target, but texturing is disabled). - return bound; - } - - - ////////////////////////////////////////////////////////////// - - // Modified flag - - - public boolean isModified() { - return modified; - } - - - public void setModified() { - modified = true; - } - - - public void setModified(boolean m) { - modified = m; - } - - - public int getModifiedX1() { - return mx1; - } - - - public int getModifiedX2() { - return mx2; - } - - - public int getModifiedY1() { - return my1; - } - - - public int getModifiedY2() { - return my2; - } - - - public void updateTexels() { - updateTexelsImpl(0, 0, width, height); - } - - - public void updateTexels(int x, int y, int w, int h) { - updateTexelsImpl(x, y, w, h); - } - - - protected void updateTexelsImpl(int x, int y, int w, int h) { - int x2 = x + w; - int y2 = y + h; - - if (!modified) { - mx1 = PApplet.max(0, x); - mx2 = PApplet.min(width - 1, x2); - my1 = PApplet.max(0, y); - my2 = PApplet.min(height - 1, y2); - modified = true; - - } else { - if (x < mx1) mx1 = PApplet.max(0, x); - if (x > mx2) mx2 = PApplet.min(width - 1, x); - if (y < my1) my1 = PApplet.max(0, y); - if (y > my2) my2 = y; - - if (x2 < mx1) mx1 = PApplet.max(0, x2); - if (x2 > mx2) mx2 = PApplet.min(width - 1, x2); - if (y2 < my1) my1 = PApplet.max(0, y2); - if (y2 > my2) my2 = PApplet.min(height - 1, y2); - } - } - - - protected void loadPixels(int len) { - if (rgbaPixels == null || rgbaPixels.length < len) { - rgbaPixels = new int[len]; - } - } - - - protected void updatePixelBuffer(int[] pixels) { - pixelBuffer = PGL.updateIntBuffer(pixelBuffer, pixels, true); - } - - - //////////////////////////////////////////////////////////// - - // Buffer sink interface. - - - public void setBufferSource(Object source) { - bufferSource = source; - getSourceMethods(); - } - - - public void copyBufferFromSource(Object natRef, ByteBuffer byteBuf, - int w, int h) { - if (bufferCache == null) { - bufferCache = new LinkedList(); - } - - if (bufferCache.size() + 1 <= MAX_BUFFER_CACHE_SIZE) { - bufferCache.add(new BufferData(natRef, byteBuf.asIntBuffer(), w, h)); - } else { - // The buffer cache reached the maximum size, so we just dispose - // the new buffer. - try { - disposeBufferMethod.invoke(bufferSource, new Object[] { natRef }); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - - public boolean hasBufferSource() { - return bufferSource != null; - } - - - public boolean hasBuffers() { - return bufferSource != null && bufferCache != null && - 0 < bufferCache.size(); - } - - - protected boolean bufferUpdate() { - BufferData data = null; - try { - data = bufferCache.remove(0); - } catch (NoSuchElementException ex) { - PGraphics.showWarning("Don't have pixel data to copy to texture"); - } - - if (data != null) { - if ((data.w != width) || (data.h != height)) { - init(data.w, data.h); - } - setNative(data.rgbBuf, 0, 0, width, height); - - data.dispose(); - - return true; - } else { - return false; - } - } - - - protected boolean bufferUpdate(int[] pixels) { - BufferData data = null; - try { - data = bufferCache.remove(0); - } catch (NoSuchElementException ex) { - PGraphics.showWarning("Don't have pixel data to copy to texture"); - } - - if (data != null) { - if ((data.w != width) || (data.h != height)) { - init(data.w, data.h); - } - setNative(data.rgbBuf, 0, 0, width, height); - - data.rgbBuf.get(pixels); - convertToARGB(pixels); - - data.dispose(); - - return true; - } else { - return false; - } - } - - - protected void getSourceMethods() { - try { - disposeBufferMethod = bufferSource.getClass(). - getMethod("disposeBuffer", new Class[] { Object.class }); - } catch (Exception e) { - throw new RuntimeException("Provided source object doesn't have a " + - "disposeBuffer method."); - } - } - - - //////////////////////////////////////////////////////////// - - // Utilities - - - /** - * Flips intArray along the X axis. - * @param intArray int[] - * @param mult int - */ - protected void flipArrayOnX(int[] intArray, int mult) { - int index = 0; - int xindex = mult * (width - 1); - for (int x = 0; x < width / 2; x++) { - for (int y = 0; y < height; y++) { - int i = index + mult * y * width; - int j = xindex + mult * y * width; - - for (int c = 0; c < mult; c++) { - int temp = intArray[i]; - intArray[i] = intArray[j]; - intArray[j] = temp; - - i++; - j++; - } - - } - index += mult; - xindex -= mult; - } - } - - - /** - * Flips intArray along the Y axis. - * @param intArray int[] - * @param mult int - */ - protected void flipArrayOnY(int[] intArray, int mult) { - int index = 0; - int yindex = mult * (height - 1) * width; - for (int y = 0; y < height / 2; y++) { - for (int x = 0; x < mult * width; x++) { - int temp = intArray[index]; - intArray[index] = intArray[yindex]; - intArray[yindex] = temp; - - index++; - yindex++; - } - yindex -= mult * width * 2; - } - } - - - /** - * 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 - * @param w int - * @param h int - */ - protected void convertToRGBA(int[] intArray, int[] tIntArray, int arrayFormat, - int w, int h) { - if (PGL.BIG_ENDIAN) { - switch (arrayFormat) { - 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]; - } - 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; - } - 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); - } - break; - } - - } else { - // LITTLE_ENDIAN - // ARGB native, and RGBA opengl means ABGR on windows - // 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) { - 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; - } - 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); - } - 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); - } - break; - - } - - } - } - - - /** - * Reorders an OpenGL pixel array (RGBA) into ARGB. The array must be - * of size width * height. - * @param intArray int[] - */ - protected void convertToARGB(int[] intArray) { - int t = 0; - int p = 0; - if (PGL.BIG_ENDIAN) { - - // RGBA to ARGB conversion: shifting RGB 8 bits to the right, - // 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); - } - } - - } else { - - // We have to convert ABGR into ARGB, so R and B must be swapped, - // 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) | - (pixel & 0xFF00FF00); - - } - } - } - } - - - /////////////////////////////////////////////////////////// - - // Allocate/release texture. - - - protected void setSize(int w, int h) { - width = w; - height = h; - - if (PGraphicsOpenGL.npotTexSupported) { - glWidth = w; - glHeight = h; - } else { - glWidth = PGL.nextPowerOfTwo(w); - glHeight = PGL.nextPowerOfTwo(h); - } - - if (glWidth > PGraphicsOpenGL.maxTextureSize || - glHeight > PGraphicsOpenGL.maxTextureSize) { - glWidth = glHeight = 0; - throw new RuntimeException("Image width and height cannot be" + - " larger than " + - PGraphicsOpenGL.maxTextureSize + - " with this graphics card."); - } - - // If non-power-of-two textures are not supported, and the specified width - // or height is non-power-of-two, then glWidth (glHeight) will be greater - // than w (h) because it is chosen to be the next power of two, and this - // quotient will give the appropriate maximum texture coordinate value given - // this situation. - maxTexcoordU = (float)width / glWidth; - maxTexcoordV = (float)height / glHeight; - } - - - /** - * Allocates the opengl texture object. - */ - protected void allocate() { - dispose(); // Just in the case this object is being re-allocated. - - boolean enabledTex = false; - if (!pgl.texturingIsEnabled(glTarget)) { - pgl.enableTexturing(glTarget); - enabledTex = true; - } - - context = pgl.getCurrentContext(); - glName = PGraphicsOpenGL.createTextureObject(context); - - pgl.bindTexture(glTarget, glName); - pgl.texParameteri(glTarget, PGL.TEXTURE_MIN_FILTER, glMinFilter); - pgl.texParameteri(glTarget, PGL.TEXTURE_MAG_FILTER, glMagFilter); - pgl.texParameteri(glTarget, PGL.TEXTURE_WRAP_S, glWrapS); - pgl.texParameteri(glTarget, PGL.TEXTURE_WRAP_T, glWrapT); - if (PGraphicsOpenGL.anisoSamplingSupported) { - pgl.texParameterf(glTarget, PGL.TEXTURE_MAX_ANISOTROPY, - PGraphicsOpenGL.maxAnisoAmount); - } - - // First, we use glTexImage2D to set the full size of the texture (glW/glH - // might be diff from w/h in the case that the GPU doesn't support NPOT - // textures) - pgl.texImage2D(glTarget, 0, glFormat, glWidth, glHeight, 0, - PGL.RGBA, PGL.UNSIGNED_BYTE, null); - - // Makes sure that the texture buffer in video memory doesn't contain - // any garbage. - pgl.initTexture(glTarget, PGL.RGBA, width, height); - - pgl.bindTexture(glTarget, 0); - if (enabledTex) { - pgl.disableTexturing(glTarget); - } - bound = false; - } - - - /** - * Marks the texture object for deletion. - */ - protected void dispose() { - if (glName != 0) { - PGraphicsOpenGL.finalizeTextureObject(glName, context); - glName = 0; - } - } - - - protected boolean contextIsOutdated() { - boolean outdated = !pgl.contextIsCurrent(context); - if (outdated) { - // Removing the texture object from the renderer's list so it - // doesn't get deleted by OpenGL. The texture object was - // automatically disposed when the old context was destroyed. - PGraphicsOpenGL.removeTextureObject(glName, context); - - // And then set the id to zero, so it doesn't try to be - // deleted when the object's finalizer is invoked by the GC. - glName = 0; - } - return outdated; - } - - - public void colorBuffer(boolean value) { - colorBuffer = value; - } - - - public boolean colorBuffer() { - return colorBuffer; - } - - - /////////////////////////////////////////////////////////// - - // Utilities. - - - // Copies source texture tex into this. - protected void copyTexture(Texture tex, int x, int y, int w, int h, - boolean scale) { - if (tex == null) { - throw new RuntimeException("Source texture is null"); - } - - if (tempFbo == null) { - tempFbo = new FrameBuffer(glWidth, glHeight); - } - - // This texture is the color (destination) buffer of the FBO. - tempFbo.setColorBuffer(this); - tempFbo.disableDepthTest(); - - // FBO copy: - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(tempFbo); - // Clear the color buffer to make sure that the alpha of the - pgl.clearColor(0, 0, 0, 0); - pgl.clear(PGL.COLOR_BUFFER_BIT); - if (scale) { - // Rendering tex into "this", and scaling the source rectangle - // 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); - - } else { - // Rendering tex into "this" but without scaling so the contents - // of the source texture fall in the corresponding texels of the - // destination. - pgl.drawTexture(tex.glTarget, tex.glName, - tex.glWidth, tex.glHeight, tempFbo.width, tempFbo.height, - x, y, w, h, x, y, w, h); - } - PGraphicsOpenGL.popFramebuffer(); - updateTexels(x, y, w, h); - } - - - // Copies source texture tex into this. - protected void copyTexture(int texTarget, int texName, - int texWidth, int texHeight, - int x, int y, int w, int h, boolean scale) { - if (tempFbo == null) { - tempFbo = new FrameBuffer(glWidth, glHeight); - } - - // This texture is the color (destination) buffer of the FBO. - tempFbo.setColorBuffer(this); - tempFbo.disableDepthTest(); - - // FBO copy: - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(tempFbo); - if (scale) { - // Rendering tex into "this", and scaling the source rectangle - // to cover the entire destination region. - pgl.drawTexture(texTarget, texName, - texWidth, texHeight, tempFbo.width, tempFbo.height, - x, y, w, h, 0, 0, width, height); - - } else { - // Rendering tex into "this" but without scaling so the contents - // of the source texture fall in the corresponding texels of the - // destination. - pgl.drawTexture(texTarget, texName, - texWidth, texHeight, tempFbo.width, tempFbo.height, - x, y, w, h, x, y, w, h); - } - PGraphicsOpenGL.popFramebuffer(); - updateTexels(x, y, w, h); - } - - - protected void copyObject(Texture src) { - // The OpenGL texture of this object is replaced with the one from the - // source object, so we delete the former to avoid resource wasting. - dispose(); - - width = src.width; - height = src.height; - - glName = src.glName; - glTarget = src.glTarget; - glFormat = src.glFormat; - glMinFilter = src.glMinFilter; - glMagFilter = src.glMagFilter; - - glWidth= src.glWidth; - glHeight = src.glHeight; - - usingMipmaps = src.usingMipmaps; - usingRepeat = src.usingRepeat; - maxTexcoordU = src.maxTexcoordU; - maxTexcoordV = src.maxTexcoordV; - - invertedX = src.invertedX; - invertedY = src.invertedY; - } - - - /////////////////////////////////////////////////////////// - - // Parameter handling - - - public Parameters getParameters() { - Parameters res = new Parameters(); - - if (glTarget == PGL.TEXTURE_2D) { - res.target = TEX2D; - } - - if (glFormat == PGL.RGB) { - res.format = RGB; - } else if (glFormat == PGL.RGBA) { - res.format = ARGB; - } else if (glFormat == PGL.ALPHA) { - res.format = ALPHA; - } - - if (glMagFilter == PGL.NEAREST && glMinFilter == PGL.NEAREST) { - res.sampling = POINT; - res.mipmaps = false; - } else if (glMagFilter == PGL.NEAREST && glMinFilter == PGL.LINEAR) { - res.sampling = LINEAR; - res.mipmaps = false; - } else if (glMagFilter == PGL.NEAREST && - glMinFilter == PGL.LINEAR_MIPMAP_NEAREST) { - res.sampling = LINEAR; - res.mipmaps = true; - } else if (glMagFilter == PGL.LINEAR && glMinFilter == PGL.LINEAR) { - res.sampling = BILINEAR; - res.mipmaps = false; - } else if (glMagFilter == PGL.LINEAR && - glMinFilter == PGL.LINEAR_MIPMAP_NEAREST) { - res.sampling = BILINEAR; - res.mipmaps = true; - } else if (glMagFilter == PGL.LINEAR && - glMinFilter == PGL.LINEAR_MIPMAP_LINEAR) { - res.sampling = TRILINEAR; - res.mipmaps = true; - } - - if (glWrapS == PGL.CLAMP_TO_EDGE) { - res.wrapU = CLAMP; - } else if (glWrapS == PGL.REPEAT) { - res.wrapU = REPEAT; - } - - if (glWrapT == PGL.CLAMP_TO_EDGE) { - res.wrapV = CLAMP; - } else if (glWrapT == PGL.REPEAT) { - res.wrapV = REPEAT; - } - - return res; - } - - - /** - * Sets texture target and internal format according to the target and - * type specified. - * @param target int - * @param params GLTextureParameters - */ - protected void setParameters(Parameters params) { - if (params.target == TEX2D) { - glTarget = PGL.TEXTURE_2D; - } else { - throw new RuntimeException("Unknown texture target"); - } - - if (params.format == RGB) { - glFormat = PGL.RGB; - } else if (params.format == ARGB) { - glFormat = PGL.RGBA; - } else if (params.format == ALPHA) { - glFormat = PGL.ALPHA; - } else { - throw new RuntimeException("Unknown texture format"); - } - - 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; - } else if (params.sampling == BILINEAR) { - glMagFilter = PGL.LINEAR; - glMinFilter = params.mipmaps && PGL.MIPMAPS_ENABLED ? - 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; - } else { - throw new RuntimeException("Unknown texture filtering mode"); - } - - if (params.wrapU == CLAMP) { - glWrapS = PGL.CLAMP_TO_EDGE; - } else if (params.wrapU == REPEAT) { - glWrapS = PGL.REPEAT; - } else { - throw new RuntimeException("Unknown wrapping mode"); - } - - if (params.wrapV == CLAMP) { - glWrapT = PGL.CLAMP_TO_EDGE; - } else if (params.wrapV == REPEAT) { - glWrapT = PGL.REPEAT; - } else { - throw new RuntimeException("Unknown wrapping mode"); - } - - usingMipmaps = glMinFilter == PGL.LINEAR_MIPMAP_NEAREST || - glMinFilter == PGL.LINEAR_MIPMAP_LINEAR; - - usingRepeat = glWrapS == PGL.REPEAT || glWrapT == PGL.REPEAT; - - invertedX = false; - invertedY = false; - } - - - /////////////////////////////////////////////////////////////////////////// - - // Parameters object - - - /** - * This class stores the parameters for a texture: target, internal format, - * minimization filter and magnification filter. - */ - static public class Parameters { - /** - * Texture target. - */ - public int target; - - /** - * Texture internal format. - */ - public int format; - - /** - * Texture filtering (POINT, LINEAR, BILINEAR or TRILINEAR). - */ - public int sampling; - - /** - * Use mipmaps or not. - */ - public boolean mipmaps; - - /** - * Wrapping mode along U. - */ - public int wrapU; - - /** - * Wrapping mode along V. - */ - public int wrapV; - - /** - * Sets all the parameters to default values. - */ - public Parameters() { - this.target = TEX2D; - this.format = ARGB; - this.sampling = BILINEAR; - this.mipmaps = true; - this.wrapU = CLAMP; - this.wrapV = CLAMP; - } - - public Parameters(int format) { - this.target = TEX2D; - this.format = format; - this.sampling = BILINEAR; - this.mipmaps = true; - this.wrapU = CLAMP; - this.wrapV = CLAMP; - } - - public Parameters(int format, int sampling) { - this.target = TEX2D; - this.format = format; - this.sampling = sampling; - this.mipmaps = true; - this.wrapU = CLAMP; - this.wrapV = CLAMP; - } - - public Parameters(int format, int sampling, boolean mipmaps) { - this.target = TEX2D; - this.format = format; - this.mipmaps = mipmaps; - if (sampling == TRILINEAR && !mipmaps) { - this.sampling = BILINEAR; - } else { - this.sampling = sampling; - } - this.wrapU = CLAMP; - this.wrapV = CLAMP; - } - - public Parameters(int format, int sampling, boolean mipmaps, int wrap) { - this.target = TEX2D; - this.format = format; - this.mipmaps = mipmaps; - if (sampling == TRILINEAR && !mipmaps) { - this.sampling = BILINEAR; - } else { - this.sampling = sampling; - } - this.wrapU = wrap; - this.wrapV = wrap; - } - - public Parameters(Parameters src) { - set(src); - } - - public void set(int format) { - this.format = format; - } - - public void set(int format, int sampling) { - this.format = format; - this.sampling = sampling; - } - - public void set(int format, int sampling, boolean mipmaps) { - this.format = format; - this.sampling = sampling; - this.mipmaps = mipmaps; - } - - public void set(Parameters src) { - this.target = src.target; - this.format = src.format; - this.sampling = src.sampling; - this.mipmaps = src.mipmaps; - this.wrapU = src.wrapU; - this.wrapV = src.wrapV; - } - } - - /** - * This class stores a buffer copied from the buffer source. - * - */ - protected class BufferData { - int w, h; - // Native buffer object. - Object natBuf; - // Buffer viewed as int. - IntBuffer rgbBuf; - - BufferData(Object nat, IntBuffer rgb, int w, int h) { - natBuf = nat; - rgbBuf = rgb; - this.w = w; - this.h = h; - } - - void dispose() { - try { - // Disposing the native buffer. - disposeBufferMethod.invoke(bufferSource, new Object[] { natBuf }); - natBuf = null; - rgbBuf = null; - } catch (Exception e) { - e.printStackTrace(); - } - } - } -} +/* + 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.opengl; + +import processing.core.PApplet; +import processing.core.PConstants; +import processing.core.PGraphics; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.LinkedList; +import java.util.NoSuchElementException; + +/** + * This class wraps an OpenGL texture. + * By Andres Colubri + * + */ +public class Texture implements PConstants { + /** + * Texture with normalized UV. + */ + protected static final int TEX2D = 0; + /** + * Texture with un-normalized UV. + */ + protected static final int TEXRECT = 1; + + /** Point sampling: both magnification and minification filtering are set + * to nearest */ + protected static final int POINT = 2; + /** Linear sampling: magnification filtering is nearest, minification set + * 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 + * 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. */ + 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; + public int glTarget; + public int glFormat; + public int glMinFilter; + public int glMagFilter; + public int glWrapS; + public int glWrapT; + public int glWidth; + public int glHeight; + + 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 + // FrameBuffer object. + + protected boolean usingMipmaps; + protected boolean usingRepeat; + protected float maxTexcoordU; + protected float maxTexcoordV; + protected boolean bound; + + protected boolean invertedX; + protected boolean invertedY; + + 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; + protected int mx1, my1, mx2, my2; + + protected Object bufferSource; + protected LinkedList bufferCache = null; + protected LinkedList usedBuffers = null; + protected Method disposeBufferMethod; + public static final int MAX_BUFFER_CACHE_SIZE = 3; + + //////////////////////////////////////////////////////////// + + // Constructors. + + + public Texture() { + pgl = PGraphicsOpenGL.pgl; + context = pgl.createEmptyContext(); + + colorBuffer = false; + + glName = 0; + } + + + /** + * Creates an instance of PTexture with size width x height. The texture is + * initialized (empty) to that size. + * @param width int + * @param height int + */ + public Texture(int width, int height) { + this(width, height, new Parameters()); + } + + + /** + * Creates an instance of PTexture with size width x height and with the + * specified parameters. The texture is initialized (empty) to that size. + * @param width int + * @param height int + * @param params Parameters + */ + public Texture(int width, int height, Object params) { + pgl = PGraphicsOpenGL.pgl; + context = pgl.createEmptyContext(); + + colorBuffer = false; + + glName = 0; + + init(width, height, (Parameters)params); + } + + + @Override + protected void finalize() throws Throwable { + try { + if (glName != 0) { + PGraphicsOpenGL.finalizeTextureObject(glName, context); + } + } finally { + super.finalize(); + } + } + + + //////////////////////////////////////////////////////////// + + // Init, resize methods + + + /** + * Sets the size of the image and texture to width x height. If the texture is + * already initialized, it first destroys the current OpenGL texture object + * and then creates a new one with the specified size. + * @param width int + * @param height int + */ + public void init(int width, int height) { + Parameters params; + if (0 < glName) { + // Re-initializing a pre-existing texture. + // We use the current parameters as default: + params = getParameters(); + } else { + // Just built-in default parameters otherwise: + params = new Parameters(); + } + init(width, height, params); + } + + + /** + * Sets the size of the image and texture to width x height, and the + * parameters of the texture to params. If the texture is already initialized, + * it first destroys the current OpenGL texture object and then creates a new + * one with the specified size. + * @param width int + * @param height int + * @param params GLTextureParameters + */ + public void init(int width, int height, Parameters params) { + setParameters(params); + setSize(width, height); + allocate(); + } + + + /** + * Initializes the texture using GL parameters + */ + public void init(int width, int height, + int glName, int glTarget, int glFormat, + int glWidth, int glHeight, + int glMinFilter, int glMagFilter, + int glWrapS, int glWrapT) { + this.width = width; + this.height = height; + + this.glName = glName; + this.glTarget = glTarget; + this.glFormat = glFormat; + this.glWidth = glWidth; + this.glHeight = glHeight; + this.glMinFilter = glMinFilter; + this.glMagFilter = glMagFilter; + this.glWrapS = glWrapS; + this.glWrapT = glWrapT; + + maxTexcoordU = (float)width / glWidth; + maxTexcoordV = (float)height / glHeight; + + usingMipmaps = glMinFilter == PGL.LINEAR_MIPMAP_NEAREST || + glMinFilter == PGL.LINEAR_MIPMAP_LINEAR; + + usingRepeat = glWrapS == PGL.REPEAT || glWrapT == PGL.REPEAT; + } + + + public void resize(int wide, int high) { + // Marking the texture object as finalized so it is deleted + // when creating the new texture. + dispose(); + + // Creating new texture with the appropriate size. + Texture tex = new Texture(wide, high, getParameters()); + + // Copying the contents of this texture into tex. + tex.set(this); + + // Now, overwriting "this" with tex. + copyObject(tex); + + // Nullifying some utility objects so they are recreated with the + // appropriate size when needed. + tempFbo = null; + } + + + /** + * Returns true if the texture has been initialized. + * @return boolean + */ + public boolean available() { + return 0 < glName; + } + + + //////////////////////////////////////////////////////////// + + // Set methods + + + public void set(Texture tex) { + copyTexture(tex, 0, 0, tex.width, tex.height, true); + } + + + public void set(Texture tex, int x, int y, int w, int h) { + copyTexture(tex, x, y, w, h, true); + } + + + public void set(int texTarget, int texName, int texWidth, int texHeight, + int w, int h) { + copyTexture(texTarget, texName, texWidth, texHeight, 0, 0, w, h, true); + } + + + public void set(int texTarget, int texName, int texWidth, int texHeight, + int target, int tex, int x, int y, int w, int h) { + copyTexture(texTarget, texName, texWidth, texHeight, x, y, w, h, true); + } + + + public void set(int[] pixels) { + set(pixels, 0, 0, width, height, ARGB); + } + + + public void set(int[] pixels, int format) { + set(pixels, 0, 0, width, height, format); + } + + + public void set(int[] pixels, int x, int y, int w, int h) { + set(pixels, x, y, w, h, ARGB); + } + + + public void set(int[] pixels, int x, int y, int w, int h, int format) { + if (pixels == null) { + PGraphics.showWarning("The pixels array is null."); + return; + } + if (pixels.length < w * h) { + PGraphics.showWarning("The pixel array has a length of " + + pixels.length + ", but it should be at least " + + w * h); + return; + } + + if (pixels.length == 0) { + // Nothing to do (means that w == h == 0) but not an erroneous situation + return; + } + + boolean enabledTex = false; + if (!pgl.texturingIsEnabled(glTarget)) { + pgl.enableTexturing(glTarget); + enabledTex = true; + } + pgl.bindTexture(glTarget, glName); + + if (usingMipmaps) { + if (PGraphicsOpenGL.autoMipmapGenSupported) { + // Automatic mipmap generation. + loadPixels(w * h); + convertToRGBA(pixels, format, w, h); + updatePixelBuffer(rgbaPixels); + pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, + pixelBuffer); + pgl.generateMipmap(glTarget); + } else { + // TODO: finish manual mipmap generation, replacing Bitmap with AWT's BufferedImage, + // making it work in npot textures (embed npot tex into larger pot tex?), subregions, + // and moving GLUtils.texImage2D (originally from Android SDK) into PGL. + // Actually, this whole code should go into PGL, so the Android implementation can + // use Bitmap, and desktop use BufferedImage. + + /* + if (w != width || h != height) { + System.err.println("Sorry but I don't know how to generate mipmaps for a subregion."); + return; + } + + // Code by Mike Miller obtained from here: + // http://insanitydesign.com/wp/2009/08/01/android-opengl-es-mipmaps/ + int w0 = glWidth; + int h0 = glHeight; + int[] argbPixels = new int[w0 * h0]; + convertToARGB(pixels, argbPixels, format); + int level = 0; + int denom = 1; + + // We create a Bitmap because then we use its built-in filtered downsampling + // functionality. + Bitmap bitmap = Bitmap.createBitmap(w0, h0, Config.ARGB_8888); + bitmap.setPixels(argbPixels, 0, w0, 0, 0, w0, h0); + + while (w0 >= 1 || h0 >= 1) { + //First of all, generate the texture from our bitmap and set it to the according level + GLUtils.texImage2D(glTarget, level, bitmap, 0); + + // We are done. + if (w0 == 1 && h0 == 1) { + break; + } + + // Increase the mipmap level + level++; + denom *= 2; + + // Downsampling bitmap. We must eventually arrive to the 1x1 level, + // and if the width and height are different, there will be a few 1D + // texture levels just before. + // This update formula also allows for NPOT resolutions. + w0 = PApplet.max(1, PApplet.floor((float)glWidth / denom)); + h0 = PApplet.max(1, PApplet.floor((float)glHeight / denom)); + // (see getScaledInstance in AWT Image) + Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, w0, h0, true); + + // Clean up + bitmap.recycle(); + bitmap = bitmap2; + } + */ + + loadPixels(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, format, w, h); + updatePixelBuffer(rgbaPixels); + pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, + pixelBuffer); + } + + pgl.bindTexture(glTarget, 0); + if (enabledTex) { + pgl.disableTexturing(glTarget); + } + + releasePixelBuffer(); + releaseRGBAPixels(); + + updateTexels(x, y, w, h); + } + + + //////////////////////////////////////////////////////////// + + // Native set methods + + + public void setNative(int[] pixels) { + setNative(pixels, 0, 0, width, height); + } + + + public void setNative(int[] pixels, int x, int y, int w, int h) { + updatePixelBuffer(pixels); + setNative(pixelBuffer, x, y, w, h); + releasePixelBuffer(); + } + + + public void setNative(IntBuffer pixBuf, int x, int y, int w, int h) { + if (pixBuf == null) { + pixBuf = null; + PGraphics.showWarning("The pixel buffer is null."); + return; + } + if (pixBuf.capacity() < w * h) { + PGraphics.showWarning("The pixel bufer has a length of " + + pixBuf.capacity() + ", but it should be at least " + + w * h); + return; + } + + if (pixBuf.capacity() == 0) { + // Nothing to do (means that w == h == 0) but not an erroneous situation + return; + } + + boolean enabledTex = false; + if (!pgl.texturingIsEnabled(glTarget)) { + pgl.enableTexturing(glTarget); + enabledTex = true; + } + pgl.bindTexture(glTarget, glName); + + if (usingMipmaps) { + if (PGraphicsOpenGL.autoMipmapGenSupported) { + pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, + pixBuf); + pgl.generateMipmap(glTarget); + } else { + pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, + pixBuf); + } + } else { + pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, + pixBuf); + } + + pgl.bindTexture(glTarget, 0); + if (enabledTex) { + pgl.disableTexturing(glTarget); + } + + updateTexels(x, y, w, h); + } + + + //////////////////////////////////////////////////////////// + + // Get methods + + + /** + * Copy texture to pixels. Involves video memory to main memory transfer (slow). + */ + public void get(int[] pixels) { + if (pixels == null) { + throw new RuntimeException("Trying to copy texture to null pixels array"); + } + if (pixels.length != width * height) { + throw new RuntimeException("Trying to copy texture to pixels array of " + + "wrong size"); + } + + if (tempFbo == null) { + tempFbo = new FrameBuffer(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); + tempFbo.readPixels(); + PGraphicsOpenGL.popFramebuffer(); + + tempFbo.getPixels(pixels); + convertToARGB(pixels); + + if (invertedX) flipArrayOnX(pixels, 1); + if (invertedY) flipArrayOnY(pixels, 1); + } + + + //////////////////////////////////////////////////////////// + + // Put methods (the source texture is not resized to cover the entire + // destination). + + + public void put(Texture tex) { + copyTexture(tex, 0, 0, tex.width, tex.height, false); + } + + + public void put(Texture tex, int x, int y, int w, int h) { + copyTexture(tex, x, y, w, h, false); + } + + + public void put(int texTarget, int texName, int texWidth, int texHeight, + int w, int h) { + copyTexture(texTarget, texName, texWidth, texHeight, 0, 0, w, h, false); + } + + + public void put(int texTarget, int texName, int texWidth, int texHeight, + int target, int tex, int x, int y, int w, int h) { + copyTexture(texTarget, texName, texWidth, texHeight, x, y, w, h, false); + } + + + //////////////////////////////////////////////////////////// + + // Get OpenGL parameters + + + /** + * Returns true or false whether or not the texture is using mipmaps. + * @return boolean + */ + public boolean usingMipmaps() { + return usingMipmaps; + } + + + public void usingMipmaps(boolean mipmaps, int sampling) { + if (mipmaps) { + if (glMinFilter != PGL.LINEAR_MIPMAP_NEAREST && + glMinFilter != PGL.LINEAR_MIPMAP_LINEAR) { + if (sampling == POINT) { + glMagFilter = PGL.NEAREST; + glMinFilter = PGL.NEAREST; + } else if (sampling == LINEAR) { + glMagFilter = PGL.NEAREST; + glMinFilter = + PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR; + } else if (sampling == BILINEAR) { + glMagFilter = PGL.LINEAR; + glMinFilter = + PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR; + } else if (sampling == TRILINEAR) { + glMagFilter = PGL.LINEAR; + glMinFilter = + PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_LINEAR : PGL.LINEAR; + } else { + throw new RuntimeException("Unknown texture filtering mode"); + } + } + + usingMipmaps = true; + } else { + if (glMinFilter == PGL.LINEAR_MIPMAP_NEAREST || + glMinFilter == PGL.LINEAR_MIPMAP_LINEAR) { + glMinFilter = PGL.LINEAR; + } + usingMipmaps = false; + } + + bind(); + pgl.texParameteri(glTarget, PGL.TEXTURE_MIN_FILTER, glMinFilter); + pgl.texParameteri(glTarget, PGL.TEXTURE_MAG_FILTER, glMagFilter); + if (usingMipmaps) { + if (PGraphicsOpenGL.autoMipmapGenSupported) { + pgl.generateMipmap(glTarget); + } else { + // TODO: need manual generation here.. + } + } + unbind(); + } + + + /** + * Returns true or false whether or not the texture is using repeat wrap mode + * along either U or V directions. + * @return boolean + */ + public boolean usingRepeat() { + return usingRepeat; + } + + + public void usingRepeat(boolean repeat) { + if (repeat) { + glWrapS = PGL.REPEAT; + glWrapT = PGL.REPEAT; + usingRepeat = true; + } else { + glWrapS = PGL.CLAMP_TO_EDGE; + glWrapT = PGL.CLAMP_TO_EDGE; + usingRepeat = false; + } + + bind(); + pgl.texParameteri(glTarget, PGL.TEXTURE_WRAP_S, glWrapS); + pgl.texParameteri(glTarget, PGL.TEXTURE_WRAP_T, glWrapT); + unbind(); + } + + + /** + * Returns the maximum possible value for the texture coordinate U + * (horizontal). + * @return float + */ + public float maxTexcoordU() { + return maxTexcoordU; + } + + + /** + * Returns the maximum possible value for the texture coordinate V (vertical). + * @return float + */ + public float maxTexcoordV() { + return maxTexcoordV; + } + + + /** + * Returns true if the texture is inverted along the horizontal direction. + * @return boolean; + */ + public boolean invertedX() { + return invertedX; + } + + + /** + * Sets the texture as inverted or not along the horizontal direction. + * @param v boolean; + */ + public void invertedX(boolean v) { + invertedX = v; + } + + + /** + * Returns true if the texture is inverted along the vertical direction. + * @return boolean; + */ + public boolean invertedY() { + return invertedY; + } + + + /** + * Sets the texture as inverted or not along the vertical direction. + * @param v boolean; + */ + public void invertedY(boolean v) { + invertedY = v; + } + + + //////////////////////////////////////////////////////////// + + // Bind/unbind + + + public void bind() { + // Binding a texture automatically enables texturing for the + // texture target from that moment onwards. Unbinding the texture + // won't disable texturing. + if (!pgl.texturingIsEnabled(glTarget)) { + pgl.enableTexturing(glTarget); + } + pgl.bindTexture(glTarget, glName); + bound = true; + } + + + public void unbind() { + if (pgl.textureIsBound(glTarget, glName)) { + // We don't want to unbind another texture + // that might be bound instead of this one. + if (!pgl.texturingIsEnabled(glTarget)) { + pgl.enableTexturing(glTarget); + pgl.bindTexture(glTarget, 0); + pgl.disableTexturing(glTarget); + } else { + pgl.bindTexture(glTarget, 0); + } + } + bound = false; + } + + + public boolean bound() { + // A true result might not necessarily mean that texturing is enabled + // (a texture can be bound to the target, but texturing is disabled). + return bound; + } + + + ////////////////////////////////////////////////////////////// + + // Modified flag + + + public boolean isModified() { + return modified; + } + + + public void setModified() { + modified = true; + } + + + public void setModified(boolean m) { + modified = m; + } + + + public int getModifiedX1() { + return mx1; + } + + + public int getModifiedX2() { + return mx2; + } + + + public int getModifiedY1() { + return my1; + } + + + public int getModifiedY2() { + return my2; + } + + + public void updateTexels() { + updateTexelsImpl(0, 0, width, height); + } + + + public void updateTexels(int x, int y, int w, int h) { + updateTexelsImpl(x, y, w, h); + } + + + protected void updateTexelsImpl(int x, int y, int w, int h) { + int x2 = x + w; + int y2 = y + h; + + if (!modified) { + mx1 = PApplet.max(0, x); + mx2 = PApplet.min(width - 1, x2); + my1 = PApplet.max(0, y); + my2 = PApplet.min(height - 1, y2); + modified = true; + + } else { + if (x < mx1) mx1 = PApplet.max(0, x); + if (x > mx2) mx2 = PApplet.min(width - 1, x); + if (y < my1) my1 = PApplet.max(0, y); + if (y > my2) my2 = y; + + if (x2 < mx1) mx1 = PApplet.max(0, x2); + if (x2 > mx2) mx2 = PApplet.min(width - 1, x2); + if (y2 < my1) my1 = PApplet.max(0, y2); + if (y2 > my2) my2 = PApplet.min(height - 1, y2); + } + } + + + protected void loadPixels(int len) { + if (rgbaPixels == null || rgbaPixels.length < len) { + rgbaPixels = new int[len]; + } + } + + + protected void updatePixelBuffer(int[] pixels) { + pixelBuffer = PGL.updateIntBuffer(pixelBuffer, pixels, true); + pixBufUpdateCount++; + } + + + //////////////////////////////////////////////////////////// + + // Buffer sink interface. + + + public void setBufferSource(Object source) { + bufferSource = source; + getSourceMethods(); + } + + + public void copyBufferFromSource(Object natRef, ByteBuffer byteBuf, + int w, int h) { + if (bufferCache == null) { + bufferCache = new LinkedList(); + } + + if (bufferCache.size() + 1 <= MAX_BUFFER_CACHE_SIZE) { + bufferCache.add(new BufferData(natRef, byteBuf.asIntBuffer(), w, h)); + } else { + // The buffer cache reached the maximum size, so we just dispose + // the new buffer by adding it to the list of used buffers. + try { + usedBuffers.add(new BufferData(natRef, byteBuf.asIntBuffer(), w, h)); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + + public void disposeSourceBuffer() { + if (usedBuffers == null) return; + + while (0 < usedBuffers.size()) { + BufferData data = null; + try { + data = usedBuffers.remove(0); + } catch (NoSuchElementException ex) { + PGraphics.showWarning("Cannot remove used buffer"); + } + if (data != null) { + data.dispose(); + } + } + } + + public void getBufferPixels(int[] pixels) { + 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(); + } + if (data != null) { + data.rgbBuf.rewind(); + data.rgbBuf.get(pixels); + convertToARGB(pixels); + } + } + + + public boolean hasBufferSource() { + return bufferSource != null; + } + + + public boolean hasBuffers() { + return bufferSource != null && bufferCache != null && + 0 < bufferCache.size(); + } + + + protected boolean bufferUpdate() { + BufferData data = null; + try { + data = bufferCache.remove(0); + } catch (NoSuchElementException ex) { + PGraphics.showWarning("Don't have pixel data to copy to texture"); + } + + if (data != null) { + if ((data.w != width) || (data.h != height)) { + init(data.w, data.h); + } + data.rgbBuf.rewind(); + setNative(data.rgbBuf, 0, 0, width, height); + + // Putting the buffer in the used buffers list to dispose at the end of + // draw. + if (usedBuffers == null) { + usedBuffers = new LinkedList(); + } + usedBuffers.add(data); + + return true; + } else { + return false; + } + } + + + protected void getSourceMethods() { + try { + disposeBufferMethod = bufferSource.getClass(). + getMethod("disposeBuffer", new Class[] { Object.class }); + } catch (Exception e) { + throw new RuntimeException("Provided source object doesn't have a " + + "disposeBuffer method."); + } + } + + + //////////////////////////////////////////////////////////// + + // Utilities + + + /** + * Flips intArray along the X axis. + * @param intArray int[] + * @param mult int + */ + protected void flipArrayOnX(int[] intArray, int mult) { + int index = 0; + int xindex = mult * (width - 1); + for (int x = 0; x < width / 2; x++) { + for (int y = 0; y < height; y++) { + int i = index + mult * y * width; + int j = xindex + mult * y * width; + + for (int c = 0; c < mult; c++) { + int temp = intArray[i]; + intArray[i] = intArray[j]; + intArray[j] = temp; + + i++; + j++; + } + + } + index += mult; + xindex -= mult; + } + } + + + /** + * Flips intArray along the Y axis. + * @param intArray int[] + * @param mult int + */ + protected void flipArrayOnY(int[] intArray, int mult) { + int index = 0; + int yindex = mult * (height - 1) * width; + for (int y = 0; y < height / 2; y++) { + for (int x = 0; x < mult * width; x++) { + int temp = intArray[index]; + intArray[index] = intArray[yindex]; + intArray[yindex] = temp; + + index++; + yindex++; + } + yindex -= mult * width * 2; + } + } + + + /** + * Reorders a pixel array in the given format into the order required by + * 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[] pixels, int format, int w, int h) { + if (PGL.BIG_ENDIAN) { + 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< 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< 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< pixels.length; i++) { + int pixel = pixels[i]; + rgbaPixels[i] = (pixel << 8) | ((pixel >> 24) & 0xFF); + } + break; + } + } else { + // LITTLE_ENDIAN + // ARGB native, and RGBA opengl means ABGR on windows + // 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 (format) { + case ALPHA: + // Converting xxxA into ARGB, with RGB set to white. + 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< 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 < 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 pixels int[] + */ + protected void convertToARGB(int[] pixels) { + int t = 0; + int p = 0; + if (PGL.BIG_ENDIAN) { + // RGBA to ARGB conversion: shifting RGB 8 bits to the right, + // and placing A 24 bits to the left. + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int pixel = pixels[p++]; + pixels[t++] = (pixel >>> 8) | ((pixel << 24) & 0xFF000000); + } + } + } else { + // We have to convert ABGR into ARGB, so R and B must be swapped, + // A and G just brought back in. + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int pixel = pixels[p++]; + pixels[t++] = ((pixel & 0xFF) << 16) | ((pixel & 0xFF0000) >> 16) | + (pixel & 0xFF00FF00); + } + } + } + } + + + /////////////////////////////////////////////////////////// + + // Allocate/release texture. + + + protected void setSize(int w, int h) { + width = w; + height = h; + + if (PGraphicsOpenGL.npotTexSupported) { + glWidth = w; + glHeight = h; + } else { + glWidth = PGL.nextPowerOfTwo(w); + glHeight = PGL.nextPowerOfTwo(h); + } + + if (glWidth > PGraphicsOpenGL.maxTextureSize || + glHeight > PGraphicsOpenGL.maxTextureSize) { + glWidth = glHeight = 0; + throw new RuntimeException("Image width and height cannot be" + + " larger than " + + PGraphicsOpenGL.maxTextureSize + + " with this graphics card."); + } + + // If non-power-of-two textures are not supported, and the specified width + // or height is non-power-of-two, then glWidth (glHeight) will be greater + // than w (h) because it is chosen to be the next power of two, and this + // quotient will give the appropriate maximum texture coordinate value given + // this situation. + maxTexcoordU = (float)width / glWidth; + maxTexcoordV = (float)height / glHeight; + } + + + /** + * Allocates the opengl texture object. + */ + protected void allocate() { + dispose(); // Just in the case this object is being re-allocated. + + boolean enabledTex = false; + if (!pgl.texturingIsEnabled(glTarget)) { + pgl.enableTexturing(glTarget); + enabledTex = true; + } + + context = pgl.getCurrentContext(); + glName = PGraphicsOpenGL.createTextureObject(context); + + pgl.bindTexture(glTarget, glName); + pgl.texParameteri(glTarget, PGL.TEXTURE_MIN_FILTER, glMinFilter); + pgl.texParameteri(glTarget, PGL.TEXTURE_MAG_FILTER, glMagFilter); + pgl.texParameteri(glTarget, PGL.TEXTURE_WRAP_S, glWrapS); + pgl.texParameteri(glTarget, PGL.TEXTURE_WRAP_T, glWrapT); + if (PGraphicsOpenGL.anisoSamplingSupported) { + pgl.texParameterf(glTarget, PGL.TEXTURE_MAX_ANISOTROPY, + PGraphicsOpenGL.maxAnisoAmount); + } + + // First, we use glTexImage2D to set the full size of the texture (glW/glH + // might be diff from w/h in the case that the GPU doesn't support NPOT + // textures) + pgl.texImage2D(glTarget, 0, glFormat, glWidth, glHeight, 0, + PGL.RGBA, PGL.UNSIGNED_BYTE, null); + + // Makes sure that the texture buffer in video memory doesn't contain + // any garbage. + pgl.initTexture(glTarget, PGL.RGBA, width, height); + + pgl.bindTexture(glTarget, 0); + if (enabledTex) { + pgl.disableTexturing(glTarget); + } + bound = false; + } + + + /** + * Marks the texture object for deletion. + */ + protected void dispose() { + if (glName != 0) { + PGraphicsOpenGL.finalizeTextureObject(glName, context); + glName = 0; + } + } + + + protected boolean contextIsOutdated() { + boolean outdated = !pgl.contextIsCurrent(context); + if (outdated) { + // Removing the texture object from the renderer's list so it + // doesn't get deleted by OpenGL. The texture object was + // automatically disposed when the old context was destroyed. + PGraphicsOpenGL.removeTextureObject(glName, context); + + // And then set the id to zero, so it doesn't try to be + // deleted when the object's finalizer is invoked by the GC. + glName = 0; + } + return outdated; + } + + + public void colorBuffer(boolean value) { + colorBuffer = value; + } + + + public boolean colorBuffer() { + return colorBuffer; + } + + + /////////////////////////////////////////////////////////// + + // Utilities. + + + // Copies source texture tex into this. + protected void copyTexture(Texture tex, int x, int y, int w, int h, + boolean scale) { + if (tex == null) { + throw new RuntimeException("Source texture is null"); + } + + if (tempFbo == null) { + tempFbo = new FrameBuffer(glWidth, glHeight); + } + + // This texture is the color (destination) buffer of the FBO. + tempFbo.setColorBuffer(this); + tempFbo.disableDepthTest(); + + // FBO copy: + PGraphicsOpenGL.pushFramebuffer(); + PGraphicsOpenGL.setFramebuffer(tempFbo); + // Clear the color buffer to make sure that the alpha channel is set to + // full transparency + pgl.clearColor(0, 0, 0, 0); + pgl.clear(PGL.COLOR_BUFFER_BIT); + if (scale) { + // Rendering tex into "this", and scaling the source rectangle + // 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); + + } else { + // Rendering tex into "this" but without scaling so the contents + // of the source texture fall in the corresponding texels of the + // destination. + pgl.drawTexture(tex.glTarget, tex.glName, + tex.glWidth, tex.glHeight, tempFbo.width, tempFbo.height, + x, y, w, h, x, y, w, h); + } + PGraphicsOpenGL.popFramebuffer(); + + updateTexels(x, y, w, h); + } + + + // Copies source texture tex into this. + protected void copyTexture(int texTarget, int texName, + int texWidth, int texHeight, + int x, int y, int w, int h, boolean scale) { + if (tempFbo == null) { + tempFbo = new FrameBuffer(glWidth, glHeight); + } + + // This texture is the color (destination) buffer of the FBO. + tempFbo.setColorBuffer(this); + tempFbo.disableDepthTest(); + + // FBO copy: + PGraphicsOpenGL.pushFramebuffer(); + PGraphicsOpenGL.setFramebuffer(tempFbo); + if (scale) { + // Rendering tex into "this", and scaling the source rectangle + // to cover the entire destination region. + pgl.drawTexture(texTarget, texName, + texWidth, texHeight, tempFbo.width, tempFbo.height, + x, y, w, h, 0, 0, width, height); + + } else { + // Rendering tex into "this" but without scaling so the contents + // of the source texture fall in the corresponding texels of the + // destination. + pgl.drawTexture(texTarget, texName, + texWidth, texHeight, tempFbo.width, tempFbo.height, + x, y, w, h, x, y, w, h); + } + PGraphicsOpenGL.popFramebuffer(); + updateTexels(x, y, w, h); + } + + + protected void copyObject(Texture src) { + // The OpenGL texture of this object is replaced with the one from the + // source object, so we delete the former to avoid resource wasting. + dispose(); + + width = src.width; + height = src.height; + + glName = src.glName; + glTarget = src.glTarget; + glFormat = src.glFormat; + glMinFilter = src.glMinFilter; + glMagFilter = src.glMagFilter; + + glWidth= src.glWidth; + glHeight = src.glHeight; + + usingMipmaps = src.usingMipmaps; + usingRepeat = src.usingRepeat; + maxTexcoordU = src.maxTexcoordU; + maxTexcoordV = src.maxTexcoordV; + + invertedX = src.invertedX; + invertedY = src.invertedY; + } + + + // 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 + + + public Parameters getParameters() { + Parameters res = new Parameters(); + + if (glTarget == PGL.TEXTURE_2D) { + res.target = TEX2D; + } + + if (glFormat == PGL.RGB) { + res.format = RGB; + } else if (glFormat == PGL.RGBA) { + res.format = ARGB; + } else if (glFormat == PGL.ALPHA) { + res.format = ALPHA; + } + + if (glMagFilter == PGL.NEAREST && glMinFilter == PGL.NEAREST) { + res.sampling = POINT; + res.mipmaps = false; + } else if (glMagFilter == PGL.NEAREST && glMinFilter == PGL.LINEAR) { + res.sampling = LINEAR; + res.mipmaps = false; + } else if (glMagFilter == PGL.NEAREST && + glMinFilter == PGL.LINEAR_MIPMAP_NEAREST) { + res.sampling = LINEAR; + res.mipmaps = true; + } else if (glMagFilter == PGL.LINEAR && glMinFilter == PGL.LINEAR) { + res.sampling = BILINEAR; + res.mipmaps = false; + } else if (glMagFilter == PGL.LINEAR && + glMinFilter == PGL.LINEAR_MIPMAP_NEAREST) { + res.sampling = BILINEAR; + res.mipmaps = true; + } else if (glMagFilter == PGL.LINEAR && + glMinFilter == PGL.LINEAR_MIPMAP_LINEAR) { + res.sampling = TRILINEAR; + res.mipmaps = true; + } + + if (glWrapS == PGL.CLAMP_TO_EDGE) { + res.wrapU = CLAMP; + } else if (glWrapS == PGL.REPEAT) { + res.wrapU = REPEAT; + } + + if (glWrapT == PGL.CLAMP_TO_EDGE) { + res.wrapV = CLAMP; + } else if (glWrapT == PGL.REPEAT) { + res.wrapV = REPEAT; + } + + return res; + } + + + /** + * Sets texture target and internal format according to the target and + * type specified. + * @param target int + * @param params GLTextureParameters + */ + protected void setParameters(Parameters params) { + if (params.target == TEX2D) { + glTarget = PGL.TEXTURE_2D; + } else { + throw new RuntimeException("Unknown texture target"); + } + + if (params.format == RGB) { + glFormat = PGL.RGB; + } else if (params.format == ARGB) { + glFormat = PGL.RGBA; + } else if (params.format == ALPHA) { + glFormat = PGL.ALPHA; + } else { + 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 = mipmaps ? PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR; + } else if (params.sampling == BILINEAR) { + glMagFilter = PGL.LINEAR; + glMinFilter = mipmaps ? PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR; + } else if (params.sampling == TRILINEAR) { + glMagFilter = PGL.LINEAR; + glMinFilter = mipmaps ? PGL.LINEAR_MIPMAP_LINEAR : PGL.LINEAR; + } else { + throw new RuntimeException("Unknown texture filtering mode"); + } + + if (params.wrapU == CLAMP) { + glWrapS = PGL.CLAMP_TO_EDGE; + } else if (params.wrapU == REPEAT) { + glWrapS = PGL.REPEAT; + } else { + throw new RuntimeException("Unknown wrapping mode"); + } + + if (params.wrapV == CLAMP) { + glWrapT = PGL.CLAMP_TO_EDGE; + } else if (params.wrapV == REPEAT) { + glWrapT = PGL.REPEAT; + } else { + throw new RuntimeException("Unknown wrapping mode"); + } + + usingMipmaps = glMinFilter == PGL.LINEAR_MIPMAP_NEAREST || + glMinFilter == PGL.LINEAR_MIPMAP_LINEAR; + + usingRepeat = glWrapS == PGL.REPEAT || glWrapT == PGL.REPEAT; + + invertedX = false; + invertedY = false; + } + + + /////////////////////////////////////////////////////////////////////////// + + // Parameters object + + + /** + * This class stores the parameters for a texture: target, internal format, + * minimization filter and magnification filter. + */ + static public class Parameters { + /** + * Texture target. + */ + public int target; + + /** + * Texture internal format. + */ + public int format; + + /** + * Texture filtering (POINT, LINEAR, BILINEAR or TRILINEAR). + */ + public int sampling; + + /** + * Use mipmaps or not. + */ + public boolean mipmaps; + + /** + * Wrapping mode along U. + */ + public int wrapU; + + /** + * Wrapping mode along V. + */ + public int wrapV; + + /** + * Sets all the parameters to default values. + */ + public Parameters() { + this.target = TEX2D; + this.format = ARGB; + this.sampling = BILINEAR; + this.mipmaps = true; + this.wrapU = CLAMP; + this.wrapV = CLAMP; + } + + public Parameters(int format) { + this.target = TEX2D; + this.format = format; + this.sampling = BILINEAR; + this.mipmaps = true; + this.wrapU = CLAMP; + this.wrapV = CLAMP; + } + + public Parameters(int format, int sampling) { + this.target = TEX2D; + this.format = format; + this.sampling = sampling; + this.mipmaps = true; + this.wrapU = CLAMP; + this.wrapV = CLAMP; + } + + public Parameters(int format, int sampling, boolean mipmaps) { + this.target = TEX2D; + this.format = format; + this.mipmaps = mipmaps; + if (sampling == TRILINEAR && !mipmaps) { + this.sampling = BILINEAR; + } else { + this.sampling = sampling; + } + this.wrapU = CLAMP; + this.wrapV = CLAMP; + } + + public Parameters(int format, int sampling, boolean mipmaps, int wrap) { + this.target = TEX2D; + this.format = format; + this.mipmaps = mipmaps; + if (sampling == TRILINEAR && !mipmaps) { + this.sampling = BILINEAR; + } else { + this.sampling = sampling; + } + this.wrapU = wrap; + this.wrapV = wrap; + } + + public Parameters(Parameters src) { + set(src); + } + + public void set(int format) { + this.format = format; + } + + public void set(int format, int sampling) { + this.format = format; + this.sampling = sampling; + } + + public void set(int format, int sampling, boolean mipmaps) { + this.format = format; + this.sampling = sampling; + this.mipmaps = mipmaps; + } + + public void set(Parameters src) { + this.target = src.target; + this.format = src.format; + this.sampling = src.sampling; + this.mipmaps = src.mipmaps; + this.wrapU = src.wrapU; + this.wrapV = src.wrapV; + } + } + + /** + * This class stores a buffer copied from the buffer source. + * + */ + protected class BufferData { + int w, h; + // Native buffer object. + Object natBuf; + // Buffer viewed as int. + IntBuffer rgbBuf; + + BufferData(Object nat, IntBuffer rgb, int w, int h) { + natBuf = nat; + rgbBuf = rgb; + this.w = w; + this.h = h; + } + + void dispose() { + try { + // Disposing the native buffer. + disposeBufferMethod.invoke(bufferSource, new Object[] { natBuf }); + natBuf = null; + rgbBuf = null; + } catch (Exception e) { + e.printStackTrace(); + } + } + } +} \ No newline at end of file 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 5cd384a37..c6d6d570b 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -1,16 +1,47 @@ -0219 core (2.0.1) +0224 core +X PImage resize() causes PImage not to be rendered in JAVA2D +X https://github.com/processing/processing/issues/2179 + +fixed in 2.1 +X draw() called again before finishing on OS X (retina issue) +X https://github.com/processing/processing/issues/1709 high -_ 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 +_ zero alpha values still a problem with retina renderer +_ https://github.com/processing/processing/issues/2030 +_ Sort out blending differences with P2D/P3D +_ 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 +_ 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 _ 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 +table +_ addRow() is not efficient, probably need to do the doubling +_ or have a setIncrement() function? +_ it would default to 1 on tables loaded from a file +_ and default to doubling when created with "new Table" +_ row count and array size are combined.. need to break apart +_ match and iterators +_ add match version that returns table that's only a pointer to original +_ save the constructor for the version that actually copies data +_ the table pointer version will be speedy and allow chaining decisions/misc _ add options for image.save() (or saveImage?) @@ -45,10 +76,6 @@ _ the registered method for size() also needs to be called _ get() or copy()? for vectors, image, etc. _ toArray(), toArray(float[]), toVectorArray(), toVectorArray(PVector[]) _ toColorArray(), toColorArray(float[])... -_ load/save methods.. is it save("blah.svg") or saveSVG("blah.svg") -X also works that way with tables -X decision: useExtension() or something like that -_ require people to put things in the data folder _ make sure that loadXxxx() methods are used after init() _ nasty errors when loadImage/Font/createFont/etc used outside _ decision: add error messages where possible @@ -297,6 +324,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 @@ -371,6 +400,9 @@ _ i.e. M with several coords means moveto followed by many linetos _ also curveto with multiple sets of points is ignored _ document somehow.. svg viewer will be discontinued _ http://www.adobe.com/svg/eol.html +_ PShape getVertex() not implemented properly for SVG files +X https://code.google.com/p/processing/issues/detail?id=1558 +_ https://github.com/processing/processing/issues/1596 CORE / PVector @@ -446,7 +478,9 @@ _ shared intf for 3D view data across PGraphicsOpenGL and PGraphicsAndroid3D _ libraries have to do a lot of casting _ opengl isn't drawing rectangles out to raw properly with fonts _ when not in textMode(SHAPE) should have rects - +_ InvScreenX, Y, Z to convert screen (mouse) coordinates to model coordinates +_ https://github.com/processing/processing/issues/1609 +X https://code.google.com/p/processing/issues/detail?id=1571 CORE / Mac OS X @@ -458,6 +492,9 @@ _ -Xdock:icon= CORE / Events +_ Implement a way to disable automatic key repeat +_ https://github.com/processing/processing/issues/1622 +X https://code.google.com/p/processing/issues/detail?id=1584 _ touch events.. can't do MouseEvent et al with Android _ http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html _ http://www.html5rocks.com/en/mobile/touch.html @@ -467,8 +504,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 c914961d9..100f787e8 100644 --- a/done.txt +++ b/done.txt @@ -1,3 +1,351 @@ +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 +X slash breaks syntax highlighting (with spaces) +X https://github.com/processing/processing/issues/1681 +X Fix from Github user hamzaissa +X selectInput() in exported OS X sketch treats .app package as a folder +X https://github.com/processing/processing/issues/1959 +X waiting on retina support for JDK 7 +o b86 supposed to have some support (not available yet) +o http://jdk8.java.net/download.html +X code with a NUL character causes an error +X https://github.com/processing/processing/issues/1973 +X also remove NUL characters when loading a file +X Add "Processing Foundation" to the Help menu +X https://github.com/processing/processing/issues/1908 +X Update JNA from 3.2.4 to 3.5.2 +X https://maven.java.net/content/repositories/releases/net/java/dev/jna/jna/3.5.2/jna-3.5.2.jar +X https://maven.java.net/content/repositories/releases/net/java/dev/jna/platform/3.5.2/platform-3.5.2.jar +X problem with associating .pde files +X https://github.com/processing/processing/issues/286 +X http://code.google.com/p/processing/issues/detail?id=247 +o In regedit: Navigate to Computer\HKEY_CLASSES_ROOT\Applications and find your .exe name. Navigate under its name to shell>open>command. In the Default change its location to the actual location of the executable, hit okay and then try and reassociate the file type as you normally would. +X UnsatisfiedLinkError causes huge message... +X error report cleanups haven't been fixed yet +X reported by Dan +X this should be better now +X add exception wrapper for startup +X Add methods to move files to Trash/Recycle Bin where available +X allow delete of files in unsaved sketches +X https://github.com/processing/processing/issues/1942 +X https://github.com/processing/processing/pull/1945 +X proxy server requirement causes problems +X contrib manager, update checks are broken +X https://github.com/processing/processing/issues/1476 +X might be able to fix this with something in preferences.txt? +X http://docs.oracle.com/javase/6/docs/technotes/guides/net/proxies.html +o Update Java version in the download to be the latest Java 6 +o https://github.com/processing/processing/issues/1841 +X try to use appbundler to create a version that includes a JRE (JDK) +X http://java.net/downloads/appbundler/appbundler.html +X docs.oracle.com/javase/7/docs/technotes/guides/jweb/packagingAppsForMac.html +X http://www.intransitione.com/blog/take-java-to-app-store/ +X hobbling along, should be ready soon + +cleaning/earlier +X common error messages +X with a proper list, we can add links when throwing an error in the PDE +X Dan started this on the Wiki +o build is currently broken for fresh checkout due to changes to file layout +o something that gets fixed by 'make clean' +o also test on windows and linux +o add a check to make sure that people aren't running from the dmg +o doesn't actually cause any problems, so don't bother? +o code to hide menubar.. just include JNA and call from there? +NSMenu.setMenubarVisible(false); +Then we used Cocoa via JNI: +if([NSMenu menuBarVisible]){ + [NSMenu setMenuBarVisible:NO]; +} +You can't do that from the AWT event thread. You need to do a -performSelectorOnMainThread to do that on the AppKit event thread. +Please see for more information, particularly the section about "Calling AppKit from AWT/Swing". + +manager +X change location of the manager download +X check to see if manager items from the download can be updated +X oops, probably not, because they're part of the distribution +X and folks won't be able to write to those directories +X changed manager to go to download.processing.org/latest.txt +X and uses a redirect from there (hopeully that's followed?) +X libraries need to support multiple categories +X https://github.com/processing/processing/issues/1970 +X restrict library categories to the ones in the document +X if it's not correct, shows up as 'other' +X catch Error (not just Exception) objects during load +X handles UnsupportedClassVersionError and others +X argh.. the 'old' folder is really poorly done +X attempt to install multiple will cause havoc (fail because 'old' exists) +o remove flagging for deletion +o half-installed mode causes a lot of trouble +o maybe it's reading from tmp folders? +o https://github.com/processing/processing/issues/1875 +X can't fix, no response +X remove "Compilations" category for libraries +X modes shouldn't have categories? +X was counting "Unknown" as a category +X modes and tools require restart (per ContributionType class) +X but no message is provided anywhere? +X mode install requires restart *and* still doesn't show as installed +X even though it gets added to the modes menu properly after the restart +X https://github.com/processing/processing/issues/1782 +X Update example list when library is installed +X https://github.com/processing/processing/issues/1909 +X https://github.com/processing/processing/pull/1925 +X Contributed modes should show up in mode menu after installation +X waiting for fixed CoffeeScript mode to test this one +X https://github.com/processing/processing/issues/1504 +X http://code.google.com/p/processing/issues/detail?id=1466 +X modes require restart, that's now properly shown as a message + + +0219 pde (2.0.1) +X modes, tools, libraries not copying/moving properly on Windows +X https://github.com/processing/processing/issues/1781 +X undo seems to not be going to the right location (now with example) +X https://github.com/processing/processing/issues/707 +X http://code.google.com/p/processing/issues/detail?id=668 +X fixes from Josh Giesbrecht +X line ending problem with args.txt for Windows when exporting from others +X (exporting from OS X to Windows) +X https://github.com/processing/processing/issues/1890 +X add option to remove the background image at the top of the window + + 0218 pde (2.0) X Example window has the Java application icon X https://github.com/processing/processing/issues/1800 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/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.form b/experimental/src/processing/mode/experimental/VariableInspector.form deleted file mode 100755 index a5f40f1d3..000000000 --- a/experimental/src/processing/mode/experimental/VariableInspector.form +++ /dev/null @@ -1,53 +0,0 @@ - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    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..28a2e0277 --- /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/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/template.plist b/java/application/template.plist index 36822013a..a9e22b9c5 100755 --- a/java/application/template.plist +++ b/java/application/template.plist @@ -63,8 +63,14 @@ true com.apple.hwaccel true + apple.awt.use-file-dialog-packages - false + true diff --git a/java/examples/Basics/Form/BezierEllipse/BezierEllipse.pde b/java/examples/Basics/Form/BezierEllipse/BezierEllipse.pde deleted file mode 100644 index b079c6f56..000000000 --- a/java/examples/Basics/Form/BezierEllipse/BezierEllipse.pde +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Bezier Ellipse - * By Ira Greenberg - * - * Generates an ellipse using bezier() and - * trig functions. Approximately every 1/2 - * second a new ellipse is plotted using - * random values for control/anchor points. - */ - -// arrays to hold ellipse coordinate data -float[] px, py, cx, cy, cx2, cy2; - -// global variable-points in ellipse -int pts = 4; - -color controlPtCol = #222222; -color anchorPtCol = #BBBBBB; - -void setup(){ - size(640, 360); - setEllipse(pts, 65, 65); - frameRate(1); -} - -void draw(){ - background(145); - drawEllipse(); - setEllipse(int(random(3, 12)), random(-100, 150), random(-100, 150)); -} - -// Draw ellipse with anchor/control points -void drawEllipse(){ - strokeWeight(1.125); - stroke(255); - noFill(); - // Create ellipse - for (int i=0; i0){ - line(px[i], py[i], cx2[i-1], cy2[i-1]); - } - line(px[i], py[i], cx[i], cy[i]); - } - - for ( int i=0; i< pts; i++){ - fill(controlPtCol); - noStroke(); - // Control handles - ellipse(cx[i], cy[i], 4, 4); - ellipse(cx2[i], cy2[i], 4, 4); - - fill(anchorPtCol); - stroke(0); - // Anchor points - rect(px[i], py[i], 5, 5); - } -} - -// Fill arrays with ellipse coordinate data -void setEllipse(int points, float radius, float controlRadius){ - pts = points; - px = new float[points]; - py = new float[points]; - cx = new float[points]; - cy = new float[points]; - cx2 = new float[points]; - cy2 = new float[points]; - float angle = 360.0/points; - float controlAngle1 = angle/3.0; - float controlAngle2 = controlAngle1*2.0; - for ( int i=0; i 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..5f4719dac --- a/java/libraries/dxf/build.xml +++ b/java/libraries/dxf/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/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/BigWindow/BigWindow.pde b/java/libraries/glw/examples/BigWindow/BigWindow.pde new file mode 100644 index 000000000..3149b6996 --- /dev/null +++ b/java/libraries/glw/examples/BigWindow/BigWindow.pde @@ -0,0 +1,13 @@ +import processing.glw.*; + +void setup() { + size(2560, 1440, GLW.P2D); + frameRate(180); +} + +void draw() { + background(255, 0, 0); + + fill(255); + text("FPS: " + frameRate, mouseX, mouseY); +} 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..c45805cb6 --- /dev/null +++ b/java/libraries/glw/src/processing/glw/GLW.java @@ -0,0 +1,6 @@ +package processing.glw; + +public interface GLW { + static final String P2D = "processing.glw.PGraphics2D"; + static final String P3D = "processing.glw.PGraphics3D"; +} 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..b28f93f31 --- /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..d436454c2 --- /dev/null +++ b/java/libraries/glw/src/processing/glw/PGraphicsGLW.java @@ -0,0 +1,36 @@ +/* -*- 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; + +/** + * LWJGL renderer. + * + */ +public class PGraphicsGLW extends PGraphicsOpenGL { + protected PGL createPGL(PGraphicsOpenGL pg) { + return new PNEWT(pg); + } +} 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..61922b288 --- /dev/null +++ b/java/libraries/glw/src/processing/glw/PNEWT.java @@ -0,0 +1,93 @@ +/* -*- 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.GLCapabilities; +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.opengl.PGraphicsOpenGL; +import processing.opengl.PJOGL; + +public class PNEWT extends PJOGL { + + static { + WINDOW_TOOLKIT = NEWT; + EVENTS_TOOLKIT = NEWT; + USE_FBOLAYER_BY_DEFAULT = false; + USE_JOGL_FBOLAYER = false; + } + + + public PNEWT(PGraphicsOpenGL pg) { + super(pg); + } + + + protected void initSurface(int antialias) { + if (profile == null) { + profile = GLProfile.getDefault(); + } else { + window.removeGLEventListener(listener); + pg.parent.remove(canvasNEWT); + } + + // 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); + + if (1 < antialias) { + caps.setSampleBuffers(true); + caps.setNumSamples(antialias); + } else { + caps.setSampleBuffers(false); + } + fboLayerRequested = false; + + window = GLWindow.create(caps); + window.setSize(pg.width, pg.height); + window.setVisible(true); + pg.parent.frame.setVisible(false); + + canvas = canvasNEWT; + canvasAWT = null; + + window.addWindowListener(new WindowAdapter() { + @Override + public void windowDestroyNotify(final WindowEvent e) { + System.exit(0); + } + }); + + registerListeners(); + } + + +} 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 1d41920dd..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); - } - } - - - protected 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); - } - - - protected 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..6409152ae --- /dev/null +++ b/java/libraries/lwjgl/src/processing/lwjgl/PLWJGL.java @@ -0,0 +1,1994 @@ +/* -*- 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(); + } + + + 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 + + public void bindFramebuffer(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); + } +} 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/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..c122c2876 --- a/java/libraries/pdf/build.xml +++ b/java/libraries/pdf/build.xml @@ -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/examples/ManyPages/ManyPages.pde b/java/libraries/pdf/examples/ManyPages/ManyPages.pde index de0e1a83c..776a4f040 100644 --- a/java/libraries/pdf/examples/ManyPages/ManyPages.pde +++ b/java/libraries/pdf/examples/ManyPages/ManyPages.pde @@ -14,7 +14,6 @@ void setup() { size(600, 600); frameRate(4); pdf = (PGraphicsPDF)beginRecord(PDF, "Lines.pdf"); - beginRecord(pdf); } void draw() { diff --git a/java/libraries/pdf/src/processing/pdf/PGraphicsPDF.java b/java/libraries/pdf/src/processing/pdf/PGraphicsPDF.java index 2c6e8ae88..b49895706 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) / (float)imageWidth, + (y2 - y1) / (float)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..b68641ac0 100644 --- a/java/libraries/serial/examples/SerialCallResponse/SerialCallResponse.pde +++ b/java/libraries/serial/examples/SerialCallResponse/SerialCallResponse.pde @@ -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/SerialMultiple/SerialMultiple.pde b/java/libraries/serial/examples/SerialMultiple/SerialMultiple.pde index 4334e1a2b..cd46cbcd8 100644 --- a/java/libraries/serial/examples/SerialMultiple/SerialMultiple.pde +++ b/java/libraries/serial/examples/SerialMultiple/SerialMultiple.pde @@ -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..52463e324 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,443 @@ */ 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); + parent.registerMethod("dispose", this); + parent.registerMethod("pre", 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); - } + // 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; } - this.rate = irate; + // 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; + } - 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; + 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()); + } try { - Enumeration portList = CommPortIdentifier.getPortIdentifiers(); - while (portList.hasMoreElements()) { - CommPortIdentifier portId = - (CommPortIdentifier) portList.nextElement(); + serialEventMethod = parent.getClass().getMethod("serialEvent", new Class[] { this.getClass() }); + } catch (Exception e) { + } - 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"); - } + try { + serialAvailableMethod = parent.getClass().getMethod("serialAvailable", new Class[] { this.getClass() }); + } catch (Exception e) { + } + } + + + public void dispose() { + stop(); + } + + + public void pre() { + if (serialAvailableMethod != null && invokeSerialAvailable) { + invokeSerialAvailable = false; + try { + serialAvailableMethod.invoke(parent, new Object[] { this }); + } catch (Exception e) { + System.err.println("Error, disabling serialAvailable() for "+port.getPortName()); + System.err.println(e.getLocalizedMessage()); + serialAvailableMethod = null; + } + } + } + + + public int available() { + return (inBuffer-readOffset); + } + + + public void buffer(int size) { + bufferUntilSize = size; + } + + + public void bufferUntil(int inByte) { + bufferUntilSize = 0; + bufferUntilByte = (byte)inByte; + } + + + public void clear() { + synchronized (buffer) { + inBuffer = 0; + readOffset = 0; + } + } + + + 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 (inBuffer == readOffset) { + return -1; + } + + synchronized (buffer) { + int ret = buffer[inBuffer-1] & 0xFF; + inBuffer = 0; + readOffset = 0; + return ret; + } + } + + + public char lastChar() { + return (char)last(); + } + + + 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; } } - - } catch (Exception e) { - errorMessage("", e); - //exception = e; - //e.printStackTrace(); - port = null; - input = null; - output = null; - } - - parent.registerMethod("dispose", 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 - } - } - - - /** - * @generate Serial_stop.xml - * @webref serial:serial - * @usage web_application - */ - public void stop() { - dispose(); - } - - - /** - * Used by PApplet to shut things down. - */ - public void dispose() { - try { - if (input != null) { - input.close(); - input = null; + if (found == -1) { + return null; } - } catch (Exception e) { - e.printStackTrace(); + + 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; } - try { - if (output != null) { - output.close(); - output = 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 0; } - } catch (Exception e) { - e.printStackTrace(); - } - try { - if (port != null) { - port.close(); - port = null; + // 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; } - } catch (Exception e) { - e.printStackTrace(); + System.arraycopy(buffer, readOffset, dest, 0, toCopy); + readOffset += toCopy; + if (inBuffer == readOffset) { + inBuffer = 0; + readOffset = 0; + } + return toCopy; + } + } + + + public char readChar() { + return (char) read(); + } + + + public String readString() { + if (inBuffer == readOffset) { + return null; + } + return new String(readBytes()); + } + + + public String readStringUntil(int inByte) { + byte temp[] = readBytesUntil(inByte); + if (temp == null) { + return null; + } else { + return new String(temp); } } - /** - * 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 serialEvent(SerialPortEvent event) { + if (event.getEventType() == SerialPortEvent.RXCHAR) { + int toRead; try { - while (input.available() > 0) { + while (0 < (toRead = port.getInputBufferBytesCount())) { + // this method can be called from the context of another thread synchronized (buffer) { - if (bufferLast == buffer.length) { - byte temp[] = new byte[bufferLast << 1]; - System.arraycopy(buffer, 0, temp, 0, bufferLast); + // 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; } - buffer[bufferLast++] = (byte) input.read(); + // 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 ((bufferUntil && - (buffer[bufferLast-1] == bufferUntilByte)) || - (!bufferUntil && - ((bufferLast - bufferIndex) >= bufferSize))) { + 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, new Object[] { this }); } catch (Exception e) { - String msg = "error, disabling serialEvent() for " + port; - System.err.println(msg); - e.printStackTrace(); + System.err.println("Error, disabling serialEvent() for "+port.getPortName()); + System.err.println(e.getLocalizedMessage()); serialEventMethod = null; } } } + invokeSerialAvailable = true; } } - - } catch (IOException e) { - errorMessage("serialEvent", e); + } catch (SerialPortException e) { + throw new RuntimeException("Error reading from serial port " + e.getPortName() + ": " + e.getExceptionType()); } } } - - /** - * @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); - } - - - /** - * @generate Serial_clear.xml - * @webref serial:serial - * @usage web_application - */ - 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; - } - } - - - /** - * @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 int last() { - if (bufferIndex == bufferLast) return -1; - synchronized (buffer) { - int outgoing = buffer[bufferLast-1]; - bufferIndex = 0; - bufferLast = 0; - return outgoing; - } - } - - - /** - * @generate Serial_readChar.xml - * @webref serial:serial - * @usage web_application - */ - 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; - 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 + + 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 { - 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); + port.setDTR(state); + } catch (SerialPortException e) { + throw new RuntimeException("Error setting the DTR line: " + e.getExceptionType()); } } - /** - * @param bytes[] data to write - */ - public void write(byte bytes[]) { + + public void setRTS(boolean state) { 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(); + port.setRTS(state); + } catch (SerialPortException e) { + throw new RuntimeException("Error setting the RTS line: " + e.getExceptionType()); } } - - /** - * @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(); + + public void stop() { 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); - } - } - - } catch (UnsatisfiedLinkError e) { - //System.err.println("1"); - errorMessage("ports", e); - - } catch (Exception e) { - //System.err.println("2"); - errorMessage("ports", e); + port.closePort(); + } catch (SerialPortException e) { + // ignored } - //System.err.println("move out"); - String outgoing[] = new String[list.size()]; - list.copyInto(outgoing); - return outgoing; + 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()); + } + } - /** - * 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 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/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/src/processing/video/Capture.java b/java/libraries/video/src/processing/video/Capture.java index b7627b267..7cad36876 100644 --- a/java/libraries/video/src/processing/video/Capture.java +++ b/java/libraries/video/src/processing/video/Capture.java @@ -110,6 +110,8 @@ public class Capture extends PImage implements PConstants { protected Object bufferSink; protected Method sinkCopyMethod; protected Method sinkSetMethod; + protected Method sinkDisposeMethod; + protected Method sinkGetMethod; protected String copyMask; protected Buffer natBuffer = null; protected BufferDataAppSink natSink = null; @@ -268,6 +270,10 @@ public class Capture extends PImage implements PConstants { pipeline.dispose(); pipeline = null; + + parent.g.removeCache(this); + parent.unregisterMethod("dispose", this); + parent.unregisterMethod("post", this); } } @@ -417,7 +423,29 @@ public class Capture extends PImage implements PConstants { newFrame = true; } - + + public synchronized void loadPixels() { + super.loadPixels(); + 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 + // pixel array, which we can just get from natBuffer. + IntBuffer buf = natBuffer.getByteBuffer().asIntBuffer(); + buf.rewind(); + buf.get(pixels); + Video.convertToARGB(pixels, width, height); + } else if (sinkGetMethod != null) { + try { + sinkGetMethod.invoke(bufferSink, new Object[] { pixels }); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + //////////////////////////////////////////////////////////// // List methods. @@ -800,6 +828,7 @@ public class Capture extends PImage implements PConstants { // register methods parent.registerMethod("dispose", this); + parent.registerMethod("post", this); setEventHandlerObject(parent); @@ -976,6 +1005,12 @@ public class Capture extends PImage implements PConstants { available = true; 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(); + } natBuffer = buffer; // Creates a movieEvent. @@ -1129,6 +1164,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", + new Class[] { }); + } catch (Exception e) { + throw new RuntimeException("Capture: provided sink object doesn't have " + + "a disposeSourceBuffer method."); + } + + try { + 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."); + } } @@ -1139,4 +1190,15 @@ 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 { + sinkDisposeMethod.invoke(bufferSink, new Object[] {}); + } catch (Exception e) { + 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 7e55b10a3..fcb0056e6 100644 --- a/java/libraries/video/src/processing/video/Movie.java +++ b/java/libraries/video/src/processing/video/Movie.java @@ -82,6 +82,8 @@ public class Movie extends PImage implements PConstants { protected Object bufferSink; protected Method sinkCopyMethod; protected Method sinkSetMethod; + protected Method sinkDisposeMethod; + protected Method sinkGetMethod; protected String copyMask; protected Buffer natBuffer = null; protected BufferDataAppSink natSink = null; @@ -133,6 +135,10 @@ public class Movie extends PImage implements PConstants { playbin.dispose(); playbin = null; + + parent.g.removeCache(this); + parent.unregisterMethod("dispose", this); + parent.unregisterMethod("post", this); } } @@ -542,6 +548,28 @@ public class Movie extends PImage implements PConstants { } + public synchronized void loadPixels() { + super.loadPixels(); + 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 + // pixel array, which we can just get from natBuffer. + IntBuffer buf = natBuffer.getByteBuffer().asIntBuffer(); + buf.rewind(); + buf.get(pixels); + Video.convertToARGB(pixels, width, height); + } else if (sinkGetMethod != null) { + try { + sinkGetMethod.invoke(bufferSink, new Object[] { pixels }); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + //////////////////////////////////////////////////////////// // Initialization methods. @@ -609,11 +637,11 @@ public class Movie extends PImage implements PConstants { // we've got a valid movie! let's rock. try { - // PApplet.println("we've got a valid movie! let's rock."); this.filename = filename; // for error messages // register methods parent.registerMethod("dispose", this); + parent.registerMethod("post", this); setEventHandlerObject(parent); @@ -742,6 +770,12 @@ public class Movie extends PImage implements PConstants { available = true; 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(); + } natBuffer = buffer; if (playing) { @@ -894,6 +928,22 @@ public class Movie extends PImage implements PConstants { throw new RuntimeException("Movie: provided sink object doesn't have a " + "setBufferSource method."); } + + try { + sinkDisposeMethod = bufferSink.getClass().getMethod("disposeSourceBuffer", + new Class[] { }); + } catch (Exception e) { + throw new RuntimeException("Movie: provided sink object doesn't have " + + "a disposeSourceBuffer method."); + } + + try { + sinkGetMethod = bufferSink.getClass().getMethod("getBufferPixels", + new Class[] { int[].class }); + } catch (Exception e) { + throw new RuntimeException("Movie: provided sink object doesn't have " + + "a getBufferPixels method."); + } } @@ -902,6 +952,17 @@ public class Movie extends PImage implements PConstants { copyMask = "red_mask=(int)0xFF000000, green_mask=(int)0xFF0000, blue_mask=(int)0xFF00"; } else { copyMask = "red_mask=(int)0xFF, green_mask=(int)0xFF00, blue_mask=(int)0xFF0000"; + } + } + + + public synchronized void post() { + if (useBufferSink && sinkDisposeMethod != null) { + try { + sinkDisposeMethod.invoke(bufferSink, new Object[] {}); + } catch (Exception e) { + e.printStackTrace(); + } } } } diff --git a/java/libraries/video/src/processing/video/Video.java b/java/libraries/video/src/processing/video/Video.java index 3b3263426..1eaa0e090 100644 --- a/java/libraries/video/src/processing/video/Video.java +++ b/java/libraries/video/src/processing/video/Video.java @@ -26,7 +26,9 @@ package processing.video; import org.gstreamer.*; import processing.core.PApplet; import processing.core.PConstants; + import java.io.File; +import java.nio.ByteOrder; import java.util.List; /** @@ -192,5 +194,36 @@ public class Video implements PConstants { static protected long secToNanoLong(float sec) { Float f = new Float(sec * 1E9); return f.longValue(); + } + + + /** + * Reorders an OpenGL pixel array (RGBA) into ARGB. The array must be + * of size width * height. + * @param pixels int[] + */ + static protected void convertToARGB(int[] pixels, int width, int height) { + int t = 0; + int p = 0; + if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) { + // RGBA to ARGB conversion: shifting RGB 8 bits to the right, + // and placing A 24 bits to the left. + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int pixel = pixels[p++]; + pixels[t++] = (pixel >>> 8) | ((pixel << 24) & 0xFF000000); + } + } + } else { + // We have to convert ABGR into ARGB, so R and B must be swapped, + // A and G just brought back in. + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int pixel = pixels[p++]; + pixels[t++] = ((pixel & 0xFF) << 16) | ((pixel & 0xFF0000) >> 16) | + (pixel & 0xFF00FF00); + } + } + } } } diff --git a/todo.txt b/todo.txt index 761c5b1d5..a24414ca3 100644 --- a/todo.txt +++ b/todo.txt @@ -1,55 +1,68 @@ -0219 pde (2.0.1) +0224 pde +X readStringUntil() missing from new serial library +X https://github.com/processing/processing/issues/2174 + +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 high +_ adding characters doesn't happen on position of cursor +_ https://github.com/processing/processing/issues/2180 +_ Chinese text is overlapped in Processing 2.1 editor +_ https://github.com/processing/processing/issues/2173 + + +medium +_ 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? +_ 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 -_ UnsatisfiedLinkError causes huge message... -_ error report cleanups haven't been fixed yet -_ reported by Dan -_ proxy server requirement causes problems -_ contrib manager, update checks are broken -_ https://github.com/processing/processing/issues/1476 -_ send info on 'check for updates' so we know about libs/modes/etc? -_ how to disclose to users? -_ only send for items that are part of the public list -_ otherwise we're sending private libraries/installs -_ although this won't pick up old libraries not on the new system -_ 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? +_ 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 +_ change Windows export to use launch4j instead of the launcher.cpp file +_ actually call ant from inside p5? +_ emacs style errors in commander aren't quite right +_ https://github.com/processing/processing/issues/2158 + +post 2.1 cleaning +_ remove video for macosx32 from the repo permanently +_ 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) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . -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 - -_ common error messages -_ with a proper list, we can add links when throwing an error in the PDE _ 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 @@ -66,8 +79,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, .. ? @@ -111,6 +122,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 @@ -214,15 +228,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 @@ -262,9 +278,12 @@ _ 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 +_ https://github.com/processing/processing/issues/1936 _ copy running code from /tmp/buildXXxxx on crash of p5 _ should probably make a way to save/recover code _ make the buildXxxx folders relate to time/date? @@ -303,8 +322,6 @@ _ 'recent' menu doesn't respect examples folder of other p5 versions _ could write that into the file, that it's an example _ or write the path as shown in the PDE to the file as simpler _ 'recent' menu paths can get enormous -_ temporary files (for sketches and logs) are not deleted -_ http://code.google.com/p/processing/issues/detail?id=562 _ don't let people into the /tmp folder for sketches o don't use tmp folder for sketches? _ restrict more things like "show sketch folder" @@ -316,8 +333,6 @@ _ remove sketch.properties when moving back to the default? _ or can we not do this, because it's used to set the 'next' mode _ add means to import .zip files from file/url into sketchbook, library, etc. _ super easy given current code implementation, might help usability -_ / breaks syntax highlighting -_ https://github.com/processing/processing/issues/1681 _ 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 @@ -325,9 +340,6 @@ _ 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" -_ undo seems to not be going to the right location (now with example) -_ https://github.com/processing/processing/issues/707 -X http://code.google.com/p/processing/issues/detail?id=668 _ improve update check message "a new release (1.0.1) is available" _ be more descriptive, use a second line in latest.txt _ maybe just include the full text of the update message there? @@ -350,8 +362,10 @@ _ add auto-save to the editor _ http://code.google.com/p/processing/issues/detail?id=92 _ add mnemonics for menus (alt-f to open 'file') _ http://code.google.com/p/processing/issues/detail?id=12 +_ https://github.com/processing/processing/issues/51 _ option to just print all code in project _ http://code.google.com/p/processing/issues/detail?id=11 +_ https://github.com/processing/processing/issues/50 _ or option to export all the code as colored html? _ dim edit menus as appropriate during selection/no selection/etc _ http://code.google.com/p/processing/issues/detail?id=14 @@ -429,6 +443,8 @@ _ 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 @@ -437,8 +453,6 @@ _ mark example files as untitled _ though will that require the sketch to be saved before export? _ 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 @@ -447,6 +461,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.. @@ -491,7 +507,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 @@ -510,10 +527,30 @@ _ https://github.com/processing/processing/issues/943 PDE / Manager -_ Contributed modes should show up in mode menu after installation -_ waiting for fixed CoffeeScript mode to test this one -_ https://github.com/processing/processing/issues/1504 -X http://code.google.com/p/processing/issues/detail?id=1466 +_ 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 +_ if not it'll make things gross (andre sier flob library) +_ why wasn't Library moved to LibraryContribution? +_ or that LibraryContribution needs to be a wrapper around it? +_ send info on 'check for updates' so we know about libs/modes/etc? +_ how to disclose to users? +_ only send for items that are part of the public list +_ otherwise we're sending private libraries/installs +_ although this won't pick up old libraries not on the new system +_ 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 +_ contrib library examples are not read-only +_ another point for doing .zip files to prevent overwriting +_ add BookContribution? +_ will offer to open the contrib manager even though it's already open +_ though this was looking at libraries, and there may have been modes +_ is there an "all" view for the mgr that shows libs, modes, etc? +_ update button only showing up when item is selected feels awkward +_ "update all" would be useful +_ "Update 4 items" as a button name _ new libraries not picked up when changing sketchbook location _ make sure contrib manager can run w/o a network connection _ or if a bad document comes through, it can recover @@ -523,11 +560,11 @@ _ alternating blue/white backgrounds aren't updated after changing filter _ just need to call a repaint() after a filter change? _ check with Casey about coloring for error messages _ test on Windows and Linux -_ scrolls to bottom of window after updating the list _ font size for "Downloading" on progress bar is too large _ but changing the size breaks the vertical centering _ highlight color seems to be incorrect? _ after installing, the item in the manager list doesn't change color +_ scrolls to bottom of window after updating the list _ wheel mouse is super jumpy _ something about unit increment in ContributionListPanel _ arrow keys up/down move scroll bar, not selection @@ -551,12 +588,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 @@ -571,6 +602,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 +_ https://github.com/processing/processing/issues/67 _ 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 @@ -626,20 +658,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 @@ -659,10 +683,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 @@ -681,18 +707,6 @@ _ need to unpack InvocationTargetException in xxxxxxEvent calls _ http://processing.org/discourse/yabb_beta/YaBB.cgi?board=VideoCamera;action=display;num=1116850328#3 -LIBRARIES / Serial - -_ 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 - - //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// @@ -711,8 +725,7 @@ _ https://github.com/processing/processing/issues/1840 _ need to have ecj.jar accessible to ant, then modify build.xml to use this: _ -_ Update Java version in the download to be the latest Java 6 -_ https://github.com/processing/processing/issues/1841 +_ 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) @@ -753,33 +766,28 @@ _ 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 _ Update Windows icons for multiple sizes, implement them in the PDE _ http://code.google.com/p/processing/issues/detail?id=632 -_ problem with associating .pde files -_ http://code.google.com/p/processing/issues/detail?id=247 -_ http://support.microsoft.com/kb/184082 -_ http://msdn.microsoft.com/en-us/library/cc144175%28v=VS.85%29.aspx -_ http://msdn.microsoft.com/en-us/library/cc144104%28v=VS.85%29.aspx -_ http://msdn.microsoft.com/en-us/library/cc144067%28v=VS.85%29.aspx -_ In regedit: Navigate to Computer\HKEY_CLASSES_ROOT\Applications and find your .exe name. Navigate under its name to shell>open>command. In the Default change its location to the actual location of the executable, hit okay and then try and reassociate the file type as you normally would. DIST / Mac OS X + +_ 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 _ 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 _ OS X not opening a sketch at all on pde double-click? (though opening the app) -_ waiting on retina support for JDK 7 -_ http://bugs.sun.com/view_bug.do?bug_id=8009754 -_ b86 supposed to have some support (not available yet) -_ http://jdk8.java.net/download.html -_ "Are you sure you want to quit?" when switching modes on Oracle JVM -_ because it's OS X, but menubar isn't available _ LWJGL forum discussion _ http://lwjgl.org/forum/index.php/topic,4711.225.html _ change cmd line for OS X to use symlink? @@ -805,21 +813,6 @@ _ also applies to horizontal scrolling on the mouse _ http://code.google.com/p/processing/issues/detail?id=141 _ Exiting a sketch with Command-Q or File > Quit doesn't call stop() on OS X _ http://code.google.com/p/processing/issues/detail?id=147 -_ add a check to make sure that people aren't running from the dmg -_ doesn't actually cause any problems, so don't bother? -_ code to hide menubar.. just include JNA and call from there? -NSMenu.setMenubarVisible(false); -Then we used Cocoa via JNI: -if([NSMenu menuBarVisible]){ - [NSMenu setMenuBarVisible:NO]; -} -You can't do that from the AWT event thread. You need to do a -performSelectorOnMainThread to do that on the AppKit event thread. -Please see for more information, particularly the section about "Calling AppKit from AWT/Swing". - -_ try to use appbundler to create a version that includes a JRE (JDK) -_ http://java.net/downloads/appbundler/appbundler.html -_ http://docs.oracle.com/javase/7/docs/technotes/guides/jweb/packagingAppsForMac.html -_ http://www.intransitione.com/blog/take-java-to-app-store/ _ investigate the sandboxing situation on OS X _ http://developer.apple.com/library/mac/#documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html#//apple_ref/doc/uid/TP40011195-CH4-SW1 most sandbox violations are triggered by attempts to read/write to the @@ -833,7 +826,6 @@ the Apple site(*) you can think whether your application is performing an operation that would require enabling a specific entitlement, like connecting to a network, printing, interacting with a usb or bluetooth device, etc.. - I encountered this problem too, I forgot to add it to my guide.... If you sign all the files in the bundle it won't work as codesign doesn't follow the symlinks. @@ -903,12 +895,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 -_ build is currently broken for fresh checkout due to changes to file layout -_ something that gets fixed by 'make clean' -_ also test on windows and linux _ p5 assets need to be licensed differently from the source