├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── all.pro ├── common.pri ├── demo ├── examples │ ├── filter-banner.html │ ├── filter-demo.html │ ├── filter-demo.qml │ ├── filter-plain.html │ ├── logo-transition.blend │ ├── logo-transition.mtl │ ├── logo-transition.obj │ ├── producer-demo.html │ ├── transition-demo.html │ ├── transition-demo3d.qml │ ├── transition-shader-crosszoom.html │ ├── transition-shader-glslio.html │ ├── transition-shader-pagecurl.html │ └── transitions.json └── mlt │ ├── blue.mlt │ ├── consumer_av │ ├── consumer_sdl │ ├── green.mlt │ ├── mlt_filter_banner_html │ ├── mlt_filter_demo_html │ ├── mlt_filter_demo_qml │ ├── mlt_filter_plain_html │ ├── mlt_producer_demo_html │ ├── mlt_producer_plain_html │ ├── mlt_transition_demo3d_qml │ ├── mlt_transition_demo_html │ ├── mlt_transition_shader_crosszoom_html │ ├── mlt_transition_shader_glslio_html │ ├── mlt_transition_shader_pagecurl_html │ └── red.mlt ├── doc ├── Doxyfile ├── effects_3d.dox ├── effects_authoring.dox ├── examples.dox ├── footer.html ├── license.dox ├── logo.png ├── logo.xcf.gz ├── mainpage.dox └── mlt.dox ├── mlt ├── factory.cpp ├── factory.h ├── mlt.pro ├── panzoom_producer.cpp ├── qmelt │ ├── qmelt.cpp │ └── qmelt.pro ├── service_locker.cpp ├── service_locker.h ├── service_manager.cpp ├── service_manager.h ├── webvfx_filter.cpp ├── webvfx_producer.cpp └── webvfx_transition.cpp ├── tools ├── blender │ └── addons │ │ ├── io_anim_webvfx │ │ ├── __init__.py │ │ ├── export_webvfx.py │ │ └── import_webvfx.py │ │ └── space_view3d_camera_anim_webvfx.py ├── browser │ ├── browser.cpp │ ├── browser.h │ ├── browser.pro │ ├── browser.ui │ └── main.cpp ├── js │ └── webvfx.js └── render │ ├── render.cpp │ └── render.pro ├── viewer ├── image_color.cpp ├── image_color.h ├── main.cpp ├── render_dialog.cpp ├── render_dialog.h ├── render_dialog.ui ├── viewer.cpp ├── viewer.h ├── viewer.pro └── viewer.ui └── webvfx ├── content.cpp ├── content.h ├── content_context.cpp ├── content_context.h ├── effects.cpp ├── effects.h ├── effects_impl.cpp ├── effects_impl.h ├── image.cpp ├── image.h ├── logger.cpp ├── logger.h ├── parameters.cpp ├── parameters.h ├── qml_content.cpp ├── qml_content.h ├── render_strategy.cpp ├── render_strategy.h ├── resources ├── org │ └── webvfx │ │ └── WebVfx │ │ ├── AnimatedCamera.qml │ │ ├── BlenderItem3D.qml │ │ ├── TextTexture.qml │ │ └── qmldir ├── resources.qrc └── webvfx │ └── scripts │ ├── animation.js │ ├── easing.js │ ├── filterkit.js │ ├── shaderkit.js │ ├── threeworld.js │ └── tracker.js ├── web_content.cpp ├── web_content.h ├── webvfx.cpp ├── webvfx.h ├── webvfx.pro └── webvfx_mac.mm /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | Makefile.* 3 | debug 4 | release 5 | build 6 | doxydoc 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "mlt/qmelt/mlt"] 2 | path = mlt/qmelt/mlt 3 | url = ../mlt.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Hewlett-Packard nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IMPORTANT 2 | 3 | THIS PROJECT IS DEPRECATED BY THE MLT TEAM AND NO LONGER MAINTAINED. 4 | 5 | The QtWebKit library it uses was deprecated by the Qt project since 2015. We are aware of the GitHub project to keep 6 | the library alive. However, we no longer want to maintain this sub-project due to minimal resources and lack of 7 | contribution. Also, the integration of QtWebKit into Qt 6 is uncertain. 8 | 9 | # Overview 10 | 11 | WebVfx is a video effects library that allows effects to be implemented using [WebKit HTML](http://trac.webkit.org/wiki/QtWebKit) or [Qt QML](http://doc.qt.io/qt-5/qmlapplications.html). 12 | 13 | ## Prerequisites 14 | 15 | You will need [Qt](http://qt-project.org/downloads/) installed (5.2 or later recommended). Qt includes QtWebKit and QML, but since v5.6, you need to [build QtWebKit](http://trac.webkit.org/wiki/QtWebKit#BuildInstructions) yourself. 16 | You may also want to build [Qt3D](http://doc.qt.io/qt-5/qt3d-index.html) - 3D extensions to QML. WebVfx includes support for Qt3D, and Qt include Qt3D as of v5.6. 17 | 18 | WebVfx includes an MLT service that exposes producers, filters and transitions implemented in WebVfx. Install [MLT Framework](https://www.mltframework.org/) 0.7.2 or greater to build the plugin. 19 | 20 | ## Building 21 | 22 | In the webvfx directory run `qmake -r PREFIX=/usr/local` and then `make install`. `PREFIX` determines where WebVfx will be installed. If MLT is installed in a non-standard location, you may need to set the `PKG_CONFIG_PATH` environment variable to where its pkgconfig file lives, e.g. `PKG_CONFIG_PATH=/usr/local/lib/pkgconfig`. 23 | 24 | The [MLT melt](https://www.mltframework.org/twiki/bin/view/MLT/MltMelt) command 25 | will not work with WebVfx on Windows or macOS because the Qt event loop must run 26 | on the main thread. If you set `MLT_SOURCE` to the root of your MLT source code 27 | directory, then a `qmelt` executable will be installed which behaves the same as 28 | `melt` but works with WebVfx on Windows or macOS. e.g. `qmake -r 29 | PREFIX=/usr/local MLT_SOURCE=~/Projects/mlt`. 30 | The MLT source is available as a git submodule to make this more convenient. If 31 | you checkout the git submodule, then it is detected and there is no need to set 32 | MLT_SOURCE. 33 | 34 | `make doxydoc` to generate the documentation using Doxygen. 35 | You can also `make uninstall`, `make clean` and `make distclean`. 36 | 37 | ## Demos 38 | 39 | See the [documentation](https://www.mltframework.org/doxygen/webvfx/) for examples. 40 | 41 | ## License 42 | 43 | Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 44 | Use of this source code is governed by a BSD-style license that can be 45 | found in the LICENSE file. 46 | -------------------------------------------------------------------------------- /all.pro: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | isEmpty(PREFIX) { 6 | message("Install PREFIX not set, using default.") 7 | } 8 | isEqual(QT_MAJOR_VERSION, 5):cache() 9 | include(common.pri) 10 | 11 | !minQtVersion(5, 2, 0) { 12 | message("Cannot build WebVfx with Qt version $${QT_VERSION}.") 13 | error("Use at least Qt 5.2.0.") 14 | } 15 | 16 | TEMPLATE = subdirs 17 | CONFIG += ordered 18 | 19 | SUBDIRS += webvfx 20 | SUBDIRS += viewer 21 | SUBDIRS += tools/render 22 | SUBDIRS += tools/browser 23 | 24 | system(pkg-config --exists mlt-framework) { 25 | SUBDIRS += mlt 26 | mlt.depends = webvfx 27 | isEmpty(MLT_SOURCE) { 28 | warning("MLT_SOURCE not set, skipping qmelt. Set MLT_SOURCE to the MLT source code directory to build qmelt.") 29 | } else { 30 | SUBDIRS += mlt/qmelt 31 | } 32 | } else { 33 | warning("MLT framework not found, skipping MLT plugin. Need to set PKG_CONFIG_PATH environment variable?") 34 | } 35 | 36 | viewer.depends = webvfx 37 | render.depends = webvfx 38 | 39 | # Documentation 40 | doxydoc.target = doxydoc 41 | doxydoc.commands = echo PROJECT_NUMBER=`git describe --always --dirty` | cat - doc/Doxyfile | doxygen - 42 | doxydoc.depends = FORCE 43 | QMAKE_EXTRA_TARGETS += doxydoc 44 | -------------------------------------------------------------------------------- /common.pri: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | isEmpty(PREFIX) { 6 | unix:PREFIX = /usr/local 7 | } 8 | 9 | CONFIG += warn_on debug_and_release 10 | 11 | CONFIG(debug, debug|release) { 12 | DESTDIR = $$PWD/build/debug 13 | } else { 14 | DESTDIR = $$PWD/build/release 15 | } 16 | 17 | OBJECTS_DIR = $$DESTDIR/.obj/$$TARGET 18 | MOC_DIR = $$DESTDIR/.moc/$$TARGET 19 | RCC_DIR = $$DESTDIR/.rcc/$$TARGET 20 | UI_DIR = $$DESTDIR/.ui/$$TARGET 21 | 22 | INCLUDEPATH += $$PWD 23 | 24 | exists(mlt/qmelt/mlt/README) { 25 | MLT_SOURCE = mlt 26 | } 27 | 28 | defineTest(minQtVersion) { 29 | maj = $$1 30 | min = $$2 31 | patch = $$3 32 | isEqual(QT_MAJOR_VERSION, $$maj) { 33 | isEqual(QT_MINOR_VERSION, $$min) { 34 | isEqual(QT_PATCH_VERSION, $$patch) { 35 | return(true) 36 | } 37 | greaterThan(QT_PATCH_VERSION, $$patch) { 38 | return(true) 39 | } 40 | } 41 | greaterThan(QT_MINOR_VERSION, $$min) { 42 | return(true) 43 | } 44 | } 45 | greaterThan(QT_MAJOR_VERSION, $$maj) { 46 | return(true) 47 | } 48 | return(false) 49 | } 50 | -------------------------------------------------------------------------------- /demo/examples/filter-banner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 34 | 35 | 36 | 37 | 93 | 94 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /demo/examples/filter-demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /demo/examples/filter-demo.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | Rectangle { 4 | width: 576; 5 | height: 432 6 | color: "lightgray" 7 | 8 | Component.onCompleted: { 9 | webvfx.imageTypeMap = { "sourceImage" : webvfx.SourceImageType }; 10 | webvfx.readyRender(true); 11 | } 12 | 13 | Image { 14 | id: image 15 | width: parent.width 16 | height: parent.height 17 | clip: true 18 | } 19 | Text { 20 | id: timeText 21 | anchors.verticalCenter: parent.verticalCenter 22 | anchors.horizontalCenter: parent.horizontalCenter 23 | font.pointSize: 24 24 | font.bold: true 25 | } 26 | Connections { 27 | target: webvfx 28 | onRenderRequested: { 29 | image.source = webvfx.getImageUrl("sourceImage"); 30 | image.rotation = time * 360; 31 | timeText.text = "rotating " + Math.round(image.rotation); 32 | timeText.rotation = -time * 360; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /demo/examples/filter-plain.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 21 | 22 | 23 | Plain HTML, does not call webvfx.readyRender
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /demo/examples/logo-transition.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mltframework/webvfx/a61f116b966a43ec7cb28209e84a77a1701e4b47/demo/examples/logo-transition.blend -------------------------------------------------------------------------------- /demo/examples/logo-transition.mtl: -------------------------------------------------------------------------------- 1 | # Blender MTL File: 'logo-transition.blend' 2 | # Material Count: 2 3 | newmtl Cube 4 | Ns 96.078431 5 | Ka 1.000000 1.000000 1.000000 6 | Kd 0.640000 0.640000 0.640000 7 | Ks 0.500000 0.500000 0.500000 8 | Ni 1.000000 9 | d 1.000000 10 | illum 2 11 | 12 | 13 | newmtl Material 14 | Ns 96.078431 15 | Ka 0.100000 0.100000 0.100000 16 | Kd 0.640000 0.283344 0.091671 17 | Ks 0.500000 0.500000 0.500000 18 | Ni 1.000000 19 | d 1.000000 20 | illum 2 21 | 22 | 23 | -------------------------------------------------------------------------------- /demo/examples/producer-demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 29 | 54 | 55 | 56 |
57 |
58 |
59 | 60 | 61 | -------------------------------------------------------------------------------- /demo/examples/transition-demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32 | 77 | 78 | 79 | 80 | 81 | 82 |
title
83 | 84 | 85 | -------------------------------------------------------------------------------- /demo/examples/transition-shader-crosszoom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | 17 | 18 | 69 | 70 | 71 | 72 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /demo/examples/transition-shader-glslio.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | 17 | 18 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /demo/examples/transition-shader-pagecurl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | 17 | 18 | 58 | 59 | 60 | 66 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | -------------------------------------------------------------------------------- /demo/mlt/blue.mlt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | producer 5 | color 6 | blue 7 | 1 8 | #timecode# 9 | 10 | filter 11 | data_show 12 | 1 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /demo/mlt/consumer_av: -------------------------------------------------------------------------------- 1 | -consumer avformat target=\"${VFX_OUTPUT:-melt.mov}\" f=mov vcodec=rawvideo pix_fmt=uyvy422 vtag=yuvs an=1 rescale=lanczos frame_rate_num=30 frame_rate_den=1 width=640 height=360 display_aspect_num=640 display_aspect_den=360 progressive=1 sample_aspect_num=1 sample_aspect_den=1 2 | -------------------------------------------------------------------------------- /demo/mlt/consumer_sdl: -------------------------------------------------------------------------------- 1 | -consumer sdl audio_off=1 frame_rate_num=30 frame_rate_den=1 width=640 height=360 display_aspect_num=640 display_aspect_den=360 progressive=1 sample_aspect_num=1 sample_aspect_den=1 2 | -------------------------------------------------------------------------------- /demo/mlt/green.mlt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | producer 5 | color 6 | green 7 | 1 8 | #timecode# 9 | 10 | filter 11 | data_show 12 | 1 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /demo/mlt/mlt_filter_banner_html: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export DISPLAY=${DISPLAY:-:0} 4 | "${VFX_MELT:-melt}" -verbose "${VFX_SOURCE:-red.mlt}" out=299 \ 5 | -filter webvfx:../examples/filter-banner.html out=299 \ 6 | Title="${VFX_TITLE:-WebVfx Banner Demo}" \ 7 | $(eval echo $(< "${VFX_CONSUMER:-consumer_sdl}")) 8 | -------------------------------------------------------------------------------- /demo/mlt/mlt_filter_demo_html: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export DISPLAY=${DISPLAY:-:0} 4 | "${VFX_MELT:-melt}" -verbose "${VFX_SOURCE:-red.mlt}" out=299 \ 5 | -filter webvfx:../examples/filter-demo.html out=299 \ 6 | $(eval echo $(< "${VFX_CONSUMER:-consumer_sdl}")) 7 | -------------------------------------------------------------------------------- /demo/mlt/mlt_filter_demo_qml: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export DISPLAY=${DISPLAY:-:0} 4 | "${VFX_MELT:-melt}" -verbose "${VFX_SOURCE:-red.mlt}" out=299 \ 5 | -filter webvfx:../examples/filter-demo.qml out=299 \ 6 | $(eval echo $(< "${VFX_CONSUMER:-consumer_sdl}")) 7 | -------------------------------------------------------------------------------- /demo/mlt/mlt_filter_plain_html: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export DISPLAY=${DISPLAY:-:0} 4 | "${VFX_MELT:-melt}" -verbose "${VFX_SOURCE:-red.mlt}" out=299 \ 5 | -filter webvfx:plain:../examples/filter-plain.html out=299 \ 6 | $(eval echo $(< "${VFX_CONSUMER:-consumer_sdl}")) 7 | -------------------------------------------------------------------------------- /demo/mlt/mlt_producer_demo_html: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export DISPLAY=${DISPLAY:-:0} 4 | "${VFX_MELT:-melt}" -verbose \ 5 | -producer webvfx:../examples/producer-demo.html length=199 \ 6 | title="${VFX_TITLE:-WebVfx Producer Demo}" \ 7 | $(eval echo $(< "${VFX_CONSUMER:-consumer_sdl}")) 8 | -------------------------------------------------------------------------------- /demo/mlt/mlt_producer_plain_html: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export DISPLAY=${DISPLAY:-:0} 4 | "${VFX_MELT:-melt}" -verbose \ 5 | -producer webvfx:plain:http://bl.ocks.org/mbostock/raw/1353700/ length=199 \ 6 | $(eval echo $(< "${VFX_CONSUMER:-consumer_sdl}")) 7 | -------------------------------------------------------------------------------- /demo/mlt/mlt_transition_demo3d_qml: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export DISPLAY=${DISPLAY:-:0} 4 | "${VFX_MELT:-melt}" -verbose "${VFX_SOURCE:-red.mlt}" out=349 \ 5 | -track -blank 69 \ 6 | "${VFX_TARGET:-blue.mlt}" out=349 \ 7 | -transition webvfx:../examples/transition-demo3d.qml in=70 out=349 \ 8 | title="${VFX_TITLE:-Hello 3D!}" \ 9 | $(eval echo $(< "${VFX_CONSUMER:-consumer_sdl}")) 10 | -------------------------------------------------------------------------------- /demo/mlt/mlt_transition_demo_html: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export DISPLAY=${DISPLAY:-:0} 4 | "${VFX_MELT:-melt}" -verbose "${VFX_SOURCE:-red.mlt}" out=149 \ 5 | -track -blank 59 \ 6 | "${VFX_TARGET:-blue.mlt}" out=149 \ 7 | -transition webvfx:../examples/transition-demo.html in=60 out=149 \ 8 | producer.backgroundImage.resource="${VFX_BACKGROUND:-green.mlt}" \ 9 | title="${VFX_TITLE:-Testing WebVfx Transition}" \ 10 | $(eval echo $(< "${VFX_CONSUMER:-consumer_sdl}")) 11 | -------------------------------------------------------------------------------- /demo/mlt/mlt_transition_shader_crosszoom_html: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export DISPLAY=${DISPLAY:-:0} 4 | "${VFX_MELT:-melt}" -verbose "${VFX_SOURCE:-red.mlt}" out=349 \ 5 | -track -blank 69 \ 6 | "${VFX_TARGET:-blue.mlt}" out=349 \ 7 | -transition webvfx:../examples/transition-shader-crosszoom.html in=70 out=349 \ 8 | Strength=0.3 \ 9 | $(eval echo $(< "${VFX_CONSUMER:-consumer_sdl}")) 10 | -------------------------------------------------------------------------------- /demo/mlt/mlt_transition_shader_glslio_html: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export DISPLAY=${DISPLAY:-:0} 4 | "${VFX_MELT:-qmelt}" -verbose "${VFX_SOURCE:-red.mlt}" out=349 \ 5 | -track -blank 69 \ 6 | "${VFX_TARGET:-blue.mlt}" out=349 \ 7 | -transition webvfx:../examples/transition-shader-glslio.html in=70 out=349 \ 8 | glslio_name=swap \ 9 | $(eval echo $(< "${VFX_CONSUMER:-consumer_sdl}")) 10 | -------------------------------------------------------------------------------- /demo/mlt/mlt_transition_shader_pagecurl_html: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export DISPLAY=${DISPLAY:-:0} 4 | "${VFX_MELT:-melt}" -verbose "${VFX_SOURCE:-red.mlt}" out=349 \ 5 | -track -blank 69 \ 6 | "${VFX_TARGET:-blue.mlt}" out=349 \ 7 | -transition webvfx:../examples/transition-shader-pagecurl.html in=70 out=349 \ 8 | $(eval echo $(< "${VFX_CONSUMER:-consumer_sdl}")) 9 | -------------------------------------------------------------------------------- /demo/mlt/red.mlt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | producer 5 | color 6 | red 7 | 1 8 | #timecode# 9 | 10 | filter 11 | data_show 12 | 1 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /doc/effects_3d.dox: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | /*! 5 | @page effects_3d 3D Effects Authoring 6 | 7 | QML is more interesting as a video effects technology when it 8 | is extended with 9 | QtQuick3D. 10 | 11 | Textured 3D scenes can be modeled in a tool like 12 | Blender. 13 | This can be exported as a Wavefront *.obj file which 14 | the QtQuick3D asset importer can load. 15 | Then frames of video can be applied as textures to named pieces of the model, 16 | while the camera view is animated for the duration of the effect. 17 | 18 | If your QML imports the @c org.webvfx.WebVfx namespace, this will 19 | make available some additional useful QML elements. 20 | @code 21 | import org.webvfx.WebVfx 1.0 22 | @endcode 23 | 24 | @li @c BlenderItem - this is an 25 | @c Item3D 26 | that applies a coordinate system 27 | transformation to convert from Blenders coordinate system to QtQuick3D. 28 | If the mesh was exported from Blender, you should load it inside 29 | a @c BlenderItem instead of an ordinary @c Item3D. 30 | @li @c AnimatedCamera - this is a @c Camera 31 | that supports an additional @c animationData property. 32 | This property should be set to the JSON camera path data exported 33 | by the Blender addon in @c tools/blender/webvfx-camera.py 34 | @li @c TextTexture - this is a @c Text 35 | element that renders itself to an image which can then be used 36 | as a 3D texture. The texture image is accessed using the @c textureImage 37 | property. 38 | 39 | See @ref examples/transition-demo3d.qml "demo/examples/transition-demo3d.qml" 40 | for an example. 41 | */ -------------------------------------------------------------------------------- /doc/effects_authoring.dox: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | /*! 5 | @page effects_authoring Effects Authoring 6 | 7 | %WebVfx supports both @ref qml_effects_authoring and 8 | @ref web_effects_authoring for developing video effects. 9 | Both have a lot in common. 10 | %WebVfx loads the effect content (HTML or QML) and exposes a 11 | JavaScript context object named @c webvfx to the effect implementation. 12 | 13 | If the effect will need to access frames of video, 14 | it must set the @c webvfx.imageTypeMap property to a map 15 | describing the names it will use for each video source. 16 | 17 | @anchor image_types 18 | Each name should be mapped to one of the enumerations: 19 | 20 | @li @c webvfx.SourceImageType 21 | Indicates the image name is the source image of a transition 22 | (the image being transitioned from), or the primary image of a filter. 23 | @li @c webvfx.TargetImageType 24 | Indicates the image name is the target image of a transition 25 | (the image being transitioned to). 26 | @li @c webvfx.ExtraImageType 27 | Indicates the image name is an extra asset. 28 | There can be multiple image names with this type. 29 | 30 | For example: 31 | @code 32 | webvfx.imageTypeMap = { "sourceImage" : webvfx.SourceImageType, 33 | "targetImage" : webvfx.TargetImageType } 34 | @endcode 35 | 36 | The effect can request additional named parameters as part of initialization 37 | by calling @c webvfx.getStringParameter(name) or 38 | @c webvfx.getNumberParameter(name) 39 | 40 | The effect must connect the @c webvfx.renderRequested(time) signal. 41 | See @ref qml_effects_authoring or @ref web_effects_authoring for 42 | how to connect to this signal. 43 | 44 | When the effect has fully loaded (including any external resources 45 | being loaded asynchronously), it should call: 46 | @code 47 | webvfx.readyRender(true) 48 | @endcode 49 | If the load failed for any reason, pass @c false instead. 50 | 51 | Now %WebVfx will start rendering frames of video. It will pull the current 52 | frame from each of the video sources specified in @c webvfx.imageTypeMap 53 | then invoke the @c webvfx.renderRequested(time) signal. 54 | The time is a normalized time from 0 to 1.0 indicating the position 55 | in the transition or effect. The effect should then request any images 56 | it specified in @c webvfx.imageTypeMap each time it handles 57 | @c renderRequested. Images can be requested by calling 58 | @c webvfx.getImage(name) where @c name is the string image name specified 59 | in @c imageTypeMap. See @ref qml_effects_authoring or 60 | @ref web_effects_authoring for how to use the returned image object. 61 | 62 | The effect should configure itself using the @c time value and the 63 | images it retrieved before returning from the @c renderRequested slot. 64 | 65 | 66 | @section qml_effects_authoring QML Effects Authoring 67 | 68 | Effects can be authored using Qt Quick 69 | QML, 70 | a declarative UI language. 71 | 72 | The @c webvfx.renderRequested(time) signal can be handled using 73 | the QML 74 | Connections 75 | element with @c webvfx as the target. 76 | The @c time parameter is available to the code, e.g.: 77 | @code 78 | Connections { 79 | target: webvfx 80 | onRenderRequested: { 81 | effect.textureImage = webvfx.getImage("sourceImage"); 82 | console.log("render: " + time); 83 | } 84 | } 85 | @endcode 86 | 87 | Video frame images retrieved via @c webvfx.getImage(name) are QImage 88 | objects. These can be assigned directly to some QML properties, 89 | e.g. Effect.textureImage. 90 | Other QML properties require an image URL - this can be retrived via 91 | @c webvfx.getImageUrl(name). It is more efficient to use the image 92 | directly when possible, instead of the URL. 93 | 94 | @sa See the QML demo 95 | @ref examples/filter-demo.qml "demo/examples/filter-demo.qml" 96 | 97 | QML is more interesting as a video effects technology when it 98 | is extended with 3D support - see @ref effects_3d. 99 | 100 | @section web_effects_authoring Web (HTML) Effects Authoring 101 | 102 | Effects can be authored using 103 | Qt WebKit Widgets 104 | HTML. 105 | 106 | The @c webvfx.renderRequested(time) signal can be handled by connecting 107 | it to a JavaScript function that takes a @c time parameter, using 108 | @c webvfx.renderRequested.connect: 109 | @code 110 | function render(time) { 111 | console.log("render: " + time); 112 | } 113 | webvfx.renderRequested.connect(render); 114 | @endcode 115 | 116 | @c webvfx.getImage(name) returns a JavaScript image proxy object 117 | for the current frame of video for the named image. 118 | This must be assigned to a DOM @c Image element so that it can be 119 | used in the HTML. 120 | The Qt WebKit Bridge 121 | provides a method @c assignToHTMLImageElement() to do this. 122 | You can assign to a new @c Image: 123 | @code 124 | var image = new Image() 125 | webvfx.getImage("sourceImage").assignToHTMLImageElement(image); 126 | @endcode 127 | or reference an existing one in the DOM 128 | @code 129 | 130 | [...] 131 | webvfx.getImage("sourceImage").assignToHTMLImageElement(document.getElementById("image")); 132 | @endcode 133 | 134 | @sa See the HTML demos: 135 | @li @ref examples/producer-demo.html "demo/examples/producer-demo.html" 136 | @li @ref examples/filter-demo.html "demo/examples/filter-demo.html" 137 | @li @ref examples/transition-demo.html "demo/examples/transition-demo.html" 138 | 139 | 140 | @subsection web_effects_shader_authoring WebGL GLSL Shader Effects Authoring 141 | 142 | %WebVfx includes a simple framework for implementing 2D GLSL fragment shader 143 | effects. This requires Qt WebKit to be compiled with WebGL enabled. 144 | A recent build should be used so the @c toImageData feature is available. 145 | 146 | The HTML effect should reference the @c shader.js JavaScript resource: 147 | @code 148 | 149 | @endcode 150 | The GLSL code can be placed in a @c script element: 151 | @code 152 | 153 | @endcode 154 | 155 | The GLSL must declare a varying @c texCoord which carries the texture 156 | coordinates from the vertex shader. 157 | @code 158 | varying vec2 texCoord; 159 | @endcode 160 | It should also declare any uniforms it uses. 161 | Uniform values can be set on each render cycle from JavaScript 162 | using @c updateUniform(name,value). 163 | If the uniform is a sampler2D texture, it should use the @c ImageData 164 | returned from the %WebVfx image object: 165 | @code 166 | shader.updateUniform("sourceTex", webvfx.getImage("sourceImage").toImageData()); 167 | @endcode 168 | 169 | See the sample CrossZoom and PageCurl shaders for complete examples: 170 | @li @ref examples/transition-shader-crosszoom.html "demo/examples/transition-shader-crosszoom.html" 171 | @li @ref examples/transition-shader-pagecurl.html "demo/examples/transition-shader-pagecurl.html" 172 | 173 | 174 | @section effects_authoring_tools Tools 175 | 176 | A couple of simple tools are provided to help authoring effects. 177 | 178 | @c webvfx_browser (%WebVfx Browser.app on MacOS) is a trivial 179 | wrapper around QtWebKit. This makes it easy to visit any website 180 | and see if the version of QtWebKit you are using supports various 181 | HTML features. 182 | 183 | @c webvfx_viewer (%WebVfx Viewer.app on MacOS) allows you to 184 | load your HTML or QML effects and exposes the @c webvfx context object 185 | to them, and generates images that your effect can request using 186 | @c webvfx.getImage(name). 187 | It has a slider along the bottom that lets you control the rendering time 188 | (0..1.0) and a tab to let you set the rendering size. 189 | */ 190 | -------------------------------------------------------------------------------- /doc/examples.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | @example examples/producer-demo.html 3 | This is an example MLT producer implemented in HTML. 4 | It renders a user settable @em title parameter 5 | with an animated CSS shadow effect. 6 | The rendered video can be viewed here 7 | demo/mlt/mlt_producer_demo_html. 8 | 9 | @example examples/filter-demo.html 10 | This is an example MLT filter implemented in HTML. 11 | It renders the video into an HTML canvas with a circle clip applied. 12 | The rendered video can be viewed here 13 | demo/mlt/mlt_filter_demo_html. 14 | 15 | @example examples/transition-demo.html 16 | This is an example MLT transition implemented in HTML. 17 | It uses all three @ref image_types "image types" 18 | and a string @em title parameter. 19 | The rendered video can be viewed here 20 | demo/mlt/mlt_transition_demo_html 21 | 22 | @example examples/transition-shader-crosszoom.html 23 | This is an example MLT transition implemented in HTML. 24 | It uses the %WebVfx WebGL shader support framework to implement 25 | a 2D transition using GLSL. 26 | The rendered video can be viewed here 27 | demo/mlt/mlt_transition_shader_crosszoom_html 28 | 29 | @example examples/transition-shader-pagecurl.html 30 | This is an example MLT transition implemented in HTML. 31 | It uses the %WebVfx WebGL shader support framework to implement 32 | a 2D page curl transition using GLSL. 33 | The rendered video can be viewed here 34 | demo/mlt/mlt_transition_shader_pagecurl_html 35 | 36 | @example examples/filter-demo.qml 37 | This is an example MLT filter implemented in QML. 38 | It rotates the video in one direction while rotating 39 | some changing text in the other direction. 40 | The rendered video can be viewed here 41 | demo/mlt/mlt_filter_demo_qml 42 | 43 | @example examples/transition-demo3d.qml 44 | This is an example MLT transition implemented in QML with QtQuick3D. 45 | It animates the camera along a path from the source video texture 46 | to the target video texture. 47 | It also textures a string @em title parameter onto a surface in the model. 48 | The rendered video can be viewed here 49 | demo/mlt/mlt_transition_demo3d_qml 50 | 51 | */ 52 | -------------------------------------------------------------------------------- /doc/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /doc/license.dox: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | /*! 5 | @page license License 6 | 7 | @verbinclude LICENSE 8 | 9 | */ -------------------------------------------------------------------------------- /doc/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mltframework/webvfx/a61f116b966a43ec7cb28209e84a77a1701e4b47/doc/logo.png -------------------------------------------------------------------------------- /doc/logo.xcf.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mltframework/webvfx/a61f116b966a43ec7cb28209e84a77a1701e4b47/doc/logo.xcf.gz -------------------------------------------------------------------------------- /doc/mainpage.dox: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | /*! 5 | @mainpage 6 | 7 | %WebVfx is a video effects framework that allows video effects 8 | (filters, transitions etc.) to be authored using web technologies 9 | (HTML, CSS, JavaScript, WebGL etc.) or with 10 | Qt Quick 11 | QML (a declarative CSS and JavaScript like language). 12 | See @ref effects_authoring for documentation and samples. 13 | 14 | %WebVfx includes plugins for the 15 | MLT 16 | open source multimedia framework. 17 | See @ref mlt for information on how to use %WebVfx with MLT. 18 | 19 | Sample effects included with the distribution, and videos 20 | rendered using them with MLT can be viewed here: 21 | @li @ref examples/transition-demo3d.qml "demo/examples/transition-demo3d.qml" 22 | @li @ref examples/transition-demo.html "demo/examples/transition-demo.html" 23 | @li @ref examples/transition-shader-crosszoom.html "demo/examples/transition-shader-crosszoom.html" 24 | @li @ref examples/transition-shader-pagecurl.html "demo/examples/transition-shader-pagecurl.html" 25 | @li @ref examples/producer-demo.html "demo/examples/producer-demo.html" 26 | @li @ref examples/filter-demo.html "demo/examples/filter-demo.html" 27 | @li @ref examples/filter-demo.qml "demo/examples/filter-demo.qml" 28 | 29 | %WebVfx is hosted at github, 30 | see the README 31 | there for build instructions. 32 | 33 | %WebVfx is licensed using a BSD-style @ref license. 34 | */ 35 | -------------------------------------------------------------------------------- /doc/mlt.dox: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | /*! 5 | @page mlt MLT Plugins 6 | 7 | %WebVfx implements producer, filter and transition plugin services for the 8 | MLT Framework. 9 | The MLT service ID is @c webvfx and the service argument 10 | or @c resource property is the path to the HTML or QML effect implementation. 11 | 12 | MLT services are implemented as %WebVfx effects - see @ref effects_authoring. 13 | The pathname to the HTML or QML effects implementation can be provided 14 | as the service contructor argument, or specified using the @c resource 15 | property. e.g. 16 | @code 17 | melt -transition webvfx:/path/to/transition.html 18 | @endcode 19 | @code 20 | melt -transition webvfx resource=/path/to/transition.html 21 | @endcode 22 | 23 | @section mlt_panzoom Panzoom Producer Plugin 24 | 25 | %WebVfx also provides an image producer that implements a pan and zoom effect. 26 | This is similar to the MLT @c affine filter, but more efficient. 27 | The MLT service ID is @c webvfx.panzoom and the @c resource property 28 | is the source image to load. 29 | The @c geometry property holds the MLT geometry keyframe specifications. 30 | 31 | @section mlt_extra Extra Images 32 | 33 | Effects that use extra images (i.e. that register @c webvfx.ExtraImageType 34 | image names) accept an additional set of attributes of the form: 35 | producer.name.producer_attribute. 36 | @li @em name - Name of the extra image. This is the name that was 37 | tagged with @c webvfx.ExtraImageType. 38 | @li @em producer_attribute - Any valid producer attribute. 39 | At a minimum the @c resource attribute must be specified. 40 | @c in and @c out can also be specified (e.g. to extract a single 41 | frame from a video). e.g. for an extra image named @c "foobar" 42 | we might have: 43 | @code 44 | producer.foobar.resource=/some/file.mov 45 | producer.foobar.in=90 46 | producer.foobar.out=200 47 | @endcode 48 | 49 | The example 50 | @ref examples/transition-demo.html "demo/examples/transition-demo.html" 51 | transition uses an 52 | extra image named @c "backgroundImage": 53 | @dontinclude transition-demo.html 54 | @skip webvfx.imageTypeMap 55 | @until }; 56 | 57 | The melt script example demo/mlt/mlt_transition_demo_html specifies 58 | the @c producer.backgroundImage.resource attribute for the transition: 59 | @include mlt_transition_demo_html 60 | 61 | @section mlt_demos Demos 62 | 63 | A set of bash scripts are provided to run @em melt with the sample 64 | effects. These are located in the @c demo/mlt directory and should 65 | be run in that directory. 66 | They are configurable through @c VFX_ prefixed environment variables. 67 | e.g. to run the @c mlt_transition_demo_html script, you can change 68 | the default source and target videos using @c VFX_SOURCE and @c VFX_TARGET. 69 | @code 70 | VFX_SOURCE=/videos/source.mov VFX_TARGET=/videos/target.mov ./mlt_transition_demo_html 71 | @endcode 72 | The demos default to using the SDL consumer, this can be changed to the avformat 73 | consumer by setting @c VFX_CONSUMER=consumer_av and setting @c VFX_OUTPUT 74 | to the path of the output QuickTime movie file to be created. 75 | 76 | On MacOS, you will need to build @c qmelt and set @c VFX_MELT=qmelt 77 | to run the demos. 78 | 79 | @htmlonly 80 | Each of the demos has been rendered as a video and can be previewed 81 | from the corresponding example. 82 | @endhtmlonly 83 | */ -------------------------------------------------------------------------------- /mlt/factory.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | extern "C" { 9 | #include 10 | #include 11 | #include 12 | } 13 | #include "factory.h" 14 | 15 | namespace MLTWebVfx 16 | { 17 | class Logger : public WebVfx::Logger 18 | { 19 | void log(const QString& message) { 20 | //XXX use log level once passed 21 | //XXX any way to get service into here? 22 | mlt_log(NULL, MLT_LOG_INFO, "%s\n", message.toLatin1().constData()); 23 | } 24 | }; 25 | } 26 | 27 | static void* createService(mlt_profile profile, 28 | mlt_service_type serviceType, 29 | const char*, const void* fileName) 30 | { 31 | if (!WebVfx::initialize()) 32 | return 0; 33 | 34 | mlt_service service = 0; 35 | switch (serviceType) { 36 | case producer_type: 37 | service = MLTWebVfx::createProducer(profile); 38 | break; 39 | case filter_type: 40 | service = MLTWebVfx::createFilter(); 41 | break; 42 | case transition_type: 43 | service = MLTWebVfx::createTransition(); 44 | break; 45 | default: 46 | return 0; 47 | break; 48 | } 49 | 50 | if (fileName) { 51 | mlt_properties_set(MLT_SERVICE_PROPERTIES(service), 52 | "resource", static_cast(fileName)); 53 | } 54 | 55 | return service; 56 | } 57 | 58 | #if defined(__GNUC__) && __GNUC__ >= 4 59 | #define EXPORT __attribute__((visibility ("default"))) 60 | #else 61 | #define EXPORT 62 | #endif 63 | 64 | extern "C" EXPORT MLT_REPOSITORY 65 | { 66 | // Prevent ourself from being unloaded (dlclose) when MLT shuts down. 67 | // Some pieces of QtWebKit (e.g. WebWorkers) live past event loop exit 68 | // and cause a crash if we are unloaded from memory. 69 | // See https://bugs.webkit.org/show_bug.cgi?id=72538 70 | QLibrary lib("libmltwebvfx"); 71 | lib.load(); 72 | 73 | MLT_REGISTER(producer_type, "webvfx", createService); 74 | MLT_REGISTER(filter_type, "webvfx", createService); 75 | MLT_REGISTER(transition_type, "webvfx", createService); 76 | 77 | MLT_REGISTER(producer_type, "webvfx.panzoom", MLTWebVfx::createPanzoomProducer); 78 | 79 | // Register shutdown hook - even if we don't initialize WebVfx 80 | // we want our logger deleted. 81 | mlt_factory_register_for_clean_up(0, reinterpret_cast(WebVfx::shutdown)); 82 | WebVfx::setLogger(new MLTWebVfx::Logger()); 83 | } 84 | -------------------------------------------------------------------------------- /mlt/factory.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef MLTWEBVFX_FACTORY_H_ 6 | #define MLTWEBVFX_FACTORY_H_ 7 | 8 | extern "C" { 9 | #include 10 | #include 11 | } 12 | 13 | namespace MLTWebVfx 14 | { 15 | 16 | mlt_service createProducer(mlt_profile profile); 17 | mlt_service createFilter(); 18 | mlt_service createTransition(); 19 | 20 | void* createPanzoomProducer(mlt_profile profile, mlt_service_type type, const char* id, const void* fileName); 21 | 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /mlt/mlt.pro: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | include(../common.pri) 6 | TEMPLATE = lib 7 | 8 | HEADERS += factory.h 9 | HEADERS += service_locker.h 10 | HEADERS += service_manager.h 11 | 12 | SOURCES += factory.cpp 13 | SOURCES += panzoom_producer.cpp 14 | SOURCES += service_locker.cpp 15 | SOURCES += service_manager.cpp 16 | SOURCES += webvfx_filter.cpp 17 | SOURCES += webvfx_producer.cpp 18 | SOURCES += webvfx_transition.cpp 19 | 20 | CONFIG += plugin shared 21 | 22 | TARGET = mltwebvfx 23 | 24 | LIBS += -L$$DESTDIR -lwebvfx 25 | 26 | macx:isEqual(QT_MAJOR_VERSION, 5) { 27 | # QMake from Qt 5.1.0 on OSX is messing with the environment in which it runs 28 | # pkg-config such that the PKG_CONFIG_PATH env var is not set. 29 | isEmpty(MLT_PREFIX) { 30 | MLT_PREFIX = /opt/local 31 | } 32 | INCLUDEPATH += $$MLT_PREFIX/include 33 | INCLUDEPATH += $$MLT_PREFIX/include/mlt 34 | LIBS += -L$$MLT_PREFIX/lib -lmlt 35 | } else { 36 | CONFIG += link_pkgconfig 37 | PKGCONFIG += mlt-framework 38 | } 39 | win32 { 40 | QT += opengl quick 41 | isEqual(QT_MAJOR_VERSION, 5) { 42 | QT += webkitwidgets 43 | } else { 44 | QT += webkit 45 | } 46 | LIBS += -lglu32 -lopengl32 -lpthread 47 | } 48 | 49 | QMAKE_RPATHDIR += $$PREFIX/lib 50 | 51 | # Install in mlt plugins directory 52 | target.path = $$system(pkg-config --variable=libdir mlt-framework)/mlt 53 | INSTALLS += target 54 | # Add mlt plugins to rpath so we can dlopen ourself without a full path. 55 | QMAKE_RPATHDIR += $$target.path 56 | -------------------------------------------------------------------------------- /mlt/panzoom_producer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | extern "C" { 6 | #include 7 | #include 8 | #include 9 | #include 10 | } 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "factory.h" 17 | 18 | static const char* kPanzoomFilenamePropertyName = "webvfx.panzoom.filename"; 19 | static const char* kPanzoomProducerPropertyName = "WebVfxPanzoomProducer"; 20 | static const char* kPanzoomPositionPropertyName = "webvfx.panzoom.position"; 21 | static const char* kPanzoomQImagePropertyName = "webvfx.panzoom.QImage"; 22 | static const char* kPanzoomGeometryPropertyName = "webvfx.panzoom.Geometry"; 23 | 24 | 25 | static void destroyQImage(QImage* image) { 26 | delete image; 27 | } 28 | 29 | static QSize computeMaxSize(mlt_geometry geometry) { 30 | QSize size(0, 0); 31 | struct mlt_geometry_item_s item; 32 | int position = 0; 33 | while (!mlt_geometry_next_key(geometry, &item, position)) { 34 | if (item.w > size.width()) 35 | size.setWidth(item.w); 36 | if (item.h > size.height()) 37 | size.setHeight(item.h); 38 | position = item.frame + 1; 39 | } 40 | return size; 41 | } 42 | 43 | static mlt_geometry getGeometry(mlt_producer producer, mlt_properties frameProperties) { 44 | mlt_properties producerProperties = MLT_PRODUCER_PROPERTIES(producer); 45 | mlt_geometry geometry = static_cast(mlt_properties_get_data(producerProperties, kPanzoomGeometryPropertyName, NULL)); 46 | if (!geometry) { 47 | int nwidth = mlt_properties_get_int(frameProperties, "normalised_width"); 48 | int nheight = mlt_properties_get_int(frameProperties, "normalised_height"); 49 | char* spec = mlt_properties_get(producerProperties, "geometry"); 50 | geometry = mlt_geometry_init(); 51 | mlt_geometry_parse(geometry, spec, mlt_producer_get_length(producer), 52 | nwidth, nheight); 53 | mlt_properties_set_data(producerProperties, 54 | kPanzoomGeometryPropertyName, geometry, 0, 55 | reinterpret_cast(mlt_geometry_close), NULL); 56 | } 57 | return geometry; 58 | } 59 | 60 | static QImage* getPrescaledSourceImage(mlt_producer producer, mlt_geometry geometry) { 61 | mlt_properties producerProperties = MLT_PRODUCER_PROPERTIES(producer); 62 | QImage* image = static_cast(mlt_properties_get_data(producerProperties, kPanzoomQImagePropertyName, NULL)); 63 | if (!image) { 64 | const char* fileName = mlt_properties_get(producerProperties, kPanzoomFilenamePropertyName); 65 | if (!fileName) 66 | fileName = mlt_properties_get(producerProperties, "resource"); 67 | 68 | image = new QImage(fileName); 69 | if (image) { 70 | mlt_properties_set_data(producerProperties, kPanzoomQImagePropertyName, image, 0, reinterpret_cast(destroyQImage), NULL); 71 | if (image->isNull()) { 72 | QImageReader reader(fileName); 73 | reader.setDecideFormatFromContent(true); 74 | *image = reader.read(); 75 | if (image->isNull()) { 76 | mlt_log(MLT_PRODUCER_SERVICE(producer), MLT_LOG_ERROR, 77 | "Failed to load QImage '%s'\n", fileName); 78 | return NULL; 79 | } 80 | } 81 | 82 | // Prescale image down to max size needed 83 | QSize maxSize(computeMaxSize(geometry)); 84 | QSize imageSize(image->size()); 85 | if (maxSize.width() < imageSize.width() && maxSize.height() < imageSize.height()) 86 | *image = image->scaled(maxSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); 87 | } 88 | else { 89 | mlt_log(MLT_PRODUCER_SERVICE(producer), MLT_LOG_ERROR, 90 | "Failed to create QImage\n"); 91 | return NULL; 92 | } 93 | } 94 | return image; 95 | } 96 | 97 | static int producerGetImage(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int /*writable*/) { 98 | int error = 0; 99 | 100 | // Obtain properties of frame 101 | mlt_properties properties = MLT_FRAME_PROPERTIES(frame); 102 | 103 | // Obtain the producer for this frame 104 | mlt_producer producer = static_cast(mlt_properties_get_data(properties, kPanzoomProducerPropertyName, NULL)); 105 | 106 | // Allocate the image 107 | *format = mlt_image_rgb24; 108 | int size = *width * *height * 3; 109 | *buffer = (uint8_t*)mlt_pool_alloc(size); 110 | if (!*buffer) 111 | return 1; 112 | 113 | // Update the frame 114 | mlt_frame_set_image(frame, *buffer, size, mlt_pool_release); 115 | mlt_properties_set_int(properties, "width", *width); 116 | mlt_properties_set_int(properties, "height", *height); 117 | 118 | mlt_geometry geometry = getGeometry(producer, properties); 119 | QImage* image = getPrescaledSourceImage(producer, geometry); 120 | if (!image) 121 | return 1; 122 | 123 | struct mlt_geometry_item_s item; 124 | mlt_position position = mlt_properties_get_position(properties, kPanzoomPositionPropertyName); 125 | mlt_geometry_fetch(geometry, &item, position); 126 | 127 | // Compute scale to "meet" the geometry rect 128 | float scaleWidth = item.w / image->width(); 129 | float scaleHeight = item.h / image->height(); 130 | float scale = qMin(scaleWidth, scaleHeight); 131 | 132 | // If aspect ratio differs, need to center image 133 | if (scaleWidth > scaleHeight) 134 | item.x += (item.w - scaleHeight * image->width()) / 2.0; 135 | else if (scaleHeight > scaleWidth) 136 | item.y += (item.h - scaleWidth * image->height()) / 2.0; 137 | 138 | QImage targetImage(static_cast(*buffer), *width, *height, *width * 3, QImage::Format_RGB888); 139 | 140 | QTransform tx(QTransform::fromTranslate(item.x, item.y).scale(scale, scale)); 141 | 142 | // Clear buffer if image doesn't fill it 143 | QRect rect(tx.mapRect(image->rect())); 144 | if (!rect.contains(targetImage.rect())) 145 | memset(*buffer, 0, size); 146 | 147 | QPainter painter(&targetImage); 148 | painter.setTransform(tx); 149 | painter.setRenderHint(QPainter::SmoothPixmapTransform); 150 | painter.drawImage(QPointF(0, 0), *image); 151 | 152 | return error; 153 | } 154 | 155 | static int getFrame(mlt_producer producer, mlt_frame_ptr frame, int /*index*/) { 156 | // Generate a frame 157 | *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); 158 | 159 | if (*frame) { 160 | // Obtain properties of frame and producer 161 | mlt_properties properties = MLT_FRAME_PROPERTIES(*frame); 162 | 163 | // Obtain properties of producer 164 | mlt_properties producerProperties = MLT_PRODUCER_PROPERTIES(producer); 165 | 166 | // Set the producer on the frame properties 167 | mlt_properties_set_data(properties, kPanzoomProducerPropertyName, producer, 0, NULL, NULL); 168 | 169 | // Update timecode on the frame we're creating 170 | mlt_position position = mlt_producer_position(producer); 171 | mlt_frame_set_position(*frame, position); 172 | mlt_properties_set_position(properties, kPanzoomPositionPropertyName, position); 173 | 174 | // Set producer-specific frame properties 175 | mlt_properties_set_int(properties, "progressive", 1); 176 | mlt_properties_set_double(properties, "aspect_ratio", mlt_properties_get_double(producerProperties, "aspect_ratio")); 177 | 178 | // Push the get_image method 179 | mlt_frame_push_get_image(*frame, producerGetImage); 180 | } 181 | 182 | // Calculate the next timecode 183 | mlt_producer_prepare_next(producer); 184 | 185 | return 0; 186 | } 187 | 188 | void* MLTWebVfx::createPanzoomProducer(mlt_profile profile, mlt_service_type, const char*, const void* fileName) { 189 | mlt_producer self = mlt_producer_new(profile); 190 | if (self) { 191 | mlt_properties properties = MLT_PRODUCER_PROPERTIES(self); 192 | self->get_frame = getFrame; 193 | if (fileName) { 194 | mlt_properties_set(properties, kPanzoomFilenamePropertyName, 195 | static_cast(fileName)); 196 | } 197 | mlt_properties_set(properties, "geometry", "0/0:100%x100%"); 198 | } 199 | return self; 200 | } 201 | -------------------------------------------------------------------------------- /mlt/qmelt/qmelt.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #undef main 6 | 7 | #include 8 | #include 9 | 10 | extern "C" int melt_main(int argc, char** argv); 11 | 12 | class MeltThread : public QThread 13 | { 14 | public: 15 | MeltThread(int argc, char** argv) 16 | : QThread(), argc(argc), argv(argv) { 17 | } 18 | 19 | protected: 20 | void run() { 21 | qApp->exit(melt_main(argc, argv)); 22 | } 23 | 24 | private: 25 | int argc; 26 | char** argv; 27 | }; 28 | 29 | 30 | int main(int argc, char** argv) 31 | { 32 | QApplication app(argc, argv); 33 | MeltThread meltThread(argc, argv); 34 | meltThread.start(); 35 | return app.exec(); 36 | } 37 | -------------------------------------------------------------------------------- /mlt/qmelt/qmelt.pro: -------------------------------------------------------------------------------- 1 | include(../../common.pri) 2 | isEmpty(MLT_SOURCE) { 3 | error("qmelt can only be built when MLT_SOURCE is set to the MLT source code directory.") 4 | } 5 | 6 | TEMPLATE = app 7 | 8 | SOURCES += qmelt.cpp 9 | SOURCES += $$MLT_SOURCE/src/melt/melt.c 10 | SOURCES += $$MLT_SOURCE/src/melt/io.c 11 | 12 | DEFINES += main=melt_main 13 | DEFINES += VERSION=\\\"qmelt\\\" 14 | 15 | INCLUDEPATH += $$MLT_SOURCE 16 | 17 | CONFIG -= app_bundle 18 | 19 | macx:isEqual(QT_MAJOR_VERSION, 5) { 20 | # QMake from Qt 5.1.0 on OSX is messing with the environment in which it runs 21 | # pkg-config such that the PKG_CONFIG_PATH env var is not set. 22 | isEmpty(MLT_PREFIX) { 23 | MLT_PREFIX = /opt/local 24 | } 25 | INCLUDEPATH += $$MLT_PREFIX/include 26 | INCLUDEPATH += $$MLT_PREFIX/include/mlt 27 | LIBS += -L$$MLT_PREFIX/lib -lmlt 28 | } else { 29 | CONFIG += link_pkgconfig 30 | PKGCONFIG += mlt-framework 31 | } 32 | macx:DEFINES += MELT_NOSDL 33 | win32 { 34 | CONFIG += console 35 | DEFINES += MELT_NOSDL 36 | } 37 | 38 | isEqual(QT_MAJOR_VERSION, 5) { 39 | QT += widgets 40 | } 41 | 42 | TARGET = qmelt 43 | QMAKE_RPATHDIR += $$PREFIX/lib 44 | 45 | target.path = $$PREFIX/bin 46 | INSTALLS += target 47 | -------------------------------------------------------------------------------- /mlt/service_locker.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | extern "C" { 8 | #include 9 | } 10 | #include "service_locker.h" 11 | #include "service_manager.h" 12 | 13 | 14 | namespace MLTWebVfx 15 | { 16 | const char* ServiceLocker::kManagerPropertyName = "WebVfxManager"; 17 | 18 | 19 | ServiceLocker::ServiceLocker(mlt_service service) 20 | : service(service) 21 | { 22 | mlt_service_lock(service); 23 | } 24 | 25 | ServiceLocker::~ServiceLocker() 26 | { 27 | mlt_service_unlock(service); 28 | } 29 | 30 | bool ServiceLocker::initialize(int width, int height) 31 | { 32 | // If we don't have a ServiceManager, create one and store on service 33 | mlt_properties properties = MLT_SERVICE_PROPERTIES(service); 34 | manager = static_cast(mlt_properties_get_data(properties, ServiceLocker::kManagerPropertyName, 0)); 35 | if (!manager) { 36 | manager = new ServiceManager(service); 37 | bool result = manager->initialize(width, height); 38 | if (!result) { 39 | destroyManager(manager); 40 | mlt_log(service, MLT_LOG_ERROR, "Failed to create WebVfx ServiceManager\n"); 41 | return result; 42 | } 43 | 44 | mlt_properties_set_data(properties, kManagerPropertyName, manager, 0, reinterpret_cast(destroyManager), NULL); 45 | } 46 | return true; 47 | } 48 | 49 | ServiceManager* ServiceLocker::getManager() 50 | { 51 | return manager; 52 | } 53 | 54 | void ServiceLocker::destroyManager(ServiceManager* manager) 55 | { 56 | delete manager; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /mlt/service_locker.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef MLTWEBVFX_SERVICE_LOCKER_H_ 6 | #define MLTWEBVFX_SERVICE_LOCKER_H_ 7 | 8 | extern "C" { 9 | #include 10 | } 11 | 12 | 13 | namespace MLTWebVfx 14 | { 15 | class ServiceManager; 16 | 17 | class ServiceLocker 18 | { 19 | public: 20 | ServiceLocker(mlt_service service); 21 | ~ServiceLocker(); 22 | 23 | bool initialize(int width, int height); 24 | ServiceManager* getManager(); 25 | 26 | private: 27 | static const char* kManagerPropertyName; 28 | static void destroyManager(ServiceManager* manager); 29 | mlt_service service; 30 | ServiceManager* manager; 31 | }; 32 | 33 | } 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /mlt/service_manager.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef MLTWEBVFX_SERVICE_MANAGER_H_ 6 | #define MLTWEBVFX_SERVICE_MANAGER_H_ 7 | 8 | #include 9 | extern "C" { 10 | #include 11 | } 12 | #include 13 | 14 | 15 | namespace WebVfx 16 | { 17 | class Effects; 18 | class Image; 19 | } 20 | 21 | namespace MLTWebVfx 22 | { 23 | class ImageProducer; 24 | class ServiceLocker; 25 | class ServiceParameters; 26 | 27 | class ServiceManager 28 | { 29 | public: 30 | const QString& getSourceImageName() { return sourceImageName; } 31 | const QString& getTargetImageName() { return targetImageName; } 32 | void setImageForName(const QString& name, WebVfx::Image* image); 33 | int render(WebVfx::Image* outputImage, mlt_position position, 34 | mlt_position length, double zoom, bool hasAlpha = false); 35 | void setupConsumerListener(mlt_frame frame); 36 | void onConsumerStopping(); 37 | 38 | private: 39 | friend class ServiceLocker; 40 | ServiceManager(mlt_service service); 41 | ~ServiceManager(); 42 | bool initialize(int width, int height); 43 | 44 | mlt_service service; 45 | mlt_properties event; 46 | WebVfx::Effects* effects; 47 | ServiceParameters* parameters; 48 | 49 | QString sourceImageName; 50 | QString targetImageName; 51 | std::vector* imageProducers; 52 | }; 53 | 54 | } 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /mlt/webvfx_filter.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | extern "C" { 6 | #include 7 | #include 8 | #include 9 | #include 10 | } 11 | #include 12 | #include 13 | #include "factory.h" 14 | #include "service_locker.h" 15 | #include "service_manager.h" 16 | 17 | 18 | static int filterGetImage(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int /*writable*/) { 19 | int error = 0; 20 | 21 | // Get the filter 22 | mlt_filter filter = (mlt_filter)mlt_frame_pop_service(frame); 23 | mlt_properties properties = MLT_FILTER_PROPERTIES(filter); 24 | mlt_position position = mlt_filter_get_position(filter, frame); 25 | mlt_position length = mlt_filter_get_length2(filter, frame); 26 | 27 | // If not a plain resource, then disable preview scaling. 28 | const char* resource = mlt_properties_get(properties, "resource"); 29 | int use_preview_scale = mlt_properties_get_int(properties, "mlt_resolution_scale"); 30 | if (!use_preview_scale && resource) { 31 | mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); 32 | std::string resource2(resource); 33 | std::string plain = "plain:"; 34 | if (profile && resource2.substr(0, plain.size()) != plain) { 35 | *width = profile->width; 36 | *height = profile->height; 37 | } 38 | } 39 | 40 | // Get the source image, we will also write our output to it 41 | *format = mlt_image_rgb24a; 42 | if ((error = mlt_frame_get_image(frame, image, format, width, height, 1)) != 0) 43 | return error; 44 | 45 | // Add mlt_profile_scale_width and mlt_profile_scale_height properties for scripts. 46 | mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); 47 | double scale = mlt_profile_scale_width(profile, *width); 48 | mlt_properties_set_double(properties, "mlt_profile_scale_width", scale); 49 | mlt_properties_set_double(properties, "mlt_profile_scale_height", 50 | mlt_profile_scale_height(profile, *height)); 51 | 52 | { // Scope the lock 53 | MLTWebVfx::ServiceLocker locker(MLT_FILTER_SERVICE(filter)); 54 | if (!locker.initialize(*width, *height)) 55 | return 1; 56 | 57 | bool hasAlpha = (*format == mlt_image_rgb24a); 58 | std::unique_ptr sourceImage; 59 | std::unique_ptr renderedImage; 60 | uint8_t* buffer = nullptr; 61 | 62 | if (mlt_properties_get_int(properties, "transparent")) { 63 | int size = mlt_image_format_size(*format, *width, *height, NULL); 64 | // Create a new buffer for the source image. 65 | buffer = (uint8_t*) mlt_pool_alloc(size); 66 | memcpy(buffer, *image, size); 67 | // Make the background white. 68 | memset(*image, 255 , size); 69 | size = *width * *height * (hasAlpha? 4 : 3); 70 | // Make the background transparent. 71 | for (int i = 0; i < *width * *height; i++) 72 | (*image)[4 * i + 3] = 0; 73 | sourceImage.reset(new WebVfx::Image(buffer, *width, *height, size, hasAlpha)); 74 | renderedImage.reset(new WebVfx::Image(*image, *width, *height, size, hasAlpha)); 75 | } else { 76 | int size = *width * *height * (hasAlpha? 4 : 3); 77 | sourceImage.reset(new WebVfx::Image(*image, *width, *height, size, hasAlpha)); 78 | renderedImage.reset(new WebVfx::Image(*image, *width, *height, size, hasAlpha)); 79 | } 80 | 81 | MLTWebVfx::ServiceManager* manager = locker.getManager(); 82 | manager->setImageForName(manager->getSourceImageName(), sourceImage.get()); 83 | manager->setupConsumerListener(frame); 84 | 85 | // If there is a consumer set on the frame and the consumer is stopped, 86 | // skip the render step to avoid deadlock. Another thread could have 87 | // already called mlt_consumer_stop() thereby triggering 88 | // ServiceManager::onConsumerStopping() and Effects::renderComplete(). 89 | mlt_consumer consumer = static_cast( 90 | mlt_properties_get_data(MLT_FRAME_PROPERTIES(frame), "consumer", NULL)); 91 | if (!consumer || !mlt_consumer_is_stopped(consumer)) 92 | manager->render(renderedImage.get(), position, length, scale, hasAlpha); 93 | mlt_pool_release(buffer); 94 | } 95 | 96 | return error; 97 | } 98 | 99 | static mlt_frame filterProcess(mlt_filter filter, mlt_frame frame) { 100 | // Push the frame filter 101 | mlt_frame_push_service(frame, filter); 102 | mlt_frame_push_get_image(frame, filterGetImage); 103 | 104 | return frame; 105 | } 106 | 107 | mlt_service MLTWebVfx::createFilter() { 108 | mlt_filter self = mlt_filter_new(); 109 | if (self) { 110 | self->process = filterProcess; 111 | return MLT_FILTER_SERVICE(self); 112 | } 113 | return 0; 114 | } 115 | -------------------------------------------------------------------------------- /mlt/webvfx_producer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | extern "C" { 6 | #include 7 | #include 8 | #include 9 | } 10 | #include 11 | #include "factory.h" 12 | #include "service_locker.h" 13 | #include "service_manager.h" 14 | 15 | static const char* kWebVfxProducerPropertyName = "WebVfxProducer"; 16 | static const char* kWebVfxPositionPropertyName = "webvfx.position"; 17 | 18 | static int producerGetImage(mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int /*writable*/) { 19 | int error = 0; 20 | mlt_properties properties = MLT_FRAME_PROPERTIES(frame); 21 | mlt_producer producer = (mlt_producer)mlt_properties_get_data(properties, kWebVfxProducerPropertyName, NULL); 22 | mlt_properties producer_props = MLT_PRODUCER_PROPERTIES(producer); 23 | int size; 24 | int bpp; 25 | bool hasAlpha = false; 26 | 27 | // If not a plain resource, then disable preview scaling. 28 | const char* resource = mlt_properties_get(producer_props, "resource"); 29 | int use_preview_scale = mlt_properties_get_int(producer_props, "mlt_resolution_scale"); 30 | if (!use_preview_scale && resource) { 31 | mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(producer)); 32 | std::string resource2(resource); 33 | std::string plain = "plain:"; 34 | if (profile && resource2.substr(0, plain.size()) != plain) { 35 | *width = profile->width; 36 | *height = profile->height; 37 | } 38 | } 39 | 40 | // Add mlt_profile_scale_width and mlt_profile_scale_height properties for scripts. 41 | mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE(producer)); 42 | double scale = mlt_profile_scale_width(profile, *width); 43 | mlt_properties_set_double(properties, "mlt_profile_scale_width", scale); 44 | mlt_properties_set_double(properties, "mlt_profile_scale_height", 45 | mlt_profile_scale_height(profile, *height)); 46 | 47 | { 48 | MLTWebVfx::ServiceLocker locker(MLT_PRODUCER_SERVICE(producer)); 49 | if (!locker.initialize(*width, *height)) 50 | return 1; 51 | 52 | if (mlt_properties_get_int( producer_props, "transparent") ) { 53 | *format = mlt_image_rgb24a; 54 | hasAlpha = true; 55 | } 56 | else { 57 | *format = mlt_image_rgb24; 58 | } 59 | // Get bpp from image format 60 | size = mlt_image_format_size(*format, *width, *height, &bpp); 61 | *buffer = (uint8_t*)mlt_pool_alloc(size); 62 | mlt_frame_set_image(frame, *buffer, size, mlt_pool_release); 63 | 64 | // Make the background white. 65 | memset(*buffer, 255, size); 66 | size = *width * *height * bpp; 67 | if (hasAlpha) { 68 | // Make the background transparent. 69 | for (int i = 0; i < *width * *height; i++) 70 | (*buffer)[4 * i + 3] = 0; 71 | } 72 | WebVfx::Image outputImage(*buffer, *width, *height, size, hasAlpha); 73 | locker.getManager()->setupConsumerListener(frame); 74 | 75 | // If there is a consumer set on the frame and the consumer is stopped, 76 | // skip the render step to avoid deadlock. Another thread could have 77 | // already called mlt_consumer_stop() thereby triggering 78 | // ServiceManager::onConsumerStopping() and Effects::renderComplete(). 79 | mlt_consumer consumer = static_cast( 80 | mlt_properties_get_data(MLT_FRAME_PROPERTIES(frame), "consumer", NULL)); 81 | if (!consumer || !mlt_consumer_is_stopped(consumer)) { 82 | locker.getManager()->render(&outputImage, 83 | mlt_properties_get_position(properties, kWebVfxPositionPropertyName), 84 | mlt_producer_get_length(producer), 85 | scale, 86 | hasAlpha); 87 | } 88 | } 89 | mlt_properties_set_int(properties, "meta.media.width", *width); 90 | mlt_properties_set_int(properties, "meta.media.height", *height); 91 | 92 | return error; 93 | } 94 | 95 | static int getFrame(mlt_producer producer, mlt_frame_ptr frame, int /*index*/) { 96 | // Generate a frame 97 | *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer)); 98 | 99 | if (*frame) { 100 | // Obtain properties of frame and producer 101 | mlt_properties properties = MLT_FRAME_PROPERTIES(*frame); 102 | 103 | // Set the producer on the frame properties 104 | mlt_properties_set_data(properties, kWebVfxProducerPropertyName, producer, 0, NULL, NULL); 105 | 106 | // Update timecode on the frame we're creating 107 | mlt_position position = mlt_producer_position(producer); 108 | mlt_frame_set_position(*frame, position); 109 | mlt_properties_set_position(properties, kWebVfxPositionPropertyName, position); 110 | 111 | // Set producer-specific frame properties 112 | mlt_properties producer_props = MLT_PRODUCER_PROPERTIES(producer); 113 | mlt_properties_set_int(properties, "meta.media.progressive", 1); 114 | mlt_properties_set_int(properties, "progressive", 1 ); 115 | mlt_frame_set_aspect_ratio(*frame, 116 | mlt_properties_get_double(producer_props, "meta.media.sample_aspect_num") / 117 | mlt_properties_get_double(producer_props, "meta.media.sample_aspect_den")); 118 | mlt_profile profile = mlt_service_profile(MLT_PRODUCER_SERVICE( producer )); 119 | mlt_properties_set_double(properties, "aspect_ratio", mlt_profile_sar(profile)); 120 | 121 | // Push the get_image method 122 | mlt_frame_push_get_image(*frame, producerGetImage); 123 | } 124 | 125 | // Calculate the next timecode 126 | mlt_producer_prepare_next(producer); 127 | 128 | return 0; 129 | } 130 | 131 | static void producer_close( mlt_producer parent ) 132 | { 133 | // Close the parent 134 | parent->close = NULL; 135 | mlt_producer_close( parent ); 136 | // Free the memory 137 | free( parent ); 138 | } 139 | 140 | mlt_service MLTWebVfx::createProducer(mlt_profile profile) { 141 | mlt_producer producer = mlt_producer_new(profile); 142 | if (producer) { 143 | producer->get_frame = getFrame; 144 | producer->close = (mlt_destructor) producer_close; 145 | mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); 146 | mlt_properties_set_int( properties, "meta.media.progressive", 1 ); 147 | mlt_properties_set_int( properties, "meta.media.sample_aspect_num", 1 ); 148 | mlt_properties_set_int( properties, "meta.media.sample_aspect_den", 1 ); 149 | mlt_properties_set_int(properties, "meta.media.width", profile->width); 150 | mlt_properties_set_int(properties, "meta.media.height", profile->height); 151 | return MLT_PRODUCER_SERVICE(producer); 152 | } 153 | return 0; 154 | } 155 | -------------------------------------------------------------------------------- /mlt/webvfx_transition.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | extern "C" { 6 | #include 7 | #include 8 | #include 9 | #include 10 | } 11 | #include 12 | #include 13 | #include "factory.h" 14 | #include "service_locker.h" 15 | #include "service_manager.h" 16 | 17 | 18 | static int transitionGetImage(mlt_frame aFrame, uint8_t **image, mlt_image_format *format, int *width, int *height, int /*writable*/) { 19 | int error = 0; 20 | 21 | mlt_frame bFrame = mlt_frame_pop_frame(aFrame); 22 | mlt_transition transition = (mlt_transition)mlt_frame_pop_service(aFrame); 23 | mlt_properties properties = MLT_TRANSITION_PROPERTIES(transition); 24 | 25 | mlt_position position = mlt_transition_get_position(transition, aFrame); 26 | mlt_position length = mlt_transition_get_length(transition); 27 | 28 | // If not a plain resource, then disable preview scaling. 29 | const char* resource = mlt_properties_get(properties, "resource"); 30 | int use_preview_scale = mlt_properties_get_int(properties, "mlt_resolution_scale"); 31 | if (!use_preview_scale && resource) { 32 | mlt_profile profile = mlt_service_profile(MLT_TRANSITION_SERVICE(transition)); 33 | std::string resource2(resource); 34 | std::string plain = "plain:"; 35 | if (profile && resource2.substr(0, plain.size()) != plain) { 36 | *width = profile->width; 37 | *height = profile->height; 38 | } 39 | } 40 | 41 | // Get the aFrame image, we will write our output to it 42 | *format = mlt_image_rgb24; 43 | if ((error = mlt_frame_get_image(aFrame, image, format, width, height, 1)) != 0) 44 | return error; 45 | // Get the bFrame image, we won't write to it 46 | uint8_t *bImage = NULL; 47 | int bWidth = 0, bHeight = 0; 48 | if ((error = mlt_frame_get_image(bFrame, &bImage, format, &bWidth, &bHeight, 0)) != 0) 49 | return error; 50 | 51 | // Add mlt_profile_scale_width and mlt_profile_scale_height properties for scripts. 52 | mlt_profile profile = mlt_service_profile(MLT_TRANSITION_SERVICE(transition)); 53 | double scale = mlt_profile_scale_width(profile, *width); 54 | mlt_properties_set_double(properties, "mlt_profile_scale_width", scale); 55 | mlt_properties_set_double(properties, "mlt_profile_scale_height", 56 | mlt_profile_scale_height(profile, *height)); 57 | 58 | { // Scope the lock 59 | MLTWebVfx::ServiceLocker locker(MLT_TRANSITION_SERVICE(transition)); 60 | if (!locker.initialize(*width, *height)) 61 | return 1; 62 | 63 | bool hasAlpha = (*format == mlt_image_rgb24a); 64 | int size = *width * *height * (hasAlpha? 4 : 3); 65 | MLTWebVfx::ServiceManager* manager = locker.getManager(); 66 | WebVfx::Image renderedImage(*image, *width, *height, size, hasAlpha); 67 | manager->setImageForName(manager->getSourceImageName(), &renderedImage); 68 | size = bWidth * bHeight * (hasAlpha? 4 : 3); 69 | WebVfx::Image targetImage(bImage, bWidth, bHeight, size, hasAlpha); 70 | manager->setImageForName(manager->getTargetImageName(), &targetImage); 71 | manager->setupConsumerListener(aFrame); 72 | 73 | // If there is a consumer set on the frame and the consumer is stopped, 74 | // skip the render step to avoid deadlock. Another thread could have 75 | // already called mlt_consumer_stop() thereby triggering 76 | // ServiceManager::onConsumerStopping() and Effects::renderComplete(). 77 | mlt_consumer consumer = static_cast( 78 | mlt_properties_get_data(MLT_FRAME_PROPERTIES(aFrame), "consumer", NULL)); 79 | if (!consumer || !mlt_consumer_is_stopped(consumer)) { 80 | manager->render(&renderedImage, 81 | position, length, 82 | scale); 83 | } 84 | } 85 | 86 | return error; 87 | } 88 | 89 | static mlt_frame transitionProcess(mlt_transition transition, mlt_frame aFrame, mlt_frame bFrame) { 90 | mlt_frame_push_service(aFrame, transition); 91 | mlt_frame_push_frame(aFrame, bFrame); 92 | mlt_frame_push_get_image(aFrame, transitionGetImage); 93 | return aFrame; 94 | } 95 | 96 | mlt_service MLTWebVfx::createTransition() { 97 | mlt_transition self = mlt_transition_new(); 98 | if (self) { 99 | self->process = transitionProcess; 100 | // Video only transition 101 | mlt_properties_set_int(MLT_TRANSITION_PROPERTIES(self), "_transition_type", 1); 102 | return MLT_TRANSITION_SERVICE(self); 103 | } 104 | return 0; 105 | } 106 | -------------------------------------------------------------------------------- /tools/blender/addons/io_anim_webvfx/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | bl_info = { 6 | "name": "WebVfx Object Animation JSON Format", 7 | "description": "Import/export object animation data in JSON for use with WebVfx", 8 | "author": "Andrew Wason ", 9 | "version": (1, 0), 10 | "blender": (2, 5, 8), 11 | "api": 38205, 12 | "location": "File > Import-Export", 13 | "warning": '', # used for warning icon and text in addons panel 14 | "wiki_url": '', 15 | "tracker_url": '', 16 | "category": "Import-Export" 17 | } 18 | 19 | # To support reload properly, try to access a package var, if it's there, reload everything 20 | if "bpy" in locals(): 21 | import imp 22 | if "import_webvfx" in locals(): 23 | imp.reload(import_webvfx) 24 | if "export_webvfx" in locals(): 25 | imp.reload(export_webvfx) 26 | 27 | import bpy 28 | from bpy.props import BoolProperty, StringProperty 29 | from bpy_extras.io_utils import ImportHelper, ExportHelper 30 | 31 | 32 | KeyframeGroup = "LocRot" 33 | CoordNames = ['X', 'Y', 'Z'] 34 | CurveNames = { 'location': 'location', 'rotation_euler': 'rotation'} 35 | 36 | 37 | class ImportWebVfx(bpy.types.Operator, ImportHelper): 38 | '''Load WebVfx animation JSON file, replacing animation of the selected object''' 39 | bl_idname = "import_anim.webvfx" 40 | bl_label = "Import WebVfx JSON" 41 | bl_options = {'REGISTER', 'UNDO'} 42 | 43 | filename_ext = '.js' 44 | filter_glob = StringProperty(default="*.js", options={'HIDDEN'}) 45 | 46 | @classmethod 47 | def poll(cls, context): 48 | return context.object 49 | 50 | def execute(self, context): 51 | from . import import_webvfx 52 | return import_webvfx.load(self, context, **self.as_keywords(ignore=("filter_glob",))) 53 | 54 | 55 | class ExportWebVfx(bpy.types.Operator, ExportHelper): 56 | '''Save WebVfx animation JSON file for the selected object(s)''' 57 | bl_idname = "export_anim.webvfx" 58 | bl_label = "Export WebVfx JSON" 59 | 60 | filename_ext = '.js' 61 | filter_glob = StringProperty(default="*.js", options={'HIDDEN'}) 62 | 63 | option_compact = BoolProperty(name="Compact", description="Compact JSON", default=True) 64 | option_varname = StringProperty(name="var Name", description="JavaScript variable name") 65 | 66 | @classmethod 67 | def poll(cls, context): 68 | return context.object and context.object.animation_data 69 | 70 | def execute(self, context): 71 | from . import export_webvfx 72 | return export_webvfx.save(self, context, **self.as_keywords(ignore=("check_existing", "filter_glob"))) 73 | 74 | def draw(self, context): 75 | layout = self.layout 76 | layout.prop(self.properties, "option_compact") 77 | layout.prop(self.properties, "option_varname") 78 | 79 | def menu_func_import(self, context): 80 | self.layout.operator(ImportWebVfx.bl_idname, text="WebVfx Animation JSON (.js)") 81 | 82 | 83 | def menu_func_export(self, context): 84 | self.layout.operator(ExportWebVfx.bl_idname, text="WebVfx Animation JSON (.js)") 85 | 86 | def register(): 87 | bpy.utils.register_module(__name__) 88 | 89 | bpy.types.INFO_MT_file_import.append(menu_func_import) 90 | bpy.types.INFO_MT_file_export.append(menu_func_export) 91 | 92 | 93 | def unregister(): 94 | bpy.utils.unregister_module(__name__) 95 | 96 | bpy.types.INFO_MT_file_import.remove(menu_func_import) 97 | bpy.types.INFO_MT_file_export.remove(menu_func_export) 98 | 99 | def reportError(op, msg): 100 | op.report({'ERROR'}, msg) 101 | 102 | if __name__ == "__main__": 103 | register() 104 | -------------------------------------------------------------------------------- /tools/blender/addons/io_anim_webvfx/export_webvfx.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import bpy 6 | import math 7 | import json 8 | from io_anim_webvfx import CurveNames, CoordNames 9 | 10 | # Adjust control points so the total length of the "handles" 11 | # is not more than the horizontal distance between the keyframe points. 12 | def correctControlPoints(points): 13 | # Handle deltas 14 | h1 = [points[0][0] - points[1][0], points[0][1] - points[1][1]] 15 | h2 = [points[3][0] - points[2][0], points[3][1] - points[2][1]] 16 | 17 | length = points[3][0] - points[0][0] 18 | len1 = math.fabs(h1[0]) 19 | len2 = math.fabs(h2[0]) 20 | 21 | if (len1 + len2) == 0: 22 | return 23 | 24 | # The handles cross, force apart by proportion of overlap 25 | if (len1 + len2) > length: 26 | overlap = length / (len1 + len2) 27 | points[1][0] = points[0][0] - overlap * h1[0] 28 | points[1][1] = points[0][1] - overlap * h1[1] 29 | points[2][0] = points[3][0] - overlap * h2[0] 30 | points[2][1] = points[3][1] - overlap * h2[1] 31 | 32 | def buildAnimation(animObject): 33 | action = animObject.animation_data.action 34 | fcurves = action.fcurves 35 | frame_range = list(action.frame_range) 36 | 37 | animation = {'name': animObject.name, 'range': frame_range} 38 | # Save FOV if object is a Camera 39 | if animObject.type == 'CAMERA': 40 | animation['horizontalFOV'] = animObject.data.angle 41 | 42 | for f in fcurves: 43 | name = (CurveNames[f.data_path] + CoordNames[f.array_index]) 44 | # Single keyframe - use constant value 45 | if len(f.keyframe_points) == 1: 46 | animation[name] = f.keyframe_points[0].co[1] 47 | # Otherwise get the 4 control points - this keyframes point 48 | # and it's right handle, then the next keyframes left handle 49 | # and the next keyframes point 50 | else: 51 | segments = [] 52 | for i in range(len(f.keyframe_points) - 1): 53 | k = f.keyframe_points[i] 54 | nextk = f.keyframe_points[i+1] 55 | points = [list(k.co), 56 | list(k.handle_right), 57 | list(nextk.handle_left), 58 | list(nextk.co)] 59 | correctControlPoints(points) 60 | segments.append({'range': [k.co[0], nextk.co[0]], 61 | 'bezierPoints': points}) 62 | animation[name] = segments 63 | 64 | return animation 65 | 66 | def save(operator, context, option_compact, option_varname, filepath=""): 67 | if len(context.selected_objects) > 1: 68 | animation = [] 69 | for obj in context.selected_objects: 70 | if obj.animation_data: 71 | animation.append(buildAnimation(obj)) 72 | else: 73 | animation = buildAnimation(context.object) 74 | 75 | animationJS = "" 76 | if option_varname: 77 | animationJS = "var %s =\n" % option_varname 78 | 79 | if option_compact: 80 | animationJS += json.dumps(animation, sort_keys=True, 81 | separators=(',',':')) 82 | else: 83 | animationJS += json.dumps(animation, sort_keys=True, indent=4, 84 | separators=(',',': ')) 85 | 86 | with open(filepath, "w") as file: 87 | file.write(animationJS) 88 | return {'FINISHED'} 89 | -------------------------------------------------------------------------------- /tools/blender/addons/io_anim_webvfx/import_webvfx.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import bpy 6 | from io_anim_webvfx import KeyframeGroup, CurveNames, CoordNames, reportError 7 | import numbers 8 | import json 9 | 10 | def importAnimation(animObject, animation): 11 | animObject.animation_data_clear() 12 | animObject.animation_data_create() 13 | 14 | action = bpy.data.actions.new("%sAction" % animObject.name) 15 | action.groups.new(KeyframeGroup) 16 | 17 | # Restore camera FOV 18 | if animObject.type == 'CAMERA' and 'horizontalFOV' in animation: 19 | animObject.data.angle = animation['horizontalFOV'] 20 | 21 | animRange = animation['range'] 22 | 23 | for curve in CurveNames: 24 | for coord in range(3): 25 | fcurve = action.fcurves.new(curve, coord, KeyframeGroup) 26 | curveData = animation[CurveNames[curve] + CoordNames[coord]] 27 | # Constant value 28 | if isinstance(curveData, numbers.Real): 29 | fcurve.keyframe_points.insert(animRange[0], curveData) 30 | # Array of segment control point data 31 | else: 32 | for segment in curveData: 33 | points = segment['bezierPoints'] 34 | keyframe = fcurve.keyframe_points.insert(points[0][0], 35 | points[0][1]) 36 | keyframe.handle_right = points[1] 37 | keyframe = fcurve.keyframe_points.insert(points[3][0], 38 | points[3][1]) 39 | keyframe.handle_left = points[2] 40 | 41 | animObject.animation_data.action = action 42 | 43 | 44 | def load(operator, context, filepath=""): 45 | with open(filepath, "r") as file: 46 | animationJS = file.read() 47 | # Deal with variable declaration 48 | var = animationJS.find("=") 49 | if var >= 0: 50 | animationJS = animationJS[var+1:] 51 | animation = json.loads(animationJS) 52 | 53 | if isinstance(animation, list): 54 | for anim in animation: 55 | if 'name' in anim: 56 | if anim['name'] in bpy.data.objects: 57 | importAnimation(bpy.data.objects[anim['name']], anim) 58 | else: 59 | reportError(self, 'Animation missing name') 60 | else: 61 | reportError(self, 'No object found for animation "%s"' % anim['name']) 62 | else: 63 | importAnimation(context.object, animation) 64 | return {'FINISHED'} 65 | -------------------------------------------------------------------------------- /tools/browser/browser.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include "browser.h" 8 | 9 | 10 | Browser::Browser(QWidget *parent) 11 | : QMainWindow(parent) 12 | { 13 | setupUi(this); 14 | QWebSettings *settings = webView->settings(); 15 | settings->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); 16 | settings->setAttribute(QWebSettings::SiteSpecificQuirksEnabled, false); 17 | settings->setAttribute(QWebSettings::AcceleratedCompositingEnabled, true); 18 | settings->setAttribute(QWebSettings::WebGLEnabled, true); 19 | } 20 | 21 | void Browser::on_urlEdit_returnPressed() 22 | { 23 | webView->load(QUrl::fromUserInput(urlEdit->text())); 24 | } 25 | 26 | void Browser::on_webView_urlChanged(const QUrl& url) 27 | { 28 | urlEdit->setText(url.toString()); 29 | } 30 | 31 | -------------------------------------------------------------------------------- /tools/browser/browser.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef BROWSER_H 6 | #define BROWSER_H 7 | 8 | #include 9 | #include "ui_browser.h" 10 | 11 | class Browser : public QMainWindow, private Ui::Browser 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | Browser(QWidget *parent = 0); 17 | 18 | private slots: 19 | void on_urlEdit_returnPressed(); 20 | void on_webView_urlChanged(const QUrl&); 21 | }; 22 | 23 | #endif 24 | 25 | -------------------------------------------------------------------------------- /tools/browser/browser.pro: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | include(../../common.pri) 6 | TEMPLATE = app 7 | 8 | SOURCES = main.cpp browser.cpp 9 | HEADERS = browser.h 10 | FORMS = browser.ui 11 | 12 | isEqual(QT_MAJOR_VERSION, 5) { 13 | QT += webkitwidgets 14 | } else { 15 | QT += webkit 16 | } 17 | !mac:TARGET = webvfx_browser 18 | mac:TARGET = "WebVfx Browser" 19 | 20 | target.path = $$PREFIX/bin 21 | INSTALLS += target 22 | -------------------------------------------------------------------------------- /tools/browser/browser.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Browser 4 | 5 | 6 | 7 | 0 8 | 0 9 | 800 10 | 600 11 | 12 | 13 | 14 | QtBrowser 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | about:blank 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 0 36 | 0 37 | 800 38 | 22 39 | 40 | 41 | 42 | 43 | File 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | Quit 52 | 53 | 54 | Ctrl+Q 55 | 56 | 57 | 58 | 59 | 60 | QWebView 61 | QWidget 62 |
QWebView
63 |
64 |
65 | 66 | 67 | 68 | actionQuit 69 | triggered() 70 | Browser 71 | close() 72 | 73 | 74 | -1 75 | -1 76 | 77 | 78 | 399 79 | 299 80 | 81 | 82 | 83 | 84 |
85 | -------------------------------------------------------------------------------- /tools/browser/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | #include "browser.h" 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | QApplication app(argc, argv); 11 | 12 | app.setOrganizationDomain("webvfx.org"); 13 | app.setOrganizationName("WebVfx"); 14 | app.setApplicationName("QtBrowser"); 15 | 16 | Browser browser; 17 | browser.show(); 18 | return app.exec(); 19 | } 20 | 21 | -------------------------------------------------------------------------------- /tools/js/webvfx.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Stubs for the webvfx JS context object. 6 | // This can be included in an HTML effect so it can be tested 7 | // in a web browser. 8 | 9 | var webvfx = { 10 | TargetImageType: 1, 11 | SourceImageType: 2, 12 | ExtraImageType: 3, 13 | 14 | imageMap: {}, // User can set this to map image names to Images 15 | parameterMap: {}, // User can set this to map param names to strings/numbers 16 | 17 | readyRender: function (v) {}, 18 | getImage: function (name) { 19 | var imageMap = this.imageMap; 20 | return { 21 | toImageData: function () { 22 | var image = imageMap[name]; 23 | var canvas = document.createElement("canvas"); 24 | if (image) { 25 | canvas.width = image.width; 26 | canvas.height = image.height; 27 | } 28 | else 29 | canvas.width = canvas.height = 320; 30 | var context = canvas.getContext('2d'); 31 | if (image) 32 | context.drawImage(image, 0, 0); 33 | else { 34 | context.fillStyle = 'rgb(255,0,0)'; 35 | context.fillRect(0, 0, canvas.width, canvas.height); 36 | } 37 | return context.getImageData(0, 0, canvas.width, canvas.height); 38 | }, 39 | assignToHTMLImageElement: function (image) { 40 | if (imageMap[name]) 41 | image.src = imageMap[name].src; 42 | } 43 | }; 44 | }, 45 | getStringParameter: function (name) { 46 | return this.parameterMap[name]; 47 | }, 48 | getNumberParameter: function (name) { 49 | return parseFloat(this.parameterMap[name]); 50 | }, 51 | renderRequested: { 52 | connect: function (a, b) { 53 | if (a instanceof Function) 54 | this.render = a; 55 | else { 56 | this.self = a; 57 | this.render = b; 58 | } 59 | } 60 | }, 61 | render: function (t) { 62 | this.renderRequested.render.call(this.renderRequested.self, t); 63 | }, 64 | }; -------------------------------------------------------------------------------- /tools/render/render.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | void usage(const char* name) { 14 | std::cerr << "Usage: " << name << " -s|--size x [-p|--parameter =]... [-i|--image =] [-t|--times ,,...] [-c|--comment ] -o|--output [-h|--help] " << std::endl; 15 | } 16 | 17 | int main(int argc, char* argv[]) { 18 | if (argc <= 1) { 19 | usage(argv[0]); 20 | return 1; 21 | } 22 | 23 | std::map parameterMap; 24 | std::map imageMap; 25 | QFileInfo outputFile; 26 | int width = 0, height = 0; 27 | QStringList renderTimes; 28 | QString comment; 29 | 30 | static struct option long_options[] = { 31 | {"help", no_argument, 0, 'h'}, 32 | // Render size 33 | {"size", required_argument, 0, 's'}, 34 | // Parameter name=value to expose to effect 35 | {"parameter", required_argument, 0, 'p'}, 36 | // Name and filename of an input image 37 | {"image", required_argument, 0, 'i'}, 38 | // Times to render at 39 | {"times", required_argument, 0, 't'}, 40 | // Comment 41 | {"comment", required_argument, 0, 'c'}, 42 | // Output filename to write images to 43 | {"output", required_argument, 0, 'o'}, 44 | {0, 0, 0, 0} 45 | }; 46 | int option_index = 0; 47 | int c; 48 | while ((c = getopt_long(argc, argv, "hs:p:i:t:c:o:", 49 | long_options, &option_index)) != -1) { 50 | switch (c) { 51 | case 'h': 52 | usage(argv[0]); 53 | return 1; 54 | case 's': { 55 | char* p = strchr(optarg, 'x'); 56 | if (!p) { 57 | usage(argv[0]); 58 | return 1; 59 | } 60 | width = QString::fromUtf8(optarg, p - optarg).toInt(); 61 | height = QString::fromUtf8(p + 1).toInt(); 62 | break; 63 | } 64 | case 'p': { 65 | char* p = strchr(optarg, '='); 66 | if (!p) { 67 | usage(argv[0]); 68 | return 1; 69 | } 70 | QString name(QString::fromUtf8(optarg, p - optarg)); 71 | QString value(QString::fromUtf8(p + 1)); 72 | parameterMap.insert(std::pair(name, value)); 73 | break; 74 | } 75 | case 'i': { 76 | char* p = strchr(optarg, '='); 77 | if (!p) { 78 | usage(argv[0]); 79 | return 1; 80 | } 81 | QString name(QString::fromUtf8(optarg, p - optarg)); 82 | QImage image(QString::fromUtf8(p + 1)); 83 | image = image.convertToFormat(QImage::Format_RGB888); 84 | imageMap.insert(std::pair(name, image)); 85 | break; 86 | } 87 | case 't': 88 | renderTimes = QString::fromUtf8(optarg).split(","); 89 | break; 90 | case 'c': 91 | comment = QString::fromUtf8(optarg); 92 | break; 93 | case 'o': 94 | outputFile = QFileInfo(QString::fromUtf8(optarg)); 95 | break; 96 | case '?': 97 | default: 98 | return 1; 99 | } 100 | } 101 | 102 | if (outputFile.filePath().isEmpty() || optind >= argc || width == 0 || height == 0) { 103 | usage(argv[0]); 104 | return 1; 105 | } 106 | if (renderTimes.empty()) 107 | renderTimes.append("0"); 108 | 109 | const char* effectFile = argv[optind]; 110 | 111 | class Logger : public WebVfx::Logger { 112 | public: 113 | void log(const QString& message) { 114 | std::cerr << message.toStdString() << std::endl; 115 | } 116 | }; 117 | WebVfx::setLogger(new Logger()); 118 | 119 | class Parameters : public WebVfx::Parameters { 120 | public: 121 | Parameters(std::map &map) : map(map) {} 122 | QString getStringParameter(const QString& name) { 123 | return map[name]; 124 | } 125 | double getNumberParameter(const QString& name) { 126 | return map[name].toDouble(); 127 | } 128 | QVariantMap getRectParameter(const QString& name) { 129 | QStringList parts = map[name].split(' '); 130 | QVariantMap map; 131 | int i = 0; 132 | if (parts.size() > i) { 133 | map["x"] = parts[i++].toDouble(); 134 | if (parts.size() > i) { 135 | map["y"] = parts[i++].toDouble(); 136 | if (parts.size() > i) { 137 | map["width"] = parts[i++].toDouble(); 138 | if (parts.size() > i) { 139 | map["height"] = parts[i++].toDouble(); 140 | if (parts.size() > i) { 141 | map["opacity"] = parts[i++].toDouble(); 142 | } 143 | } 144 | } 145 | } 146 | } 147 | return map; 148 | } 149 | private: 150 | std::map& map; 151 | }; 152 | 153 | class AutoWebVfx { 154 | public: 155 | //XXX check return code 156 | AutoWebVfx() { WebVfx::initialize(); } 157 | ~AutoWebVfx() { WebVfx::shutdown(); } 158 | }; 159 | AutoWebVfx vfx; 160 | 161 | WebVfx::Effects* effects = WebVfx::createEffects(effectFile, width, height, new Parameters(parameterMap)); 162 | if (!effects) { 163 | std::cerr << "Failed to create Effects" << std::endl; 164 | return 1; 165 | } 166 | 167 | uchar data[width * height * 3]; 168 | WebVfx::Image renderImage(data, width, height, sizeof(data)); 169 | 170 | QString outputPathTemplate(outputFile.path() + "/" + outputFile.completeBaseName() 171 | + "-%1." + outputFile.suffix()); 172 | 173 | for (int i = 0; i < renderTimes.size(); ++i) { 174 | // Set input images 175 | std::map::iterator it; 176 | for (it = imageMap.begin(); it != imageMap.end(); it++) { 177 | QImage image((*it).second); 178 | // Use constBits to avoid a deep copy 179 | WebVfx::Image inputImage(const_cast(image.constBits()), 180 | image.width(), image.height(), 181 | image.byteCount()); 182 | effects->setImage((*it).first, &inputImage); 183 | } 184 | 185 | // Render 186 | double time = renderTimes.at(i).toDouble(); 187 | if (!effects->render(time, &renderImage)) { 188 | std::cerr << "Failed to render time " << time << std::endl; 189 | return 1; 190 | } 191 | 192 | // Save image to disk 193 | QImage outputImage((const uchar*)renderImage.pixels(), 194 | renderImage.width(), renderImage.height(), 195 | renderImage.bytesPerLine(), QImage::Format_RGB888); 196 | if (!comment.isEmpty()) 197 | outputImage.setText("Comment", comment); 198 | outputImage.save(outputPathTemplate.arg(renderTimes.at(i))); 199 | } 200 | 201 | effects->destroy(); effects = 0; 202 | 203 | return 0; 204 | } 205 | -------------------------------------------------------------------------------- /tools/render/render.pro: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | include(../../common.pri) 6 | TEMPLATE = app 7 | 8 | SOURCES += render.cpp 9 | 10 | CONFIG += console 11 | mac:CONFIG -= app_bundle 12 | TARGET = webvfx_render 13 | LIBS += -L$$DESTDIR -lwebvfx 14 | 15 | QMAKE_RPATHDIR += $$PREFIX/lib 16 | 17 | target.path = $$PREFIX/bin 18 | INSTALLS += target 19 | -------------------------------------------------------------------------------- /viewer/image_color.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "image_color.h" 12 | 13 | ImageColor::ImageColor(QWidget* parent) 14 | : QPushButton(parent) 15 | { 16 | setFlat(true); 17 | setAutoFillBackground(true); 18 | setImageColor(color); 19 | connect(this, SIGNAL(clicked(bool)), SLOT(onClicked(bool))); 20 | } 21 | 22 | void ImageColor::setImageColor(const QColor& color) 23 | { 24 | this->color = color; 25 | QPalette palette; 26 | palette.setBrush(QPalette::Button, QBrush(color)); 27 | setPalette(palette); 28 | fillImage(); 29 | 30 | emit imageChanged(objectName(), getImage()); 31 | } 32 | 33 | void ImageColor::setImageSize(const QSize& size) 34 | { 35 | if (image.size() == size) 36 | return; 37 | image = QImage(size, QImage::Format_RGB888); 38 | fillImage(); 39 | 40 | emit imageChanged(objectName(), getImage()); 41 | } 42 | 43 | const WebVfx::Image ImageColor::getImage() 44 | { 45 | return WebVfx::Image(image.bits(), image.width(), image.height(), image.byteCount()); 46 | } 47 | 48 | void ImageColor::onClicked(bool) 49 | { 50 | QColor newColor = QColorDialog::getColor(color, this, tr("Image Color")); 51 | if (!newColor.isValid()) 52 | return; 53 | setImageColor(newColor); 54 | } 55 | 56 | void ImageColor::fillImage() 57 | { 58 | if (image.isNull()) 59 | return; 60 | 61 | QSize size = image.size(); 62 | QRectF rect(0, 0, size.width(), size.height()); 63 | 64 | QPainter painter(&image); 65 | painter.setRenderHint(QPainter::TextAntialiasing, true); 66 | 67 | // Fill background 68 | painter.fillRect(rect, color); 69 | 70 | // Draw name centered, pick color to contrast with background 71 | painter.setPen(color.lightnessF() < 0.5 ? Qt::white : Qt::black); 72 | painter.setFont(QFont("Arial", 30)); 73 | painter.drawText(rect, Qt::AlignCenter, objectName()); 74 | 75 | // Outline edges 76 | const int penWidth = 10; 77 | QPen pen(color.lightnessF() < 0.5 ? color.lighter() : color.darker()); 78 | pen.setWidth(penWidth); 79 | pen.setJoinStyle(Qt::MiterJoin); 80 | painter.setPen(pen); 81 | painter.drawRect(rect.adjusted(penWidth/2, penWidth/2, -penWidth/2, -penWidth/2)); 82 | 83 | painter.end(); 84 | } 85 | -------------------------------------------------------------------------------- /viewer/image_color.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef IMAGE_COLOR_H 6 | #define IMAGE_COLOR_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | class ImageColor : public QPushButton 14 | { 15 | Q_OBJECT 16 | Q_PROPERTY(QColor imageColor READ imageColor WRITE setImageColor) 17 | Q_PROPERTY(QSize imageSize READ imageSize WRITE setImageSize) 18 | 19 | public: 20 | ImageColor(QWidget *parent = 0); 21 | 22 | const QColor imageColor() { return color; } 23 | void setImageColor(const QColor& color); 24 | const QSize imageSize() { return image.size(); } 25 | void setImageSize(const QSize& size); 26 | const WebVfx::Image getImage(); 27 | 28 | signals: 29 | void imageChanged(const QString& name, WebVfx::Image image); 30 | 31 | private slots: 32 | void onClicked(bool); 33 | 34 | private: 35 | void fillImage(); 36 | QColor color; 37 | QImage image; 38 | }; 39 | 40 | #endif 41 | 42 | -------------------------------------------------------------------------------- /viewer/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | #include "viewer.h" 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | QApplication app(argc, argv); 11 | 12 | app.setOrganizationDomain("webvfx.org"); 13 | app.setOrganizationName("WebVfx"); 14 | app.setApplicationName("WebVfx Viewer"); 15 | 16 | Viewer viewer; 17 | viewer.show(); 18 | 19 | QStringList args(QApplication::arguments()); 20 | if (args.size() > 1) 21 | viewer.loadFile(args.at(1)); 22 | 23 | return app.exec(); 24 | } 25 | 26 | -------------------------------------------------------------------------------- /viewer/render_dialog.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | #include "render_dialog.h" 9 | 10 | RenderDialog::RenderDialog(QWidget* parent) 11 | : QDialog(parent) 12 | { 13 | setupUi(this); 14 | setAttribute(Qt::WA_DeleteOnClose); 15 | } 16 | 17 | void RenderDialog::setImage(const QImage& image) 18 | { 19 | imageLabel->setPixmap(QPixmap::fromImage(image)); 20 | } 21 | -------------------------------------------------------------------------------- /viewer/render_dialog.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef RENDER_DIALOG_H 6 | #define RENDER_DIALOG_H 7 | 8 | #include 9 | #include "ui_render_dialog.h" 10 | 11 | class QImage; 12 | class QLabel; 13 | 14 | class RenderDialog : public QDialog, private Ui::RenderDialog 15 | { 16 | Q_OBJECT 17 | public: 18 | RenderDialog(QWidget* parent=0); 19 | void setImage(const QImage& image); 20 | }; 21 | 22 | #endif 23 | 24 | -------------------------------------------------------------------------------- /viewer/render_dialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | RenderDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 386 10 | 286 11 | 12 | 13 | 14 | Rendered Image 15 | 16 | 17 | 18 | 19 | 20 | 21 | 0 22 | 0 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Qt::Horizontal 31 | 32 | 33 | QDialogButtonBox::Close 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | buttonBox 43 | accepted() 44 | RenderDialog 45 | accept() 46 | 47 | 48 | 248 49 | 254 50 | 51 | 52 | 157 53 | 274 54 | 55 | 56 | 57 | 58 | buttonBox 59 | rejected() 60 | RenderDialog 61 | reject() 62 | 63 | 64 | 316 65 | 260 66 | 67 | 68 | 286 69 | 274 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /viewer/viewer.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef VIEWER_H 6 | #define VIEWER_H 7 | 8 | #include 9 | #include 10 | #include "ui_viewer.h" 11 | 12 | namespace WebVfx 13 | { 14 | class Content; 15 | } 16 | 17 | class QDoubleSpinBox; 18 | class QLabel; 19 | 20 | class Viewer : public QMainWindow, private Ui::Viewer 21 | { 22 | Q_OBJECT 23 | 24 | public: 25 | Viewer(); 26 | void loadFile(const QString& fileName); 27 | 28 | private slots: 29 | void on_actionOpen_triggered(bool); 30 | void onContentLoadFinished(bool); 31 | void on_actionReload_triggered(bool); 32 | void on_actionRenderImage_triggered(bool); 33 | void on_resizeButton_clicked(); 34 | void on_timeSlider_valueChanged(int); 35 | void onTimeSpinBoxValueChanged(double); 36 | void on_addParameterButton_clicked(); 37 | void on_deleteParameterButton_clicked(); 38 | void onImageChanged(const QString&, WebVfx::Image); 39 | 40 | private: 41 | void createContent(const QString& fileName); 42 | void setContentUIEnabled(bool enable); 43 | void handleResize(); 44 | void setImagesOnContent(); 45 | void setupImages(const QSize& size); 46 | double sliderTimeValue(int value); 47 | QLabel* sizeLabel; 48 | QDoubleSpinBox* timeSpinBox; 49 | WebVfx::Content* content; 50 | }; 51 | 52 | #endif 53 | 54 | -------------------------------------------------------------------------------- /viewer/viewer.pro: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | include(../common.pri) 6 | TEMPLATE = app 7 | 8 | SOURCES += main.cpp 9 | SOURCES += image_color.cpp 10 | SOURCES += render_dialog.cpp 11 | SOURCES += viewer.cpp 12 | HEADERS += image_color.h 13 | HEADERS += render_dialog.h 14 | HEADERS += viewer.h 15 | FORMS += render_dialog.ui 16 | FORMS += viewer.ui 17 | 18 | isEqual(QT_MAJOR_VERSION, 5) { 19 | QT += webkitwidgets 20 | } else { 21 | QT += webkit 22 | } 23 | QT += quick 24 | 25 | !mac:TARGET = webvfx_viewer 26 | mac:TARGET = "WebVfx Viewer" 27 | LIBS += -L$$DESTDIR -lwebvfx 28 | 29 | QMAKE_RPATHDIR += $$PREFIX/lib 30 | 31 | target.path = $$PREFIX/bin 32 | INSTALLS += target 33 | -------------------------------------------------------------------------------- /webvfx/content.cpp: -------------------------------------------------------------------------------- 1 | #include "webvfx/content.h" 2 | 3 | WebVfx::Content::~Content() 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /webvfx/content.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef WEBVFX_CONTENT_H_ 6 | #define WEBVFX_CONTENT_H_ 7 | 8 | #include "webvfx/effects.h" 9 | 10 | class QPainter; 11 | class QSize; 12 | class QString; 13 | class QUrl; 14 | class QWidget; 15 | 16 | namespace WebVfx 17 | { 18 | 19 | class Image; 20 | 21 | class Content 22 | { 23 | public: 24 | Content() {}; 25 | virtual ~Content() = 0; 26 | 27 | // Implementor should emit contentLoadFinished(bool) signal 28 | virtual void loadContent(const QUrl& url) = 0; 29 | virtual void setContentSize(const QSize& size) = 0; 30 | virtual const Effects::ImageTypeMap& getImageTypeMap() = 0; 31 | virtual void setImage(const QString& name, Image* image) = 0; 32 | virtual void setZoom(const qreal zoom) = 0; 33 | virtual bool renderContent(double time, Image* renderImage) = 0; 34 | virtual void paintContent(QPainter* painter) = 0; 35 | 36 | virtual QWidget* createView(QWidget* parent) = 0; 37 | virtual void reload() = 0; 38 | }; 39 | 40 | } 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /webvfx/content_context.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "webvfx/content_context.h" 5 | #include "webvfx/image.h" 6 | #include "webvfx/parameters.h" 7 | 8 | namespace WebVfx 9 | { 10 | 11 | ContentContext::ContentContext(QObject* parent, Parameters* parameters) 12 | : QObject(parent) 13 | , parameters(parameters) 14 | , renderCount(0) 15 | { 16 | } 17 | 18 | ContentContext::~ContentContext() 19 | { 20 | delete parameters; 21 | } 22 | 23 | void ContentContext::render(double time) 24 | { 25 | renderCount++; 26 | emit renderRequested(time); 27 | 28 | // Delete all QImage wrappers - data is not valid after render() 29 | imageMap.clear(); 30 | } 31 | 32 | void ContentContext::setImage(const QString& name, Image* image) 33 | { 34 | // Create a QImage wrapper for the image data 35 | QImage qimage((uchar*)image->pixels(), 36 | image->width(), image->height(), 37 | image->bytesPerLine(), image->hasAlpha() ? QImage::Format_RGBA8888 : QImage::Format_RGB888); 38 | imageMap.insert(name, qimage); 39 | } 40 | 41 | double ContentContext::getNumberParameter(const QString& name) 42 | { 43 | if (parameters) 44 | return parameters->getNumberParameter(name); 45 | else 46 | return 0; 47 | } 48 | 49 | QString ContentContext::getStringParameter(const QString& name) 50 | { 51 | if (parameters) 52 | return parameters->getStringParameter(name); 53 | else 54 | return QString(); 55 | } 56 | 57 | QVariantMap ContentContext::getRectParameter(const QString& name) 58 | { 59 | if (parameters) 60 | return parameters->getRectParameter(name); 61 | else 62 | return QVariantMap(); 63 | } 64 | 65 | void ContentContext::setImageTypeMap(const QVariantMap& variantMap) 66 | { 67 | // Convert QVariantMap to ImageTypeMap 68 | QMapIterator it(variantMap); 69 | while (it.hasNext()) { 70 | it.next(); 71 | //XXX validate the type enums 72 | imageTypeMap[it.key()] = static_cast(it.value().toInt()); 73 | } 74 | } 75 | 76 | // QtWebkit Bridge converts QImages to QPixmaps. 77 | // QML Qt3D QDeclarativeEffect::setTextureImage also does, 78 | // and it takes a QImage - so better to return QImage here 79 | // instead of QPixmap to avoid too many conversions. 80 | // One issue is the next time we write to the QImage, it's shared data 81 | // will be duplicated. But since it is being transferred to a QPixmap, 82 | // this shouldn't be an issue (i.e. we should be the only reference). 83 | // http://qt-project.org/doc/latest/qimage.html#bits 84 | QImage ContentContext::getImage(const QString& name) 85 | { 86 | return imageMap.value(name); 87 | } 88 | 89 | QUrl ContentContext::getImageUrl(const QString& name) 90 | { 91 | // Include renderCount in URL - this makes the URL unique 92 | // so QML will actually reload the image. 93 | return QUrl::fromEncoded(QString("image://webvfx/%1/%2").arg(name).arg(renderCount).toLatin1(), QUrl::StrictMode); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /webvfx/content_context.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef WEBVFX_CONTENT_CONTEXT_H_ 6 | #define WEBVFX_CONTENT_CONTEXT_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "webvfx/effects.h" 15 | 16 | class QSize; 17 | class QString; 18 | 19 | namespace WebVfx 20 | { 21 | 22 | class Image; 23 | class Parameters; 24 | 25 | // See QtWebKit Bridge docs: 26 | // http://qt-project.org/doc/latest/qtwebkit-bridge.html 27 | 28 | class ContentContext : public QObject 29 | { 30 | Q_OBJECT 31 | //XXX We could use Q_ENUMS for this once this bug is fixed 32 | // https://bugreports.qt-project.org/browse/QTBUG-12706 33 | Q_PROPERTY(int SourceImageType READ getSourceImageType CONSTANT FINAL) 34 | Q_PROPERTY(int TargetImageType READ getTargetImageType CONSTANT FINAL) 35 | Q_PROPERTY(int ExtraImageType READ getExtraImageType CONSTANT FINAL) 36 | // Page contents should set this if it consumes images. 37 | // JS: 38 | // webvfx.imageTypeMap = { "video" : webvfx.SourceImageType } 39 | Q_PROPERTY(QVariantMap imageTypeMap WRITE setImageTypeMap) 40 | 41 | public: 42 | ContentContext(QObject* parent, Parameters* parameters); 43 | ~ContentContext(); 44 | 45 | // Inform page contents to render at time. 46 | // emits renderRequested signal to the page contents. 47 | void render(double time); 48 | 49 | // Set an image for the given name. 50 | // image must remain valid until render() is called. 51 | void setImage(const QString& name, Image* image); 52 | 53 | // Page contents can use these to retrieve parameters. 54 | // JS: var title = webvfx.getStringParameter("title"); 55 | Q_INVOKABLE double getNumberParameter(const QString& name); 56 | Q_INVOKABLE QString getStringParameter(const QString& name); 57 | Q_INVOKABLE QVariantMap getRectParameter(const QString& name); 58 | 59 | // Page contents can retrieve named images. 60 | // JS: 61 | // var image = new Image(); 62 | // webvfx.getImage("video").assignToHTMLElement(image); 63 | Q_INVOKABLE QImage getImage(const QString& name); 64 | 65 | // Return URL for use in QML to reference the named image 66 | Q_INVOKABLE QUrl getImageUrl(const QString& name); 67 | 68 | int getSourceImageType() { return Effects::SourceImageType; } 69 | int getTargetImageType() { return Effects::TargetImageType; } 70 | int getExtraImageType() { return Effects::ExtraImageType; } 71 | void setImageTypeMap(const QVariantMap& imageTypeMap); 72 | 73 | const Effects::ImageTypeMap& getImageTypeMap() { return imageTypeMap; } 74 | 75 | signals: 76 | // Page contents must signal this when ready to render. 77 | // status indicates setup failure/success. 78 | void readyRender(bool status); 79 | 80 | // Signal raised when page contents should render for the given time. 81 | // time is normalized 0..1.0 82 | // JS: webvfx.renderRequested.connect(function (time) { doSomething(); }) 83 | void renderRequested(double time); 84 | 85 | private: 86 | Parameters* parameters; 87 | QHash imageMap; 88 | Effects::ImageTypeMap imageTypeMap; 89 | unsigned int renderCount; 90 | }; 91 | 92 | } 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /webvfx/effects.cpp: -------------------------------------------------------------------------------- 1 | #include "webvfx/effects.h" 2 | 3 | WebVfx::Effects::~Effects() 4 | { 5 | } 6 | 7 | -------------------------------------------------------------------------------- /webvfx/effects.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef WEBVFX_EFFECTS_H_ 6 | #define WEBVFX_EFFECTS_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | namespace WebVfx 14 | { 15 | 16 | /*! 17 | * @brief An effects implementation that can consume video frame images and 18 | * render output. 19 | * 20 | * Instances of this class are created with WebVfx::createEffects() 21 | * and can be accessed from any thread, but the class is not threadsafe 22 | * so access should be synchronized. 23 | */ 24 | class Effects 25 | { 26 | public: 27 | /*! 28 | * @brief Describes the type of an image. 29 | */ 30 | enum ImageType { 31 | /*! 32 | * The source (origin/from) image in a transition, 33 | * or the image being processed in a filter. 34 | */ 35 | SourceImageType=1, 36 | /*! 37 | * The target (destination/to) image in a transition. 38 | */ 39 | TargetImageType, 40 | /*! 41 | * An extra image not directly participating in a 42 | * filter or transition. 43 | */ 44 | ExtraImageType 45 | }; 46 | 47 | typedef QMap ImageTypeMap; 48 | typedef QMapIterator ImageTypeMapIterator; 49 | 50 | /*! 51 | * @brief Describes the image names this effect will request. 52 | * 53 | * Images for these names will need to be set using setImage() 54 | * each time before render() is called. 55 | * @return a map mapping image names to their ImageType 56 | */ 57 | virtual const ImageTypeMap& getImageTypeMap() = 0; 58 | 59 | /*! 60 | * @brief Set an Image for the given @c name. 61 | * 62 | * @param name Name of the image 63 | * @param image Image data. The Image pixels must remain valid until 64 | * render() is called. 65 | */ 66 | virtual void setImage(const QString& name, Image* image) = 0; 67 | 68 | /*! 69 | * @brief Set the zoom level. 70 | * 71 | * @param zoom the zoom factor 72 | */ 73 | virtual void setZoom(const qreal zoom) = 0; 74 | 75 | /*! 76 | * @brief Renders the effect for the given @c time. 77 | * 78 | * Prior to calling render() each time, all named images must 79 | * be set via setImage(). 80 | * @param time Time to render image for, must be from 0 to 1.0. 81 | * @param renderImage Image buffer to render into. 82 | */ 83 | virtual bool render(double time, Image* renderImage) = 0; 84 | 85 | /*! 86 | * @brief Destroy the effect 87 | */ 88 | virtual void destroy() = 0; 89 | 90 | /*! 91 | * @brief Indicate that rendering is done. 92 | * 93 | * @param result This is the success/fail value to return from render(). 94 | */ 95 | virtual void renderComplete(bool result) = 0; 96 | 97 | /*! 98 | * @brief Reload the content. 99 | */ 100 | virtual void reload() = 0; 101 | 102 | protected: 103 | Effects() {}; 104 | virtual ~Effects() = 0; 105 | }; 106 | 107 | } 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /webvfx/effects_impl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "webvfx/content.h" 11 | #include "webvfx/effects_impl.h" 12 | #include "webvfx/image.h" 13 | #include "webvfx/parameters.h" 14 | #include "webvfx/qml_content.h" 15 | #include "webvfx/web_content.h" 16 | #include "webvfx/webvfx.h" 17 | 18 | 19 | namespace WebVfx 20 | { 21 | 22 | EffectsImpl::EffectsImpl() 23 | : QObject(0) 24 | , content(0) 25 | , mutex(0) 26 | , waitCondition(0) 27 | , initializeResult(false) 28 | , renderResult(false) 29 | { 30 | } 31 | 32 | EffectsImpl::~EffectsImpl() 33 | { 34 | delete content; 35 | } 36 | 37 | bool EffectsImpl::initialize(const QString& fileName, int width, int height, Parameters* parameters, bool isTransparent) 38 | { 39 | if (onUIThread()) { 40 | log("WebVfx::Effects cannot be initialized on main UI thread."); 41 | return false; 42 | } 43 | 44 | QUrl url(fileName); 45 | bool isPlain = (url.scheme() == "plain"); 46 | if (isPlain) { 47 | // Remove the "plain:" prefix interpeted as the scheme. 48 | url = QUrl(url.toString(QUrl::RemoveScheme)); 49 | } 50 | // If no scheme or the scheme is a Windows drive letter. 51 | if (url.scheme().size() < 2) { 52 | // Prepend the file scheme. 53 | url = QUrl::fromLocalFile(QFileInfo(url.toString()).absoluteFilePath()); 54 | if (!url.isValid()) { 55 | log(QLatin1Literal("Invalid URL: ") % fileName); 56 | return false; 57 | } 58 | } 59 | 60 | QSize size(width, height); 61 | 62 | QMutex mutex; 63 | QWaitCondition waitCondition; 64 | this->mutex = &mutex; 65 | this->waitCondition = &waitCondition; 66 | { 67 | QMutexLocker locker(&mutex); 68 | // Move ourself onto GUI thread and create our Content there. 69 | // Invoke this async then wait for result. 70 | this->moveToThread(QApplication::instance()->thread()); 71 | QMetaObject::invokeMethod(this, "initializeInvokable", 72 | Qt::QueuedConnection, 73 | Q_ARG(QUrl, url), Q_ARG(QSize, size), 74 | Q_ARG(Parameters*, parameters), 75 | Q_ARG(bool, isPlain), 76 | Q_ARG(bool, isTransparent)); 77 | //XXX should we wait with a timeout and fail if expires? 78 | waitCondition.wait(&mutex); 79 | } 80 | this->mutex = 0; 81 | this->waitCondition = 0; 82 | return initializeResult; 83 | } 84 | 85 | void EffectsImpl::initializeComplete(bool result) 86 | { 87 | if (mutex && waitCondition) { 88 | QMutexLocker locker(mutex); 89 | initializeResult = result; 90 | waitCondition->wakeAll(); 91 | } 92 | } 93 | 94 | void EffectsImpl::destroy() 95 | { 96 | deleteLater(); 97 | } 98 | 99 | bool EffectsImpl::onUIThread() { 100 | return QThread::currentThread() == QApplication::instance()->thread(); 101 | } 102 | 103 | const Effects::ImageTypeMap& EffectsImpl::getImageTypeMap() 104 | { 105 | return content->getImageTypeMap(); 106 | } 107 | 108 | void EffectsImpl::setImage(const QString& name, Image* image) 109 | { 110 | // This may create a QImage and modify QHash - both of those classes 111 | // are reentrant, so should be safe to do on calling thread as long 112 | // as access to this EffectsImpl is synchronized. 113 | content->setImage(name, image); 114 | } 115 | 116 | void EffectsImpl::setZoom(const qreal zoom) 117 | { 118 | content->setZoom(zoom); 119 | } 120 | 121 | bool EffectsImpl::render(double time, Image* renderImage) 122 | { 123 | if (onUIThread()) { 124 | renderInvokable(time, renderImage); 125 | } 126 | else { 127 | QMutex mutex; 128 | QWaitCondition waitCondition; 129 | this->mutex = &mutex; 130 | this->waitCondition = &waitCondition; 131 | { 132 | QMutexLocker locker(&mutex); 133 | QMetaObject::invokeMethod(this, "renderInvokable", 134 | Qt::QueuedConnection, 135 | Q_ARG(double, time), 136 | Q_ARG(Image*, renderImage)); 137 | //XXX should we wait with a timeout and fail if expires? 138 | waitCondition.wait(&mutex); 139 | } 140 | this->mutex = 0; 141 | this->waitCondition = 0; 142 | } 143 | return renderResult; 144 | } 145 | 146 | void EffectsImpl::renderComplete(bool result) 147 | { 148 | if (mutex && waitCondition) { 149 | QMutexLocker locker(mutex); 150 | renderResult = result; 151 | waitCondition->wakeAll(); 152 | } 153 | } 154 | 155 | void EffectsImpl::reload() 156 | { 157 | if (onUIThread()) { 158 | reloadInvokable(); 159 | } 160 | else { 161 | QMutex mutex; 162 | QWaitCondition waitCondition; 163 | this->mutex = &mutex; 164 | this->waitCondition = &waitCondition; 165 | { 166 | QMutexLocker locker(&mutex); 167 | QMetaObject::invokeMethod(this, "reloadInvokable", Qt::QueuedConnection); 168 | //XXX should we wait with a timeout and fail if expires? 169 | waitCondition.wait(&mutex); 170 | } 171 | this->mutex = 0; 172 | this->waitCondition = 0; 173 | } 174 | } 175 | 176 | void EffectsImpl::initializeInvokable(const QUrl& url, const QSize& size, Parameters* parameters, bool isPlain, bool isTransparent) 177 | { 178 | QString path(url.path()); 179 | // We can't parent QmlContent since we aren't a QWidget. 180 | // So don't parent either content, and destroy them explicitly. 181 | if (path.endsWith(".html", Qt::CaseInsensitive) || path.endsWith(".htm", Qt::CaseInsensitive) || !url.isLocalFile()) { 182 | WebContent* webContent = new WebContent(size, parameters); 183 | content = webContent; 184 | if (isTransparent) 185 | webContent->setTransparent(); 186 | 187 | if (isPlain) { 188 | connect(webContent, SIGNAL(contentPreLoadFinished(bool)), SLOT(initializeComplete(bool))); 189 | } 190 | else { 191 | connect(webContent, SIGNAL(contentLoadFinished(bool)), SLOT(initializeComplete(bool))); 192 | } 193 | } 194 | else if (path.endsWith(".qml", Qt::CaseInsensitive)) { 195 | QmlContent* qmlContent = new QmlContent(size, parameters); 196 | content = qmlContent; 197 | if (isPlain) { 198 | connect(qmlContent, SIGNAL(contentPreLoadFinished(bool)), SLOT(initializeComplete(bool))); 199 | } 200 | else { 201 | connect(qmlContent, SIGNAL(contentLoadFinished(bool)), SLOT(initializeComplete(bool))); 202 | } 203 | } 204 | else { 205 | log(QLatin1Literal("WebVfx Filename must end with '.html', '.htm', or '.qml': ") % path); 206 | return; 207 | } 208 | 209 | content->loadContent(url); 210 | } 211 | 212 | void EffectsImpl::renderInvokable(double time, Image* renderImage) 213 | { 214 | content->setContentSize(QSize(renderImage->width(), renderImage->height())); 215 | renderComplete(content->renderContent(time, renderImage)); 216 | } 217 | 218 | void EffectsImpl::reloadInvokable() 219 | { 220 | content->reload(); 221 | } 222 | 223 | } 224 | -------------------------------------------------------------------------------- /webvfx/effects_impl.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef WEBVFX_EFFECTS_IMPL_H_ 6 | #define WEBVFX_EFFECTS_IMPL_H_ 7 | 8 | #include 9 | #include "webvfx/effects.h" 10 | 11 | class QMutex; 12 | class QWaitCondition; 13 | class QSize; 14 | class QString; 15 | class QUrl; 16 | 17 | namespace WebVfx 18 | { 19 | 20 | class Content; 21 | class Image; 22 | class Parameters; 23 | 24 | class EffectsImpl : public QObject, public Effects 25 | { 26 | Q_OBJECT 27 | public: 28 | EffectsImpl(); 29 | 30 | // EffectsImpl will take ownership of Parameters 31 | bool initialize(const QString& fileName, int width, int height, Parameters* parameters = 0, bool isTransparent = false); 32 | const ImageTypeMap& getImageTypeMap(); 33 | void setImage(const QString& name, Image* image); 34 | void setZoom(const qreal zoom); 35 | bool render(double time, Image* renderImage); 36 | void destroy(); 37 | void renderComplete(bool result); 38 | void reload(); 39 | 40 | private slots: 41 | void initializeComplete(bool result); 42 | 43 | private: 44 | ~EffectsImpl(); 45 | Q_INVOKABLE void initializeInvokable(const QUrl& url, const QSize& size, Parameters* parameters, bool isPlain, bool isTransparent = false); 46 | Q_INVOKABLE void renderInvokable(double time, Image* renderImage); 47 | Q_INVOKABLE void reloadInvokable(); 48 | 49 | // Test if we are currently on the UI thread 50 | bool onUIThread(); 51 | 52 | Content* content; 53 | QMutex* mutex; 54 | QWaitCondition* waitCondition; 55 | bool initializeResult; 56 | bool renderResult; 57 | }; 58 | 59 | } 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /webvfx/image.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "webvfx/image.h" 4 | 5 | namespace WebVfx 6 | { 7 | 8 | const int Image::BytesPerPixel; 9 | 10 | void Image::copyPixels(const Image& sourceImage, Image& targetImage) 11 | { 12 | if (targetImage.byteCount_ == sourceImage.byteCount_) 13 | memcpy(targetImage.pixels_, sourceImage.pixels_, targetImage.byteCount_); 14 | else { 15 | unsigned char* sourceP = sourceImage.pixels_; 16 | int sourceRowBytes = sourceImage.bytesPerLine(); 17 | unsigned char* targetP = targetImage.pixels_; 18 | int targetRowBytes = targetImage.bytesPerLine(); 19 | for (int i = 0; i < targetImage.height_; i++) { 20 | memcpy(targetP, sourceP, targetRowBytes); 21 | sourceP += sourceRowBytes; 22 | targetP += targetRowBytes; 23 | } 24 | } 25 | } 26 | 27 | void Image::copyPixelsFrom(const Image& sourceImage) { 28 | Q_ASSERT(compatible(sourceImage)); 29 | copyPixels(sourceImage, *this); 30 | } 31 | 32 | void Image::copyPixelsTo(Image& targetImage) const { 33 | Q_ASSERT(compatible(targetImage)); 34 | copyPixels(*this, targetImage); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /webvfx/image.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef WEBVFX_IMAGE_H_ 6 | #define WEBVFX_IMAGE_H_ 7 | 8 | namespace WebVfx 9 | { 10 | 11 | /*! 12 | * @brief Lightweight class for passing around a reference to a buffer of 13 | * raw image data. 14 | * 15 | * Image is a wrapper around a buffer of RGB(A) image data, 16 | * along with metadata about it (width, height, bytes per row etc.). 17 | * The underlying image data buffer is not owned by Image and 18 | * its lifetime must exceed that of the referencing Image instance. 19 | */ 20 | class Image 21 | { 22 | public: 23 | //! @deprecated Number of bytes per pixel. 24 | static const int BytesPerPixel = 3; 25 | 26 | Image() 27 | : pixels_(0) 28 | , width_(0) 29 | , height_(0) 30 | , byteCount_(0) 31 | , hasAlpha_(false) {} 32 | 33 | /*! 34 | * @param pixels 24-bit RGB or 32-bit RGBA image data. Must be valid for the lifetime 35 | * of the Image. 36 | * @param width Width of the image in pixels 37 | * @param height Height of the image in pixels 38 | * @param byteCount Number of bytes in the image. 39 | * A row can have more than @c width * @c BytesPerPixel bytes. 40 | */ 41 | Image(unsigned char* pixels, int width, int height, int byteCount, bool hasAlpha = false) 42 | : pixels_(pixels) 43 | , width_(width) 44 | , height_(height) 45 | , byteCount_(byteCount) 46 | , hasAlpha_(hasAlpha) {} 47 | 48 | bool isNull() { return !pixels_; } 49 | unsigned char* pixels() { return pixels_; } 50 | const unsigned char* pixels() const { return pixels_; } 51 | int width() const { return width_; } 52 | int height() const { return height_; } 53 | int bytesPerLine() const { return (height_ > 0)? (byteCount_ / height_) : 0; } 54 | int byteCount() const { return byteCount_; } 55 | bool hasAlpha() const { return hasAlpha_; } 56 | 57 | void copyPixelsFrom(const Image& sourceImage); 58 | void copyPixelsTo(Image& targetImage) const; 59 | bool compatible(const Image& image) const { 60 | return (image.pixels_ && pixels_ 61 | && image.pixels_ != pixels_ 62 | && image.width_ == width_ 63 | && image.height_ == height_); 64 | } 65 | 66 | private: 67 | static void copyPixels(const Image& sourceImage, Image& targetImage); 68 | 69 | unsigned char* pixels_; 70 | int width_; 71 | int height_; 72 | int byteCount_; 73 | bool hasAlpha_; 74 | }; 75 | 76 | } 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /webvfx/logger.cpp: -------------------------------------------------------------------------------- 1 | #include "webvfx/logger.h" 2 | 3 | WebVfx::Logger::~Logger() 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /webvfx/logger.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef WEBVFX_LOGGER_H_ 6 | #define WEBVFX_LOGGER_H_ 7 | 8 | class QString; 9 | 10 | namespace WebVfx { 11 | 12 | /*! 13 | * @brief Callback interface to expose logging. 14 | * 15 | * An instance of this class should be passed to WebVfx::setLogger(). 16 | * WebVfx will then log all messages using that instance. 17 | */ 18 | class Logger 19 | { 20 | public: 21 | virtual ~Logger() = 0; 22 | 23 | /*! 24 | * @brief Called whenever WebVfx needs to log a message. 25 | * 26 | * @param msg Message to be logged. 27 | */ 28 | //XXX should add log level enum 29 | virtual void log(const QString& msg) = 0; 30 | }; 31 | 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /webvfx/parameters.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "webvfx/parameters.h" 3 | 4 | WebVfx::Parameters::~Parameters() 5 | { 6 | } 7 | 8 | double WebVfx::Parameters::getNumberParameter(const QString&) 9 | { 10 | return 0; 11 | } 12 | 13 | QString WebVfx::Parameters::getStringParameter(const QString&) 14 | { 15 | return QString(); 16 | } 17 | 18 | QVariantMap WebVfx::Parameters::getRectParameter(const QString&) 19 | { 20 | return QVariantMap(); 21 | } 22 | -------------------------------------------------------------------------------- /webvfx/parameters.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef WEBVFX_PARAMETERS_H_ 6 | #define WEBVFX_PARAMETERS_H_ 7 | 8 | #include 9 | 10 | class QString; 11 | 12 | namespace WebVfx 13 | { 14 | 15 | /*! 16 | * @brief Callback interface to expose named parameter values to an effect. 17 | * 18 | * An instance of this class should be passed to WebVfx::createEffects() 19 | * to provide the parameters for that Effects instance. 20 | */ 21 | class Parameters 22 | { 23 | public: 24 | Parameters() {}; 25 | virtual ~Parameters() = 0; 26 | /*! 27 | * @brief Return a numeric value for parameter @c name. 28 | * 29 | * @param name Parameter name 30 | */ 31 | virtual double getNumberParameter(const QString& name); 32 | /*! 33 | * @brief Return a string value for parameter @c name. 34 | * 35 | * @param name Parameter name 36 | */ 37 | virtual QString getStringParameter(const QString& name); 38 | /*! 39 | * @brief Return a rectangle value for parameter @c name. 40 | * 41 | * Rectangle is a map of doubles with the following keys: 42 | * - x 43 | * - y 44 | * - width 45 | * - height 46 | * - opacity 47 | * @param name Parameter name 48 | */ 49 | virtual QVariantMap getRectParameter(const QString& name); 50 | }; 51 | 52 | } 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /webvfx/qml_content.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "webvfx/image.h" 14 | #include "webvfx/qml_content.h" 15 | #include "webvfx/render_strategy.h" 16 | #include "webvfx/webvfx.h" 17 | 18 | 19 | namespace WebVfx 20 | { 21 | 22 | class PixmapProvider : public QQuickImageProvider 23 | { 24 | public: 25 | PixmapProvider(ContentContext* contentContext) 26 | : QQuickImageProvider(QQuickImageProvider::Pixmap) 27 | , contentContext(contentContext) 28 | { 29 | } 30 | 31 | QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize) 32 | { 33 | // URLs are of the form image://webvfx// 34 | // where is a unique ID to force refresh and is ignored. 35 | QImage image(contentContext->getImage(id.section('/', 0, 0))); 36 | QPixmap pixmap(QPixmap::fromImage(image)); 37 | 38 | if (size) 39 | *size = pixmap.size(); 40 | 41 | if (!requestedSize.isEmpty()) 42 | return pixmap.scaled(requestedSize, Qt::IgnoreAspectRatio, 43 | Qt::SmoothTransformation); 44 | 45 | return pixmap; 46 | } 47 | 48 | private: 49 | ContentContext* contentContext; 50 | }; 51 | 52 | //////////////////// 53 | 54 | QmlContent::QmlContent(const QSize& size, Parameters* parameters) 55 | : QQuickView(0) 56 | , pageLoadFinished(LoadNotFinished) 57 | , contextLoadFinished(LoadNotFinished) 58 | , contentContext(new ContentContext(this, parameters)) 59 | , m_zoom(1.0) 60 | { 61 | // Add root of our qrc:/ resource path so embedded QML components are available. 62 | engine()->addImportPath(":/"); 63 | 64 | /* setInteractive(false); */ 65 | setResizeMode(QQuickView::SizeRootObjectToView); 66 | /* setResizeAnchor(QDeclarativeView::AnchorViewCenter); */ 67 | resize(size); 68 | setColor(Qt::transparent); 69 | 70 | // Expose context to the QML 71 | rootContext()->setContextProperty("webvfx", contentContext); 72 | 73 | // Register image provider for image://webvfx// 74 | engine()->addImageProvider(QLatin1String("webvfx"), new PixmapProvider(contentContext)); 75 | 76 | connect(this, SIGNAL(statusChanged(QQuickView::Status)), SLOT(qmlViewStatusChanged(QQuickView::Status))); 77 | connect(engine(), SIGNAL(warnings(QList)), SLOT(logWarnings(QList))); 78 | connect(contentContext, SIGNAL(readyRender(bool)), SLOT(contentContextLoadFinished(bool))); 79 | } 80 | 81 | QmlContent::~QmlContent() 82 | { 83 | } 84 | 85 | void QmlContent::qmlViewStatusChanged(QQuickView::Status status) 86 | { 87 | if (status != QQuickView::Ready && status != QQuickView::Error) 88 | return; 89 | 90 | if (pageLoadFinished == LoadNotFinished) 91 | pageLoadFinished = (status == QQuickView::Ready) ? LoadSucceeded : LoadFailed; 92 | 93 | // This is useful when webvfx.renderReady(true) is not used. 94 | emit contentPreLoadFinished(pageLoadFinished == LoadSucceeded); 95 | 96 | if (pageLoadFinished == LoadFailed || contextLoadFinished != LoadNotFinished) { 97 | logWarnings(errors()); 98 | emit contentLoadFinished(contextLoadFinished == LoadSucceeded && pageLoadFinished == LoadSucceeded); 99 | } 100 | } 101 | 102 | QWidget* QmlContent::createView(QWidget* parent) 103 | { 104 | return QWidget::createWindowContainer(this, parent); 105 | } 106 | 107 | void QmlContent::contentContextLoadFinished(bool result) 108 | { 109 | if (contextLoadFinished == LoadNotFinished) 110 | contextLoadFinished = result ? LoadSucceeded : LoadFailed; 111 | if (contextLoadFinished == LoadFailed || pageLoadFinished != LoadNotFinished) { 112 | logWarnings(errors()); 113 | emit contentLoadFinished(contextLoadFinished == LoadSucceeded && pageLoadFinished == LoadSucceeded); 114 | } 115 | } 116 | 117 | void QmlContent::logWarnings(const QList& warnings) 118 | { 119 | foreach (const QQmlError& warning, warnings) { 120 | log(warning.toString()); 121 | } 122 | } 123 | 124 | void QmlContent::loadContent(const QUrl& url) 125 | { 126 | pageLoadFinished = LoadNotFinished; 127 | contextLoadFinished = LoadNotFinished; 128 | 129 | setSource(url); 130 | 131 | /* grabWindow will not work until a gl context has been initialized, which does not happen until 132 | * the quickwindow has been shown once */ 133 | show(); 134 | } 135 | 136 | void QmlContent::setContentSize(const QSize& size) 137 | { 138 | if (m_zoom > 0.0) 139 | resize(size.width() / m_zoom, size.height() / m_zoom); 140 | else 141 | resize(size); 142 | } 143 | 144 | bool QmlContent::renderContent(double time, Image* renderImage) 145 | { 146 | contentContext->render(time); 147 | 148 | if (renderImage) { 149 | hide(); 150 | QImage sourceImage = grabWindow(); 151 | QImage targetImage((uchar*)renderImage->pixels(), renderImage->width(), 152 | renderImage->height(), renderImage->bytesPerLine(), 153 | renderImage->hasAlpha()? QImage::Format_RGBA8888 : QImage::Format_RGB888); 154 | QPainter p(&targetImage); 155 | p.drawImage(QPoint(), sourceImage); 156 | m_mostRecentImage = sourceImage; 157 | } 158 | logWarnings(errors()); 159 | return true; 160 | } 161 | 162 | void QmlContent::paintContent(QPainter* painter) 163 | { 164 | painter->drawImage(QPoint(), m_mostRecentImage); 165 | } 166 | 167 | void QmlContent::setZoom(const qreal zoom) 168 | { 169 | if (rootObject()) { 170 | rootObject()->setTransformOrigin(QQuickItem::TopLeft); 171 | rootObject()->setScale(zoom); 172 | m_zoom = zoom; 173 | } 174 | } 175 | 176 | void QmlContent::reload() 177 | { 178 | engine()->clearComponentCache(); 179 | setSource(source()); 180 | } 181 | 182 | } 183 | -------------------------------------------------------------------------------- /webvfx/qml_content.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef WEBVFX_QML_CONTENT_H_ 6 | #define WEBVFX_QML_CONTENT_H_ 7 | 8 | #include 9 | #include 10 | #include "webvfx/content.h" 11 | #include "webvfx/content_context.h" 12 | #include "webvfx/effects.h" 13 | #include "webvfx/image.h" 14 | 15 | class QSize; 16 | class QUrl; 17 | 18 | namespace WebVfx 19 | { 20 | 21 | class Image; 22 | class Parameters; 23 | 24 | class QmlContent : public QQuickView, public virtual Content 25 | { 26 | Q_OBJECT 27 | public: 28 | QmlContent(const QSize& size, Parameters* parameters); 29 | ~QmlContent(); 30 | 31 | void loadContent(const QUrl& url); 32 | void setContentSize(const QSize& size); 33 | const Effects::ImageTypeMap& getImageTypeMap() { return contentContext->getImageTypeMap(); }; 34 | bool renderContent(double time, Image* renderImage); 35 | void paintContent(QPainter* painter); 36 | void setImage(const QString& name, Image* image) { contentContext->setImage(name, image); } 37 | void setZoom(const qreal zoom); 38 | void reload(); 39 | 40 | QWidget* createView(QWidget* parent); 41 | 42 | signals: 43 | void contentLoadFinished(bool result); 44 | void contentPreLoadFinished(bool result); 45 | 46 | private slots: 47 | void qmlViewStatusChanged(QQuickView::Status status); 48 | void contentContextLoadFinished(bool result); 49 | void logWarnings(const QList& warnings); 50 | 51 | private: 52 | enum LoadStatus { LoadNotFinished, LoadFailed, LoadSucceeded }; 53 | LoadStatus pageLoadFinished; 54 | LoadStatus contextLoadFinished; 55 | ContentContext* contentContext; 56 | QImage m_mostRecentImage; 57 | qreal m_zoom; 58 | }; 59 | 60 | } 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /webvfx/render_strategy.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "webvfx/image.h" 7 | #include "webvfx/content.h" 8 | #include "webvfx/render_strategy.h" 9 | #include "webvfx/webvfx.h" 10 | 11 | 12 | namespace WebVfx 13 | { 14 | 15 | GLWidgetRenderStrategy::GLWidgetRenderStrategy(QGLWidget* glWidget) 16 | : glWidget(glWidget) 17 | , fbo(0) 18 | { 19 | glWidget->makeCurrent(); 20 | if (!QGLFramebufferObject::hasOpenGLFramebufferObjects() 21 | || !QGLFramebufferObject::hasOpenGLFramebufferBlit()) { 22 | log("GLWidgetRenderStrategy: FBOs not fully supported, GL rendering will not work"); 23 | } 24 | glWidget->doneCurrent(); 25 | } 26 | 27 | GLWidgetRenderStrategy::~GLWidgetRenderStrategy() 28 | { 29 | delete fbo; 30 | } 31 | 32 | void GLWidgetRenderStrategy::createFBO(const QSize& size) 33 | { 34 | if (fbo && fbo->size() == size) 35 | return; 36 | 37 | delete fbo; 38 | fbo = new QGLFramebufferObject(size, GL_TEXTURE_2D); 39 | } 40 | 41 | 42 | bool GLWidgetRenderStrategy::render(Content* content, Image* renderImage) 43 | { 44 | if (!renderImage) 45 | return false; 46 | 47 | glWidget->makeCurrent(); 48 | QSize size(renderImage->width(), renderImage->height()); 49 | createFBO(size); 50 | 51 | // Render frame into QGLWidget. 52 | // This isn't really valid for a hidden QGLWidget due to the OpenGL 53 | // "pixel ownership test". But it works with some graphics drivers. 54 | 55 | fbo->bind(); 56 | glClear(GL_COLOR_BUFFER_BIT); 57 | QPainter painter(fbo); 58 | painter.translate(0, size.height()); 59 | painter.scale(1, -1); 60 | painter.setRenderHints(QPainter::Antialiasing | 61 | QPainter::TextAntialiasing | 62 | QPainter::SmoothPixmapTransform, true); 63 | content->paintContent(&painter); 64 | painter.end(); 65 | 66 | glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); 67 | glPixelStorei(GL_PACK_ALIGNMENT, 1); 68 | if (renderImage->hasAlpha()) { 69 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 70 | glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 71 | glReadPixels(0, 0, renderImage->width(), renderImage->height(), 72 | GL_RGBA, GL_UNSIGNED_BYTE, renderImage->pixels()); 73 | } 74 | else { 75 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 76 | glPixelStorei(GL_UNPACK_ALIGNMENT, 3); 77 | glReadPixels(0, 0, renderImage->width(), renderImage->height(), 78 | GL_RGB, GL_UNSIGNED_BYTE, renderImage->pixels()); 79 | } 80 | glPopClientAttrib(); 81 | fbo->release(); 82 | glWidget->doneCurrent(); 83 | 84 | return true; 85 | } 86 | 87 | FBORenderStrategy::FBORenderStrategy(QGLWidget* glWidget) 88 | : glWidget(glWidget) 89 | , multisampleFBO(0) 90 | , resolveFBO(0) 91 | { 92 | glWidget->makeCurrent(); 93 | if (!QGLFramebufferObject::hasOpenGLFramebufferObjects() 94 | || !QGLFramebufferObject::hasOpenGLFramebufferBlit()) { 95 | log("FBORenderStrategy: FBOs not fully supported, antialiasing will not work"); 96 | } 97 | glWidget->doneCurrent(); 98 | } 99 | 100 | FBORenderStrategy::~FBORenderStrategy() 101 | { 102 | delete multisampleFBO; 103 | delete resolveFBO; 104 | } 105 | 106 | void FBORenderStrategy::createFBOs(const QSize& size) 107 | { 108 | if (multisampleFBO && resolveFBO && resolveFBO->size() == size) 109 | return; 110 | 111 | // Create a multisample FBO and an FBO to resolve into 112 | QGLFramebufferObjectFormat fboFormat; 113 | fboFormat.setSamples(4); 114 | fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil); 115 | delete multisampleFBO; 116 | multisampleFBO = new QGLFramebufferObject(size, fboFormat); 117 | delete resolveFBO; 118 | resolveFBO = new QGLFramebufferObject(size); 119 | } 120 | 121 | bool FBORenderStrategy::render(Content* content, Image* renderImage) 122 | { 123 | if (!renderImage) 124 | return false; 125 | 126 | glWidget->makeCurrent(); 127 | QSize size(renderImage->width(), renderImage->height()); 128 | createFBOs(size); 129 | 130 | // Render frame into multisample FBO 131 | QPainter painter(multisampleFBO); 132 | painter.setRenderHints(QPainter::Antialiasing | 133 | QPainter::TextAntialiasing | 134 | QPainter::SmoothPixmapTransform, true); 135 | content->paintContent(&painter); 136 | painter.end(); 137 | 138 | // Blit from multisample to resolve FBO. 139 | // Rects are setup so the image is vertically flipped when blitted 140 | // so when we read the pixels back they are the right way up. 141 | // OpenGL does everything "upside down". 142 | QRect srcRect(0, 0, renderImage->width(), renderImage->height()); 143 | QRect dstRect(0, renderImage->height(), 144 | renderImage->width(), -renderImage->height()); 145 | QGLFramebufferObject::blitFramebuffer(resolveFBO, srcRect, 146 | multisampleFBO, dstRect); 147 | 148 | // Read back the pixels from the resolve FBO 149 | resolveFBO->bind(); 150 | glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); 151 | glPixelStorei(GL_PACK_ALIGNMENT, 1); 152 | 153 | if (renderImage->hasAlpha()) { 154 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 155 | glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 156 | glReadPixels(0, 0, renderImage->width(), renderImage->height(), 157 | GL_RGBA, GL_UNSIGNED_BYTE, renderImage->pixels()); 158 | } 159 | else { 160 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 161 | glPixelStorei(GL_UNPACK_ALIGNMENT, 3); 162 | glReadPixels(0, 0, renderImage->width(), renderImage->height(), 163 | GL_RGB, GL_UNSIGNED_BYTE, renderImage->pixels()); 164 | } 165 | 166 | glPopClientAttrib(); 167 | 168 | resolveFBO->release(); 169 | glWidget->doneCurrent(); 170 | 171 | return true; 172 | } 173 | 174 | bool ImageRenderStrategy::render(Content* content, Image* renderImage) 175 | { 176 | if (!renderImage || !renderImage->pixels() || !renderImage->width() || !renderImage->height()) 177 | return false; 178 | // QImage referencing our Image bits 179 | QImage image((uchar*)renderImage->pixels(), renderImage->width(), 180 | renderImage->height(), renderImage->bytesPerLine(), 181 | renderImage->hasAlpha()? QImage::Format_RGBA8888 : QImage::Format_RGB888); 182 | // Paint into image 183 | QPainter painter(&image); 184 | painter.setRenderHints(QPainter::Antialiasing | 185 | QPainter::TextAntialiasing | 186 | QPainter::SmoothPixmapTransform, true); 187 | content->paintContent(&painter); 188 | painter.end(); 189 | return true; 190 | } 191 | 192 | } 193 | -------------------------------------------------------------------------------- /webvfx/render_strategy.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef WEBVFX_RENDER_STRATEGY_H_ 6 | #define WEBVFX_RENDER_STRATEGY_H_ 7 | 8 | #include 9 | 10 | class QGLFramebufferObject; 11 | class QGLWidget; 12 | 13 | namespace WebVfx 14 | { 15 | 16 | class Content; 17 | class Image; 18 | 19 | class RenderStrategy 20 | { 21 | public: 22 | RenderStrategy() {} 23 | virtual ~RenderStrategy() {} 24 | virtual bool render(Content* content, Image* renderImage) = 0; 25 | }; 26 | 27 | class GLWidgetRenderStrategy : public RenderStrategy 28 | { 29 | public: 30 | // Renderer does not take ownership of QGLWidget 31 | GLWidgetRenderStrategy(QGLWidget* glWidget); 32 | ~GLWidgetRenderStrategy(); 33 | 34 | bool render(Content* content, Image* renderImage); 35 | 36 | private: 37 | void createFBO(const QSize& size); 38 | 39 | QGLWidget* glWidget; 40 | QGLFramebufferObject* fbo; 41 | }; 42 | 43 | class FBORenderStrategy : public RenderStrategy 44 | { 45 | public: 46 | // Renderer does not take ownership of QGLWidget 47 | FBORenderStrategy(QGLWidget* glWidget); 48 | ~FBORenderStrategy(); 49 | 50 | bool render(Content* content, Image* renderImage); 51 | 52 | private: 53 | void createFBOs(const QSize& size); 54 | 55 | QGLWidget* glWidget; 56 | QGLFramebufferObject* multisampleFBO; 57 | QGLFramebufferObject* resolveFBO; 58 | }; 59 | 60 | class ImageRenderStrategy : public RenderStrategy 61 | { 62 | public: 63 | ImageRenderStrategy() {} 64 | 65 | bool render(Content* content, Image* renderImage); 66 | }; 67 | 68 | } 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /webvfx/resources/org/webvfx/WebVfx/AnimatedCamera.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import Qt 4.7 6 | import Qt3D 1.0 7 | 8 | // QtQuick3D Camera that supports animation data exported from Blender 9 | 10 | import "qrc:/webvfx/scripts/animation.js" as Anim 11 | 12 | Camera { 13 | 14 | function computeFOV(animationConstructed, width, height) { 15 | var animation = getAnimation(); 16 | if (!animation) 17 | return 0; 18 | return animation.radians2degrees(animation.verticalFOV(width / height)); 19 | } 20 | 21 | function updateCamera() { 22 | var animation = getAnimation(); 23 | animation.evaluateTime(animationTime); 24 | 25 | // Blender Euler order is XYZ order. But all Blender eulers 26 | // seem to be reversed (e.g. XYZ is ZYX, YXZ ix ZXY etc.), 27 | // so we use ZYX order here. 28 | var cx = Math.cos(animation.rotationX); 29 | var cy = Math.cos(animation.rotationY); 30 | var cz = Math.cos(animation.rotationZ); 31 | var sx = Math.sin(animation.rotationX); 32 | var sy = Math.sin(animation.rotationY); 33 | var sz = Math.sin(animation.rotationZ); 34 | 35 | var cc = cx*cz; 36 | var cs = cx*sz; 37 | var sc = sx*cz; 38 | var ss = sx*sz; 39 | 40 | var m12 = sy*sc-cs; 41 | var m22 = sy*ss+cc; 42 | var m32 = cy*sx; 43 | 44 | var m13 = sy*cc+ss; 45 | var m23 = sy*cs-sc; 46 | var m33 = cy*cx; 47 | 48 | // Eye is at translation position 49 | eye = Qt.vector3d(animation.locationX, 50 | animation.locationY, 51 | animation.locationZ); 52 | 53 | // Direction is eye looking down Z (m13, m23, m33). 54 | // LookAt is (eye - direction) 55 | center = Qt.vector3d(eye.x - m13, eye.y - m23, eye.z - m33) 56 | 57 | // Up vector can just be read out of the matrix (y axis) 58 | // (m12, m22, m32) 59 | upVector = Qt.vector3d(m12, m22, m32); 60 | } 61 | 62 | function getAnimation() { 63 | return Anim.WebVfx.Animation.prototype.global; 64 | } 65 | 66 | // We can't bind to a JS native property, so this is a pseudo-property 67 | // that changes whenever the native JS Animation instance is changed. 68 | property int animationConstructed: 0 69 | 70 | // Animation data JSON 71 | property variant animationData 72 | onAnimationDataChanged: { 73 | // QML doesn't allow globals, and storing a JS object as a variant 74 | // property doesn't work - so stash it on the prototype. 75 | Anim.WebVfx.Animation.prototype.global = new Anim.WebVfx.Animation(animationData); 76 | // Tweak property so data binding fires 77 | animationConstructed += 1; 78 | updateCamera(); 79 | } 80 | 81 | 82 | // Animation time (0..1) 83 | property real animationTime: 0 84 | onAnimationTimeChanged: updateCamera() 85 | 86 | // If true, then reverse the sense of time 87 | property bool reverseTime: false 88 | 89 | // Need to make this a property because Camera doesn't 90 | // support arbitrary children. 91 | // Also this allows the user to override us. 92 | property Connections connections : Connections { 93 | target: webvfx 94 | onRenderRequested: animationTime = reverseTime ? 1 - time : time 95 | } 96 | 97 | // Recompute FOV if viewport size or Animation change. 98 | // animationConstructed is a pseudo-property used to detect when 99 | // an Animation has been constructed. 100 | fieldOfView: computeFOV(animationConstructed, width, height) 101 | } 102 | -------------------------------------------------------------------------------- /webvfx/resources/org/webvfx/WebVfx/BlenderItem3D.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import Qt 4.7 6 | import Qt3D 1.0 7 | 8 | // Use this to import models exported from Blender. 9 | // Converts from Blender to QtQuick3D coordinate system. 10 | 11 | Item3D { 12 | pretransform: [ 13 | Rotation3D { 14 | angle: 90 15 | axis: Qt.vector3d(1, 0, 0) 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /webvfx/resources/org/webvfx/WebVfx/TextTexture.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import Qt 4.7 6 | 7 | // Exposes rendered text as a texture image. 8 | // We render inside a transparent rectangle to ensure the width/height 9 | // is honored in the captured pixmap so when we texture we don't get distorted. 10 | 11 | Rectangle { 12 | property alias text: internalText.text 13 | property alias color: internalText.color 14 | property alias font: internalText.font 15 | property alias textureImage: capturedText.pixmap 16 | 17 | // Transparent background on Rectangle 18 | color: "#00000000" 19 | // Apply effect that captures all rendering to an image 20 | effect: Capture { id: capturedText } 21 | 22 | Text { 23 | id: internalText 24 | smooth: true 25 | width: parent.width 26 | height: parent.height 27 | // If the text won't fit, scale ourself down so it does 28 | scale: paintedWidth > width ? (width / paintedWidth) : 1 29 | transformOrigin: Item.TopLeft 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /webvfx/resources/org/webvfx/WebVfx/qmldir: -------------------------------------------------------------------------------- 1 | AnimatedCamera 1.0 AnimatedCamera.qml 2 | BlenderItem3D 1.0 BlenderItem3D.qml 3 | TextTexture 1.0 TextTexture.qml -------------------------------------------------------------------------------- /webvfx/resources/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | org/webvfx/WebVfx/qmldir 6 | org/webvfx/WebVfx/AnimatedCamera.qml 7 | org/webvfx/WebVfx/BlenderItem3D.qml 8 | org/webvfx/WebVfx/TextTexture.qml 9 | webvfx/scripts/animation.js 10 | webvfx/scripts/easing.js 11 | webvfx/scripts/shaderkit.js 12 | webvfx/scripts/filterkit.js 13 | webvfx/scripts/tracker.js 14 | webvfx/scripts/threeworld.js 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /webvfx/resources/webvfx/scripts/animation.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | var WebVfx = WebVfx || {}; 6 | 7 | // range is an array of [beginX,endX] x coordinates that the endpoints 8 | // of this segment cover. 9 | // points is an array of four Bezier [x,y] control points, 10 | // [[ax,ay],[bx,by],[cx,cy],[dx,dy]] 11 | // See http://www.flong.com/texts/code/shapers_bez/ 12 | WebVfx.BezierSegment = function (range, points) { 13 | this.range = range; 14 | this.xCoefficients = this.polynomialCoefficients(points, 0); 15 | this.yCoefficients = this.polynomialCoefficients(points, 1); 16 | } 17 | 18 | WebVfx.BezierSegment.prototype.TOLERANCE = 0.000001; 19 | WebVfx.BezierSegment.prototype.ITERATIONS = 5; 20 | 21 | // Return y value for given x. 22 | WebVfx.BezierSegment.prototype.evaluate = function(x) { 23 | // Solve for t given x (using Newton-Raphson), 24 | // then solve for y given t. 25 | // For first guess, linearly interpolate to get t. 26 | var t = x / (this.range[1] - this.range[0] + 1); 27 | var oldt = t; 28 | for (var i = 0; i < this.ITERATIONS; i++) { 29 | var currentX = this.evaluatePolynomial(t, this.xCoefficients); 30 | var currentSlope = this.slope(t, this.xCoefficients); 31 | t -= (currentX - x) * currentSlope; 32 | t = this.clamp(t); 33 | if (Math.abs(oldt - t) <= this.TOLERANCE) 34 | break; 35 | oldt = t; 36 | } 37 | return this.evaluatePolynomial(t, this.yCoefficients); 38 | } 39 | 40 | // Return polynomial coefficients [a,b,c,d] for control 41 | // points in p (array of 4 [x,y] control points), 42 | // for coordinate i (0 for x, 1 for y). 43 | // See http://www.cs.binghamton.edu/~reckert/460/bezier.htm 44 | WebVfx.BezierSegment.prototype.polynomialCoefficients = function(p, i) { 45 | return [p[3][i] - 3 * p[2][i] + 3 * p[1][i] - p[0][i], 46 | 3 * p[2][i] - 6 * p[1][i] + 3 * p[0][i], 47 | 3 * p[1][i] - 3 * p[0][i], 48 | p[0][i]]; 49 | } 50 | 51 | // Evaluate cubic polynomial at time t. 52 | // c are the polynomial coefficients [a,b,c,d] 53 | WebVfx.BezierSegment.prototype.evaluatePolynomial = function(t, c) { 54 | // Use Horners rule for polynomial evaluation 55 | return ((c[0] * t + c[1]) * t + c[2]) * t + c[3]; 56 | } 57 | 58 | // Return slope given t for coefficients c 59 | WebVfx.BezierSegment.prototype.slope = function(t, c) { 60 | return 1.0 / (3.0 * c[0] * t * t + 2.0 * c[1] * t + c[2]); 61 | } 62 | 63 | WebVfx.BezierSegment.prototype.clamp = function(v) { 64 | if (v < 0.0) 65 | return 0.0; 66 | else if (v > 1.0) 67 | return 1.0; 68 | return v; 69 | } 70 | 71 | /////////// 72 | 73 | // segments is an ordered array of non overlapping BezierSegments 74 | WebVfx.BezierCurve = function (segments) { 75 | this.segments = segments; 76 | this.currentSegment = null; 77 | } 78 | 79 | // Binary search to find segment that contains x 80 | WebVfx.BezierCurve.prototype.findSegment = function(x) { 81 | var startIndex = 0; 82 | var stopIndex = this.segments.length - 1; 83 | var middleIndex = Math.floor((stopIndex + startIndex) / 2); 84 | while (startIndex < stopIndex) { 85 | var segment = this.segments[middleIndex]; 86 | if (x < segment.range[0]) 87 | stopIndex = middleIndex - 1; 88 | else if (x > segment.range[1]) 89 | startIndex = middleIndex + 1; 90 | else 91 | return segment; 92 | middleIndex = Math.floor((stopIndex + startIndex) / 2); 93 | } 94 | // We failed to find the segment, return first or last. 95 | // Segment will clamp x to it's range. 96 | return this.segments[middleIndex]; 97 | } 98 | 99 | WebVfx.BezierCurve.prototype.evaluate = function(x) { 100 | // Find current segment if we are out of range 101 | if (this.currentSegment == null || x < this.currentSegment.range[0] || 102 | x > this.currentSegment.range[1]) 103 | this.currentSegment = this.findSegment(x); 104 | 105 | return this.currentSegment.evaluate(x); 106 | } 107 | 108 | /////////// 109 | 110 | WebVfx.ConstantValue = function (value) { 111 | this.value = value; 112 | } 113 | 114 | WebVfx.ConstantValue.prototype.evaluate = function(x) { 115 | return this.value; 116 | } 117 | 118 | /////////// 119 | 120 | // animation is the animation data exported from Blender webvfx-camera.py add-on 121 | WebVfx.Animation = function (animationData) { 122 | if (!animationData) 123 | return; 124 | this.range = animationData['range']; 125 | 126 | this.locationXAnimation = this.processAnimation(animationData['locationX']); 127 | this.locationYAnimation = this.processAnimation(animationData['locationY']); 128 | this.locationZAnimation = this.processAnimation(animationData['locationZ']); 129 | this.rotationXAnimation = this.processAnimation(animationData['rotationX']); 130 | this.rotationYAnimation = this.processAnimation(animationData['rotationY']); 131 | this.rotationZAnimation = this.processAnimation(animationData['rotationZ']); 132 | 133 | // Horizontal field of view in radians 134 | this.horizontalFOV = animationData['horizontalFOV']; 135 | 136 | this.evaluateTime(0); 137 | } 138 | 139 | WebVfx.Animation.prototype.processAnimation = function(animation) { 140 | if (typeof(animation) == 'number') 141 | return new WebVfx.ConstantValue(animation); 142 | else { 143 | var segments = []; 144 | for (var i = 0; i < animation.length; i++) { 145 | segments[i] = new WebVfx.BezierSegment(animation[i]['range'], 146 | animation[i]['bezierPoints']); 147 | } 148 | return new WebVfx.BezierCurve(segments); 149 | } 150 | return null; 151 | } 152 | 153 | // Evaluate for x which must be in the animations range 154 | // After evaluating, location[XYZ] and rotation[XYZ] will be updated. 155 | // Rotation are Blender Euler angles of order XYZ - but Blender XYZ 156 | // is really ZYX order. 157 | WebVfx.Animation.prototype.evaluate = function(x) { 158 | if (this.currentX == x) 159 | return; 160 | this.currentX = x; 161 | 162 | this.rotationX = this.rotationXAnimation.evaluate(x); 163 | this.rotationY = this.rotationYAnimation.evaluate(x); 164 | this.rotationZ = this.rotationZAnimation.evaluate(x); 165 | 166 | this.locationX = this.locationXAnimation.evaluate(x); 167 | this.locationY = this.locationYAnimation.evaluate(x); 168 | this.locationZ = this.locationZAnimation.evaluate(x); 169 | } 170 | 171 | // time is normalized 0..1 172 | WebVfx.Animation.prototype.evaluateTime = function(time) { 173 | // Find x corresponding to time 174 | this.evaluate(this.range[0] + time * (this.range[1] - this.range[0] + 1)); 175 | } 176 | 177 | // Compute vertical field of view in radians, 178 | // given viewport aspect (width/height) 179 | WebVfx.Animation.prototype.verticalFOV = function(aspect) { 180 | return 2 * Math.atan(Math.tan(this.horizontalFOV / 2) / aspect); 181 | } 182 | 183 | WebVfx.Animation.prototype.radians2degrees = function (radians) { 184 | return radians * 180 / Math.PI; 185 | } 186 | -------------------------------------------------------------------------------- /webvfx/resources/webvfx/scripts/tracker.js: -------------------------------------------------------------------------------- 1 | var WebVfx = WebVfx || {}; 2 | 3 | // Generic resource tracker - calls _callback_ when count reaches 0 4 | WebVfx.Tracker = function (callback) { 5 | this.callback = callback; 6 | this.count = 0; 7 | this.started = false; 8 | }; 9 | 10 | WebVfx.Tracker.prototype = { 11 | increment: function () { 12 | this.count++; 13 | }, 14 | decrement: function () { 15 | if (--this.count <= 0 && this.started) 16 | this.callback(); 17 | }, 18 | start: function () { 19 | if (this.count <= 0) 20 | this.callback(); 21 | else 22 | this.started = true; 23 | } 24 | }; 25 | 26 | /////////// 27 | 28 | // Waits for a font specified by @font-face to be loaded and useable. 29 | // Otherwise drawing to a canvas draws nothing: 30 | // http://code.google.com/p/chromium/issues/detail?id=32879 31 | // And measuring text will return the wrong size: 32 | // http://stackoverflow.com/questions/3311005/jquery-working-out-wrong-height-due-to-font-face 33 | // Based on Google WebFont Loader: 34 | // https://github.com/typekit/webfontloader/blob/master/src/core/fontwatchrunner.js 35 | // This currently has issues with QtWebKit: 36 | // https://bugs.webkit.org/show_bug.cgi?id=55036 37 | 38 | // _trackedFont_ is the font name used with @font-face 39 | // _fallbackFont_ is a builtin font with different metrics than _trackedFont_ 40 | // (e.g. "monospace") 41 | // _callback_ will be called before _tracker_ is decremented - so it 42 | // can add additional tracking if needed 43 | WebVfx.FontTracker = function (trackedFont, fallbackFont, tracker, callback) { 44 | var trackedSpan = this.createSpan(trackedFont + "," + fallbackFont); 45 | var fallbackSpan = this.createSpan(fallbackFont); 46 | if (!this.isFontLoaded(trackedSpan, fallbackSpan)) { 47 | var self = this; 48 | tracker.increment(); 49 | var intervalID = setInterval(function () { 50 | if (self.isFontLoaded(trackedSpan, fallbackSpan)) { 51 | clearInterval(intervalID); 52 | if (callback) callback(); 53 | tracker.decrement(); 54 | } 55 | }, 25); 56 | } 57 | else if (callback) 58 | callback(); 59 | } 60 | 61 | WebVfx.FontTracker.prototype = { 62 | createSpan: function (fontFamily) { 63 | var styleString = "position:absolute; visibility:hidden; font-size:300px; width:auto; height:auto; line-height:normal; margin:0; padding:0; font-variant:normal; font-family:"; 64 | var span = document.createElement("span"); 65 | span.style.cssText = styleString + fontFamily; 66 | span.textContent = "BESIsi"; 67 | document.body.appendChild(span); 68 | return span; 69 | }, 70 | isFontLoaded: function (trackedSpan, fallbackSpan) { 71 | if (trackedSpan.offsetWidth != fallbackSpan.offsetWidth || 72 | trackedSpan.offsetHeight != fallbackSpan.offsetHeight) { 73 | document.body.removeChild(trackedSpan); 74 | document.body.removeChild(fallbackSpan); 75 | return true; 76 | } 77 | return false; 78 | } 79 | } -------------------------------------------------------------------------------- /webvfx/web_content.cpp: -------------------------------------------------------------------------------- 1 | #ifdef WEBVFX_GRAPHICSVIEW 2 | #include 3 | #include 4 | #include 5 | #endif 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "webvfx/image.h" 15 | #include "webvfx/render_strategy.h" 16 | #include "webvfx/web_content.h" 17 | #include "webvfx/webvfx.h" 18 | 19 | namespace WebVfx 20 | { 21 | 22 | WebPage::WebPage(QObject* parent) : QWebPage(parent) { 23 | mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); 24 | mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); 25 | 26 | settings()->setAttribute(QWebSettings::SiteSpecificQuirksEnabled, false); 27 | settings()->setAttribute(QWebSettings::AcceleratedCompositingEnabled, true); 28 | settings()->setAttribute(QWebSettings::WebGLEnabled, true); 29 | } 30 | 31 | bool WebPage::shouldInterruptJavaScript() { 32 | return false; 33 | } 34 | 35 | void WebPage::javaScriptAlert(QWebFrame *, const QString &msg) { 36 | log(QLatin1Literal("JavaScript alert: ") % msg); 37 | } 38 | 39 | void WebPage::javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID) { 40 | QString msg(message); 41 | if (!sourceID.isEmpty()) 42 | msg % QLatin1Literal(" (") % sourceID.section('/', -1) % QLatin1Literal(":") % QString::number(lineNumber) % QLatin1Literal(")"); 43 | log(msg); 44 | } 45 | 46 | bool WebPage::acceptNavigationRequest(QWebFrame*, const QNetworkRequest&, NavigationType) { 47 | //XXX we want to prevent JS from navigating the page - does this prevent our initial load? 48 | //return false; 49 | return true; 50 | } 51 | 52 | //////////////////// 53 | 54 | WebContent::WebContent(const QSize& size, Parameters* parameters) 55 | #ifdef WEBVFX_GRAPHICSVIEW 56 | : QGraphicsView((QWidget*)0) 57 | , webView(new QGraphicsWebView) 58 | #else 59 | : QObject(0) 60 | #endif 61 | , webPage(new WebPage(this)) 62 | , pageLoadFinished(LoadNotFinished) 63 | , contextLoadFinished(LoadNotFinished) 64 | , contentContext(new ContentContext(this, parameters)) 65 | , renderStrategy(0) 66 | { 67 | // Enabling WEBVFX_GRAPHICSVIEW is the most efficient rendering path, especially 68 | // for WebGL. But due to bug https://bugs.webkit.org/show_bug.cgi?id=63946 69 | // we must use GLWidgetRenderStrategy which will not work with all OpenGL drivers. 70 | #ifdef WEBVFX_GRAPHICSVIEW 71 | // Turn off scrollbars 72 | setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 73 | setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 74 | setInteractive(false); 75 | setFrameStyle(0); 76 | 77 | webView->setPage(webPage); 78 | setScene(new QGraphicsScene(this)); 79 | // Scene owns webView 80 | scene()->addItem(webView); 81 | 82 | // Must use a QGraphicsWebView with a parent QAbstractScrollArea 83 | // with a QGLWidget viewport, then WebGL will find this QGLWidget 84 | // and share it for rendering. 85 | // See WebKit: 86 | // GraphicsContext3DQt.cpp GraphicsContext3DInternal::getViewportGLWidget 87 | // The QGLWidget also makes WebKit use the GL TextureMapper. 88 | // See WebKit: 89 | // PageClientQt.cpp PageClientQGraphicsWidget::setRootGraphicsLayer 90 | QGLWidget* glWidget = new QGLWidget; 91 | setViewport(glWidget); 92 | // OpenGL needs this 93 | setViewportUpdateMode(QGraphicsView::FullViewportUpdate); 94 | 95 | renderStrategy = new GLWidgetRenderStrategy(glWidget); 96 | #else 97 | renderStrategy = new ImageRenderStrategy(); 98 | #endif 99 | 100 | connect(webPage, SIGNAL(loadFinished(bool)), 101 | SLOT(webPageLoadFinished(bool))); 102 | connect(contentContext, SIGNAL(readyRender(bool)), 103 | SLOT(contentContextLoadFinished(bool))); 104 | connect(webPage->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), 105 | SLOT(injectContentContext())); 106 | 107 | setContentSize(size); 108 | } 109 | 110 | WebContent::~WebContent() 111 | { 112 | delete renderStrategy; 113 | } 114 | 115 | void WebContent::injectContentContext() 116 | { 117 | webPage->mainFrame()->addToJavaScriptWindowObject("webvfx", contentContext); 118 | } 119 | 120 | void WebContent::webPageLoadFinished(bool result) 121 | { 122 | if (pageLoadFinished == LoadNotFinished) 123 | pageLoadFinished = result ? LoadSucceeded : LoadFailed; 124 | 125 | // This is useful when webvfx.renderReady(true) is not used. 126 | emit contentPreLoadFinished(pageLoadFinished == LoadSucceeded); 127 | 128 | if (pageLoadFinished == LoadFailed || contextLoadFinished != LoadNotFinished) 129 | emit contentLoadFinished(contextLoadFinished == LoadSucceeded && pageLoadFinished == LoadSucceeded); 130 | } 131 | 132 | void WebContent::contentContextLoadFinished(bool result) 133 | { 134 | if (contextLoadFinished == LoadNotFinished) 135 | contextLoadFinished = result ? LoadSucceeded : LoadFailed; 136 | if (contextLoadFinished == LoadFailed || pageLoadFinished != LoadNotFinished) 137 | emit contentLoadFinished(contextLoadFinished == LoadSucceeded && pageLoadFinished == LoadSucceeded); 138 | } 139 | 140 | void WebContent::loadContent(const QUrl& url) 141 | { 142 | pageLoadFinished = LoadNotFinished; 143 | contextLoadFinished = LoadNotFinished; 144 | 145 | webPage->mainFrame()->load(url); 146 | } 147 | 148 | void WebContent::setContentSize(const QSize& size) { 149 | #ifdef WEBVFX_GRAPHICSVIEW 150 | if (size != this->size()) { 151 | webView->resize(size); 152 | resize(size); 153 | viewport()->resize(size); 154 | } 155 | #else 156 | if (webPage->viewportSize() != size) 157 | webPage->setViewportSize(size); 158 | #endif 159 | } 160 | 161 | bool WebContent::renderContent(double time, Image* renderImage) 162 | { 163 | // Allow the page to render for this time 164 | contentContext->render(time); 165 | return renderStrategy->render(this, renderImage); 166 | } 167 | 168 | void WebContent::paintContent(QPainter* painter) 169 | { 170 | webPage->mainFrame()->render(painter); 171 | } 172 | 173 | void WebContent::setZoom(const qreal zoom) 174 | { 175 | webPage->mainFrame()->setZoomFactor(zoom); 176 | } 177 | 178 | QWidget* WebContent::createView(QWidget* parent) 179 | { 180 | #ifdef WEBVFX_GRAPHICSVIEW 181 | setParent(parent); 182 | return this; 183 | #else 184 | QWebView* webView = new QWebView(parent); 185 | setParent(webView); 186 | webView->setPage(webPage); 187 | return webView; 188 | #endif 189 | } 190 | 191 | void WebContent::reload() 192 | { 193 | webPage->triggerAction(QWebPage::ReloadAndBypassCache); 194 | } 195 | 196 | QWebSettings* WebContent::settings() 197 | { 198 | return webPage->settings(); 199 | } 200 | 201 | void WebContent::setTransparent() 202 | { 203 | if (webPage) { 204 | QPalette pal = webPage->palette(); 205 | pal.setBrush(QPalette::Base, Qt::transparent); 206 | webPage->setPalette(pal); 207 | } 208 | } 209 | 210 | } 211 | -------------------------------------------------------------------------------- /webvfx/web_content.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef WEBVFX_WEB_CONTENT_H_ 6 | #define WEBVFX_WEB_CONTENT_H_ 7 | 8 | 9 | #include 10 | 11 | #ifdef WEBVFX_GRAPHICSVIEW 12 | #include 13 | #endif 14 | #include 15 | #include "webvfx/content.h" 16 | #include "webvfx/content_context.h" 17 | #include "webvfx/effects.h" 18 | 19 | class QGraphicsWebView; 20 | class QImage; 21 | class QSize; 22 | class QWebFrame; 23 | 24 | namespace WebVfx 25 | { 26 | 27 | class Image; 28 | class Parameters; 29 | class RenderStrategy; 30 | class WebPage; 31 | 32 | class WebContent 33 | #ifdef WEBVFX_GRAPHICSVIEW 34 | : public QGraphicsView 35 | #else 36 | : public QObject 37 | #endif 38 | , public virtual Content 39 | { 40 | Q_OBJECT 41 | public: 42 | WebContent(const QSize& size, Parameters* parameters); 43 | ~WebContent(); 44 | 45 | void loadContent(const QUrl& url); 46 | void setContentSize(const QSize& size); 47 | const Effects::ImageTypeMap& getImageTypeMap() { return contentContext->getImageTypeMap(); }; 48 | bool renderContent(double time, Image* renderImage); 49 | void paintContent(QPainter* painter); 50 | void setImage(const QString& name, Image* image) { contentContext->setImage(name, image); } 51 | void setZoom(const qreal zoom); 52 | 53 | QWidget* createView(QWidget* parent); 54 | void reload(); 55 | 56 | // For debugging with Viewer 57 | QWebSettings* settings(); 58 | 59 | void setTransparent(); 60 | 61 | signals: 62 | void contentLoadFinished(bool result); 63 | void contentPreLoadFinished(bool result); 64 | 65 | private slots: 66 | void injectContentContext(); 67 | void webPageLoadFinished(bool result); 68 | void contentContextLoadFinished(bool result); 69 | 70 | private: 71 | #ifdef WEBVFX_GRAPHICSVIEW 72 | QGraphicsWebView* webView; 73 | #endif 74 | WebPage* webPage; 75 | enum LoadStatus { LoadNotFinished, LoadFailed, LoadSucceeded }; 76 | LoadStatus pageLoadFinished; 77 | LoadStatus contextLoadFinished; 78 | ContentContext* contentContext; 79 | RenderStrategy* renderStrategy; 80 | }; 81 | 82 | //////////////////// 83 | 84 | class QWebFrame; 85 | class QNetworkRequest; 86 | 87 | class WebPage : public QWebPage 88 | { 89 | Q_OBJECT 90 | public: 91 | WebPage(QObject* parent); 92 | 93 | private slots: 94 | bool shouldInterruptJavaScript(); 95 | 96 | protected: 97 | void javaScriptAlert(QWebFrame *, const QString &msg); 98 | void javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID); 99 | bool acceptNavigationRequest(QWebFrame*, const QNetworkRequest&, NavigationType); 100 | }; 101 | 102 | } 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /webvfx/webvfx.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "webvfx/logger.h" 14 | #include "webvfx/webvfx.h" 15 | #include "webvfx/effects_impl.h" 16 | 17 | #ifndef Q_OS_WIN 18 | #define WEBVFX_IGNORE_SEGV 19 | #endif 20 | #ifndef Q_OS_MAC 21 | #include 22 | #ifdef WEBVFX_IGNORE_SEGV 23 | #include 24 | #include 25 | #include 26 | #endif 27 | #endif 28 | 29 | namespace WebVfx 30 | { 31 | 32 | static bool s_initialized = false; 33 | 34 | static Logger* s_logger = 0; 35 | static bool s_ownApp = false; 36 | 37 | static QMutex s_initializedMutex; 38 | 39 | #ifdef Q_OS_MAC 40 | bool isMainThread(); 41 | #else 42 | static pthread_t s_uiThread; 43 | typedef QPair ThreadSync; 44 | 45 | #ifdef WEBVFX_IGNORE_SEGV 46 | static void handleSEGV(int) 47 | { 48 | const char msg[] = "WebVfx: Caught SIGSEGV during shutdown. Exiting successfully.\n"; 49 | (void)write(STDERR_FILENO, msg, sizeof(msg)); 50 | tcflush(STDERR_FILENO, TCOFLUSH); 51 | _exit(EXIT_SUCCESS); 52 | } 53 | #endif 54 | 55 | void* uiEventLoop(void* data) 56 | { 57 | ThreadSync* threadSync = static_cast(data); 58 | 59 | static const char *const empty = ""; 60 | int argc = 1; 61 | QApplication app(argc, (char**)&empty); 62 | s_ownApp = true; 63 | 64 | // Signal s_initialized() that app has been created 65 | { 66 | QMutexLocker eventLoopLock(threadSync->first); 67 | threadSync->second->wakeOne(); 68 | threadSync = 0; 69 | } 70 | 71 | // Enter event loop 72 | app.exec(); 73 | QCoreApplication::processEvents(); 74 | 75 | #ifdef WEBVFX_IGNORE_SEGV 76 | // WebKit threads can outlive QApplication and global Qt internal state. 77 | // This can lead to crashes on shutdown. 78 | // As a temporary workaround, trap SIGSEGV and exit normally during shutdown. 79 | // https://bugs.webkit.org/show_bug.cgi?id=72538 80 | struct sigaction action; 81 | action.sa_handler = handleSEGV; 82 | sigemptyset(&action.sa_mask); 83 | action.sa_flags = 0; 84 | sigaction(SIGSEGV, &action, NULL); 85 | #endif 86 | 87 | return 0; 88 | } 89 | #endif 90 | 91 | void setLogger(Logger* logger) 92 | { 93 | s_logger = logger; 94 | } 95 | 96 | bool initialize() 97 | { 98 | QMutexLocker initLock(&s_initializedMutex); 99 | 100 | if (s_initialized) 101 | return true; 102 | 103 | // For non-mac, spawn a GUI application thread if qApp doesn't already exist. 104 | // For mac, the qApp must be created on the main thread, so check we are 105 | // on the main thread and create it - user must then call processEvents() 106 | // from the main thread. 107 | // https://bugreports.qt-project.org/browse/QTBUG-7393 108 | if (!qApp) { 109 | #ifdef Q_OS_MAC 110 | if (!isMainThread()) { 111 | log("WebVfx must be initialized on the main thread on MacOS"); 112 | return false; 113 | } 114 | 115 | // Create a QApplication, we will delete it in processEvents() 116 | static const char *const empty = ""; 117 | int argc = 1; 118 | new QApplication(argc, (char**)&empty); 119 | s_ownApp = true; 120 | #else 121 | { 122 | #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) 123 | if (std::getenv("DISPLAY") == 0) { 124 | log("DISPLAY environment variable not set"); 125 | return false; 126 | } 127 | #endif 128 | QMutex uiThreadMutex; 129 | QWaitCondition uiThreadCondition; 130 | ThreadSync uiThreadSync(&uiThreadMutex, &uiThreadCondition); 131 | 132 | QMutexLocker uiThreadLock(&uiThreadMutex); 133 | 134 | //XXX check return 135 | pthread_create(&s_uiThread, 0, uiEventLoop, &uiThreadSync); 136 | 137 | // Wait for signal that ui thread has created qApp 138 | uiThreadCondition.wait(&uiThreadMutex); 139 | } 140 | #endif 141 | } 142 | 143 | // Register metatypes for queued connections 144 | qRegisterMetaType("Parameters*"); 145 | qRegisterMetaType("Image*"); 146 | 147 | s_initialized = true; 148 | return true; 149 | } 150 | 151 | Effects* createEffects(const QString& fileName, int width, int height, Parameters* parameters, bool isTransparent) 152 | { 153 | EffectsImpl* effects = new EffectsImpl(); 154 | if (!effects->initialize(fileName, width, height, parameters, isTransparent)) { 155 | effects->destroy(); 156 | return 0; 157 | } 158 | return effects; 159 | } 160 | 161 | int processEvents() 162 | { 163 | #ifdef Q_OS_MAC 164 | // We didn't create the app, the user should be running its event loop. 165 | if (!s_ownApp) 166 | return 0; 167 | if (!isMainThread()) { 168 | log("WebVfx::processEvents() must be called from the main thread."); 169 | return 1; 170 | } 171 | int result = qApp->exec(); 172 | delete qApp; 173 | return result; 174 | #else 175 | return 0; 176 | #endif 177 | } 178 | 179 | void shutdown() 180 | { 181 | QMutexLocker initLock(&s_initializedMutex); 182 | 183 | // Delete the s_logger even if not initialized 184 | delete s_logger; s_logger = 0; 185 | 186 | if (!s_initialized) 187 | return; 188 | 189 | // If we created s_uiThread, then we created QApplication. 190 | if (s_ownApp) { 191 | // Quit the application 192 | if (qApp) 193 | qApp->quit(); 194 | #ifndef Q_OS_MAC 195 | // Wait for the app thread to finish 196 | pthread_join(s_uiThread, 0); 197 | #endif 198 | s_ownApp = false; 199 | } 200 | else 201 | QCoreApplication::processEvents(); 202 | } 203 | 204 | void log(const QString& msg) 205 | { 206 | if (s_logger) 207 | s_logger->log(msg); 208 | } 209 | 210 | } 211 | -------------------------------------------------------------------------------- /webvfx/webvfx.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef WEBVFX_WEBVFX_H_ 6 | #define WEBVFX_WEBVFX_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | /*! 13 | * @brief Public entry points into WebVfx 14 | * 15 | * These are the methods and classes that will be needed to host 16 | * WebVfx effect rendering. 17 | */ 18 | namespace WebVfx { 19 | 20 | /*! 21 | * @brief Set a Logger implementation 22 | * 23 | * WebVfx will take ownership of the logger and delete it at shutdown. 24 | * setLogger() must be called only once and must be called before initialize(). 25 | */ 26 | void setLogger(Logger* logger); 27 | 28 | /*! 29 | * @brief Log a message 30 | * 31 | * Logs a message using the currently set Logger implementation. 32 | */ 33 | void log(const QString& msg); 34 | 35 | /*! 36 | * @brief Initialize the WebVfx framework 37 | * 38 | * initialize() is threadsafe and may be called multiple times. 39 | * It must be called before calling createEffects() for the first time. 40 | * 41 | * On MacOS, in a non Qt host application, initialize() and processEvents() 42 | * must be called from the main thread. 43 | * In a Qt based MacOS application, or a non-MacOS application, 44 | * initialize() can be called from any thread and processEvents() 45 | * need not be called. 46 | * See QTBUG-7393. 47 | * 48 | * @return Indicates whether initialization was successful 49 | */ 50 | bool initialize(); 51 | 52 | /*! 53 | * @brief Create an Effects instance 54 | * 55 | * @param fileName Path to a QML or HTML effects implementation. 56 | * @c fileName must end in @c .html, @c .htm, or @c .qml 57 | * @param width Initial width of effect in pixels 58 | * @param height Initial height of effect in pixels 59 | * @param parameters Parameters implementation to provide named parameter 60 | * values for this effect 61 | */ 62 | Effects* createEffects(const QString& fileName, int width, int height, Parameters* parameters = 0, bool isTransparent = false); 63 | 64 | /*! 65 | * @brief Workaround for MacOS 66 | * 67 | * Must be called on the main thread after initialize() in 68 | * non-Qt based MacOS applications if Effects is going to be used 69 | * from any other threads. 70 | * It is a noop on other platforms or on Qt-based MacOS applications. 71 | * @note This method will block until shutdown() is called. 72 | */ 73 | int processEvents(); 74 | 75 | /*! 76 | * @brief Shut down WebVfx. 77 | * 78 | * All Effects should be destroyed before calling shutdown(). 79 | * shutdown() can be called from any thread. 80 | * It is safe to call shutdown() more than once, 81 | * or without having called initialize(). 82 | */ 83 | void shutdown(); 84 | 85 | } 86 | #endif 87 | -------------------------------------------------------------------------------- /webvfx/webvfx.pro: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011 Hewlett-Packard Development Company, L.P. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | include(../common.pri) 6 | TEMPLATE = lib 7 | VERSION = 1.2.0 8 | 9 | HEADERS += content.h 10 | HEADERS += content_context.h 11 | HEADERS += effects.h 12 | HEADERS += effects_impl.h 13 | HEADERS += image.h 14 | HEADERS += logger.h 15 | HEADERS += parameters.h 16 | HEADERS += qml_content.h 17 | HEADERS += render_strategy.h 18 | HEADERS += web_content.h 19 | HEADERS += webvfx.h 20 | 21 | SOURCES += content.cpp 22 | SOURCES += content_context.cpp 23 | SOURCES += effects.cpp 24 | SOURCES += effects_impl.cpp 25 | SOURCES += image.cpp 26 | SOURCES += logger.cpp 27 | SOURCES += parameters.cpp 28 | SOURCES += qml_content.cpp 29 | SOURCES += render_strategy.cpp 30 | SOURCES += web_content.cpp 31 | SOURCES += webvfx.cpp 32 | macx:OBJECTIVE_SOURCES += webvfx_mac.mm 33 | 34 | RESOURCES += resources/resources.qrc 35 | 36 | macx:LIBS += -framework Foundation 37 | 38 | win32:CONFIG += staticlib 39 | unix:CONFIG += shared thread 40 | 41 | isEqual(QT_MAJOR_VERSION, 5) { 42 | QT += webkitwidgets 43 | } else { 44 | QT += webkit 45 | } 46 | QT += opengl quick 47 | 48 | TARGET = webvfx 49 | 50 | target.path = $$PREFIX/lib 51 | INSTALLS += target 52 | -------------------------------------------------------------------------------- /webvfx/webvfx_mac.mm: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef Q_OS_MAC 4 | 5 | #include 6 | #include "webvfx/webvfx.h" 7 | 8 | namespace WebVfx { 9 | 10 | bool isMainThread() 11 | { 12 | return [NSThread isMainThread]; 13 | } 14 | 15 | } 16 | 17 | 18 | #endif 19 | --------------------------------------------------------------------------------