diff --git a/.gitignore b/.gitignore index 3ea645b5..64320d12 100644 --- a/.gitignore +++ b/.gitignore @@ -1,154 +1,160 @@ -/nbproject/private/ -/lib.netutil/build/ -/lib.tinkerforge/build/ -/build/ -/lib.miglayout/build/ -/praxis.core/build/ -/praxis.audio.loader/build/ -/praxis.laf/build/ -/praxis.nb.lookup/build/ -/praxis.meta/build/ -/praxis.settings/build/ -/praxis.swing/build/ -/praxis.util/build/ -/praxis.impl/build/ -/praxis.tinkerforge/build/ -/praxis.hub/build/ -/praxis.osc/build/ -/praxis.script/build/ -/praxis.gui/build/ -/praxis.components/build/ -/praxis.midi/build/ -/praxis.video/build/ -/praxis.video.render/build/ -/praxis.video.pipes/build/ -/praxis.video.software/build/ -/lib.janino/build/ -/praxis.java/build/ -/praxis.video.java/build/ -/lib.rsyntaxtextarea/build/ -/praxis.texteditor/build/ -/audio.ops/build/ -/praxis.terminal/build/ -/praxis.player/build/ -/audio.ops.impl/build/ -/lib.lwjgl/build/ -/audio.ops.gpl/build/ -/audio.pipes/build/ -/audio.servers/build/ -/audio.servers.javasound/build/ -/praxis.video.opengl/build/ -/lib.jna/build/ -/audio.servers.jack/build/ -/praxis.audio/build/ -/audio.jnajack/build/ -/lib.gstreamer-java/build/ -/praxis.video.gstreamer/build/ -/praxis.video.gstreamer-osx/build/ -/praxis.video.gstreamer-win/build/ -/praxis.laf/nbproject/private/ -/praxis.hub/nbproject/private/ -/lib.tinkerforge/nbproject/private/ -/praxis.video/nbproject/private/ -/praxis.midi/nbproject/private/ -/audio.servers.javasound/nbproject/private/ -/lib.rsyntaxtextarea/nbproject/private/ -/praxis.gui/nbproject/private/ -/praxis.audio/nbproject/private/ -/praxis.video.pipes/nbproject/private/ -/praxis.video.render/nbproject/private/ -/praxis.java/nbproject/private/ -/lib.janino/nbproject/private/ -/praxis.util/nbproject/private/ -/praxis.video.opengl/nbproject/private/ -/praxis.impl/nbproject/private/ -/audio.pipes/nbproject/private/ -/lib.lwjgl/nbproject/private/ -/audio.servers.jack/nbproject/private/ -/praxis.audio.loader/nbproject/private/ -/praxis.osc/nbproject/private/ -/audio.ops.gpl/nbproject/private/ -/lib.gstreamer-java/nbproject/private/ -/audio.jnajack/nbproject/private/ -/praxis.player/nbproject/private/ -/praxis.video.gstreamer/nbproject/private/ -/praxis.video.java/nbproject/private/ -/audio.ops/nbproject/private/ -/praxis.script/nbproject/private/ -/praxis.video.gstreamer-win/nbproject/private/ -/praxis.video.software/nbproject/private/ -/lib.miglayout/nbproject/private/ -/praxis.texteditor/nbproject/private/ -/lib.jna/nbproject/private/ -/audio.ops.impl/nbproject/private/ -/praxis.tinkerforge/nbproject/private/ -/lib.netutil/nbproject/private/ -/praxis.settings/nbproject/private/ -/praxis.components/nbproject/private/ -/praxis.nb.lookup/nbproject/private/ -/praxis.terminal/nbproject/private/ -/audio.servers/nbproject/private/ -/praxis.core/nbproject/private/ -/praxis.swing/nbproject/private/ -/praxis.meta/nbproject/private/ -/praxis.video.gstreamer-osx/nbproject/private/ -/lib.commons-compiler/build/ -/lib.compiler.pjc/build/ -/lib.compiler/build/ -/lib.commons-compiler/nbproject/private/ -/lib.compiler/nbproject/private/ -/lib.compiler.pjc/nbproject/private/ -/praxis.nb.classpath/build/ -/praxis.nb.classpath/nbproject/private/ -/praxis.compiler/nbproject/private/ -/lib.janino.commons/build/ -/praxis.compiler.javac/build/ -/praxis.compiler/build/ -/praxis.compiler.javac/nbproject/private/ -/lib.janino.commons/nbproject/private/ -/praxis.logging/nbproject/private/ -/praxis.logging/build/ -/lib.jogl/build/ -/lib.processing/build/ -/lib.jogl/nbproject/private/ -/lib.processing/nbproject/private/ -/praxis.video.pgl/nbproject/private/ -/praxis.video.pgl/build/ -/dist/ -/praxis.code/build/ -/praxis.code/nbproject/private/ -/praxis.video.code/build/ -/praxis.video.code/nbproject/private/ -/praxis.video.pgl.custom/build/ -/praxis.video.pgl.code/build/ -/praxis.video.pgl.code/nbproject/private/ -/praxis.hub.net/build/ -/praxis.hub.net/nbproject/private/ -/praxis.core.components/build/ -/praxis.core.components/nbproject/private/ -/praxis.core.factory/build/ -/praxis.video.factory/build/ -/praxis.core.factory/nbproject/private/ -/praxis.video.factory/nbproject/private/ -/praxis.video.gstreamer-osx/release/modules/lib/macosx64/ -/praxis.video.gstreamer-win/release/modules/lib/windows32/ -/praxis.video.gstreamer-win/release/modules/lib/windows64/ -/praxis.core.code/build/ -/praxis.core.code/nbproject/private/ -/praxis.audio.code/build/ -/praxis.audio.factory/build/ -/praxis.audio.factory/nbproject/private/ -/praxis.tracker/build/ -/praxis.audio.code/nbproject/private/ -/praxis.tracker/nbproject/private/ -/praxis.video.gst1/build/ -/lib.gst1-java-core/build/ -/lib.gst1-java-core/nbproject/private/ -/praxis.video.gst1/nbproject/private/ -/praxis.code.services/build/ -/praxis.code.services/nbproject/private/ -/lib.darcula/build/ -/lib.nanohttpd/build/ -/praxis.data/build/ -/praxis.data/nbproject/private/ -/lib.jna.platform/build/ \ No newline at end of file +/nbproject/private/ +/lib.netutil/build/ +/lib.tinkerforge/build/ +/build/ +/lib.miglayout/build/ +/praxis.core/build/ +/praxis.audio.loader/build/ +/praxis.laf/build/ +/praxis.nb.lookup/build/ +/praxis.meta/build/ +/praxis.settings/build/ +/praxis.swing/build/ +/praxis.util/build/ +/praxis.impl/build/ +/praxis.tinkerforge/build/ +/praxis.hub/build/ +/praxis.osc/build/ +/praxis.script/build/ +/praxis.gui/build/ +/praxis.components/build/ +/praxis.midi/build/ +/praxis.video/build/ +/praxis.video.render/build/ +/praxis.video.pipes/build/ +/praxis.video.software/build/ +/lib.janino/build/ +/praxis.java/build/ +/praxis.video.java/build/ +/lib.rsyntaxtextarea/build/ +/praxis.texteditor/build/ +/audio.ops/build/ +/praxis.terminal/build/ +/praxis.player/build/ +/audio.ops.impl/build/ +/lib.lwjgl/build/ +/audio.ops.gpl/build/ +/audio.pipes/build/ +/audio.servers/build/ +/audio.servers.javasound/build/ +/praxis.video.opengl/build/ +/lib.jna/build/ +/audio.servers.jack/build/ +/praxis.audio/build/ +/audio.jnajack/build/ +/lib.gstreamer-java/build/ +/praxis.video.gstreamer/build/ +/praxis.video.gstreamer-osx/build/ +/praxis.video.gstreamer-win/build/ +/praxis.laf/nbproject/private/ +/praxis.hub/nbproject/private/ +/lib.tinkerforge/nbproject/private/ +/praxis.video/nbproject/private/ +/praxis.midi/nbproject/private/ +/audio.servers.javasound/nbproject/private/ +/lib.rsyntaxtextarea/nbproject/private/ +/praxis.gui/nbproject/private/ +/praxis.audio/nbproject/private/ +/praxis.video.pipes/nbproject/private/ +/praxis.video.render/nbproject/private/ +/praxis.java/nbproject/private/ +/lib.janino/nbproject/private/ +/praxis.util/nbproject/private/ +/praxis.video.opengl/nbproject/private/ +/praxis.impl/nbproject/private/ +/audio.pipes/nbproject/private/ +/lib.lwjgl/nbproject/private/ +/audio.servers.jack/nbproject/private/ +/praxis.audio.loader/nbproject/private/ +/praxis.osc/nbproject/private/ +/audio.ops.gpl/nbproject/private/ +/lib.gstreamer-java/nbproject/private/ +/audio.jnajack/nbproject/private/ +/praxis.player/nbproject/private/ +/praxis.video.gstreamer/nbproject/private/ +/praxis.video.java/nbproject/private/ +/audio.ops/nbproject/private/ +/praxis.script/nbproject/private/ +/praxis.video.gstreamer-win/nbproject/private/ +/praxis.video.software/nbproject/private/ +/lib.miglayout/nbproject/private/ +/praxis.texteditor/nbproject/private/ +/lib.jna/nbproject/private/ +/audio.ops.impl/nbproject/private/ +/praxis.tinkerforge/nbproject/private/ +/lib.netutil/nbproject/private/ +/praxis.settings/nbproject/private/ +/praxis.components/nbproject/private/ +/praxis.nb.lookup/nbproject/private/ +/praxis.terminal/nbproject/private/ +/audio.servers/nbproject/private/ +/praxis.core/nbproject/private/ +/praxis.swing/nbproject/private/ +/praxis.meta/nbproject/private/ +/praxis.video.gstreamer-osx/nbproject/private/ +/lib.commons-compiler/build/ +/lib.compiler.pjc/build/ +/lib.compiler/build/ +/lib.commons-compiler/nbproject/private/ +/lib.compiler/nbproject/private/ +/lib.compiler.pjc/nbproject/private/ +/praxis.nb.classpath/build/ +/praxis.nb.classpath/nbproject/private/ +/praxis.compiler/nbproject/private/ +/lib.janino.commons/build/ +/praxis.compiler.javac/build/ +/praxis.compiler/build/ +/praxis.compiler.javac/nbproject/private/ +/lib.janino.commons/nbproject/private/ +/praxis.logging/nbproject/private/ +/praxis.logging/build/ +/lib.jogl/build/ +/lib.processing/build/ +/lib.jogl/nbproject/private/ +/lib.processing/nbproject/private/ +/praxis.video.pgl/nbproject/private/ +/praxis.video.pgl/build/ +/dist/ +/praxis.code/build/ +/praxis.code/nbproject/private/ +/praxis.video.code/build/ +/praxis.video.code/nbproject/private/ +/praxis.video.pgl.custom/build/ +/praxis.video.pgl.code/build/ +/praxis.video.pgl.code/nbproject/private/ +/praxis.hub.net/build/ +/praxis.hub.net/nbproject/private/ +/praxis.core.components/build/ +/praxis.core.components/nbproject/private/ +/praxis.core.factory/build/ +/praxis.video.factory/build/ +/praxis.core.factory/nbproject/private/ +/praxis.video.factory/nbproject/private/ +/praxis.video.gstreamer-osx/release/modules/lib/macosx64/ +/praxis.video.gstreamer-win/release/modules/lib/windows32/ +/praxis.video.gstreamer-win/release/modules/lib/windows64/ +/praxis.core.code/build/ +/praxis.core.code/nbproject/private/ +/praxis.audio.code/build/ +/praxis.audio.factory/build/ +/praxis.audio.factory/nbproject/private/ +/praxis.tracker/build/ +/praxis.audio.code/nbproject/private/ +/praxis.tracker/nbproject/private/ +/praxis.video.gst1/build/ +/lib.gst1-java-core/build/ +/lib.gst1-java-core/nbproject/private/ +/praxis.video.gst1/nbproject/private/ +/praxis.code.services/build/ +/praxis.code.services/nbproject/private/ +/lib.darcula/build/ +/lib.nanohttpd/build/ +/praxis.data/build/ +/praxis.data/nbproject/private/ +/lib.jna.platform/build/ +/lib.syphon/build/ +/praxis.video.pgl.syphon/build/ +/praxis.video.pgl.syphon/nbproject/private/ +/praxis.video.pgl.spout/build/ +/lib.spout/build/ +/praxis.video.pgl.spout/nbproject/private/ \ No newline at end of file diff --git a/lib.gst1-java-core/manifest.mf b/lib.gst1-java-core/manifest.mf index 88056424..3dce1fc9 100644 --- a/lib.gst1-java-core/manifest.mf +++ b/lib.gst1-java-core/manifest.mf @@ -2,5 +2,5 @@ Manifest-Version: 1.0 AutoUpdate-Show-In-Client: true OpenIDE-Module: org.freedesktop.gstreamer OpenIDE-Module-Localizing-Bundle: org/freedesktop/gstreamer/Bundle.properties -OpenIDE-Module-Specification-Version: 0.9.160303 +OpenIDE-Module-Specification-Version: 0.9.161201 diff --git a/lib.gst1-java-core/nbproject/genfiles.properties b/lib.gst1-java-core/nbproject/genfiles.properties index e6120fbf..896f7feb 100644 --- a/lib.gst1-java-core/nbproject/genfiles.properties +++ b/lib.gst1-java-core/nbproject/genfiles.properties @@ -1,8 +1,8 @@ -build.xml.data.CRC32=47d0585a +build.xml.data.CRC32=901b2e09 build.xml.script.CRC32=71534ec1 -build.xml.stylesheet.CRC32=a56c6a5b@2.67.1 +build.xml.stylesheet.CRC32=a56c6a5b@2.71.1 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. -nbproject/build-impl.xml.data.CRC32=47d0585a +nbproject/build-impl.xml.data.CRC32=901b2e09 nbproject/build-impl.xml.script.CRC32=0556a71e -nbproject/build-impl.xml.stylesheet.CRC32=238281d1@2.67.1 +nbproject/build-impl.xml.stylesheet.CRC32=238281d1@2.71.1 diff --git a/lib.gst1-java-core/nbproject/project.properties b/lib.gst1-java-core/nbproject/project.properties index 47c097ba..42826920 100644 --- a/lib.gst1-java-core/nbproject/project.properties +++ b/lib.gst1-java-core/nbproject/project.properties @@ -1,4 +1,4 @@ -file.reference.gst1-java-core-0.9-160303.jar=release/modules/ext/gst1-java-core-0.9-160303.jar +file.reference.gst1-java-core-0.9-161201.jar=release/modules/ext/gst1-java-core-0.9-161201.jar is.autoload=true javac.compilerargs=-Xlint -Xlint:-serial javac.source=1.8 diff --git a/lib.gst1-java-core/nbproject/project.xml b/lib.gst1-java-core/nbproject/project.xml index 4f828767..9f7b1ad0 100644 --- a/lib.gst1-java-core/nbproject/project.xml +++ b/lib.gst1-java-core/nbproject/project.xml @@ -29,8 +29,8 @@ org.freedesktop.gstreamer.query - ext/gst1-java-core-0.9-160303.jar - release/modules/ext/gst1-java-core-0.9-160303.jar + ext/gst1-java-core-0.9-161201.jar + release/modules/ext/gst1-java-core-0.9-161201.jar diff --git a/lib.gst1-java-core/release/modules/ext/gst1-java-core-0.9-160303.jar b/lib.gst1-java-core/release/modules/ext/gst1-java-core-0.9-160303.jar deleted file mode 100644 index d0e6dc5e..00000000 Binary files a/lib.gst1-java-core/release/modules/ext/gst1-java-core-0.9-160303.jar and /dev/null differ diff --git a/lib.gst1-java-core/release/modules/ext/gst1-java-core-0.9-161201.jar b/lib.gst1-java-core/release/modules/ext/gst1-java-core-0.9-161201.jar new file mode 100644 index 00000000..7c1d772a Binary files /dev/null and b/lib.gst1-java-core/release/modules/ext/gst1-java-core-0.9-161201.jar differ diff --git a/lib.jogl/release/modules/ext/jogl-all.jar b/lib.jogl/release/modules/ext/jogl-all.jar index f73174f9..e2c7b44b 100644 Binary files a/lib.jogl/release/modules/ext/jogl-all.jar and b/lib.jogl/release/modules/ext/jogl-all.jar differ diff --git a/lib.processing/manifest.mf b/lib.processing/manifest.mf index 06671022..bc720724 100644 --- a/lib.processing/manifest.mf +++ b/lib.processing/manifest.mf @@ -1,5 +1,5 @@ Manifest-Version: 1.0 OpenIDE-Module: processing.core OpenIDE-Module-Localizing-Bundle: processing/core/Bundle.properties -OpenIDE-Module-Specification-Version: 3.0.1 +OpenIDE-Module-Specification-Version: 3.2.3 diff --git a/lib.processing/nbproject/genfiles.properties b/lib.processing/nbproject/genfiles.properties index 30b28586..cc66fe63 100644 --- a/lib.processing/nbproject/genfiles.properties +++ b/lib.processing/nbproject/genfiles.properties @@ -1,8 +1,8 @@ -build.xml.data.CRC32=0a597dde +build.xml.data.CRC32=455094f1 build.xml.script.CRC32=5af10cd5 -build.xml.stylesheet.CRC32=a56c6a5b@2.67.1 +build.xml.stylesheet.CRC32=a56c6a5b@2.71.1 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. -nbproject/build-impl.xml.data.CRC32=0a597dde +nbproject/build-impl.xml.data.CRC32=455094f1 nbproject/build-impl.xml.script.CRC32=c2c5dd3f -nbproject/build-impl.xml.stylesheet.CRC32=238281d1@2.67.1 +nbproject/build-impl.xml.stylesheet.CRC32=238281d1@2.71.1 diff --git a/lib.processing/nbproject/project.xml b/lib.processing/nbproject/project.xml index dc053e1f..0304f330 100644 --- a/lib.processing/nbproject/project.xml +++ b/lib.processing/nbproject/project.xml @@ -15,15 +15,12 @@ - - net.neilcsmith.praxis.code - net.neilcsmith.praxis.video.pgl - net.neilcsmith.praxis.video.pgl.code + processing.core processing.data processing.event processing.opengl - + ext/core.jar release/modules/ext/core.jar diff --git a/lib.processing/release/modules/ext/core.jar b/lib.processing/release/modules/ext/core.jar index fb39cd10..e9ebb5b3 100644 Binary files a/lib.processing/release/modules/ext/core.jar and b/lib.processing/release/modules/ext/core.jar differ diff --git a/lib.spout/build.xml b/lib.spout/build.xml new file mode 100644 index 00000000..6465d4eb --- /dev/null +++ b/lib.spout/build.xml @@ -0,0 +1,8 @@ + + + + + + Builds, tests, and runs the project spout. + + diff --git a/lib.spout/manifest.mf b/lib.spout/manifest.mf new file mode 100644 index 00000000..4be8d4a9 --- /dev/null +++ b/lib.spout/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +AutoUpdate-Show-In-Client: true +OpenIDE-Module: spout +OpenIDE-Module-Localizing-Bundle: spout/Bundle.properties +OpenIDE-Module-Specification-Version: 2.0.6.0 + diff --git a/lib.spout/nbproject/build-impl.xml b/lib.spout/nbproject/build-impl.xml new file mode 100644 index 00000000..61a0ce56 --- /dev/null +++ b/lib.spout/nbproject/build-impl.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + You must set 'suite.dir' to point to your containing module suite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib.spout/nbproject/genfiles.properties b/lib.spout/nbproject/genfiles.properties new file mode 100644 index 00000000..b1d21762 --- /dev/null +++ b/lib.spout/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=c470240c +build.xml.script.CRC32=8feaeda8 +build.xml.stylesheet.CRC32=a56c6a5b@2.71.1 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=c470240c +nbproject/build-impl.xml.script.CRC32=ecd8f66a +nbproject/build-impl.xml.stylesheet.CRC32=238281d1@2.71.1 diff --git a/lib.spout/nbproject/project.properties b/lib.spout/nbproject/project.properties new file mode 100644 index 00000000..357190e4 --- /dev/null +++ b/lib.spout/nbproject/project.properties @@ -0,0 +1 @@ +is.autoload=true diff --git a/lib.spout/nbproject/project.xml b/lib.spout/nbproject/project.xml new file mode 100644 index 00000000..09412f16 --- /dev/null +++ b/lib.spout/nbproject/project.xml @@ -0,0 +1,27 @@ + + + org.netbeans.modules.apisupport.project + + + spout + + + + processing.core + + + + 3.2.3 + + + + + spout + + + ext/spout.jar + release/modules/ext/spout.jar + + + + diff --git a/lib.spout/nbproject/suite.properties b/lib.spout/nbproject/suite.properties new file mode 100644 index 00000000..364e160e --- /dev/null +++ b/lib.spout/nbproject/suite.properties @@ -0,0 +1 @@ +suite.dir=${basedir}/.. diff --git a/lib.spout/release/modules/ext/spout.jar b/lib.spout/release/modules/ext/spout.jar new file mode 100644 index 00000000..408a1f09 Binary files /dev/null and b/lib.spout/release/modules/ext/spout.jar differ diff --git a/lib.spout/release/modules/lib/JNISpout_32.dll b/lib.spout/release/modules/lib/JNISpout_32.dll new file mode 100644 index 00000000..099b97eb Binary files /dev/null and b/lib.spout/release/modules/lib/JNISpout_32.dll differ diff --git a/lib.spout/release/modules/lib/JNISpout_64.dll b/lib.spout/release/modules/lib/JNISpout_64.dll new file mode 100644 index 00000000..bee57466 Binary files /dev/null and b/lib.spout/release/modules/lib/JNISpout_64.dll differ diff --git a/lib.spout/src/spout/Bundle.properties b/lib.spout/src/spout/Bundle.properties new file mode 100644 index 00000000..0c226e34 --- /dev/null +++ b/lib.spout/src/spout/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=lib.spout diff --git a/lib.syphon/build.xml b/lib.syphon/build.xml new file mode 100644 index 00000000..40776423 --- /dev/null +++ b/lib.syphon/build.xml @@ -0,0 +1,8 @@ + + + + + + Builds, tests, and runs the project codeanticode.syphon. + + diff --git a/lib.syphon/manifest.mf b/lib.syphon/manifest.mf new file mode 100644 index 00000000..7f7b2737 --- /dev/null +++ b/lib.syphon/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +AutoUpdate-Show-In-Client: true +OpenIDE-Module: codeanticode.syphon +OpenIDE-Module-Localizing-Bundle: codeanticode/syphon/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/lib.syphon/nbproject/build-impl.xml b/lib.syphon/nbproject/build-impl.xml new file mode 100644 index 00000000..606d0536 --- /dev/null +++ b/lib.syphon/nbproject/build-impl.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + You must set 'suite.dir' to point to your containing module suite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib.syphon/nbproject/genfiles.properties b/lib.syphon/nbproject/genfiles.properties new file mode 100644 index 00000000..786878f5 --- /dev/null +++ b/lib.syphon/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=a15ed878 +build.xml.script.CRC32=4e0e891f +build.xml.stylesheet.CRC32=a56c6a5b@2.71.1 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=a15ed878 +nbproject/build-impl.xml.script.CRC32=9f3f1cde +nbproject/build-impl.xml.stylesheet.CRC32=238281d1@2.71.1 diff --git a/lib.syphon/nbproject/project.properties b/lib.syphon/nbproject/project.properties new file mode 100644 index 00000000..00895c5d --- /dev/null +++ b/lib.syphon/nbproject/project.properties @@ -0,0 +1,3 @@ +file.reference.jsyphon.jar=release/modules/ext/jsyphon.jar +file.reference.Syphon.jar=release/modules/ext/Syphon.jar +is.autoload=true diff --git a/lib.syphon/nbproject/project.xml b/lib.syphon/nbproject/project.xml new file mode 100644 index 00000000..56072c25 --- /dev/null +++ b/lib.syphon/nbproject/project.xml @@ -0,0 +1,31 @@ + + + org.netbeans.modules.apisupport.project + + + codeanticode.syphon + + + + processing.core + + + + 3.2.3 + + + + + codeanticode.syphon + + + ext/jsyphon.jar + release/modules/ext/jsyphon.jar + + + ext/Syphon.jar + release/modules/ext/Syphon.jar + + + + diff --git a/lib.syphon/nbproject/suite.properties b/lib.syphon/nbproject/suite.properties new file mode 100644 index 00000000..29d7cc9b --- /dev/null +++ b/lib.syphon/nbproject/suite.properties @@ -0,0 +1 @@ +suite.dir=${basedir}/.. diff --git a/lib.syphon/release/modules/ext/Syphon.jar b/lib.syphon/release/modules/ext/Syphon.jar new file mode 100644 index 00000000..8cab869e Binary files /dev/null and b/lib.syphon/release/modules/ext/Syphon.jar differ diff --git a/lib.syphon/release/modules/ext/jsyphon.jar b/lib.syphon/release/modules/ext/jsyphon.jar new file mode 100644 index 00000000..79d027f9 Binary files /dev/null and b/lib.syphon/release/modules/ext/jsyphon.jar differ diff --git a/lib.syphon/release/modules/lib/Syphon b/lib.syphon/release/modules/lib/Syphon new file mode 100644 index 00000000..1a702acd Binary files /dev/null and b/lib.syphon/release/modules/lib/Syphon differ diff --git a/lib.syphon/release/modules/lib/libJSyphon.jnilib b/lib.syphon/release/modules/lib/libJSyphon.jnilib new file mode 100644 index 00000000..e15721e6 Binary files /dev/null and b/lib.syphon/release/modules/lib/libJSyphon.jnilib differ diff --git a/lib.syphon/src/codeanticode/syphon/Bundle.properties b/lib.syphon/src/codeanticode/syphon/Bundle.properties new file mode 100644 index 00000000..e2371a08 --- /dev/null +++ b/lib.syphon/src/codeanticode/syphon/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=lib.syphon diff --git a/nbproject/project.properties b/nbproject/project.properties index 8a612a2a..3142e4a8 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -1,128 +1,136 @@ -app.icon=branding/core/core.jar/org/netbeans/core/startup/frame48.gif -app.name=${branding.token} -app.title=praxis -app.conf=praxis.conf -auxiliary.org-netbeans-modules-apisupport-installer.license-type=no -auxiliary.org-netbeans-modules-apisupport-installer.os-linux=true -auxiliary.org-netbeans-modules-apisupport-installer.os-macosx=false -auxiliary.org-netbeans-modules-apisupport-installer.os-solaris=false -auxiliary.org-netbeans-modules-apisupport-installer.os-windows=false -auxiliary.org-netbeans-modules-apisupport-installer.pack200-enabled=false -modules=\ - ${project.net.neilcsmith.praxis.core}:\ - ${project.net.neilcsmith.praxis.impl}:\ - ${project.net.neilcsmith.praxis.hub}:\ - ${project.net.neilcsmith.praxis.script}:\ - ${project.net.neilcsmith.praxis.components}:\ - ${project.net.neilcsmith.audio.ops}:\ - ${project.net.neilcsmith.jnajack}:\ - ${project.net.miginfocom}:\ - ${project.net.neilcsmith.praxis.gui}:\ - ${project.net.neilcsmith.praxis.util}:\ - ${project.net.neilcsmith.audio.servers}:\ - ${project.net.neilcsmith.praxis.midi}:\ - ${project.net.neilcsmith.praxis.audio}:\ - ${project.net.neilcsmith.praxis.video}:\ - ${project.net.neilcsmith.audioops.impl.gpl}:\ - ${project.net.neilcsmith.audioops.impl}:\ - ${project.net.neilcsmith.audioservers.javasound}:\ - ${project.net.neilcsmith.audioservers.jack}:\ - ${project.net.neilcsmith.praxis.player}:\ - ${project.net.neilcsmith.praxis.terminal}:\ - ${project.net.neilcsmith.praxis.settings}:\ - ${project.org.jaudiolibs.pipes}:\ - ${project.net.neilcsmith.praxis.audio.io}:\ - ${project.com.tinkerforge}:\ - ${project.net.neilcsmith.praxis.tinkerforge}:\ - ${project.net.neilcsmith.praxis.video.render}:\ - ${project.net.neilcsmith.praxis.video.pipes}:\ - ${project.net.neilcsmith.praxis.texteditor}:\ - ${project.net.neilcsmith.praxis.nblookup}:\ - ${project.net.neilcsmith.praxis.meta}:\ - ${project.de.sciss.net}:\ - ${project.net.neilcsmith.praxis.osc}:\ - ${project.com.sun.jna}:\ - ${project.net.neilcsmith.praxis.nb.classpath}:\ - ${project.org.praxislive.compiler.javac}:\ - ${project.net.neilcsmith.praxis.compiler}:\ - ${project.javax.media.opengl}:\ - ${project.processing.core}:\ - ${project.net.neilcsmith.praxis.video.pgl}:\ - ${project.net.neilcsmith.praxis.code}:\ - ${project.net.neilcsmith.praxis.video.code}:\ - ${project.net.neilcsmith.praxis.video.pgl.custom}:\ - ${project.net.neilcsmith.praxis.hub.net}:\ - ${project.net.neilcsmith.praxis.logging}:\ - ${project.net.neilcsmith.praxis.core.factory}:\ - ${project.net.neilcsmith.praxis.video.factory}:\ - ${project.net.neilcsmith.praxis.core.code}:\ - ${project.net.neilcsmith.praxis.audio.code}:\ - ${project.net.neilcsmith.praxis.audio.factory}:\ - ${project.net.neilcsmith.praxis.tracker}:\ - ${project.org.freedesktop.gstreamer}:\ - ${project.net.neilcsmith.praxis.video.gst1}:\ - ${project.net.neilcsmith.praxis.code.services}:\ - ${project.com.bulenkov.darcula}:\ - ${project.fi.iki.elonen}:\ - ${project.net.neilcsmith.praxis.data}:\ - ${project.com.sun.jna.platform} -project.com.bulenkov.darcula=lib.darcula -project.com.sun.jna=lib.jna -project.com.sun.jna.platform=lib.jna.platform -project.com.tinkerforge=lib.tinkerforge -project.de.sciss.net=lib.netutil -project.fi.iki.elonen=lib.nanohttpd -project.javax.media.opengl=lib.jogl -project.net.miginfocom=lib.miglayout -project.net.neilcsmith.audio.ops=audio.ops -project.net.neilcsmith.audio.servers=audio.servers -project.net.neilcsmith.audioops.impl=audio.ops.impl -project.net.neilcsmith.audioops.impl.gpl=audio.ops.gpl -project.net.neilcsmith.audioservers.jack=audio.servers.jack -project.net.neilcsmith.audioservers.javasound=audio.servers.javasound -project.net.neilcsmith.jnajack=audio.jnajack -project.net.neilcsmith.praxis.audio=praxis.audio -project.net.neilcsmith.praxis.audio.code=praxis.audio.code -project.net.neilcsmith.praxis.audio.factory=praxis.audio.factory -project.net.neilcsmith.praxis.audio.io=praxis.audio.loader -project.net.neilcsmith.praxis.code=praxis.code -project.net.neilcsmith.praxis.code.services=praxis.code.services -project.net.neilcsmith.praxis.compiler=praxis.compiler -project.net.neilcsmith.praxis.components=praxis.components -project.net.neilcsmith.praxis.core=praxis.core -project.net.neilcsmith.praxis.core.code=praxis.core.code -project.net.neilcsmith.praxis.core.factory=praxis.core.factory -project.net.neilcsmith.praxis.data=praxis.data -project.net.neilcsmith.praxis.gui=praxis.gui -project.net.neilcsmith.praxis.hub=praxis.hub -project.net.neilcsmith.praxis.hub.net=praxis.hub.net -project.net.neilcsmith.praxis.impl=praxis.impl -project.net.neilcsmith.praxis.logging=praxis.logging -project.net.neilcsmith.praxis.meta=praxis.meta -project.net.neilcsmith.praxis.midi=praxis.midi -project.net.neilcsmith.praxis.nb.classpath=praxis.nb.classpath -project.net.neilcsmith.praxis.nblookup=praxis.nb.lookup -project.net.neilcsmith.praxis.osc=praxis.osc -project.net.neilcsmith.praxis.player=praxis.player -project.net.neilcsmith.praxis.script=praxis.script -project.net.neilcsmith.praxis.settings=praxis.settings -project.net.neilcsmith.praxis.terminal=praxis.terminal -project.net.neilcsmith.praxis.texteditor=praxis.texteditor -project.net.neilcsmith.praxis.tinkerforge=praxis.tinkerforge -project.net.neilcsmith.praxis.tracker=praxis.tracker -project.net.neilcsmith.praxis.util=praxis.util -project.net.neilcsmith.praxis.video=praxis.video -project.net.neilcsmith.praxis.video.code=praxis.video.code -project.net.neilcsmith.praxis.video.factory=praxis.video.factory -project.net.neilcsmith.praxis.video.gst1=praxis.video.gst1 -project.net.neilcsmith.praxis.video.pgl=praxis.video.pgl -project.net.neilcsmith.praxis.video.pgl.custom=praxis.video.pgl.code -project.net.neilcsmith.praxis.video.pipes=praxis.video.pipes -project.net.neilcsmith.praxis.video.render=praxis.video.render -project.org.freedesktop.gstreamer=lib.gst1-java-core -project.org.jaudiolibs.pipes=audio.pipes -project.org.praxislive.compiler.javac=praxis.compiler.javac -project.processing.core=lib.processing - -# good settings for Jack ? : -J-XX:+UseConcMarkSweepGC -J-XX:+CMSIncrementalMode -J-XX:+CMSIncrementalPacing -run.args.extra=--nosplash --slave --network all +app.icon=branding/core/core.jar/org/netbeans/core/startup/frame48.gif +app.name=${branding.token} +app.title=praxis +app.conf=praxis.conf +auxiliary.org-netbeans-modules-apisupport-installer.license-type=no +auxiliary.org-netbeans-modules-apisupport-installer.os-linux=true +auxiliary.org-netbeans-modules-apisupport-installer.os-macosx=false +auxiliary.org-netbeans-modules-apisupport-installer.os-solaris=false +auxiliary.org-netbeans-modules-apisupport-installer.os-windows=false +auxiliary.org-netbeans-modules-apisupport-installer.pack200-enabled=false +modules=\ + ${project.net.neilcsmith.praxis.core}:\ + ${project.net.neilcsmith.praxis.impl}:\ + ${project.net.neilcsmith.praxis.hub}:\ + ${project.net.neilcsmith.praxis.script}:\ + ${project.net.neilcsmith.praxis.components}:\ + ${project.net.neilcsmith.audio.ops}:\ + ${project.net.neilcsmith.jnajack}:\ + ${project.net.miginfocom}:\ + ${project.net.neilcsmith.praxis.gui}:\ + ${project.net.neilcsmith.praxis.util}:\ + ${project.net.neilcsmith.audio.servers}:\ + ${project.net.neilcsmith.praxis.midi}:\ + ${project.net.neilcsmith.praxis.audio}:\ + ${project.net.neilcsmith.praxis.video}:\ + ${project.net.neilcsmith.audioops.impl.gpl}:\ + ${project.net.neilcsmith.audioops.impl}:\ + ${project.net.neilcsmith.audioservers.javasound}:\ + ${project.net.neilcsmith.audioservers.jack}:\ + ${project.net.neilcsmith.praxis.player}:\ + ${project.net.neilcsmith.praxis.terminal}:\ + ${project.net.neilcsmith.praxis.settings}:\ + ${project.org.jaudiolibs.pipes}:\ + ${project.net.neilcsmith.praxis.audio.io}:\ + ${project.com.tinkerforge}:\ + ${project.net.neilcsmith.praxis.tinkerforge}:\ + ${project.net.neilcsmith.praxis.video.render}:\ + ${project.net.neilcsmith.praxis.video.pipes}:\ + ${project.net.neilcsmith.praxis.texteditor}:\ + ${project.net.neilcsmith.praxis.nblookup}:\ + ${project.net.neilcsmith.praxis.meta}:\ + ${project.de.sciss.net}:\ + ${project.net.neilcsmith.praxis.osc}:\ + ${project.com.sun.jna}:\ + ${project.net.neilcsmith.praxis.nb.classpath}:\ + ${project.org.praxislive.compiler.javac}:\ + ${project.net.neilcsmith.praxis.compiler}:\ + ${project.javax.media.opengl}:\ + ${project.processing.core}:\ + ${project.net.neilcsmith.praxis.video.pgl}:\ + ${project.net.neilcsmith.praxis.code}:\ + ${project.net.neilcsmith.praxis.video.code}:\ + ${project.net.neilcsmith.praxis.video.pgl.custom}:\ + ${project.net.neilcsmith.praxis.hub.net}:\ + ${project.net.neilcsmith.praxis.logging}:\ + ${project.net.neilcsmith.praxis.core.factory}:\ + ${project.net.neilcsmith.praxis.video.factory}:\ + ${project.net.neilcsmith.praxis.core.code}:\ + ${project.net.neilcsmith.praxis.audio.code}:\ + ${project.net.neilcsmith.praxis.audio.factory}:\ + ${project.net.neilcsmith.praxis.tracker}:\ + ${project.org.freedesktop.gstreamer}:\ + ${project.net.neilcsmith.praxis.video.gst1}:\ + ${project.net.neilcsmith.praxis.code.services}:\ + ${project.com.bulenkov.darcula}:\ + ${project.fi.iki.elonen}:\ + ${project.net.neilcsmith.praxis.data}:\ + ${project.com.sun.jna.platform}:\ + ${project.codeanticode.syphon}:\ + ${project.net.neilcsmith.praxis.video.pgl.syphon}:\ + ${project.net.neilcsmith.praxis.video.pgl.spout}:\ + ${project.spout} +project.codeanticode.syphon=lib.syphon +project.com.bulenkov.darcula=lib.darcula +project.com.sun.jna=lib.jna +project.com.sun.jna.platform=lib.jna.platform +project.com.tinkerforge=lib.tinkerforge +project.de.sciss.net=lib.netutil +project.fi.iki.elonen=lib.nanohttpd +project.javax.media.opengl=lib.jogl +project.net.miginfocom=lib.miglayout +project.net.neilcsmith.audio.ops=audio.ops +project.net.neilcsmith.audio.servers=audio.servers +project.net.neilcsmith.audioops.impl=audio.ops.impl +project.net.neilcsmith.audioops.impl.gpl=audio.ops.gpl +project.net.neilcsmith.audioservers.jack=audio.servers.jack +project.net.neilcsmith.audioservers.javasound=audio.servers.javasound +project.net.neilcsmith.jnajack=audio.jnajack +project.net.neilcsmith.praxis.audio=praxis.audio +project.net.neilcsmith.praxis.audio.code=praxis.audio.code +project.net.neilcsmith.praxis.audio.factory=praxis.audio.factory +project.net.neilcsmith.praxis.audio.io=praxis.audio.loader +project.net.neilcsmith.praxis.code=praxis.code +project.net.neilcsmith.praxis.code.services=praxis.code.services +project.net.neilcsmith.praxis.compiler=praxis.compiler +project.net.neilcsmith.praxis.components=praxis.components +project.net.neilcsmith.praxis.core=praxis.core +project.net.neilcsmith.praxis.core.code=praxis.core.code +project.net.neilcsmith.praxis.core.factory=praxis.core.factory +project.net.neilcsmith.praxis.data=praxis.data +project.net.neilcsmith.praxis.gui=praxis.gui +project.net.neilcsmith.praxis.hub=praxis.hub +project.net.neilcsmith.praxis.hub.net=praxis.hub.net +project.net.neilcsmith.praxis.impl=praxis.impl +project.net.neilcsmith.praxis.logging=praxis.logging +project.net.neilcsmith.praxis.meta=praxis.meta +project.net.neilcsmith.praxis.midi=praxis.midi +project.net.neilcsmith.praxis.nb.classpath=praxis.nb.classpath +project.net.neilcsmith.praxis.nblookup=praxis.nb.lookup +project.net.neilcsmith.praxis.osc=praxis.osc +project.net.neilcsmith.praxis.player=praxis.player +project.net.neilcsmith.praxis.script=praxis.script +project.net.neilcsmith.praxis.settings=praxis.settings +project.net.neilcsmith.praxis.terminal=praxis.terminal +project.net.neilcsmith.praxis.texteditor=praxis.texteditor +project.net.neilcsmith.praxis.tinkerforge=praxis.tinkerforge +project.net.neilcsmith.praxis.tracker=praxis.tracker +project.net.neilcsmith.praxis.util=praxis.util +project.net.neilcsmith.praxis.video=praxis.video +project.net.neilcsmith.praxis.video.code=praxis.video.code +project.net.neilcsmith.praxis.video.factory=praxis.video.factory +project.net.neilcsmith.praxis.video.gst1=praxis.video.gst1 +project.net.neilcsmith.praxis.video.pgl=praxis.video.pgl +project.net.neilcsmith.praxis.video.pgl.custom=praxis.video.pgl.code +project.net.neilcsmith.praxis.video.pgl.spout=praxis.video.pgl.spout +project.net.neilcsmith.praxis.video.pgl.syphon=praxis.video.pgl.syphon +project.net.neilcsmith.praxis.video.pipes=praxis.video.pipes +project.net.neilcsmith.praxis.video.render=praxis.video.render +project.org.freedesktop.gstreamer=lib.gst1-java-core +project.org.jaudiolibs.pipes=audio.pipes +project.org.praxislive.compiler.javac=praxis.compiler.javac +project.processing.core=lib.processing + +project.spout=lib.spout +# good settings for Jack ? : -J-XX:+UseConcMarkSweepGC -J-XX:+CMSIncrementalMode -J-XX:+CMSIncrementalPacing +run.args.extra=--nosplash --slave --network all diff --git a/praxis.video.code/src/net/neilcsmith/praxis/video/code/FontLoader.java b/praxis.video.code/src/net/neilcsmith/praxis/video/code/FontLoader.java new file mode 100644 index 00000000..3410ff0d --- /dev/null +++ b/praxis.video.code/src/net/neilcsmith/praxis/video/code/FontLoader.java @@ -0,0 +1,96 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. + * + * + * Parts of the API of this package, as well as some of the code, is derived from + * the Processing project (http://processing.org) + * + * Copyright (c) 2004-09 Ben Fry and Casey Reas + * Copyright (c) 2001-04 Massachusetts Institute of Technology + * + */ +package net.neilcsmith.praxis.video.code; + +import java.awt.Font; +import java.awt.FontFormatException; +import java.io.IOException; +import java.net.URI; +import java.util.Objects; +import net.neilcsmith.praxis.code.ResourceProperty; +import net.neilcsmith.praxis.video.code.userapi.PFont; +import net.neilcsmith.praxis.video.render.utils.FontUtils; + +/** + * + * @author Neil C Smith (http://neilcsmith.net) + */ +class FontLoader extends ResourceProperty.Loader { + + private final static FontLoader INSTANCE = new FontLoader(); + + private FontLoader() { + super(PFont.class); + } + + @Override + public PFont load(URI uri) throws IOException { + try { + Font baseFont = FontUtils.load(uri); + return new PFontImpl(baseFont); + } catch (FontFormatException ex) { + throw new IOException(ex); + } + } + + static FontLoader getDefault() { + return INSTANCE; + } + + private static class PFontImpl extends PFont { + + private final Font baseFont; + + private PFontImpl(Font baseFont) { + this.baseFont = baseFont; + } + + @Override + protected Font getFont() { + return baseFont; + } + + @Override + public int hashCode() { + return Objects.hashCode(baseFont); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof PFontImpl) { + return ((PFontImpl) obj).baseFont.equals(baseFont); + } else { + return false; + } + } + + } + +} diff --git a/praxis.video.code/src/net/neilcsmith/praxis/video/code/VideoCodeConnector.java b/praxis.video.code/src/net/neilcsmith/praxis/video/code/VideoCodeConnector.java index 6e5e257f..d43ce6a3 100644 --- a/praxis.video.code/src/net/neilcsmith/praxis/video/code/VideoCodeConnector.java +++ b/praxis.video.code/src/net/neilcsmith/praxis/video/code/VideoCodeConnector.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Neil C Smith. + * Copyright 2017 Neil C Smith. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as @@ -32,6 +32,7 @@ import net.neilcsmith.praxis.code.userapi.P; import net.neilcsmith.praxis.core.Port; import net.neilcsmith.praxis.video.code.userapi.OffScreen; +import net.neilcsmith.praxis.video.code.userapi.PFont; import net.neilcsmith.praxis.video.code.userapi.PImage; /** @@ -90,6 +91,21 @@ protected void analyseField(Field field) { } } + if (PFont.class.isAssignableFrom(field.getType())) { + P p = field.getAnnotation(P.class); + if (p != null) { + ResourceProperty.Descriptor fpd = + ResourceProperty.Descriptor.create(this, p, field, FontLoader.getDefault()); + if (fpd != null) { + addControl(fpd); + if (shouldAddPort(field)) { + addPort(fpd.createPortDescriptor()); + } + return; + } + } + } + if (field.isAnnotationPresent(OffScreen.class)) { OffScreenGraphicsInfo osgi = OffScreenGraphicsInfo.create(field); if (osgi != null) { diff --git a/praxis.video.code/src/net/neilcsmith/praxis/video/code/VideoCodeDelegate.java b/praxis.video.code/src/net/neilcsmith/praxis/video/code/VideoCodeDelegate.java index 389bab1d..ae174b5e 100644 --- a/praxis.video.code/src/net/neilcsmith/praxis/video/code/VideoCodeDelegate.java +++ b/praxis.video.code/src/net/neilcsmith/praxis/video/code/VideoCodeDelegate.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Neil C Smith. + * Copyright 2017 Neil C Smith. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as @@ -31,6 +31,7 @@ package net.neilcsmith.praxis.video.code; import net.neilcsmith.praxis.code.DefaultCodeDelegate; +import net.neilcsmith.praxis.video.code.userapi.PFont; import net.neilcsmith.praxis.video.code.userapi.PGraphics; import net.neilcsmith.praxis.video.code.userapi.PImage; import net.neilcsmith.praxis.video.render.SurfaceOp; @@ -230,6 +231,18 @@ public void strokeWeight(double weight) { pg.strokeWeight(weight); } + public void text(String text, double x, double y) { + pg.text(text, x, y); + } + + public void textFont(PFont font) { + pg.textFont(font); + } + + public void textFont(PFont font, double size) { + pg.textFont(font, size); + } + public void translate(double x, double y) { pg.translate(x, y); } diff --git a/praxis.video.code/src/net/neilcsmith/praxis/video/code/userapi/PFont.java b/praxis.video.code/src/net/neilcsmith/praxis/video/code/userapi/PFont.java new file mode 100644 index 00000000..c4b80245 --- /dev/null +++ b/praxis.video.code/src/net/neilcsmith/praxis/video/code/userapi/PFont.java @@ -0,0 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. + * + * + * Parts of the API of this package, as well as some of the code, is derived from + * the Processing project (http://processing.org) + * + * Copyright (c) 2004-09 Ben Fry and Casey Reas + * Copyright (c) 2001-04 Massachusetts Institute of Technology + * + */ +package net.neilcsmith.praxis.video.code.userapi; + +import java.awt.Font; + +/** + * + * @author Neil C Smith (http://neilcsmith.net) + */ +public abstract class PFont { + + protected abstract Font getFont(); + +} diff --git a/praxis.video.code/src/net/neilcsmith/praxis/video/code/userapi/PGraphics.java b/praxis.video.code/src/net/neilcsmith/praxis/video/code/userapi/PGraphics.java index 671ca815..5db12a36 100644 --- a/praxis.video.code/src/net/neilcsmith/praxis/video/code/userapi/PGraphics.java +++ b/praxis.video.code/src/net/neilcsmith/praxis/video/code/userapi/PGraphics.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2016 Neil C Smith. + * Copyright 2017 Neil C Smith. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as @@ -31,6 +31,7 @@ import java.awt.BasicStroke; import java.awt.Color; +import java.awt.Font; import java.awt.Polygon; import java.awt.Shape; import java.awt.geom.AffineTransform; @@ -45,6 +46,7 @@ import net.neilcsmith.praxis.video.render.ops.RectFill; import net.neilcsmith.praxis.video.render.ops.ScaledBlit; import net.neilcsmith.praxis.video.render.ops.ShapeRender; +import net.neilcsmith.praxis.video.render.ops.TextRender; import net.neilcsmith.praxis.video.render.ops.TransformBlit; /** @@ -62,11 +64,13 @@ public abstract class PGraphics extends PImage { private BasicStroke stroke = new BasicStroke(1); private PShape shape; private AffineTransform transform; + private Font font; private final Blit blit; private final ScaledBlit scaledBlit; private final TransformBlit transformBlit; private final RectFill rectFill; private final ShapeRender shapeRender; + private final TextRender textRender; protected PGraphics(int width, int height) { super(width, height); @@ -75,6 +79,7 @@ protected PGraphics(int width, int height) { this.transformBlit = new TransformBlit(); this.rectFill = new RectFill(); this.shapeRender = new ShapeRender(); + this.textRender = new TextRender(); } protected abstract Surface getSurface(); @@ -392,6 +397,18 @@ public void strokeWeight(double weight) { } stroke = new BasicStroke((float) weight); } + + public void text(String text, double x, double y) { + renderText(text, x, y); + } + + public void textFont(PFont font) { + textFont(font, 12); + } + + public void textFont(PFont font, double size) { + this.font = font.getFont().deriveFont((float) size); + } public void translate(double x, double y) { if (transform == null) { @@ -433,6 +450,22 @@ private void renderShape(Shape shape) { getSurface().process(shapeRender); } + + private void renderText(String text, double x, double y) { + if (fillColor == null || font == null) { + return; + } + + textRender.setFont(font) + .setColor(fillColor) + .setTransform(transform) + .setX(x) + .setY(y) + .setText(text); + getSurface().process(textRender); + + } + private void polygon(final int[] xPoints, final int[] yPoints, final int nPoints) { renderShape(new Polygon(xPoints, yPoints, nPoints)); diff --git a/praxis.video.gst1/src/net/neilcsmith/praxis/video/gst1/components/AbstractGstDelegate.java b/praxis.video.gst1/src/net/neilcsmith/praxis/video/gst1/components/AbstractGstDelegate.java index 9eaa0927..5d973ae5 100644 --- a/praxis.video.gst1/src/net/neilcsmith/praxis/video/gst1/components/AbstractGstDelegate.java +++ b/praxis.video.gst1/src/net/neilcsmith/praxis/video/gst1/components/AbstractGstDelegate.java @@ -43,6 +43,7 @@ import org.freedesktop.gstreamer.Bus; import org.freedesktop.gstreamer.Caps; import org.freedesktop.gstreamer.Element; +import org.freedesktop.gstreamer.FlowReturn; import org.freedesktop.gstreamer.Gst; import org.freedesktop.gstreamer.GstObject; import org.freedesktop.gstreamer.Pipeline; @@ -346,7 +347,7 @@ protected void doEOS() { private class NewSampleListener implements AppSink.NEW_SAMPLE { @Override - public void newBuffer(AppSink sink) { + public FlowReturn newSample(AppSink sink) { surfaceLock.lock(); Sample sample = sink.pullSample(); Structure capsStruct = sample.getCaps().getStructure(0); @@ -368,6 +369,7 @@ public void newBuffer(AppSink sink) { } finally { surfaceLock.unlock(); } + return FlowReturn.OK; } } @@ -375,7 +377,7 @@ public void newBuffer(AppSink sink) { private class NewPrerollListener implements AppSink.NEW_PREROLL { @Override - public void newPreroll(AppSink sink) { + public FlowReturn newPreroll(AppSink sink) { surfaceLock.lock(); Sample sample = sink.pullPreroll(); Structure capsStruct = sample.getCaps().getStructure(0); @@ -397,6 +399,7 @@ public void newPreroll(AppSink sink) { } finally { surfaceLock.unlock(); } + return FlowReturn.OK; } } diff --git a/praxis.video.gst1/src/net/neilcsmith/praxis/video/gst1/components/PlayBinDelegate.java b/praxis.video.gst1/src/net/neilcsmith/praxis/video/gst1/components/PlayBinDelegate.java index f929aabf..1292c090 100644 --- a/praxis.video.gst1/src/net/neilcsmith/praxis/video/gst1/components/PlayBinDelegate.java +++ b/praxis.video.gst1/src/net/neilcsmith/praxis/video/gst1/components/PlayBinDelegate.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2015 Neil C Smith. + * Copyright 2017 Neil C Smith. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as @@ -109,12 +109,18 @@ protected void doStop() { super.doStop(); this.rate = 1; } - - @Override protected void doEOS() { - doSeek(true, -1); + try { + if (isLooping()) { + doSeek(true, -1); + } else { + stop(); + } + } catch (Exception ex) { + error("", ex); + } } private void doSeek(boolean eos, long position) { diff --git a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/FontLoader.java b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/FontLoader.java new file mode 100644 index 00000000..008ec571 --- /dev/null +++ b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/FontLoader.java @@ -0,0 +1,99 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. + * + * + * Parts of the API of this package, as well as some of the code, is derived from + * the Processing project (http://processing.org) + * + * Copyright (c) 2004-09 Ben Fry and Casey Reas + * Copyright (c) 2001-04 Massachusetts Institute of Technology + * + */ +package net.neilcsmith.praxis.video.pgl.code; + +import java.awt.Font; +import java.awt.FontFormatException; +import java.io.IOException; +import java.net.URI; +import java.util.Objects; +import net.neilcsmith.praxis.code.ResourceProperty; +import net.neilcsmith.praxis.video.pgl.PGLContext; +import net.neilcsmith.praxis.video.pgl.code.userapi.PFont; +import net.neilcsmith.praxis.video.render.utils.FontUtils; + +/** + * + * @author Neil C Smith (http://neilcsmith.net) + */ +class FontLoader extends ResourceProperty.Loader { + + private final static FontLoader INSTANCE = new FontLoader(); + + private FontLoader() { + super(PFont.class); + } + + @Override + public PFont load(URI uri) throws IOException { + try { + Font baseFont = FontUtils.load(uri); + return new PFontImpl(baseFont); + } catch (FontFormatException ex) { + throw new IOException(ex); + } + } + + static FontLoader getDefault() { + return INSTANCE; + } + + private static class PFontImpl extends PFont { + + private final Font baseFont; + + private PFontImpl(Font baseFont) { + this.baseFont = baseFont; + } + + @Override + protected processing.core.PFont unwrap(PGLContext context, double size) { + return context.asPFont(baseFont.deriveFont((float) size)); + } + + @Override + public int hashCode() { + return Objects.hashCode(baseFont); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof PFontImpl) { + return ((PFontImpl) obj).baseFont.equals(baseFont); + } else { + return false; + } + } + + + + } + +} diff --git a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/P2DCodeConnector.java b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/P2DCodeConnector.java index 579bfd0f..84b486a2 100644 --- a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/P2DCodeConnector.java +++ b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/P2DCodeConnector.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2016 Neil C Smith. + * Copyright 2017 Neil C Smith. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as @@ -29,26 +29,26 @@ import net.neilcsmith.praxis.code.userapi.In; import net.neilcsmith.praxis.code.userapi.P; import net.neilcsmith.praxis.core.Port; +import net.neilcsmith.praxis.video.pgl.code.userapi.PFont; import net.neilcsmith.praxis.video.pgl.code.userapi.PImage; - +import net.neilcsmith.praxis.video.pgl.code.userapi.PShape; /** * * @author Neil C Smith */ public class P2DCodeConnector extends CodeConnector { - + public final static String SETUP = "setup"; public final static String DRAW = "draw"; - + private PGLVideoOutputPort.Descriptor output; - public P2DCodeConnector(CodeFactory.Task creator, P2DCodeDelegate delegate) { super(creator, delegate); } - + PGLVideoOutputPort.Descriptor extractOutput() { return output; } @@ -62,18 +62,19 @@ protected void addDefaultPorts() { @Override protected void analyseField(Field field) { - if (PImage.class.isAssignableFrom(field.getType())) { - In ann = field.getAnnotation(In.class); - if (ann != null) { - field.setAccessible(true); - addPort(new PGLVideoInputPort.Descriptor(findID(field), ann.value(), field)); - return; - } - - P p = field.getAnnotation(P.class); - if (p != null) { - ResourceProperty.Descriptor ipd = - ResourceProperty.Descriptor.create(this, p, field, ImageLoader.getDefault()); + + In ann = field.getAnnotation(In.class); + if (ann != null && PImage.class.isAssignableFrom(field.getType())) { + field.setAccessible(true); + addPort(new PGLVideoInputPort.Descriptor(findID(field), ann.value(), field)); + return; + } + + P p = field.getAnnotation(P.class); + if (p != null) { + if (PImage.class.isAssignableFrom(field.getType())) { + ResourceProperty.Descriptor ipd + = ResourceProperty.Descriptor.create(this, p, field, ImageLoader.getDefault()); if (ipd != null) { addControl(ipd); if (shouldAddPort(field)) { @@ -82,11 +83,33 @@ protected void analyseField(Field field) { return; } } + + if (PFont.class.isAssignableFrom(field.getType())) { + ResourceProperty.Descriptor fpd + = ResourceProperty.Descriptor.create(this, p, field, FontLoader.getDefault()); + if (fpd != null) { + addControl(fpd); + if (shouldAddPort(field)) { + addPort(fpd.createPortDescriptor()); + } + return; + } + } + + if (PShape.class.isAssignableFrom(field.getType())) { + ResourceProperty.Descriptor spd + = ResourceProperty.Descriptor.create(this, p, field, ShapeLoader.getDefault()); + if (spd != null) { + addControl(spd); + if (shouldAddPort(field)) { + addPort(spd.createPortDescriptor()); + } + return; + } + } } + super.analyseField(field); } - - - } diff --git a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/P2DCodeDelegate.java b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/P2DCodeDelegate.java index 712e762b..e367290b 100644 --- a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/P2DCodeDelegate.java +++ b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/P2DCodeDelegate.java @@ -1,5 +1,3 @@ - - package net.neilcsmith.praxis.video.pgl.code; import net.neilcsmith.praxis.code.DefaultCodeDelegate; @@ -8,13 +6,13 @@ import net.neilcsmith.praxis.video.pgl.code.userapi.PGraphics2D; import net.neilcsmith.praxis.video.pgl.code.userapi.PImage; import net.neilcsmith.praxis.video.pgl.code.userapi.PShader; - +import net.neilcsmith.praxis.video.pgl.code.userapi.PShape; public class P2DCodeDelegate extends DefaultCodeDelegate { - + public int width; public int height; - + PGraphics2D pg; void setupGraphics(PGraphics2D pg, int width, int height) { @@ -22,19 +20,18 @@ void setupGraphics(PGraphics2D pg, int width, int height) { this.width = width; this.height = height; } - - public void setup() {} - - public void draw() {} - - // extension delegate methods + public void setup() { + } + + public void draw() { + } + + // extension delegate methods public PShader createShader(String vertShader, String fragShader) { return pg.createShader(vertShader, fragShader); } - - - + // delegate methods public void beginShape() { pg.beginShape(); @@ -80,6 +77,14 @@ public void endShape(Constants.ShapeEndMode mode) { pg.endShape(mode); } + public PShape createShape() { + return pg.createShape(); + } + + public PShape createShape(Constants.ShapeType type) { + return pg.createShape(type); + } + public void clip(double a, double b, double c, double d) { pg.clip(a, b, c, d); } @@ -212,6 +217,18 @@ public void image(PImage img, double a, double b, double c, double d, int u1, in pg.image(img, a, b, c, d, u1, v1, u2, v2); } + public void shape(PShape shape) { + pg.shape(shape); + } + + public void shape(PShape shape, double x, double y) { + pg.shape(shape, x, y); + } + + public void shape(PShape shape, double a, double b, double c, double d) { + pg.shape(shape, a, b, c, d); + } + public double textAscent() { return pg.textAscent(); } @@ -443,7 +460,7 @@ public void clear() { public void background(PImage image) { pg.background(image); } - + public void shader(PShader shader) { pg.shader(shader); } @@ -455,5 +472,5 @@ public void resetShader() { public void filter(PShader shader) { pg.filter(shader); } - + } diff --git a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/P3DCodeConnector.java b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/P3DCodeConnector.java index f232886e..5853619e 100644 --- a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/P3DCodeConnector.java +++ b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/P3DCodeConnector.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2015 Neil C Smith. + * Copyright 2017 Neil C Smith. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as @@ -29,26 +29,26 @@ import net.neilcsmith.praxis.code.userapi.In; import net.neilcsmith.praxis.code.userapi.P; import net.neilcsmith.praxis.core.Port; +import net.neilcsmith.praxis.video.pgl.code.userapi.PFont; import net.neilcsmith.praxis.video.pgl.code.userapi.PImage; - +import net.neilcsmith.praxis.video.pgl.code.userapi.PShape; /** * * @author Neil C Smith */ public class P3DCodeConnector extends CodeConnector { - + public final static String SETUP = "setup"; public final static String DRAW = "draw"; - + private PGLVideoOutputPort.Descriptor output; - public P3DCodeConnector(CodeFactory.Task creator, P3DCodeDelegate delegate) { super(creator, delegate); } - + PGLVideoOutputPort.Descriptor extractOutput() { return output; } @@ -62,18 +62,19 @@ protected void addDefaultPorts() { @Override protected void analyseField(Field field) { - if (PImage.class.isAssignableFrom(field.getType())) { - In ann = field.getAnnotation(In.class); - if (ann != null) { - field.setAccessible(true); - addPort(new PGLVideoInputPort.Descriptor(findID(field), ann.value(), field)); - return; - } - - P p = field.getAnnotation(P.class); - if (p != null) { - ResourceProperty.Descriptor ipd = - ResourceProperty.Descriptor.create(this, p, field, ImageLoader.getDefault()); + + In ann = field.getAnnotation(In.class); + if (ann != null && PImage.class.isAssignableFrom(field.getType())) { + field.setAccessible(true); + addPort(new PGLVideoInputPort.Descriptor(findID(field), ann.value(), field)); + return; + } + + P p = field.getAnnotation(P.class); + if (p != null) { + if (PImage.class.isAssignableFrom(field.getType())) { + ResourceProperty.Descriptor ipd + = ResourceProperty.Descriptor.create(this, p, field, ImageLoader.getDefault()); if (ipd != null) { addControl(ipd); if (shouldAddPort(field)) { @@ -82,10 +83,33 @@ protected void analyseField(Field field) { return; } } + + if (PFont.class.isAssignableFrom(field.getType())) { + ResourceProperty.Descriptor fpd + = ResourceProperty.Descriptor.create(this, p, field, FontLoader.getDefault()); + if (fpd != null) { + addControl(fpd); + if (shouldAddPort(field)) { + addPort(fpd.createPortDescriptor()); + } + return; + } + } + + if (PShape.class.isAssignableFrom(field.getType())) { + ResourceProperty.Descriptor spd + = ResourceProperty.Descriptor.create(this, p, field, ShapeLoader.getDefault()); + if (spd != null) { + addControl(spd); + if (shouldAddPort(field)) { + addPort(spd.createPortDescriptor()); + } + return; + } + } } + super.analyseField(field); } - - } diff --git a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/P3DCodeDelegate.java b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/P3DCodeDelegate.java index cee3a630..e425a163 100644 --- a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/P3DCodeDelegate.java +++ b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/P3DCodeDelegate.java @@ -28,7 +28,7 @@ import net.neilcsmith.praxis.video.pgl.code.userapi.PGraphics3D; import net.neilcsmith.praxis.video.pgl.code.userapi.PImage; import net.neilcsmith.praxis.video.pgl.code.userapi.PShader; - +import net.neilcsmith.praxis.video.pgl.code.userapi.PShape; public class P3DCodeDelegate extends DefaultCodeDelegate { @@ -43,18 +43,17 @@ void setupGraphics(PGraphics3D pg, int width, int height) { this.height = height; } - public void setup() {} - - public void draw() {} - - // extension delegate methods + public void setup() { + } + + public void draw() { + } + // extension delegate methods public PShader createShader(String vertShader, String fragShader) { return pg.createShader(vertShader, fragShader); } - - - + // delegate methods public void beginCamera() { pg.beginCamera(); @@ -308,6 +307,14 @@ public void endShape(Constants.ShapeEndMode mode) { pg.endShape(mode); } + public PShape createShape() { + return pg.createShape(); + } + + public PShape createShape(Constants.ShapeType type) { + return pg.createShape(type); + } + public void clip(double a, double b, double c, double d) { pg.clip(a, b, c, d); } @@ -440,6 +447,18 @@ public void image(PImage img, double a, double b, double c, double d, int u1, in pg.image(img, a, b, c, d, u1, v1, u2, v2); } + public void shape(PShape shape) { + pg.shape(shape); + } + + public void shape(PShape shape, double x, double y) { + pg.shape(shape, x, y); + } + + public void shape(PShape shape, double a, double b, double c, double d) { + pg.shape(shape, a, b, c, d); + } + public double textAscent() { return pg.textAscent(); } @@ -683,8 +702,5 @@ public void resetShader() { public void filter(PShader shader) { pg.filter(shader); } - - - - + } diff --git a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/PShapeSVG.java b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/PShapeSVG.java new file mode 100644 index 00000000..e7471b49 --- /dev/null +++ b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/PShapeSVG.java @@ -0,0 +1,1793 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012-15 The Processing Foundation + Copyright (c) 2006-12 Ben Fry and Casey Reas + Copyright (c) 2004-06 Michael Chang + + 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 net.neilcsmith.praxis.video.pgl.code; + +import processing.data.*; + +// TODO replace these with PMatrix2D +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; + +import java.util.Map; +import java.util.HashMap; +import processing.core.PApplet; +import processing.core.PConstants; +import processing.core.PGraphics; +import processing.core.PMatrix2D; +import processing.core.PShape; + + +/** + * This class is not part of the Processing API and should not be used + * directly. Instead, use loadShape() and methods like it, which will make + * use of this class. Using this class directly will cause your code to break + * when combined with future versions of Processing. + *

+ * SVG stands for Scalable Vector Graphics, a portable graphics format. + * It is a vector format so it allows for "infinite" resolution and relatively + * small file sizes. Most modern media software can view SVG files, including + * Adobe products, Firefox, etc. Illustrator and Inkscape can edit SVG files. + * View the SVG specification here. + *

+ * We have no intention of turning this into a full-featured SVG library. + * The goal of this project is a basic shape importer that originally was small + * enough to be included with applets, meaning that its download size should be + * in the neighborhood of 25-30 Kb. Though we're far less limited nowadays on + * size constraints, we remain extremely limited in terms of time, and do not + * have volunteers who are available to maintain a larger SVG library. + *

+ * For more sophisticated import/export, consider the + * Batik + * library from the Apache Software Foundation. + *

+ * Batik is used in the SVG Export library in Processing 3, however using it + * for full SVG import is still a considerable amount of work. Wiring it to + * Java2D wouldn't be too bad, but using it with OpenGL, JavaFX, and features + * like begin/endRecord() and begin/endRaw() would be considerable effort. + *

+ * Future improvements to this library may focus on this properly supporting + * a specific subset of SVG, for instance the simpler SVG profiles known as + * SVG Tiny or Basic, + * although we still would not support the interactivity options. + * + *


+ * + * A minimal example program using SVG: + * (assuming a working moo.svg is in your data folder) + * + *

+ * PShape moo;
+ *
+ * void setup() {
+ *   size(400, 400);
+ *   moo = loadShape("moo.svg");
+ * }
+ * void draw() {
+ *   background(255);
+ *   shape(moo, mouseX, mouseY);
+ * }
+ * 
+ */ +class PShapeSVG extends PShape { + XML element; + + /// Values between 0 and 1. + protected float opacity; + float strokeOpacity; + float fillOpacity; + + /** Width of containing SVG (used for percentages). */ + protected float svgWidth; + + /** Height of containing SVG (used for percentages). */ + protected float svgHeight; + + /** √((w² + h²)/2) of containing SVG (used for percentages). */ + protected float svgSizeXY; + + protected Gradient strokeGradient; + String strokeName; // id of another object, gradients only? + + protected Gradient fillGradient; + String fillName; // id of another object + + + /** + * Initializes a new SVG object from the given XML object. + */ + public PShapeSVG(XML svg) { + this(null, svg, true); + + if (!svg.getName().equals("svg")) { + if (svg.getName().toLowerCase().equals("html")) { + // Common case is that files aren't downloaded properly + throw new RuntimeException("This appears to be a web page, not an SVG file."); + } else { + throw new RuntimeException("The root node is not , it's <" + svg.getName() + ">"); + } + } + } + + + protected PShapeSVG(PShapeSVG parent, XML properties, boolean parseKids) { + setParent(parent); + + // Need to get width/height in early. + if (properties.getName().equals("svg")) { + String unitWidth = properties.getString("width"); + String unitHeight = properties.getString("height"); + + // Can't handle width/height as percentages easily. I'm just going + // to put in 100 as a dummy value, beacuse this means that it will + // come out as a reasonable value. + if (unitWidth != null) width = parseUnitSize(unitWidth, 100); + if (unitHeight != null) height = parseUnitSize(unitHeight, 100); + + String viewBoxStr = properties.getString("viewBox"); + if (viewBoxStr != null) { + float[] viewBox = PApplet.parseFloat(PApplet.splitTokens(viewBoxStr)); + if (unitWidth == null || unitHeight == null) { + // Not proper parsing of the viewBox, but will cover us for cases where + // the width and height of the object is not specified. + width = viewBox[2]; + height = viewBox[3]; + } else { + // http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute + // TODO: preserveAspectRatio. + if (matrix == null) matrix = new PMatrix2D(); + matrix.scale(width/viewBox[2], height/viewBox[3]); + matrix.translate(-viewBox[0], -viewBox[1]); + } + } + + // Negative size is illegal. + if (width < 0 || height < 0) + throw new RuntimeException(": width (" + width + + ") and height (" + height + ") must not be negative."); + + // It's technically valid to have width or height == 0. Not specified at + // all is what to test for. + if ((unitWidth == null || unitHeight == null) && viewBoxStr == null) { + //throw new RuntimeException("width/height not specified"); +// PGraphics.showWarning("The width and/or height is not " + +// "readable in the tag of this file."); + // For the spec, the default is 100% and 100%. For purposes + // here, insert a dummy value because this is prolly just a + // font or something for which the w/h doesn't matter. + width = 1; + height = 1; + } + + svgWidth = width; + svgHeight = height; + svgSizeXY = PApplet.sqrt((svgWidth*svgWidth + svgHeight*svgHeight)/2.0f); + } + + element = properties; + name = properties.getString("id"); + // @#$(* adobe illustrator mangles names of objects when re-saving + if (name != null) { + while (true) { + String[] m = PApplet.match(name, "_x([A-Za-z0-9]{2})_"); + if (m == null) break; + char repair = (char) PApplet.unhex(m[1]); + name = name.replace(m[0], "" + repair); + } + } + + String displayStr = properties.getString("display", "inline"); + visible = !displayStr.equals("none"); + + String transformStr = properties.getString("transform"); + if (transformStr != null) { + if (matrix == null) { + matrix = parseTransform(transformStr); + } else { + matrix.preApply(parseTransform(transformStr)); + } + } + + if (parseKids) { + parseColors(properties); + parseChildren(properties); + } + } + + + // Broken out so that subclasses can copy any additional variables + // (i.e. fillGradientPaint and strokeGradientPaint) + protected void setParent(PShapeSVG parent) { + // Need to set this so that findChild() works. + // Otherwise 'parent' is null until addChild() is called later. + this.parent = parent; + + if (parent == null) { + // set values to their defaults according to the SVG spec + stroke = false; + strokeColor = 0xff000000; + strokeWeight = 1; + strokeCap = PConstants.SQUARE; // equivalent to BUTT in svg spec + strokeJoin = PConstants.MITER; + strokeGradient = null; +// strokeGradientPaint = null; + strokeName = null; + + fill = true; + fillColor = 0xff000000; + fillGradient = null; +// fillGradientPaint = null; + fillName = null; + + //hasTransform = false; + //transformation = null; //new float[] { 1, 0, 0, 1, 0, 0 }; + + // svgWidth, svgHeight, and svgXYSize done below. + + strokeOpacity = 1; + fillOpacity = 1; + opacity = 1; + + } else { + stroke = parent.stroke; + strokeColor = parent.strokeColor; + strokeWeight = parent.strokeWeight; + strokeCap = parent.strokeCap; + strokeJoin = parent.strokeJoin; + strokeGradient = parent.strokeGradient; +// strokeGradientPaint = parent.strokeGradientPaint; + strokeName = parent.strokeName; + + fill = parent.fill; + fillColor = parent.fillColor; + fillGradient = parent.fillGradient; +// fillGradientPaint = parent.fillGradientPaint; + fillName = parent.fillName; + + svgWidth = parent.svgWidth; + svgHeight = parent.svgHeight; + svgSizeXY = parent.svgSizeXY; + + opacity = parent.opacity; + } + + // The rect and ellipse modes are set to CORNER since it is the expected + // mode for svg shapes. + rectMode = CORNER; + ellipseMode = CORNER; + } + + + /** Factory method for subclasses. */ + protected PShapeSVG createShape(PShapeSVG parent, XML properties, boolean parseKids) { + return new PShapeSVG(parent, properties, parseKids); + } + + + protected void parseChildren(XML graphics) { + XML[] elements = graphics.getChildren(); + children = new PShape[elements.length]; + childCount = 0; + + for (XML elem : elements) { + PShape kid = parseChild(elem); + if (kid != null) addChild(kid); + } + children = (PShape[]) PApplet.subset(children, 0, childCount); + } + + + /** + * Parse a child XML element. + * Override this method to add parsing for more SVG elements. + */ + protected PShape parseChild(XML elem) { +// System.err.println("parsing child in pshape " + elem.getName()); + String name = elem.getName(); + PShapeSVG shape = null; + + + if (name == null) { + // just some whitespace that can be ignored (hopefully) + + } else if (name.equals("g")) { + shape = createShape(this, elem, true); + + } else if (name.equals("defs")) { + // generally this will contain gradient info, so may + // as well just throw it into a group element for parsing + shape = createShape(this, elem, true); + + } else if (name.equals("line")) { + shape = createShape(this, elem, true); + shape.parseLine(); + + } else if (name.equals("circle")) { + shape = createShape(this, elem, true); + shape.parseEllipse(true); + + } else if (name.equals("ellipse")) { + shape = createShape(this, elem, true); + shape.parseEllipse(false); + + } else if (name.equals("rect")) { + shape = createShape(this, elem, true); + shape.parseRect(); + + } else if (name.equals("polygon")) { + shape = createShape(this, elem, true); + shape.parsePoly(true); + + } else if (name.equals("polyline")) { + shape = createShape(this, elem, true); + shape.parsePoly(false); + + } else if (name.equals("path")) { + shape = createShape(this, elem, true); + shape.parsePath(); + + } else if (name.equals("radialGradient")) { + return new RadialGradient(this, elem); + + } else if (name.equals("linearGradient")) { + return new LinearGradient(this, elem); + + } else if (name.equals("font")) { + return new Font(this, elem); + +// } else if (name.equals("font-face")) { +// return new FontFace(this, elem); + +// } else if (name.equals("glyph") || name.equals("missing-glyph")) { +// return new FontGlyph(this, elem); + + } else if (name.equals("text")) { // || name.equals("font")) { +// PGraphics.showWarning("Text and fonts in SVG files are " + +// "not currently supported, convert text to outlines instead."); + + } else if (name.equals("filter")) { +// PGraphics.showWarning("Filters are not supported."); + + } else if (name.equals("mask")) { +// PGraphics.showWarning("Masks are not supported."); + + } else if (name.equals("pattern")) { +// PGraphics.showWarning("Patterns are not supported."); + + } else if (name.equals("stop")) { + // stop tag is handled by gradient parser, so don't warn about it + + } else if (name.equals("sodipodi:namedview")) { + // these are always in Inkscape files, the warnings get tedious + + } else if (name.equals("metadata") + || name.equals("title") || name.equals("desc")) { + // fontforge just stuffs in as a comment. + // All harmless stuff, irrelevant to rendering. + return null; + + } else if (!name.startsWith("#")) { +// PGraphics.showWarning("Ignoring <" + name + "> tag."); +// new Exception().printStackTrace(); + } + return shape; + } + + + protected void parseLine() { + kind = LINE; + family = PRIMITIVE; + params = new float[] { + getFloatWithUnit(element, "x1", svgWidth), + getFloatWithUnit(element, "y1", svgHeight), + getFloatWithUnit(element, "x2", svgWidth), + getFloatWithUnit(element, "y2", svgHeight) + }; + } + + + /** + * Handles parsing ellipse and circle tags. + * @param circle true if this is a circle and not an ellipse + */ + protected void parseEllipse(boolean circle) { + kind = ELLIPSE; + family = PRIMITIVE; + params = new float[4]; + + params[0] = getFloatWithUnit(element, "cx", svgWidth); + params[1] = getFloatWithUnit(element, "cy", svgHeight); + + float rx, ry; + if (circle) { + rx = ry = getFloatWithUnit(element, "r", svgSizeXY); + } else { + rx = getFloatWithUnit(element, "rx", svgWidth); + ry = getFloatWithUnit(element, "ry", svgHeight); + } + params[0] -= rx; + params[1] -= ry; + + params[2] = rx*2; + params[3] = ry*2; + } + + + protected void parseRect() { + kind = RECT; + family = PRIMITIVE; + params = new float[] { + getFloatWithUnit(element, "x", svgWidth), + getFloatWithUnit(element, "y", svgHeight), + getFloatWithUnit(element, "width", svgWidth), + getFloatWithUnit(element, "height", svgHeight) + }; + } + + + /** + * Parse a polyline or polygon from an SVG file. + * Syntax defined at http://www.w3.org/TR/SVG/shapes.html#PointsBNF + * @param close true if shape is closed (polygon), false if not (polyline) + */ + protected void parsePoly(boolean close) { + family = PATH; + this.close = close; + + String pointsAttr = element.getString("points"); + if (pointsAttr != null) { + String[] pointsBuffer = PApplet.splitTokens(pointsAttr); + vertexCount = pointsBuffer.length; + vertices = new float[vertexCount][2]; + for (int i = 0; i < vertexCount; i++) { + String pb[] = PApplet.splitTokens(pointsBuffer[i], ", \t\r\n"); + vertices[i][X] = Float.parseFloat(pb[0]); + vertices[i][Y] = Float.parseFloat(pb[1]); + } + } + } + + + protected void parsePath() { + family = PATH; + kind = 0; + + String pathData = element.getString("d"); + if (pathData == null || PApplet.trim(pathData).length() == 0) { + return; + } + char[] pathDataChars = pathData.toCharArray(); + + StringBuilder pathBuffer = new StringBuilder(); + boolean lastSeparate = false; + + for (int i = 0; i < pathDataChars.length; i++) { + char c = pathDataChars[i]; + boolean separate = false; + + if (c == 'M' || c == 'm' || + c == 'L' || c == 'l' || + c == 'H' || c == 'h' || + c == 'V' || c == 'v' || + c == 'C' || c == 'c' || // beziers + c == 'S' || c == 's' || + c == 'Q' || c == 'q' || // quadratic beziers + c == 'T' || c == 't' || + c == 'A' || c == 'a' || // elliptical arc + c == 'Z' || c == 'z' || // closepath + c == ',') { + separate = true; + if (i != 0) { + pathBuffer.append("|"); + } + } + if (c == 'Z' || c == 'z') { + separate = false; + } + if (c == '-' && !lastSeparate) { + // allow for 'e' notation in numbers, e.g. 2.10e-9 + // http://dev.processing.org/bugs/show_bug.cgi?id=1408 + if (i == 0 || pathDataChars[i-1] != 'e') { + pathBuffer.append("|"); + } + } + if (c != ',') { + pathBuffer.append(c); //"" + pathDataBuffer.charAt(i)); + } + if (separate && c != ',' && c != '-') { + pathBuffer.append("|"); + } + lastSeparate = separate; + } + + // use whitespace constant to get rid of extra spaces and CR or LF + String[] pathTokens = + PApplet.splitTokens(pathBuffer.toString(), "|" + WHITESPACE); + vertices = new float[pathTokens.length][2]; + vertexCodes = new int[pathTokens.length]; + + float cx = 0; + float cy = 0; + int i = 0; + + char implicitCommand = '\0'; +// char prevCommand = '\0'; + boolean prevCurve = false; + float ctrlX, ctrlY; + // store values for closepath so that relative coords work properly + float movetoX = 0; + float movetoY = 0; + + while (i < pathTokens.length) { + char c = pathTokens[i].charAt(0); + if (((c >= '0' && c <= '9') || (c == '-')) && implicitCommand != '\0') { + c = implicitCommand; + i--; + } else { + implicitCommand = c; + } + switch (c) { + + case 'M': // M - move to (absolute) + cx = PApplet.parseFloat(pathTokens[i + 1]); + cy = PApplet.parseFloat(pathTokens[i + 2]); + movetoX = cx; + movetoY = cy; + parsePathMoveto(cx, cy); + implicitCommand = 'L'; + i += 3; + break; + + case 'm': // m - move to (relative) + cx = cx + PApplet.parseFloat(pathTokens[i + 1]); + cy = cy + PApplet.parseFloat(pathTokens[i + 2]); + movetoX = cx; + movetoY = cy; + parsePathMoveto(cx, cy); + implicitCommand = 'l'; + i += 3; + break; + + case 'L': + cx = PApplet.parseFloat(pathTokens[i + 1]); + cy = PApplet.parseFloat(pathTokens[i + 2]); + parsePathLineto(cx, cy); + i += 3; + break; + + case 'l': + cx = cx + PApplet.parseFloat(pathTokens[i + 1]); + cy = cy + PApplet.parseFloat(pathTokens[i + 2]); + parsePathLineto(cx, cy); + i += 3; + break; + + // horizontal lineto absolute + case 'H': + cx = PApplet.parseFloat(pathTokens[i + 1]); + parsePathLineto(cx, cy); + i += 2; + break; + + // horizontal lineto relative + case 'h': + cx = cx + PApplet.parseFloat(pathTokens[i + 1]); + parsePathLineto(cx, cy); + i += 2; + break; + + case 'V': + cy = PApplet.parseFloat(pathTokens[i + 1]); + parsePathLineto(cx, cy); + i += 2; + break; + + case 'v': + cy = cy + PApplet.parseFloat(pathTokens[i + 1]); + parsePathLineto(cx, cy); + i += 2; + break; + + // C - curve to (absolute) + case 'C': { + float ctrlX1 = PApplet.parseFloat(pathTokens[i + 1]); + float ctrlY1 = PApplet.parseFloat(pathTokens[i + 2]); + float ctrlX2 = PApplet.parseFloat(pathTokens[i + 3]); + float ctrlY2 = PApplet.parseFloat(pathTokens[i + 4]); + float endX = PApplet.parseFloat(pathTokens[i + 5]); + float endY = PApplet.parseFloat(pathTokens[i + 6]); + parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY); + cx = endX; + cy = endY; + i += 7; + prevCurve = true; + } + break; + + // c - curve to (relative) + case 'c': { + float ctrlX1 = cx + PApplet.parseFloat(pathTokens[i + 1]); + float ctrlY1 = cy + PApplet.parseFloat(pathTokens[i + 2]); + float ctrlX2 = cx + PApplet.parseFloat(pathTokens[i + 3]); + float ctrlY2 = cy + PApplet.parseFloat(pathTokens[i + 4]); + float endX = cx + PApplet.parseFloat(pathTokens[i + 5]); + float endY = cy + PApplet.parseFloat(pathTokens[i + 6]); + parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY); + cx = endX; + cy = endY; + i += 7; + prevCurve = true; + } + break; + + // S - curve to shorthand (absolute) + // Draws a cubic Bézier curve from the current point to (x,y). The first + // control point is assumed to be the reflection of the second control + // point on the previous command relative to the current point. + // (x2,y2) is the second control point (i.e., the control point + // at the end of the curve). S (uppercase) indicates that absolute + // coordinates will follow; s (lowercase) indicates that relative + // coordinates will follow. Multiple sets of coordinates may be specified + // to draw a polybézier. At the end of the command, the new current point + // becomes the final (x,y) coordinate pair used in the polybézier. + case 'S': { + // (If there is no previous command or if the previous command was not + // an C, c, S or s, assume the first control point is coincident with + // the current point.) + if (!prevCurve) { + ctrlX = cx; + ctrlY = cy; + } else { + float ppx = vertices[vertexCount-2][X]; + float ppy = vertices[vertexCount-2][Y]; + float px = vertices[vertexCount-1][X]; + float py = vertices[vertexCount-1][Y]; + ctrlX = px + (px - ppx); + ctrlY = py + (py - ppy); + } + float ctrlX2 = PApplet.parseFloat(pathTokens[i + 1]); + float ctrlY2 = PApplet.parseFloat(pathTokens[i + 2]); + float endX = PApplet.parseFloat(pathTokens[i + 3]); + float endY = PApplet.parseFloat(pathTokens[i + 4]); + parsePathCurveto(ctrlX, ctrlY, ctrlX2, ctrlY2, endX, endY); + cx = endX; + cy = endY; + i += 5; + prevCurve = true; + } + break; + + // s - curve to shorthand (relative) + case 's': { + if (!prevCurve) { + ctrlX = cx; + ctrlY = cy; + } else { + float ppx = vertices[vertexCount-2][X]; + float ppy = vertices[vertexCount-2][Y]; + float px = vertices[vertexCount-1][X]; + float py = vertices[vertexCount-1][Y]; + ctrlX = px + (px - ppx); + ctrlY = py + (py - ppy); + } + float ctrlX2 = cx + PApplet.parseFloat(pathTokens[i + 1]); + float ctrlY2 = cy + PApplet.parseFloat(pathTokens[i + 2]); + float endX = cx + PApplet.parseFloat(pathTokens[i + 3]); + float endY = cy + PApplet.parseFloat(pathTokens[i + 4]); + parsePathCurveto(ctrlX, ctrlY, ctrlX2, ctrlY2, endX, endY); + cx = endX; + cy = endY; + i += 5; + prevCurve = true; + } + break; + + // Q - quadratic curve to (absolute) + // Draws a quadratic Bézier curve from the current point to (x,y) using + // (x1,y1) as the control point. Q (uppercase) indicates that absolute + // coordinates will follow; q (lowercase) indicates that relative + // coordinates will follow. Multiple sets of coordinates may be specified + // to draw a polybézier. At the end of the command, the new current point + // becomes the final (x,y) coordinate pair used in the polybézier. + case 'Q': { + ctrlX = PApplet.parseFloat(pathTokens[i + 1]); + ctrlY = PApplet.parseFloat(pathTokens[i + 2]); + float endX = PApplet.parseFloat(pathTokens[i + 3]); + float endY = PApplet.parseFloat(pathTokens[i + 4]); + //parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY); + parsePathQuadto(ctrlX, ctrlY, endX, endY); + cx = endX; + cy = endY; + i += 5; + prevCurve = true; + } + break; + + // q - quadratic curve to (relative) + case 'q': { + ctrlX = cx + PApplet.parseFloat(pathTokens[i + 1]); + ctrlY = cy + PApplet.parseFloat(pathTokens[i + 2]); + float endX = cx + PApplet.parseFloat(pathTokens[i + 3]); + float endY = cy + PApplet.parseFloat(pathTokens[i + 4]); + //parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY); + parsePathQuadto(ctrlX, ctrlY, endX, endY); + cx = endX; + cy = endY; + i += 5; + prevCurve = true; + } + break; + + // T - quadratic curveto shorthand (absolute) + // The control point is assumed to be the reflection of the control + // point on the previous command relative to the current point. + case 'T': { + // If there is no previous command or if the previous command was + // not a Q, q, T or t, assume the control point is coincident + // with the current point. + if (!prevCurve) { + ctrlX = cx; + ctrlY = cy; + } else { + float ppx = vertices[vertexCount-2][X]; + float ppy = vertices[vertexCount-2][Y]; + float px = vertices[vertexCount-1][X]; + float py = vertices[vertexCount-1][Y]; + ctrlX = px + (px - ppx); + ctrlY = py + (py - ppy); + } + float endX = PApplet.parseFloat(pathTokens[i + 1]); + float endY = PApplet.parseFloat(pathTokens[i + 2]); + //parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY); + parsePathQuadto(ctrlX, ctrlY, endX, endY); + cx = endX; + cy = endY; + i += 3; + prevCurve = true; + } + break; + + // t - quadratic curveto shorthand (relative) + case 't': { + if (!prevCurve) { + ctrlX = cx; + ctrlY = cy; + } else { + float ppx = vertices[vertexCount-2][X]; + float ppy = vertices[vertexCount-2][Y]; + float px = vertices[vertexCount-1][X]; + float py = vertices[vertexCount-1][Y]; + ctrlX = px + (px - ppx); + ctrlY = py + (py - ppy); + } + float endX = cx + PApplet.parseFloat(pathTokens[i + 1]); + float endY = cy + PApplet.parseFloat(pathTokens[i + 2]); + //parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY); + parsePathQuadto(ctrlX, ctrlY, endX, endY); + cx = endX; + cy = endY; + i += 3; + prevCurve = true; + } + break; + + // A - elliptical arc to (absolute) + case 'A': { + float rx = PApplet.parseFloat(pathTokens[i + 1]); + float ry = PApplet.parseFloat(pathTokens[i + 2]); + float angle = PApplet.parseFloat(pathTokens[i + 3]); + boolean fa = PApplet.parseFloat(pathTokens[i + 4]) != 0; + boolean fs = PApplet.parseFloat(pathTokens[i + 5]) != 0; + float endX = PApplet.parseFloat(pathTokens[i + 6]); + float endY = PApplet.parseFloat(pathTokens[i + 7]); + parsePathArcto(cx, cy, rx, ry, angle, fa, fs, endX, endY); + cx = endX; + cy = endY; + i += 8; + prevCurve = true; + } + break; + + // a - elliptical arc to (relative) + case 'a': { + float rx = PApplet.parseFloat(pathTokens[i + 1]); + float ry = PApplet.parseFloat(pathTokens[i + 2]); + float angle = PApplet.parseFloat(pathTokens[i + 3]); + boolean fa = PApplet.parseFloat(pathTokens[i + 4]) != 0; + boolean fs = PApplet.parseFloat(pathTokens[i + 5]) != 0; + float endX = cx + PApplet.parseFloat(pathTokens[i + 6]); + float endY = cy + PApplet.parseFloat(pathTokens[i + 7]); + parsePathArcto(cx, cy, rx, ry, angle, fa, fs, endX, endY); + cx = endX; + cy = endY; + i += 8; + prevCurve = true; + } + break; + + case 'Z': + case 'z': + // since closing the path, the 'current' point needs + // to return back to the last moveto location. + // http://code.google.com/p/processing/issues/detail?id=1058 + cx = movetoX; + cy = movetoY; + close = true; + i++; + break; + + default: + String parsed = + PApplet.join(PApplet.subset(pathTokens, 0, i), ","); + String unparsed = + PApplet.join(PApplet.subset(pathTokens, i), ","); + System.err.println("parsed: " + parsed); + System.err.println("unparsed: " + unparsed); + throw new RuntimeException("shape command not handled: " + pathTokens[i]); + } +// prevCommand = c; + } + } + + +// private void parsePathCheck(int num) { +// if (vertexCount + num-1 >= vertices.length) { +// //vertices = (float[][]) PApplet.expand(vertices); +// float[][] temp = new float[vertexCount << 1][2]; +// System.arraycopy(vertices, 0, temp, 0, vertexCount); +// vertices = temp; +// } +// } + + private void parsePathVertex(float x, float y) { + if (vertexCount == vertices.length) { + //vertices = (float[][]) PApplet.expand(vertices); + float[][] temp = new float[vertexCount << 1][2]; + System.arraycopy(vertices, 0, temp, 0, vertexCount); + vertices = temp; + } + vertices[vertexCount][X] = x; + vertices[vertexCount][Y] = y; + vertexCount++; + } + + + private void parsePathCode(int what) { + if (vertexCodeCount == vertexCodes.length) { + vertexCodes = PApplet.expand(vertexCodes); + } + vertexCodes[vertexCodeCount++] = what; + } + + + private void parsePathMoveto(float px, float py) { + if (vertexCount > 0) { + parsePathCode(BREAK); + } + parsePathCode(VERTEX); + parsePathVertex(px, py); + } + + + private void parsePathLineto(float px, float py) { + parsePathCode(VERTEX); + parsePathVertex(px, py); + } + + + private void parsePathCurveto(float x1, float y1, + float x2, float y2, + float x3, float y3) { + parsePathCode(BEZIER_VERTEX); + parsePathVertex(x1, y1); + parsePathVertex(x2, y2); + parsePathVertex(x3, y3); + } + +// private void parsePathQuadto(float x1, float y1, +// float cx, float cy, +// float x2, float y2) { +// //System.out.println("quadto: " + x1 + "," + y1 + " " + cx + "," + cy + " " + x2 + "," + y2); +//// parsePathCode(BEZIER_VERTEX); +// parsePathCode(QUAD_BEZIER_VERTEX); +// // x1/y1 already covered by last moveto, lineto, or curveto +// +// parsePathVertex(x1 + ((cx-x1)*2/3.0f), y1 + ((cy-y1)*2/3.0f)); +// parsePathVertex(x2 + ((cx-x2)*2/3.0f), y2 + ((cy-y2)*2/3.0f)); +// parsePathVertex(x2, y2); +// } + + private void parsePathQuadto(float cx, float cy, + float x2, float y2) { + //System.out.println("quadto: " + x1 + "," + y1 + " " + cx + "," + cy + " " + x2 + "," + y2); +// parsePathCode(BEZIER_VERTEX); + parsePathCode(QUADRATIC_VERTEX); + // x1/y1 already covered by last moveto, lineto, or curveto + parsePathVertex(cx, cy); + parsePathVertex(x2, y2); + } + + + // Approximates elliptical arc by several bezier segments. + // Meets SVG standard requirements from: + // http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands + // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes + // Based on arc to bezier curve equations from: + // http://www.spaceroots.org/documents/ellipse/node22.html + private void parsePathArcto(float x1, float y1, + float rx, float ry, + float angle, + boolean fa, boolean fs, + float x2, float y2) { + if (x1 == x2 && y1 == y2) return; + if (rx == 0 || ry == 0) { parsePathLineto(x2, y2); return; } + + rx = PApplet.abs(rx); ry = PApplet.abs(ry); + + float phi = PApplet.radians(((angle % 360) + 360) % 360); + float cosPhi = PApplet.cos(phi), sinPhi = PApplet.sin(phi); + + float x1r = ( cosPhi * (x1 - x2) + sinPhi * (y1 - y2)) / 2; + float y1r = (-sinPhi * (x1 - x2) + cosPhi * (y1 - y2)) / 2; + + float cxr, cyr; + { + float A = (x1r*x1r) / (rx*rx) + (y1r*y1r) / (ry*ry); + if (A > 1) { + // No solution, scale ellipse up according to SVG standard + float sqrtA = PApplet.sqrt(A); + rx *= sqrtA; cxr = 0; + ry *= sqrtA; cyr = 0; + } else { + float k = ((fa == fs) ? -1f : 1f) * + PApplet.sqrt((rx*rx * ry*ry) / ((rx*rx * y1r*y1r) + (ry*ry * x1r*x1r)) - 1f); + cxr = k * rx * y1r / ry; + cyr = -k * ry * x1r / rx; + } + } + + float cx = cosPhi * cxr - sinPhi * cyr + (x1 + x2) / 2; + float cy = sinPhi * cxr + cosPhi * cyr + (y1 + y2) / 2; + + float phi1, phiDelta; + { + float sx = ( x1r - cxr) / rx, sy = ( y1r - cyr) / ry; + float tx = (-x1r - cxr) / rx, ty = (-y1r - cyr) / ry; + phi1 = PApplet.atan2(sy, sx); + phiDelta = (((PApplet.atan2(ty, tx) - phi1) % TWO_PI) + TWO_PI) % TWO_PI; + if (!fs) phiDelta -= TWO_PI; + } + + // One segment can not cover more that PI, less than PI/2 is + // recommended to avoid visible inaccuracies caused by rounding errors + int segmentCount = PApplet.ceil(PApplet.abs(phiDelta) / TWO_PI * 4); + + float inc = phiDelta / segmentCount; + float a = PApplet.sin(inc) * + (PApplet.sqrt(4 + 3 * PApplet.sq(PApplet.tan(inc / 2))) - 1) / 3; + + float sinPhi1 = PApplet.sin(phi1), cosPhi1 = PApplet.cos(phi1); + + float p1x = x1; + float p1y = y1; + float relq1x = a * (-rx * cosPhi * sinPhi1 - ry * sinPhi * cosPhi1); + float relq1y = a * (-rx * sinPhi * sinPhi1 + ry * cosPhi * cosPhi1); + + for (int i = 0; i < segmentCount; i++) { + float eta = phi1 + (i + 1) * inc; + float sinEta = PApplet.sin(eta), cosEta = PApplet.cos(eta); + + float p2x = cx + rx * cosPhi * cosEta - ry * sinPhi * sinEta; + float p2y = cy + rx * sinPhi * cosEta + ry * cosPhi * sinEta; + float relq2x = a * (-rx * cosPhi * sinEta - ry * sinPhi * cosEta); + float relq2y = a * (-rx * sinPhi * sinEta + ry * cosPhi * cosEta); + + if (i == segmentCount - 1) { p2x = x2; p2y = y2; } + + parsePathCode(BEZIER_VERTEX); + parsePathVertex(p1x + relq1x, p1y + relq1y); + parsePathVertex(p2x - relq2x, p2y - relq2y); + parsePathVertex(p2x, p2y); + + p1x = p2x; relq1x = relq2x; + p1y = p2y; relq1y = relq2y; + } + } + + + /** + * Parse the specified SVG matrix into a PMatrix2D. Note that PMatrix2D + * is rotated relative to the SVG definition, so parameters are rearranged + * here. More about the transformation matrices in + * this section + * of the SVG documentation. + * @param matrixStr text of the matrix param. + * @return a good old-fashioned PMatrix2D + */ + static protected PMatrix2D parseTransform(String matrixStr) { + matrixStr = matrixStr.trim(); + PMatrix2D outgoing = null; + int start = 0; + int stop = -1; + while ((stop = matrixStr.indexOf(')', start)) != -1) { + PMatrix2D m = parseSingleTransform(matrixStr.substring(start, stop+1)); + if (outgoing == null) { + outgoing = m; + } else { + outgoing.apply(m); + } + start = stop + 1; + } + return outgoing; + } + + + static protected PMatrix2D parseSingleTransform(String matrixStr) { + //String[] pieces = PApplet.match(matrixStr, "^\\s*(\\w+)\\((.*)\\)\\s*$"); + String[] pieces = PApplet.match(matrixStr, "[,\\s]*(\\w+)\\((.*)\\)"); + if (pieces == null) { + System.err.println("Could not parse transform " + matrixStr); + return null; + } + float[] m = PApplet.parseFloat(PApplet.splitTokens(pieces[2], ", ")); + if (pieces[1].equals("matrix")) { + return new PMatrix2D(m[0], m[2], m[4], m[1], m[3], m[5]); + + } else if (pieces[1].equals("translate")) { + float tx = m[0]; + float ty = (m.length == 2) ? m[1] : m[0]; + return new PMatrix2D(1, 0, tx, 0, 1, ty); + + } else if (pieces[1].equals("scale")) { + float sx = m[0]; + float sy = (m.length == 2) ? m[1] : m[0]; + return new PMatrix2D(sx, 0, 0, 0, sy, 0); + + } else if (pieces[1].equals("rotate")) { + float angle = m[0]; + + if (m.length == 1) { + float c = PApplet.cos(angle); + float s = PApplet.sin(angle); + // SVG version is cos(a) sin(a) -sin(a) cos(a) 0 0 + return new PMatrix2D(c, -s, 0, s, c, 0); + + } else if (m.length == 3) { + PMatrix2D mat = new PMatrix2D(0, 1, m[1], 1, 0, m[2]); + mat.rotate(m[0]); + mat.translate(-m[1], -m[2]); + return mat; + } + + } else if (pieces[1].equals("skewX")) { + return new PMatrix2D(1, 0, 1, PApplet.tan(m[0]), 0, 0); + + } else if (pieces[1].equals("skewY")) { + return new PMatrix2D(1, 0, 1, 0, PApplet.tan(m[0]), 0); + } + return null; + } + + + protected void parseColors(XML properties) { + if (properties.hasAttribute("opacity")) { + String opacityText = properties.getString("opacity"); + setOpacity(opacityText); + } + + if (properties.hasAttribute("stroke")) { + String strokeText = properties.getString("stroke"); + setColor(strokeText, false); + } + + if (properties.hasAttribute("stroke-opacity")) { + String strokeOpacityText = properties.getString("stroke-opacity"); + setStrokeOpacity(strokeOpacityText); + } + + if (properties.hasAttribute("stroke-width")) { + // if NaN (i.e. if it's 'inherit') then default back to the inherit setting + String lineweight = properties.getString("stroke-width"); + setStrokeWeight(lineweight); + } + + if (properties.hasAttribute("stroke-linejoin")) { + String linejoin = properties.getString("stroke-linejoin"); + setStrokeJoin(linejoin); + } + + if (properties.hasAttribute("stroke-linecap")) { + String linecap = properties.getString("stroke-linecap"); + setStrokeCap(linecap); + } + + // fill defaults to black (though stroke defaults to "none") + // http://www.w3.org/TR/SVG/painting.html#FillProperties + if (properties.hasAttribute("fill")) { + String fillText = properties.getString("fill"); + setColor(fillText, true); + } + + if (properties.hasAttribute("fill-opacity")) { + String fillOpacityText = properties.getString("fill-opacity"); + setFillOpacity(fillOpacityText); + } + + if (properties.hasAttribute("style")) { + String styleText = properties.getString("style"); + String[] styleTokens = PApplet.splitTokens(styleText, ";"); + + //PApplet.println(styleTokens); + for (int i = 0; i < styleTokens.length; i++) { + String[] tokens = PApplet.splitTokens(styleTokens[i], ":"); + //PApplet.println(tokens); + + tokens[0] = PApplet.trim(tokens[0]); + + if (tokens[0].equals("fill")) { + setColor(tokens[1], true); + + } else if(tokens[0].equals("fill-opacity")) { + setFillOpacity(tokens[1]); + + } else if(tokens[0].equals("stroke")) { + setColor(tokens[1], false); + + } else if(tokens[0].equals("stroke-width")) { + setStrokeWeight(tokens[1]); + + } else if(tokens[0].equals("stroke-linecap")) { + setStrokeCap(tokens[1]); + + } else if(tokens[0].equals("stroke-linejoin")) { + setStrokeJoin(tokens[1]); + + } else if(tokens[0].equals("stroke-opacity")) { + setStrokeOpacity(tokens[1]); + + } else if(tokens[0].equals("opacity")) { + setOpacity(tokens[1]); + + } else { + // Other attributes are not yet implemented + } + } + } + } + + + void setOpacity(String opacityText) { + opacity = PApplet.parseFloat(opacityText); + strokeColor = ((int) (opacity * 255)) << 24 | strokeColor & 0xFFFFFF; + fillColor = ((int) (opacity * 255)) << 24 | fillColor & 0xFFFFFF; + } + + + void setStrokeWeight(String lineweight) { + strokeWeight = parseUnitSize(lineweight, svgSizeXY); + } + + + void setStrokeOpacity(String opacityText) { + strokeOpacity = PApplet.parseFloat(opacityText); + strokeColor = ((int) (strokeOpacity * 255)) << 24 | strokeColor & 0xFFFFFF; + } + + + void setStrokeJoin(String linejoin) { + if (linejoin.equals("inherit")) { + // do nothing, will inherit automatically + + } else if (linejoin.equals("miter")) { + strokeJoin = PConstants.MITER; + + } else if (linejoin.equals("round")) { + strokeJoin = PConstants.ROUND; + + } else if (linejoin.equals("bevel")) { + strokeJoin = PConstants.BEVEL; + } + } + + + void setStrokeCap(String linecap) { + if (linecap.equals("inherit")) { + // do nothing, will inherit automatically + + } else if (linecap.equals("butt")) { + strokeCap = PConstants.SQUARE; + + } else if (linecap.equals("round")) { + strokeCap = PConstants.ROUND; + + } else if (linecap.equals("square")) { + strokeCap = PConstants.PROJECT; + } + } + + + void setFillOpacity(String opacityText) { + fillOpacity = PApplet.parseFloat(opacityText); + fillColor = ((int) (fillOpacity * 255)) << 24 | fillColor & 0xFFFFFF; + } + + + void setColor(String colorText, boolean isFill) { + colorText = colorText.trim(); + int opacityMask = fillColor & 0xFF000000; + boolean visible = true; + int color = 0; + String name = ""; +// String lColorText = colorText.toLowerCase(); + Gradient gradient = null; +// Object paint = null; + if (colorText.equals("none")) { + visible = false; + } else if (colorText.startsWith("url(#")) { + name = colorText.substring(5, colorText.length() - 1); + Object object = findChild(name); + if (object instanceof Gradient) { + gradient = (Gradient) object; + // in 3.0a11, do this on first draw inside PShapeJava2D +// paint = calcGradientPaint(gradient); //, opacity); + } else { +// visible = false; + System.err.println("url " + name + " refers to unexpected data: " + object); + } + } else { + // Prints errors itself. + color = opacityMask | parseSimpleColor(colorText); + } + if (isFill) { + fill = visible; + fillColor = color; + fillName = name; + fillGradient = gradient; +// fillGradientPaint = paint; + } else { + stroke = visible; + strokeColor = color; + strokeName = name; + strokeGradient = gradient; +// strokeGradientPaint = paint; + } + } + + + /** + * Parses the "color" datatype only, and prints an error if it is not of this form. + * http://www.w3.org/TR/SVG/types.html#DataTypeColor + * @return 0xRRGGBB (no alpha). Zero on error. + */ + static protected int parseSimpleColor(String colorText) { + colorText = colorText.toLowerCase().trim(); + //if (colorNames.containsKey(colorText)) { + if (colorNames.hasKey(colorText)) { + return colorNames.get(colorText); + } else if (colorText.startsWith("#")) { + if (colorText.length() == 4) { + // Short form: #ABC, transform to long form #AABBCC + colorText = colorText.replaceAll("^#(.)(.)(.)$", "#$1$1$2$2$3$3"); + } + return (Integer.parseInt(colorText.substring(1), 16)) & 0xFFFFFF; + //System.out.println("hex for fill is " + PApplet.hex(fillColor)); + } else if (colorText.startsWith("rgb")) { + return parseRGB(colorText); + } else { + System.err.println("Cannot parse \"" + colorText + "\"."); + return 0; + } + } + + + /** + * Deliberately conforms to the HTML 4.01 color spec + en-gb grey, rather + * than the (unlikely to be useful) entire 147-color system used in SVG. + */ + static protected IntDict colorNames = new IntDict(new Object[][] { + { "aqua", 0x00ffff }, + { "black", 0x000000 }, + { "blue", 0x0000ff }, + { "fuchsia", 0xff00ff }, + { "gray", 0x808080 }, + { "grey", 0x808080 }, + { "green", 0x008000 }, + { "lime", 0x00ff00 }, + { "maroon", 0x800000 }, + { "navy", 0x000080 }, + { "olive", 0x808000 }, + { "purple", 0x800080 }, + { "red", 0xff0000 }, + { "silver", 0xc0c0c0 }, + { "teal", 0x008080 }, + { "white", 0xffffff }, + { "yellow", 0xffff00 } + }); + + /* + static protected Map colorNames; + static { + colorNames = new HashMap(); + colorNames.put("aqua", 0x00ffff); + colorNames.put("black", 0x000000); + colorNames.put("blue", 0x0000ff); + colorNames.put("fuchsia", 0xff00ff); + colorNames.put("gray", 0x808080); + colorNames.put("grey", 0x808080); + colorNames.put("green", 0x008000); + colorNames.put("lime", 0x00ff00); + colorNames.put("maroon", 0x800000); + colorNames.put("navy", 0x000080); + colorNames.put("olive", 0x808000); + colorNames.put("purple", 0x800080); + colorNames.put("red", 0xff0000); + colorNames.put("silver", 0xc0c0c0); + colorNames.put("teal", 0x008080); + colorNames.put("white", 0xffffff); + colorNames.put("yellow", 0xffff00); + } + */ + + static protected int parseRGB(String what) { + int leftParen = what.indexOf('(') + 1; + int rightParen = what.indexOf(')'); + String sub = what.substring(leftParen, rightParen); + String[] values = PApplet.splitTokens(sub, ", "); + int rgbValue = 0; + if (values.length == 3) { + // Color spec allows for rgb values to be percentages. + for (int i = 0; i < 3; i++) { + rgbValue <<= 8; + if (values[i].endsWith("%")) { + rgbValue |= (int)(PApplet.constrain(255*parseFloatOrPercent(values[i]), 0, 255)); + } else { + rgbValue |= PApplet.constrain(PApplet.parseInt(values[i]), 0, 255); + } + } + } else System.err.println("Could not read color \"" + what + "\"."); + + return rgbValue; + } + + + //static protected Map parseStyleAttributes(String style) { + static protected StringDict parseStyleAttributes(String style) { + //Map table = new HashMap(); + StringDict table = new StringDict(); +// if (style == null) return table; + if (style != null) { + String[] pieces = style.split(";"); + for (int i = 0; i < pieces.length; i++) { + String[] parts = pieces[i].split(":"); + //table.put(parts[0], parts[1]); + table.set(parts[0], parts[1]); + } + } + return table; + } + + + /** + * Used in place of element.getFloatAttribute(a) because we can + * have a unit suffix (length or coordinate). + * @param element what to parse + * @param attribute name of the attribute to get + * @param relativeTo (float) Used for %. When relative to viewbox, should + * be svgWidth for horizontal dimentions, svgHeight for vertical, and + * svgXYSize for anything else. + * @return unit-parsed version of the data + */ + static protected float getFloatWithUnit(XML element, String attribute, float relativeTo) { + String val = element.getString(attribute); + return (val == null) ? 0 : parseUnitSize(val, relativeTo); + } + + + /** + * Parse a size that may have a suffix for its units. + * This assumes 90dpi, which implies, as given in the + * units spec: + *
    + *
  • "1pt" equals "1.25px" (and therefore 1.25 user units) + *
  • "1pc" equals "15px" (and therefore 15 user units) + *
  • "1mm" would be "3.543307px" (3.543307 user units) + *
  • "1cm" equals "35.43307px" (and therefore 35.43307 user units) + *
  • "1in" equals "90px" (and therefore 90 user units) + *
+ * @param relativeTo (float) Used for %. When relative to viewbox, should + * be svgWidth for horizontal dimentions, svgHeight for vertical, and + * svgXYSize for anything else. + */ + static protected float parseUnitSize(String text, float relativeTo) { + int len = text.length() - 2; + + if (text.endsWith("pt")) { + return PApplet.parseFloat(text.substring(0, len)) * 1.25f; + } else if (text.endsWith("pc")) { + return PApplet.parseFloat(text.substring(0, len)) * 15; + } else if (text.endsWith("mm")) { + return PApplet.parseFloat(text.substring(0, len)) * 3.543307f; + } else if (text.endsWith("cm")) { + return PApplet.parseFloat(text.substring(0, len)) * 35.43307f; + } else if (text.endsWith("in")) { + return PApplet.parseFloat(text.substring(0, len)) * 90; + } else if (text.endsWith("px")) { + return PApplet.parseFloat(text.substring(0, len)); + } else if (text.endsWith("%")) { + return relativeTo * parseFloatOrPercent(text); + } else { + return PApplet.parseFloat(text); + } + } + + + static protected float parseFloatOrPercent(String text) { + text = text.trim(); + if (text.endsWith("%")) { + return Float.parseFloat(text.substring(0, text.length() - 1)) / 100.0f; + } else { + return Float.parseFloat(text); + } + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + static public class Gradient extends PShapeSVG { + AffineTransform transform; + + public float[] offset; + public int[] color; + public int count; + + public Gradient(PShapeSVG parent, XML properties) { + super(parent, properties, true); + + XML elements[] = properties.getChildren(); + offset = new float[elements.length]; + color = new int[elements.length]; + + // + for (int i = 0; i < elements.length; i++) { + XML elem = elements[i]; + String name = elem.getName(); + if (name.equals("stop")) { + String offsetAttr = elem.getString("offset"); + offset[count] = parseFloatOrPercent(offsetAttr); + + String style = elem.getString("style"); + //Map styles = parseStyleAttributes(style); + StringDict styles = parseStyleAttributes(style); + + String colorStr = styles.get("stop-color"); + if (colorStr == null) { + colorStr = elem.getString("stop-color"); + if (colorStr == null) colorStr = "#000000"; + } + String opacityStr = styles.get("stop-opacity"); + if (opacityStr == null) { + opacityStr = elem.getString("stop-opacity"); + if (opacityStr == null) opacityStr = "1"; + } + int tupacity = PApplet.constrain( + (int)(PApplet.parseFloat(opacityStr) * 255), 0, 255); + color[count] = (tupacity << 24) | parseSimpleColor(colorStr); + count++; + } + } + offset = PApplet.subset(offset, 0, count); + color = PApplet.subset(color, 0, count); + } + } + + + public class LinearGradient extends Gradient { + public float x1, y1, x2, y2; + + public LinearGradient(PShapeSVG parent, XML properties) { + super(parent, properties); + + this.x1 = getFloatWithUnit(properties, "x1", svgWidth); + this.y1 = getFloatWithUnit(properties, "y1", svgHeight); + this.x2 = getFloatWithUnit(properties, "x2", svgWidth); + this.y2 = getFloatWithUnit(properties, "y2", svgHeight); + + String transformStr = + properties.getString("gradientTransform"); + + if (transformStr != null) { + float t[] = parseTransform(transformStr).get(null); + this.transform = new AffineTransform(t[0], t[3], t[1], t[4], t[2], t[5]); + + Point2D t1 = transform.transform(new Point2D.Float(x1, y1), null); + Point2D t2 = transform.transform(new Point2D.Float(x2, y2), null); + + this.x1 = (float) t1.getX(); + this.y1 = (float) t1.getY(); + this.x2 = (float) t2.getX(); + this.y2 = (float) t2.getY(); + } + } + } + + + public class RadialGradient extends Gradient { + public float cx, cy, r; + + public RadialGradient(PShapeSVG parent, XML properties) { + super(parent, properties); + + this.cx = getFloatWithUnit(properties, "cx", svgWidth); + this.cy = getFloatWithUnit(properties, "cy", svgHeight); + this.r = getFloatWithUnit(properties, "r", svgSizeXY); + + String transformStr = + properties.getString("gradientTransform"); + + if (transformStr != null) { + float t[] = parseTransform(transformStr).get(null); + this.transform = new AffineTransform(t[0], t[3], t[1], t[4], t[2], t[5]); + + Point2D t1 = transform.transform(new Point2D.Float(cx, cy), null); + Point2D t2 = transform.transform(new Point2D.Float(cx + r, cy), null); + + this.cx = (float) t1.getX(); + this.cy = (float) t1.getY(); + this.r = (float) (t2.getX() - t1.getX()); + } + } + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + public static class Font extends PShapeSVG { + public FontFace face; + + public Map namedGlyphs; + public Map unicodeGlyphs; + + public int glyphCount; + public FontGlyph[] glyphs; + public FontGlyph missingGlyph; + + int horizAdvX; + + + public Font(PShapeSVG parent, XML properties) { + super(parent, properties, false); +// handle(parent, properties); + + XML[] elements = properties.getChildren(); + + horizAdvX = properties.getInt("horiz-adv-x", 0); + + namedGlyphs = new HashMap(); + unicodeGlyphs = new HashMap(); + glyphCount = 0; + glyphs = new FontGlyph[elements.length]; + + for (int i = 0; i < elements.length; i++) { + String name = elements[i].getName(); + XML elem = elements[i]; + if (name == null) { + // skip it + } else if (name.equals("glyph")) { + FontGlyph fg = new FontGlyph(this, elem, this); + if (fg.isLegit()) { + if (fg.name != null) { + namedGlyphs.put(fg.name, fg); + } + if (fg.unicode != 0) { + unicodeGlyphs.put(Character.valueOf(fg.unicode), fg); + } + } + glyphs[glyphCount++] = fg; + + } else if (name.equals("missing-glyph")) { +// System.out.println("got missing glyph inside "); + missingGlyph = new FontGlyph(this, elem, this); + } else if (name.equals("font-face")) { + face = new FontFace(this, elem); + } else { + System.err.println("Ignoring " + name + " inside "); + } + } + } + + + protected void drawShape() { + // does nothing for fonts + } + + + public void drawString(PGraphics g, String str, float x, float y, float size) { + // 1) scale by the 1.0/unitsPerEm + // 2) scale up by a font size + g.pushMatrix(); + float s = size / face.unitsPerEm; + //System.out.println("scale is " + s); + // swap y coord at the same time, since fonts have y=0 at baseline + g.translate(x, y); + g.scale(s, -s); + char[] c = str.toCharArray(); + for (int i = 0; i < c.length; i++) { + // call draw on each char (pulling it w/ the unicode table) + FontGlyph fg = unicodeGlyphs.get(Character.valueOf(c[i])); + if (fg != null) { + fg.draw(g); + // add horizAdvX/unitsPerEm to the x coordinate along the way + g.translate(fg.horizAdvX, 0); + } else { + System.err.println("'" + c[i] + "' not available."); + } + } + g.popMatrix(); + } + + + public void drawChar(PGraphics g, char c, float x, float y, float size) { + g.pushMatrix(); + float s = size / face.unitsPerEm; + g.translate(x, y); + g.scale(s, -s); + FontGlyph fg = unicodeGlyphs.get(Character.valueOf(c)); + if (fg != null) g.shape(fg); + g.popMatrix(); + } + + + public float textWidth(String str, float size) { + float w = 0; + char[] c = str.toCharArray(); + for (int i = 0; i < c.length; i++) { + // call draw on each char (pulling it w/ the unicode table) + FontGlyph fg = unicodeGlyphs.get(Character.valueOf(c[i])); + if (fg != null) { + w += (float) fg.horizAdvX / face.unitsPerEm; + } + } + return w * size; + } + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + static class FontFace extends PShapeSVG { + int horizOriginX; // dflt 0 + int horizOriginY; // dflt 0 +// int horizAdvX; // no dflt? + int vertOriginX; // dflt horizAdvX/2 + int vertOriginY; // dflt ascent + int vertAdvY; // dflt 1em (unitsPerEm value) + + String fontFamily; + int fontWeight; // can also be normal or bold (also comma separated) + String fontStretch; + int unitsPerEm; // dflt 1000 + int[] panose1; // dflt "0 0 0 0 0 0 0 0 0 0" + int ascent; + int descent; + int[] bbox; // spec says comma separated, tho not w/ forge + int underlineThickness; + int underlinePosition; + //String unicodeRange; // gonna ignore for now + + + public FontFace(PShapeSVG parent, XML properties) { + super(parent, properties, true); + + unitsPerEm = properties.getInt("units-per-em", 1000); + } + + + protected void drawShape() { + // nothing to draw in the font face attribute + } + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + public static class FontGlyph extends PShapeSVG { // extends Path + public String name; + char unicode; + int horizAdvX; + + public FontGlyph(PShapeSVG parent, XML properties, Font font) { + super(parent, properties, true); + super.parsePath(); // ?? + + name = properties.getString("glyph-name"); + String u = properties.getString("unicode"); + unicode = 0; + if (u != null) { + if (u.length() == 1) { + unicode = u.charAt(0); + //System.out.println("unicode for " + name + " is " + u); + } else { + System.err.println("unicode for " + name + + " is more than one char: " + u); + } + } + if (properties.hasAttribute("horiz-adv-x")) { + horizAdvX = properties.getInt("horiz-adv-x"); + } else { + horizAdvX = font.horizAdvX; + } + } + + + protected boolean isLegit() { // TODO need a better way to handle this... + return vertexCount != 0; + } + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + /** + * Get a particular element based on its SVG ID. When editing SVG by hand, + * this is the id="" tag on any SVG element. When editing from Illustrator, + * these IDs can be edited by expanding the layers palette. The names used + * in the layers palette, both for the layers or the shapes and groups + * beneath them can be used here. + *
+   * // This code grabs "Layer 3" and the shapes beneath it.
+   * PShape layer3 = svg.getChild("Layer 3");
+   * 
+ */ + @Override + public PShape getChild(String name) { + PShape found = super.getChild(name); + if (found == null) { + // Otherwise try with underscores instead of spaces + // (this is how Illustrator handles spaces in the layer names). + found = super.getChild(name.replace(' ', '_')); + } + // Set bounding box based on the parent bounding box + if (found != null) { +// found.x = this.x; +// found.y = this.y; + found.width = this.width; + found.height = this.height; + } + return found; + } + + + /** + * Prints out the SVG document. Useful for parsing. + */ + public void print() { + PApplet.println(element.toString()); + } +} diff --git a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/ShapeLoader.java b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/ShapeLoader.java new file mode 100644 index 00000000..a9fb7d10 --- /dev/null +++ b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/ShapeLoader.java @@ -0,0 +1,88 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. + * + * + * Parts of the API of this package, as well as some of the code, is derived from + * the Processing project (http://processing.org) + * + * Copyright (c) 2004-09 Ben Fry and Casey Reas + * Copyright (c) 2001-04 Massachusetts Institute of Technology + * + */ +package net.neilcsmith.praxis.video.pgl.code; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import net.neilcsmith.praxis.code.ResourceProperty; +import net.neilcsmith.praxis.video.pgl.code.userapi.PShape; +import processing.data.XML; + +/** + * + * @author Neil C Smith + */ +class ShapeLoader extends ResourceProperty.Loader { + + private final static ShapeLoader INSTANCE = new ShapeLoader(); + + private ShapeLoader() { + super(PShape.class); + } + + @Override + public PShape load(URI uri) throws IOException { + String path = uri.getPath(); + if (path.endsWith(".svg") || path.endsWith(".svgz")) { + return loadSVG(uri); + } else { + throw new IOException("Unknown file type"); + } + } + + private PShape loadSVG(URI uri) throws IOException { + try { + InputStream is = uri.toURL().openStream(); + XML xml = new XML(is); + PShapeSVG svg = new PShapeSVG(xml); + return new PShapeImpl(svg); + } catch (Exception ex) { + if (ex instanceof IOException) { + throw (IOException) ex; + } else { + throw new IOException(ex); + } + } + } + + static ShapeLoader getDefault() { + return INSTANCE; + } + + private static class PShapeImpl extends PShape { + + private PShapeImpl(processing.core.PShape shape) { + super(shape); + } + + } + +} diff --git a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/userapi/Constants.java b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/userapi/Constants.java index f0c25c63..64ad95a2 100644 --- a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/userapi/Constants.java +++ b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/userapi/Constants.java @@ -69,6 +69,10 @@ private Constants() { public final static ShapeEndMode OPEN = ShapeEndMode.Open; public final static ShapeEndMode CLOSE = ShapeEndMode.Close; + public final static ShapeType GROUP = ShapeType.Group; + public final static ShapeType PATH = ShapeType.Path; + public final static ShapeType GEOMETRY = ShapeType.Geometry; + public final static String DEFAULT_VERTEX_SHADER = ShaderConstants.DEFAULT_VERTEX_SHADER; public final static String DEFAULT_FRAGMENT_SHADER = @@ -181,5 +185,20 @@ public int unwrap() { } } + public static enum ShapeType { + Group(PConstants.GROUP), + Path(processing.core.PShape.PATH), + Geometry(processing.core.PShape.GEOMETRY); + + private final int value; + + private ShapeType(int value) { + this.value = value; + } + + public int unwrap() { + return value; + } + } } diff --git a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/userapi/PFont.java b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/userapi/PFont.java index 844a4b40..cd1f6f1e 100644 --- a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/userapi/PFont.java +++ b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/userapi/PFont.java @@ -1,21 +1,39 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. */ package net.neilcsmith.praxis.video.pgl.code.userapi; +import net.neilcsmith.praxis.video.pgl.PGLContext; + /** * * @author Neil C Smith */ public abstract class PFont { - private processing.core.PFont font; - - + protected processing.core.PFont unwrap(PGLContext context) { + return unwrap(context, 12); + } - protected abstract processing.core.PFont unwrap(); + protected abstract processing.core.PFont unwrap(PGLContext context, double size); } diff --git a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/userapi/PGraphics.java b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/userapi/PGraphics.java index 01878673..dea45051 100644 --- a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/userapi/PGraphics.java +++ b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/userapi/PGraphics.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Neil C Smith. + * Copyright 2017 Neil C Smith. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as @@ -88,9 +88,14 @@ public void endShape(Constants.ShapeEndMode mode) { g.endShape(mode.unwrap()); } -// public PShape createShape() { -// return g.createShape(); -// } + public PShape createShape() { + return new PShape(g.createShape(), context); + } + + public PShape createShape(Constants.ShapeType type) { + return new PShape(g.createShape(type.unwrap()), context); + } + // // public PShape createShape(PShape source) { // return g.createShape(source); @@ -286,17 +291,17 @@ public void image(PImage img, double a, double b, double c, double d, int u1, in // g.shapeMode(mode); // } // -// public void shape(PShape shape) { -// g.shape(shape); -// } -// -// public void shape(PShape shape, double x, double y) { -// g.shape(shape, (float)x, (float)y); -// } -// -// public void shape(PShape shape, double a, double b, double c, double d) { -// g.shape(shape, (float)a, (float)b, (float)c, (float)d); -// } + public void shape(PShape shape) { + g.shape(shape.unwrap(context)); + } + + public void shape(PShape shape, double x, double y) { + g.shape(shape.unwrap(context), (float)x, (float)y); + } + + public void shape(PShape shape, double a, double b, double c, double d) { + g.shape(shape.unwrap(context), (float)a, (float)b, (float)c, (float)d); + } // public void textAlign(int alignX) { // g.textAlign(alignX); @@ -315,11 +320,11 @@ public double textDescent() { } public void textFont(PFont font) { - g.textFont(font.unwrap()); + g.textFont(font.unwrap(context)); } public void textFont(PFont font, double size) { - g.textFont(font.unwrap(), (float)size); + g.textFont(font.unwrap(context, (float) size)); } public void textLeading(double leading) { diff --git a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/userapi/PShape.java b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/userapi/PShape.java index 286054fb..7a925ead 100644 --- a/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/userapi/PShape.java +++ b/praxis.video.pgl.code/src/net/neilcsmith/praxis/video/pgl/code/userapi/PShape.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Neil C Smith. + * Copyright 2017 Neil C Smith. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as @@ -30,14 +30,809 @@ package net.neilcsmith.praxis.video.pgl.code.userapi; +import net.neilcsmith.praxis.code.userapi.PVector; +import net.neilcsmith.praxis.video.pgl.PGLContext; + /** * * @author Neil C Smith */ -/*public*/ class PShape { +public class PShape { + + private final processing.core.PShape shape; + private final PGLContext context; + + protected PShape(processing.core.PShape shape) { + this(shape, null); + } + + PShape(processing.core.PShape shape, PGLContext context) { + this.shape = shape; + this.context = context; + } + + processing.core.PShape unwrap(PGLContext context) { + if (this.context != null && this.context == context) { + return shape; + } else { + return context.asPGLShape(shape); + } + } - private processing.core.PShape shape; +// public PShape /* void */ setFamily(int family) { +// shape.setFamily(family); +// } +// +// public PShape /* void */ setKind(int kind) { +// shape.setKind(kind); +// } +// +// public PShape /* void */ setName(String name) { +// shape.setName(name); +// } +// +// public String getName() { +// return shape.getName(); +// } +// +// public boolean isVisible() { +// return shape.isVisible(); +// } +// +// public PShape /* void */ setVisible(boolean visible) { +// shape.setVisible(visible); +// } + + public PShape /* void */ disableStyle() { + shape.disableStyle(); + return this; + } + + public PShape /* void */ enableStyle() { + shape.enableStyle(); + return this; + } + + public double getWidth() { + return shape.getWidth(); + } + + public double getHeight() { + return shape.getHeight(); + } + + public double getDepth() { + return shape.getDepth(); + } + + public boolean is2D() { + return shape.is2D(); + } + + public boolean is3D() { + return shape.is3D(); + } + + public void set3D(boolean val) { + shape.set3D(val); + } + + public PShape /* void */ textureMode(Constants.TextureMode mode) { + shape.textureMode(mode.unwrap()); + return this; + } + + public PShape /* void */ texture(PImage tex) { + shape.texture(tex.unwrap(context)); + return this; + } + + public PShape /* void */ noTexture() { + shape.noTexture(); + return this; + } + + public PShape /* void */ beginContour() { + shape.beginContour(); + return this; + } + + public PShape /* void */ endContour() { + shape.endContour(); + return this; + } + + public PShape /* void */ vertex(double x, double y) { + shape.vertex((float)x, (float)y); + return this; + } + + public PShape /* void */ vertex(double x, double y, double u, double v) { + shape.vertex((float)x, (float)y, (float)u, (float)v); + return this; + } + + public PShape /* void */ vertex(double x, double y, double z) { + shape.vertex((float)x, (float)y, (float)z); + return this; + } + + public PShape /* void */ vertex(double x, double y, double z, double u, double v) { + shape.vertex((float)x, (float)y, (float)z, (float)u, (float)v); + return this; + } + + public PShape /* void */ normal(double nx, double ny, double nz) { + shape.normal((float)nx, (float)ny, (float)nz); + return this; + } + + public PShape /* void */ attribPosition(String name, double x, double y, double z) { + shape.attribPosition(name, (float)x, (float)y, (float)z); + return this; + } + + public PShape /* void */ attribNormal(String name, double nx, double ny, double nz) { + shape.attribNormal(name, (float)nx, (float)ny, (float)nz); + return this; + } + +// public PShape /* void */ attribColor(String name, int color) { +// shape.attribColor(name, color); +// } + + public PShape /* void */ attrib(String name, double... values) { + shape.attrib(name, doublesToFloats(values)); + return this; + } + + public PShape /* void */ attrib(String name, int... values) { + shape.attrib(name, values); + return this; + } + + public PShape /* void */ attrib(String name, boolean... values) { + shape.attrib(name, values); + return this; + } + + public PShape /* void */ beginShape() { + shape.beginShape(); + return this; + } + + public PShape /* void */ beginShape(Constants.ShapeMode kind) { + shape.beginShape(kind.unwrap()); + return this; + } + + public void endShape() { + shape.endShape(); + } + + public void endShape(Constants.ShapeEndMode mode) { + shape.endShape(mode.unwrap()); + } + + public PShape /* void */ strokeWeight(double weight) { + shape.strokeWeight((float)weight); + return this; + } + +// public PShape /* void */ strokeJoin(int join) { +// shape.strokeJoin(join); +// } +// +// public PShape /* void */ strokeCap(int cap) { +// shape.strokeCap(cap); +// } + + public PShape /* void */ noFill() { + shape.noFill(); + return this; + } + +// public PShape /* void */ fill(int rgb) { +// shape.fill(rgb); +// } +// +// public PShape /* void */ fill(int rgb, double alpha) { +// shape.fill(rgb, alpha); +// } + + public PShape /* void */ fill(double gray) { + shape.fill((float)gray); + return this; + } + + public PShape /* void */ fill(double gray, double alpha) { + shape.fill((float)gray, (float)alpha); + return this; + } + + public PShape /* void */ fill(double x, double y, double z) { + shape.fill((float)x, (float)y, (float)z); + return this; + } + + public PShape /* void */ fill(double x, double y, double z, double a) { + shape.fill((float)x, (float)y, (float)z, (float)a); + return this; + } + + public PShape /* void */ noStroke() { + shape.noStroke(); + return this; + } + +// public PShape /* void */ stroke(int rgb) { +// shape.stroke(rgb); +// } +// +// public PShape /* void */ stroke(int rgb, double alpha) { +// shape.stroke(rgb, alpha); +// } + + public PShape /* void */ stroke(double gray) { + shape.stroke((float)gray); + return this; + } + + public PShape /* void */ stroke(double gray, double alpha) { + shape.stroke((float)gray, (float)alpha); + return this; + } + + public PShape /* void */ stroke(double x, double y, double z) { + shape.stroke((float)x, (float)y, (float)z); + return this; + } + + public PShape /* void */ stroke(double x, double y, double z, double alpha) { + shape.stroke((float)x, (float)y, (float)z, (float)alpha); + return this; + } + + public PShape /* void */ noTint() { + shape.noTint(); + return this; + } + +// public PShape /* void */ tint(int rgb) { +// shape.tint(rgb); +// } +// +// public PShape /* void */ tint(int rgb, double alpha) { +// shape.tint(rgb, (float)alpha); +// } + + public PShape /* void */ tint(double gray) { + shape.tint((float)gray); + return this; + } + + public PShape /* void */ tint(double gray, double alpha) { + shape.tint((float)gray, (float)alpha); + return this; + } + + public PShape /* void */ tint(double x, double y, double z) { + shape.tint((float)x, (float)y, (float)z); + return this; + } + + public PShape /* void */ tint(double x, double y, double z, double alpha) { + shape.tint((float)x, (float)y, (float)z, (float)alpha); + return this; + } + +// public PShape /* void */ ambient(int rgb) { +// shape.ambient(rgb); +// } + + public PShape /* void */ ambient(double gray) { + shape.ambient((float)gray); + return this; + } + + public PShape /* void */ ambient(double x, double y, double z) { + shape.ambient((float)x, (float)y, (float)z); + return this; + } + +// public PShape /* void */ specular(int rgb) { +// shape.specular(rgb); +// } + + public PShape /* void */ specular(double gray) { + shape.specular((float)gray); + return this; + } + + public PShape /* void */ specular(double x, double y, double z) { + shape.specular((float)x, (float)y, (float)z); + return this; + } + +// public PShape /* void */ emissive(int rgb) { +// shape.emissive(rgb); +// } + + public PShape /* void */ emissive(double gray) { + shape.emissive((float)gray); + return this; + } + + public PShape /* void */ emissive(double x, double y, double z) { + shape.emissive((float)x, (float)y, (float)z); + return this; + } + + public PShape /* void */ shininess(double shine) { + shape.shininess((float)shine); + return this; + } + +// public PShape /* void */ bezierDetail(int detail) { +// shape.bezierDetail(detail); +// } + + public PShape /* void */ bezierVertex(double x2, double y2, double x3, double y3, double x4, double y4) { + shape.bezierVertex((float)x2, (float)y2, (float)x3, (float)y3, (float)x4, (float)y4); + return this; + } + + public PShape /* void */ bezierVertex(double x2, double y2, double z2, double x3, double y3, double z3, double x4, double y4, double z4) { + shape.bezierVertex((float)x2, (float)y2, (float)z2, (float)x3, (float)y3, (float)z3, (float)x4, (float)y4, (float)z4); + return this; + } + + public PShape /* void */ quadraticVertex(double cx, double cy, double x3, double y3) { + shape.quadraticVertex((float)cx, (float)cy, (float)x3, (float)y3); + return this; + } + + public PShape /* void */ quadraticVertex(double cx, double cy, double cz, double x3, double y3, double z3) { + shape.quadraticVertex((float)cx, (float)cy, (float)cz, (float)x3, (float)y3, (float)z3); + return this; + } + + public PShape /* void */ curveDetail(int detail) { + shape.curveDetail(detail); + return this; + } + + public PShape /* void */ curveTightness(double tightness) { + shape.curveTightness((float)tightness); + return this; + } + + public PShape /* void */ curveVertex(double x, double y) { + shape.curveVertex((float)x, (float)y); + return this; + } + + public PShape /* void */ curveVertex(double x, double y, double z) { + shape.curveVertex((float)x, (float)y, (float)z); + return this; + } +// +// public PShape /* void */ draw(PGraphics g) { +// shape.draw(g); +// } + +// public PShape getParent() { +// return shape.getParent(); +// } + + public int getChildCount() { + return shape.getChildCount(); + } + +// public processing.core.PShape[] getChildren() { +// return shape.getChildren(); +// } + + public PShape getChild(int index) { + return new PShape(shape.getChild(index), context); + } + + public PShape getChild(String target) { + return new PShape(shape.getChild(target), context); + } + +// public processing.core.PShape findChild(String target) { +// return shape.findChild(target); +// } + + public PShape /* void */ addChild(PShape who) { + shape.addChild(who.shape); + return this; + } + + public PShape /* void */ addChild(PShape who, int idx) { + shape.addChild(who.shape, idx); + return this; + } + + public PShape /* void */ removeChild(int idx) { + shape.removeChild(idx); + return this; + } + +// public PShape /* void */ addName(String nom, processing.core.PShape shape) { +// this.shape.addName(nom, shape); +// } + +// public int getChildIndex(processing.core.PShape who) { +// return shape.getChildIndex(who); +// } + + public PShape getTessellation() { + return new PShape(shape.getTessellation(), context); + } + +// public int getFamily() { +// return shape.getFamily(); +// } + +// public int getKind() { +// return shape.getKind(); +// } + +// public double[] getParams() { +// return shape.getParams(); +// } +// +// public double[] getParams(double[] target) { +// return shape.getParams(target); +// } + +// public double getParam(int index) { +// return shape.getParam(index); +// } + +// public PShape /* void */ setPath(int vcount, double[][] verts) { +// shape.setPath(vcount, verts); +// } + + public int getVertexCount() { + return shape.getVertexCount(); + } + + public PVector getVertex(int index) { + processing.core.PVector v = shape.getVertex(index); + return new PVector(v.x, v.y, v.z); + } + + public PVector getVertex(int index, PVector vec) { + processing.core.PVector v = shape.getVertex(index); + vec.x = v.x; + vec.y = v.y; + vec.z = v.z; + return vec; + } + + public double getVertexX(int index) { + return shape.getVertexX(index); + } + + public double getVertexY(int index) { + return shape.getVertexY(index); + } + + public double getVertexZ(int index) { + return shape.getVertexZ(index); + } + + public void setVertex(int index, double x, double y) { + shape.setVertex(index, (float) x, (float) y); + } + + public void setVertex(int index, double x, double y, double z) { + shape.setVertex(index, (float) x, (float) y, (float) z); + } + + public void setVertex(int index, PVector vec) { + shape.setVertex(index, new processing.core.PVector( + (float)vec.x, (float)vec.y, (float)vec.z)); + } + + public PVector getNormal(int index) { + processing.core.PVector v = shape.getNormal(index); + return new PVector(v.x, v.y, v.z); + } + + public PVector getNormal(int index, PVector vec) { + processing.core.PVector v = shape.getNormal(index); + vec.x = v.x; + vec.y = v.y; + vec.z = v.z; + return vec; + } + + public double getNormalX(int index) { + return shape.getNormalX(index); + } + + public double getNormalY(int index) { + return shape.getNormalY(index); + } + + public double getNormalZ(int index) { + return shape.getNormalZ(index); + } + + public void setNormal(int index, double nx, double ny, double nz) { + shape.setNormal(index, (float)nx, (float)ny, (float)nz); + } + + public void setAttrib(String name, int index, double... values) { + shape.setAttrib(name, index, doublesToFloats(values)); + } + + public void setAttrib(String name, int index, int... values) { + shape.setAttrib(name, index, values); + } + + public void setAttrib(String name, int index, boolean... values) { + shape.setAttrib(name, index, values); + } + + public double getTextureU(int index) { + return shape.getTextureU(index); + } + + public double getTextureV(int index) { + return shape.getTextureV(index); + } + + public void setTextureUV(int index, double u, double v) { + shape.setTextureUV(index, (float)u, (float)v); + } + + public void setTextureMode(int mode) { + shape.setTextureMode(mode); + } + + public void setTexture(PImage tex) { + shape.setTexture(tex.unwrap(context)); + } + +// public int getFill(int index) { +// return shape.getFill(index); +// } +// +// public PShape /* void */ setFill(boolean fill) { +// shape.setFill(fill); +// } +// +// public PShape /* void */ setFill(int fill) { +// shape.setFill(fill); +// } +// +// public PShape /* void */ setFill(int index, int fill) { +// shape.setFill(index, fill); +// } +// +// public int getTint(int index) { +// return shape.getTint(index); +// } +// +// public PShape /* void */ setTint(boolean tint) { +// shape.setTint(tint); +// } +// +// public PShape /* void */ setTint(int fill) { +// shape.setTint(fill); +// } +// +// public PShape /* void */ setTint(int index, int tint) { +// shape.setTint(index, tint); +// } + +// public int getStroke(int index) { +// return shape.getStroke(index); +// } +// +// public void setStroke(boolean stroke) { +// shape.setStroke(stroke); +// } +// +// public void setStroke(int stroke) { +// shape.setStroke(stroke); +// } +// +// public void setStroke(int index, int stroke) { +// shape.setStroke(index, stroke); +// } + + public double getStrokeWeight(int index) { + return shape.getStrokeWeight(index); + } + + public void setStrokeWeight(double weight) { + shape.setStrokeWeight((float)weight); + } + + public void setStrokeWeight(int index, double weight) { + shape.setStrokeWeight(index, (float)weight); + } + +// public PShape /* void */ setStrokeJoin(int join) { +// shape.setStrokeJoin(join); +// } +// +// public PShape /* void */ setStrokeCap(int cap) { +// shape.setStrokeCap(cap); +// } + +// public int getAmbient(int index) { +// return shape.getAmbient(index); +// } +// +// public PShape /* void */ setAmbient(int ambient) { +// shape.setAmbient(ambient); +// } +// +// public PShape /* void */ setAmbient(int index, int ambient) { +// shape.setAmbient(index, ambient); +// } +// +// public int getSpecular(int index) { +// return shape.getSpecular(index); +// } +// +// public PShape /* void */ setSpecular(int specular) { +// shape.setSpecular(specular); +// } +// +// public PShape /* void */ setSpecular(int index, int specular) { +// shape.setSpecular(index, specular); +// } +// +// public int getEmissive(int index) { +// return shape.getEmissive(index); +// } +// +// public PShape /* void */ setEmissive(int emissive) { +// shape.setEmissive(emissive); +// } +// +// public PShape /* void */ setEmissive(int index, int emissive) { +// shape.setEmissive(index, emissive); +// } +// +// public double getShininess(int index) { +// return shape.getShininess(index); +// } +// +// public PShape /* void */ setShininess(double shine) { +// shape.setShininess((float)shine); +// } +// +// public PShape /* void */ setShininess(int index, double shine) { +// shape.setShininess(index, (float)shine); +// } + +// public int[] getVertexCodes() { +// return shape.getVertexCodes(); +// } +// +// public int getVertexCodeCount() { +// return shape.getVertexCodeCount(); +// } +// +// public int getVertexCode(int index) { +// return shape.getVertexCode(index); +// } + + public boolean isClosed() { + return shape.isClosed(); + } + + public boolean contains(double x, double y) { + return shape.contains((float)x, (float)y); + } + + public PShape /* void */ translate(double x, double y) { + shape.translate((float)x, (float)y); + return this; + } + + public PShape /* void */ translate(double x, double y, double z) { + shape.translate((float)x, (float)y, (float)z); + return this; + } + + public PShape /* void */ rotateX(double angle) { + shape.rotateX((float)angle); + return this; + } + + public PShape /* void */ rotateY(double angle) { + shape.rotateY((float)angle); + return this; + } + + public PShape /* void */ rotateZ(double angle) { + shape.rotateZ((float)angle); + return this; + } + + public PShape /* void */ rotate(double angle) { + shape.rotate((float)angle); + return this; + } + + public PShape /* void */ rotate(double angle, double v0, double v1, double v2) { + shape.rotate((float)angle, (float)v0, (float)v1, (float)v2); + return this; + } + + public PShape /* void */ scale(double s) { + shape.scale((float)s); + return this; + } + + public PShape /* void */ scale(double x, double y) { + shape.scale((float)x, (float)y); + return this; + } + + public PShape /* void */ scale(double x, double y, double z) { + shape.scale((float)x, (float)y, (float)z); + return this; + } + + public PShape /* void */ resetMatrix() { + shape.resetMatrix(); + return this; + } +// +// public PShape /* void */ applyMatrix(PMatrix source) { +// shape.applyMatrix(source); +// } +// +// public PShape /* void */ applyMatrix(PMatrix2D source) { +// shape.applyMatrix(source); +// } +// +// public PShape /* void */ applyMatrix(double n00, double n01, double n02, double n10, double n11, double n12) { +// shape.applyMatrix(n00, n01, n02, n10, n11, n12); +// } +// +// public PShape /* void */ applyMatrix(PMatrix3D source) { +// shape.applyMatrix(source); +// } +// +// public PShape /* void */ applyMatrix(double n00, double n01, double n02, double n03, double n10, double n11, double n12, double n13, double n20, double n21, double n22, double n23, double n30, double n31, double n32, double n33) { +// shape.applyMatrix(n00, n01, n02, n03, n10, n11, n12, n13, n20, n21, n22, n23, n30, n31, n32, n33); +// } +// +// public PShape /* void */ colorMode(int mode) { +// shape.colorMode(mode); +// } +// +// public PShape /* void */ colorMode(int mode, double max) { +// shape.colorMode(mode, max); +// } +// +// public PShape /* void */ colorMode(int mode, double maxX, double maxY, double maxZ) { +// shape.colorMode(mode, maxX, maxY, maxZ); +// } +// +// public PShape /* void */ colorMode(int mode, double maxX, double maxY, double maxZ, double maxA) { +// shape.colorMode(mode, maxX, maxY, maxZ, maxA); +// } + private static float[] doublesToFloats(double[] values) { + float[] v = new float[values.length]; + for (int i=0; i + + + + + Builds, tests, and runs the project net.neilcsmith.praxis.video.pgl.spout. + + diff --git a/praxis.video.pgl.spout/manifest.mf b/praxis.video.pgl.spout/manifest.mf new file mode 100644 index 00000000..e8cec0c4 --- /dev/null +++ b/praxis.video.pgl.spout/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +AutoUpdate-Show-In-Client: true +OpenIDE-Module: net.neilcsmith.praxis.video.pgl.spout +OpenIDE-Module-Localizing-Bundle: net/neilcsmith/praxis/video/pgl/spout/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/praxis.video.pgl.spout/nbproject/build-impl.xml b/praxis.video.pgl.spout/nbproject/build-impl.xml new file mode 100644 index 00000000..f2c2b771 --- /dev/null +++ b/praxis.video.pgl.spout/nbproject/build-impl.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + You must set 'suite.dir' to point to your containing module suite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/praxis.video.pgl.spout/nbproject/genfiles.properties b/praxis.video.pgl.spout/nbproject/genfiles.properties new file mode 100644 index 00000000..1f2c506a --- /dev/null +++ b/praxis.video.pgl.spout/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=2ca985bf +build.xml.script.CRC32=aec261c3 +build.xml.stylesheet.CRC32=a56c6a5b@2.71.1 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=2ca985bf +nbproject/build-impl.xml.script.CRC32=09d5b075 +nbproject/build-impl.xml.stylesheet.CRC32=238281d1@2.71.1 diff --git a/praxis.video.pgl.spout/nbproject/project.properties b/praxis.video.pgl.spout/nbproject/project.properties new file mode 100644 index 00000000..b45e3b40 --- /dev/null +++ b/praxis.video.pgl.spout/nbproject/project.properties @@ -0,0 +1,2 @@ +javac.source=1.8 +javac.compilerargs=-Xlint -Xlint:-serial diff --git a/praxis.video.pgl.spout/nbproject/project.xml b/praxis.video.pgl.spout/nbproject/project.xml new file mode 100644 index 00000000..10acb647 --- /dev/null +++ b/praxis.video.pgl.spout/nbproject/project.xml @@ -0,0 +1,37 @@ + + + org.netbeans.modules.apisupport.project + + + net.neilcsmith.praxis.video.pgl.spout + + + + net.neilcsmith.praxis.video.pgl + + + + + + + + processing.core + + + + 3.2.3 + + + + spout + + + + 2.0.6.0 + + + + + + + diff --git a/praxis.video.pgl.spout/nbproject/suite.properties b/praxis.video.pgl.spout/nbproject/suite.properties new file mode 100644 index 00000000..364e160e --- /dev/null +++ b/praxis.video.pgl.spout/nbproject/suite.properties @@ -0,0 +1 @@ +suite.dir=${basedir}/.. diff --git a/praxis.video.pgl.spout/src/META-INF/services/net.neilcsmith.praxis.video.pgl.PGLTextureSharer b/praxis.video.pgl.spout/src/META-INF/services/net.neilcsmith.praxis.video.pgl.PGLTextureSharer new file mode 100644 index 00000000..ee6505b6 --- /dev/null +++ b/praxis.video.pgl.spout/src/META-INF/services/net.neilcsmith.praxis.video.pgl.PGLTextureSharer @@ -0,0 +1 @@ +net.neilcsmith.praxis.video.pgl.spout.SpoutTextureSharer \ No newline at end of file diff --git a/praxis.video.pgl.spout/src/net/neilcsmith/praxis/video/pgl/spout/Bundle.properties b/praxis.video.pgl.spout/src/net/neilcsmith/praxis/video/pgl/spout/Bundle.properties new file mode 100644 index 00000000..e3657811 --- /dev/null +++ b/praxis.video.pgl.spout/src/net/neilcsmith/praxis/video/pgl/spout/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=praxis.video.pgl.spout diff --git a/praxis.video.pgl.spout/src/net/neilcsmith/praxis/video/pgl/spout/SpoutReceiver.java b/praxis.video.pgl.spout/src/net/neilcsmith/praxis/video/pgl/spout/SpoutReceiver.java new file mode 100644 index 00000000..2a3c74ff --- /dev/null +++ b/praxis.video.pgl.spout/src/net/neilcsmith/praxis/video/pgl/spout/SpoutReceiver.java @@ -0,0 +1,71 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. + */ +package net.neilcsmith.praxis.video.pgl.spout; + +import java.util.Optional; +import net.neilcsmith.praxis.video.pgl.PGLContext; +import net.neilcsmith.praxis.video.pgl.PGLTextureSharer; +import processing.core.PConstants; +import processing.core.PGraphics; +import processing.core.PImage; +import spout.Spout; + +/** + * + * @author neilcsmith + */ +class SpoutReceiver implements PGLTextureSharer.Receiver { + + private final Spout client; + + private PImage cache; + + SpoutReceiver(PGLContext context, String serverID) { + client = new Spout(context.parent()); + cache = context.parent().createImage(context.parent().width, context.parent().height, PConstants.ARGB); + if (serverID != null && !serverID.isEmpty()) { + client.createReceiver(serverID); + } + } + + @Override + public boolean hasNewFrame() { + return true; + } + + @Override + public Optional acquireFrame() { + cache = client.receiveTexture(cache); + return Optional.ofNullable((PImage)cache); + } + + @Override + public void dispose() { +// client.closeReceiver(); + client.dispose(); + if (cache != null) { +// cache.dispose(); + cache = null; + } + } + +} diff --git a/praxis.video.pgl.spout/src/net/neilcsmith/praxis/video/pgl/spout/SpoutSender.java b/praxis.video.pgl.spout/src/net/neilcsmith/praxis/video/pgl/spout/SpoutSender.java new file mode 100644 index 00000000..07bc008c --- /dev/null +++ b/praxis.video.pgl.spout/src/net/neilcsmith/praxis/video/pgl/spout/SpoutSender.java @@ -0,0 +1,57 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. + */ +package net.neilcsmith.praxis.video.pgl.spout; + +import net.neilcsmith.praxis.video.pgl.PGLContext; +import net.neilcsmith.praxis.video.pgl.PGLTextureSharer; +import processing.core.PImage; +import spout.Spout; + +/** + * + * @author neilcsmith + */ +class SpoutSender implements PGLTextureSharer.Sender { + + private final Spout server; + + SpoutSender(PGLContext context, String serverID) { + server = new Spout(context.parent()); + if (serverID.isEmpty()) { + server.createSender("PraxisLIVE"); + } else { + server.createSender(serverID); + } + } + + @Override + public void sendFrame(PImage frame) { + server.sendTexture(frame); + } + + @Override + public void dispose() { +// server.closeSender(); + server.dispose(); + } + +} diff --git a/praxis.video.pgl.spout/src/net/neilcsmith/praxis/video/pgl/spout/SpoutTextureSharer.java b/praxis.video.pgl.spout/src/net/neilcsmith/praxis/video/pgl/spout/SpoutTextureSharer.java new file mode 100644 index 00000000..3fd39675 --- /dev/null +++ b/praxis.video.pgl.spout/src/net/neilcsmith/praxis/video/pgl/spout/SpoutTextureSharer.java @@ -0,0 +1,50 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. + */ +package net.neilcsmith.praxis.video.pgl.spout; + +import net.neilcsmith.praxis.video.pgl.PGLContext; +import net.neilcsmith.praxis.video.pgl.PGLTextureSharer; +import processing.core.PApplet; +import processing.core.PConstants; + +/** + * + * @author neilcsmith + */ +public class SpoutTextureSharer implements PGLTextureSharer { + + @Override + public PGLTextureSharer.Sender createSender(PGLContext context, String id) throws Exception { + return new SpoutSender(context, id); + } + + @Override + public PGLTextureSharer.Receiver createReceiver(PGLContext context, String serverID) throws Exception { + return new SpoutReceiver(context, serverID); + } + + @Override + public boolean isSupported() { + return PApplet.platform == PConstants.WINDOWS; + } + +} diff --git a/praxis.video.pgl.syphon/build.xml b/praxis.video.pgl.syphon/build.xml new file mode 100644 index 00000000..709e0481 --- /dev/null +++ b/praxis.video.pgl.syphon/build.xml @@ -0,0 +1,8 @@ + + + + + + Builds, tests, and runs the project net.neilcsmith.praxis.video.pgl.syphon. + + diff --git a/praxis.video.pgl.syphon/manifest.mf b/praxis.video.pgl.syphon/manifest.mf new file mode 100644 index 00000000..e0b981d7 --- /dev/null +++ b/praxis.video.pgl.syphon/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +AutoUpdate-Show-In-Client: true +OpenIDE-Module: net.neilcsmith.praxis.video.pgl.syphon +OpenIDE-Module-Localizing-Bundle: net/neilcsmith/praxis/video/pgl/syphon/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/praxis.video.pgl.syphon/nbproject/build-impl.xml b/praxis.video.pgl.syphon/nbproject/build-impl.xml new file mode 100644 index 00000000..dd658855 --- /dev/null +++ b/praxis.video.pgl.syphon/nbproject/build-impl.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + You must set 'suite.dir' to point to your containing module suite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/praxis.video.pgl.syphon/nbproject/genfiles.properties b/praxis.video.pgl.syphon/nbproject/genfiles.properties new file mode 100644 index 00000000..e421deaf --- /dev/null +++ b/praxis.video.pgl.syphon/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=62656345 +build.xml.script.CRC32=562dd8af +build.xml.stylesheet.CRC32=a56c6a5b@2.71.1 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=62656345 +nbproject/build-impl.xml.script.CRC32=ffbb828f +nbproject/build-impl.xml.stylesheet.CRC32=238281d1@2.71.1 diff --git a/praxis.video.pgl.syphon/nbproject/project.properties b/praxis.video.pgl.syphon/nbproject/project.properties new file mode 100644 index 00000000..234017c6 --- /dev/null +++ b/praxis.video.pgl.syphon/nbproject/project.properties @@ -0,0 +1,2 @@ +javac.source=1.8 +javac.compilerargs=-Xlint -Xlint:-serial diff --git a/praxis.video.pgl.syphon/nbproject/project.xml b/praxis.video.pgl.syphon/nbproject/project.xml new file mode 100644 index 00000000..5a44f59d --- /dev/null +++ b/praxis.video.pgl.syphon/nbproject/project.xml @@ -0,0 +1,37 @@ + + + org.netbeans.modules.apisupport.project + + + net.neilcsmith.praxis.video.pgl.syphon + + + + codeanticode.syphon + + + + 1.0 + + + + net.neilcsmith.praxis.video.pgl + + + + + + + + processing.core + + + + 3.2.3 + + + + + + + diff --git a/praxis.video.pgl.syphon/nbproject/suite.properties b/praxis.video.pgl.syphon/nbproject/suite.properties new file mode 100644 index 00000000..29d7cc9b --- /dev/null +++ b/praxis.video.pgl.syphon/nbproject/suite.properties @@ -0,0 +1 @@ +suite.dir=${basedir}/.. diff --git a/praxis.video.pgl.syphon/src/META-INF/services/net.neilcsmith.praxis.video.pgl.PGLTextureSharer b/praxis.video.pgl.syphon/src/META-INF/services/net.neilcsmith.praxis.video.pgl.PGLTextureSharer new file mode 100644 index 00000000..f37fbdd2 --- /dev/null +++ b/praxis.video.pgl.syphon/src/META-INF/services/net.neilcsmith.praxis.video.pgl.PGLTextureSharer @@ -0,0 +1 @@ +net.neilcsmith.praxis.video.pgl.syphon.SyphonTextureSharer \ No newline at end of file diff --git a/praxis.video.pgl.syphon/src/net/neilcsmith/praxis/video/pgl/syphon/Bundle.properties b/praxis.video.pgl.syphon/src/net/neilcsmith/praxis/video/pgl/syphon/Bundle.properties new file mode 100644 index 00000000..b3b3b2bc --- /dev/null +++ b/praxis.video.pgl.syphon/src/net/neilcsmith/praxis/video/pgl/syphon/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=praxis.video.pgl.syphon diff --git a/praxis.video.pgl.syphon/src/net/neilcsmith/praxis/video/pgl/syphon/SyphonReceiver.java b/praxis.video.pgl.syphon/src/net/neilcsmith/praxis/video/pgl/syphon/SyphonReceiver.java new file mode 100644 index 00000000..c2af629c --- /dev/null +++ b/praxis.video.pgl.syphon/src/net/neilcsmith/praxis/video/pgl/syphon/SyphonReceiver.java @@ -0,0 +1,73 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. + */ +package net.neilcsmith.praxis.video.pgl.syphon; + +import codeanticode.syphon.SyphonClient; +import java.util.Optional; +import net.neilcsmith.praxis.video.pgl.PGLContext; +import net.neilcsmith.praxis.video.pgl.PGLTextureSharer; +import processing.core.PGraphics; +import processing.core.PImage; + +/** + * + * @author neilcsmith + */ +class SyphonReceiver implements PGLTextureSharer.Receiver { + + private final SyphonClient client; + + private PGraphics cache; + + SyphonReceiver(PGLContext context, String serverID) { + if (serverID.isEmpty()) { + client = new SyphonClient(context.parent()); + } else { + client = new SyphonClient(context.parent(), "", serverID); + } + } + + @Override + public boolean hasNewFrame() { + return client.newFrame(); + } + + @Override + public Optional acquireFrame() { + if (client.newFrame()) { + cache = client.getGraphics(cache); + } + return Optional.ofNullable(cache); + } + + @Override + public void dispose() { + client.stop(); + if (cache != null) { + cache.dispose(); + cache = null; + } + } + + + +} diff --git a/praxis.video.pgl.syphon/src/net/neilcsmith/praxis/video/pgl/syphon/SyphonSender.java b/praxis.video.pgl.syphon/src/net/neilcsmith/praxis/video/pgl/syphon/SyphonSender.java new file mode 100644 index 00000000..ddbbee13 --- /dev/null +++ b/praxis.video.pgl.syphon/src/net/neilcsmith/praxis/video/pgl/syphon/SyphonSender.java @@ -0,0 +1,55 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. + */ +package net.neilcsmith.praxis.video.pgl.syphon; + +import codeanticode.syphon.SyphonServer; +import net.neilcsmith.praxis.video.pgl.PGLContext; +import net.neilcsmith.praxis.video.pgl.PGLTextureSharer; +import processing.core.PImage; + +/** + * + * @author neilcsmith + */ +class SyphonSender implements PGLTextureSharer.Sender { + + private final SyphonServer server; + + SyphonSender(PGLContext context, String serverID) { + if (serverID.isEmpty()) { + server = new SyphonServer(context.parent(), "PraxisLIVE"); + } else { + server = new SyphonServer(context.parent(), serverID); + } + } + + @Override + public void sendFrame(PImage frame) { + server.sendImage(frame); + } + + @Override + public void dispose() { + server.stop(); + } + +} diff --git a/praxis.video.pgl.syphon/src/net/neilcsmith/praxis/video/pgl/syphon/SyphonTextureSharer.java b/praxis.video.pgl.syphon/src/net/neilcsmith/praxis/video/pgl/syphon/SyphonTextureSharer.java new file mode 100644 index 00000000..e0cfd057 --- /dev/null +++ b/praxis.video.pgl.syphon/src/net/neilcsmith/praxis/video/pgl/syphon/SyphonTextureSharer.java @@ -0,0 +1,50 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. + */ +package net.neilcsmith.praxis.video.pgl.syphon; + +import net.neilcsmith.praxis.video.pgl.PGLContext; +import net.neilcsmith.praxis.video.pgl.PGLTextureSharer; +import processing.core.PApplet; +import processing.core.PConstants; + +/** + * + * @author neilcsmith + */ +public class SyphonTextureSharer implements PGLTextureSharer { + + @Override + public Sender createSender(PGLContext context, String id) throws Exception { + return new SyphonSender(context, id); + } + + @Override + public Receiver createReceiver(PGLContext context, String serverID) throws Exception { + return new SyphonReceiver(context, serverID); + } + + @Override + public boolean isSupported() { + return PApplet.platform == PConstants.MACOSX; + } + +} diff --git a/praxis.video.pgl/src/META-INF/services/net.neilcsmith.praxis.video.PlayerFactory$Provider b/praxis.video.pgl/src/META-INF/services/net.neilcsmith.praxis.video.PlayerFactory$Provider index ed864c96..ae5a38e0 100644 --- a/praxis.video.pgl/src/META-INF/services/net.neilcsmith.praxis.video.PlayerFactory$Provider +++ b/praxis.video.pgl/src/META-INF/services/net.neilcsmith.praxis.video.PlayerFactory$Provider @@ -1 +1,5 @@ -net.neilcsmith.praxis.video.pgl.PGLPlayerFactoryProvider \ No newline at end of file +net.neilcsmith.praxis.video.pgl.PGLPlayerFactory$Default +net.neilcsmith.praxis.video.pgl.PGLPlayerFactory$GLES2 +net.neilcsmith.praxis.video.pgl.PGLPlayerFactory$GL2 +net.neilcsmith.praxis.video.pgl.PGLPlayerFactory$GL3 +net.neilcsmith.praxis.video.pgl.PGLPlayerFactory$GL4 \ No newline at end of file diff --git a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLComponentFactoryProvider.java b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLComponentFactoryProvider.java index 1e608b0e..ae3a0eca 100644 --- a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLComponentFactoryProvider.java +++ b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLComponentFactoryProvider.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Neil C Smith. + * Copyright 2017 Neil C Smith. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as @@ -32,10 +32,10 @@ public class PGLComponentFactoryProvider implements ComponentFactoryProvider { - private static Factory instance = new Factory(); + private static final Factory INSTANCE = new Factory(); public ComponentFactory getFactory() { - return instance; + return INSTANCE; } private static class Factory extends AbstractComponentFactory { @@ -46,8 +46,9 @@ private Factory() { private void build() { - addComponent("video:opengl:filter", data(PGLFilter.GLFilter.class).deprecated()); addComponent("video:gl:filter", data(PGLFilter.class)); + addComponent("video:gl:receive", data(PGLReceiver.class)); + addComponent("video:gl:send", data(PGLSender.class)); } } diff --git a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLContext.java b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLContext.java index b875ebad..3df5b79f 100644 --- a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLContext.java +++ b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLContext.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2016 Neil C Smith. + * Copyright 2017 Neil C Smith. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as @@ -31,18 +31,25 @@ import java.util.logging.Level; import java.util.logging.Logger; import com.jogamp.opengl.GL2; +import java.awt.Font; +import java.util.LinkedHashMap; +import java.util.Map.Entry; import net.neilcsmith.praxis.video.pgl.ops.PGLOpCache; import net.neilcsmith.praxis.video.render.NativePixelData; import net.neilcsmith.praxis.video.render.PixelData; import net.neilcsmith.praxis.video.render.Surface; import net.neilcsmith.praxis.video.render.SurfaceOp; +import net.neilcsmith.praxis.video.render.utils.PixelArrayCache; import processing.core.PApplet; import processing.core.PConstants; import static processing.core.PConstants.ARGB; +import processing.core.PFont; import processing.core.PGraphics; import processing.core.PImage; +import processing.core.PShape; import processing.opengl.PGL; import processing.opengl.PGraphicsOpenGL; +import processing.opengl.PShapeOpenGL; import processing.opengl.Texture; /** @@ -54,30 +61,36 @@ public final class PGLContext { private final static Logger LOG = Logger.getLogger(PGLContext.class.getName()); private final PApplet applet; + private final PGLProfile profile; private final int width; private final int height; private final int cacheMax = 8; private final PGLOpCache opCache; + private final FontCache fontCache; private final List cache; private final List aliens; private final WeakHashMap surfaces; + private final WeakHashMap shapes; private final ReadPixelsOp readOp; private final PImage CLEAR_RGB; private final PImage CLEAR_ARGB; PGraphics current; //@TODO use primary().getCurrent? private IntBuffer scratchBuffer; - PGLContext(PApplet applet, int width, int height) { + PGLContext(PApplet applet, PGLProfile profile, int width, int height) { this.applet = applet; + this.profile = profile; this.width = width; this.height = height; cache = new ArrayList<>(cacheMax); aliens = new ArrayList<>(cacheMax); surfaces = new WeakHashMap<>(); + shapes = new WeakHashMap<>(); readOp = new ReadPixelsOp(); CLEAR_RGB = new PImage(width, height, PImage.RGB); CLEAR_ARGB = new PImage(width, height, PImage.ARGB); opCache = new PGLOpCache(this); + fontCache = new FontCache(); } public PImage asImage(Surface surface) { @@ -121,6 +134,16 @@ public PGLSurface createSurface(int width, int height, boolean alpha) { return s; } + public PFont asPFont(Font font) { + return fontCache.computeIfAbsent(font, f -> new PFont(f, true)); + } + + public PShapeOpenGL asPGLShape(PShape shape) { + return shapes.computeIfAbsent(shape, s -> { + return PShapeOpenGL.createShape(primary(), s); + }); + } + PGLOpCache getOpCache() { return opCache; } @@ -160,7 +183,34 @@ void releaseGraphics(PGLGraphics pgl) { cache.add(0, pgl); } - void writePixelsARGB(IntBuffer data, Texture tex) { + void writePixels(int[] data, boolean alpha, Texture tex) { + int size = tex.width * tex.height; + IntBuffer buffer = getScratchBuffer(size); + if (profile != PGLProfile.GLES2) { + buffer.put(data, 0, size); + buffer.rewind(); + writePixelsARGB(buffer, tex); + } else { + if (alpha) { + for (int i = 0; i < size; i++) { + int color = data[i]; + int rb = color & 0x00FF00FF; + data[i] = (color & 0xFF00FF00) | (rb << 16) | (rb >> 16); + } + } else { + for (int i = 0; i < size; i++) { + int color = data[i]; + int rb = color & 0x00FF00FF; + data[i] = 0xFF000000 | (color & 0x0000FF00) | (rb << 16) | (rb >> 16); + } + } + buffer.put(data, 0, size); + buffer.rewind(); + writePixelsRGBA(buffer, tex); + } + } + + private void writePixelsARGB(IntBuffer data, Texture tex) { PGL pgl = ((PGLGraphics) primary()).pgl; boolean enabledTex = false; if (!pgl.isEnabled(tex.glTarget)) { @@ -187,6 +237,33 @@ void writePixelsARGB(IntBuffer data, Texture tex) { tex.updateTexels(); } + private void writePixelsRGBA(IntBuffer data, Texture tex) { + PGL pgl = ((PGLGraphics) primary()).pgl; + boolean enabledTex = false; + if (!pgl.isEnabled(tex.glTarget)) { + pgl.enable(tex.glTarget); + enabledTex = true; + } + pgl.bindTexture(tex.glTarget, tex.glName); + pgl.texSubImage2D(tex.glTarget, + 0, + 0, + 0, + tex.width, + tex.height, + PGL.RGBA, + PGL.UNSIGNED_BYTE, + data); + if (tex.usingMipmaps() && PGraphicsOpenGL.autoMipmapGenSupported) { + pgl.generateMipmap(tex.glTarget); + } + pgl.bindTexture(tex.glTarget, 0); + if (enabledTex) { + pgl.disable(tex.glTarget); + } + tex.updateTexels(); + } + IntBuffer getScratchBuffer(int size) { if (scratchBuffer == null || scratchBuffer.capacity() < size) { scratchBuffer = ByteBuffer.allocateDirect(size * 4).order(ByteOrder.nativeOrder()).asIntBuffer(); @@ -242,7 +319,7 @@ private PImage asAlienImage(Surface alien) { private PGLTexture createAlienTexture(int width, int height, boolean alpha) { Texture.Parameters params = new Texture.Parameters(); params.mipmaps = false; - if (!alpha) { + if (!alpha && profile != PGLProfile.GLES2) { params.format = PConstants.RGB; } PGLTexture texture = new PGLTexture(primary(), width, height, params); @@ -280,6 +357,19 @@ private static class AlienImageReference { private PImage image; } + private static class FontCache extends LinkedHashMap { + + private FontCache() { + super(8, 0.75f, true); + } + + @Override + protected boolean removeEldestEntry(Entry eldest) { + return size() > 8; + } + + } + private class ReadPixelsOp implements SurfaceOp { private Texture texture; @@ -296,28 +386,29 @@ public void process(PixelData output, PixelData... inputs) { if (output instanceof NativePixelData && output.getScanline() == output.getWidth()) { LOG.fine("PixelData is native"); NativePixelData nOut = (NativePixelData) output; - writePixelsARGB(nOut.getNativeData().asIntBuffer(), texture); + if (profile != PGLProfile.GLES2) { + writePixelsARGB(nOut.getNativeData().asIntBuffer(), texture); + } else { + writePixels(output.getData(), output.hasAlpha(), texture); + } + } else if (output.getScanline() == output.getWidth() && output.getOffset() == 0) { + writePixels(output.getData(), output.hasAlpha(), texture); } else { int size = output.getWidth() * output.getHeight(); - LOG.fine("Size of pixel data = " + size); - IntBuffer buffer = getScratchBuffer(size); - LOG.fine("Size of buffer = " + buffer.capacity()); - if (output.getScanline() == output.getWidth()) { - buffer.put(output.getData(), output.getOffset(), size); - } else { - int i = output.getOffset(); - int w = output.getWidth(); - int h = output.getHeight(); - int sl = output.getScanline(); - int[] data = output.getData(); - for (int v = 0; v < h; v++) { - buffer.put(data, i, w); - i += sl; - } + int[] pixels = PixelArrayCache.acquire(size, false); + IntBuffer buffer = IntBuffer.wrap(pixels); + int i = output.getOffset(); + int w = output.getWidth(); + int h = output.getHeight(); + int sl = output.getScanline(); + int[] data = output.getData(); + for (int v = 0; v < h; v++) { + buffer.put(data, i, w); + i += sl; } - buffer.rewind(); - writePixelsARGB(buffer, texture); + writePixels(pixels, output.hasAlpha(), texture); + PixelArrayCache.release(pixels); } } } diff --git a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLGraphics.java b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLGraphics.java index bd197ff8..2ff6f367 100644 --- a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLGraphics.java +++ b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLGraphics.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2015 Neil C Smith. + * Copyright 2017 Neil C Smith. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as @@ -21,7 +21,6 @@ */ package net.neilcsmith.praxis.video.pgl; -import java.nio.IntBuffer; import processing.core.PImage; import processing.core.PStyle; import processing.opengl.PGL; @@ -39,6 +38,8 @@ public class PGLGraphics extends PGraphics2D { private final PGLContext context; private PGLTexture pixelTexture; private PImage pixelImage; + private boolean inText; + private boolean lastInText; PGLGraphics(PGLContext context, boolean primary, int w, int h) { this.context = context; @@ -61,11 +62,12 @@ void writePixelsARGB(int[] pixels, boolean hasAlpha) { pixelTexture.invertedY(true); pixelImage = wrapTexture(pixelTexture); } - int len = width * height; - IntBuffer buf = context.getScratchBuffer(len); - buf.put(pixels, 0, len); - buf.rewind(); - context.writePixelsARGB(buf, pixelTexture); + // int len = width * height; + // IntBuffer buf = context.getScratchBuffer(len); + // buf.put(pixels, 0, len); + // buf.rewind(); + // context.writePixelsARGB(buf, pixelTexture); + context.writePixels(pixels, hasAlpha, pixelTexture); int curBlend = blendMode; if (hasAlpha) { blendMode(REPLACE); @@ -91,111 +93,70 @@ protected void readPixelsARGB(int[] pixels) { @Override protected void blendModeImpl() { - if (blendMode != lastBlendMode) { + if (blendMode != lastBlendMode + || inText != lastInText) { flush(); } pgl.enable(PGL.BLEND); - switch (blendMode) { - case REPLACE: + if (inText) { + if (blendMode == BLEND) { if (blendEqSupported) { pgl.blendEquation(PGL.FUNC_ADD); } - pgl.blendFunc(PGL.ONE, PGL.ZERO); - break; - case BLEND: - if (blendEqSupported) { - pgl.blendEquation(PGL.FUNC_ADD); - } - pgl.blendFunc(PGL.ONE, PGL.ONE_MINUS_SRC_ALPHA); - break; - case ADD: - if (blendEqSupported) { - pgl.blendEquation(PGL.FUNC_ADD); - } - pgl.blendFunc(PGL.ONE, PGL.ONE); - break; - case SUBTRACT: - if (blendEqSupported) { - pgl.blendEquationSeparate(PGL.FUNC_REVERSE_SUBTRACT, - PGL.FUNC_ADD); - } - pgl.blendFunc(PGL.ONE, PGL.ONE); - break; - case MULTIPLY: - if (blendEqSupported) { - pgl.blendEquation(PGL.FUNC_ADD); - } - pgl.blendFunc(PGL.DST_COLOR, PGL.ONE_MINUS_SRC_ALPHA); - break; - default: - throw new IllegalArgumentException(); + pgl.blendFunc(PGL.SRC_ALPHA, PGL.ONE_MINUS_SRC_ALPHA); + } + } else { + + switch (blendMode) { + case REPLACE: + if (blendEqSupported) { + pgl.blendEquation(PGL.FUNC_ADD); + } + pgl.blendFunc(PGL.ONE, PGL.ZERO); + break; + case BLEND: + if (blendEqSupported) { + pgl.blendEquation(PGL.FUNC_ADD); + } + pgl.blendFunc(PGL.ONE, PGL.ONE_MINUS_SRC_ALPHA); + break; + case ADD: + if (blendEqSupported) { + pgl.blendEquation(PGL.FUNC_ADD); + } + pgl.blendFunc(PGL.ONE, PGL.ONE); + break; + case SUBTRACT: + if (blendEqSupported) { + pgl.blendEquationSeparate(PGL.FUNC_REVERSE_SUBTRACT, + PGL.FUNC_ADD); + } + pgl.blendFunc(PGL.ONE, PGL.ONE); + break; + case MULTIPLY: + if (blendEqSupported) { + pgl.blendEquation(PGL.FUNC_ADD); + } + pgl.blendFunc(PGL.DST_COLOR, PGL.ONE_MINUS_SRC_ALPHA); + break; + default: + throw new IllegalArgumentException(); + } } -//// -// if (blendMode == SUBTRACT) { -// if (blendEqSupported) { -// pgl.blendEquation(PGL.FUNC_REVERSE_SUBTRACT); -// pgl.blendFunc(PGL.ONE, PGL.SRC_ALPHA); -// } else { -// throw new IllegalArgumentException(); -// } -// -// } else if (blendMode == LIGHTEST) { -// if (blendEqSupported) { -// pgl.blendEquation(PGL.FUNC_MAX); -// pgl.blendFunc(PGL.SRC_ALPHA, PGL.DST_ALPHA); -// } else { -// PGraphics.showWarning(BLEND_DRIVER_ERROR, "LIGHTEST"); -// } -// -// } else if (blendMode == DARKEST) { -// if (blendEqSupported) { -// pgl.blendEquation(PGL.FUNC_MIN); -// pgl.blendFunc(PGL.SRC_ALPHA, PGL.DST_ALPHA); -// } else { -// PGraphics.showWarning(BLEND_DRIVER_ERROR, "DARKEST"); -// } -// -// } 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 (blendMode == MULTIPLY) { -// if (blendEqSupported) { -// pgl.blendEquation(PGL.FUNC_ADD); -// } -// pgl.blendFunc(PGL.DST_COLOR, PGL.SRC_COLOR); -// -// } else if (blendMode == SCREEN) { -// if (blendEqSupported) { -// pgl.blendEquation(PGL.FUNC_ADD); -// } -// pgl.blendFunc(PGL.ONE_MINUS_DST_COLOR, PGL.ONE); -// -// } else if (blendMode == DIFFERENCE) { -// PGraphics.showWarning(BLEND_RENDERER_ERROR, "DIFFERENCE"); -// -// } else if (blendMode == OVERLAY) { -// PGraphics.showWarning(BLEND_RENDERER_ERROR, "OVERLAY"); -// -// } else if (blendMode == HARD_LIGHT) { -// PGraphics.showWarning(BLEND_RENDERER_ERROR, "HARD_LIGHT"); -// -// } else if (blendMode == SOFT_LIGHT) { -// PGraphics.showWarning(BLEND_RENDERER_ERROR, "SOFT_LIGHT"); -// -// } else if (blendMode == DODGE) { -// PGraphics.showWarning(BLEND_RENDERER_ERROR, "DODGE"); -// -// } else if (blendMode == BURN) { -// PGraphics.showWarning(BLEND_RENDERER_ERROR, "BURN"); -// } -// lastBlendMode = blendMode; + lastInText = inText; + } + + @Override + protected void textLineImpl(char[] buffer, int start, int stop, float x, float y) { + int savedBlendMode = blendMode; + inText = true; + super.textLineImpl(buffer, start, stop, x, y); + inText = false; + blendMode(savedBlendMode); } @Override diff --git a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLGraphics3D.java b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLGraphics3D.java index ab5fefa7..c50aed25 100644 --- a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLGraphics3D.java +++ b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLGraphics3D.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2015 Neil C Smith. + * Copyright 2017 Neil C Smith. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as @@ -21,7 +21,6 @@ */ package net.neilcsmith.praxis.video.pgl; -import java.nio.IntBuffer; import processing.core.PImage; import processing.opengl.PGL; import processing.opengl.PGraphics3D; @@ -32,25 +31,27 @@ * @author Neil C Smith */ public class PGLGraphics3D extends PGraphics3D { - + final static String ID = PGLGraphics3D.class.getName(); - + private final PGLContext context; private PGLTexture pixelTexture; private PImage pixelImage; - + private boolean inText; + private boolean lastInText; + PGLGraphics3D(PGLContext context, boolean primary, int w, int h) { this.context = context; setParent(context.parent()); setPrimary(primary); setSize(w, h); } - + public PGLContext getContext() { return context; } - - void writePixelsARGB(int[] pixels, boolean hasAlpha) { + + void writePixelsARGB(int[] pixels, boolean hasAlpha) { if (drawing) { flush(); } @@ -60,11 +61,12 @@ void writePixelsARGB(int[] pixels, boolean hasAlpha) { pixelTexture.invertedY(true); pixelImage = wrapTexture(pixelTexture); } - int len = width * height; - IntBuffer buf = context.getScratchBuffer(len); - buf.put(pixels, 0, len); - buf.rewind(); - context.writePixelsARGB(buf, pixelTexture); +// int len = width * height; +// IntBuffer buf = context.getScratchBuffer(len); +// buf.put(pixels, 0, len); +// buf.rewind(); +// context.writePixelsARGB(buf, pixelTexture); + context.writePixels(pixels, hasAlpha, pixelTexture); int curBlend = blendMode; if (hasAlpha) { blendMode(REPLACE); @@ -75,7 +77,7 @@ void writePixelsARGB(int[] pixels, boolean hasAlpha) { copy(pixelImage, 0, 0, width, height, 0, height, width, -height); blendMode(curBlend); } - + protected void readPixelsARGB(int[] pixels) { this.pixels = pixels; this.pixelBuffer = context.getScratchBuffer(pixels.length); @@ -85,110 +87,77 @@ protected void readPixelsARGB(int[] pixels) { readPixels(); this.pixels = null; this.pixelBuffer = null; - + } - + @Override protected void blendModeImpl() { - if (blendMode != lastBlendMode) { + if (blendMode != lastBlendMode + || inText != lastInText) { flush(); } - + pgl.enable(PGL.BLEND); - - if (blendMode == REPLACE) { - if (blendEqSupported) { - pgl.blendEquation(PGL.FUNC_ADD); - } - pgl.blendFunc(PGL.ONE, PGL.ZERO); - - } else if (blendMode == BLEND) { - if (blendEqSupported) { - pgl.blendEquation(PGL.FUNC_ADD); - } - pgl.blendFunc(PGL.ONE, PGL.ONE_MINUS_SRC_ALPHA); - - } else if (blendMode == ADD) { - if (blendEqSupported) { - pgl.blendEquation(PGL.FUNC_ADD); - } - pgl.blendFunc(PGL.ONE, PGL.ONE); - - } else if (blendMode == MULTIPLY) { - if (blendEqSupported) { - pgl.blendEquation(PGL.FUNC_ADD); + + if (inText) { + if (blendMode == BLEND) { + if (blendEqSupported) { + pgl.blendEquation(PGL.FUNC_ADD); + } + pgl.blendFunc(PGL.SRC_ALPHA, PGL.ONE_MINUS_SRC_ALPHA); } - pgl.blendFunc(PGL.DST_COLOR, PGL.ONE_MINUS_SRC_ALPHA); - } else { - throw new IllegalArgumentException(); + + switch (blendMode) { + case REPLACE: + if (blendEqSupported) { + pgl.blendEquation(PGL.FUNC_ADD); + } + pgl.blendFunc(PGL.ONE, PGL.ZERO); + break; + case BLEND: + if (blendEqSupported) { + pgl.blendEquation(PGL.FUNC_ADD); + } + pgl.blendFunc(PGL.ONE, PGL.ONE_MINUS_SRC_ALPHA); + break; + case ADD: + if (blendEqSupported) { + pgl.blendEquation(PGL.FUNC_ADD); + } + pgl.blendFunc(PGL.ONE, PGL.ONE); + break; + case SUBTRACT: + if (blendEqSupported) { + pgl.blendEquationSeparate(PGL.FUNC_REVERSE_SUBTRACT, + PGL.FUNC_ADD); + } + pgl.blendFunc(PGL.ONE, PGL.ONE); + break; + case MULTIPLY: + if (blendEqSupported) { + pgl.blendEquation(PGL.FUNC_ADD); + } + pgl.blendFunc(PGL.DST_COLOR, PGL.ONE_MINUS_SRC_ALPHA); + break; + default: + throw new IllegalArgumentException(); + } } - -//// -// if (blendMode == SUBTRACT) { -// if (blendEqSupported) { -// pgl.blendEquation(PGL.FUNC_REVERSE_SUBTRACT); -// pgl.blendFunc(PGL.ONE, PGL.SRC_ALPHA); -// } else { -// throw new IllegalArgumentException(); -// } -// -// } else if (blendMode == LIGHTEST) { -// if (blendEqSupported) { -// pgl.blendEquation(PGL.FUNC_MAX); -// pgl.blendFunc(PGL.SRC_ALPHA, PGL.DST_ALPHA); -// } else { -// PGraphics.showWarning(BLEND_DRIVER_ERROR, "LIGHTEST"); -// } -// -// } else if (blendMode == DARKEST) { -// if (blendEqSupported) { -// pgl.blendEquation(PGL.FUNC_MIN); -// pgl.blendFunc(PGL.SRC_ALPHA, PGL.DST_ALPHA); -// } else { -// PGraphics.showWarning(BLEND_DRIVER_ERROR, "DARKEST"); -// } -// -// } 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 (blendMode == MULTIPLY) { -// if (blendEqSupported) { -// pgl.blendEquation(PGL.FUNC_ADD); -// } -// pgl.blendFunc(PGL.DST_COLOR, PGL.SRC_COLOR); -// -// } else if (blendMode == SCREEN) { -// if (blendEqSupported) { -// pgl.blendEquation(PGL.FUNC_ADD); -// } -// pgl.blendFunc(PGL.ONE_MINUS_DST_COLOR, PGL.ONE); -// -// } else if (blendMode == DIFFERENCE) { -// PGraphics.showWarning(BLEND_RENDERER_ERROR, "DIFFERENCE"); -// -// } else if (blendMode == OVERLAY) { -// PGraphics.showWarning(BLEND_RENDERER_ERROR, "OVERLAY"); -// -// } else if (blendMode == HARD_LIGHT) { -// PGraphics.showWarning(BLEND_RENDERER_ERROR, "HARD_LIGHT"); -// -// } else if (blendMode == SOFT_LIGHT) { -// PGraphics.showWarning(BLEND_RENDERER_ERROR, "SOFT_LIGHT"); -// -// } else if (blendMode == DODGE) { -// PGraphics.showWarning(BLEND_RENDERER_ERROR, "DODGE"); -// -// } else if (blendMode == BURN) { -// PGraphics.showWarning(BLEND_RENDERER_ERROR, "BURN"); -// } -// + lastBlendMode = blendMode; + lastInText = inText; } - + + @Override + protected void textLineImpl(char[] buffer, int start, int stop, float x, float y) { + int savedBlendMode = blendMode; + inText = true; + super.textLineImpl(buffer, start, stop, x, y); + inText = false; + blendMode(savedBlendMode); + } + @Override public void beginDraw() { if (drawing) { @@ -201,7 +170,7 @@ public void beginDraw() { super.beginDraw(); context.current = this; } - + @Override public void endDraw() { if (!drawing) { @@ -211,14 +180,14 @@ public void endDraw() { super.endDraw(); context.current = getPrimaryPG(); } - + void endOffscreen() { if (context.current != getPrimaryPG()) { context.current.endDraw(); context.current = getPrimaryPG(); } } - + @Override protected void colorCalc(float gray, float alpha) { if (gray > colorModeX) { @@ -227,21 +196,21 @@ protected void colorCalc(float gray, float alpha) { if (alpha > colorModeA) { alpha = colorModeA; } - + if (gray < 0) { gray = 0; } if (alpha < 0) { alpha = 0; } - + calcA = (alpha == colorModeA) ? 1 : alpha / colorModeA; - + calcR = gray / colorModeX; calcR = (alpha == colorModeA) ? calcR : calcR * calcA; calcG = calcR; calcB = calcR; - + calcRi = (int) (calcR * 255); calcGi = (int) (calcG * 255); calcBi = (int) (calcB * 255); @@ -249,7 +218,7 @@ protected void colorCalc(float gray, float alpha) { calcColor = (calcAi << 24) | (calcRi << 16) | (calcGi << 8) | calcBi; calcAlpha = (calcAi != 255); } - + @Override protected void colorCalc(float x, float y, float z, float a) { if (x > colorModeX) { @@ -264,7 +233,7 @@ protected void colorCalc(float x, float y, float z, float a) { if (a > colorModeA) { a = colorModeA; } - + if (x < 0) { x = 0; } @@ -277,16 +246,16 @@ protected void colorCalc(float x, float y, float z, float a) { if (a < 0) { a = 0; } - + calcA = (a == colorModeA) ? 1 : a / colorModeA; - + switch (colorMode) { case RGB: calcR = x / colorModeX; calcG = y / colorModeY; calcB = z / colorModeZ; break; - + case HSB: x /= colorModeX; // h y /= colorModeY; // s @@ -294,14 +263,14 @@ protected void colorCalc(float x, float y, float z, float a) { if (y == 0) { // saturation == 0 calcR = calcG = calcB = z; - + } else { float which = (x - (int) x) * 6.0f; float f = which - (int) which; float p = z * (1.0f - y); float q = z * (1.0f - y * f); float t = z * (1.0f - (y * (1.0f - f))); - + switch ((int) which) { case 0: calcR = z; @@ -337,13 +306,13 @@ protected void colorCalc(float x, float y, float z, float a) { } break; } - + if (a != colorModeA) { calcR *= calcA; calcG *= calcA; calcB *= calcA; } - + calcRi = (int) (255 * calcR); calcGi = (int) (255 * calcG); calcBi = (int) (255 * calcB); @@ -351,12 +320,12 @@ protected void colorCalc(float x, float y, float z, float a) { calcColor = (calcAi << 24) | (calcRi << 16) | (calcGi << 8) | calcBi; calcAlpha = (calcAi != 255); } - + @Override protected PGL createPGL(PGraphicsOpenGL pg) { return new PGLJOGL(pg); } - + @Override public void dispose() { @@ -365,12 +334,12 @@ public void dispose() { } super.dispose(); - + } - + @Override public processing.core.PSurface createSurface() { return new PGLGraphicsPSurface(this); } - + } diff --git a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLPlayer.java b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLPlayer.java index 06aa569b..69623850 100644 --- a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLPlayer.java +++ b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLPlayer.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2015 Neil C Smith. + * Copyright 2017 Neil C Smith. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as @@ -33,10 +33,12 @@ import net.neilcsmith.praxis.video.pipes.FrameRateListener; import net.neilcsmith.praxis.video.pipes.VideoPipe; import processing.core.PApplet; +import processing.core.PConstants; import processing.core.PGraphics; import processing.core.PImage; import processing.core.PSurface; import processing.opengl.PJOGL; +import processing.opengl.PSurfaceJOGL; /** * @@ -51,6 +53,7 @@ public class PGLPlayer implements Player { private final long frameNanos; private final WindowHints wHints; private final QueueContext queue; + private final PGLProfile profile; private volatile boolean running = false; // flag to control animation private volatile long time; @@ -66,7 +69,8 @@ public class PGLPlayer implements Player { int outputRotation, int outputDevice, WindowHints wHints, - QueueContext queue) { + QueueContext queue, + PGLProfile profile) { if (width <= 0 || height <= 0 || fps <= 0) { throw new IllegalArgumentException(); } @@ -80,6 +84,7 @@ public class PGLPlayer implements Player { this.outputDevice = outputDevice; this.wHints = wHints; this.queue = queue; + this.profile = profile; sink = new PGLOutputSink(); } @@ -193,16 +198,16 @@ private class Applet extends PApplet { private PGLSurface pglSurface; private Applet() { - context = new PGLContext(this, surfaceWidth, surfaceHeight); + context = new PGLContext(this, profile, surfaceWidth, surfaceHeight); } @Override protected PGraphics makeGraphics(int w, int h, String renderer, String path, boolean primary) { - if (PGLGraphics.ID.equals(renderer)) { + if (PGLGraphics.ID.equals(renderer) || P2D.equals(renderer)) { PGLGraphics pgl = new PGLGraphics(context, primary, w, h); // pgl.setParent(this); return pgl; - } else if (PGLGraphics3D.ID.equals(renderer)) { + } else if (PGLGraphics3D.ID.equals(renderer) || P3D.equals(renderer)) { PGLGraphics3D pgl3d = new PGLGraphics3D(context, primary, w, h); // pgl3d.setParent(this); return pgl3d; @@ -214,7 +219,20 @@ protected PGraphics makeGraphics(int w, int h, String renderer, String path, boo @Override public void settings() { - PJOGL.profile = 3; + switch (profile) { + case GL2: + PJOGL.profile = 1; + break; + case GL3: + PJOGL.profile = 3; + break; + case GL4: + PJOGL.profile = 4; + break; + case GLES2: + PJOGL.profile = 2; + break; + } if (wHints.isFullScreen()) { if (outputDevice > -1) { fullScreen(PGLGraphics.ID, outputDevice + 1); @@ -228,6 +246,7 @@ public void settings() { @Override protected PSurface initSurface() { + PSurfaceJOGL.profile = null; PSurface s = super.initSurface(); s.setTitle(wHints.getTitle()); GLWindow window = (GLWindow) surface.getNative(); diff --git a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLPlayerFactory.java b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLPlayerFactory.java new file mode 100644 index 00000000..88b1d483 --- /dev/null +++ b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLPlayerFactory.java @@ -0,0 +1,187 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. + */ +package net.neilcsmith.praxis.video.pgl; + +import com.jogamp.opengl.GLProfile; +import net.neilcsmith.praxis.video.ClientConfiguration; +import net.neilcsmith.praxis.video.Player; +import net.neilcsmith.praxis.video.PlayerConfiguration; +import net.neilcsmith.praxis.video.PlayerFactory; +import net.neilcsmith.praxis.video.QueueContext; +import net.neilcsmith.praxis.video.WindowHints; + +/** + * + * @author Neil C Smith (http://neilcsmith.net) + */ +public class PGLPlayerFactory implements PlayerFactory { + + private final PGLProfile profile; + + private PGLPlayerFactory() { + this(null); + } + + private PGLPlayerFactory(PGLProfile profile) { + this.profile = profile; + } + + @Override + public Player createPlayer(PlayerConfiguration config, ClientConfiguration[] clients) + throws Exception { + if (clients.length != 1 || clients[0].getSourceCount() != 0 || clients[0].getSinkCount() != 1) { + throw new IllegalArgumentException("Invalid client configuration"); + } + + int width = config.getWidth(); + int height = config.getHeight(); + int outWidth = width; + int outHeight = height; + int rotation = 0; + int device = -1; + + PGLProfile glProfile = profile; + if (profile == null) { + glProfile = GLProfile.isAvailable(GLProfile.GL2GL3) ? PGLProfile.GL3 : PGLProfile.GLES2; + } + + ClientConfiguration.Dimension dim + = clients[0].getLookup().get(ClientConfiguration.Dimension.class); + if (dim != null) { + outWidth = dim.getWidth(); + outHeight = dim.getHeight(); + } + + ClientConfiguration.Rotation rot + = clients[0].getLookup().get(ClientConfiguration.Rotation.class); + if (rot != null) { + rotation = rot.getAngle(); + } + switch (rotation) { + case 0: + case 90: + case 180: + case 270: + break; + default: + rotation = 0; + } + + ClientConfiguration.DeviceIndex dev + = clients[0].getLookup().get(ClientConfiguration.DeviceIndex.class); + if (dev != null) { + device = dev.getValue(); + } + + WindowHints wHints = clients[0].getLookup().get(WindowHints.class); + if (wHints == null) { + wHints = new WindowHints(); + } + + QueueContext queue = config.getLookup().get(QueueContext.class); + + return new PGLPlayer( + config.getWidth(), + config.getHeight(), + config.getFPS(), + outWidth, + outHeight, + rotation, + device, + wHints, + queue, + glProfile); + + } + + + public static class Default implements PlayerFactory.Provider { + + @Override + public PlayerFactory getFactory() { + return new PGLPlayerFactory(null); + } + + @Override + public String getLibraryName() { + return "OpenGL"; + } + + } + + public static class GL2 implements PlayerFactory.Provider { + + @Override + public PlayerFactory getFactory() { + return new PGLPlayerFactory(PGLProfile.GL2); + } + + @Override + public String getLibraryName() { + return "OpenGL:GL2"; + } + + } + + public static class GL3 implements PlayerFactory.Provider { + + @Override + public PlayerFactory getFactory() { + return new PGLPlayerFactory(PGLProfile.GL3); + } + + @Override + public String getLibraryName() { + return "OpenGL:GL3"; + } + + } + + public static class GL4 implements PlayerFactory.Provider { + + @Override + public PlayerFactory getFactory() { + return new PGLPlayerFactory(PGLProfile.GL4); + } + + @Override + public String getLibraryName() { + return "OpenGL:GL4"; + } + + } + + public static class GLES2 implements PlayerFactory.Provider { + + @Override + public PlayerFactory getFactory() { + return new PGLPlayerFactory(PGLProfile.GLES2); + } + + @Override + public String getLibraryName() { + return "OpenGL:GLES2"; + } + + } + +} diff --git a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLPlayerFactoryProvider.java b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLPlayerFactoryProvider.java index a801b560..48c13f55 100644 --- a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLPlayerFactoryProvider.java +++ b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLPlayerFactoryProvider.java @@ -32,6 +32,7 @@ * * @author nsigma */ +@Deprecated public class PGLPlayerFactoryProvider implements PlayerFactory.Provider { private final static String LIBRARY_NAME = "OpenGL"; @@ -107,7 +108,8 @@ public Player createPlayer(PlayerConfiguration config, ClientConfiguration[] cli rotation, device, wHints, - queue); + queue, + PGLProfile.GL3); } } diff --git a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLProfile.java b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLProfile.java new file mode 100644 index 00000000..0d144b7e --- /dev/null +++ b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLProfile.java @@ -0,0 +1,32 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. + */ +package net.neilcsmith.praxis.video.pgl; + +/** + * + * @author Neil C Smith (http://neilcsmith.net) + */ +public enum PGLProfile { + + GL2, GL3, GL4, GLES2; + +} diff --git a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLReceiver.java b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLReceiver.java new file mode 100644 index 00000000..82db3e7d --- /dev/null +++ b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLReceiver.java @@ -0,0 +1,138 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. + */ +package net.neilcsmith.praxis.video.pgl; + +import java.util.logging.Level; +import java.util.logging.Logger; +import net.neilcsmith.praxis.core.ExecutionContext; +import net.neilcsmith.praxis.core.Port; +import net.neilcsmith.praxis.impl.AbstractExecutionContextComponent; +import net.neilcsmith.praxis.impl.StringProperty; +import net.neilcsmith.praxis.impl.TriggerControl; +import net.neilcsmith.praxis.video.impl.DefaultVideoOutputPort; +import net.neilcsmith.praxis.video.pipes.impl.SingleOut; +import net.neilcsmith.praxis.video.render.Surface; +import processing.core.PGraphics; + +/** + * + * @author neilcsmith + */ +public final class PGLReceiver extends AbstractExecutionContextComponent { + + private final Delegator delegator; + private final PGLTextureSharer textureSharer; + + private PGLTextureSharer.Receiver receiver; + private String serverID = ""; + private boolean active; + + public PGLReceiver() { + delegator = new Delegator(); + textureSharer = PGLTextureSharer.find().orElse(null); + registerPort(Port.OUT, new DefaultVideoOutputPort(delegator)); + registerControl("server-id", StringProperty.builder().binding(new StringProperty.Binding() { + @Override + public void setBoundValue(long time, String value) { + if (active) { + throw new UnsupportedOperationException("Can't set server ID while active"); + } + serverID = value; + } + + @Override + public String getBoundValue() { + return serverID; + } + }).build()); + TriggerControl start = TriggerControl.create(time -> active = true); + TriggerControl stop = TriggerControl.create((long time) -> { + PGLReceiver.this.handleDispose(); + }); + registerControl("start", start); + registerControl("stop", stop); + registerPort("start", start.createPort()); + registerPort("stop", stop.createPort()); + } + + @Override + public void stateChanged(ExecutionContext source) { + if (source.getState() != ExecutionContext.State.ACTIVE) { + handleDispose(); + } + } + + @Override + public void hierarchyChanged() { + handleDispose(); + super.hierarchyChanged(); + } + + private void handleProcess(PGLSurface surface) { + if (active) { + if (receiver == null) { + initReceiver(surface.getContext()); + if (receiver == null) { + active = false; + return; + } + } + receiver.acquireFrame().ifPresent(f -> { + PGraphics g = surface.getGraphics(); + g.image(f, 0, 0, g.width, g.height); + }); + } + } + + private void initReceiver(PGLContext context) { + if (textureSharer != null) { + try { + receiver = textureSharer.createReceiver(context, serverID); + } catch (Exception ex) { + Logger.getLogger(PGLReceiver.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + private void handleDispose() { + active = false; + if (receiver != null) { + receiver.dispose(); + receiver = null; + } + } + + private class Delegator extends SingleOut { + + @Override + protected void process(Surface surface, boolean rendering) { + if (rendering) { + surface.clear(); + if (surface instanceof PGLSurface) { + handleProcess((PGLSurface) surface); + } + } + } + + } + +} diff --git a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLSender.java b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLSender.java new file mode 100644 index 00000000..2c5e021d --- /dev/null +++ b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLSender.java @@ -0,0 +1,138 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. + */ +package net.neilcsmith.praxis.video.pgl; + +import java.util.logging.Level; +import java.util.logging.Logger; +import net.neilcsmith.praxis.core.ExecutionContext; +import net.neilcsmith.praxis.core.Port; +import net.neilcsmith.praxis.impl.AbstractExecutionContextComponent; +import net.neilcsmith.praxis.impl.StringProperty; +import net.neilcsmith.praxis.impl.TriggerControl; +import net.neilcsmith.praxis.video.impl.DefaultVideoInputPort; +import net.neilcsmith.praxis.video.impl.DefaultVideoOutputPort; +import net.neilcsmith.praxis.video.pipes.impl.SingleInOut; +import net.neilcsmith.praxis.video.render.Surface; +import processing.core.PGraphics; + +/** + * + * @author neilcsmith + */ +public final class PGLSender extends AbstractExecutionContextComponent { + + private final Delegator delegator; + private final PGLTextureSharer textureSharer; + + private PGLTextureSharer.Sender sender; + private String serverID = ""; + private boolean active; + + public PGLSender() { + delegator = new Delegator(); + textureSharer = PGLTextureSharer.find().orElse(null); + registerPort(Port.IN, new DefaultVideoInputPort(delegator)); + registerPort(Port.OUT, new DefaultVideoOutputPort(delegator)); + registerControl("server-id", StringProperty.builder().binding(new StringProperty.Binding() { + @Override + public void setBoundValue(long time, String value) { + if (active) { + throw new UnsupportedOperationException("Can't set server ID while active"); + } + serverID = value; + } + + @Override + public String getBoundValue() { + return serverID; + } + }).build()); + TriggerControl start = TriggerControl.create(time -> active = true); + TriggerControl stop = TriggerControl.create((long time) -> { + PGLSender.this.handleDispose(); + }); + registerControl("start", start); + registerControl("stop", stop); + registerPort("start", start.createPort()); + registerPort("stop", stop.createPort()); + } + + @Override + public void stateChanged(ExecutionContext source) { + if (source.getState() != ExecutionContext.State.ACTIVE) { + handleDispose(); + } + } + + @Override + public void hierarchyChanged() { + handleDispose(); + super.hierarchyChanged(); + } + + private void handleProcess(PGLSurface surface) { + if (active) { + if (sender == null) { + initSender(surface.getContext()); + if (sender == null) { + active = false; + return; + } + } + PGraphics pg = surface.getGraphics(); + pg.endDraw(); + sender.sendFrame(pg); + } + } + + private void initSender(PGLContext context) { + if (textureSharer != null) { + try { + sender = textureSharer.createSender(context, serverID); + } catch (Exception ex) { + Logger.getLogger(PGLSender.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + private void handleDispose() { + active = false; + if (sender != null) { + sender.dispose(); + sender = null; + } + } + + private class Delegator extends SingleInOut { + + @Override + protected void process(Surface surface, boolean rendering) { + if (rendering) { + if (surface instanceof PGLSurface) { + handleProcess((PGLSurface) surface); + } + } + } + + } + +} diff --git a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLTextureSharer.java b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLTextureSharer.java new file mode 100644 index 00000000..ed4f0e59 --- /dev/null +++ b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/PGLTextureSharer.java @@ -0,0 +1,68 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. + */ +package net.neilcsmith.praxis.video.pgl; + +import java.util.Optional; +import net.neilcsmith.praxis.core.Lookup; +import processing.core.PImage; + +/** + * + * @author neilcsmith + */ +public interface PGLTextureSharer { + + public Sender createSender(PGLContext context, String id) throws Exception; + + public Receiver createReceiver(PGLContext context, String server) throws Exception; + + public boolean isSupported(); + + + public static Optional find() { + for (PGLTextureSharer t : Lookup.SYSTEM.getAll(PGLTextureSharer.class)) { + if (t.isSupported()) { + return Optional.of(t); + } + } + return Optional.empty(); + } + + public static interface Sender { + + public void sendFrame(PImage frame); + + public void dispose(); + + } + + public static interface Receiver { + + public boolean hasNewFrame(); + + public Optional acquireFrame(); + + public void dispose(); + + } + +} diff --git a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/ops/PGLOpCache.java b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/ops/PGLOpCache.java index 85c7333f..47bb11ac 100644 --- a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/ops/PGLOpCache.java +++ b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/ops/PGLOpCache.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2016 Neil C Smith. + * Copyright 2017 Neil C Smith. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as @@ -29,6 +29,7 @@ import net.neilcsmith.praxis.video.render.ops.RectFill; import net.neilcsmith.praxis.video.render.ops.ScaledBlit; import net.neilcsmith.praxis.video.render.ops.ShapeRender; +import net.neilcsmith.praxis.video.render.ops.TextRender; import net.neilcsmith.praxis.video.render.ops.TransformBlit; /** @@ -48,6 +49,7 @@ public PGLOpCache(PGLContext context) { cache.put(TransformBlit.class, new PGLTransformBlitOp()); cache.put(RectFill.class, new PGLRectFillOp()); cache.put(ShapeRender.class, new PGLShapeRenderOp()); + cache.put(TextRender.class, new PGLTextRenderOp()); } public PGLOp find(SurfaceOp op) { diff --git a/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/ops/PGLTextRenderOp.java b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/ops/PGLTextRenderOp.java new file mode 100644 index 00000000..e398ef47 --- /dev/null +++ b/praxis.video.pgl/src/net/neilcsmith/praxis/video/pgl/ops/PGLTextRenderOp.java @@ -0,0 +1,82 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. + */ +package net.neilcsmith.praxis.video.pgl.ops; + +import java.awt.Color; +import java.awt.geom.AffineTransform; +import java.util.logging.Level; +import net.neilcsmith.praxis.video.pgl.PGLGraphics; +import net.neilcsmith.praxis.video.pgl.PGLSurface; +import static net.neilcsmith.praxis.video.pgl.ops.PGLOp.LOG; +import net.neilcsmith.praxis.video.render.Surface; +import net.neilcsmith.praxis.video.render.SurfaceOp; +import net.neilcsmith.praxis.video.render.ops.TextRender; + +/** + * + * @author Neil C Smith (http://neilcsmith.net) + */ +public class PGLTextRenderOp extends PGLOp { + + private final double[] matrix; + + public PGLTextRenderOp() { + super(TextRender.class); + matrix = new double[6]; + } + + @Override + public void process(SurfaceOp op, PGLSurface output, Bypass bypass, Surface... inputs) { + if (process((TextRender) op, output)) { + return; + } + bypass.process(op, inputs); + } + + private boolean process(TextRender op, PGLSurface dst) { + try { + PGLGraphics pg = dst.getGraphics(); + pg.beginDraw(); + Color color = op.getColor(); + pg.fill(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()); + pg.textFont(dst.getContext().asPFont(op.getFont())); + AffineTransform tr = op.getTransform(); + if (tr != null) { + tr.getMatrix(matrix); + pg.pushMatrix(); + pg.applyMatrix((float) matrix[0], (float) matrix[2], (float) matrix[4], + (float) matrix[1], (float) matrix[3], (float) matrix[5]); + pg.text(op.getText(), (float) op.getX(), (float) op.getY()); + pg.popMatrix(); + } else { + pg.text(op.getText(), (float) op.getX(), (float) op.getY()); + } + return true; + } catch (Exception ex) { + LOG.log(Level.SEVERE, "Text blit threw exception", ex); + return false; + } + } + + + +} diff --git a/praxis.video.render/src/net/neilcsmith/praxis/video/render/ops/TextRender.java b/praxis.video.render/src/net/neilcsmith/praxis/video/render/ops/TextRender.java new file mode 100644 index 00000000..440bc54c --- /dev/null +++ b/praxis.video.render/src/net/neilcsmith/praxis/video/render/ops/TextRender.java @@ -0,0 +1,132 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. + */ +package net.neilcsmith.praxis.video.render.ops; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import net.neilcsmith.praxis.video.render.PixelData; +import net.neilcsmith.praxis.video.render.SurfaceOp; +import net.neilcsmith.praxis.video.render.utils.ImageUtils; + +/** + * + * @author Neil C Smith (http://neilcsmith.net) + */ +public class TextRender implements SurfaceOp { + + private String text; + private Font font; + private Color color; + private double x; + private double y; + private AffineTransform transform; + + public TextRender() { + text = ""; + color = Color.WHITE; + x = 0; + y = 0; + } + + public String getText() { + return text; + } + + public TextRender setText(String text) { + if (text == null) { + text = ""; + } + this.text = text; + return this; + } + + public Font getFont() { + return font; + } + + public TextRender setFont(Font font) { + this.font = font; + return this; + } + + public Color getColor() { + return color; + } + + public TextRender setColor(Color color) { + if (color == null) { + color = Color.WHITE; + } + this.color = color; + return this; + } + + public double getX() { + return x; + } + + public TextRender setX(double x) { + this.x = x; + return this; + } + + public double getY() { + return y; + } + + public TextRender setY(double y) { + this.y = y; + return this; + } + + public AffineTransform getTransform() { + return transform; + } + + public TextRender setTransform(AffineTransform transform) { + this.transform = transform; + return this; + } + + @Override + public void process(PixelData output, PixelData... inputs) { + if (text == null || text.isEmpty() || font == null) { + return; + } + BufferedImage im = ImageUtils.toImage(output); + Graphics2D g = im.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + if (transform != null) { + g.setTransform(transform); + } + g.setColor(color); + g.setFont(font); + g.drawString(text, (float) x, (float) y); + + } + +} diff --git a/praxis.video.render/src/net/neilcsmith/praxis/video/render/utils/FontUtils.java b/praxis.video.render/src/net/neilcsmith/praxis/video/render/utils/FontUtils.java new file mode 100644 index 00000000..b45e4716 --- /dev/null +++ b/praxis.video.render/src/net/neilcsmith/praxis/video/render/utils/FontUtils.java @@ -0,0 +1,50 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Neil C Smith. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 3 for more details. + * + * You should have received a copy of the GNU General Public License version 3 + * along with this work; if not, see http://www.gnu.org/licenses/ + * + * + * Please visit http://neilcsmith.net if you need additional information or + * have any questions. + */ +package net.neilcsmith.praxis.video.render.utils; + +import java.awt.Font; +import java.awt.FontFormatException; +import java.io.IOException; +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author Neil C Smith (http://neilcsmith.net) + */ +public class FontUtils { + + private final static Map cache = new HashMap<>(); + + private FontUtils() {} + + public synchronized static Font load(URI location) throws FontFormatException, IOException { + Font font = cache.get(location); + if (font == null) { + font = Font.createFont(Font.TRUETYPE_FONT, location.toURL().openStream()); + cache.put(location, font); + } + return font; + } + +} diff --git a/praxis.video/src/net/neilcsmith/praxis/video/components/DefaultVideoRoot.java b/praxis.video/src/net/neilcsmith/praxis/video/components/DefaultVideoRoot.java index 05cdeb6f..a62f88c9 100644 --- a/praxis.video/src/net/neilcsmith/praxis/video/components/DefaultVideoRoot.java +++ b/praxis.video/src/net/neilcsmith/praxis/video/components/DefaultVideoRoot.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2015 Neil C Smith. + * Copyright 2017 Neil C Smith. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as @@ -21,6 +21,8 @@ */ package net.neilcsmith.praxis.video.components; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; @@ -47,11 +49,16 @@ * @author Neil C Smith */ public class DefaultVideoRoot extends AbstractRoot implements FrameRateListener { - - // @TODO build list from player search + private final static String SOFTWARE = "Software"; - private final static String OPENGL = "OpenGL"; - + private final static List RENDERERS = new ArrayList<>(); + static { + RENDERERS.add(SOFTWARE); + for (PlayerFactory.Provider renderer : Lookup.SYSTEM.getAll(PlayerFactory.Provider.class)) { + RENDERERS.add(renderer.getLibraryName()); + } + } + private final static Logger LOG = Logger.getLogger(DefaultVideoRoot.class.getName()); private final static int WIDTH_DEFAULT = 640; @@ -72,8 +79,8 @@ public DefaultVideoRoot() { } private void buildControls() { - renderer = StringProperty.builder().defaultValue(SOFTWARE).allowedValues(SOFTWARE, OPENGL).build(); - + renderer = StringProperty.builder().defaultValue(SOFTWARE).allowedValues(RENDERERS.toArray(new String[0])).build(); + registerControl("renderer", renderer); registerControl("width", IntProperty.create(new WidthBinding(), 1, 16384, width)); registerControl("height", IntProperty.create(new HeightBinding(), 1, 16384, height)); @@ -160,7 +167,6 @@ protected void stopping() { player.terminate(); interrupt(); - } private class VideoContextImpl extends VideoContext {