From 6a990dd8eb4918e407cec59874eef340bd4d1a4b Mon Sep 17 00:00:00 2001 From: Lee Morgan Date: Mon, 8 Dec 2014 17:22:44 +0000 Subject: [PATCH] Emscripten minimal adaptor build A bare bones adaptor build that isn't compatible with toolkit. Change-Id: Ice7a5953d34da55d4121e3b4bceb212dbfac36e1 --- README | 45 +- adaptors/common/gl/egl-implementation.h | 4 +- .../egl-implementation-emscripten.cpp | 561 +++ adaptors/emscripten/main.cpp | 110 + adaptors/emscripten/sdl-application.cpp | 269 ++ adaptors/emscripten/sdl-application.h | 143 + adaptors/emscripten/sdl-gesture-manager.h | 146 + .../emscripten/sdl-gl-sync-abstraction.cpp | 32 + adaptors/emscripten/sdl-gl-sync-abstraction.h | 48 + adaptors/emscripten/sdl-render-controller.h | 83 + .../emscripten/wrappers/actor-wrapper.cpp | 306 ++ adaptors/emscripten/wrappers/actor-wrapper.h | 99 + .../emscripten/wrappers/animation-wrapper.cpp | 211 + .../emscripten/wrappers/animation-wrapper.h | 137 + adaptors/emscripten/wrappers/dali-wrapper.cpp | 1279 ++++++ adaptors/emscripten/wrappers/dali-wrapper.js | 3498 +++++++++++++++++ .../emscripten/wrappers/emscripten-utils.cpp | 70 + .../emscripten/wrappers/emscripten-utils.h | 71 + .../emscripten/wrappers/handle-wrapper.cpp | 201 + adaptors/emscripten/wrappers/handle-wrapper.h | 148 + .../emscripten/wrappers/image-wrapper.cpp | 54 + adaptors/emscripten/wrappers/image-wrapper.h | 61 + .../wrappers/property-buffer-wrapper.cpp | 40 + .../wrappers/property-buffer-wrapper.h | 50 + .../wrappers/property-value-wrapper.cpp | 315 ++ .../wrappers/property-value-wrapper.h | 69 + .../wrappers/render-task-wrapper.cpp | 50 + .../emscripten/wrappers/render-task-wrapper.h | 64 + .../wrappers/shader-effect-wrapper.cpp | 96 + .../wrappers/shader-effect-wrapper.h | 65 + adaptors/emscripten/wrappers/signal-holder.h | 49 + .../emscripten/wrappers/type-info-wrapper.cpp | 117 + .../emscripten/wrappers/type-info-wrapper.h | 103 + automated-tests/CMakeLists.txt | 2 + .../test-gl-abstraction.h | 1 - build/emscripten/.gitignore | 1 + build/emscripten/CMakeLists.txt | 115 + build/emscripten/build.sh | 36 + .../emscripten/emscripten-callbacks.cpp | 200 + .../emscripten/emscripten-callbacks.h | 118 + .../emscripten-platform-abstraction.cpp | 415 ++ .../emscripten-platform-abstraction.h | 216 + platform-abstractions/emscripten/file.list | 13 + 43 files changed, 9707 insertions(+), 4 deletions(-) create mode 100644 adaptors/emscripten/egl-implementation-emscripten.cpp create mode 100644 adaptors/emscripten/main.cpp create mode 100644 adaptors/emscripten/sdl-application.cpp create mode 100644 adaptors/emscripten/sdl-application.h create mode 100644 adaptors/emscripten/sdl-gesture-manager.h create mode 100644 adaptors/emscripten/sdl-gl-sync-abstraction.cpp create mode 100644 adaptors/emscripten/sdl-gl-sync-abstraction.h create mode 100644 adaptors/emscripten/sdl-render-controller.h create mode 100644 adaptors/emscripten/wrappers/actor-wrapper.cpp create mode 100644 adaptors/emscripten/wrappers/actor-wrapper.h create mode 100644 adaptors/emscripten/wrappers/animation-wrapper.cpp create mode 100644 adaptors/emscripten/wrappers/animation-wrapper.h create mode 100644 adaptors/emscripten/wrappers/dali-wrapper.cpp create mode 100644 adaptors/emscripten/wrappers/dali-wrapper.js create mode 100644 adaptors/emscripten/wrappers/emscripten-utils.cpp create mode 100644 adaptors/emscripten/wrappers/emscripten-utils.h create mode 100644 adaptors/emscripten/wrappers/handle-wrapper.cpp create mode 100644 adaptors/emscripten/wrappers/handle-wrapper.h create mode 100644 adaptors/emscripten/wrappers/image-wrapper.cpp create mode 100644 adaptors/emscripten/wrappers/image-wrapper.h create mode 100644 adaptors/emscripten/wrappers/property-buffer-wrapper.cpp create mode 100644 adaptors/emscripten/wrappers/property-buffer-wrapper.h create mode 100644 adaptors/emscripten/wrappers/property-value-wrapper.cpp create mode 100644 adaptors/emscripten/wrappers/property-value-wrapper.h create mode 100644 adaptors/emscripten/wrappers/render-task-wrapper.cpp create mode 100644 adaptors/emscripten/wrappers/render-task-wrapper.h create mode 100644 adaptors/emscripten/wrappers/shader-effect-wrapper.cpp create mode 100644 adaptors/emscripten/wrappers/shader-effect-wrapper.h create mode 100644 adaptors/emscripten/wrappers/signal-holder.h create mode 100644 adaptors/emscripten/wrappers/type-info-wrapper.cpp create mode 100644 adaptors/emscripten/wrappers/type-info-wrapper.h create mode 100644 build/emscripten/.gitignore create mode 100644 build/emscripten/CMakeLists.txt create mode 100755 build/emscripten/build.sh create mode 100644 platform-abstractions/emscripten/emscripten-callbacks.cpp create mode 100644 platform-abstractions/emscripten/emscripten-callbacks.h create mode 100644 platform-abstractions/emscripten/emscripten-platform-abstraction.cpp create mode 100644 platform-abstractions/emscripten/emscripten-platform-abstraction.h create mode 100755 platform-abstractions/emscripten/file.list diff --git a/README b/README index 248c63aae..49bd8fc61 100644 --- a/README +++ b/README @@ -7,7 +7,10 @@ T.O.C. 2.2. Building the Repository 2.3. Build target options 2.4. Building and executing test cases - + 3. Building for Emscripten + 3.1. Setup dali-env & build dali-core + 3.2. Build the minimal dali-adaptor + 3.3. Using the Javascript Build 1. GBS Builds @@ -16,7 +19,6 @@ T.O.C. gbs build -A [TARGET_ARCH] - 2. Building for Ubuntu desktop ============================== @@ -59,3 +61,42 @@ Add to the gbs build line: --define "%target_gles_version X" -------------------------------------- See the README.md in dali-adaptor/automated-tests. + + +3. Building for Emscripten +-------------------------- + +Currently the build for emscripten uses a minimal adaptor which does not support dali-toolkit. + + +3.1. Setup dali-env & build dali-core +------------------------------------- + + The build requires the Emscripten SDK, this is installed automatically by running dali_env with the correct parameters. + dali_env is part of the dali-core repository. + Please see the README within dali-core to setup dali_env and build dali-core. + + +3.2. Build the minimal dali-adaptor +----------------------------------- + + Use the build.sh script build adaptor. + This uses emscriptens emcc to compile byte code to javascript (full OpenGL ES support with stb-image loading library and cpp bindings). + Note: Please view the build.sh script for debug build options. + + # cd ./build/emscripten + # ./build.sh + + +3.3. Using the Javascript Build +------------------------------- + + The build will create 1 main Javascript artifact, and its html counterpart; dali-emscripten.js + + This is required by any dali JS app, and must be located in the same directory as the app JS in order for the browser to find it. + + After the build, the necessary artifacts (which include dali-wrapper.js) will be placed in the dali-env directory under opt/share/emscripten: + + dali-env/opt/share/emscripten + + If dali-demo is built, any JS examples will also be placed in this directory, so they are ready to run. diff --git a/adaptors/common/gl/egl-implementation.h b/adaptors/common/gl/egl-implementation.h index cf4b6e335..47976d10e 100644 --- a/adaptors/common/gl/egl-implementation.h +++ b/adaptors/common/gl/egl-implementation.h @@ -24,7 +24,7 @@ #include // INTERNAL INCLUDES -#include +#include namespace Dali { @@ -178,7 +178,9 @@ private: Vector mContextAttribs; EGLNativeDisplayType mEglNativeDisplay; +#ifndef EMSCRIPTEN EGLNativeWindowType mEglNativeWindow; +#endif EGLNativePixmapType mCurrentEglNativePixmap; EGLDisplay mEglDisplay; diff --git a/adaptors/emscripten/egl-implementation-emscripten.cpp b/adaptors/emscripten/egl-implementation-emscripten.cpp new file mode 100644 index 000000000..f13e348a9 --- /dev/null +++ b/adaptors/emscripten/egl-implementation-emscripten.cpp @@ -0,0 +1,561 @@ +/* +Copyright (c) 2000-2013 Samsung Electronics Co., Ltd All Rights Reserved + +This file is part of Dali Adaptor + +PROPRIETARY/CONFIDENTIAL + +This software is the confidential and proprietary information of +SAMSUNG ELECTRONICS ("Confidential Information"). You shall not +disclose such Confidential Information and shall use it only in +accordance with the terms of the license agreement you entered +into with SAMSUNG ELECTRONICS. + +SAMSUNG make no representations or warranties about the suitability +of the software, either express or implied, including but not limited +to the implied warranties of merchantability, fitness for a particular +purpose, or non-infringement. SAMSUNG shall not be liable for any +damages suffered by licensee as a result of using, modifying or +distributing this software or its derivatives. +*/ + + +// CLASS HEADER +#include + +// EXTERNAL INCLUDES +#include +#include +#include +#include + +// INTERNAL INCLUDES +namespace +{ + +#if defined(DEBUG_ENABLED) + +void PrintConfigs(EGLDisplay d) +{ + EGLint numConfigs; + + eglGetConfigs(d, NULL, 0, &numConfigs); + EGLConfig *configs = new EGLConfig[numConfigs]; + + eglGetConfigs(d, configs, numConfigs, &numConfigs); + + printf("Configurations: N=%d\n", numConfigs); + printf(" - config id\n"); + printf(" - buffer size\n"); + printf(" - level\n"); + printf(" - double buffer\n"); + printf(" - stereo\n"); + printf(" - r, g, b\n"); + printf(" - depth\n"); + printf(" - stencil\n"); + + printf(" bf lv d st colorbuffer dp st supported \n"); + printf(" id sz l b ro r g b a th cl surfaces \n"); + printf("----------------------------------------------\n"); + for (EGLint i = 0; i < numConfigs; i++) { + EGLint id, size, level; + EGLint red, green, blue, alpha; + EGLint depth, stencil; + EGLint surfaces; + EGLint doubleBuf = 1, stereo = 0; + char surfString[100] = ""; + + eglGetConfigAttrib(d, configs[i], EGL_CONFIG_ID, &id); + eglGetConfigAttrib(d, configs[i], EGL_BUFFER_SIZE, &size); + eglGetConfigAttrib(d, configs[i], EGL_LEVEL, &level); + + eglGetConfigAttrib(d, configs[i], EGL_RED_SIZE, &red); + eglGetConfigAttrib(d, configs[i], EGL_GREEN_SIZE, &green); + eglGetConfigAttrib(d, configs[i], EGL_BLUE_SIZE, &blue); + eglGetConfigAttrib(d, configs[i], EGL_ALPHA_SIZE, &alpha); + eglGetConfigAttrib(d, configs[i], EGL_DEPTH_SIZE, &depth); + eglGetConfigAttrib(d, configs[i], EGL_STENCIL_SIZE, &stencil); + eglGetConfigAttrib(d, configs[i], EGL_SURFACE_TYPE, &surfaces); + + if (surfaces & EGL_WINDOW_BIT) + strcat(surfString, "win,"); + if (surfaces & EGL_PBUFFER_BIT) + strcat(surfString, "pb,"); + if (surfaces & EGL_PIXMAP_BIT) + strcat(surfString, "pix,"); + if (strlen(surfString) > 0) + surfString[strlen(surfString) - 1] = 0; + + printf("0x%02x %2d %2d %c %c %2d %2d %2d %2d %2d %2d %-12s\n", + id, size, level, + doubleBuf ? 'y' : '.', + stereo ? 'y' : '.', + red, green, blue, alpha, + depth, stencil, surfString); + } + + delete [] configs; +} + +#endif + +} // namespace anon + +namespace Dali +{ +namespace Internal +{ +namespace Adaptor +{ + +#define TEST_EGL_ERROR(lastCommand) \ +{ \ + EGLint err = eglGetError(); \ + if (err != EGL_SUCCESS) \ + { \ + printf("EGL error after %s code=%x\n", lastCommand, err); \ + DALI_LOG_ERROR("EGL error after %s code=%x\n", lastCommand,err); \ + DALI_ASSERT_ALWAYS(0 && "EGL error"); \ + } \ +} + +EglImplementation::EglImplementation() + : mEglNativeDisplay(0), + mCurrentEglNativePixmap(0), + mEglDisplay(0), + mEglConfig(0), + mEglContext(0), + mCurrentEglSurface(0), + mGlesInitialized(false), + mIsOwnSurface(true), + mContextCurrent(false), + mIsWindow(true), + mColorDepth(COLOR_DEPTH_24) +{ +} + +EglImplementation::~EglImplementation() +{ + TerminateGles(); +} + +bool EglImplementation::InitializeGles( EGLNativeDisplayType display, bool isOwnSurface ) +{ + if ( !mGlesInitialized ) + { + mEglNativeDisplay = display; + + //@todo see if we can just EGL_DEFAULT_DISPLAY instead + mEglDisplay = eglGetDisplay(mEglNativeDisplay); + + EGLint majorVersion = 0; + EGLint minorVersion = 0; + if ( !eglInitialize( mEglDisplay, &majorVersion, &minorVersion ) ) + { + return false; + } + eglBindAPI(EGL_OPENGL_ES_API); + +#if defined(DEBUG_ENABLED) + PrintConfigs(mEglDisplay); +#endif + + mContextAttribs.Clear(); + +#if DALI_GLES_VERSION >= 30 + + mContextAttribs.Reserve(5); + mContextAttribs.PushBack( EGL_CONTEXT_MAJOR_VERSION_KHR ); + mContextAttribs.PushBack( 3 ); + mContextAttribs.PushBack( EGL_CONTEXT_MINOR_VERSION_KHR ); + mContextAttribs.PushBack( 0 ); + +#else // DALI_GLES_VERSION >= 30 + + mContextAttribs.Reserve(3); + mContextAttribs.PushBack( EGL_CONTEXT_CLIENT_VERSION ); + mContextAttribs.PushBack( 2 ); + +#endif // DALI_GLES_VERSION >= 30 + + mContextAttribs.PushBack( EGL_NONE ); + + mGlesInitialized = true; + mIsOwnSurface = isOwnSurface; + } + + return mGlesInitialized; +} + +bool EglImplementation::CreateContext() +{ + // make sure a context isn't created twice + DALI_ASSERT_ALWAYS( (mEglContext == 0) && "EGL context recreated" ); + DALI_ASSERT_ALWAYS( mGlesInitialized ); + + mEglContext = eglCreateContext(mEglDisplay, mEglConfig, NULL, &(mContextAttribs[0])); + + // if emscripten ignore this (egl spec says non gles2 implementation must return EGL_BAD_MATCH if it doesnt support gles2) + // so just ignore error for now.... + // TEST_EGL_ERROR("eglCreateContext render thread"); + // DALI_ASSERT_ALWAYS( EGL_NO_CONTEXT != mEglContext && "EGL context not created" ); + + return true; +} + +void EglImplementation::DestroyContext() +{ + DALI_ASSERT_ALWAYS( mEglContext && "no EGL context" ); + + eglDestroyContext( mEglDisplay, mEglContext ); + mEglContext = 0; +} + +void EglImplementation::DestroySurface() +{ + if(mIsOwnSurface && mCurrentEglSurface) + { + eglDestroySurface( mEglDisplay, mCurrentEglSurface ); + mCurrentEglSurface = 0; + } +} + +void EglImplementation::MakeContextCurrent() +{ + mContextCurrent = true; + + if(mIsOwnSurface) + { + eglMakeCurrent( mEglDisplay, mCurrentEglSurface, mCurrentEglSurface, mEglContext ); + } + + EGLint error = eglGetError(); + + if ( error != EGL_SUCCESS ) + { + switch (error) + { + case EGL_BAD_DISPLAY: + { + DALI_LOG_ERROR("EGL_BAD_DISPLAY : Display is not an EGL display connection"); + break; + } + case EGL_NOT_INITIALIZED: + { + DALI_LOG_ERROR("EGL_NOT_INITIALIZED : Display has not been initialized"); + break; + } + case EGL_BAD_SURFACE: + { + DALI_LOG_ERROR("EGL_BAD_SURFACE : Draw or read is not an EGL surface"); + break; + } + case EGL_BAD_CONTEXT: + { + DALI_LOG_ERROR("EGL_BAD_CONTEXT : Context is not an EGL rendering context"); + break; + } + case EGL_BAD_MATCH: + { + DALI_LOG_ERROR("EGL_BAD_MATCH : Draw or read are not compatible with context, or if context is set to EGL_NO_CONTEXT and draw or read are not set to EGL_NO_SURFACE, or if draw or read are set to EGL_NO_SURFACE and context is not set to EGL_NO_CONTEXT"); + break; + } + case EGL_BAD_ACCESS: + { + DALI_LOG_ERROR("EGL_BAD_ACCESS : Context is current to some other thread"); + break; + } + case EGL_BAD_NATIVE_PIXMAP: + { + DALI_LOG_ERROR("EGL_BAD_NATIVE_PIXMAP : A native pixmap underlying either draw or read is no longer valid."); + break; + } + case EGL_BAD_NATIVE_WINDOW: + { + DALI_LOG_ERROR("EGL_BAD_NATIVE_WINDOW : A native window underlying either draw or read is no longer valid."); + break; + } + case EGL_BAD_CURRENT_SURFACE: + { + DALI_LOG_ERROR("EGL_BAD_CURRENT_SURFACE : The previous context has unflushed commands and the previous surface is no longer valid."); + break; + } + case EGL_BAD_ALLOC: + { + DALI_LOG_ERROR("EGL_BAD_ALLOC : Allocation of ancillary buffers for draw or read were delayed until eglMakeCurrent is called, and there are not enough resources to allocate them"); + break; + } + case EGL_CONTEXT_LOST: + { + DALI_LOG_ERROR("EGL_CONTEXT_LOST : If a power management event has occurred. The application must destroy all contexts and reinitialise OpenGL ES state and objects to continue rendering"); + break; + } + default: + { + DALI_LOG_ERROR("Unknown error"); + break; + } + } + DALI_ASSERT_ALWAYS(false && "MakeContextCurrent failed!"); + } + + DALI_LOG_WARNING("- EGL Information\nVendor: %s\nVersion: %s\nClient APIs: %s\nExtensions: %s\n", + eglQueryString(mEglDisplay, EGL_VENDOR), + eglQueryString(mEglDisplay, EGL_VERSION), + eglQueryString(mEglDisplay, EGL_CLIENT_APIS), + eglQueryString(mEglDisplay, EGL_EXTENSIONS)); + +} + +void EglImplementation::MakeContextNull() +{ + mContextCurrent = false; + // clear the current context + eglMakeCurrent( mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); +} + +void EglImplementation::TerminateGles() +{ + if ( mGlesInitialized ) + { + // in latest Mali DDK (r2p3 ~ r3p0 in April, 2012), + // MakeContextNull should be called before eglDestroy surface + // to prevent crash in _mali_surface_destroy_callback + MakeContextNull(); + + if(mIsOwnSurface && mCurrentEglSurface) + { + eglDestroySurface(mEglDisplay, mCurrentEglSurface); + } + eglDestroyContext(mEglDisplay, mEglContext); + + eglTerminate(mEglDisplay); + + mEglDisplay = NULL; + mEglConfig = NULL; + mEglContext = NULL; + mCurrentEglSurface = NULL; + + mGlesInitialized = false; + } +} + +bool EglImplementation::IsGlesInitialized() const +{ + return mGlesInitialized; +} + +void EglImplementation::SwapBuffers() +{ + eglSwapBuffers( mEglDisplay, mCurrentEglSurface ); +} + +void EglImplementation::CopyBuffers() +{ + eglCopyBuffers( mEglDisplay, mCurrentEglSurface, mCurrentEglNativePixmap ); +} + +void EglImplementation::WaitGL() +{ + eglWaitGL(); +} + +void EglImplementation::ChooseConfig( bool isWindowType, ColorDepth depth ) +{ + if(mEglConfig && isWindowType == mIsWindow && mColorDepth == depth) + { + return; + } + + mIsWindow = isWindowType; + + EGLint numConfigs; + Vector configAttribs; + configAttribs.Reserve(31); + + if(isWindowType) + { + configAttribs.PushBack( EGL_SURFACE_TYPE ); + configAttribs.PushBack( EGL_WINDOW_BIT ); + } + else + { + DALI_ASSERT_ALWAYS(!"uninplemented"); + configAttribs.PushBack( EGL_SURFACE_TYPE ); + configAttribs.PushBack( EGL_PIXMAP_BIT ); + } + + configAttribs.PushBack( EGL_RENDERABLE_TYPE ); + +#if DALI_GLES_VERSION >= 30 + DALI_ASSERT_ALWAYS(!"uninplemented"); + +#ifdef _ARCH_ARM_ + configAttribs.PushBack( EGL_OPENGL_ES3_BIT_KHR ); +#else + // There is a bug in the desktop emulator + // Requesting for ES3 causes eglCreateContext even though it allows to ask + // for a configuration that supports GLES 3.0 + configAttribs.PushBack( EGL_OPENGL_ES2_BIT ); +#endif // _ARCH_ARM_ + +#else // DALI_GLES_VERSION >= 30 + + configAttribs.PushBack( EGL_OPENGL_ES2_BIT ); + +#endif //DALI_GLES_VERSION >= 30 + + configAttribs.PushBack( EGL_RED_SIZE ); + configAttribs.PushBack( 8 ); + configAttribs.PushBack( EGL_GREEN_SIZE ); + configAttribs.PushBack( 8 ); + configAttribs.PushBack( EGL_BLUE_SIZE ); + configAttribs.PushBack( 8 ); + + // + // Setting the alpha crashed .... need SDL_SetVideo(...) with alpha somehow?? + // + + configAttribs.PushBack( EGL_ALPHA_SIZE ); + configAttribs.PushBack( 8 ); + configAttribs.PushBack( EGL_DEPTH_SIZE ); + configAttribs.PushBack( 24 ); + + configAttribs.PushBack( EGL_NONE ); + + if ( eglChooseConfig( mEglDisplay, &(configAttribs[0]), &mEglConfig, 1, &numConfigs ) != EGL_TRUE ) + { + EGLint error = eglGetError(); + switch (error) + { + case EGL_BAD_DISPLAY: + { + DALI_LOG_ERROR("Display is not an EGL display connection"); + break; + } + case EGL_BAD_ATTRIBUTE: + { + DALI_LOG_ERROR("The parameter confirAttribs contains an invalid frame buffer configuration attribute or an attribute value that is unrecognized or out of range"); + break; + } + case EGL_NOT_INITIALIZED: + { + DALI_LOG_ERROR("Display has not been initialized"); + break; + } + case EGL_BAD_PARAMETER: + { + DALI_LOG_ERROR("The parameter numConfig is NULL"); + break; + } + default: + { + DALI_LOG_ERROR("Unknown error"); + } + } + DALI_ASSERT_ALWAYS(false && "eglChooseConfig failed!"); + } + + if ( numConfigs != 1 ) + { + DALI_LOG_ERROR("No configurations found."); + TEST_EGL_ERROR("eglChooseConfig"); + } +} + + +void EglImplementation::CreateSurfaceWindow( EGLNativeWindowType window, ColorDepth depth ) +{ + DALI_ASSERT_ALWAYS( ( mCurrentEglSurface == 0 ) && "EGL surface already exists" ); + + mColorDepth = depth; + mIsWindow = true; + + // egl choose config + static_cast(window); + EGLNativeWindowType dummyWindow = NULL; + + mCurrentEglSurface = eglCreateWindowSurface( mEglDisplay, mEglConfig, dummyWindow, NULL ); + + + TEST_EGL_ERROR("eglCreateWindowSurface"); + + DALI_ASSERT_ALWAYS( mCurrentEglSurface && "Create window surface failed" ); +} + + +EGLSurface EglImplementation::CreateSurfacePixmap( EGLNativePixmapType pixmap, ColorDepth depth ) +{ + DALI_ASSERT_ALWAYS( mCurrentEglSurface == 0 && "Cannot create more than one instance of surface pixmap" ); + + mCurrentEglNativePixmap = pixmap; + mColorDepth = depth; + mIsWindow = false; + + // egl choose config + ChooseConfig(mIsWindow, mColorDepth); + + mCurrentEglSurface = eglCreatePixmapSurface( mEglDisplay, mEglConfig, mCurrentEglNativePixmap, NULL ); + TEST_EGL_ERROR("eglCreatePixmapSurface"); + + DALI_ASSERT_ALWAYS( mCurrentEglSurface && "Create pixmap surface failed" ); + + return mCurrentEglSurface; +} + +bool EglImplementation::ReplaceSurfaceWindow( EGLNativeWindowType window ) +{ + DALI_ASSERT_ALWAYS(!"Unimplemented"); + + bool contextLost = false; + + // the surface is bound to the context, so set the context to null + MakeContextNull(); + + // destroy the surface + DestroySurface(); + + // create the EGL surface + CreateSurfaceWindow( window, mColorDepth ); + + // set the context to be current with the new surface + MakeContextCurrent(); + + return contextLost; +} + +bool EglImplementation::ReplaceSurfacePixmap( EGLNativePixmapType pixmap, EGLSurface& eglSurface ) +{ + bool contextLost = false; + + // the surface is bound to the context, so set the context to null + MakeContextNull(); + + // destroy the surface + DestroySurface(); + + // create the EGL surface + eglSurface = CreateSurfacePixmap( pixmap, mColorDepth ); + + // set the context to be current with the new surface + MakeContextCurrent(); + + return contextLost; +} + +EGLDisplay EglImplementation::GetDisplay() const +{ + return mEglDisplay; +} + +EGLDisplay EglImplementation::GetContext() const +{ + return mEglContext; +} + +} // namespace Adaptor + +} // namespace Internal + +} // namespace Dali + diff --git a/adaptors/emscripten/main.cpp b/adaptors/emscripten/main.cpp new file mode 100644 index 000000000..e4064e0ae --- /dev/null +++ b/adaptors/emscripten/main.cpp @@ -0,0 +1,110 @@ + +#include "sdl-application.h" +#include + +#include + +#include + +// main loop function called by emscripten/browser +extern void emscripten_set_main_loop(void (*func)(), int fps, int simulate_infinite_loop); + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ +extern void RenderFinished(); +}; +}; +}; + +Dali::SdlApplication *app = NULL; + +void EmscriptenMouseEvent(double x, double y, int downUpMotion) +{ + DALI_ASSERT_ALWAYS(app); + + if(app) + { + app->SendTouchEvent(x,y, downUpMotion); + } +} + +void ProcessEvents() +{ + SDL_PumpEvents(); + + // + // This wasnt working prior to emscripten v1.25 + // + // But it only gives event inside the gl window. + // When dragging (for rotation etc) we want the drag/rotate to continue outside the window + // + // So we'll disable this handling for now + // + SDL_Event event; + while (SDL_PollEvent(&event)) + { + } // Poll(event) + +} // ProcessEvents() + + +void EmscriptenUpdateOnce() +{ + DALI_ASSERT_ALWAYS(app); + app->DoUpdate(); +}; + +void EmscriptenRenderOnce() +{ + DALI_ASSERT_ALWAYS(app); + + static int w = 0; + static int h = 0; + + // 'Module' here should be 'dali' with emcc switch -s EXPORT_NAME="dali" + // but on upgrading to emscripten 1.34.2 it's broken. + int _x = EM_ASM_INT_V({ return Module.canvas.width; }); + int _y = EM_ASM_INT_V({ return Module.canvas.height; }); + + bool resize = false; + if( _x != w ) + { + w = _x; + resize = true; + } + if( _y != h ) + { + h = _y; + resize = true; + } + if( resize ) + { + app->SetSurfaceWidth(w, h); + } + + ProcessEvents(); + + EmscriptenUpdateOnce(); + + app->DoRender(); + + Dali::Internal::Emscripten::RenderFinished(); +} + +int main(int argc, char *argv[]) +{ + using namespace Dali; + + // need to reference everything as emscripten/llvm will cut it all out so put an ImageActor here + Dali::ImageActor ia; + + app = new SdlApplication( 0, 0, SdlApplication::DEFAULT_HORIZONTAL_DPI, SdlApplication::DEFAULT_VERTICAL_DPI ); + + emscripten_set_main_loop(EmscriptenRenderOnce, 0, 1); + + return 1; +} diff --git a/adaptors/emscripten/sdl-application.cpp b/adaptors/emscripten/sdl-application.cpp new file mode 100644 index 000000000..132cddf74 --- /dev/null +++ b/adaptors/emscripten/sdl-application.cpp @@ -0,0 +1,269 @@ +// EXTERNAL INCLUDES +#include "sdl-application.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// INTERNAL INCLUDES +#include "integration-api/egl-interface.h" +#include "base/separate-update-render/frame-time.h" +#include + +#include "platform-abstractions/emscripten/emscripten-callbacks.h" + +// emscripten are using SDL version 1.3 that isnt available for download +// but version 2.0 is close to version 1.3 but version 1.2 support removed. +// This is one way it isnt. +#if SDL_MAJOR_VERSION == 2 +# define VIDEO_FLAGS SDL_WINDOW_OPENGL +#else +# define VIDEO_FLAGS SDL_OPENGL +#endif + +namespace +{ + +int _kbhit() +{ + static const int STDIN = 0; + static bool initialized = false; + + if (! initialized) + { + // Use termios to turn off line buffering + termios term; + tcgetattr(STDIN, &term); + term.c_lflag &= ~ICANON; + tcsetattr(STDIN, TCSANOW, &term); + setbuf(stdin, NULL); + initialized = true; + } + + int bytesWaiting; + ioctl(STDIN, FIONREAD, &bytesWaiting); + return bytesWaiting; +} + + +void LogFunction(Dali::Integration::Log::DebugPriority priority, std::string& message) +{ + printf("%s", message.c_str()); + EM_ASM( console.log( message.c_str() ) ); +} + +} + +namespace Dali +{ + +typedef ::Pixmap XPixmap; +typedef ::Window XWindow; +typedef ::Display XDisplay; +typedef ::Screen XScreen; + +const unsigned int SdlApplication::DEFAULT_SURFACE_WIDTH = 600; +const unsigned int SdlApplication::DEFAULT_SURFACE_HEIGHT= 480; + + +SdlApplication::SdlApplication( size_t surfaceWidth, + size_t surfaceHeight, + float horizontalDpi, + float verticalDpi ) + : mCore( NULL ), + mSurfaceWidth( surfaceWidth ), + mSurfaceHeight( surfaceHeight ), + mFrame( 0u ), + mSeconds(0), + mMicroSeconds(0) +{ + + EGLNativeDisplayType display = (EGLNativeDisplayType)XOpenDisplay(NULL); + bool isOwnSurface = true; + mEglImplementation.InitializeGles( display, isOwnSurface ); + + SdlCreateWindow(surfaceWidth, surfaceHeight, "Dali"); + + bool isWindowType = true; + Dali::ColorDepth depth = Dali::COLOR_DEPTH_32; + mEglImplementation.ChooseConfig( isWindowType, depth ); + + EGLNativeWindowType window = NULL; + + mEglImplementation.CreateSurfaceWindow( window, depth ); + + mEglImplementation.CreateContext(); + + mEglImplementation.MakeContextCurrent(); + + // + // SDL/EGL setup, now create core + // + mCore = Dali::Integration::Core::New( + mRenderController, + mPlatformAbstraction, + mGlAbstraction, + mGlSyncAbstraction, + mGestureManager, + ResourcePolicy::DataRetention::DALI_RETAINS_ALL_DATA); + + mCore->ContextCreated(); + mCore->SurfaceResized( mSurfaceWidth, mSurfaceHeight ); + mCore->SetDpi( horizontalDpi, verticalDpi ); + + Dali::Integration::Log::InstallLogFunction( LogFunction ); + + mCore->SceneCreated(); +} + +SdlApplication::~SdlApplication() +{ + Dali::Integration::Log::UninstallLogFunction(); + delete mCore; + SDL_Quit(); +} + +void SdlApplication::SdlCreateWindow(size_t surfaceWidth, + size_t surfaceHeight, + const std::string &title) +{ + if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) + { + DALI_LOG_WARNING("SDL_Init Err: %s\n", SDL_GetError() ); + DALI_ASSERT_ALWAYS(!"Couldn't initialize SDL"); + } + + // load support for the JPG and PNG image formats + int flags = IMG_INIT_JPG | IMG_INIT_PNG; + int initted = IMG_Init(flags); + if( (initted & flags) != flags) + { + DALI_LOG_WARNING("IMG_Init Err:%s\n", IMG_GetError()); + DALI_ASSERT_ALWAYS("!IMG_Init: Failed to init required jpg and png support!\n"); + } + + float gamma = 0.0; + int bpp = 0; // 0 means current display bpp + + Uint32 video_flags = VIDEO_FLAGS; + + SDL_Surface *surface = SDL_SetVideoMode( static_cast(surfaceWidth), + static_cast(surfaceHeight), + bpp, + video_flags ); + + if ( surface == NULL ) + { + DALI_LOG_WARNING("Couldn't set GL mode: %s\n", SDL_GetError()); + DALI_ASSERT_ALWAYS("Couldn't set GL mode"); + SDL_Quit(); + exit(1); + } + + /* Set the window manager title bar */ + SDL_WM_SetCaption( title.c_str(), "daliweb" ); + + /* Set the gamma for the window */ + if ( gamma != 0.0 ) + { + SDL_SetGamma(gamma, gamma, gamma); + } + +} + +void SdlApplication::DoUpdate( void ) +{ + // pump events + mCore->ProcessEvents(); + + // Update Time values + static Internal::Adaptor::FrameTime frameTime; + static bool init = false; + if( !init ) + { + frameTime.SetMinimumFrameTimeInterval( 16667 ); + init = true; + } + + static unsigned int frameNo = 0; + frameNo++; + frameTime.SetSyncTime(frameNo); + + float lastFrameDelta( 0.0f ); + unsigned int lastSyncTime( 0 ); + unsigned int nextSyncTime( 0 ); + frameTime.PredictNextSyncTime( lastFrameDelta, lastSyncTime, nextSyncTime ); + + Integration::UpdateStatus status; + + mCore->Update( lastFrameDelta, lastSyncTime, nextSyncTime, status ); + + Dali::Internal::Emscripten::stats.lastFrameDeltaSeconds = lastFrameDelta; + Dali::Internal::Emscripten::stats.lastSyncTimeMilliseconds = lastSyncTime; + Dali::Internal::Emscripten::stats.nextSyncTimeMilliseconds = nextSyncTime; + + Dali::Internal::Emscripten::stats.keepUpdating = status.keepUpdating; + Dali::Internal::Emscripten::stats.needsNotification = status.needsNotification; + Dali::Internal::Emscripten::stats.secondsFromLastFrame = status.secondsFromLastFrame; + +} + + +void SdlApplication::DoRender() +{ + // render + mCore->Render( mRenderStatus ); + + mFrame++; + + Dali::Internal::Emscripten::stats.frameCount = mFrame; + + mEglImplementation.SwapBuffers(); + +} + +void SdlApplication::SendTouchEvent(double x, double y, int mouseState) +{ + TouchPoint::State state = TouchPoint::Up; + if( 0 == mouseState ) + { + state = TouchPoint::Down; + } + else if( 1 == mouseState ) + { + state = TouchPoint::Up; + } + else if( 2 == mouseState ) + { + state = TouchPoint::Motion; + } + + Dali::Integration::TouchEvent e; + e.AddPoint( TouchPoint( 0, + state, + static_cast(x), + static_cast(y) ) ); + + mCore->QueueEvent(e); +} + +void SdlApplication::SetSurfaceWidth( unsigned int width, unsigned height ) +{ + mSurfaceWidth = width; + mSurfaceHeight = height; + + mCore->SurfaceResized( mSurfaceWidth, mSurfaceHeight ); +} + +} diff --git a/adaptors/emscripten/sdl-application.h b/adaptors/emscripten/sdl-application.h new file mode 100644 index 000000000..d95c4db58 --- /dev/null +++ b/adaptors/emscripten/sdl-application.h @@ -0,0 +1,143 @@ +#ifndef __DALI_SDL_APPLICATION_H__ +#define __DALI_SDL_APPLICATION_H__ + +/* +Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + +This file is part of Dali + +PROPRIETARY/CONFIDENTIAL + +This software is the confidential and proprietary information of +SAMSUNG ELECTRONICS ("Confidential Information"). You shall not +disclose such Confidential Information and shall use it only in +accordance with the terms of the license agreement you entered +into with SAMSUNG ELECTRONICS. + +SAMSUNG make no representations or warranties about the suitability +of the software, either express or implied, including but not limited +to the implied warranties of merchantability, fitness for a particular +purpose, or non-infringement. SAMSUNG shall not be liable for any +damages suffered by licensee as a result of using, modifying or +distributing this software or its derivatives. +*/ + +// INTERNAL INCLUDES +#include "platform-abstractions/emscripten/emscripten-platform-abstraction.h" +#include "sdl-gesture-manager.h" +#include "adaptors/common/gl/gl-implementation.h" +#include "sdl-gl-sync-abstraction.h" +#include "sdl-render-controller.h" +#include +#include +#include +#include + +namespace Dali +{ + +/** + * + * An SdlApplication object for Emscripten main() + * + * This provides libSDL calls for creating the application surface and running + * the update and rendering. + * The libSDL calls are stubbed by Emscripten into browser canvas construction + * calls. The update and rendering are exposed as we are in a single threaded + * environment and the browser needs to control the main loop. + * Emscripten provides emscripten_set_main_loop() for the 'loop' function which + * should render one frame and return + * + */ +class DALI_IMPORT_API SdlApplication : public ConnectionTracker +{ +public: + + // Default values derived from H2 device. + static const unsigned int DEFAULT_SURFACE_WIDTH; + static const unsigned int DEFAULT_SURFACE_HEIGHT; + +#ifdef _CPP11 + static constexpr float DEFAULT_HORIZONTAL_DPI = 220.0f; + static constexpr float DEFAULT_VERTICAL_DPI = 217.0f; +#else + static const float DEFAULT_HORIZONTAL_DPI = 220.0f; + static const float DEFAULT_VERTICAL_DPI = 217.0f; +#endif + + static const unsigned int DEFAULT_RENDER_INTERVAL = 1; + + /** + * Constructor + * + * @param[in] surfaceWidth Initial width of the browser canvas + * @param[in] surfaceHeight Initial height of the browser canvas + * @param[in] horizontalDpi Horizontal Dpi + * @param[in] verticalDpi Vertical Dpi + */ + SdlApplication( size_t surfaceWidth = DEFAULT_SURFACE_WIDTH, + size_t surfaceHeight = DEFAULT_SURFACE_HEIGHT, + float horizontalDpi = DEFAULT_HORIZONTAL_DPI, + float verticalDpi = DEFAULT_VERTICAL_DPI ); + + /** + * Destructor + */ + virtual ~SdlApplication(); + + /** + * Run the update once. + * The browser is a single threaded environment and this is called on a browser callback. + */ + void DoUpdate(); + + /** + * Render once. The browser is a single threaded environment. + * The browser is a single threaded environment and this is called on a browser callback. + */ + void DoRender(); + + /** + * Send Touch event into Dali event loop. + * The allows the browser to supply touch events. + */ + void SendTouchEvent(double x, double y, int downUpMotion); + + /** + * Set surface width. + * The allows the browser to tell Dali that the rendering canvas has changed size. + * + * @param[in] width The surface/canvas width + * @param[in] height The surface/canvas height + */ + void SetSurfaceWidth( unsigned int width, unsigned height ); + +protected: + EmscriptenPlatformAbstraction mPlatformAbstraction; + SdlGlSyncAbstraction mGlSyncAbstraction; + SdlRenderController mRenderController; + Internal::Adaptor::GlImplementation mGlAbstraction; + SdlGestureManager mGestureManager; + + Integration::UpdateStatus mStatus; + Integration::RenderStatus mRenderStatus; + + Integration::Core* mCore; + Internal::Adaptor::EglImplementation mEglImplementation; + + unsigned int mSurfaceWidth; + unsigned int mSurfaceHeight; + unsigned int mFrame; + + unsigned int mSeconds; + unsigned int mMicroSeconds; + + void SdlCreateWindow(size_t surfaceWidth, + size_t surfaceHeight, + const std::string &title); + +}; + +} // Dali + +#endif // header diff --git a/adaptors/emscripten/sdl-gesture-manager.h b/adaptors/emscripten/sdl-gesture-manager.h new file mode 100644 index 000000000..ddea4b76f --- /dev/null +++ b/adaptors/emscripten/sdl-gesture-manager.h @@ -0,0 +1,146 @@ +#ifndef __DALI_SDL_GESTURE_MANAGER_H__ +#define __DALI_SDL_GESTURE_MANAGER_H__ + +/* +Copyright (c) 2000-2012 Samsung Electronics Co., Ltd All Rights Reserved + +This file is part of Dali + +PROPRIETARY/CONFIDENTIAL + +This software is the confidential and proprietary information of +SAMSUNG ELECTRONICS ("Confidential Information"). You shall not +disclose such Confidential Information and shall use it only in +accordance with the terms of the license agreement you entered +into with SAMSUNG ELECTRONICS. + +SAMSUNG make no representations or warranties about the suitability +of the software, either express or implied, including but not limited +to the implied warranties of merchantability, fitness for a particular +purpose, or non-infringement. SAMSUNG shall not be liable for any +damages suffered by licensee as a result of using, modifying or +distributing this software or its derivatives. +*/ + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +/** + * Concrete implementation of the gesture manager class. + * + * A stubb class to give to core in the Emscripten/browser environment + */ +class DALI_IMPORT_API SdlGestureManager : public Dali::Integration::GestureManager +{ + +public: + + /** + * Constructor + */ + SdlGestureManager() + { + Initialize(); + } + + /** + * Destructor + */ + virtual ~SdlGestureManager() + { + } + + /** + * @copydoc Dali::Integration::GestureManager::Register(Gesture::Type) + */ + virtual void Register(const Integration::GestureRequest& request) + { + mFunctionsCalled.Register = true; + } + + /** + * @copydoc Dali::Integration::GestureManager::Unregister(Gesture::Type) + */ + virtual void Unregister(const Integration::GestureRequest& request) + { + mFunctionsCalled.Unregister = true; + } + + /** + * @copydoc Dali::Integration::GestureManager::Update(Gesture::Type) + */ + virtual void Update(const Integration::GestureRequest& request) + { + mFunctionsCalled.Update = true; + } + +public: // TEST FUNCTIONS + + // Enumeration of Gesture Manager methods + enum SdlFuncEnum + { + RegisterType, + UnregisterType, + UpdateType, + }; + + /** Call this every test */ + void Initialize() + { + mFunctionsCalled.Reset(); + } + + bool WasCalled(SdlFuncEnum func) + { + switch(func) + { + case RegisterType: return mFunctionsCalled.Register; + case UnregisterType: return mFunctionsCalled.Unregister; + case UpdateType: return mFunctionsCalled.Update; + } + return false; + } + + void ResetCallStatistics(SdlFuncEnum func) + { + switch(func) + { + case RegisterType: mFunctionsCalled.Register = false; break; + case UnregisterType: mFunctionsCalled.Unregister = false; break; + case UpdateType: mFunctionsCalled.Update = false; break; + } + } + +private: + + struct SdlFunctions + { + SdlFunctions() + : Register(false), + Unregister(false), + Update(false) + { + } + + void Reset() + { + Register = false; + Unregister = false; + Update = false; + } + + bool Register; + bool Unregister; + bool Update; + }; + + SdlFunctions mFunctionsCalled; +}; + +} // Dali + +#endif // __DALI_SDL_GESTURE_MANAGER_H__ diff --git a/adaptors/emscripten/sdl-gl-sync-abstraction.cpp b/adaptors/emscripten/sdl-gl-sync-abstraction.cpp new file mode 100644 index 000000000..fe52e6cd7 --- /dev/null +++ b/adaptors/emscripten/sdl-gl-sync-abstraction.cpp @@ -0,0 +1,32 @@ +#include "sdl-gl-sync-abstraction.h" + +using namespace Dali; + + +class SdlSyncObject : public Integration::GlSyncAbstraction::SyncObject +{ +public: + virtual bool IsSynced() { return true; } +}; + + +SdlGlSyncAbstraction::~SdlGlSyncAbstraction() +{ +} + +bool SdlGlSyncAbstraction::SyncObject::IsSynced() +{ + return true; +} + +Integration::GlSyncAbstraction::SyncObject* SdlGlSyncAbstraction::CreateSyncObject() +{ + return new SdlSyncObject(); +} + + +void SdlGlSyncAbstraction::DestroySyncObject(Integration::GlSyncAbstraction::SyncObject* syncObject) +{ + delete static_cast( syncObject ); +} + diff --git a/adaptors/emscripten/sdl-gl-sync-abstraction.h b/adaptors/emscripten/sdl-gl-sync-abstraction.h new file mode 100644 index 000000000..21b536b88 --- /dev/null +++ b/adaptors/emscripten/sdl-gl-sync-abstraction.h @@ -0,0 +1,48 @@ +#ifndef __DALI_INTEGRATION_SDL_GL_SYNC_ABSTRACTION_H__ +#define __DALI_INTEGRATION_SDL_GL_SYNC_ABSTRACTION_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.0 (the License); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://floralicense.org/license/ +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include + +namespace Dali +{ + +/** + * + * This is a stubb GLSyncAbstraction class to pass to core for the Emscripten/browser environment + */ +class DALI_IMPORT_API SdlGlSyncAbstraction : public Dali::Integration::GlSyncAbstraction +{ +public: + + virtual ~SdlGlSyncAbstraction(); + + /** + * @copydoc Dali::Integration::GlSyncAbstraction::CreateSyncObject() + */ + virtual Integration::GlSyncAbstraction::SyncObject* CreateSyncObject(); + + /** + * @copydoc Dali::Integration::GlSyncAbstraction::DestroySyncObject(Integration::GlSyncAbstraction::SyncObject* syncObject) + */ + virtual void DestroySyncObject(Integration::GlSyncAbstraction::SyncObject* syncObject); +}; + +} // namespace Dali + +#endif // __DALI_INTEGRATION_SDL_GL_SYNC_ABSTRACTION_H__ diff --git a/adaptors/emscripten/sdl-render-controller.h b/adaptors/emscripten/sdl-render-controller.h new file mode 100644 index 000000000..d113c6906 --- /dev/null +++ b/adaptors/emscripten/sdl-render-controller.h @@ -0,0 +1,83 @@ +#ifndef __SDL_RENDER_CONTROLLER_H__ +#define __SDL_RENDER_CONTROLLER_H__ + +/* +Copyright (c) 2000-2012 Samsung Electronics Co., Ltd All Rights Reserved + +This file is part of Dali + +PROPRIETARY/CONFIDENTIAL + +This software is the confidential and proprietary information of +SAMSUNG ELECTRONICS ("Confidential Information"). You shall not +disclose such Confidential Information and shall use it only in +accordance with the terms of the license agreement you entered +into with SAMSUNG ELECTRONICS. + +SAMSUNG make no representations or warranties about the suitability +of the software, either express or implied, including but not limited +to the implied warranties of merchantability, fitness for a particular +purpose, or non-infringement. SAMSUNG shall not be liable for any +damages suffered by licensee as a result of using, modifying or +distributing this software or its derivatives. +*/ + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +/** + * This class is a stubb RenderController to pass to core in the Emscripten/browser environment. + * + */ +class DALI_IMPORT_API SdlRenderController : public Dali::Integration::RenderController +{ +public: + SdlRenderController() + { + Initialize(); + } + + ~SdlRenderController() + { + } + + /** + * @copydoc Dali::Integration::RenderController::RequestUpdate() + */ + virtual void RequestUpdate() + { + mRequestUpdateCalled = true; + } + + /** + * @copydoc Dali::Integration::RenderController::RequestProcessEventsOnIdle() + */ + virtual void RequestProcessEventsOnIdle() + { + } + + /** + * @copydoc Dali::Integration::RenderController::RequestNotificationEventOnIdle() + */ + virtual void RequestNotificationEventOnIdle() + { + mRequestNotificationEventOnIdleCalled = true; + } + + void Initialize() + { + mRequestUpdateCalled = false; + mRequestNotificationEventOnIdleCalled = false; + } + +private: + bool mRequestUpdateCalled; + bool mRequestNotificationEventOnIdleCalled; +}; + +} // Dali + +#endif diff --git a/adaptors/emscripten/wrappers/actor-wrapper.cpp b/adaptors/emscripten/wrappers/actor-wrapper.cpp new file mode 100644 index 000000000..47f81eef5 --- /dev/null +++ b/adaptors/emscripten/wrappers/actor-wrapper.cpp @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "actor-wrapper.h" + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include "emscripten-utils.h" +#include "signal-holder.h" + +namespace +{ + enum ConditionType + { + False, ///< Result Always False + LessThan, ///< Magnitude of type is less than float value (arg0). + GreaterThan, ///< Magnitude of type is greater than float value (arg0). + Inside, ///< Magnitude of type is within float values (arg0 & arg1). + Outside, ///< Magnitude of type is outside float values (arg0 & arg1). + Step, ///< Value of type has crossed a step amount + VariableStep ///< Similar to step, except user can define a list of steps from reference value + }; + + Dali::Scripting::StringEnum ConditionTypeTable[] = + { { "False", ConditionType::False}, + { "LessThan", ConditionType::LessThan}, + { "GreaterThan", ConditionType::GreaterThan}, + { "Inside", ConditionType::Inside}, + { "Outside", ConditionType::Outside}, + { "Step", ConditionType::Step}, + { "VariableStep", ConditionType::VariableStep} + }; + +const unsigned int ConditionTypeTableSize = sizeof( ConditionTypeTable ) / sizeof( ConditionTypeTable[0] ); + +}; + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +emscripten::val JavascriptValue( const Dali::Property::Value& v ); + +/** + * Struct to hold notification callback + */ +struct EmscriptenNotifiy +{ + EmscriptenNotifiy(const emscripten::val& callback ) + : mCallback(callback) {}; + emscripten::val mCallback; + + void operator()(void) + { + emscripten::val ret = mCallback(); + } +}; + +/** + * Emscripten touched signal callback. + * + * Provides more contextual state information to the browser with the OnTouched signal. + * + */ +struct EmscriptenTouchedSignal : public BaseSignalSlot +{ + EmscriptenTouchedSignal(const emscripten::val& callback, unsigned int id) + : mCallback(callback), mId(id) {}; + emscripten::val mCallback; + unsigned int mId; + + bool OnTouched(Dali::Actor actor, const Dali::TouchEvent& event) + { + Dali::Property::Map map; + Dali::Property::Array points; + + map["pointCount"] = static_cast(event.GetPointCount()); + map["time"] = static_cast(event.time); + + for(TouchPointContainer::const_iterator iter = event.points.begin(); + iter != event.points.end(); ++iter) { + const Dali::TouchPoint& pt = *iter; + Dali::Property::Map pointMap; + pointMap["deviceId"] = pt.deviceId; + pointMap["hitActorId"] = static_cast(pt.hitActor.GetId()); + pointMap["local"] = pt.local; + pointMap["screen"]= pt.screen; + + switch(pt.state) + { + case TouchPoint::Down: { pointMap["state"] = "Down"; break; } + case TouchPoint::Up: { pointMap["state"] = "Up"; break; } + case TouchPoint::Motion: { pointMap["state"] = "Motion"; break; } + case TouchPoint::Leave: { pointMap["state"] = "Leave"; break; } + case TouchPoint::Stationary: { pointMap["state"] = "Stationary"; break; } + case TouchPoint::Interrupted: { pointMap["state"] = "Interrupted"; break; } + case TouchPoint::Last: { pointMap["state"] = "Last"; break; } + }; + + points.PushBack(pointMap); + } + + map["points"] = points; + + Dali::Property::Value value(map); + emscripten::val ret = mCallback( actor, JavascriptValue(value) ); + return true; + } + + bool OnHovered(Dali::Actor actor, const Dali::HoverEvent& event) + { + Dali::Property::Map map; + Dali::Property::Array points; + + map["pointCount"] = static_cast(event.GetPointCount()); + map["time"] = static_cast(event.time); + + for(TouchPointContainer::const_iterator iter = event.points.begin(); + iter != event.points.end(); ++iter) { + const Dali::TouchPoint& pt = *iter; + Dali::Property::Map pointMap; + pointMap["deviceId"] = pt.deviceId; + pointMap["hitActorId"] = static_cast(pt.hitActor.GetId()); + pointMap["local"] = pt.local; + pointMap["screen"]= pt.screen; + + switch(pt.state) + { + case TouchPoint::Down: { pointMap["state"] = "Down"; break; } + case TouchPoint::Up: { pointMap["state"] = "Up"; break; } + case TouchPoint::Motion: { pointMap["state"] = "Motion"; break; } + case TouchPoint::Leave: { pointMap["state"] = "Leave"; break; } + case TouchPoint::Stationary: { pointMap["state"] = "Stationary"; break; } + case TouchPoint::Interrupted: { pointMap["state"] = "Interrupted"; break; } + case TouchPoint::Last: { pointMap["state"] = "Last"; break; } + }; + + points.PushBack(pointMap); + } + + map["points"] = points; + + Dali::Property::Value value(map); + emscripten::val ret = mCallback( actor, JavascriptValue(value) ); + return true; + } + +}; + +/** + * Struct to wrap a generic Emscripten callback. + * + */ +struct EmscriptenSignal +{ + EmscriptenSignal(const emscripten::val& callback, unsigned int id) + : mCallback(callback), mId(id) {}; + emscripten::val mCallback; + unsigned int mId; + bool operator()() + { + Dali::Actor a = Dali::Stage::GetCurrent().GetRootLayer().FindChildById(mId); + if(a) + { + emscripten::val ret = mCallback( a ); + } + else + { + emscripten::val ret = mCallback(); + } + return true; + } +}; + +bool ConnectSignal( Dali::Actor actor, + SignalHolder& signalHolder, + const std::string& signalName, + const emscripten::val& javascriptFunction ) +{ + bool ret = false; + if(0 == signalName.compare("touched")) + { + EmscriptenTouchedSignal* slot = new EmscriptenTouchedSignal(javascriptFunction, actor.GetId()); + actor.TouchedSignal().Connect(slot, &EmscriptenTouchedSignal::OnTouched); + signalHolder.add(slot); + ret = true; + } + else if(0 == signalName.compare("hovered")) + { + EmscriptenTouchedSignal* slot = new EmscriptenTouchedSignal(javascriptFunction, actor.GetId()); + actor.HoveredSignal().Connect(slot, &EmscriptenTouchedSignal::OnHovered); + signalHolder.add(slot); + ret = true; + } + else + { + actor.ConnectSignal( &signalHolder, signalName, EmscriptenSignal(javascriptFunction, actor.GetId())); + ret = true; + } + + return ret; +} + +unsigned int AddressOf(Dali::Actor self) +{ + return (unsigned int)&self.GetBaseObject(); +} + +std::vector ScreenToLocal(Dali::Actor self, float screenX, float screenY) +{ + std::vector ret; + float localX = 0.f; + float localY = 0.f; + bool ok = self.ScreenToLocal(localX, localY, screenX, screenY); + if( ok ) + { + ret.push_back(localX); + ret.push_back(localY); + ret.push_back(1.0); + } + else + { + ret.push_back(0.0); + ret.push_back(0.0); + ret.push_back(0.0); + } + return ret; +} + +void SetPropertyNotification( Dali::Actor self, + SignalHolder& signalHolder, + Dali::Property::Index index, const std::string& propertyConditionType, float arg0, float arg1, + const emscripten::val& javascriptFunction) +{ + unsigned int i = FindEnumIndex( propertyConditionType.c_str(), ConditionTypeTable, ConditionTypeTableSize ); + + if( i < ConditionTypeTableSize ) + { + ConditionType type = static_cast(ConditionTypeTable[i].value); + Dali::PropertyNotification notification; + switch(type) + { + case ConditionType::False: + { + notification = self.AddPropertyNotification( index, Dali::LessThanCondition(arg0) ); + } + case ConditionType::LessThan: + { + notification = self.AddPropertyNotification( index, Dali::LessThanCondition(arg0) ); + } + case ConditionType::GreaterThan: + { + notification = self.AddPropertyNotification( index, Dali::GreaterThanCondition(arg0) ); + } + case ConditionType::Inside: + { + notification = self.AddPropertyNotification( index, Dali::InsideCondition(arg0, arg1) ); + } + case ConditionType::Outside: + { + notification = self.AddPropertyNotification( index, Dali::OutsideCondition(arg0, arg1) ); + } + case ConditionType::Step: + { + notification = self.AddPropertyNotification( index, Dali::StepCondition(arg0, arg1) ); + } + case ConditionType::VariableStep: + { + notification = self.AddPropertyNotification( index, Dali::StepCondition(arg0, arg1) ); + } + }; + + if(notification) + { + notification.NotifySignal().Connect( &signalHolder, FunctorDelegate::New(EmscriptenNotifiy(javascriptFunction)) ); + } + else + { + EM_ASM( throw "Cannot set notification" ); + } + } +} + + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali diff --git a/adaptors/emscripten/wrappers/actor-wrapper.h b/adaptors/emscripten/wrappers/actor-wrapper.h new file mode 100644 index 000000000..6a35cda25 --- /dev/null +++ b/adaptors/emscripten/wrappers/actor-wrapper.h @@ -0,0 +1,99 @@ +#ifndef __DALI_ACTOR_WRAPPER_H__ +#define __DALI_ACTOR_WRAPPER_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include +#include "emscripten/emscripten.h" +#include "emscripten/val.h" + +// INTERNAL INCLUDES +#include "signal-holder.h" + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +/** + * Exposes a representation of the actor base object pointer for javascript debugging + * + * @param[in] self The dali actor + * + * @return the address as an int + */ +unsigned int AddressOf(Dali::Actor self); + +/** + * Provides screen to local values conveniently for javascript + * + * @param[in] self The dali actor + * @param[in] screenX The screen X coordinate + * @param[in] screenY The screen Y coordinate + * + * @return the local coordinates + */ +std::vector ScreenToLocal(Dali::Actor self, float screenX, float screenY); + +/** + * Connect a javascript function to a dali signal. + * + * Handles touched and hovered specially to provide more context than Dali provides. + * + * @param[in] actor The dali actor + * @param[in] signalHolder The Dali signal holder + * @param[in] signalName The name of the signal + * @param[in] javascriptFunction The function to call back when the signal is triggered + * + * @return the local coordinates + * + */ +bool ConnectSignal( Dali::Actor actor, + SignalHolder& signalHolder, + const std::string& signalName, + const emscripten::val& javascriptFunction ); + +/** + * Sets a javascript function to an actor property notification + * + * @param[in] self The dali actor + * @param[in] signalHolder The Dali signal holder + * @param[in] index The property Index + * @param[in] propertyConditionType The condition type name + * @param[in] arg0 The property notification arg0 + * @param[in] arg1 The property notification arg1 + * @param[in] propertyConditionType The condition type name + * @param[in] javascriptFunction The function to call back when the signal is triggered + * + * @return the local coordinates + + */ +void SetPropertyNotification( Dali::Actor self, + SignalHolder& signalHolder, + Dali::Property::Index index, const std::string& propertyConditionType, float arg0, float arg1, + const emscripten::val& javascriptFunction); + + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali + +#endif // header diff --git a/adaptors/emscripten/wrappers/animation-wrapper.cpp b/adaptors/emscripten/wrappers/animation-wrapper.cpp new file mode 100644 index 000000000..eafd0c4b1 --- /dev/null +++ b/adaptors/emscripten/wrappers/animation-wrapper.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// #include "animation-wrapper.h" + +// EXTERNAL INCLUDES +#include "emscripten/emscripten.h" +#include + +// INTERNAL INCLUDES +#include "property-value-wrapper.h" + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +void KeyFramesAddWithAlpha(Dali::KeyFrames& keyFrames, float progress, Property::Value& value, + const std::string& alphaFunction) +{ + Dali::AlphaFunction func(AlphaFunction::LINEAR); + + if( alphaFunction == "LINEAR" ) + { + func = Dali::AlphaFunction(AlphaFunction::LINEAR); + } + else if( alphaFunction == "REVERSE" ) + { + func = Dali::AlphaFunction(AlphaFunction::REVERSE); + } + else if( alphaFunction == "EASE_IN_SQUARE" ) + { + func = Dali::AlphaFunction(AlphaFunction::EASE_IN_SQUARE); + } + else if( alphaFunction == "EASE_OUT_SQUARE" ) + { + func = Dali::AlphaFunction(AlphaFunction::EASE_OUT_SQUARE); + } + else if( alphaFunction == "EASE_IN" ) + { + func = Dali::AlphaFunction(AlphaFunction::EASE_IN); + } + else if( alphaFunction == "EASE_OUT" ) + { + func = Dali::AlphaFunction(AlphaFunction::EASE_OUT); + } + else if( alphaFunction == "EASE_IN_OUT" ) + { + func = Dali::AlphaFunction(AlphaFunction::EASE_IN_OUT); + } + else if( alphaFunction == "EASE_IN_SINE" ) + { + func = Dali::AlphaFunction(AlphaFunction::EASE_IN_SINE); + } + else if( alphaFunction == "EASE_OUT_SINE" ) + { + func = Dali::AlphaFunction(AlphaFunction::EASE_OUT_SINE); + } + else if( alphaFunction == "EASE_IN_OUT_SINE" ) + { + func = Dali::AlphaFunction(AlphaFunction::EASE_IN_OUT_SINE); + } + else if( alphaFunction == "BOUNCE" ) + { + func = Dali::AlphaFunction(AlphaFunction::BOUNCE); + } + else if( alphaFunction == "SIN" ) + { + func = Dali::AlphaFunction(AlphaFunction::SIN); + } + else if( alphaFunction == "EASE_OUT_BACK" ) + { + func = Dali::AlphaFunction(AlphaFunction::EASE_OUT_BACK); + } + else + { + EM_ASM( console.log( "KeyFramesAddWithAlpha: Unknown alpha function" ) ); + } + + keyFrames.Add(progress, value, func); +} + +void AnimateTo(Dali::Animation& self, + Dali::Handle& handle, + const std::string& property, + const Dali::Property::Value& destinationValue, + const Dali::AlphaFunction::BuiltinFunction& alphaFunction, + const float delay, + const float duration) +{ + DALI_ASSERT_ALWAYS(self); + + Dali::Property::Index propertyIndex = handle.GetPropertyIndex(property); + + Dali::Property::Type propertyType = handle.GetPropertyType(propertyIndex); + + if( propertyType != destinationValue.GetType() ) + { + EM_ASM( throw "animateTo property types are not the same" ); + } + else if( propertyIndex != Dali::Property::INVALID_INDEX ) + { + Dali::Property target( handle, propertyIndex ); + self.AnimateTo( target, destinationValue, alphaFunction, Dali::TimePeriod(delay, duration) ); + } + else + { + EM_ASM( throw "unknown property" ); + } +} + +void AnimateBy(Dali::Animation& self, + Dali::Handle& handle, + const std::string& property, + const Dali::Property::Value& destinationValue, + const Dali::AlphaFunction::BuiltinFunction& alphaFunction, + const float delay, + const float duration) +{ + DALI_ASSERT_ALWAYS(self); + + Dali::Property::Index propertyIndex = handle.GetPropertyIndex(property); + + Dali::Property::Type propertyType = handle.GetPropertyType(propertyIndex); + + if( propertyType != destinationValue.GetType() ) + { + EM_ASM( throw "animateTo property types are not the same" ); + } + else if( propertyIndex != Dali::Property::INVALID_INDEX ) + { + Dali::Property target( handle, propertyIndex ); + self.AnimateBy( target, destinationValue, alphaFunction, Dali::TimePeriod(delay,duration)); + } + else + { + EM_ASM( throw "unknown property" ); + } +} + +void AnimateBetween(Dali::Animation& self, + Dali::Handle& handle, + const std::string& property, + Dali::KeyFrames& keyFrames, + const Dali::AlphaFunction::BuiltinFunction& alphaFunction, + const float delay, + const float duration, + const Dali::Animation::Interpolation& interpolation) +{ + DALI_ASSERT_ALWAYS(self); + + Dali::Property::Index propertyIndex = handle.GetPropertyIndex(property); + + if( propertyIndex != Dali::Property::INVALID_INDEX ) + { + Dali::Property target( handle, propertyIndex ); + self.AnimateBetween(target, keyFrames, alphaFunction, Dali::TimePeriod(delay, duration), interpolation); + } + else + { + EM_ASM( throw "unknown property" ); + } +} + +void AnimatePath(Dali::Animation& self, + Dali::Handle& target, + const Dali::Path& path, + const Dali::Vector3& forward, + const Dali::AlphaFunction::BuiltinFunction& alphaFunction, + const float delay, + const float duration) +{ + DALI_ASSERT_ALWAYS(self); + + if(!path) + { + printf("Unable to add animation, bad path object\n"); + } + else + { + Dali::Actor actor = Dali::Actor::DownCast(target); + if( !actor ) + { + printf("Unable to add path animation, bad actor\n"); + } + else + { + self.Animate( actor, path, forward, alphaFunction, Dali::TimePeriod( delay, duration ) ); + } + } +} + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali diff --git a/adaptors/emscripten/wrappers/animation-wrapper.h b/adaptors/emscripten/wrappers/animation-wrapper.h new file mode 100644 index 000000000..679181158 --- /dev/null +++ b/adaptors/emscripten/wrappers/animation-wrapper.h @@ -0,0 +1,137 @@ +#ifndef __DALI_ANIMATION_WRAPPER_H__ +#define __DALI_ANIMATION_WRAPPER_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include +#include "emscripten/emscripten.h" +#include "emscripten/bind.h" + +// INTERNAL INCLUDES +#include "handle-wrapper.h" + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +/** + * Keyframe animation helper functions for Emscripten. + * + * Allows a string alphaFunction + * + * @param[in] keyFrames The dali keyframes + * @param[in] progress The keyframe progress + * @param[in] value The property value + * @param[in] alphaFunction The alpha function by name + * + */ +void KeyFramesAddWithAlpha(Dali::KeyFrames& keyFrames, float progress, Property::Value& value, + const std::string& alphaFunction); + + +/** + * Animation helper functions for Emscripten. + * + * Allows string property names without function overloading which is unavailable with Javascript + * + * @param[in] self The animation + * @param[in] handle The handle to animate + * @param[in] property The property name to animate + * @param[in] alphaFunction The alpha function + * @param[in] delay The animation delay + * @param[in] duration The animation duration + * + */ +void AnimateTo(Dali::Animation& self, + Dali::Handle& handle, + const std::string& property, + const Dali::Property::Value& destinationValue, + const Dali::AlphaFunction::BuiltinFunction& alphaFunction, + const float delay, + const float duration); + +/** + * AnimateBy helper + * + * Allows string property names without function overloading which is unavailable with Javascript + * + * @param[in] self The animation + * @param[in] handle The handle to animate + * @param[in] property The property name to animate + * @param[in] alphaFunction The alpha function + * @param[in] delay The animation delay + * @param[in] duration The animation duration + */ +void AnimateBy(Dali::Animation& self, + Dali::Handle& handle, + const std::string& property, + const Dali::Property::Value& destinationValue, + const Dali::AlphaFunction::BuiltinFunction& alphaFunction, + const float delay, + const float duration); + +/** + * AnimateBetween helper + * + * Allows string property names without function overloading which is unavailable with Javascript + * + * @param[in] self The animation + * @param[in] handle The handle to animate + * @param[in] property The property name to animate + * @param[in] alphaFunction The alpha function + * @param[in] delay The animation delay + * @param[in] duration The animation duration + */ +void AnimateBetween(Dali::Animation& self, + Dali::Handle& handle, + const std::string& property, + Dali::KeyFrames& keyFrames, + const Dali::AlphaFunction::BuiltinFunction& alphaFunction, + const float delay, + const float duration, + const Dali::Animation::Interpolation& interpolation); + +/** + * AnimatePath helper (see AnimateTo docs above) + * + * Allows string property names without function overloading which is unavailable with Javascript + * + * @param[in] self The animation + * @param[in] target The handle to animate + * @param[in] path The dali Path + * @param[in] forward The path animation forward vector + * @param[in] delay The animation delay + * @param[in] duration The animation duration + */ +void AnimatePath(Dali::Animation& self, + Dali::Handle& target, + const Dali::Path& path, + const Dali::Vector3& forward, + const Dali::AlphaFunction::BuiltinFunction& alphaFunction, + const float delay, + const float duration); + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali + +#endif // header diff --git a/adaptors/emscripten/wrappers/dali-wrapper.cpp b/adaptors/emscripten/wrappers/dali-wrapper.cpp new file mode 100644 index 000000000..2cfd05c2d --- /dev/null +++ b/adaptors/emscripten/wrappers/dali-wrapper.cpp @@ -0,0 +1,1279 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +// EXTERNAL INCLUDES +#include + +#include +#include +#include +#include +#include +#include +#include +#include "emscripten/emscripten.h" +#include "emscripten/bind.h" +#include "emscripten/val.h" + +// INTERNAL INCLUDES +#include "platform-abstractions/emscripten/emscripten-callbacks.h" +#include "platform-abstractions/emscripten/emscripten-platform-abstraction.h" +#include "sdl-application.h" +#include "actor-wrapper.h" +#include "animation-wrapper.h" +#include "emscripten-utils.h" +#include "handle-wrapper.h" +#include "image-wrapper.h" +#include "property-buffer-wrapper.h" +#include "property-value-wrapper.h" +#include "render-task-wrapper.h" +#include "shader-effect-wrapper.h" +#include "signal-holder.h" +#include "type-info-wrapper.h" + +//////////////////////////////////////////////////////////////////////////////// +// +// The browser javascript wrapper consists of this cpp file and a counterpart +// javascript file. +// +// They work in tandem to help make the interface natural and convenient +// for a Javascript programmer. +// +// Unfortunately there is no finalize/destruction in javascript so any cpp +// objects created must be explicitly deleted. Sometimes this wrapper can +// hide the details and simple types can be immediately marshalled to and +// from javascript values. More often objects created must have +// object.delete() called at the correct time. +// +//////////////////////////////////////////////////////////////////////////////// + +extern void EmscriptenMouseEvent(double x, double y, int mouseIsDown); +extern void EmscriptenUpdateOnce(); +extern void EmscriptenRenderOnce(); + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +// Javascript callbacks. These are set in the Javascript wrapper source and called +// from the c++ wrapper source +extern emscripten::val JSGetGlyphImage; +extern emscripten::val JSGetImage; +extern emscripten::val JSGetImageMetaData; +extern emscripten::val JSRenderFinished; + +using namespace emscripten; + +/** + * Dali Vector access for Emscripten wrapping of Dali::Vector + */ +template +struct DaliVectorAccess +{ + static emscripten::val Get( const DaliVectorType& v, typename DaliVectorType::SizeType index ) + { + if (index < v.Size()) + { + return emscripten::val(v[index]); + } + else + { + return emscripten::val::undefined(); + } + } + + static bool Set( DaliVectorType& v, typename DaliVectorType::SizeType index, const typename DaliVectorType::ItemType& value) + { + v[index] = value; + return true; + } + + static size_t Size( DaliVectorType& v) + { + return v.Size(); + } +}; + +/** + * Registering a Dali Vector with Emscripten to wrap for JavaScript + */ +template +class_> register_dali_vector(const char* name) +{ + typedef Dali::Vector DaliVecType; + + void (DaliVecType::*PushBack)(const T&) = &DaliVecType::PushBack; + void (DaliVecType::*Resize)(const size_t, const T&) = &DaliVecType::Resize; + return class_(name) + .template constructor<>() + .function("push_back", PushBack) + .function("resize", Resize) + .function("size", &DaliVectorAccess::Size) + .function("get", &DaliVectorAccess::Get) + .function("set", &DaliVectorAccess::Set) +; +}; + + +namespace +{ + +std::string VersionString() +{ + std::stringstream versionString; + versionString << "DALi Core: " << Dali::CORE_MAJOR_VERSION << "." << Dali::CORE_MINOR_VERSION << "." << Dali::CORE_MICRO_VERSION << " (" << Dali::CORE_BUILD_DATE << ")" << std::endl; + return versionString.str(); +} + + +/** + * String conversions for C++ types. For PropertyValueToString() conversions. + */ +template +std::string ToString(const T& value) +{ + std::stringstream ss; + ss << value; + return ss.str(); +} + +/** + * Dali Rect to string template specialization + */ +template <> +std::string ToString(const Dali::Rect& value) +{ + std::stringstream ss; + ss << value.x << "," << value.y << "," << value.width << "," << value.height; + return ss.str(); +} + +/** + * Converts Dali Property Value to string + */ +std::string PropertyValueToString( const Dali::Property::Value& value ) +{ + std::string ret; + + switch( value.GetType() ) + { + case Dali::Property::NONE: + { + ret = "NONE"; + break; + } ///< No type + case Dali::Property::BOOLEAN: + { + ret = value.Get() ? "True" : "False"; + break; + } + case Dali::Property::FLOAT: + { + ret = ToString( value.Get() ); + break; + } + case Dali::Property::INTEGER: + { + ret = ToString( value.Get() ); + break; + } + case Dali::Property::VECTOR2: + { + ret = ToString( value.Get() ); + break; + } + case Dali::Property::VECTOR3: + { + ret = ToString( value.Get() ); + break; + } + case Dali::Property::VECTOR4: + { + ret = ToString( value.Get() ); + break; + } + case Dali::Property::MATRIX3: + { + ret = ToString( value.Get() ); + break; + } + case Dali::Property::MATRIX: + { + ret = ToString( value.Get() ); + break; + } + case Dali::Property::RECTANGLE: + { + ret = ToString( value.Get< Dali::Rect >() ); + break; + } + case Dali::Property::ROTATION: + { + break; + } + case Dali::Property::STRING: + { + ret = value.Get(); + break; + } + case Dali::Property::ARRAY: + { + Dali::Property::Array *array = value.GetArray(); + DALI_ASSERT_ALWAYS(array); + ret = std::string("Array Size=") + ToString( array->Count() ); + break; + } + case Dali::Property::MAP: + { + Dali::Property::Map *map = value.GetMap(); + DALI_ASSERT_ALWAYS(map); + ret = std::string("Map Size=") + ToString( map->Count() ); + break; + } + } + + return ret; +} + +/** + * Creates an Actor previously registered with the TypeRegistry by name + * Actors are currently differentiated in the dali-wrapper.js and have accessor functions + * to support 'property.name=' access in the Javascript side object + */ +Dali::Actor CreateActor(const std::string& name) +{ + Dali::Actor ret; + + Dali::TypeRegistry registry = Dali::TypeRegistry::Get(); + + Dali::TypeInfo typeInfo = registry.GetTypeInfo( name ); + + if(!typeInfo) + { + EM_ASM( throw "Invalid type name" ); + } + else + { + Dali::BaseHandle handle = typeInfo.CreateInstance(); + + if(!handle) + { + EM_ASM( throw "Invalid handle. Cannot downcast (not an actor)" ); + } + + if( Dali::Actor actor = Dali::Actor::DownCast(handle) ) + { + ret = actor; + } + } + + return ret; +} + +/** + * Creates any Handle from the TypeRegistry by name + */ +Dali::Handle CreateHandle(const std::string& name) +{ + Dali::Handle ret; + + Dali::TypeRegistry registry = Dali::TypeRegistry::Get(); + + Dali::TypeInfo typeInfo = registry.GetTypeInfo( name ); + + if(!typeInfo) + { + EM_ASM( throw "Invalid type name" ); + } + else + { + Dali::BaseHandle base = typeInfo.CreateInstance(); + if(!base) + { + EM_ASM( throw "Cannot create instance (not a handle)" ); + } + Dali::Handle handle = Dali::Handle::DownCast(base); + + if(!handle) + { + EM_ASM( throw "Invalid handle. Cannot downcast" ); + } + + ret = handle; + } + + return ret; +} + +/** + * The functor to be used in the hit-test algorithm to check whether the actor is hittable. + */ +bool IsActorHittableFunction(Dali::Actor actor, Dali::HitTestAlgorithm::TraverseType type) +{ + const std::string& name = actor.GetName(); + // by convention if not visible, or with a * starting the name or if the root layer + return actor.IsVisible() && (name.size() ? name[0] != '*' : true); +}; + +/* + * Hit Test wrapper + * + */ +Dali::Actor HitTest(float x, float y) +{ + Dali::HitTestAlgorithm::Results results; + Dali::HitTestAlgorithm::HitTest( Dali::Stage::GetCurrent(), Dali::Vector2(x,y), results, IsActorHittableFunction ); + return results.actor; +} + +/** + * Creates a solid colour actor + */ +Dali::Actor CreateSolidColorActor( const Dali::Vector4& color, bool border, const Dali::Vector4& borderColor, const unsigned int borderSize ) +{ + static const unsigned int MAX_BORDER_SIZE( 9 ); + + Dali::ImageActor image; + if( borderSize > MAX_BORDER_SIZE ) + { + return image; + } + + const unsigned int bitmapWidth = borderSize * 2 + 2; + + // Using a (2 + border) x (2 + border) image gives a better blend with the GL implementation + // than a (1 + border) x (1 + border) image + const unsigned int bitmapSize = bitmapWidth * bitmapWidth; + const unsigned int topLeft = bitmapWidth * borderSize + borderSize; + const unsigned int topRight = topLeft + 1; + const unsigned int bottomLeft = bitmapWidth * (borderSize + 1) + borderSize; + const unsigned int bottomRight = bottomLeft + 1; + + Dali::BufferImage imageData; + if(color.a != 1.0 || borderColor.a != 1.0) + { + imageData = Dali::BufferImage::New( bitmapWidth, bitmapWidth, Dali::Pixel::RGBA8888 ); + + // Create the image + Dali::PixelBuffer* pixbuf = imageData.GetBuffer(); + Dali::Vector4 outerColor = color; + if ( border ) + { + outerColor = borderColor; + } + + for( size_t i = 0; i < bitmapSize; ++i ) + { + if( i == topLeft || + i == topRight || + i == bottomLeft || + i == bottomRight ) + { + pixbuf[i*4+0] = 0xFF * color.r; + pixbuf[i*4+1] = 0xFF * color.g; + pixbuf[i*4+2] = 0xFF * color.b; + pixbuf[i*4+3] = 0xFF * color.a; + } + else + { + pixbuf[i*4+0] = 0xFF * outerColor.r; + pixbuf[i*4+1] = 0xFF * outerColor.g; + pixbuf[i*4+2] = 0xFF * outerColor.b; + pixbuf[i*4+3] = 0xFF * outerColor.a; + } + } + } + else + { + imageData = Dali::BufferImage::New( bitmapWidth, bitmapWidth, Dali::Pixel::RGB888 ); + + // Create the image + Dali::PixelBuffer* pixbuf = imageData.GetBuffer(); + Dali::Vector4 outerColor = color; + if ( border ) + { + outerColor = borderColor; + } + + for( size_t i = 0; i < bitmapSize; ++i ) + { + if( i == topLeft || + i == topRight || + i == bottomLeft || + i == bottomRight ) + { + pixbuf[i*3+0] = 0xFF * color.r; + pixbuf[i*3+1] = 0xFF * color.g; + pixbuf[i*3+2] = 0xFF * color.b; + } + else + { + pixbuf[i*3+0] = 0xFF * outerColor.r; + pixbuf[i*3+1] = 0xFF * outerColor.g; + pixbuf[i*3+2] = 0xFF * outerColor.b; + } + } + } + + imageData.Update(); + image = Dali::ImageActor::New( imageData ); + image.SetAnchorPoint( Dali::AnchorPoint::CENTER ); + image.SetParentOrigin( Dali::ParentOrigin::CENTER ); + + // if( border ) + // { + // image.SetStyle( Dali::ImageActor::STYLE_NINE_PATCH ); + // image.SetNinePatchBorder( Dali::Vector4::ONE * (float)borderSize * 2.0f ); + // } + + return image; +} + +} // anon namespace + +/** + * Sets the callback to getting a glyph (from dali-wrapper.js/browser) + */ +void SetCallbackGetGlyphImage(const emscripten::val& callback) +{ + JSGetGlyphImage = callback; +} + +/** + * Sets the callback to get an image (from dali-wrapper.js/browser) + */ +void SetCallbackGetImage(const emscripten::val& callback) +{ + JSGetImage = callback; +} + +/** + * Sets the callback to get image meta data (from dali-wrapper.js/browser) + */ +void SetCallbackGetImageMetadata(const emscripten::val& callback) +{ + JSGetImageMetaData = callback; +} + +/** + * Sets the callback to signal render finished to dali-wrapper.js/browser + */ +void SetCallbackRenderFinished(const emscripten::val& callback) +{ + JSRenderFinished = callback; +} + +/** + * Generates control points for a Path + */ +void GenerateControlPoints(Dali::Handle& handle, float curvature) +{ + if( handle ) + { + Dali::Path path = Dali::Path::DownCast(handle); + if(path) + { + path.GenerateControlPoints(curvature); + } + else + { + EM_ASM( throw "Handle is not a path object" ); + } + } + else + { + EM_ASM( throw "Handle is empty" ); + } + +} + +/** + * Creating property Value Objects + * javascript can't select on type so we have renamed constructors + * + * Emscripten can convert some basic types and ones we've declared as value_array(s) + * (These are given member access offsets/functions and are for simple structs etc) + * + * The composite types need converting. + */ +Dali::Property::Value PropertyValueBoolean(bool v) +{ + return Dali::Property::Value(v); +} + +Dali::Property::Value PropertyValueFloat(float v) +{ + return Dali::Property::Value(v); +} + +Dali::Property::Value PropertyValueInteger(int v) +{ + return Dali::Property::Value(v); +} + +Dali::Property::Value PropertyValueVector2( const Dali::Vector2& v ) +{ + return Dali::Property::Value(v); +} + +Dali::Property::Value PropertyValueVector3( const Dali::Vector3& v ) +{ + return Dali::Property::Value(v); +} + +Dali::Property::Value PropertyValueVector4( const Dali::Vector4& v ) +{ + return Dali::Property::Value(v); +} + +Dali::Property::Value PropertyValueIntRect( int a, int b, int c, int d ) +{ + return Dali::Property::Value(Dali::Rect( a, b, c, d)); +} + +Dali::Property::Value PropertyValueMatrix( const Dali::Matrix& v ) +{ + return Dali::Property::Value(v); +} + +Dali::Property::Value PropertyValueMatrix3( const Dali::Matrix3& v ) +{ + return Dali::Property::Value(v); +} + +Dali::Property::Value PropertyValueEuler( const Dali::Vector3& v ) +{ + return Dali::Property::Value( Dali::Quaternion( + Dali::Radian(Dali::Degree(v.x)), + Dali::Radian(Dali::Degree(v.y)), + Dali::Radian(Dali::Degree(v.z)) ) ); +} + +Dali::Property::Value PropertyValueAxisAngle( const Dali::Vector4& v ) +{ + return Dali::Quaternion( Dali::Radian(Dali::Degree(v[3])), Dali::Vector3(v) ); +} + +Dali::Property::Value PropertyValueString( const std::string& v ) +{ + return Dali::Property::Value(v); +} + +Dali::Property::Value PropertyValueContainer( const emscripten::val& v ) +{ + Dali::Property::Value ret; + RecursiveSetProperty( ret, v ); + return ret; +} + +/** + * Property value accessors from Dali Property Value + */ +bool PropertyGetBoolean(const Dali::Property::Value& v) +{ + return v.Get(); +} + +float PropertyGetFloat(const Dali::Property::Value& v) +{ + return v.Get(); +} + +int PropertyGetInteger(const Dali::Property::Value& v) +{ + return v.Get(); +} + +Dali::Vector2 PropertyGetVector2(const Dali::Property::Value& v) +{ + return v.Get(); +} + +Dali::Vector3 PropertyGetVector3(const Dali::Property::Value& v) +{ + return v.Get(); +} + +Dali::Vector4 PropertyGetVector4(const Dali::Property::Value& v) +{ + return v.Get(); +} + +emscripten::val PropertyGetIntRect(const Dali::Property::Value& v) +{ + return JavascriptValue(v); +} + +std::string PropertyGetString(const Dali::Property::Value& v) +{ + return v.Get(); +} + +emscripten::val PropertyGetMap(const Dali::Property::Value& v) +{ + return JavascriptValue(v); +} + +emscripten::val PropertyGetArray(const Dali::Property::Value& v) +{ + return JavascriptValue(v); +} + +emscripten::val PropertyGetMatrix(const Dali::Property::Value& v) +{ + return JavascriptValue(v); +} + +emscripten::val PropertyGetMatrix3(const Dali::Property::Value& v) +{ + return JavascriptValue(v); +} + +emscripten::val PropertyGetEuler(const Dali::Property::Value& v) +{ + return JavascriptValue(v); +} + +emscripten::val PropertyGetRotation(const Dali::Property::Value& v) +{ + return JavascriptValue(v); +} + +int PropertyGetType(Dali::Property::Value& v) +{ + return (int)v.GetType(); +} + +std::string PropertyGetTypeName(Dali::Property::Value& v) +{ + return Dali::PropertyTypes::GetName(v.GetType()); +} + +template +float MatrixGetter(T &v, int n) +{ + return *(v.AsFloat() + n); +} + +template +void MatrixSetter(T &v, float f, int n) +{ + *(v.AsFloat() + n) = f; +} + +float MatrixGetter0(const Dali::Matrix &v) { return MatrixGetter(v, 0); } +float MatrixGetter1(const Dali::Matrix &v) { return MatrixGetter(v, 1); } +float MatrixGetter2(const Dali::Matrix &v) { return MatrixGetter(v, 2); } +float MatrixGetter3(const Dali::Matrix &v) { return MatrixGetter(v, 3); } +float MatrixGetter4(const Dali::Matrix &v) { return MatrixGetter(v, 4); } +float MatrixGetter5(const Dali::Matrix &v) { return MatrixGetter(v, 5); } +float MatrixGetter6(const Dali::Matrix &v) { return MatrixGetter(v, 6); } +float MatrixGetter7(const Dali::Matrix &v) { return MatrixGetter(v, 7); } +float MatrixGetter8(const Dali::Matrix &v) { return MatrixGetter(v, 8); } +float MatrixGetter9(const Dali::Matrix &v) { return MatrixGetter(v, 9); } +float MatrixGetter10(const Dali::Matrix &v) { return MatrixGetter(v,10); } +float MatrixGetter11(const Dali::Matrix &v) { return MatrixGetter(v,11); } +float MatrixGetter12(const Dali::Matrix &v) { return MatrixGetter(v,12); } +float MatrixGetter13(const Dali::Matrix &v) { return MatrixGetter(v,13); } +float MatrixGetter14(const Dali::Matrix &v) { return MatrixGetter(v,14); } +float MatrixGetter15(const Dali::Matrix &v) { return MatrixGetter(v,15); } +float MatrixGetter16(const Dali::Matrix &v) { return MatrixGetter(v,16); } + +void MatrixSetter0(Dali::Matrix &v, float f) { MatrixSetter(v, f, 0); } +void MatrixSetter1(Dali::Matrix &v, float f) { MatrixSetter(v, f, 1); } +void MatrixSetter2(Dali::Matrix &v, float f) { MatrixSetter(v, f, 2); } +void MatrixSetter3(Dali::Matrix &v, float f) { MatrixSetter(v, f, 3); } +void MatrixSetter4(Dali::Matrix &v, float f) { MatrixSetter(v, f, 4); } +void MatrixSetter5(Dali::Matrix &v, float f) { MatrixSetter(v, f, 5); } +void MatrixSetter6(Dali::Matrix &v, float f) { MatrixSetter(v, f, 6); } +void MatrixSetter7(Dali::Matrix &v, float f) { MatrixSetter(v, f, 7); } +void MatrixSetter8(Dali::Matrix &v, float f) { MatrixSetter(v, f, 8); } +void MatrixSetter9(Dali::Matrix &v, float f) { MatrixSetter(v, f, 9); } +void MatrixSetter10(Dali::Matrix &v, float f) { MatrixSetter(v, f,10); } +void MatrixSetter11(Dali::Matrix &v, float f) { MatrixSetter(v, f,11); } +void MatrixSetter12(Dali::Matrix &v, float f) { MatrixSetter(v, f,12); } +void MatrixSetter13(Dali::Matrix &v, float f) { MatrixSetter(v, f,13); } +void MatrixSetter14(Dali::Matrix &v, float f) { MatrixSetter(v, f,14); } +void MatrixSetter15(Dali::Matrix &v, float f) { MatrixSetter(v, f,15); } +void MatrixSetter16(Dali::Matrix &v, float f) { MatrixSetter(v, f,16); } + +float Matrix3Getter0(const Dali::Matrix3 &v) { return MatrixGetter(v, 0); } +float Matrix3Getter1(const Dali::Matrix3 &v) { return MatrixGetter(v, 1); } +float Matrix3Getter2(const Dali::Matrix3 &v) { return MatrixGetter(v, 2); } +float Matrix3Getter3(const Dali::Matrix3 &v) { return MatrixGetter(v, 3); } +float Matrix3Getter4(const Dali::Matrix3 &v) { return MatrixGetter(v, 4); } +float Matrix3Getter5(const Dali::Matrix3 &v) { return MatrixGetter(v, 5); } +float Matrix3Getter6(const Dali::Matrix3 &v) { return MatrixGetter(v, 6); } +float Matrix3Getter7(const Dali::Matrix3 &v) { return MatrixGetter(v, 7); } +float Matrix3Getter8(const Dali::Matrix3 &v) { return MatrixGetter(v, 8); } + +void Matrix3Setter0(Dali::Matrix3 &v, float f) { MatrixSetter(v, f, 0); } +void Matrix3Setter1(Dali::Matrix3 &v, float f) { MatrixSetter(v, f, 1); } +void Matrix3Setter2(Dali::Matrix3 &v, float f) { MatrixSetter(v, f, 2); } +void Matrix3Setter3(Dali::Matrix3 &v, float f) { MatrixSetter(v, f, 3); } +void Matrix3Setter4(Dali::Matrix3 &v, float f) { MatrixSetter(v, f, 4); } +void Matrix3Setter5(Dali::Matrix3 &v, float f) { MatrixSetter(v, f, 5); } +void Matrix3Setter6(Dali::Matrix3 &v, float f) { MatrixSetter(v, f, 6); } +void Matrix3Setter7(Dali::Matrix3 &v, float f) { MatrixSetter(v, f, 7); } +void Matrix3Setter8(Dali::Matrix3 &v, float f) { MatrixSetter(v, f, 8); } + +val JavascriptUpdateCallback( val::undefined() ); +bool JavascriptUpdateCallbackSet = false; + +void JavascriptUpdate(int dt) +{ + if( JavascriptUpdateCallbackSet ) + { + JavascriptUpdateCallback( val(dt) ); + } +} + +void SetUpdateFunction( const emscripten::val& function ) +{ + JavascriptUpdateCallback = function; + JavascriptUpdateCallbackSet = true; +} + +const emscripten::val& GetUpdateFunction() +{ + return JavascriptUpdateCallback; +} + +/** + * Emscripten Bindings + * + * This uses Emscripten 'bind' (similar in design to Boost's python wrapper library). + * It provides a simplified way to wrap C++ classes and wraps Emscriptens type + * registration API. + * + * A convention below is that where there is a function or method name prepended + * with '__' there is a corresponding Javascript helper function to help with + * marshalling parameters and return values. + * + */ +EMSCRIPTEN_BINDINGS(dali_wrapper) +{ + // With a little help embind knows about vectors so we tell it which ones to instantiate + register_dali_vector("DaliVectorInt"); + register_vector("VectorString"); + register_vector("VectorInt"); + register_vector("VectorFloat"); + register_vector("VectorActor"); + + // + // Creation functions. + // + emscripten::function("VersionString", &VersionString); + emscripten::function("__createActor", &CreateActor); + emscripten::function("__createHandle", &CreateHandle); + emscripten::function("__createSolidColorActor", &CreateSolidColorActor); + + // + // Helper functions + // + emscripten::function("javascriptValue", &JavascriptValue); + emscripten::function("__hitTest", &HitTest); + emscripten::function("sendMouseEvent", &EmscriptenMouseEvent); + emscripten::function("__updateOnce", &EmscriptenUpdateOnce); + emscripten::function("__renderOnce", &EmscriptenRenderOnce); + emscripten::function("generateControlPoints", &GenerateControlPoints); + + // + // Global callback functions + // + emscripten::function("setCallbackGetGlyphImage", &SetCallbackGetGlyphImage); + emscripten::function("setCallbackGetImage", &SetCallbackGetImage); + emscripten::function("setCallbackGetImageMetadata", &SetCallbackGetImageMetadata); + emscripten::function("setCallbackRenderFinished", &SetCallbackRenderFinished); + emscripten::function("setUpdateFunction", &SetUpdateFunction); + emscripten::function("getUpdateFunction", &GetUpdateFunction); + + // + // Property value creation (Javascript can't select on type) + // + emscripten::function("PropertyValueBoolean", &PropertyValueBoolean); + emscripten::function("PropertyValueFloat", &PropertyValueFloat); + emscripten::function("PropertyValueInteger", &PropertyValueInteger); + emscripten::function("PropertyValueString", &PropertyValueString); + emscripten::function("PropertyValueVector2", &PropertyValueVector2); + emscripten::function("PropertyValueVector3", &PropertyValueVector3); + emscripten::function("PropertyValueVector4", &PropertyValueVector4); + emscripten::function("PropertyValueMatrix", &PropertyValueMatrix); + emscripten::function("PropertyValueMatrix3", &PropertyValueMatrix3); + emscripten::function("PropertyValueMap", &PropertyValueContainer); + emscripten::function("PropertyValueArray", &PropertyValueContainer); + emscripten::function("PropertyValueEuler", &PropertyValueEuler); + emscripten::function("PropertyValueAxisAngle", &PropertyValueAxisAngle); + emscripten::function("PropertyValueString", &PropertyValueString); + emscripten::function("PropertyValueIntRect", &PropertyValueIntRect); + + // + // One unfortunate aspect of wrapping for the browser is that we get no notification + // of object deletion so all JS wrapper objects must be wrapper.delete() correctly. + // + // Embind has a mechanism around this for simple c style structs decared as value_arrays. + // These are immediately transformed to Javascript arrays and don't need explicit + // destruction, however the API needs a member offset. + // + value_array("Statistics") + .element(&Dali::Internal::Emscripten::Statistics::on) + .element(&Dali::Internal::Emscripten::Statistics::frameCount) + .element(&Dali::Internal::Emscripten::Statistics::lastFrameDeltaSeconds) + .element(&Dali::Internal::Emscripten::Statistics::lastSyncTimeMilliseconds) + .element(&Dali::Internal::Emscripten::Statistics::nextSyncTimeMilliseconds) + .element(&Dali::Internal::Emscripten::Statistics::keepUpdating) + .element(&Dali::Internal::Emscripten::Statistics::needsNotification) + .element(&Dali::Internal::Emscripten::Statistics::secondsFromLastFrame) +; + + value_array("Vector2") + .element(&Dali::Vector2::x) + .element(&Dali::Vector2::y) +; + + value_array("Vector3") + .element(&Dali::Vector3::x) + .element(&Dali::Vector3::y) + .element(&Dali::Vector3::z) +; + + value_array("Vector4") + .element(&Dali::Vector4::x) + .element(&Dali::Vector4::y) + .element(&Dali::Vector4::z) + .element(&Dali::Vector4::w) +; + + value_array("Matrix") + .element(&MatrixGetter0, &MatrixSetter0) + .element(&MatrixGetter1, &MatrixSetter1) + .element(&MatrixGetter2, &MatrixSetter2) + .element(&MatrixGetter3, &MatrixSetter3) + .element(&MatrixGetter4, &MatrixSetter4) + .element(&MatrixGetter5, &MatrixSetter5) + .element(&MatrixGetter6, &MatrixSetter6) + .element(&MatrixGetter7, &MatrixSetter7) + .element(&MatrixGetter8, &MatrixSetter8) + .element(&MatrixGetter9, &MatrixSetter9) + .element(&MatrixGetter10, &MatrixSetter10) + .element(&MatrixGetter11, &MatrixSetter11) + .element(&MatrixGetter12, &MatrixSetter12) + .element(&MatrixGetter13, &MatrixSetter13) + .element(&MatrixGetter14, &MatrixSetter14) + .element(&MatrixGetter15, &MatrixSetter15) +; + + value_array("Matrix3") + .element(&Matrix3Getter0, &Matrix3Setter0) + .element(&Matrix3Getter1, &Matrix3Setter1) + .element(&Matrix3Getter2, &Matrix3Setter2) + .element(&Matrix3Getter3, &Matrix3Setter3) + .element(&Matrix3Getter4, &Matrix3Setter4) + .element(&Matrix3Getter5, &Matrix3Setter5) + .element(&Matrix3Getter6, &Matrix3Setter6) + .element(&Matrix3Getter7, &Matrix3Setter7) + .element(&Matrix3Getter8, &Matrix3Setter8) +; + + // + // enums + // + enum_("PropertyType") + .value("NONE", Dali::Property::NONE) + .value("BOOLEAN", Dali::Property::BOOLEAN) + .value("FLOAT", Dali::Property::FLOAT) + .value("INTEGER", Dali::Property::INTEGER) + .value("VECTOR2", Dali::Property::VECTOR2) + .value("VECTOR3", Dali::Property::VECTOR3) + .value("VECTOR4", Dali::Property::VECTOR4) + .value("MATRIX3", Dali::Property::MATRIX3) + .value("MATRIX", Dali::Property::MATRIX) + .value("RECTANGLE", Dali::Property::RECTANGLE) + .value("ROTATION", Dali::Property::ROTATION) + .value("STRING", Dali::Property::STRING) + .value("ARRAY", Dali::Property::ARRAY) + .value("MAP", Dali::Property::MAP) +; + + enum_("GeometryHints") + .value("HINT_NONE", Dali::ShaderEffect::HINT_NONE) + .value("HINT_GRID_X", Dali::ShaderEffect::HINT_GRID_X) + .value("HINT_GRID_Y", Dali::ShaderEffect::HINT_GRID_Y) + .value("HINT_GRID", Dali::ShaderEffect::HINT_GRID) + .value("HINT_DEPTH_BUFFER", Dali::ShaderEffect::HINT_DEPTH_BUFFER) + .value("HINT_BLENDING", Dali::ShaderEffect::HINT_BLENDING) + .value("HINT_DOESNT_MODIFY_GEOMETRY", Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY) +; + + enum_("ShaderHints") + .value("HINT_NONE", Dali::Shader::HINT_NONE) + .value("HINT_REQUIRES_SELF_DEPTH_TEST", Dali::Shader::HINT_REQUIRES_SELF_DEPTH_TEST) + .value("HINT_OUTPUT_IS_TRANSPARENT", Dali::Shader::HINT_OUTPUT_IS_TRANSPARENT) + .value("HINT_OUTPUT_IS_OPAQUE", Dali::Shader::HINT_OUTPUT_IS_OPAQUE) + .value("HINT_MODIFIES_GEOMETRY", Dali::Shader::HINT_MODIFIES_GEOMETRY) +; + + enum_("EndAction") + .value("Bake", Dali::Animation::Bake) + .value("Discard", Dali::Animation::Discard) + .value("BakeFinal", Dali::Animation::BakeFinal) +; + + enum_("Interpolation") + .value("Linear", Dali::Animation::Interpolation::Linear) + .value("Cubic", Dali::Animation::Interpolation::Cubic) +; + + enum_("GeometryType") + .value("POINTS", Dali::Geometry::POINTS) + .value("LINES", Dali::Geometry::LINES) + .value("LINE_LOOP", Dali::Geometry::LINE_LOOP) + .value("LINE_STRIP", Dali::Geometry::LINE_STRIP) + .value("TRIANGLES", Dali::Geometry::TRIANGLES) + .value("TRIANGLE_FAN", Dali::Geometry::TRIANGLE_FAN) + .value("TRIANGLE_STRIP", Dali::Geometry::TRIANGLE_STRIP) +; + + enum_("ReleasePolicy") + .value("UNUSED", Dali::Image::UNUSED) + .value("NEVER", Dali::Image::NEVER) +; + + enum_("PixelFormat") + .value("A8", Dali::Pixel::Format::A8) + .value("L8", Dali::Pixel::Format::L8) + .value("LA88", Dali::Pixel::Format::LA88) + .value("RGB565", Dali::Pixel::Format::RGB565) + .value("BGR565", Dali::Pixel::Format::BGR565) + .value("RGBA4444", Dali::Pixel::Format::RGBA4444) + .value("BGRA4444", Dali::Pixel::Format::BGRA4444) + .value("RGBA5551", Dali::Pixel::Format::RGBA5551) + .value("BGRA5551", Dali::Pixel::Format::BGRA5551) + .value("RGB888", Dali::Pixel::Format::RGB888) + .value("RGB8888", Dali::Pixel::Format::RGB8888) + .value("BGR8888", Dali::Pixel::Format::BGR8888) + .value("RGBA8888", Dali::Pixel::Format::RGBA8888) + .value("BGRA8888", Dali::Pixel::Format::BGRA8888) + // GLES 3 Standard compressed formats: + .value("COMPRESSED_R11_EAC", Dali::Pixel::Format::COMPRESSED_R11_EAC) + .value("COMPRESSED_SIGNED_R11_EAC", Dali::Pixel::Format::COMPRESSED_SIGNED_R11_EAC) + .value("COMPRESSED_RG11_EAC", Dali::Pixel::Format::COMPRESSED_RG11_EAC) + .value("COMPRESSED_SIGNED_RG11_EAC", Dali::Pixel::Format::COMPRESSED_SIGNED_RG11_EAC) + .value("COMPRESSED_RGB8_ETC2", Dali::Pixel::Format::COMPRESSED_RGB8_ETC2) + .value("COMPRESSED_SRGB8_ETC2", Dali::Pixel::Format::COMPRESSED_SRGB8_ETC2) + .value("COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2", Dali::Pixel::Format::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2) + .value("COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2", Dali::Pixel::Format::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2) + .value("COMPRESSED_RGBA8_ETC2_EAC", Dali::Pixel::Format::COMPRESSED_RGBA8_ETC2_EAC) + .value("COMPRESSED_SRGB8_ALPHA8_ETC2_EAC", Dali::Pixel::Format::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC) + // GLES 2 extension compressed formats: + .value("COMPRESSED_RGB8_ETC1", Dali::Pixel::Format::COMPRESSED_RGB8_ETC1) + .value("COMPRESSED_RGB_PVRTC_4BPPV1", Dali::Pixel::Format::COMPRESSED_RGB_PVRTC_4BPPV1) +; + + enum_("FaceCullingMode") + .value("NONE", Dali::Renderer::NONE) + .value("CULL_BACK", Dali::Renderer::CULL_BACK) + .value("CULL_FRONT", Dali::Renderer::CULL_FRONT) + .value("CULL_BACK_AND_FRONT", Dali::Renderer::CULL_BACK_AND_FRONT) +; + + enum_("BlendingMode") + .value("OFF", Dali::BlendingMode::OFF) + .value("AUTO", Dali::BlendingMode::AUTO) + .value("ON", Dali::BlendingMode::ON) +; + + enum_("AlphaFunction") + .value("DEFAULT", Dali::AlphaFunction::BuiltinFunction::DEFAULT) + .value("LINEAR", Dali::AlphaFunction::BuiltinFunction::LINEAR) + .value("REVERSE", Dali::AlphaFunction::BuiltinFunction::REVERSE) + .value("EASE_IN_SQUARE", Dali::AlphaFunction::BuiltinFunction::EASE_IN_SQUARE) + .value("EASE_OUT_SQUARE", Dali::AlphaFunction::BuiltinFunction::EASE_OUT_SQUARE) + .value("EASE_IN", Dali::AlphaFunction::BuiltinFunction::EASE_IN) + .value("EASE_OUT", Dali::AlphaFunction::BuiltinFunction::EASE_OUT) + .value("EASE_IN_OUT", Dali::AlphaFunction::BuiltinFunction::EASE_IN_OUT) + .value("EASE_IN_SINE", Dali::AlphaFunction::BuiltinFunction::EASE_IN_SINE) + .value("EASE_OUT_SINE", Dali::AlphaFunction::BuiltinFunction::EASE_OUT_SINE) + .value("EASE_IN_OUT_SINE", Dali::AlphaFunction::BuiltinFunction::EASE_IN_OUT_SINE) + .value("BOUNCE", Dali::AlphaFunction::BuiltinFunction::BOUNCE) + .value("SIN", Dali::AlphaFunction::BuiltinFunction::SIN) + .value("EASE_OUT_BACK", Dali::AlphaFunction::BuiltinFunction::EASE_OUT_BACK) +; + + // + // classes + // + + // we need property map as an object rather than straight conversion to javascript 'object' + // because its ordered. And PropertyBuffer needs an order. + class_("PropertyMap") + .constructor<>() + .function("count", &Dali::Property::Map::Count) + .function("empty", &Dali::Property::Map::Empty) + .function("__insert", select_overload< void(const std::string&, const Dali::Property::Value&) > (&Dali::Property::Map::Insert)) + .function("__get", &PropertyMapGet) + .function("__getValue", &Dali::Property::Map::GetValue) + .function("getKey", &Dali::Property::Map::GetKey) + .function("clear", &Dali::Property::Map::Clear) + .function("merge", &Dali::Property::Map::Merge) +; + + class_("PropertyValue") + .constructor<>() + .function("getType", &PropertyGetType) + .function("getTypeName", &PropertyGetTypeName) + .function("getBoolean", &PropertyGetBoolean) + .function("getFloat", &PropertyGetFloat) + .function("getInteger", &PropertyGetInteger) + .function("getVector2", &PropertyGetVector2) + .function("getVector3", &PropertyGetVector3) + .function("getVector4", &PropertyGetVector4) + .function("getString", &PropertyGetString) + .function("getMap", &PropertyGetMap) + .function("getArray", &PropertyGetArray) + .function("getMatrix", &PropertyGetMatrix) + .function("getMatrix3", &PropertyGetMatrix3) + .function("getEuler", &PropertyGetEuler) + .function("getRotation", &PropertyGetRotation) + .function("getIntRect", &PropertyGetIntRect) +; + + class_("BaseHandle") + .function("ok", &BaseHandleOk) + .function("getTypeName", &BaseHandle::GetTypeName) +; + + class_>("TypeInfo") + .function("getName", &Dali::TypeInfo::GetName) + .function("getBaseName", &Dali::TypeInfo::GetBaseName) + .function("getProperties", &GetAllProperties) + .function("getActions", &GetActions) + .function("getSignals", &GetSignals) + .function("getPropertyIndices", &Dali::TypeInfo::GetPropertyIndices) +; + + class_("TypeRegistry") + .constructor<>(&Dali::TypeRegistry::Get) + .function("getTypeNameCount", &Dali::TypeRegistry::GetTypeNameCount) + .function("getTypeName", &Dali::TypeRegistry::GetTypeName) + .function("getTypeInfo", select_overload< Dali::TypeInfo(const std::string&) > (&Dali::TypeRegistry::GetTypeInfo)) +; + + class_("SignalHolder") + .constructor<>() +; + + class_>("Handle") + .function("__registerProperty", &RegisterProperty) + .function("__registerAnimatedProperty", &RegisterAnimatedProperty) + .function("setSelf", &SetSelf) + .function("setProperty", &SetProperty) + .function("getProperty", &GetProperty) + .function("getPropertyIndex", &GetPropertyIndex) + .function("getProperties", &GetProperties) + .function("getPropertyIndices", &Handle::GetPropertyIndices) + .function("getPropertyTypeFromName", &GetPropertyTypeFromName) + .function("getPropertyTypeName", &GetPropertyTypeName) + .function("registerProperty", &RegisterProperty) + .function("registerAnimatedProperty", &RegisterAnimatedProperty) + .function("getTypeInfo", &GetTypeInfo) + .function("isPropertyWritable", &Handle::IsPropertyWritable) + .function("isPropertyAnimatable", &Handle::IsPropertyAnimatable) + .function("isPropertyAConstraintInput", &Handle::IsPropertyAConstraintInput) +; + + class_>("Path") + .constructor<>(&Dali::Path::New) + .function("addPoint", &Dali::Path::AddPoint) + .function("addControlPoint", &Dali::Path::AddControlPoint) + .function("generateControlPoints", &Dali::Path::GenerateControlPoints) + .function("sample", &Dali::Path::Sample) + .function("getPoint", &Dali::Path::GetPoint) + .function("getControlPoint", &Dali::Path::GetControlPoint) + .function("getPointCount", &Dali::Path::GetPointCount) +; + + class_("KeyFrames") + .constructor<>(&Dali::KeyFrames::New) + .function("add", select_overload(&Dali::KeyFrames::Add)) + .function("addWithAlpha", &KeyFramesAddWithAlpha) +; + + class_("Animation") + .constructor(&Dali::Animation::New) + .function("__animateTo", &AnimateTo) + .function("__animateBy", &AnimateBy) + .function("__animateBetween", &AnimateBetween) + .function("__animatePath", &AnimatePath) + .function("setDuration", &Dali::Animation::SetDuration) + .function("getDuration", &Dali::Animation::GetDuration) + .function("setLooping", &Dali::Animation::SetLooping) + .function("isLooping", &Dali::Animation::IsLooping) + .function("setEndAction", &Dali::Animation::SetEndAction) + .function("getEndAction", &Dali::Animation::GetEndAction) + .function("setDisconnectAction", &Dali::Animation::SetDisconnectAction) + .function("getDisconnectAction", &Dali::Animation::GetDisconnectAction) + .function("setCurrentProgress", &Dali::Animation::SetCurrentProgress) + .function("getCurrentProgress", &Dali::Animation::GetCurrentProgress) + .function("setSpeedFactor", &Dali::Animation::SetSpeedFactor) + .function("getSpeedFactor", &Dali::Animation::GetSpeedFactor) + .function("setPlayRange", &Dali::Animation::SetPlayRange) + .function("getPlayRange", &Dali::Animation::GetPlayRange) + .function("play", &Dali::Animation::Play) + .function("playFrom", &Dali::Animation::PlayFrom) + .function("pause", &Dali::Animation::Pause) + .function("stop", &Dali::Animation::Stop) + .function("clear", &Dali::Animation::Clear) +; + + class_("PropertyBuffer") + .constructor(Dali::PropertyBuffer::New) + .function("setData", &SetPropertyBufferDataRaw) +; + + class_("Geometry") + .constructor<>(&Dali::Geometry::New) + .function("addVertexBuffer", &Dali::Geometry::AddVertexBuffer) + .function("getNumberOfVertexBuffers", &Dali::Geometry::GetNumberOfVertexBuffers) + .function("setIndexBuffer", &Dali::Geometry::SetIndexBuffer) + .function("setGeometryType", &Dali::Geometry::SetGeometryType) + .function("getGeometryType", &Dali::Geometry::GetGeometryType) + .function("setRequiresDepthTesting", &Dali::Geometry::SetRequiresDepthTesting) + .function("getRequiresDepthTesting", &Dali::Geometry::GetRequiresDepthTesting) +; + + class_("Image") +; + + class_ >("BufferImage") + .constructor(&BufferImageNew) +; + + class_ >("EncodedBufferImage") + .constructor(&EncodedBufferImageNew) +; + + class_("Sampler") + .constructor<>(&Dali::Sampler::New) +; + + class_>("Shader") + .constructor<>(&Dali::Shader::New) +; + + class_>("Material") + .constructor<>(&Dali::Material::New) + .function("setShader", &Dali::Material::SetShader) + .function("getShader", &Dali::Material::GetShader) + .function("addTexture", &Dali::Material::AddTexture) + .function("getNumberOfTextures", &Dali::Material::GetNumberOfTextures) + .function("removeTexture", &Dali::Material::RemoveTexture) +; + + class_>("Renderer") + .constructor<>(&Dali::Renderer::New) + .function("setGeometry", &Dali::Renderer::SetGeometry) + .function("getGeometry", &Dali::Renderer::GetGeometry) + .function("setMaterial", &Dali::Renderer::SetMaterial) + .function("getMaterial", &Dali::Renderer::GetMaterial) +; + + class_>("ShaderEffect") + .constructor(&CreateShaderEffect) + .function("setEffectImage", &Dali::ShaderEffect::SetEffectImage) + .function("__setUniform", &SetUniform) +; + + class_>("Actor") + .constructor<>(&Dali::Actor::New) + .function("add", &Dali::Actor::Add) + .function("remove", &Dali::Actor::Remove) + .function("getId", &Dali::Actor::GetId) + .function("__getParent", &Dali::Actor::GetParent) + .function("__findChildById", &Dali::Actor::FindChildById) + .function("__findChildByName", &Dali::Actor::FindChildByName) + .function("__getChildAt", &Dali::Actor::GetChildAt) + .function("getChildCount", &Dali::Actor::GetChildCount) + .function("__screenToLocal", + select_overload (Dali::Actor, float, float)>(&ScreenToLocal)) + .function("addressOf", &AddressOf) + .function("__connect", &ConnectSignal) + .function("__setPropertyNotification", &SetPropertyNotification) + .function("addRenderer", &Dali::Actor::AddRenderer) + .function("getRendererCount", &Dali::Actor::GetRendererCount) + .function("removeRenderer", + select_overload(&Dali::Actor::RemoveRenderer)) + .function("__getRendererAt", &Dali::Actor::GetRendererAt) +; + + class_>("CameraActor") + .constructor<>( select_overload(&Dali::CameraActor::New)) +; + + class_>("Layer") + .constructor<>(&Dali::Layer::New) + .function("raise", &Dali::Layer::Raise) + .function("lower", &Dali::Layer::Lower) +; + + class_("Stage") + .constructor<>(&Dali::Stage::GetCurrent) + .function("add", &Dali::Stage::Add) + .function("remove", &Dali::Stage::Remove) + .function("__getRootLayer", &Dali::Stage::GetRootLayer) + .function("getLayer", &Dali::Stage::GetLayer) + .function("getRenderTaskList", &Dali::Stage::GetRenderTaskList) + .function("setBackgroundColor", &Dali::Stage::SetBackgroundColor) +; + + class_("RenderTaskList") + .function("createTask", &Dali::RenderTaskList::CreateTask) + .function("removeTask", &Dali::RenderTaskList::RemoveTask) + .function("getTaskCount", &Dali::RenderTaskList::GetTaskCount) + .function("getTask", &Dali::RenderTaskList::GetTask) +; + + class_("RenderTask") + .function("__getCameraActor", &Dali::RenderTask::GetCameraActor) + .function("setCameraActor", &Dali::RenderTask::SetCameraActor) + .function("setSourceActor", &Dali::RenderTask::SetSourceActor) + .function("setExclusive", &Dali::RenderTask::SetExclusive) + .function("setInputEnabled", &Dali::RenderTask::SetInputEnabled) + .function("setViewportPosition", &Dali::RenderTask::SetViewportPosition) + .function("setViewportSize", &Dali::RenderTask::SetViewportSize) + .function("getCurrentViewportPosition", &Dali::RenderTask::GetCurrentViewportPosition) + .function("getCurrentViewportSize", &Dali::RenderTask::GetCurrentViewportSize) + .function("setClearColor", &Dali::RenderTask::SetClearColor) + .function("getClearColor", &Dali::RenderTask::GetClearColor) + .function("setClearEnabled", &Dali::RenderTask::SetClearEnabled) + .function("getClearEnabled", &Dali::RenderTask::GetClearEnabled) + .function("screenToLocal", + select_overload(&ScreenToLocal)) + .function("worldToScreen", &WorldToScreen) +; + +} + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali diff --git a/adaptors/emscripten/wrappers/dali-wrapper.js b/adaptors/emscripten/wrappers/dali-wrapper.js new file mode 100644 index 000000000..dd91d2608 --- /dev/null +++ b/adaptors/emscripten/wrappers/dali-wrapper.js @@ -0,0 +1,3498 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* eslint-env browser */ +/* eslint "brace-style": [2, "1tbs"] */ +/* eslint "no-console": 0 */ +/* eslint "no-underscore-dangle": 0 */ + +/******************************************************************************* + * + * The javascript counterpart to the C++ DALi wrapper. + * + * Provides helper functionality to make the use of the dali module. + * + * Where possible it creates a natural Javascript API. One problem + * is that wrapped C++ objects must be explicitly deleted '.delete()'. + * This is because javascript in the browser has no available hook to watch + * for an objects destruction. + * + * This file combines several 'Modules' and could be split when using a + * a recombining web build tool. + * + * + ******************************************************************************/ + +// forward refs for lint +var Module; +// On upgrading to emscripten 1.34.2 and -s EXPORT_NAME="dali" on emcc command +// line no longer seems to work so set it here. +var dali = Module; +var document; +var console; + +//------------------------------------------------------------------------------ +// +// Math Module +// +//------------------------------------------------------------------------------ + +/** + * Matrix Multiplication + * @method matrixByMatrix + * @param {Array} A Matrix4 array + * @param {Array} B Matrix4 array + * @return {Array} Matrix4 + */ +dali.matrixByMatrix = function(A, B) { + "use strict"; + + var ret = [1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ]; + + ret[0] = A[0] * B[0] + A[1] * B[4] + A[2] * B[8] + A[3] * B[12]; + ret[4] = A[4] * B[0] + A[5] * B[4] + A[6] * B[8] + A[7] * B[12]; + ret[8] = A[8] * B[0] + A[9] * B[4] + A[10] * B[8] + A[11] * B[12]; + ret[12] = A[12] * B[0] + A[13] * B[4] + A[14] * B[8] + A[15] * B[12]; + + ret[1] = A[0] * B[1] + A[1] * B[5] + A[2] * B[9] + A[3] * B[13]; + ret[5] = A[4] * B[1] + A[5] * B[5] + A[6] * B[9] + A[7] * B[13]; + ret[9] = A[8] * B[1] + A[9] * B[5] + A[10] * B[9] + A[11] * B[13]; + ret[13] = A[12] * B[1] + A[13] * B[5] + A[14] * B[9] + A[15] * B[13]; + + ret[2] = A[0] * B[2] + A[1] * B[6] + A[2] * B[10] + A[3] * B[14]; + ret[6] = A[4] * B[2] + A[5] * B[6] + A[6] * B[10] + A[7] * B[14]; + ret[10] = A[8] * B[2] + A[9] * B[6] + A[10] * B[10] + A[11] * B[14]; + ret[14] = A[12] * B[2] + A[13] * B[6] + A[14] * B[10] + A[15] * B[14]; + + ret[3] = A[0] * B[3] + A[1] * B[7] + A[2] * B[11] + A[3] * B[15]; + ret[7] = A[4] * B[3] + A[5] * B[7] + A[6] * B[11] + A[7] * B[15]; + ret[11] = A[8] * B[3] + A[9] * B[7] + A[10] * B[11] + A[11] * B[15]; + ret[15] = A[12] * B[3] + A[13] * B[7] + A[14] * B[11] + A[15] * B[15]; + + return ret; +}; + +/** + * Matrix Vector4 Multiplication + * @method matrixByVector + * @param {Array} A Matrix4 array + * @param {Array} v Vector4 + * @return {Array} Vector4 + */ +dali.matrixByVector = function(A, v) { + "use strict"; + + var x = v[0]; + var y = v[1]; + var z = v[2]; + var w = 1; + + if (v.length === 4) { + w = v[3]; + } + + return [ + A[0] * x + A[4] * y + A[8] * z + A[12] * w, + A[1] * x + A[5] * y + A[9] * z + A[13] * w, + A[2] * x + A[6] * y + A[10] * z + A[14] * w, + A[3] * x + A[7] * y + A[11] * z + A[15] * w + ]; +}; + +/** + * Get Matrix Determinant + * @method matrixDeterminant + * @param {Array} A Matrix4 array + * @return {float} Determinant + */ +dali.matrixDeterminant = function(A) { + "use strict"; + + var n11 = A[0], + n12 = A[4], + n13 = A[8], + n14 = A[12]; + var n21 = A[1], + n22 = A[5], + n23 = A[9], + n24 = A[13]; + var n31 = A[2], + n32 = A[6], + n33 = A[10], + n34 = A[14]; + var n41 = A[3], + n42 = A[7], + n43 = A[11], + n44 = A[15]; + + var m0 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44; + var m4 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44; + var m8 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44; + var m12 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; + + return n11 * m0 + n21 * m4 + n31 * m8 + n41 * m12; +}; + +/** + * Matrix Multiplication by scalar + * @method matrixByScalar + * @param {Array} A Matrix4 array + * @param {float} s float + * @return {Array} Matrix4 + */ +dali.matrixByScalar = function(A, s) { + "use strict"; + return [A[0] * s, A[1] * s, A[2] * s, A[3] * s, + A[4] * s, A[5] * s, A[6] * s, A[7] * s, + A[8] * s, A[9] * s, A[10] * s, A[11] * s, + A[12] * s, A[13] * s, A[14] * s, A[15] * s + ]; +}; + +/** + * Matrix Inverse. Raises if there is none. + * @method matrixInverse + * @param {Array} A Matrix4 array + * @return {Array} Inverse Matrix4 + */ +dali.matrixInverse = function(A) { + "use strict"; + var ret = [1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ]; + + ret[0] = A[5] * A[10] * A[15] - A[5] * A[11] * A[14] - A[9] * A[6] * A[15] + A[9] * A[7] * A[14] + A[13] * A[6] * A[11] - A[13] * A[7] * A[10]; + ret[4] = -A[4] * A[10] * A[15] + A[4] * A[11] * A[14] + A[8] * A[6] * A[15] - A[8] * A[7] * A[14] - A[12] * A[6] * A[11] + A[12] * A[7] * A[10]; + ret[8] = A[4] * A[9] * A[15] - A[4] * A[11] * A[13] - A[8] * A[5] * A[15] + A[8] * A[7] * A[13] + A[12] * A[5] * A[11] - A[12] * A[7] * A[9]; + ret[12] = -A[4] * A[9] * A[14] + A[4] * A[10] * A[13] + A[8] * A[5] * A[14] - A[8] * A[6] * A[13] - A[12] * A[5] * A[10] + A[12] * A[6] * A[9]; + + ret[1] = -A[1] * A[10] * A[15] + A[1] * A[11] * A[14] + A[9] * A[2] * A[15] - A[9] * A[3] * A[14] - A[13] * A[2] * A[11] + A[13] * A[3] * A[10]; + ret[5] = A[0] * A[10] * A[15] - A[0] * A[11] * A[14] - A[8] * A[2] * A[15] + A[8] * A[3] * A[14] + A[12] * A[2] * A[11] - A[12] * A[3] * A[10]; + ret[9] = -A[0] * A[9] * A[15] + A[0] * A[11] * A[13] + A[8] * A[1] * A[15] - A[8] * A[3] * A[13] - A[12] * A[1] * A[11] + A[12] * A[3] * A[9]; + ret[13] = A[0] * A[9] * A[14] - A[0] * A[10] * A[13] - A[8] * A[1] * A[14] + A[8] * A[2] * A[13] + A[12] * A[1] * A[10] - A[12] * A[2] * A[9]; + + ret[2] = A[1] * A[6] * A[15] - A[1] * A[7] * A[14] - A[5] * A[2] * A[15] + A[5] * A[3] * A[14] + A[13] * A[2] * A[7] - A[13] * A[3] * A[6]; + ret[6] = -A[0] * A[6] * A[15] + A[0] * A[7] * A[14] + A[4] * A[2] * A[15] - A[4] * A[3] * A[14] - A[12] * A[2] * A[7] + A[12] * A[3] * A[6]; + ret[10] = A[0] * A[5] * A[15] - A[0] * A[7] * A[13] - A[4] * A[1] * A[15] + A[4] * A[3] * A[13] + A[12] * A[1] * A[7] - A[12] * A[3] * A[5]; + ret[14] = -A[0] * A[5] * A[14] + A[0] * A[6] * A[13] + A[4] * A[1] * A[14] - A[4] * A[2] * A[13] - A[12] * A[1] * A[6] + A[12] * A[2] * A[5]; + + ret[3] = -A[1] * A[6] * A[11] + A[1] * A[7] * A[10] + A[5] * A[2] * A[11] - A[5] * A[3] * A[10] - A[9] * A[2] * A[7] + A[9] * A[3] * A[6]; + ret[7] = A[0] * A[6] * A[11] - A[0] * A[7] * A[10] - A[4] * A[2] * A[11] + A[4] * A[3] * A[10] + A[8] * A[2] * A[7] - A[8] * A[3] * A[6]; + ret[11] = -A[0] * A[5] * A[11] + A[0] * A[7] * A[9] + A[4] * A[1] * A[11] - A[4] * A[3] * A[9] - A[8] * A[1] * A[7] + A[8] * A[3] * A[5]; + ret[15] = A[0] * A[5] * A[10] - A[0] * A[6] * A[9] - A[4] * A[1] * A[10] + A[4] * A[2] * A[9] + A[8] * A[1] * A[6] - A[8] * A[2] * A[5]; + + var det = A[0] * ret[0] + A[1] * ret[4] + A[2] * ret[8] + A[3] * ret[12]; + + if (det === 0) { + throw "no inverse"; + } + + return dali.matrixByScalar(ret, 1 / det); +}; + +/** + * Radian to degree + * @method degree + * @param {float} radians + * @return {float} degrees + */ +dali.degree = function(radians) { + "use strict"; + return (radians * 180.0) / Math.PI; +}; + +/** + * Degree to Radians + * @method radian + * @param {float} degree + * @return {float} radian + */ +dali.radian = function(degrees) { + "use strict"; + return (degrees / 180.0) * Math.PI; +}; + +/** + * Length of a vector + * @method vectorLength + * @param {array} Vector4 + * @return {float} The length of a vector + */ +dali.vectorLength = function(array) { + "use strict"; + var N = 3; // array.length; + + var length = 0; + for (var i = 0; i < N; ++i) { + length += array[i] * array[i]; + } + return Math.sqrt(length); +}; + +/** + * Length of a vector squared + * @method vectorLengthSquared + * @param {array} Vector4 + * @return {float} The length of a vector squared + */ +dali.vectorLengthSquared = function(array) { + "use strict"; + var N = 3; // array.length; + + var length = 0; + for (var i = 0; i < N; ++i) { + length += array[i] * array[i]; + } + return length; +}; + +/** + * Normalized vector + * @method normalize + * @param {array} Vector4 + * @return {float} The normalized vector + */ +dali.normalize = function(array) { + "use strict"; + var N = 3; // array.length; + + var length = 0; + for (var i = 0; i < 3; ++i) { + length += array[i] * array[i]; + } + length = Math.sqrt(length); + + if (length !== 0) { + var ret = []; + for (i = 0; i < N; ++i) { + ret.push(array[i] / length); + } + for (i = N; i < array.length; ++i) { + ret.push(array[i]); + } + return ret; + } else { + return array; + } + +}; + +/** + * AxisAngle conversion to Quaternion + * @method axisAngleToQuaternion + * @param {array} axisAngle Vector4 [Axis.x, Axis.y, Axis.z, Angle] + * @return {array} Quaternion + */ +dali.axisAngleToQuaternion = function(axisAngle) { + "use strict"; + var an = dali.normalize(axisAngle); + var angle = axisAngle[axisAngle.length - 1]; + var halfTheta = angle * 0.5; + var sinThetaByTwo = Math.sin(halfTheta); + var cosThetaByTwo = Math.cos(halfTheta); + return [an[0] * sinThetaByTwo, + an[1] * sinThetaByTwo, + an[2] * sinThetaByTwo, + cosThetaByTwo + ]; +}; + +/** + * Vector3 dot product + * @method vectorDot + * @param {array} v1 Vector3 + * @param {array} v2 Vector3 + * @return {array} Quaternion + */ +dali.vectorDot = function(v1, v2) { + "use strict"; + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; +}; + +/** + * Vector4 dot product + * @method vectorDot + * @param {array} v1 Vector4 + * @param {array} v2 Vector4 + * @return {float} Dot product + */ +dali.vectorDot4 = function(v1, v2) { + "use strict"; + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2] + v1[3] * v2[3]; +}; + +/** + * Vector3 cross product + * @method vectorCross + * @param {array} v1 Vector3 + * @param {array} v2 Vector3 + * @return {array} Vector3 cross product + */ +dali.vectorCross = function(v1, v2) { + "use strict"; + var v1X = v1[0]; + var v1Y = v1[1]; + var v1Z = v1[2]; + + var v2X = v2[0]; + var v2Y = v2[1]; + var v2Z = v2[2]; + + return [v1Y * v2Z - v1Z * v2Y, + v1Z * v2X - v1X * v2Z, + v1X * v2Y - v1Y * v2X + ]; +}; + +/** + * VectorN dot product + * @method vectorByScalar + * @param {array} v1 VectorN + * @param {array} v2 VectorN + * @return {array} VectorN * s + */ +dali.vectorByScalar = function(v1, s) { + "use strict"; + var ret = []; + for (var i = 0, len = v1.length; i < len; i++) { + ret.push(v1[i] * s); + } + return ret; +}; + +/** + * VectorN dot product + * @method vectorAdd + * @param {array} v1 VectorN + * @param {array} v2 VectorN + * @param {array} ..vN VectorN + * @return {array} v1 + v2 + ... + vN + */ +dali.vectorAdd = function() { + "use strict"; + var ret = arguments[0]; + var l = ret.length; + for (var i = 1, len = arguments.length; i < len; i++) { + var v = arguments[i]; + for (var j = 0; j < l; j++) { + ret[j] += v[j]; + } + } + return ret; +}; + +/** + * Quaternion by quaternion + * @method quatByQuat + * @param {array} q1 Quaternion + * @param {array} q2 Quaternion + * @return {array} Quaternion + */ +dali.quatByQuat = function(q1, q2) { + "use strict"; + var q1X = q1[0]; + var q1Y = q1[1]; + var q1Z = q1[2]; + var q1W = q1[3]; + + var q2X = q2[0]; + var q2Y = q2[1]; + var q2Z = q2[2]; + var q2W = q2[3]; + + return [q1Y * q2Z - q1Z * q2Y + q1W * q2X + q1X * q2W, + q1Z * q2X - q1X * q2Z + q1W * q2Y + q1Y * q2W, + q1X * q2Y - q1Y * q2X + q1W * q2Z + q1Z * q2W, + q1W * q2W - dali.vectorDot(q1, q2) + ]; +}; + +/** + * Quaternion to Vector4 Axis angle + * @method quaternionToAxisAngle + * @param {array} q Quaternion + * @return {array} Vector4 [Axis.x, Axis.y, Axis.z, Angle] + */ +dali.quaternionToAxisAngle = function(q) { + "use strict"; + var angle = Math.acos(q[3]); + var sine = Math.sin(angle); + + if (sine === 0.0) { + throw "Cannot convert quaternion"; + } + + var sinfThetaInv = 1.0 / sine; + + return [q[0] * sinfThetaInv, + q[1] * sinfThetaInv, + q[2] * sinfThetaInv, + angle * 2.0 + ]; +}; + +/** + * Euler angles to Quaternion + * @method eulerToQuaternion + * @param {float} rxPitch Euler radians pitch + * @param {float} ryYaw Euler radians yaw + * @param {float} rzRoll Euler radians roll + * @return {array} Quaternion + */ +dali.eulerToQuaternion = function(rXPitch, rYYaw, rZRoll) +{ + var halfX = 0.5 * rXPitch; + var halfY = 0.5 * rYYaw; + var halfZ = 0.5 * rZRoll; + + var cosX2 = Math.cos(halfX); + var cosY2 = Math.cos(halfY); + var cosZ2 = Math.cos(halfZ); + + var sinX2 = Math.sin(halfX); + var sinY2 = Math.sin(halfY); + var sinZ2 = Math.sin(halfZ); + + return [ cosZ2 * cosY2 * sinX2 - sinZ2 * sinY2 * cosX2, + cosZ2 * sinY2 * cosX2 + sinZ2 * cosY2 * sinX2, + sinZ2 * cosY2 * cosX2 - cosZ2 * sinY2 * sinX2, + cosZ2 * cosY2 * cosX2 + sinZ2 * sinY2 * sinX2 ]; +}; + +/** + * Euler angles to Vector4 Axis angle + * @method eulerToAxisAngle + * @param {float} eulerInDegrees + * @return {array} Vector4 [Axis.x, Axis.y, Axis.z, Angle] + */ +dali.eulerToAxisAngle = function(eulerInDegrees) +{ + var q = dali.eulerToQuaternion(dali.radian(eulerInDegrees[0]),dali.radian(eulerInDegrees[1]), dali.radian(eulerInDegrees[2])); + var aa = dali.quaternionToAxisAngle(q); + aa[3] = dali.degree(aa[3]); // @todo - radian? + return aa; +}; + +/** + * Axis angle to Euler + * @method axisAngleToEuler + * @param {float} axisAngle [Axis.x, Axis.y, Axis.z, Angle] + * @return {array} Vector4 [roll, pitch, yaw] + */ +dali.axisAngleToEuler = function(axisAngle) +{ + // presume return from dali.js is degrees + axisAngle[3] = dali.radian(axisAngle[3]); + var q = dali.axisAngleToQuaternion(axisAngle); + return dali.quaternionToEuler(q).map(dali.degree); +}; + +/** + * Euler angles to Vector4 Axis angle + * @method quaternionToMatrix + * @param {float} axisAngle [Axis.x, Axis.y, Axis.z, Angle] + * @return {array} Vector4 + */ +dali.quaternionToMatrix = function(q) { + "use strict"; + var x = q[0]; + var y = q[1]; + var z = q[2]; + var w = q[3]; + var xx = x * x; + var yy = y * y; + var zz = z * z; + var xy = x * y; + var xz = x * z; + var wx = w * x; + var wy = w * y; + var wz = w * z; + var yz = y * z; + + var m = [1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1]; + + m[0] = 1.0 - 2.0 * (yy + zz); + m[1] = 2.0 * (xy + wz); + m[2] = 2.0 * (xz - wy); + m[3] = 0.0; + + m[4] = 2.0 * (xy - wz); + m[5] = 1.0 - 2.0 * (xx + zz); + m[6] = 2.0 * (yz + wx); + m[7] = 0.0; + + m[8] = 2.0 * (xz + wy); + m[9] = 2.0 * (yz - wx); + m[10] = 1.0 - 2.0 * (xx + yy); + m[11] = 0.0; + + m[12] = 0.0; + m[13] = 0.0; + m[14] = 0.0; + m[15] = 1.0; + + return m; +}; + +/** + * Quaternion to Euler + * @method quaternionToEuler + * @param {array} q Quaternion + * @return {array} Vector3 [roll, pitch, yaw] + */ +dali.quaternionToEuler = function(q) { + "use strict"; + var x = q[0]; + var y = q[1]; + var z = q[2]; + var w = q[3]; + + var sqw = w * w; + var sqx = x * x; + var sqy = y * y; + var sqz = z * z; + + return [ Math.atan2(2.0 * (y * z + x * w), -sqx - sqy + sqz + sqw), + Math.asin(-2.0 * (x * z - y * w)), + Math.atan2(2.0 * (x * y + z * w), sqx - sqy - sqz + sqw)]; +}; + + +/** + * Gets screen coordinates of world position + * @method worldToScreen + * @param {array} position array + * @param {Dali.RenderTask} renderTask Dali RenderTask object + * @return {array} Vector3 Screen position + */ +dali.worldToScreen = function(position, renderTask) { + "use strict"; + var useFirstRenderTask = false; + + if (typeof renderTask === "undefined") { + useFirstRenderTask = true; + } else if (renderTask === null) { // null is an object + useFirstRenderTask = true; + } + + if (useFirstRenderTask) { + var tasks = dali.stage.getRenderTaskList(); + renderTask = tasks.getTask(0); + tasks.delete(); // wrapper + } + + var camera = renderTask.getCameraActor(); + var pos = renderTask.getCurrentViewportPosition(); + var size = renderTask.getCurrentViewportSize(); + + var mat = dali.matrixByMatrix(camera.viewMatrix, camera.projectionMatrix); + var p = dali.matrixByVector(mat, position); + var depthRange = [0, 1]; + var viewport = [pos[0], pos[1], size[0], size[1]]; + + var div; + // homogenous divide + if(0.0 === p[3]) { + div = 1.0; + } else { + div = 1 / p[3]; + } + + camera.delete(); // wrapper + + return [ + (1 + p[0] * div) * viewport[2] / 2 + viewport[0], (1 - p[1] * div) * viewport[3] / 2 + viewport[1], (p[2] * div) * (depthRange[1] - depthRange[0]) + depthRange[0], + div + ]; +}; + +/** + * Gets matrix identity + * @method matrixIdentity + * @return {array} Matrix4 identity + */ +dali.matrixIdentity = function() { + "use strict"; + return [1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ]; +}; + +/** + * Gets matrix identity with position transformation + * @method matrixTransform + * @param {float} x X position + * @param {float} y Y position + * @param {float} z Z position + * @return {array} Matrix4 + */ +dali.matrixTransform = function(x, y, z) { + "use strict"; + return [1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + x, y, z, 1 + ]; +}; + +/** + * Gets matrix identity with position transformation + * @method screenToPlaneLocal + * @param {float} screenX Screen X position + * @param {float} screenY Screen Y position + * @param {Dali.RenderTask} Dali RenderTask + * @param {array} planeOrientationMatrix + * @param {float} planeWidth + * @param {float} planeHeight + * @return {array} Local coordinates + */ +dali.screenToPlaneLocal = function(screenX, screenY, renderTask, planeOrientationMatrix, planeWidth, planeHeight) { + "use strict"; + + var camera = renderTask.getCameraActor(); + + var pos = renderTask.getCurrentViewportPosition(); + var size = renderTask.getCurrentViewportSize(); + var viewportX = pos[0]; + var viewportY = pos[1]; + var viewportW = size[0]; + var viewportH = size[1]; + var modelView = dali.matrixByMatrix(planeOrientationMatrix, camera.viewMatrix); + + var inverseMvp = dali.matrixInverse( + dali.matrixByMatrix(modelView, camera.projectionMatrix)); + + var screenPos = [screenX - viewportX, + viewportH - (screenY - viewportY), + 0.0, 1.0 + ]; + + screenPos[2] = 0.0; + + var oglScreenPos = [(screenPos[0] / viewportW) * 2 - 1, (screenPos[1] / viewportH) * 2 - 1, (screenPos[2]) * 2 - 1, + 1 + ]; + + + var nearPoint = dali.matrixByVector(inverseMvp, oglScreenPos); + + if (nearPoint[3] === 0.0) { + throw "Unproject near fails"; + } + + nearPoint[3] = 1 / nearPoint[3]; + nearPoint[0] = nearPoint[0] * nearPoint[3]; + nearPoint[1] = nearPoint[1] * nearPoint[3]; + nearPoint[2] = nearPoint[2] * nearPoint[3]; + + + oglScreenPos[2] = 1.0 * 2 - 1; + + var farPoint = dali.matrixByVector(inverseMvp, oglScreenPos); + + if (farPoint[3] === 0.0) { + throw "Unproject far fails"; + } + + farPoint[3] = 1 / farPoint[3]; + farPoint[0] = farPoint[0] * farPoint[3]; + farPoint[1] = farPoint[1] * farPoint[3]; + farPoint[2] = farPoint[2] * farPoint[3]; + + if (!((farPoint[2] < 0) && (nearPoint[2] > 0))) { + throw "ray not crossing xy plane"; + } + + var dist = nearPoint[2] / (nearPoint[2] - farPoint[2]); + + var intersect = [nearPoint[0] + (farPoint[0] - nearPoint[0]) * dist, + nearPoint[1] + (farPoint[1] - nearPoint[1]) * dist, + 0.0 + ]; + + intersect[0] = intersect[0] + planeWidth * 0.5; + intersect[1] = intersect[1] + planeHeight * 0.5; + + return intersect; +}; + +/** + * Gets matrix identity with position transformation + * @method screenToLocal + * @param {float} screenX Screen X position + * @param {float} screenY Screen Y position + * @param {Dali.Actor} actor Dali Actor + * @param {Dali.RenderTask} renderTask Dali RenderTask + * @return {array} Local coordinates + */ +dali.screenToLocal = function(screenX, screenY, actor, renderTask) { + "use strict"; + return dali.screenToPlaneLocal(screenX, screenY, renderTask, actor.worldMatrix, actor.size[0], actor.size[1]); +}; + +/** + * Screen to local coordinates in the XY plane + * @method screenToXY + * @param {float} screenX Screen X position + * @param {float} screenY Screen Y position + * @param {Dali.Actor} actor Dali Actor + * @param {Dali.RenderTask} renderTask Dali RenderTask + * @return {array} Local coordinates + */ +dali.screenToXY = function(screenX, screenY, actor, renderTask) { + "use strict"; + var size = dali.stage.getSize(); + return dali.screenToPlaneLocal(screenX, screenY, + renderTask, + dali.matrixIdentity(), + size[0], + size[1]); +}; + +/** + * Screen to local coordinates in the YZ plane + * @method screenToYZ + * @param {float} screenX Screen X position + * @param {float} screenY Screen Y position + * @param {Dali.Actor} actor Dali Actor + * @param {Dali.RenderTask} renderTask Dali RenderTask + * @return {array} Local coordinates + */ +dali.screenToYZ = function(screenX, screenY, actor, renderTask) { + "use strict"; + var size = dali.stage.getSize(); + var q = dali.axisAngleToQuaternion( [0, 1, 0, dali.radian(90)] ); + return dali.screenToPlaneLocal(screenX, screenY, + renderTask, + dali.quaternionToMatrix(q), + size[0], + size[1]); +}; + +/** + * Screen to local coordinates in the XZ plane + * @method screenToXZ + * @param {float} screenX Screen X position + * @param {float} screenY Screen Y position + * @param {Dali.Actor} actor Dali Actor + * @param {Dali.RenderTask} renderTask Dali RenderTask + * @return {array} Local coordinates + */ +dali.screenToXZ = function(screenX, screenY, actor, renderTask) { + "use strict"; + var size = dali.stage.getSize(); + var q = dali.axisAngleToQuaternion( [1, 0, 0, dali.radian(90)] ); + return dali.screenToPlaneLocal(screenX, screenY, + renderTask, + dali.quaternionToMatrix(q), + size[0], + size[1]); +}; + +/** + * Screen coordinates for the given renderTask + * @method screenCoordinates + * @param {Dali.Actor} actor Dali Actor + * @param {Dali.RenderTask} renderTask Dali RenderTask + * @return {array} Local coordinates + */ +dali.screenCoordinates = function(actor, renderTask) { + "use strict"; + var size = actor.size; + var w2 = size[0] / 2; + var h2 = size[1] / 2; + var actorWorldMatrix = actor.worldMatrix; + var actorWorldPosition = actor.worldPosition; + + return { + topLeft: dali.worldToScreen(dali.matrixByVector(actorWorldMatrix, [-w2, -h2, 0]), renderTask), + topRight: dali.worldToScreen(dali.matrixByVector(actorWorldMatrix, [+w2, -h2, 0]), renderTask), + bottomRight: dali.worldToScreen(dali.matrixByVector(actorWorldMatrix, [+w2, +h2, 0]), renderTask), + bottomLeft: dali.worldToScreen(dali.matrixByVector(actorWorldMatrix, [-w2, +h2, 0]), renderTask), + centre: dali.worldToScreen(actorWorldPosition, renderTask) + }; +}; + +/** + * Screen coordinates for the given renderTask + * @method screenCoordinates + * @param {Dali.Actor} actor Dali Actor + * @param {Dali.RenderTask} renderTask Dali RenderTask + * @return {array} Local coordinates + */ +dali.screenToActor = function(actor, screenPos, renderTask) { + "use strict"; + // , function will return coordinates in relation to actor`s anchorPoint (client coordinates). + var useFirstRenderTask = false; + + if (typeof renderTask === "undefined") { + useFirstRenderTask = true; + } else if (renderTask === null) { // null is an object + useFirstRenderTask = true; + } + + if (useFirstRenderTask) { + var tasks = dali.stage.getRenderTaskList(); + renderTask = tasks.getTask(0); + tasks.delete(); // wrapper + } + + var camera = renderTask.getCameraActor(); + var vpp = renderTask.getCurrentViewportPosition(); + var vps = renderTask.getCurrentViewportSize(); + + var mat = dali.matrixByMatrix(camera.projectionMatrix, camera.viewMatrix); + + var inverseMvp = dali.matrixInverse(mat); + + var x = screenPos[0]; + var y = screenPos[1]; + var z = screenPos[2]; + + var objectPos = dali.matrixByVector(inverseMvp, + // normalized +-1 + [((x - vpp[0]) / vps[0]) * 2.0 - 1.0, ((y - vpp[1]) / vps[1]) * 2.0 - 1.0, (z * 2.0) - 1.0, + 1.0 + ]); + + if (objectPos[3] === 0.0) { + throw "Cannot find screen Position"; + } + + objectPos[3] = 1 / objectPos[3]; + + return [objectPos[0] * objectPos[3], + objectPos[1] * objectPos[3], + objectPos[2] * objectPos[3], + objectPos[3] + ]; +}; + + +//------------------------------------------------------------------------------ +// +// Utils Module +// +//------------------------------------------------------------------------------ + +/** + * Cache to fix the dali get/set thread issue + * + * Property sets are cached and cleared at the renderFinished callback + */ +dali.internalUniqueId = function() { + "use strict"; +}; + +dali.internalUniqueId.prototype._id = 0; +dali.internalUniqueId.prototype.generateId = function() { + "use strict"; + return ++dali.internalUniqueId.prototype._id; +}; + +dali.internalPropertyCacheEnable = true; +dali.internalPropertyCache = {}; + +/** + * Merge two objects together in a simplistic key,value merge + * @method mergeObjects + * @param {object} o1 first object + * @param {object} o2 second object + * @return {object} The merged objects + */ +dali.mergeObjects = function(o1, o2) { + "use strict"; + for (var p in o2) { + try { + // Property in destination object set; update its value. + if ( o2[p].constructor === Object){ + o1[p] = dali.mergeObjects(o1[p], o2[p]); + + } else { + o1[p] = o2[p]; + } + } catch(e) { + // Property in destination object not set; create it and set its value. + o1[p] = o2[p]; + } + } + return o1; +}; + +//------------------------------------------------------------------------------ +// +// Callbacks Module +// +// Data dali can request during update & render loop. +// +//------------------------------------------------------------------------------ + +/** + * Gets a glyph by rendering to a hidden browser canvas + */ +/** @private */ +dali.requestedGlyphImage = function(sFontFamily, sFontStyle, fFontSize, iChar) { + "use strict"; + + var buffer = document.createElement("canvas"); + buffer.width = fFontSize; + buffer.height = fFontSize; + var ctx = buffer.getContext("2d"); + + ctx.font = sFontStyle + " " + fFontSize + "px " + sFontFamily; + ctx.fillText(String.fromCharCode(iChar), 0, 0 + fFontSize); + + var imageData = ctx.getImageData(0, 0, fFontSize, fFontSize); + + // emscripten checks only for this type if array in converting to strings + // (getImageData() returns Uint8CheckedArray or some such) + // var uint8array = new Uint8Array( imageData.data ); + + return imageData.data; // return uint8array; // +}; + +/** @private */ +dali.postRenderFunction = undefined; + +/* + * End of renderering tasks + * - Reset the property cache + */ +/** @private */ +dali.requestedRenderFinished = function() { + "use strict"; + // reset the temporary cache + dali.internalPropertyCache = {}; + if(dali.postRenderFunction) { + dali.postRenderFunction(); + } +}; + +dali.setCallbackGetGlyphImage(dali.requestedGlyphImage); +dali.setCallbackRenderFinished(dali.requestedRenderFinished); + +//------------------------------------------------------------------------------ +// +// Property Marshalling Module +// +// Javascript objects are adorned with dali properties as they are created or +// fetched from the C++ api +// +// Data is marshalled to avoid some of the necessary C++ memory management for +// small property classes. +// +//------------------------------------------------------------------------------ +dali.__propertyTypeJsLut = { boolean: dali.PropertyType.BOOLEAN, + number: dali.PropertyType.FLOAT, + string: dali.PropertyType.STRING }; + +dali.__propertyValueCtor = {}; + +[ [dali.PropertyType.INTEGER.value, dali.PropertyValueInteger], + [dali.PropertyType.FLOAT.value, dali.PropertyValueFloat], + [dali.PropertyType.STRING.value, dali.PropertyValueString], + [dali.PropertyType.VECTOR2.value, dali.PropertyValueVector2], + [dali.PropertyType.VECTOR3.value, dali.PropertyValueVector3], + [dali.PropertyType.VECTOR4.value, dali.PropertyValueVector4], + [dali.PropertyType.MATRIX.value, dali.PropertyValueMatrix], + [dali.PropertyType.MATRIX3.value, dali.PropertyValueMatrix3], + [dali.PropertyType.ARRAY.value, dali.PropertyValueArray], + [dali.PropertyType.MAP.value, dali.PropertyValueMap], + [dali.PropertyType.RECTANGLE.value, dali.PropertyValueIntRect] ].map( function(ab) { dali.__propertyValueCtor[ ab[0] ] = ab[1]; } ); + +dali.propertyTypeFromJsValue = function(value) { + return dali.__propertyTypeJsLut[ typeof(value) ]; +}; + +dali.propertyValueCtor = function(propertyType) { + return dali.__propertyValueCtor[ propertyType.value ]; +}; + +/** + * Create a Dali PropertyValue from a javascript value. + * @method DaliPropertyValue + * @param {object} object to retrieve the property type when value is an object + * @param {string} name The name of the property + * @return {Dali.PropertyValue} A Dali PropertyValue which must be '.delete()' when finished with + */ +dali.DaliPropertyValue = function(object, name, value) { + "use strict"; + + var setProperty = false; + if (typeof (value) === "boolean") { + setProperty = new dali.PropertyValueBoolean(value); + } else if (typeof (value) === "number") { + setProperty = new dali.PropertyValueFloat(value); + } else if (typeof (value) === "string") { + setProperty = new dali.PropertyValueString(value); + } else if (typeof (value) === "object") { + if (value.constructor === dali.PropertyValueValue) { + setProperty = value; + } else { + var type = false; + if (object) { + type = object.getPropertyTypeFromName(name); + } + if (type === dali.PropertyType.ROTATION) { + if (value.length === 3) { + setProperty = new dali.PropertyValueEuler(value); + } else { + setProperty = new dali.PropertyValueAxisAngle(value); + } + } else if (value.length) { + if (type === dali.PropertyType.ARRAY) { + setProperty = new dali.PropertyValueArray(value); + } else { + if (value.length === 2) { + setProperty = new dali.PropertyValueVector2(value); + } else if (value.length === 3) { + setProperty = new dali.PropertyValueVector3(value); + } else if (value.length === 4) { + if (type === dali.PropertyType.RECTANGLE) { + setProperty = new dali.PropertyValueIntRect(value[0], value[1], value[2], value[3]); + } else { + setProperty = new dali.PropertyValueVector4(value); + } + } else if (value.length === 9) { + setProperty = new dali.PropertyValueMatrix3(value); + } else if (value.length === 16) { + setProperty = new dali.PropertyValueMatrix(value); + } else { + throw new Error("Cannot set property"); + } + } + } else { + // todo; I think a map has a length.... + setProperty = new dali.PropertyValueMap(value); + } + } + } else { + if (object) { + throw object.toString() + " " + name.toString() + " " + value.toString(); + } else { + throw name.toString() + " " + value.toString(); + } + } + + return setProperty; +}; + +/** + * Get the value type held in a PropertyValue and call '.delete()' to drop the C++ backing object + * @method marshallProperty + * @param {Dali.PropertyValue} p A Dali PropertyValue. This will be '.delete()'ed. + * @return {any} The value held + */ +/** @private */ +dali.marshallProperty = function(p) { + "use strict"; + + var ret; + var type = p.getType(); + + if (type === 0) { + // raise? + throw new Error("Property has no type?"); + } else if (type === dali.PropertyType.BOOLEAN.value) { + ret = p.getBoolean(); + } else if (type === dali.PropertyType.FLOAT.value) { + ret = p.getFloat(); + } else if (type === dali.PropertyType.INTEGER.value) { + ret = p.getInteger(); + } else if (type === dali.PropertyType.VECTOR2.value) { + ret = p.getVector2(); + } else if (type === dali.PropertyType.VECTOR3.value) { + ret = p.getVector3(); + } else if (type === dali.PropertyType.VECTOR4.value) { + ret = p.getVector4(); + } else if (type === dali.PropertyType.MATRIX3.value) { + ret = p.getMatrix3(); + } else if (type === dali.PropertyType.MATRIX.value) { + ret = p.getMatrix(); + } else if( type === dali.PropertyType.RECTANGLE ) { + ret = p.getIntRect(); + } else if (type === dali.PropertyType.ROTATION.value) { + ret = p.getRotation(); + } else if (type === dali.PropertyType.STRING.value) { + ret = p.getString(); + } else if (type === dali.PropertyType.ARRAY.value) { + ret = p.getArray(); + } else if (type === dali.PropertyType.MAP.value) { + ret = p.getMap(); + } + p.delete(); + return ret; +}; + +/** + * Set a value on an object by creating and deleting a Dali PropertyValue object + * @method marshallSetProperty + * @param {object} object The object who's property to set from + * @param {string} name The property name + * @param {any} value The Javascript value + */ +/** @private */ +dali.marshallSetProperty = function(object, name, value) { + "use strict"; + var setProperty = new dali.DaliPropertyValue(object, name, value); + + if (setProperty) { + + object.setProperty(name, setProperty); + + //console.log("marshallSetProperty set property" + setProperty ); + setProperty.delete(); + + if(dali.internalPropertyCacheEnable) { + // set in cache to fix dali get/set problem + if("getId" in object ) // only with actors + { + var uniqueId = object.getId(); // _uniqueId; + if (uniqueId !== undefined) { + var objectDict = dali.internalPropertyCache[uniqueId]; + if (objectDict === undefined) { + dali.internalPropertyCache[uniqueId] = {}; + } + dali.internalPropertyCache[uniqueId][name] = value; + } + } + } + } +}; + +/** + * Get a Javascript value from an object by creating and deleting a PropertyValue + * @method marshallGetProperty + * @param {object} object The object who's property to get from + * @param {string} name The property name + * @return {any} The value of the property + */ +/** @private */ +dali.marshallGetProperty = function(object, name) { + "use strict"; + + if(dali.internalPropertyCacheEnable) { + // is the value live in the cache? if so use that value instead + if("getId" in object) { // only with actors + var uniqueId = object.getId(); // _uniqueId; + if (uniqueId !== undefined) { + var objectDict = dali.internalPropertyCache[uniqueId]; + if (objectDict !== undefined) { + var value = dali.internalPropertyCache[uniqueId][name]; + if (value !== undefined) { + return value; + } + } + } + } + } + + var ret; + var p; + p = object.getProperty(name); + if (!p) { + throw new Error("Property doesnt exist?"); + } + var type = p.getType(); + + if (type === 0) { + // raise? + throw new Error("Property has no type?"); + } else if (type === dali.PropertyType.BOOLEAN.value) { + ret = p.getBoolean(); + } else if (type === dali.PropertyType.FLOAT.value) { + ret = p.getFloat(); + } else if (type === dali.PropertyType.INTEGER.value) { + ret = p.getInteger(); + } else if (type === dali.PropertyType.VECTOR2.value) { + ret = p.getVector2(); + } else if (type === dali.PropertyType.VECTOR3.value) { + ret = p.getVector3(); + } else if (type === dali.PropertyType.VECTOR4.value) { + // VECTOR4 + ret = p.getVector4(); + } else if (type === dali.PropertyType.MATRIX3.value) { + // MATRIX3 + ret = p.getMatrix3(); + } else if (type === dali.PropertyType.MATRIX.value) { + // MATRIX + ret = p.getMatrix(); + } else if( type === dali.PropertyType.RECTANGLE ) { + ret = p.getIntRect(); + } else if (type === dali.PropertyType.ROTATION.value) { + ret = p.getRotation(); + } else if (type === dali.PropertyType.STRING.value) { + ret = p.getString(); + } else if (type === dali.PropertyType.ARRAY.value) { + ret = p.getArray(); + } else if (type === dali.PropertyType.MAP.value) { + ret = p.getMap(); + } + p.delete(); + return ret; +}; + + +/** + * Set property accessors on the given handle for each property found on the handle + * @method internalSetupProperties + * @param {Dali.BaseHandle} handle A Dali property holding object + * @return {Dali.BaseHandle} The same handle which has property accessors. + */ +/** @private */ +dali.internalSetupProperties = function(handle) { + "use strict"; + if(handle.ok()) { + var props = handle.getProperties(); + + for (var i = 0; i < props.size(); i++) { + // anon function because of closure with defineProperty + // (if just variable in loop then the variable 'address' is captured, not the value + // so it becomes last value set) + (function(name, object) { + // @todo Dali error?? name lengths should never be zero + if (name.length) { + Object.defineProperty(handle, name, { + enumerable: true, + configurable: false, + get: function() { + return dali.marshallGetProperty(handle, name); + }, + set: function(newValue) { + dali.marshallSetProperty(handle, name, newValue); + } + }); + } + })(props.get(i), handle); + } + + // handle._uniqueId = dali.internalUniqueId.prototype.generateId(); + } + + return handle; +}; + +//------------------------------------------------------------------------------ +// +// Handle API Module +// +// API Wrappers for some Dali.Handle methods to marshall properties +// +//------------------------------------------------------------------------------ + +/** + * Register a new property and add JS style property accessors + * @method registerProperty + * @param {string} name The property name + * @param {any} value Any Javascript value + * @return {int} The registered properties ID + */ +dali.Handle.prototype.registerProperty = function(name, value) { + "use strict"; + var ret = -1; + + var propertyValue = new dali.DaliPropertyValue(null, name, value); + ret = this.__registerProperty(name, propertyValue); + propertyValue.delete(); // wrapper + Object.defineProperty(this, name, { + enumerable: true, + configurable: false, + get: function() { + return dali.marshallGetProperty(this, name); + }, + set: function(newValue) { + dali.marshallSetProperty(this, name, newValue); + } + }); + + return ret; +}; + +/** + * Register a new animated property + * @method registerAnimatedProperty + * @param {string} name The property name + * @param {any} value Any Javascript value + * @return {int} The registered properties ID + */ +dali.Handle.prototype.registerAnimatedProperty = function(name, value) { + "use strict"; + var ret = -1; + + var propertyValue = new dali.DaliPropertyValue(null, name, value); + ret = this.__registerAnimatedProperty(name, propertyValue); + propertyValue.delete(); // wrapper + Object.defineProperty(this, name, { + enumerable: true, + configurable: false, + get: function() { + return dali.marshallGetProperty(this, name); + }, + set: function(newValue) { + dali.marshallSetProperty(this, name, newValue); + } + }); + return ret; +}; + +//------------------------------------------------------------------------------ +// +// Stage Module +// +//------------------------------------------------------------------------------ +dali.Stage.prototype.getRootLayer = function() { + "use strict"; + var root = this.__getRootLayer(); + dali.internalSetupProperties(root); + return root; +}; + +//------------------------------------------------------------------------------ +// +// PropertyMap Module +// +// API Wrappers for some Dali.PropertyMap methods to marshall properties +// +//------------------------------------------------------------------------------ + +/** + * Insert a value into the PropertyMap + * @method insert + * @param {string} key The key + * @param {any} value Any Javascript value + * @param {PropertyType} propertyType The Dali property type + */ +dali.PropertyMap.prototype.insert = function(key, value, propertyType) { + "use strict"; + + var type = propertyType; + + if( propertyType === undefined ) { // can be optional + propertyType = dali.propertyTypeFromJsValue(value); + } + + var constructor = dali.propertyValueCtor(propertyType); + + var setProperty = constructor( value ); + + if(setProperty) { + this.__insert(key, setProperty); + setProperty.delete(); + } +}; + +/** + * Get a value from the PropertyMap + * @method get + * @param {string} key The key + * @return The Javascript value + */ +dali.PropertyMap.prototype.get = function(key) { + "use strict"; + var p = this.__get(key); + + var ret = dali.marshallProperty(p); + + // p.delete(); // @todo should we delete here? + + return ret; +}; + +//------------------------------------------------------------------------------ +// +// PropertyBuffer Module +// +// API Wrappers for some Dali.PropertyBuffer methods to marshall properties +// +//------------------------------------------------------------------------------ +var _propertyTypeInfoList = [ + [ dali.PropertyType.FLOAT.value, { size: 4, length: 1, dataView: Float32Array }], + [ dali.PropertyType.INTEGER.value, { size: 4, length: 1, dataView: Int32Array }], + [ dali.PropertyType.VECTOR2.value, { size: 2 * 4, length: 2, dataView: Float32Array }], + [ dali.PropertyType.VECTOR3.value, { size: 3 * 4, length: 3, dataView: Float32Array }], + [ dali.PropertyType.VECTOR4.value, { size: 4 * 4, length: 4, dataView: Float32Array }], + [ dali.PropertyType.MATRIX3.value, { size: 9 * 4, length: 9, dataView: Float32Array }], + [ dali.PropertyType.MATRIX.value, { size: 16 * 4, length: 16, dataView: Float32Array }] +]; + +var _propertyTypeInfo = {}; +function _createPropertyBuffer() { + "use strict"; + for(var i = 0; i < _propertyTypeInfoList.length; i++) { + _propertyTypeInfo[ _propertyTypeInfoList[i][0] ] = _propertyTypeInfoList[i][1]; + } +} + +_createPropertyBuffer(); + +/** + * Create a Dali.PropertyBuffer from an info dictionary + * @method createPropertyBuffer + * @param {object} info + * @param {any} value Any Javascript value + * @param {PropertyType} propertyType The Dali property type + * @example + * var verts = createPropertyBuffer( {format: [ ["apos", dali.PropertyType.VECTOR2], + * ["acol", dali.PropertyType.VECTOR4] ], + * data: { "apos": [ [-halfQuadSize, -halfQuadSize], + * [+halfQuadSize, -halfQuadSize], + * [-halfQuadSize, +halfQuadSize], + * [+halfQuadSize, +halfQuadSize] + * ], + * "acol": [ [0, 0, 0, 1], + * [1, 0, 1, 1], + * [0, 1, 0, 1], + * [1, 1, 1, 1] + * ] + * } + * } + */ +dali.createPropertyBuffer = function(info) { + "use strict"; + var format = new dali.PropertyMap(); + var dataLength; + var recordSize = 0; + var i; + for(i = 0; i < info.format.length; i++) { + format.insert(info.format[i][0], info.format[i][1].value, dali.PropertyType.INTEGER); + if(dataLength === undefined) { + dataLength = info.data[info.format[i][0]].length; + } + assert(info.data[info.format[i][0]]); + assert(dataLength === info.data[info.format[i][0]].length); + recordSize += _propertyTypeInfo[info.format[i][1].value].size; + } + + var buffer = new ArrayBuffer(dataLength * recordSize); + + var recordOffset = 0; + var offset = 0; + for(i = 0; i < dataLength; i++) { + for(var j = 0; j < info.format.length; j++) { + var name = info.format[j][0]; + var type = info.format[j][1].value; + var length = _propertyTypeInfo[type].length; + var DataView = _propertyTypeInfo[type].dataView; + var view = new DataView(buffer, recordOffset + offset, length); + offset += _propertyTypeInfo[type].size; + if(length === 1) { + view[0] = info.data[name][i]; + } else { + for(var k = 0; k < length; k++) { + view[k] = info.data[name][i][k]; + } + } + } + offset = 0; + recordOffset += recordSize; + } + + var propertyBuffer = new dali.PropertyBuffer(format, dataLength); + + propertyBuffer.setData(buffer); + + format.delete(); // + + return propertyBuffer; +}; + +//------------------------------------------------------------------------------ +// +// Actor Module +// +// API Wrappers for some Dali.PropertyBuffer methods to marshall properties +// +//------------------------------------------------------------------------------ + +/** + * Gets a parent with JS style property accessors + * @method getParent + * @return The parent + */ +dali.Actor.prototype.getParent = function() { + "use strict"; + var bareActor = this.__getParent(); + if(!bareActor.ok()) { + bareActor.delete(); // wrapper + bareActor = null; + } else { + // add properties to the bare Actor + dali.internalSetupProperties(bareActor); + } + return bareActor; +}; + +/** + * Finds child by ID and adorns with JS style property accessors + * @method findChildById + * @param {int} index The ID of the child + * @return The found child or null + */ +dali.Actor.prototype.findChildById = function(index) { + "use strict"; + var bareActor = this.__findChildById(index); + if(!bareActor.ok()) { + bareActor.delete(); // wrapper + bareActor = null; + } else { + dali.internalSetupProperties(bareActor); + } + return bareActor; +}; + +/** + * Finds child by name and adorns with JS style property accessors + * @method findChildByName + * @param {string} name The ID of the child + * @return The found child or null + */ +dali.Actor.prototype.findChildByName = function(name) { + "use strict"; + var bareActor = this.__findChildByName(name); + if(!bareActor.ok()) { + bareActor.delete(); // wrapper + bareActor = null; + } else { + dali.internalSetupProperties(bareActor); + } + return bareActor; +}; + +/** + * Gets child at child index and adorns with JS style property accessors + * @method getChildAt + * @param {int} index The ID of the child + * @return The found child or null + */ +dali.Actor.prototype.getChildAt = function(index) { + "use strict"; + var bareActor = this.__getChildAt(index); + if(!bareActor.ok()) { + bareActor.delete(); // wrapper + bareActor = null; + } else { + dali.internalSetupProperties(bareActor); + } + return bareActor; +}; + +/* + * add children of actor to collection in depth first manner + */ +/** @private */ +dali.internalDepthFirstCollection = function(actor, collection) { + "use strict"; + for (var i = 0; i < actor.getChildCount(); i++) { + var a = actor.getChildAt(i); // adds properties in dotted + collection.push(a); + dali.internalDepthFirstCollection(a, collection); + } +}; + +/** + * Finds all children of the actor and adorns with JS style property accessors + * @method findAllChildren + * @return A list of children + */ +dali.Actor.prototype.findAllChildren = function() { + "use strict"; + var col = []; + dali.internalDepthFirstCollection(this, col); + return col; +}; + +/** + * Gets a childFinds all children of the actor and adorns with JS style property accessors + * @method getChildren + * @return A list of children + */ +dali.Actor.prototype.getChildren = function() { + "use strict"; + var col = []; + for (var i = 0, len = this.getChildCount(); i < len; i++) { + var c = this.getChildAt(i); + col.push(c); + } + return col; +}; + +/** + * 'directChildren' kept for GUIBuilder support + * @deprecated + */ +dali.Actor.prototype.directChildren = dali.Actor.prototype.getChildren; + +/** + * Connects a callback to a signal by name + * @method connect + * @param {string} signalName The signal to connect to + * @param {function} callback The callback to call + * @param {Dali.SignalHolder} The signal holder object that can signal connection deletion + * @return true if connection was possible + */ +dali.Actor.prototype.connect = function(signalName, callback, signalHolder) { + "use strict"; + // wrap in closure so we can setup properties in . notation + // and add actor methods to c++ raw Actor + if(signalHolder === undefined) { + // default js signal holder if none provided + signalHolder = dali.jsSignalHolder; + } + + return this.__connect( signalHolder, + signalName, + (function(cb) { + return function() { + var args = [dali.internalSetupProperties(arguments[0])]; + for(var i = 1; i < arguments.length; i++) { + args.push( arguments[i] ); + } + cb.apply(null, args); + }; + })(callback) + ); +}; + +/** + * Connects a callback to a property notification + * @method setPropertyNotification + * @param {string} property The property name + * @param {string} condition The condition [False,LessTHan,GreaterThan,Inside,Outside,Step,VariableStep] + * @param {any} arg0 The first property notification argument + * @param {any} arg1 The second property notification argument + * @param {function} callback The callback function + * @param {Dali.SignalHolder} The signal holder object that can signal connection deletion + * @return true if connection was possible + */ +dali.Actor.prototype.setPropertyNotification = function(property, condition, arg0, arg1, callback, signalHolder) { + "use strict"; + + if(signalHolder === undefined) { + // default js signal holder if none provided + signalHolder = dali.jsSignalHolder; + } + + var index = this.getPropertyIndex(property); + + this.__setPropertyNotification(signalHolder, index, condition, arg0, arg1, callback); +}; + +/** + * Gets the renderer by index + * @method getRendererAt + * @param {int} index The index of the renderer + * @return The Render or null + */ +dali.Actor.prototype.getRendererAt = function(index) { + "use strict"; + var renderer = this.__getRendererAt(index); + if(!renderer.ok()) { + renderer.delete(); // wrapper + renderer = null; + } else { + dali.internalSetupProperties(renderer); + } + return renderer; +}; + +/** private */ +dali.__ActorConstructor = dali.Actor; + +/** + * Construtor that adorns with JS style property accessors + * @return The wrapped Dali.Actor object + */ +dali.Actor = function() { + "use strict"; + var a = new dali.__ActorConstructor(); + dali.internalSetupProperties(a); + return a; +}; + +//------------------------------------------------------------------------------ +// +// ShaderEffect Module +// +//------------------------------------------------------------------------------ +dali.__ShaderEffectConstructor = dali.ShaderEffect; +dali.ShaderEffect = function() { + "use strict"; + var a = new dali.__PathConstructor(); + dali.internalSetupProperties(a); + return a; +}; + +//------------------------------------------------------------------------------ +// +// New Mesh Module +// +//------------------------------------------------------------------------------ +dali.__ShaderConstructor = dali.Shader; +dali.Shader = function(vertex, fragment, hints) { + "use strict"; + var a = new dali.__ShaderConstructor(vertex, fragment, hints); + dali.internalSetupProperties(a); + return a; +}; + +dali.__MaterialConstructor = dali.Material; +dali.Material = function(shader) { + "use strict"; + var a = new dali.__MaterialConstructor(shader); + dali.internalSetupProperties(a); + return a; +}; + +dali.__RendererConstructor = dali.Renderer; +dali.Renderer = function(geometry, material) { + "use strict"; + var a = new dali.__RendererConstructor(geometry, material); + dali.internalSetupProperties(a); + return a; +}; + +//------------------------------------------------------------------------------ +// +// Animation Module +// +//------------------------------------------------------------------------------ +dali.__PathConstructor = dali.Path; +dali.Path = function() { + "use strict"; + var a = new dali.__PathConstructor(); + dali.internalSetupProperties(a); + return a; +}; + +/** + * animateTo a value + * @method animateTo + * @param {object} The object + * @param {string} propertyName The objects property name + * @param {any} value The value + * @param {string} alphaFunction The alpha function + * @param {float} delay The delay + * @param {float} duration The duration + */ +dali.Animation.prototype.animateTo = function(object, propertyName, value, alphaFunction, delay, duration) { + "use strict"; + var propertyValue = new dali.DaliPropertyValue(object, propertyName, value); + if (propertyValue) { + this.__animateTo(object, propertyName, propertyValue, alphaFunction, delay, duration); + propertyValue.delete(); + } else { + throw new Error("Unknown property?"); + } +}; + +/** + * animateBy a value + * @method animateBy + * @param {object} The object + * @param {string} propertyName The objects property name + * @param {any} value The value + * @param {string} alphaFunction The alpha function + * @param {float} delay The delay + * @param {float} duration The duration + */ +dali.Animation.prototype.animateBy = function(object, propertyName, value, alphaFunction, delay, duration) { + "use strict"; + var propertyValue = new dali.DaliPropertyValue(object, propertyName, value); + if (propertyValue) { + this.__animateBy(object, propertyName, propertyValue, alphaFunction, delay, duration); + propertyValue.delete(); + } else { + throw new Error("Unknown property?"); + } +}; + +/** + * Animate a Path + * @method animatePath + * @param {object} The object + * @param {Dali.Path} pathObject The path object + * @param {array} forward The path forward vector + * @param {string} alphaFunction The alpha function + * @param {float} delay The delay + * @param {float} duration The duration + */ +dali.Animation.prototype.animatePath = function(object, pathObject, forward, alphaFunction, delay, duration) { + "use strict"; + this.__animatePath(object, pathObject, forward, alphaFunction, delay, duration); +}; + +/** + * animateBetween a value + * @method animateBetween + * @param {object} The object + * @param {string} propertyName The objects property name + * @param {dali.KeyFrames} keyFrames The keyframes + * @param {string} alphaFunction The alpha function + * @param {float} delay The delay + * @param {float} duration The duration + */ +dali.Animation.prototype.animateBetween = function(object, propertyName, keyFrames, alphaFunction, delay, duration, interpolation) { + "use strict"; + var propertyValue; + + var daliKeyFrames = new dali.KeyFrames(); + + for(var i = 0; i < keyFrames.length; i++) { + if(keyFrames[i].length > 2) { // has alpha + propertyValue = dali.DaliPropertyValue(null, null, keyFrames[i][1]); + if(!propertyValue) { + throw new Error("Unknown property?"); + } + daliKeyFrames.add(keyFrames[i][0], propertyValue, keyFrames[i][2]); + propertyValue.delete(); + } else { + propertyValue = dali.DaliPropertyValue(null, null, keyFrames[i][1]); + if(!propertyValue) { + throw new Error("Unknown property?"); + } + daliKeyFrames.add(keyFrames[i][0], propertyValue); + propertyValue.delete(); + } + } + + this.__animateBetween(object, propertyName, daliKeyFrames, alphaFunction, delay, duration, interpolation); + + daliKeyFrames.delete(); + +}; + +//------------------------------------------------------------------------------ +// +// RenderTask Module +// +//------------------------------------------------------------------------------ +dali.RenderTask.prototype.getCameraActor = function() { + "use strict"; + var a = this.__getCameraActor(); + if (a.ok()) { + dali.internalSetupProperties(a); + } + return a; +}; + +Object.defineProperty(dali.RenderTask.prototype, "x", { + enumerable: true, + configurable: false, + get: function() { + return this.getCurrentViewportPosition()[0]; + }, + set: function(v) { + var pos = this.getCurrentViewportPosition(); + this.setViewportPosition(v, pos[1]); + } +}); + +Object.defineProperty(dali.RenderTask.prototype, "y", { + enumerable: true, + configurable: false, + get: function() { + return this.getCurrentViewportPosition()[1]; + }, + set: function(v) { + var pos = this.getCurrentViewportPosition(); + this.setViewportPosition(pos[0], v); + } +}); + +Object.defineProperty(dali.RenderTask.prototype, "width", { + enumerable: true, + configurable: false, + get: function() { + return this.getCurrentViewportSize()[0]; + }, + set: function(v) { + var pos = this.getCurrentViewportSize(); + this.setViewportSize(v, pos[1]); + } +}); + +Object.defineProperty(dali.RenderTask.prototype, "height", { + enumerable: true, + configurable: false, + get: function() { + return this.getCurrentViewportSize()[1]; + }, + set: function(v) { + var pos = this.getCurrentViewportSize(); + this.setViewportSize(pos[0], v); + } +}); + +//------------------------------------------------------------------------------ +// +// Solid Actor Module +// +//------------------------------------------------------------------------------ + +/** + * Create a solid color actor + * @method createSolidColorActor + * @param {array} color The color + * @param {bool} border Whether to add a border + * @param {array} color The border color + * @param {float} borderSize The size of a border + * @return {Dali.Actor} The Dali actor + */ +dali.createSolidColorActor = function(color, border, borderColor, borderSize) { + "use strict"; + var a = dali.__createSolidColorActor(color, border, borderColor, borderSize); + dali.internalSetupProperties(a); + return a; +}; + +//------------------------------------------------------------------------------ +// +// Mesh import support Module +// +//------------------------------------------------------------------------------ +function ObjectLoader(fileObject) { + "use strict"; + // cached + this.self = this; + this.meshByUUID = {}; + this.geomByUUID = {}; + this.matByUUID = {}; + + this.fileObject = fileObject; +} + +function __longToArray(v) { + "use strict"; + return [((v >> 24) & 0xFF) / 255.0, ((v >> 16) & 0xFF) / 255.0, ((v >> 8) & 0xFF) / 255.0, (v & 0xFF) / 255.0]; +} + +function __isBitSet(value, bit) { + "use strict"; + return (value & (1 << bit)); +} + +ObjectLoader.prototype.__getMaterial = function(uuid) { + "use strict"; + if (!(uuid in this.matByUUID)) { + for (var i = 0, len = this.fileObject.materials.length; i < len; i++) { + var f_mat = this.fileObject["materials"][i]; + skewer.log(i + ":" + f_mat["uuid"] + " " + (f_mat["uuid"] === uuid)); + if (f_mat["uuid"] === uuid) { + assert(f_mat["type"] === "MeshPhongMaterial"); + var mat = new dali.MaterialWrapper(uuid); + mat.setDiffuseColor(__longToArray(f_mat["color"])); + mat.setAmbientColor(__longToArray(f_mat["ambient"])); + mat.setSpecularColor(__longToArray(f_mat["specular"])); + mat.setEmissiveColor(__longToArray(f_mat["emmissive"])); + mat.setShininess(f_mat["shininess"]); + this.matByUUID[uuid] = mat; + break; + } + } + } + return this.matByUUID[uuid]; +}; + +ObjectLoader.prototype.__getMeshData = function(uuid, uuid_material) { + "use strict"; + if (!(uuid in this.meshByUUID)) { + for (var i = 0, len = this.fileObject["geometries"].length; i < len; i++) { + var f_geom = this.fileObject["geometries"][i]; + if (f_geom["uuid"] === uuid) { + var f_indices, // file data + f_posns, + f_norms, + f_uvs, + f_faces; + + if (!("metadata" in f_geom)) { + f_geom["metadata"] = { + "type": "" + }; // Warning: modified input!? + } + + if ("formatVersion" in f_geom["metadata"]) // then version 3.1 + { + f_indices = f_geom["indices"]; + f_posns = f_geom["vertices"]; + f_norms = f_geom["normals"]; + f_uvs = f_geom["uvs"]; + f_faces = f_geom["faces"]; + } else if (f_geom["type"] === "Geometry") // V4 clara io output? not standard??? + { + f_indices = f_geom["data"]["indices"]; + f_posns = f_geom["data"]["vertices"]; + f_norms = f_geom["data"]["normals"]; + f_uvs = f_geom["data"]["uvs"]; + f_faces = f_geom["data"]["faces"]; + } else if (f_geom["metadata"]["type"] === "Geometry") // V4 + { + f_indices = f_geom["indices"]; + f_posns = f_geom["vertices"]; + f_norms = f_geom["normals"]; + f_uvs = f_geom["uvs"]; + f_faces = f_geom["faces"]; + } else if (f_geom["metadata"]["type"] === "BufferGeometry") // V4 + { + f_posns = f_geom["data"]["attributes"]["position"]["array"]; + f_norms = f_geom["data"]["attributes"]["norms"]["array"]; + f_uvs = f_geom["data"]["attributes"]["uv"]["array"]; + } + + var nUvLayers = 0; + + // disregard empty arrays + for (var i = 0; i < this.fileObject.uvs.length; i++) { + if (this.fileObject.uvs[i].length) + nUvLayers++; + } + + var verts = new dali.VectorVertex(); + var vert = []; //new dali.Vertex(); + for (var i = 0, len = f_posns.length / 3; i < len; i++) { + vert.push(f_posns[(i * 3) + 0]); + vert.push(f_posns[(i * 3) + 1]); + vert.push(f_posns[(i * 3) + 2]); + + vert.push(0); // norm + vert.push(0); + vert.push(0); + + vert.push(0); // uvs + vert.push(0); + + verts.push_back(vert); + } + + var mesh = new dali.MeshDataWrapper(); + var faces = new dali.VectorFaceIndex(); + var faceSets = {}; + //faceSets.length = this.fileObject.materials; + for (var i = 0, len = this.fileObject.materials.length; i < len; ++i) { + // get materials and force them to be loaded up + var mat = this.__getMaterial(this.fileObject.materials[i]["uuid"]); + } + + var idx = 0; + var idx_len = f_faces.length; + var materialUUID = undefined; + while (idx < idx_len) { + var f_type = f_faces[idx++]; + var isQuad = __isBitSet(f_type, 0); + var hasMaterial = __isBitSet(f_type, 1); + var hasFaceUv = __isBitSet(f_type, 2); + var hasFaceVertexUv = __isBitSet(f_type, 3); + var hasFaceNormal = __isBitSet(f_type, 4); + var hasFaceVertexNormal = __isBitSet(f_type, 5); + var hasFaceColor = __isBitSet(f_type, 6); + var hasFaceVertexColor = __isBitSet(f_type, 7); + + var nVertices = 3; + var faceVertexIndices; + if (isQuad) { + faces.push_back(f_faces[idx]); + faces.push_back(f_faces[idx + 1]); + faces.push_back(f_faces[idx + 2]); + + faces.push_back(f_faces[idx]); + faces.push_back(f_faces[idx + 2]); + faces.push_back(f_faces[idx + 3]); + + faceVertexIndices = [f_faces[idx], + f_faces[idx + 1], + f_faces[idx + 2] + ]; + + idx += 4; + nVertices = 4; + } else { + faces.push_back(f_faces[idx]); + faces.push_back(f_faces[idx + 1]); + faces.push_back(f_faces[idx + 2]); + + faceVertexIndices = [f_faces[idx], + f_faces[idx + 1], + f_faces[idx + 2] + ]; + + idx += 3; + } + + if (hasMaterial) { + if (materialUUID === undefined) { + materialUUID = this.fileObject.materials[f_faces[idx]]["uuid"]; + } else { + // different material per face is bonkers - I'm not going to support it. + if (this.fileObject.materials[f_faces[idx]]["uuid"] !== materialUUID) { + throw "Faces with different materials is not supported"; + } + } + idx++; + } + + + if (hasFaceUv) { + for (var i = 0; i < nUvLayers; i++) { + var uvLayer = self.fileObject.uvs[i]; + var uvIndex = f_faces[idx++]; + var u = uvLayer[uvIndex * 2]; + var v = uvLayer[uvIndex * 2 + 1]; + // discarded - tbd ? + } + } + + if (hasFaceVertexUv) { + for (var i = 0; i < nUvLayers; i++) { + var uvLayer = f_geom.uvs[i]; + var uvs = []; + for (var j = 0; j < nVertices; j++) { + var uvIndex = f_faces[idx++]; + var u = uvLayer[uvIndex * 2]; + var v = uvLayer[uvIndex * 2 + 1]; + // discarded- tbd ? + } + } + } + + if (hasFaceNormal) { + var normalIndex = f_faces[idx++] * 3; + + var x = f_geom.normals[normalIndex++]; + var y = f_geom.normals[normalIndex++]; + var z = f_geom.normals[normalIndex]; + + for (var i = 0; i < faceVertexIndices.length; i++) { + var v = vert.get(faceVertexIndices[i]); + + v[4] += x; + v[5] += y; + v[6] += z; + } + } + + if (hasFaceVertexNormal) { + for (var i = 0; i < nVertices; i++) { + var normalIndex = faces[idx] * 3; + var x = f_geom.normals[normalIndex++]; + var y = f_geom.normals[normalIndex++]; + var z = f_geom.normals[normalIndex]; + + var v = vert.get(faces[idx]); + + v[4] += x; + v[5] += y; + v[6] += z; + + idx += 1; + // face.vertexNormals.push( normal ); + } + } + + if (hasFaceColor) { + var color = f_faces[idx++]; + } + + if (hasFaceVertexColor) { + for (var i = 0; i < nVertices; i++) { + var colorIndex = faces[idx++]; + var color = f_geom.colors[colorIndex]; // ??? f_geom.colors? + // face.vertexColors.push( color ); + } + } + + var faces = null; + if (f_faces) { + for (var i = 1, len = f_faces.length; i < len; i++) { + faces.push_back(f_faces[i]); + } + } + + if (f_indices) { + faces = new dali.VectorFaceIndex(); + for (var i = 1, len = f_indices.length; i < len; i++) { + faces.push_back(f_indices[i]); + } + } + + if (!faces) { + faces = []; + for (var i = 0, len = f_posns.length; i < len; i++) { + faces.push(i); + } + } + + console.log(verts.size() + ":" + faces.size() + ":" + uuid_material); + + var material = this.__getMaterial(uuid_material); + mesh.setLineData(verts, faces, material); + } + + this.meshByUUID[uuid] = mesh; + verts.delete(); + faces.delete(); + break; + } // if uuid found + } // for geom in geometries + } // if uid ! in meshByUUID + + return this.meshByUUID[uuid]; +}; + +ObjectLoader.prototype.delete = function() { + "use strict"; + for (var a in this.meshByUUID) { + a.delete(); + } + this.meshByUUID = {}; + for (var b in this.matByUUID) { + b.delete(); + } + this.matByUUID = {}; +}; + +ObjectLoader.prototype.createMeshActors = function() { + "use strict"; + var ret = []; + if ("object" in this.fileObject) { + for (var i = 0, len = this.fileObject["object"]["children"].length; i < len; i++) { + var child = this.fileObject["children"]; + if (child["type"] === "Mesh") { + var meshData = this.__getMeshData(child["geometry"], + child["material"]); + ret.push(dali.__createMeshActor(meshData)); + meshData.delete(); + } + } + } + + var parent; + + if (ret) { + parent = new dali.Actor(); + for (var a in ret) { + parent.add(a); + a.delete(); + } + } + + return parent; +}; + +dali.createMeshActor = function(threeDjs_formatV4) { + "use strict"; + var loader = new ObjectLoader(threeDjs_formatV4); + return loader.createMeshActor(); +}; + + + +//------------------------------------------------------------------------------ +// +// Hit test +// +//------------------------------------------------------------------------------ +dali.hitTest = function(x, y) { + "use strict"; + var a = dali.__hitTest(x, y); + if (a.ok()) { + dali.internalSetupProperties(a); + return a; + } else { + return null; + } +}; + + +//------------------------------------------------------------------------------ +// +// Shader support +// +//------------------------------------------------------------------------------ + +/** + * ShaderInfo class to get shader metadata. + */ +dali.ShaderInfo = function() { + "use strict"; +}; + +// supported uniforms +dali.ShaderInfo.prototype._supportedUniformTypes = ["bool", + "int", + "float", + "vec2", "vec3", "vec4", + "bvec2", "bvec3", "bvec4", + "ivec2", "ivec3", "ivec4", + "mat2", "mat3", "mat4", + "sampler2D", + "samplerCube" + ]; + +// need to add a value to uniform registration call +dali.ShaderInfo.prototype._supportedUniformValues = [0, + 0, + 0.0, + [0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], + [0, 0], [0, 0, 0], [0, 0, 0, 0], + [0, 0], [0, 0, 0], [0, 0, 0, 0], + [1.0, 0.0, + 0.0, 1.0 + ], + [1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0 + ], + [1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + ] + ]; + + +/** + * Get shader metadata from compilation. + * + * Compiles the shader. On error set 'hasError' and error strings. On Success + * query gl for the attributes and uniforms in the shaders. + * + * @param {object} gl ie from canvas.getContext("webgl") + * @param {string} vertex shader + * @param {string} fragment shader + * @return {Object} shader metadata (see 'var info' below) + */ +dali.ShaderInfo.prototype.fromCompilation = function(gl, vertex, fragment) { + "use strict"; + var i; + var info = { + vertex: vertex, // vertex source code + fragment: fragment, // fragment source code + attributes: {}, // {aName1: {name:"aName1", ... } + uniforms: {}, // {uName1: {name:"uName1", type:"vec2", ...} + uniformUISpec: {}, // {uName1: {ui:"slider", min:0, max:1, ...} + attributeCount: 0, // Number of attributes + uniformCount: 0, // Number of uniforms + hasError: false, // compiles without error + vertexError: "", // Vertex compilation error + fragmentError: "", // Fragment compilation error + linkError: "" // Linker error + }; + + var vertexShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertexShader, vertex); + gl.compileShader(vertexShader); + + // Check the compile status, return an error if failed + if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) { + info.hasError = true; + info.vertexError = gl.getShaderInfoLog(vertexShader); + } + + var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fragmentShader, fragment); + gl.compileShader(fragmentShader); + + // Check the compile status, return an error if failed + if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { + info.hasError = true; + info.fragmentError = gl.getShaderInfoLog(fragmentShader); + } + + if(info.hasError) { + gl.deleteShader(vertexShader); + gl.deleteShader(fragmentShader); + return info; // ==> out + } else { + var program = gl.createProgram(); + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + + gl.linkProgram(program); + + if(!gl.getProgramParameter(program, gl.LINK_STATUS)) { + info.hasError = true; + info.linkError = gl.getProgramInfoLog(program); + gl.deleteProgram(program); + gl.deleteShader(vertexShader); + gl.deleteShader(fragmentShader); + return info; // ==> out + } + } + + var activeUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); + var activeAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); + + // Taken from the WebGl spec: + // http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14 + var enums = { + 0x8B50: "FLOAT_VEC2", + 0x8B51: "FLOAT_VEC3", + 0x8B52: "FLOAT_VEC4", + 0x8B53: "INT_VEC2", + 0x8B54: "INT_VEC3", + 0x8B55: "INT_VEC4", + 0x8B56: "BOOL", + 0x8B57: "BOOL_VEC2", + 0x8B58: "BOOL_VEC3", + 0x8B59: "BOOL_VEC4", + 0x8B5A: "FLOAT_MAT2", + 0x8B5B: "FLOAT_MAT3", + 0x8B5C: "FLOAT_MAT4", + 0x8B5E: "SAMPLER_2D", + 0x8B60: "SAMPLER_CUBE", + 0x1400: "BYTE", + 0x1401: "UNSIGNED_BYTE", + 0x1402: "SHORT", + 0x1403: "UNSIGNED_SHORT", + 0x1404: "INT", + 0x1405: "UNSIGNED_INT", + 0x1406: "FLOAT" + }; + + // Loop through active uniforms + for (i = 0; i < activeUniforms; i++) { + var uniform = gl.getActiveUniform(program, i); + info.uniforms[uniform.name] = {name: uniform.name, + type: uniform.type, + typeName: enums[uniform.type], + size: uniform.size}; + info.uniformCount += uniform.size; + } + + // Loop through active attributes + for (i = 0; i < activeAttributes; i++) { + var attribute = gl.getActiveAttrib(program, i); + info.attributes[attribute.name] = {name: attribute.name, + type: attribute.type, + typeName: enums[attribute.type], + size: attribute.size}; + info.attributeCount += attribute.size; + } + + // uniformUISpec + this._addUniformMetaData(vertex, info); + this._addUniformMetaData(fragment, info); + + return info; +}; + +/* + * add unform metadata from shader source comments + * ie return as an object the comment following a uniform + * uniform float uAlpha; // {"min":0, "max":1} + */ +/** private */ +dali.ShaderInfo.prototype._addUniformMetaData = function(src, metadata) { + "use strict"; + // Loop through active uniforms + for(var name in metadata.uniforms) { + var reguniform = new RegExp(name + "[^;]*;(.*)"); + + var tmp = reguniform.exec(src); + if(tmp && tmp[1]) { + var meta; + var uComments = tmp[1].trim(); + if(uComments.startsWith("//")) { // meta data in comments + try { + meta = eval("(" + uComments.substr(2) + ")"); // brackets to be expression not opening statement + if(typeof meta !== typeof ({})) { + throw ("Uniform UI Spec in comments must be an object"); + } + } catch (e) { + meta = {}; + } + } else { + meta = {}; + } + metadata.uniformUISpec[name] = meta; + } + } +}; + +/** + * Get shader metadata from regex search. + * + * Attempts a regex search to get the shader meta data. + * Use fromCompilation() instead of this function wherever compilation is + * possible as this approach will never work for all shaders. + * Does no compilation or error checking but retains fields for + * compatibility with .fromCompilation(...) + * May return an error if the regex fails. + * + * @param {string} vertex shader + * @param {string} fragment shader + * @return {Object} shader metadata (see 'var info' below) + */ +dali.ShaderInfo.prototype.fromRegEx = function(vertex, fragment) { + "use strict"; + var info = { // similar to this.fromCompilation() + vertex: vertex, // source code + fragment: fragment, + attributes: {}, // {aName1: {name:"aName1", ... } + uniforms: {}, // {uName1: {name:"uName1", type:"vec2", ...} + attributeCount: 0, + uniformCount: 0, + uniformUISpec: {}, // {uName1: {ui:"slider", min:0, max:1, ...} + hasError: false, // compiles without error + vertexError: "", + fragmentError: "", + linkError: "" + }; + + var metaVertex; + try { + metaVertex = this._getRegExMetaData(vertex); + } catch(e) { + info.hasError = true; + info.vertexError = e.message; + return info; + } + + var metaFragment; + try { + metaFragment = this._getRegExMetaData(fragment); + } catch(e) { + info.hasError = true; + info.fragmentError = e.message; + return info; + } + + var name; + + // merge + info.uniforms = metaVertex.uniformMetaData; + info.uniformUISpec = metaVertex.uniformUISpec; + + for(name in metaFragment.uniformMetaData) { + if( name in info.uniforms ) { + info.uniforms[name] = dali.mergeObjects(info.uniforms[name], metaVertex.uniformMetaData); + } else { + info.uniforms[name] = metaFragment.uniformMetaData[name]; + } + if( name in info.uniformUISpec ) { + info.uniformUISpec[name] = dali.mergeObjects(info.uniformUISpec[name], metaVertex.uniformUISpec); + } else { + info.uniformUISpec[name] = metaFragment.uniformUISpec[name]; + } + } + + info.attributes = metaVertex.attributeMetaData; + for(name in metaFragment.attributeMetaData) { + if( name in metaVertex.attributeMetaData ) { + info.attributes[name] = dali.mergeObjects(info.attributes[name], metaVertex.attributeMetaData); + } else { + info.attributes[name] = metaFragment.attributeMetaData[name]; + } + } + + return info; +}; + +/* + * Returns a string with all comments removed + */ +/** private */ +dali.ShaderInfo.prototype._removeComments = function(str) { + "use strict"; + var uid = "_" + new Date(), + primatives = [], + primIndex = 0; + + return ( + str + /* Remove strings */ + .replace(/(['"])(\\\1|.)+?\1/g, function(match){ + primatives[primIndex] = match; + return (uid + "") + primIndex++; + }) + + /* Remove Regexes */ + .replace(/([^\/])(\/(?!\*|\/)(\\\/|.)+?\/[gim]{0,3})/g, function(match, $1, $2){ + primatives[primIndex] = $2; + return $1 + (uid + "") + primIndex++; + }) + + /* + - Remove single-line comments that contain would-be multi-line delimiters + E.g. // Comment /* <-- + - Remove multi-line comments that contain would be single-line delimiters + E.g. /* // <-- + */ + .replace(/\/\/.*?\/?\*.+?(?=\n|\r|$)|\/\*[\s\S]*?\/\/[\s\S]*?\*\//g, "") + + /* + Remove single and multi-line comments, + no consideration of inner-contents + */ + .replace(/\/\/.+?(?=\n|\r|$)|\/\*[\s\S]+?\*\//g, "") + + /* + Remove multi-line comments that have a replace ending (string/regex) + Greedy, so no inner strings/regexes will stop it. + */ + .replace(RegExp("\\/\\*[\\s\\S]+" + uid + "\\d+", "g"), "") + + /* Bring back strings & regexes */ + .replace(RegExp(uid + "(\\d+)", "g"), function(match, n){ + return primatives[n]; + }) + ); +}; + +/* + * Returns true if value is in the array + */ +/** private */ +dali.ShaderInfo.prototype._contains = function(array, value) { + "use strict"; + for(var i = 0; i < array.length; i++) { + if(array[i] === value) { + return true; + } + } + // else + return false; +}; + +/* + * Get the src meta data for unforms and attributes armed only with a regexp + */ +/** private */ +dali.ShaderInfo.prototype._getRegExMetaData = function(src) { + "use strict"; + var ret = {"uniforms": [], // ["uName1", ["uName2"] + "uniformMetaData": {}, // {uName1: {type:"vec3,...} + "uniformUISpec": {}, // {ui:"slider", min:..., max:...} + "attributes": [], // ["aName2"] + "attributeMetaData": {} // ["aName2"] + }; + + // Undoubtedly this approach will be wrong. Hopefully on not too many corner cases... + // A better way is to compile the source see (fromCompilation()) + // but that requres a gl context. + var tmp; + + var definesOut = /#define[ \t]+([A-Za-z_0-9]*)[ \t]+(.*)/g; + + var reg = /[ \t]?uniform[ ]*((?:lowp|mediump|highp)?[ \t]*(bool|int|uint|float|[biu]?vec[234]|mat[234]x?[234]?|[^ ]*)[ \t]*([A-Za-z0-9]*))[ \t]*(;|\[.*\][ \t]*;)(.*)/gi; + + var regAttrib = /[ \t]?attribute[ ]*((?:lowp|mediump|highp)?[ \t]*(bool|int|uint|float|[biu]?vec[234]|mat[234]x?[234]?|[^ ]*)[ \t]*([A-Za-z0-9]*))[ \t]*(;|\[.*\][ \t]*;)(.*)/gi; + + // 1. no commented out uniforms + var noCommentSource = this._removeComments(src); + + var validUniforms = []; + while ((tmp = reg.exec(noCommentSource))) { + validUniforms.push( tmp[3] ); + } + + while ((tmp = regAttrib.exec(noCommentSource))) { + ret.attributes.push( tmp[3] ); + ret.attributeMetaData[ tmp[3] ] = {name: tmp[3], type: tmp[2] }; + } + + // 2. replace defines + var defines = []; + while ((tmp = definesOut.exec(noCommentSource))) { + defines.push([tmp[1], tmp[2]]); + } + var defineDict = {}; + var defineList = []; + while(defines.length) { + var p = defines.pop(); + var n = p[0]; + var v = p[1]; + try { + defineDict[n] = eval(v); + defineList.push([n, defineDict[n]]); + } catch(e) { + var d = /([A-Za-z]+[A-Za-z0-9]*)/g; + while ((tmp = d.exec(v))) { + if(tmp[0] in defineDict) { + v = v.replace(tmp[0], defineDict[tmp[0]]); + } else { + defines.push(p); // stick it back to try again. ...and endless loop if we can't(!) + } + } + } + } + + for(var i = 0; i < defineList.length; i++) { + var re = new RegExp(defineList[i][0], "g"); + src = src.replace(re, defineList[i][1]); + } + + // 3. get uniforms + while ((tmp = reg.exec(src))) { + if(!this._contains(validUniforms, tmp[3])) { + continue; + } + var uType = tmp[2]; + var uName = tmp[3]; + var uArray = tmp[4].slice(0, -1); + var uComments = tmp[5].trim(); + var meta; + var uiSpecMeta = null; + if(uComments.startsWith("//")) { // meta data in comments + uiSpecMeta = eval("(" + uComments.substr(2) + ")"); // brackets to be expression not opening statement + if(typeof uiSpecMeta !== typeof ({})) { + throw ("Uniform UI Spec in comments must be an object"); + } + } + + if(uiSpecMeta) { + uiSpecMeta.name = tmp[3]; + ret.uniformUISpec[uName] = uiSpecMeta; + } + + meta = {}; + meta.type = tmp[2]; + meta.name = tmp[3]; + meta.count = 0; + + var name; + if(uArray.search("[[]") >= 0) { // an array + meta.count = Number(uArray.slice(1, -1)); + } + + if(this._contains( this._supportedUniformTypes, uType) ) { + if(meta.count !== 0) { // array + for(var j = 0; j < meta.count; j++) { + ret.uniforms.push( meta.name ); + ret.uniformMetaData[ meta.name ] = {type: meta.type, + name: meta.name + "[" + j + "]", + index: j, + count: meta.count}; + } + } else { + ret.uniforms.push( meta.name ); + ret.uniformMetaData[ meta.name ] = {type: meta.type, + name: meta.name, + index: 0, + count: 0}; + } + } else { + // not a base type so need to get the compound type + var structFind = new RegExp( "(struct[ \t\n]*" + uType + "[^{]*{)([^}]*)", "g"); + var structLines = structFind.exec(src)[2].split(";"); + var structUniforms = []; + var tmpStruct; + var k; + for(var lineNo = 0; lineNo < structLines.length; lineNo++) { + var line = structLines[lineNo].replace(/\n/g, "") + ";"; + if(line !== ";") { + var structReg = /[ \t\n]*((?:lowp|mediump|highp)?[ \t\n]*(bool|int|uint|float|[biu]?vec[234]|mat[234]x?[234]?|[^ ]*)[ \t\n]*([A-Za-z0-9]*))[ \t\n]*(;|\[.*\][ \t\n]*;)/gi; + while ((tmpStruct = structReg.exec(line))) { + structUniforms.push( { type: tmpStruct[2], + name: tmpStruct[3], + count: meta.count } ); + } + } + } + if(meta.count === 0) { + for(k = 0; k < structUniforms.length; k++) { + name = uName + "." + structUniforms[k].name; + ret.uniforms.push( name ); + ret.uniformMetaData[ name ] = {type: structUniforms[k].type, + name: name, + count: meta.count, + index: 0, + structType: meta.type, + structName: meta.name}; + } + } else { // array + for(var l = 0; l < meta.count; l++) { + for(k = 0; k < structUniforms.length; k++) { + name = uName + "[" + l + "]" + "." + structUniforms[k].name; + ret.uniforms.push( name ); + ret.uniformMetaData[ name ] = {type: structUniforms[k].type, + name: name, + count: meta.count, + index: l, + structType: meta.type, + structName: meta.name}; + } + } + } + } + } + + return ret; +}; + +//------------------------------------------------------------------------------ +// +// Debug Module +// +//------------------------------------------------------------------------------ +dali.Debug = function() { + "use strict"; +}; + +dali.Debug.prototype.printTypeProperties = function(typeName) { + "use strict"; + var t = new dali.TypeRegistry(); + var info = t.getTypeInfo(typeName); + var props = info.getProperties(); + for (var i = 0; i < props.size(); i++) { + console.log(i + ":" + props.get(i)); + } + info.delete(); // wrapper + t.delete(); // wrapper +}; + +dali.Debug.prototype.printProperties = function(o) { + "use strict"; + var props = o.getProperties(); + + var len = props.size(); + for(var i = 0; i < len; i++) { + var name = props.get(i); + var type = o.getPropertyTypeName(name); + if(type !== "NONE") { + console.log(i + ":" + name + " " + type); + } else { + type = o.getPropertyTypeName(name); + console.log(i + ":" + name + " " + type + " (Not mangled)"); + } + } + props.delete(); // wrapper +}; + +dali.Debug.prototype.printTypes = function() { + "use strict"; + + var t = new dali.TypeRegistry(); + for (var i = 0; i < t.getTypeNameCount(); i++) { + console.log(t.getTypeName(i)); + } + t.delete(); // wrapper +}; + + +dali._debugPrintParents = function(actor, list) { + "use strict"; + var p = null; + + if (!actor.ok()) { + return; + } + + try { + p = actor.getParent(); + if (!p.ok()){ + p = null; + } + } catch (e) { + // console.log("Cannot get parent", e); + } + + if (p) { + list.push(p); + dali._debugPrintParents(p, list); + } +}; + +dali.Debug.prototype.printTree = function(actor) { + "use strict"; + var l = []; + dali._debugPrintParents(actor, l); + var a; + var ti; + console.log("---"); + for (var i = l.length - 1; i >= 0; i--) { + a = l[i]; + ti = a.getTypeInfo(); + console.log("|", Array(l.length - i).join("-"), ti.getName(), "P", a.position, "R", a.orientation, a.name); + ti.delete(); + } + ti = actor.getTypeInfo(); + console.log("*", Array(l.length + 1).join("*"), ti.getName(), "P", actor.position, "R", actor.orientation, actor.name); + ti.delete(); + + var children = actor.getChildren(); + for (var j = 0; j < children.length; j++) { + a = children[j]; + ti = a.getTypeInfo(); + console.log("|", Array(l.length + 1 + 1 + j).join("-"), ti.getName(), "P", a.position, "R", a.orientation, a.name); + ti.delete(); + } +}; + +dali.Debug.prototype.printRenderTask = function(rendertask) { + "use strict"; + console.log("[X,Y]", rendertask.getCurrentViewportPosition()); + console.log("[W,H]", rendertask.getCurrentViewportSize()); + + var c = rendertask.getCameraActor(); + if (!c.ok()) { + console.log("No Camera"); + } else { + console.log("Camera Pos:", c.position); + console.log("Camera Rot:", c.orientation); + console.log("Camera Inherit:", c.inheritRotation); + console.log("Camera ParentOrigin:", c.parentOrigin); + console.log("Camera AnchorPoint:", c.anchorPoint); + var p = null; + try { + p = c.getParent(); + if(!p.ok()) { + p = null; + } + } catch (e) { + console.log("Cannot get parent", e); + } + + if (!p) { + console.log("Camera has no parent?"); + } else { + var ti = p.getTypeInfo(); + console.log("Parent Name", ti.getName()); + ti.delete(); + p.delete(); + } + } +}; + +dali.Debug.prototype.printRenderTasks = function() { + "use strict"; + var stage = dali.stage; + var taskList = stage.getRenderTaskList(); + for (var i = 0; i < taskList.getTaskCount(); i++) { + var t = taskList.getTask(i); + console.log("RenderTask:", i); + this.printRenderTask(t); + t.delete(); // wrapper + } + taskList.delete(); // wrapper +}; + +dali.Debug.prototype.findFirstActor = function(actor, predicateFunction) { + "use strict"; + for (var i = 0, len = actor.getChildCount(); i < len; i++) { + var a = actor.getChildAt(i); + var found = predicateFunction(a); + if (found) { + return a; + } + var child = this.findFirstActor(a, predicateFunction); + if (child) { + return child; + } + a.delete(); + } + return null; +}; + +dali.Debug.prototype.depthVisit = function(actor, operation, dontDelete) { + "use strict"; + for (var i = 0, len = actor.getChildCount(); i < len; i++) { + var a = actor.getChildAt(i); + var done = operation(a); + if (!done) { + return false; + } + if (!this.depthVisit(a, operation, dontDelete)) { + return false; + } + var doit = true; + if (dontDelete !== undefined) { + if (dontDelete) { + doit = false; + } + } + if (doit) { + a.delete(); + } + } + return true; +}; + +dali.operationPrintProperty = function(property, all) { + "use strict"; + return (function(actor) { + if (property in actor) { + dali.log(actor.getId() + "property:" + actor[property]); + } else { + dali.log(actor.getId() + "property:n/a"); + } + return all; + }); +}; + +dali.predicatePropertyEquals = function(property, value) { + "use strict"; + return (function(actor) { + if (property in actor) { + if (actor[property] === value) { + return true; + } + } + return false; + }); +}; + +dali.typeInheritsFrom = function(type, basename) { + var inherits = false; + + var registry = new dali.TypeRegistry(); + + var base = registry.getTypeInfo( type.getBaseName() ); + + if(base.ok()) + { + inherits = (base.getName() === basename); + + while(!inherits) + { + base = registry.getTypeInfo( base.getBaseName() ); + if(base.ok()) + { + inherits = (base.getName() === basename); + } + else + { + break; + } + } + } + + return inherits; +}; + + + +//------------------------------------------------------------------------------ +// +// View Module +// +// Helper functions for creating front/top/left views with RenderTasks +// +//------------------------------------------------------------------------------ + +/** + * Sets the clear colour in a RenderTask + * @method setClearColor + * @param {int} renderTaskIndex + * @param {array} color The rgba colour array + */ +dali.setClearColor = function(renderTaskIndex, color) { + "use strict"; + var stage = dali.stage; + var taskList = stage.getRenderTaskList(); + if (renderTaskIndex >= taskList.getTaskCount()) { + console.log("RenderTaskIndex out of bounds:", renderTaskIndex); + taskList.delete(); // wrapper + return; + } + var rendertask = taskList.getTask(renderTaskIndex); + rendertask.setClearEnabled(true); + rendertask.setClearColor(color); +}; + +/** + * Gets the clear colour of a RenderTask + * @method setClearColor + * @param {int} renderTaskIndex + * @return {array} The rgba colour array + */ +dali.getClearColor = function(renderTaskIndex) { + "use strict"; + var stage = dali.stage; + var taskList = stage.getRenderTaskList(); + if (renderTaskIndex >= taskList.getTaskCount()) { + console.log("RenderTaskIndex out of bounds:", renderTaskIndex); + taskList.delete(); // wrapper + return null; + } + var rendertask = taskList.getTask(renderTaskIndex); + return rendertask.getClearColor(); +}; + +/** + * Set a front view camera with viewport x,y,w,h + * @method setFrontView + * @param {int} renderTaskIndex + * @param {int} x Viewport X + * @param {int} y Viewport Y + * @param {int} w Viewport W + * @param {int} h Viewport H + */ +dali.setFrontView = function(renderTaskIndex, x, y, w, h) { + "use strict"; + var stage = dali.stage; + var taskList = stage.getRenderTaskList(); + if (renderTaskIndex >= taskList.getTaskCount()) { + console.log("RenderTaskIndex out of bounds:", renderTaskIndex); + taskList.delete(); // wrapper + return; + } + var rendertask = taskList.getTask(renderTaskIndex); + + var c = rendertask.getCameraActor(); + assert(c.ok(), "Rendertask has no valid camera actor"); + + rendertask.setViewportPosition([x, y]); + rendertask.setViewportSize([w, h]); + c.position = [0, 0, 800]; + c.orientation = [0, 1, 0, 180]; + c.aspectRatio = w / h; + + c.delete(); // wrapper + rendertask.delete(); // wrapper + taskList.delete(); // wrapper +}; + +/** + * Set a top view camera with viewport x,y,w,h + * @method setTopView + * @param {int} renderTaskIndex + * @param {int} x Viewport X + * @param {int} y Viewport Y + * @param {int} w Viewport W + * @param {int} h Viewport H + */ +dali.setTopView = function(renderTaskIndex, x, y, w, h) { + "use strict"; + var stage = dali.stage; + var taskList = stage.getRenderTaskList(); + if (renderTaskIndex >= taskList.getTaskCount()) { + console.log("RenderTaskIndex out of bounds:", renderTaskIndex); + taskList.delete(); // wrapper + return; + } + var rendertask = taskList.getTask(renderTaskIndex); + + var c = rendertask.getCameraActor(); + assert(c.ok(), "Rendertask has no valid camera actor"); + + rendertask.setViewportPosition([x, y]); + rendertask.setViewportSize([w, h]); + + var q1 = dali.axisAngleToQuaternion([0, 1, 0, dali.radian(180)]); // yaw around to look at scene down -ve z + var q2 = dali.axisAngleToQuaternion([1, 0, 0, dali.radian(-90)]); // pitch to look at scene + var q = dali.quaternionToAxisAngle(dali.quatByQuat(q1, q2)); + + c.position = [0, -800, 0]; // @todo; get 800 from dali not hard coded here + c.orientation = [q[0], q[1], q[2], dali.degree(q[3])]; // @todo; should really all be in radians + c.aspectRatio = w / h; + + c.delete(); // wrapper + rendertask.delete(); // wrapper + taskList.delete(); // wrapper +}; + +/** + * Set a right view camera with viewport x,y,w,h + * @method setRightView + * @param {int} renderTaskIndex + * @param {int} x Viewport X + * @param {int} y Viewport Y + * @param {int} w Viewport W + * @param {int} h Viewport H + */ +dali.setRightView = function(renderTaskIndex, x, y, w, h) { + "use strict"; + var stage = dali.stage; + var taskList = stage.getRenderTaskList(); + if (renderTaskIndex >= taskList.getTaskCount()) { + console.log("RenderTaskIndex out of bounds:", renderTaskIndex); + taskList.delete(); // wrapper + return; + } + var rendertask = taskList.getTask(renderTaskIndex); + + var c = rendertask.getCameraActor(); + assert(c.ok(), "Rendertask has no valid camera actor"); + + rendertask.setViewportPosition([x, y]); + rendertask.setViewportSize([w, h]); + + var q1 = dali.axisAngleToQuaternion([0, 1, 0, dali.radian(180)]); // yaw around to look at scene down -ve z + var q2 = dali.axisAngleToQuaternion([0, 1, 0, dali.radian(90)]); // yaw again to look from right + var q = dali.quaternionToAxisAngle(dali.quatByQuat(q1, q2)); + + c.position = [800, 0, 0]; + c.orientation = [q[0], q[1], q[2], dali.degree(q[3])]; // @todo; should really all be in radians + c.aspectRatio = w / h; + + c.delete(); // wrapper + rendertask.delete(); // wrapper + taskList.delete(); // wrapper +}; + +/** + * Remove all but one render task. Presumes RenderTasks are being use only for viewing windows. + * @method onePane + */ +dali.onePane = function() { + "use strict"; + var stage = dali.stage; + var taskList = stage.getRenderTaskList(); + var tasks = []; + var i, len; + + for (i = 1, len = taskList.getTaskCount(); i < len; i++) { + tasks.push(taskList.getTask(i)); + } + + for (i = 0, len = tasks.length; i < len; i++) { + var task = tasks[i]; + // delete the camera actors we created in twoPane and threePane + var c = task.getCameraActor(); + if (c.ok()) { + var p = c.getParent(); + if (p.ok()) { + p.remove(c); + } + p.delete(); // wrapper + } + c.delete(); // wrapper + + taskList.removeTask(task); + task.delete(); // wrapper + } + + taskList.delete(); +}; + +/** + * Creates render tasks and cameras for a two pane view. + * Use setFrontView/Top/Right with 0-2 index to setup the actual views. + * (in a separate function to allow window gutters) + * @method twoPane + */ +dali.twoPane = function() { + "use strict"; + dali.onePane(); + + var stage = dali.stage; + var taskList = stage.getRenderTaskList(); + + var defaultTask = taskList.getTask(0); + var defaultCamera = defaultTask.getCameraActor(); + var defaultCameraParent = defaultCamera.getParent(); + + var t; + t = taskList.createTask(); + + var c = new dali.CameraActor(); // add camera for different viewpoint + c.position = [0, 0, 800]; + c.orientation = [0, 1, 0, 180]; + c.parentOrigin = [0.5, 0.5, 0.5]; + c.anchorPoint = [0.5, 0.5, 0.5]; + t.setCameraActor(c); + defaultCameraParent.add(c); + c.delete(); // wrapper + + t.delete(); // wrapper + + defaultCameraParent.delete(); // wrapper + defaultCamera.delete(); // wrapper + defaultTask.delete(); // wrapper + + taskList.delete(); // wrapper +}; + +/** + * Creates render tasks and cameras for a three pane view. + * Use setFrontView/Top/Right with 0-2 index to setup the actual views. + * (in a separate function to allow window gutters) + * @method threePane + */ +dali.threePane = function() { + "use strict"; + dali.onePane(); + + var stage = dali.stage; + var taskList = stage.getRenderTaskList(); + + var defaultTask = taskList.getTask(0); + var defaultCamera = defaultTask.getCameraActor(); + var defaultCameraParent = defaultCamera.getParent(); + + var t; + t = taskList.createTask(); + + var c = new dali.CameraActor(); // add camera for different viewpoint + c.position = [0, 0, 800]; + c.orientation = [0, 1, 0, 180]; + c.parentOrigin = [0.5, 0.5, 0.5]; + c.anchorPoint = [0.5, 0.5, 0.5]; + t.setCameraActor(c); + defaultCameraParent.add(c); + c.delete(); // wrapper + + t.delete(); // wrapper + + t = taskList.createTask(); + + c = new dali.CameraActor(); // add camera for different viewpoint + c.position = [0, 0, 800]; + c.orientation = [0, 1, 0, 180]; + c.parentOrigin = [0.5, 0.5, 0.5]; + c.anchorPoint = [0.5, 0.5, 0.5]; + t.setCameraActor(c); + defaultCameraParent.add(c); + c.delete(); // wrapper + + t.delete(); // wrapper + + defaultCameraParent.delete(); // wrapper + defaultCamera.delete(); // wrapper + defaultTask.delete(); // wrapper + + taskList.delete(); // wrapper +}; + +//------------------------------------------------------------------------------ +// +// Dali Initialization Module +// +//------------------------------------------------------------------------------ + +/** + * Create a Dali object by type name + * @method create + * @param {string} name The type name to create + * @return A Dali handle to the Dali object + */ +dali.create = function(name) { + "use strict"; + + var handle = dali.__createActor(name); + + if (!handle.ok()) { + handle.delete(); // handle + handle = dali.__createHandle(name); + } + + dali.internalSetupProperties(handle); + + return handle; +}; + +/** + * Creates constructors for objects found in the TypeRegistry. Some objects are + * individually wrapped. Sets some global objects eg. debug/stage. + */ +/** private */ +dali.init = function() { + "use strict"; + + console.log( dali.VersionString() ); + + dali.jsSignalHolder = new dali.SignalHolder(); // for js callbacks + + dali.debug = new dali.Debug(); + + dali.stage = new dali.Stage(); + + dali.getStage = function() { // duplication of dali.stage to stop regressions + return dali.stage; + }; + + // + // Add constructor functions to dali from the type registry + // + // Uses separate create functions to add methods for the different base classes. + // Other generic access is by properties. Currently + // + // +------------+ + // | BaseHandle | + // +------+-----+ + // | + // |--------------+------------| + // | | | + // +----+------+ +----+---+ +----+--+ + // | Animation | | Handle | | Image | + // +-----------+ +--------+ +-------+ + // + + var t = new dali.TypeRegistry(); + + // use the emscripten wrapping for these and not the typeregisitry creation function + var useWrapping = { RenderTask: 1, RenderTaskList: 1, CameraActor: 1, + TypeInfo: 1, + Path: 1, Animation: 1, + Handle: 1, Actor: 1, + PropertyMap: 1, PropertyBuffer: 1, + ShaderEffect: 1, + Image: 1, BufferImage: 1, EncodedBufferImage: 1, + Geometry: 1, Material: 1, Shader: 1, Sampler: 1, Renderer: 1 + }; + + for (var i = 0; i < t.getTypeNameCount(); i++) { + // anon function because of closure with defineProperty + // (if just variable in loop then the variable 'address' is captured, not the value + // so it becomes last value set) + (function(name) { + var createFunc; + var info = t.getTypeInfo(name); + + if(dali.typeInheritsFrom(info, "Actor")) { + createFunc = dali.__createActor; + } else if(dali.typeInheritsFrom(info, "Handle")) { + createFunc = dali.__createHandle; + } + + // @todo Dali error?? name lengths should never be zero + if (name.length && !(name in useWrapping) ) { + Object.defineProperty(dali, name, { + enumerable: true, + configurable: false, + get: function() { + return function() { + // console.log(name); + return dali.create(name); + }; + } + }); + } + })(t.getTypeName(i)); + } + + dali.__updateOnce(); + dali.__renderOnce(); + +}(); // call init + +//------------------------------------------------------------------------------ +// +// Post run +// +// Call postDaliWrapperRun() to indicate dali-wrapper.js has loaded +// and other sequential tasks can run (js files can load async) +// +//------------------------------------------------------------------------------ +if(Module) +{ + if (Module.postDaliWrapperRun) { + Module.postDaliWrapperRun(); + } +} diff --git a/adaptors/emscripten/wrappers/emscripten-utils.cpp b/adaptors/emscripten/wrappers/emscripten-utils.cpp new file mode 100644 index 000000000..7ba051cf8 --- /dev/null +++ b/adaptors/emscripten/wrappers/emscripten-utils.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "emscripten-utils.h" + +// EXTERNAL INCLUDES + + +// INTERNAL INCLUDES + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +Dali::Image CreateImageRGBA(unsigned int width, unsigned int height, const std::string& data) +{ + Dali::BufferImage b = Dali::BufferImage::New( width, height, Dali::Pixel::RGBA8888 ); + + const Dali::PixelBuffer* from = reinterpret_cast( data.c_str() ); + Dali::PixelBuffer* to = b.GetBuffer(); + + unsigned int len = std::max( width * height * 4, data.size() ); + for(int i = 0; i < len; i++) + { + *to++ = *from++; + } + return b; +} + +Dali::Image CreateImageRGB(unsigned int width, unsigned int height, const std::string& data) +{ + Dali::BufferImage b = Dali::BufferImage::New( width, height, Dali::Pixel::RGB888 ); + + const Dali::PixelBuffer* from = reinterpret_cast( data.c_str() ); + Dali::PixelBuffer* to = b.GetBuffer(); + + unsigned int len = std::max( width * height * 3, data.size() ); + for(int i = 0; i < len; i++) + { + *to++ = *from++; + } + return b; +} + +Dali::Image GetImage(const std::string& data) +{ + const uint8_t* const ptr = reinterpret_cast(data.c_str()); + return Dali::EncodedBufferImage::New(ptr, data.size()); +} + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali diff --git a/adaptors/emscripten/wrappers/emscripten-utils.h b/adaptors/emscripten/wrappers/emscripten-utils.h new file mode 100644 index 000000000..ac8f60485 --- /dev/null +++ b/adaptors/emscripten/wrappers/emscripten-utils.h @@ -0,0 +1,71 @@ +#ifndef __DALI_EMSCRIPTEN_UTILS_H__ +#define __DALI_EMSCRIPTEN_UTILS_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include +#include +#include "emscripten/val.h" + +// INTERNAL INCLUDES + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +/** + * Creates a Dali RGBA Image from raw bytes + * + * @param[in] width The image width + * @param[in] height The image height + * @param[in] data The byte data + * @returns The Dali Image + * + */ +Dali::Image CreateImageRGBA(unsigned int width, unsigned int height, const std::string& data); + +/** + * Creates a Dali RGB Image from raw bytes + * + * @param[in] width The image width + * @param[in] height The image height + * @param[in] data The byte data + * @returns The Dali Image + * + */ +Dali::Image CreateImageRGB(unsigned int width, unsigned int height, const std::string& data); + +/** + * Creates a dali Image from encoded bytes (ie jpg/png etc) + * + * @param[in] data The byte data + * + * @returns The Dali Image + * + */ +Dali::Image GetImage(const std::string& data); + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali + +#endif // header diff --git a/adaptors/emscripten/wrappers/handle-wrapper.cpp b/adaptors/emscripten/wrappers/handle-wrapper.cpp new file mode 100644 index 000000000..427211473 --- /dev/null +++ b/adaptors/emscripten/wrappers/handle-wrapper.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "handle-wrapper.h" + +// EXTERNAL INCLUDES +#include +#include + +// INTERNAL INCLUDES +#include "type-info-wrapper.h" +#include "property-value-wrapper.h" + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +bool BaseHandleOk(Dali::BaseHandle& self) +{ + return self; +} + +void SetSelf(Dali::Handle& self, Dali::Handle& other) +{ + self = other; +} + +void SetProperty(Dali::Handle& self, const std::string& name, const Dali::Property::Value& value) +{ + DALI_ASSERT_ALWAYS(self); + if( self ) + { + Dali::Property::Index index = self.GetPropertyIndex(name); + + if( Dali::Property::INVALID_INDEX != index ) + { + self.SetProperty(index, value); + } + else + { + printf("ERR Invalid property name:%s", name.c_str()); + EM_ASM( throw "Invalid property name (HandleWrapper::SetProperty)" ); + } + } + else + { + EM_ASM( throw "ActorWrapper has no actor" ); + } + +} + +Dali::Property::Value GetProperty(Dali::Handle& self, const std::string& name) +{ + DALI_ASSERT_ALWAYS(self); + Dali::Property::Value ret; + if( self ) + { + Dali::Property::Index index = self.GetPropertyIndex(name); + + if( Dali::Property::INVALID_INDEX != index ) + { + ret = self.GetProperty(index); + } + else + { + printf("ERR Invalid property name:%s", name.c_str()); + EM_ASM( throw new Error("Invalid property name (HandleWrapper::GetProperty)") ); + } + } + else + { + EM_ASM( throw new Error("ActorWrapper has no actor") ); + } + + return ret; +} + +int GetPropertyIndex(Dali::Handle& self, const std::string& name) +{ + if( self ) + { + Dali::Property::Index index = self.GetPropertyIndex(name); + + return (int)index; // self.GetPropertyIndex(name); + } + + return -1; +} + +std::vector GetProperties(Dali::Handle& self) +{ + Dali::Property::IndexContainer indices; + self.GetPropertyIndices( indices ); + std::vector names; + for(Dali::Property::IndexContainer::Iterator iter(indices.Begin()); iter != indices.End(); ++iter) + { + std::string name = self.GetPropertyName( *iter ); + + names.push_back(name); + } + return names; +} + +std::string GetPropertyTypeName(Dali::Handle& self, const std::string& name) +{ + if(self) + { + Dali::Property::Index index = self.GetPropertyIndex(name); + if(Dali::Property::INVALID_INDEX != index) + { + return Dali::PropertyTypes::GetName(self.GetPropertyType(index)); + } + } + + // if we got here + return Dali::PropertyTypes::GetName(Dali::Property::NONE); +} + +Dali::Property::Type GetPropertyTypeFromName(Dali::Handle& self, const std::string& name) +{ + Dali::Property::Type type = Dali::Property::NONE; + + if(self) + { + Dali::Property::Index index = self.GetPropertyIndex(name); + if(Dali::Property::INVALID_INDEX != index) + { + type = self.GetPropertyType(index); + } + } + + return type; +} + +Dali::Property::Index RegisterProperty(Dali::Handle& self, const std::string& name, const Dali::Property::Value& propertyValue) +{ + Dali::Property::Index ret = Dali::Property::INVALID_INDEX; + + Dali::Property::Type type = propertyValue.GetType(); + if(Dali::Property::ARRAY == type || Dali::Property::MAP == type) + { + // these types would need support in the javascript side of the wrapper + EM_ASM( throw "Property type not supported" ); + } + + if(self) + { + ret = self.RegisterProperty(name, propertyValue, Dali::Property::AccessMode::READ_WRITE); + } + return ret; +} + +Dali::TypeInfo GetTypeInfo(Dali::Handle& self) +{ + Dali::TypeInfo ret; + if( self ) + { + self.GetTypeInfo(ret); + } + return ret; +} + +Dali::Property::Index RegisterAnimatedProperty(Dali::Handle& self, const std::string& name, const Dali::Property::Value& propertyValue) +{ + Dali::Property::Index ret = Dali::Property::INVALID_INDEX; + + Dali::Property::Type type = propertyValue.GetType(); + if(Dali::Property::ARRAY == type || Dali::Property::MAP == type) + { + // these types would need support in the javascript side of the wrapper + EM_ASM( throw "Property type not supported" ); + } + + if(self) + { + ret = self.RegisterProperty(name, propertyValue, Dali::Property::AccessMode::ANIMATABLE); + } + return ret; +} + + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali diff --git a/adaptors/emscripten/wrappers/handle-wrapper.h b/adaptors/emscripten/wrappers/handle-wrapper.h new file mode 100644 index 000000000..8e39be1d6 --- /dev/null +++ b/adaptors/emscripten/wrappers/handle-wrapper.h @@ -0,0 +1,148 @@ +#ifndef __DALI_HANDLE_WRAPPER_H__ +#define __DALI_HANDLE_WRAPPER_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include + +#include "emscripten/emscripten.h" +#include "emscripten/bind.h" + +// INTERNAL INCLUDES +#include "type-info-wrapper.h" + + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +/** + * Checks if a handle is pointing somewhere + * + * @param[in] self The basehandle + * @returns true if the handle is not empty + * + */ +bool BaseHandleOk(Dali::BaseHandle& self); + +/** + * Sets a handle to point to another object + * + * @param[in] self The handle to change + * @param[in] self The handle to point to + * + */ +void SetSelf(Dali::Handle& self, Dali::Handle& other); + +/** + * Sets a property by name + * + * @param[in] self The handle + * @param[in] javascriptName The property by name + * @param[in] value The property value + * + */ +void SetProperty(Dali::Handle& self, const std::string& javascriptName, const Dali::Property::Value& value); + +/** + * Gets a property by name + * + * @param[in] self The handle + * @param[in] javascriptName The property by name + * @returns The property value + * + */ +Dali::Property::Value GetProperty(Dali::Handle& self, const std::string& javascriptName); + +/** + * Gets a property index + * + * @param[in] self The handle + * @param[in] javascriptName The property by name + * @returns The property index + * + */ +int GetPropertyIndex(Dali::Handle& self, const std::string& javascriptName); + +/** + * Gets a list of property names + * + * @param[in] self The handle + * @returns The list of property names + * + */ +std::vector GetProperties(Dali::Handle& self); + +/** + * Gets a property type by name + * + * @param[in] self The handle + * @param[in] name The property type name + * @returns The property type + * + */ +Dali::Property::Type GetPropertyTypeFromName(Dali::Handle& self, const std::string& name); + +/** + * Gets a property type name + * + * @param[in] self The handle + * @param[in] name The property by name + * @returns The property type name + * + */ +std::string GetPropertyTypeName(Dali::Handle& self, const std::string& name); + +/** + * Registers a property by name + * + * @param[in] self The handle + * @param[in] name The property by name + * @returns The property index of the newly registered property + * + */ +Dali::Property::Index RegisterProperty(Dali::Handle& self, const std::string& name, const Dali::Property::Value& propertyValue); + +/** + * Registers an animated property + * + * @param[in] self The handle + * @param[in] name The property by name + * @returns The property index of the newly registered property + * + */ +Dali::Property::Index RegisterAnimatedProperty(Dali::Handle& self, const std::string& name, const Dali::Property::Value& propertyValue); + +/** + * Gets Dali type info + * + * @param[in] self The handle + * @returns The type info + * + */ +Dali::TypeInfo GetTypeInfo(Dali::Handle& self); + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali + +#endif // header diff --git a/adaptors/emscripten/wrappers/image-wrapper.cpp b/adaptors/emscripten/wrappers/image-wrapper.cpp new file mode 100644 index 000000000..7d6ffc1a6 --- /dev/null +++ b/adaptors/emscripten/wrappers/image-wrapper.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "image-wrapper.h" + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +Dali::BufferImage BufferImageNew(const std::string& data, unsigned int width, unsigned int height, Dali::Pixel::Format pixelFormat) +{ + Dali::BufferImage b = Dali::BufferImage::New( width, height, pixelFormat ); + + const Dali::PixelBuffer* from = reinterpret_cast( data.c_str() ); + Dali::PixelBuffer* to = b.GetBuffer(); + + unsigned int len = std::min( width * height * GetBytesPerPixel(pixelFormat), data.size() ); + for(int i = 0; i < len; i++) + { + *to++ = *from++; + } + return b; +} + +Dali::EncodedBufferImage EncodedBufferImageNew(const std::string& data) +{ + const uint8_t* const ptr = reinterpret_cast(data.c_str()); + return Dali::EncodedBufferImage::New(ptr, data.size()); +} + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali diff --git a/adaptors/emscripten/wrappers/image-wrapper.h b/adaptors/emscripten/wrappers/image-wrapper.h new file mode 100644 index 000000000..c0dd1445f --- /dev/null +++ b/adaptors/emscripten/wrappers/image-wrapper.h @@ -0,0 +1,61 @@ +#ifndef __DALI_IMAGE_WRAPPER_H__ +#define __DALI_IMAGE_WRAPPER_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include +#include "emscripten/emscripten.h" +#include "emscripten/bind.h" + +// INTERNAL INCLUDES + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +/** + * Creates a new buffer image from raw data + * + * @param[in] data The image byte data + * @param[in] width The width of the image + * @param[in] height The height of the image + * @param[in] pixelFormat The pixel format + * + * @returns A Dali BufferImage + */ +Dali::BufferImage BufferImageNew(const std::string& data, unsigned int width, unsigned int height, Dali::Pixel::Format pixelFormat); + +/** + * Gets an encoded buffer from encoded data + * + * @param[in] data The image data + * + * @returns A Dali EncodedBufferImage + * + */ +Dali::EncodedBufferImage EncodedBufferImageNew(const std::string& data); + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali + +#endif // header diff --git a/adaptors/emscripten/wrappers/property-buffer-wrapper.cpp b/adaptors/emscripten/wrappers/property-buffer-wrapper.cpp new file mode 100644 index 000000000..e63fd6f5d --- /dev/null +++ b/adaptors/emscripten/wrappers/property-buffer-wrapper.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "property-buffer-wrapper.h" + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES +#include "property-value-wrapper.h" + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +void SetPropertyBufferDataRaw(Dali::PropertyBuffer& self, const std::string& data ) +{ + self.SetData( reinterpret_cast( const_cast(data.c_str()) ) ); +} + + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali diff --git a/adaptors/emscripten/wrappers/property-buffer-wrapper.h b/adaptors/emscripten/wrappers/property-buffer-wrapper.h new file mode 100644 index 000000000..bc2936c2e --- /dev/null +++ b/adaptors/emscripten/wrappers/property-buffer-wrapper.h @@ -0,0 +1,50 @@ +#ifndef __DALI_PROPERTYBUFFER_WRAPPER_H__ +#define __DALI_PROPERTYBUFFER_WRAPPER_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include +#include +#include "emscripten/emscripten.h" +#include "emscripten/val.h" +#include "emscripten/bind.h" + +// INTERNAL INCLUDES + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +/** + * Sets a property buffer from raw data + * + * @param[in] self The PropertyBuffer + * @param[in] data The raw byte data + * + */ +void SetPropertyBufferDataRaw(Dali::PropertyBuffer& self, const std::string& data ); + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali + +#endif // header diff --git a/adaptors/emscripten/wrappers/property-value-wrapper.cpp b/adaptors/emscripten/wrappers/property-value-wrapper.cpp new file mode 100644 index 000000000..0b5476185 --- /dev/null +++ b/adaptors/emscripten/wrappers/property-value-wrapper.cpp @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "property-value-wrapper.h" + +// EXTERNAL INCLUDES +#include +#include + +// INTERNAL INCLUDES + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +void RecursiveSetProperty(Dali::Property::Value& propertyValue, const emscripten::val& fromVal) +{ + static const std::string number("number"); // we could maybe just check the first three chars? (avoiding null ...if that's returned) + const std::string fromType( fromVal.typeof().as() ); + + if( fromType == "object" ) + { + // hasOwnProperty is the only way I can find to tell if the object is an array + // (keys in HasKey returns the JS keys "0","1","2","3",...) + if( fromVal.hasOwnProperty("length") ) + { + int length = fromVal["length"].as(); + // we can't tell if what the user of the property value wants with a JS Array + // by default 'standard' length arrays are always interpreted as Vector2/3/4 etc + // If the user specifically wants an array they must recast. + bool isArray = false; // nested ie [ [1,2,3], [4,5,6] ] + if( 4 == length ) + { + if( number == fromVal["0"].typeof().as() && + number == fromVal["1"].typeof().as() && + number == fromVal["2"].typeof().as() && + number == fromVal["3"].typeof().as() ) + { + propertyValue = Dali::Vector4( fromVal["0"].as(), + fromVal["1"].as(), + fromVal["2"].as(), + fromVal["3"].as() ); + } + else + { + isArray = true; + } + } + else if( 3 == length ) + { + if( number == fromVal["0"].typeof().as() && + number == fromVal["1"].typeof().as() && + number == fromVal["2"].typeof().as() ) + { + propertyValue = Dali::Vector3( fromVal["0"].as(), + fromVal["1"].as(), + fromVal["2"].as() ); + } + else + { + isArray = true; + } + } + else if( 2 == length ) + { + if( number == fromVal["0"].typeof().as() && + number == fromVal["1"].typeof().as() ) + { + propertyValue = Dali::Vector2( fromVal["0"].as(), + fromVal["1"].as() ); + } + else + { + isArray = true; + } + } + else + { + isArray = true; + } + + if( isArray ) + { + propertyValue = Dali::Property::Value(Dali::Property::ARRAY); + Dali::Property::Array* array = propertyValue.GetArray(); + for( int j = 0; j < length; ++j ) + { + Dali::Property::Value add; + array->PushBack( add ); + + std::stringstream ss; + ss << j; + + emscripten::val val = fromVal[ ss.str() ]; + RecursiveSetProperty( array->GetElementAt(j), val ); + } + } + } + else + { + propertyValue = Dali::Property::Value(Dali::Property::MAP); + Dali::Property::Map* map = propertyValue.GetMap(); + emscripten::val keys = emscripten::val::global("Object").call("keys", fromVal); + int keyLength = keys["length"].as(); + for( int j = 0; j < keyLength; ++j ) + { + Dali::Property::Value add; + std::string key = keys[j].as(); + (*map)[key] = add; + emscripten::val keyVal = fromVal[ key ]; + RecursiveSetProperty( *(map->Find( key )), keyVal ); + } + } + } + else if( fromType == "number" ) + { + propertyValue = Dali::Property::Value( fromVal.as() ); + } + else if( fromType == "string" ) + { + propertyValue = Dali::Property::Value( fromVal.as() ); + } + else + { + assert(0); + } + +} + +emscripten::val JavascriptValue( const Dali::Property::Value& v ) +{ + switch( v.GetType() ) + { + case Dali::Property::BOOLEAN: + { + return emscripten::val(v.Get()); + break; + } + case Dali::Property::FLOAT: + { + return emscripten::val(v.Get()); + break; + } + case Dali::Property::INTEGER: + { + return emscripten::val(v.Get()); + break; + } + case Dali::Property::VECTOR2: + { + Dali::Vector2 in = v.Get(); + emscripten::val out = emscripten::val::array(); + out.set("0", emscripten::val(in.x) ); + out.set("1", emscripten::val(in.y) ); + return out; + break; + } + case Dali::Property::VECTOR3: + { + Dali::Vector3 in = v.Get(); + emscripten::val out = emscripten::val::array(); + out.set("0", emscripten::val(in.x) ); + out.set("1", emscripten::val(in.y) ); + out.set("2", emscripten::val(in.z) ); + return out; + break; + } + case Dali::Property::VECTOR4: + { + Dali::Vector4 in = v.Get(); + emscripten::val out = emscripten::val::array(); + out.set("0", emscripten::val(in.x) ); + out.set("1", emscripten::val(in.y) ); + out.set("2", emscripten::val(in.z) ); + out.set("3", emscripten::val(in.w) ); + return out; + break; + } + case Dali::Property::MATRIX3: + { + emscripten::val val = emscripten::val::array(); + Dali::Matrix3 mat3 = v.Get(); + + for( int i = 0; i < 9; ++i ) + { + std::stringstream key; + key << i; + val.set( key.str(), emscripten::val(mat3.AsFloat()[i]) ); + } + return val; + break; + } + case Dali::Property::MATRIX: + { + emscripten::val val = emscripten::val::array(); + Dali::Matrix mat = v.Get(); + + for( int i = 0; i < 16; ++i ) + { + std::stringstream key; + key << i; + val.set( key.str(), emscripten::val(mat.AsFloat()[i]) ); + } + return val; + break; + } + case Dali::Property::RECTANGLE: + { + Dali::Rect in = v.Get >(); + emscripten::val out = emscripten::val::array(); + out.set("0", emscripten::val(in.x) ); + out.set("1", emscripten::val(in.y) ); + out.set("2", emscripten::val(in.width) ); + out.set("3", emscripten::val(in.height) ); + return out; + break; + } + case Dali::Property::ROTATION: + { + Dali::Quaternion in = v.Get(); + emscripten::val out = emscripten::val::array(); + Dali::Vector3 axis; + Dali::Radian angle; + in.ToAxisAngle(axis, angle); + + out.set("0", emscripten::val( axis.x ) ); + out.set("1", emscripten::val( axis.y ) ); + out.set("2", emscripten::val( axis.z ) ); + out.set("3", emscripten::val( Dali::Degree(angle).degree ) ); + + return out; + break; + } + case Dali::Property::STRING: + { + return emscripten::val( v.Get() ); + break; + } + case Dali::Property::ARRAY: + { + emscripten::val val = emscripten::val::array(); + Dali::Property::Array *array = v.GetArray(); + DALI_ASSERT_ALWAYS(array); + + for( int i = 0; i < array->Count(); ++i ) + { + Dali::Property::Value& property = array->GetElementAt( i ); + std::stringstream key; + key << i; + val.set( key.str(), JavascriptValue( property ) ); + } + + return val; + break; + } + case Dali::Property::MAP: + { + emscripten::val val = emscripten::val::object(); + Dali::Property::Map *map = v.GetMap(); + DALI_ASSERT_ALWAYS(map); + + for( int i = 0; i < map->Count(); ++i ) + { + std::string key; + StringValuePair pair = map->GetPair(i); + val.set( pair.first, JavascriptValue( pair.second ) ); + } + + return val; + break; + } + case Dali::Property::NONE: + { + break; + } + } // switch type + + return emscripten::val::undefined(); + +} + +Dali::Property::Value PropertyMapGet( Dali::Property::Map& self, const std::string& key) +{ + Dali::Property::Value ret; + + Dali::Property::Value* v = self.Find(key); + + if(v) + { + ret = *v; + } + + return ret; +} + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali diff --git a/adaptors/emscripten/wrappers/property-value-wrapper.h b/adaptors/emscripten/wrappers/property-value-wrapper.h new file mode 100644 index 000000000..86c73c89e --- /dev/null +++ b/adaptors/emscripten/wrappers/property-value-wrapper.h @@ -0,0 +1,69 @@ +#ifndef __DALI_PROPERTY_VALUE_WRAPPER_H__ +#define __DALI_PROPERTY_VALUE_WRAPPER_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include +#include +#include "emscripten/val.h" + +// INTERNAL INCLUDES + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +/** + * Sets a property value From an Emscripten value. Recursively for Maps and Arrays + * + * @param[in] propertyValue The property value to set + * @param[in] fromVal The emscripten value to set from + * + */ +void RecursiveSetProperty(Dali::Property::Value& propertyValue, const emscripten::val& fromVal); + +/** + * Set an Emscripten value from a Dali property Value + * + * @param[in] value The Dali Property value + * + * @returns The Emscripten value + * + */ +emscripten::val JavascriptValue( const Dali::Property::Value& value ); + +/** + * Gets a Dali value from a Dali Property Map + * + * @param[in] self The property map + * @param[in] key The key of the value to fetch + * + * @returns The property value + * + */ +Dali::Property::Value PropertyMapGet( Dali::Property::Map& self, const std::string& key ); + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali + +#endif // header diff --git a/adaptors/emscripten/wrappers/render-task-wrapper.cpp b/adaptors/emscripten/wrappers/render-task-wrapper.cpp new file mode 100644 index 000000000..255162d54 --- /dev/null +++ b/adaptors/emscripten/wrappers/render-task-wrapper.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "render-task-wrapper.h" + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +Dali::Vector2 ScreenToLocal(Dali::RenderTask self, Dali::Actor actor, float screenX, float screenY) +{ + float localX = 0; + float localY = 0; + self.ViewportToLocal(actor, screenX, screenY, localX, localY); + return Dali::Vector2(localX,localY); +} + +Dali::Vector2 WorldToScreen(Dali::RenderTask self, const Dali::Vector3 &position) +{ + float screenX = 0; + float screenY = 0; + self.WorldToViewport(position, screenX, screenY); + return Dali::Vector2(screenX, screenY); +} + + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali diff --git a/adaptors/emscripten/wrappers/render-task-wrapper.h b/adaptors/emscripten/wrappers/render-task-wrapper.h new file mode 100644 index 000000000..fc799c981 --- /dev/null +++ b/adaptors/emscripten/wrappers/render-task-wrapper.h @@ -0,0 +1,64 @@ +#ifndef __DALI_RENDER_TASK_WRAPPER_H__ +#define __DALI_RENDER_TASK_WRAPPER_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include +#include "emscripten/emscripten.h" +#include "emscripten/bind.h" + +// INTERNAL INCLUDES +#include "actor-wrapper.h" + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +/** + * Gets the local coordinates from the screen coordinates + * + * @param[in] self The render task + * @param[in] actor The Dali actor + * @param[in] screenX The screen X position + * @param[in] screenY The screen Y position + * + * @returns The Local coordinates + * + */ +Dali::Vector2 ScreenToLocal(Dali::RenderTask self, Dali::Actor actor, float screenX, float screenY); + +/** + * Gets the screen coordinates from a position + * + * @param[in] self The RenderTask + * @param[in] position The position + * + * @returns The screen coordinates of the actor + * + */ +Dali::Vector2 WorldToScreen(Dali::RenderTask self, const Dali::Vector3 &position); + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali + +#endif // header diff --git a/adaptors/emscripten/wrappers/shader-effect-wrapper.cpp b/adaptors/emscripten/wrappers/shader-effect-wrapper.cpp new file mode 100644 index 000000000..bd31159fb --- /dev/null +++ b/adaptors/emscripten/wrappers/shader-effect-wrapper.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "handle-wrapper.h" + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES +#include "shader-effect-wrapper.h" +#include "emscripten-utils.h" + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +Dali::ShaderEffect CreateShaderEffect( const std::string& vertexPrefix, const std::string& vertex, + const std::string& fragmentPrefix, const std::string& fragment, + int geometryHints ) +{ + Dali::ShaderEffect e = Dali::ShaderEffect::NewWithPrefix(vertexPrefix, vertex, + fragmentPrefix, fragment, + static_cast(geometryHints)); + + return e; +} + + +void SetUniform( Dali::ShaderEffect& self, const std::string& name, Dali::Property::Value& propertyValue ) +{ + switch(propertyValue.GetType()) + { + case Dali::Property::FLOAT: + { + self.SetUniform( name, propertyValue.Get() ); + break; + }; + case Dali::Property::VECTOR2: + { + self.SetUniform( name, propertyValue.Get() ); + break; + }; + case Dali::Property::VECTOR3: + { + self.SetUniform( name, propertyValue.Get() ); + break; + }; + case Dali::Property::VECTOR4: + { + self.SetUniform( name, propertyValue.Get() ); + break; + }; + case Dali::Property::MATRIX: + { + self.SetUniform( name, propertyValue.Get() ); + break; + }; + case Dali::Property::MATRIX3: + { + self.SetUniform( name, propertyValue.Get() ); + break; + }; + case Dali::Property::NONE: + case Dali::Property::BOOLEAN: + case Dali::Property::INTEGER: + case Dali::Property::ROTATION: + case Dali::Property::RECTANGLE: + case Dali::Property::STRING: + case Dali::Property::ARRAY: + case Dali::Property::MAP: + { + break; + } + } +} + + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali diff --git a/adaptors/emscripten/wrappers/shader-effect-wrapper.h b/adaptors/emscripten/wrappers/shader-effect-wrapper.h new file mode 100644 index 000000000..b2b3d0d04 --- /dev/null +++ b/adaptors/emscripten/wrappers/shader-effect-wrapper.h @@ -0,0 +1,65 @@ +#ifndef __DALI_SHADER_EFFECT_WRAPPER_H__ +#define __DALI_SHADER_EFFECT_WRAPPER_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include +#include "emscripten/emscripten.h" +#include "emscripten/bind.h" + +// INTERNAL INCLUDES + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +/** + * Creates a Dali shder effect + * + * @param[in] vertexPrefix The ShaderEffect vertexPrefix + * @param[in] vertex The ShaderEffect vertex + * @param[in] fragmentPrefix The ShaderEffect fragmentPrefix + * @param[in] fragment The ShaderEffect fragment + * @param[in] geometryHints The Geometry hints; an integer for Emscripten embind (additive enum) + * + * @returns The ShaderEffect + * + */ +Dali::ShaderEffect CreateShaderEffect( const std::string& vertexPrefix, const std::string& vertex, + const std::string& fragmentPrefix, const std::string& fragment, + int geometryHints ); + +/** + * Gets a property by name + * + * @param[in] self The ShaderEffect + * @param[in] name The uniform name + * @param[in] propertyValue The PropertyValue + * + */ +void SetUniform( Dali::ShaderEffect& self, const std::string& name, Dali::Property::Value& propertyValue ); + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali + +#endif // header diff --git a/adaptors/emscripten/wrappers/signal-holder.h b/adaptors/emscripten/wrappers/signal-holder.h new file mode 100644 index 000000000..0e96bc782 --- /dev/null +++ b/adaptors/emscripten/wrappers/signal-holder.h @@ -0,0 +1,49 @@ +#ifndef DALI_SIGNAL_HOLDER_H +#define DALI_SIGNAL_HOLDER_H + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +struct BaseSignalSlot : public Dali::ConnectionTracker +{ +}; + +/** + * A wrapper class to let JS hold Signals + */ +class SignalHolder : public Dali::ConnectionTracker +{ +public: + typedef Dali::Vector Slots; + Slots mSlots; + SignalHolder() {} + + ~SignalHolder() + { + for(Slots::Iterator iter = mSlots.Begin(); iter != mSlots.End(); ++iter) + { + delete *iter; + } + } + + /* + * Adds a base signal to the list of slots + */ + void add(BaseSignalSlot* s) { mSlots.PushBack(s); } + +private: + SignalHolder(const SignalHolder& nocopy); + SignalHolder& operator=(const SignalHolder& noassign); +}; + + +}; // Emscripten +}; // Internal +}; // Dali + + +#endif // header diff --git a/adaptors/emscripten/wrappers/type-info-wrapper.cpp b/adaptors/emscripten/wrappers/type-info-wrapper.cpp new file mode 100644 index 000000000..2380347e0 --- /dev/null +++ b/adaptors/emscripten/wrappers/type-info-wrapper.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "type-info-wrapper.h" + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES +#include "property-value-wrapper.h" + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +std::vector GetAllProperties(Dali::TypeInfo self) +{ + std::vector names; + + // get the other properties + if(Dali::Handle handle = Dali::Handle::DownCast( self.CreateInstance() ) ) + { + typedef Dali::Property::IndexContainer IndexContainer; + + Dali::Property::IndexContainer indices; + handle.GetPropertyIndices( indices ); + + for(IndexContainer::Iterator iter(indices.Begin()); iter != indices.End(); ++iter) + { + std::string name = handle.GetPropertyName( *iter ); + + names.push_back(name); + } + } + else + { + // all we can do is get the event side properties + // get the event side properties + Property::IndexContainer indices; + self.GetPropertyIndices( indices ); + for(Property::IndexContainer::Iterator iter(indices.Begin()); iter != indices.End(); ++iter) + { + std::string name = self.GetPropertyName( *iter ); + names.push_back(name); + } + } + + return names; +} + +std::vector GetPropertyIndices(Dali::TypeInfo& self) +{ + Dali::Property::IndexContainer indices; + self.GetPropertyIndices( indices ); + + std::vector ret( indices.Begin(), indices.End() ); + return ret; +} + +std::vector GetActions(Dali::TypeInfo& self) +{ + std::vector names; + std::size_t size = self.GetActionCount(); + for(std::size_t i = 0; i < size; i++) + { + names.push_back(self.GetActionName(i)); + } + return names; +} + +std::vector GetSignals(Dali::TypeInfo& self) +{ + std::vector names; + std::size_t size = self.GetSignalCount(); + for(std::size_t i = 0; i < size; i++) + { + names.push_back(self.GetSignalName(i)); + } + return names; +} + +std::vector GetBases(Dali::TypeInfo& self) +{ + std::vector names; + + Dali::TypeRegistry registry = Dali::TypeRegistry::Get(); + + Dali::TypeInfo base = registry.GetTypeInfo( self.GetBaseName() ); + + while(base) + { + base = registry.GetTypeInfo( base.GetBaseName() ); + names.push_back(base.GetName()); + } + + return names; +} + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali diff --git a/adaptors/emscripten/wrappers/type-info-wrapper.h b/adaptors/emscripten/wrappers/type-info-wrapper.h new file mode 100644 index 000000000..8c6fddae7 --- /dev/null +++ b/adaptors/emscripten/wrappers/type-info-wrapper.h @@ -0,0 +1,103 @@ +#ifndef __DALI_TYPE_INFO_WRAPPER_H__ +#define __DALI_TYPE_INFO_WRAPPER_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include + +#include "emscripten/emscripten.h" +#include "emscripten/bind.h" +#include +#include + +// INTERNAL INCLUDES + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +/** + * Gets all properties from TypeInfo + * + * @param[in] self The TypeInfo + * + * @returns A list of property names + * + */ +std::vector GetAllProperties(Dali::TypeInfo self); + +/** + * Gets all property indices from TypeInfo + * + * @param[in] self The TypeInfo + * + * @returns A list of property indices + * + */ +std::vector GetPropertyIndices(Dali::TypeInfo& self); + +/** + * Gets all actions from TypeInfo + * + * @param[in] self The TypeInfo + * + * @returns A list of property action names + * + */ +std::vector GetActions(Dali::TypeInfo& self); + +/** + * Gets all signals from TypeInfo + * + * @param[in] self The TypeInfo + * + * @returns A list of signal names + * + */ +std::vector GetSignals(Dali::TypeInfo& self); + +/** + * Gets all bases classes from TypeInfo + * + * @param[in] self The TypeInfo + * + * @returns A list of base class names + * + */ +std::vector GetBases(Dali::TypeInfo& self); + +/** + * Check if a TypeInfo has a base name + * + * @param[in] self The TypeInfo + * @param[in] baseName The base class name + * + * @returns true if baseName is a base class of self + * + */ +bool InheritsFrom(Dali::TypeInfo& self, const std::string& baseName); + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali + +#endif // header diff --git a/automated-tests/CMakeLists.txt b/automated-tests/CMakeLists.txt index 9e152031d..5b1729a73 100644 --- a/automated-tests/CMakeLists.txt +++ b/automated-tests/CMakeLists.txt @@ -1,3 +1,5 @@ +include(CheckIncludeFileCXX) + CMAKE_MINIMUM_REQUIRED(VERSION 2.8) PROJECT(tct_coreapi_utc) diff --git a/automated-tests/src/dali-adaptor/dali-test-suite-utils/test-gl-abstraction.h b/automated-tests/src/dali-adaptor/dali-test-suite-utils/test-gl-abstraction.h index 3d35394c5..56a8d81a4 100644 --- a/automated-tests/src/dali-adaptor/dali-test-suite-utils/test-gl-abstraction.h +++ b/automated-tests/src/dali-adaptor/dali-test-suite-utils/test-gl-abstraction.h @@ -21,7 +21,6 @@ // EXTERNAL INCLUDES #include #include -#include #include #include #include // for strcmp diff --git a/build/emscripten/.gitignore b/build/emscripten/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/build/emscripten/.gitignore @@ -0,0 +1 @@ +/build diff --git a/build/emscripten/CMakeLists.txt b/build/emscripten/CMakeLists.txt new file mode 100644 index 000000000..2df65faa2 --- /dev/null +++ b/build/emscripten/CMakeLists.txt @@ -0,0 +1,115 @@ +cmake_minimum_required(VERSION 2.6) + +if( ${EMSCRIPTEN} ) + message("EMSCRIPTEN BUILD") + set(CMAKE_C_COMPILER "emcc") + set(CMAKE_CXX_COMPILER "em++") +else( ${EMSCRIPTEN} ) + message( FATAL_ERROR "Native Build not supported via cmake." ) +endif( ${EMSCRIPTEN} ) + +if(NOT DEFINED ENV{DESKTOP_PREFIX}) + message( FATAL_ERROR "DESKTOP_PREFIX is required to build adaptor against dali-core. Please make sure you have sourced your setenv script (created by dali_env)." ) +endif() + +project(dali-emscripten CXX) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/") + +set(SRCS + ${CMAKE_SOURCE_DIR}/../../adaptors/emscripten/wrappers/actor-wrapper.cpp + ${CMAKE_SOURCE_DIR}/../../adaptors/emscripten/wrappers/animation-wrapper.cpp + ${CMAKE_SOURCE_DIR}/../../adaptors/emscripten/wrappers/dali-wrapper.cpp + ${CMAKE_SOURCE_DIR}/../../adaptors/emscripten/wrappers/emscripten-utils.cpp + ${CMAKE_SOURCE_DIR}/../../adaptors/emscripten/wrappers/handle-wrapper.cpp + ${CMAKE_SOURCE_DIR}/../../adaptors/emscripten/wrappers/image-wrapper.cpp + ${CMAKE_SOURCE_DIR}/../../adaptors/emscripten/wrappers/property-buffer-wrapper.cpp + ${CMAKE_SOURCE_DIR}/../../adaptors/emscripten/wrappers/property-value-wrapper.cpp + ${CMAKE_SOURCE_DIR}/../../adaptors/emscripten/wrappers/render-task-wrapper.cpp + ${CMAKE_SOURCE_DIR}/../../adaptors/emscripten/wrappers/shader-effect-wrapper.cpp + ${CMAKE_SOURCE_DIR}/../../adaptors/emscripten/wrappers/type-info-wrapper.cpp + ${CMAKE_SOURCE_DIR}/../../adaptors/base/separate-update-render/frame-time.cpp + ${CMAKE_SOURCE_DIR}/../../adaptors/base/time-service.cpp + ${CMAKE_SOURCE_DIR}/../../adaptors/common/gl/egl-image-extensions.cpp + ${CMAKE_SOURCE_DIR}/../../adaptors/common/gl/gl-extensions.cpp + ${CMAKE_SOURCE_DIR}/../../adaptors/emscripten/egl-implementation-emscripten.cpp + ${CMAKE_SOURCE_DIR}/../../adaptors/emscripten/main.cpp + ${CMAKE_SOURCE_DIR}/../../adaptors/emscripten/sdl-application.cpp + ${CMAKE_SOURCE_DIR}/../../adaptors/emscripten/sdl-gl-sync-abstraction.cpp + ${CMAKE_SOURCE_DIR}/../../platform-abstractions/emscripten/emscripten-callbacks.cpp + ${CMAKE_SOURCE_DIR}/../../platform-abstractions/emscripten/emscripten-platform-abstraction.cpp +) + +include_directories(${CMAKE_SOURCE_DIR}/../../) +include_directories(${CMAKE_SOURCE_DIR}/../../adaptors/) +include_directories(${CMAKE_SOURCE_DIR}/../../adaptors/common) +include_directories(${CMAKE_SOURCE_DIR}/../../adaptors/tizen) +include_directories(${CMAKE_SOURCE_DIR}/../../adaptors/emscripten) +include_directories(${CMAKE_SOURCE_DIR}/../../platform-abstractions/slp) + +set(VENDOR "samsung") +set(PACKAGE ${PROJECT_NAME}) +set(DESKTOP_PREFIX $ENV{DESKTOP_PREFIX}) +set(CPP_DEFINES -DDALI_GLES_VERSION=2) + +set(EMSCRIPTEN_ENV_DIR ${DESKTOP_PREFIX}/share/emscripten) + +set(DEBUG_FLAGS "-Wall -g -O2") +set(RELEASE_FLAGS "-Wall -g -O2") + +if( ${EMSCRIPTEN} ) + set(DEBUG_FLAGS "${DEBUG_FLAGS} -std=c++11 -DBOOST_ERROR_CODE_HEADER_ONLY") + set(RELEASE_FLAGS "${RELEASE_FLAGS} -std=c++11 -DBOOST_ERROR_CODE_HEADER_ONLY") +endif( ${EMSCRIPTEN} ) + + +include(FindPkgConfig) + +pkg_check_modules(pkgs REQUIRED + dali-core + # sdl from emscripten + ) + +if( ${EMSCRIPTEN} ) + +pkg_check_modules(pkgs REQUIRED + dali-core + # sdl from emscripten + ) + +else( ${EMSCRIPTEN} ) + +find_library(SDL VERSION "1.2" REQUIRED HINTS $DESKTOP_PREFIX/lib) +find_library(SDL REQUIRED) +find_library(SDL_image REQUIRED) + +find_library(jpeg REQUIRED) +find_library(ft2build REQUIRED) +find_library(turbojpeg REQUIRED) + +pkg_check_modules(pkgs REQUIRED + dali-core + # sdl from emscripten + sdl + egl + ) + +include_directories(/usr/local/include/SDL/) +set(LIBS ${LIBS} -lSDL -lSDL_image -lEGL -lGLESv2) + +endif( ${EMSCRIPTEN} ) + +# non pkg config +include_directories(${DESKTOP_PREFIX}/include/) + +add_definitions( ${CPP_DEFINES} ) # see configure_file() for *.in to *.h style + +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${DEBUG_FLAGS}") +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${RELEASE_FLAGS}") + +add_executable(${PROJECT_NAME} ${SRCS}) + +target_link_libraries(${PROJECT_NAME} ${pkgs_LDFLAGS} ${LIBS}) # "-lm" + +# Copy dali-wrapper.js to dali-env +configure_file(${CMAKE_SOURCE_DIR}/../../adaptors/emscripten/wrappers/dali-wrapper.js ${EMSCRIPTEN_ENV_DIR}/dali-wrapper.js COPYONLY) diff --git a/build/emscripten/build.sh b/build/emscripten/build.sh new file mode 100755 index 000000000..3d7a3d471 --- /dev/null +++ b/build/emscripten/build.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# This script builds dali-adaptor using Emscripten. + +# DESKTOP_PREFIX is required to build Emscripten DALi. +if [ -z ${DESKTOP_PREFIX} ]; then + echo "DESKTOP_PREFIX is required to build adaptor against dali-core. Please make sure you have sourced your setenv script (created by dali_env)." + exit 1 +fi + + +mkdir -p build +cd build +rm CMakeCache.txt + +/usr/bin/cmake .. -DCMAKE_BUILD_TYPE=Debug -DEMSCRIPTEN=1 + +make -j8 + +mv dali-emscripten dali-emscripten.bc + +# Non-optimised build. +emcc dali-emscripten.bc -o dali-emscripten.html --memory-init-file 0 -s FULL_ES2=1 -s STB_IMAGE=1 -s ALLOW_MEMORY_GROWTH=1 -s ASSERTIONS=1 -s DEMANGLE_SUPPORT=1 -s DISABLE_EXCEPTION_CATCHING=0 -s EXPORT_NAME=\"dali\" -g4 --bind + +# Debug build. +#emcc dali-emscripten.bc -o dali-emscripten.html --memory-init-file 0 -s FULL_ES2=1 -s STB_IMAGE=1 -s ALLOW_MEMORY_GROWTH=1 -s ASSERTIONS=2 -s DEMANGLE_SUPPORT=1 -s DISABLE_EXCEPTION_CATCHING=0 -s EXPORT_NAME=\"dali\" --js-opts 0 -g4 --bind + +# Optimised build. +#emcc dali-emscripten.bc -o dali-emscripten.html -s FULL_ES2=1 -s STB_IMAGE=1 -s ALLOW_MEMORY_GROWTH=1 -s ASSERTIONS=0 -s DISABLE_EXCEPTION_CATCHING=2 -s EXPORT_NAME=\"dali\" -O2 --bind + +# Copy the required built artifacts to dali-env. +mv ./dali-emscripten.js ${DESKTOP_PREFIX}/share/emscripten/ +mv ./dali-emscripten.html ${DESKTOP_PREFIX}/share/emscripten/ + +# If static memory initialisation code was created in a separate file, copy this too. +mv ./dali-emscripten.html.mem ${DESKTOP_PREFIX}/share/emscripten/ diff --git a/platform-abstractions/emscripten/emscripten-callbacks.cpp b/platform-abstractions/emscripten/emscripten-callbacks.cpp new file mode 100644 index 000000000..a7abb085e --- /dev/null +++ b/platform-abstractions/emscripten/emscripten-callbacks.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "emscripten-callbacks.h" + +// EXTERNAL INCLUDES +#include +#include +#include "emscripten/emscripten.h" +#include "emscripten/bind.h" +#include "emscripten/val.h" + +// INTERNAL INCLUDES + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +Statistics stats; + +// Javascript callbacks + +// Allows the adaptor to get a glyph image from the browser +emscripten::val JSGetGlyphImage(emscripten::val::null()); + +// Allows the adaptor to request an image from the browser +emscripten::val JSGetImage(emscripten::val::null()); + +// Allows the adaptor to get image meta data +emscripten::val JSGetImageMetaData(emscripten::val::null()); + +// Signals to the browser the end of rendering +emscripten::val JSRenderFinished(emscripten::val::null()); + + +Integration::BitmapPtr GetGlyphImage( const std::string& fontFamily, const std::string& fontStyle, float fontSize, uint32_t character ) +{ + Integration::BitmapPtr ret; + + // causes exception in browser if callback isnt set to a function; + emscripten::val val = JSGetGlyphImage(fontFamily, + fontStyle, + fontSize, + character); + + std::vector data = emscripten::vecFromJSArray(val); + + int step = fontSize * 4; + + if( data.size() ) + { + Integration::Bitmap* bitmap = Integration::Bitmap::New(Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, + ResourcePolicy::OWNED_DISCARD); // dali manages buffer + + if(bitmap) + { + Integration::Bitmap::PackedPixelsProfile* profile = bitmap->GetPackedPixelsProfile(); + + if(profile) + { + std::vector *buffer = new std::vector; + + buffer->reserve( fontSize * fontSize ); + + // take only alpha + for(int y = 0; y < fontSize; ++y) + { + for(int x = 0; x < step; x+=4) + { + buffer->push_back(data[ x + (y*step) +3]); + } + } + + if( buffer ) + { + profile->AssignBuffer(Pixel::A8, + &(*buffer)[0], + (*buffer).size(), + fontSize, + fontSize); + } + + ret = Integration::BitmapPtr( bitmap ); + } + else + { + printf("bitmap has no packedpixelsprofile\n"); + } + } + else + { + printf("bitmap not created\n"); + } + } + else + { + printf("Image data from javascript is empty\n"); + } + + return ret; +} + + +Integration::BitmapPtr GetImage( const Dali::ImageDimensions& size, + const Dali::FittingMode::Type& scalingMode, + const Dali::SamplingMode::Type& samplingMode, + const bool orientationCorrection, + const std::string& filename ) +{ + Integration::BitmapPtr ret; + + // causes exception in browser if callback isnt set to a function; + emscripten::val val = JSGetImage(filename); + + emscripten::val array = val["array"]; + int w = val["x"].as(); + int h = val["y"].as(); + + std::vector data = emscripten::vecFromJSArray(array); + + Integration::Bitmap* bitmap = Integration::Bitmap::New(Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, + Dali::ResourcePolicy::OWNED_DISCARD); // dali manages buffer + + if(bitmap) + { + Integration::Bitmap::PackedPixelsProfile* profile = bitmap->GetPackedPixelsProfile(); + + if(profile) + { + std::vector *buffer = new std::vector(data.begin(), data.end()); + + if( buffer ) + { + profile->AssignBuffer(Pixel::RGBA8888, + &(*buffer)[0], + (*buffer).size(), + w, + h); + } + + ret = Integration::BitmapPtr( bitmap ); + } + else + { + printf("bitmap has no packedpixelsprofile\n"); + } + } + else + { + printf("bitmap not created\n"); + } + + return ret; +} + +Dali::ImageDimensions LoadImageMetadata(const std::string filename, + Dali::ImageDimensions& size, + Dali::FittingMode::Type fittingMode, + Dali::SamplingMode::Type samplingMode, + bool orientationCorrection ) +{ + emscripten::val val = JSGetImageMetaData(filename); + + // @todo + // size.x = val["w"] + // size.y = val["h"] + // + return Dali::ImageDimensions(); +} + +void RenderFinished() +{ + if (JSRenderFinished.typeof().as() == "function") + { + emscripten::val val = JSRenderFinished(); + } +} + + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali + diff --git a/platform-abstractions/emscripten/emscripten-callbacks.h b/platform-abstractions/emscripten/emscripten-callbacks.h new file mode 100644 index 000000000..87bb428bd --- /dev/null +++ b/platform-abstractions/emscripten/emscripten-callbacks.h @@ -0,0 +1,118 @@ +#ifndef __DALI_EMSCRIPTEN_CALLBACKS_H__ +#define __DALI_EMSCRIPTEN_CALLBACKS_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include +#include +#include + +// INTERNAL INCLUDES + +namespace Dali +{ +namespace Internal +{ +namespace Emscripten +{ + +/** + * Glyph helper + * + * Allows Dali adaptor to request a glyph image from the browser + * + * @param[in] fontFamily The font family name + * @param[in] fontStyle The font style name + * @param[in] fontSize The font size + * @param[in] character The character + * + */ +Dali::Integration::BitmapPtr GetGlyphImage( const std::string& fontFamily, const std::string& fontStyle, float fontSize, uint32_t character ); + +/** + * Image meta data helper + * + * Allows Dali adaptor to request image metadata from image known to the browser + * + * @param[in] filename The image name (possibly not directly a filename due to the browser sandboxing) + * @param[in] size The image dimensions + * @param[in] fittingMode The dali fitting mode + * @param[in] samplingMode The dali sampling mode + * @param[in] orientationCorrection The orientation correction + * + */ +Dali::ImageDimensions LoadImageMetadata(const std::string filename, + Dali::ImageDimensions& size, + Dali::FittingMode::Type fittingMode, + Dali::SamplingMode::Type samplingMode, + bool orientationCorrection ); + +/** + * Image helper + * + * Allows Dali adaptor to request an image from the browser + * + * @param[in] size The image dimensionsn + * @param[in] fittingMode The dali fitting mode + * @param[in] samplingMode The dali sampling mode + * @param[in] orientationCorrection The orientation correction + * @param[in] filename The image name (possibly not directly a filename due to the browser sandboxing) + * + */ +Dali::Integration::BitmapPtr GetImage(const Dali::ImageDimensions& size, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const bool orientationCorrection, + const std::string& filename ); + +/** + * Debug statistics for the browser + * + */ +struct Statistics +{ + bool on; + float frameCount; + + float lastFrameDeltaSeconds; + unsigned int lastSyncTimeMilliseconds; + unsigned int nextSyncTimeMilliseconds; + + unsigned int keepUpdating; ///< A bitmask of KeepUpdating values + bool needsNotification; + float secondsFromLastFrame; + +Statistics() :on(true), + frameCount(0.0), + lastFrameDeltaSeconds(0.0), + lastSyncTimeMilliseconds(0.0), + nextSyncTimeMilliseconds(0.0), + keepUpdating(0), + needsNotification(false), + secondsFromLastFrame(0.0) + {}; +}; + +extern Statistics stats; + +}; // namespace Emscripten +}; // namespace Internal +}; // namespace Dali + +#endif // header diff --git a/platform-abstractions/emscripten/emscripten-platform-abstraction.cpp b/platform-abstractions/emscripten/emscripten-platform-abstraction.cpp new file mode 100644 index 000000000..00cec12ac --- /dev/null +++ b/platform-abstractions/emscripten/emscripten-platform-abstraction.cpp @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "emscripten-platform-abstraction.h" + +// EXTERNAL INCLUDES +#include +#include +#include +#include +#include +#include "emscripten/emscripten.h" +#include "emscripten-callbacks.h" + +#define EM_LOG(x); // EM_ASM( console.log( x ) ); + + +// INTERNAL INCLUDES +#include +#include + +namespace +{ + +Dali::Integration::BitmapPtr LoadResourceEncodedImage( Dali::RefCountedVector* encodedBlob ) +{ + Dali::Integration::BitmapPtr bitmapPtr = NULL; + + if( encodedBlob != 0 ) + { + const size_t blobSize = encodedBlob->GetVector().Size(); + uint8_t * const blobBytes = &(encodedBlob->GetVector()[0]); + DALI_ASSERT_DEBUG( blobSize > 0U ); + DALI_ASSERT_DEBUG( blobBytes != 0U ); + + if( blobBytes != 0 && blobSize > 0U ) + { + + SDL_RWops *memory = SDL_RWFromMem(blobBytes, blobSize); + + if(!memory) + { + printf(" Error Null pointer from SDL RW memory?\n"); + } + + SDL_Surface *surface = IMG_Load_RW(memory, 0); + + if(surface) + { + bitmapPtr = Dali::Integration::Bitmap::New( Dali::Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, + Dali::ResourcePolicy::OWNED_DISCARD ); // DISCARD; Dali manages + + Dali::Integration::Bitmap::PackedPixelsProfile* packedProfile = bitmapPtr->GetPackedPixelsProfile(); + DALI_ASSERT_ALWAYS(packedProfile); + + unsigned char bytesPerPixel = surface->format->BytesPerPixel; + + Dali::Integration::PixelBuffer* pixels = NULL; + + unsigned char targetBytesPerPixel = 3; // bytesPerPixel; + + // SDL in emscripten returns us a 4byteperpixel image regardless of rgb/png etc + // Theres no apparent way to differentiate an image with an alpha channel + // In Dali if an image has an alpha channel it gets sorted. This introduces odd artifacts on rotation + // as the sorting algorithm presumes front on view. + // So here well just support pngs with an alpha channel. + // We're poking around in the format as emscripten currently lacks a file memory api where we could use + // Dali's machinery to read the format. + unsigned char *pBytes = blobBytes; + + if( 0x89 == *(pBytes+0) && 0x50 == *(pBytes+1) ) // magic bytes for png_all_filters + { + pBytes+=8; // 8 bytes for header + pBytes+=4; // 4 bytes for chunk length + pBytes+=4; // 4 bytes for chunk type + // ihdr data (must be first chunk) + pBytes+=4; // 4 for width,height + pBytes+=4; + pBytes+=1; // 1 for bit depth + unsigned char ihdr_colorType= *pBytes; // 1 for bit colorType + if( (4 == ihdr_colorType || // 4 is 8,16 bit depth with alpha LA + 6 == ihdr_colorType) ) // 6 is 8,16 bit depth with alpha RGBA + { + targetBytesPerPixel = 4; + } + } + + if(3 == targetBytesPerPixel) + { + pixels = packedProfile->ReserveBuffer(Dali::Pixel::RGB888, + surface->w, surface->h, + surface->w, surface->h); + } + else if(4 == targetBytesPerPixel) + { + pixels = packedProfile->ReserveBuffer(Dali::Pixel::RGBA8888, + surface->w, surface->h, + surface->w, surface->h); + } + else + { + DALI_ASSERT_ALWAYS(0 && "bad bytes per pixel"); + } + + unsigned char* fromPtr = static_cast(surface->pixels); + + int stride = surface->pitch; + int index = 0; + for(int h = 0; h < surface->h; ++h) + { + for(int w = 0; w < (surface->w*bytesPerPixel); w+=bytesPerPixel) + { + for(int j = 0; j < targetBytesPerPixel; ++j) + { + pixels[ index++ ] = *( (fromPtr + (h * stride) ) + w + j ); + } + } + } + } // if surface + else + { + printf(" Error empty surface when decoding image? (SDL RW Memory ptr=%llx) %s. %d\n", (long long)(memory), SDL_GetError(), blobSize); + } + + } // if blobSize + else + { + printf(" Error No bytes in image?\n"); + } + + } // if encodedBlob + else + { + printf(" Error Null pointer given for decoding image?\n"); + } + + if(bitmapPtr) + { + int x = 0; + EM_ASM( console.log( "LoadResourceEncodedImage: Image:-" ) ); + x = EM_ASM_INT({ + console.log( $0 ) }, bitmapPtr->GetImageWidth() ); + x = EM_ASM_INT({ + console.log( $0 ) }, bitmapPtr->GetImageHeight() ); + x = EM_ASM_INT({ + console.log( $0 ) }, bitmapPtr->GetBufferSize() ); + + } + else + { + EM_ASM( console.log( "LoadResourceEncodedImage: no bitmap data?" ) ); + } + + return bitmapPtr; +} + +} // anon namespace + +namespace Dali +{ + +EmscriptenPlatformAbstraction::EmscriptenPlatformAbstraction() + : + mSize(10,10) +{ +} + + +EmscriptenPlatformAbstraction::~EmscriptenPlatformAbstraction() +{ +} + +void EmscriptenPlatformAbstraction::GetTimeMicroseconds(unsigned int &seconds, unsigned int µSeconds) +{ + double current = EM_ASM_DOUBLE_V({ return new Date().getTime(); }); // getTime() in ms + + seconds = static_cast(current/1000.0); + microSeconds = (static_cast(current) - seconds*1000.0) * 1000; +} + +void EmscriptenPlatformAbstraction::Suspend() +{ + DALI_ASSERT_ALWAYS("!Not Implemented"); +} + +void EmscriptenPlatformAbstraction::Resume() +{ + DALI_ASSERT_ALWAYS("!Not Implemented"); +} + +ImageDimensions EmscriptenPlatformAbstraction::GetClosestImageSize( const std::string& filename, + ImageDimensions size, + FittingMode::Type fittingMode, + SamplingMode::Type samplingMode, + bool orientationCorrection ) +{ + return Dali::Internal::Emscripten::LoadImageMetadata(filename, size, fittingMode, samplingMode, orientationCorrection); +} + +ImageDimensions EmscriptenPlatformAbstraction::GetClosestImageSize( Integration::ResourcePointer resourceBuffer, + ImageDimensions size, + FittingMode::Type fittingMode, + SamplingMode::Type samplingMode, + bool orientationCorrection ) +{ + // @todo + return Dali::ImageDimensions(); // Dali::Internal::Emscripten::LoadImageMetadata(filename, size, fittingMode, samplingMode, orientationCorrection); +} + +Integration::ResourcePointer EmscriptenPlatformAbstraction::LoadResourceSynchronously( const Integration::ResourceType& resourceType, const std::string& resourcePath ) +{ + Integration::ResourcePointer ret; + + switch(resourceType.id) + { + case Integration::ResourceBitmap: + { + const Integration::BitmapResourceType& bitmapResource( static_cast(resourceType) ); + + Integration::BitmapPtr bitmapPtr = Dali::Internal::Emscripten::GetImage( bitmapResource.size, + bitmapResource.scalingMode, + bitmapResource.samplingMode, + bitmapResource.orientationCorrection, + resourcePath ); + + ret = bitmapPtr; + } + break; + case Integration::ResourceNativeImage: + { + } + break; + case Integration::ResourceTargetImage: + { + } + break; + } // switch(resourceType->id) + + return ret; +} + +void EmscriptenPlatformAbstraction::LoadResource(const Integration::ResourceRequest& request) +{ + std::string path = request.GetPath(); + + Integration::ResourceType *type = request.GetType(); + Integration::ResourceId resourceId = request.GetId(); + Integration::ResourcePointer resourcePtr = request.GetResource(); + + if( type ) + { + switch(type->id) + { + case Integration::ResourceBitmap: + { + Integration::BitmapPtr bitmapPtr = NULL; + + if( NULL == request.GetResource().Get() ) + { + const Integration::BitmapResourceType& bitmapResource( static_cast(*type) ); + + Integration::BitmapPtr bitmapPtr = Dali::Internal::Emscripten::GetImage( bitmapResource.size, + bitmapResource.scalingMode, + bitmapResource.samplingMode, + bitmapResource.orientationCorrection, + path ); + + + + } + else + { + // 2) load it (usually on worker thread) + // DALI_LOG_TRACE_METHOD( mLogFilter ); + // DALI_LOG_INFO(mLogFilter, Debug::Verbose, "%s(%s)\n", __FUNCTION__, request.GetPath().c_str()); + + bitmapPtr = LoadResourceEncodedImage( reinterpret_cast*>( request.GetResource().Get() ) ); + } + + if( bitmapPtr ) + { + mResourceQueue.push( ResourceIdBitmapPair( resourceId, bitmapPtr ) ); + } + + } + break; + case Integration::ResourceNativeImage: + { + printf("EmscriptenPlatformAbstraction::LoadResource ResourceNativeImage\n"); + } + break; + case Integration::ResourceTargetImage: + { + printf("EmscriptenPlatformAbstraction::LoadResource ResourceTargetImage\n"); + } + break; + } // switch(id) + + } // if(type) + +} + +void EmscriptenPlatformAbstraction::SaveResource(const Integration::ResourceRequest& request) +{ + DALI_ASSERT_ALWAYS("!Not Implemented"); +} + +Integration::BitmapPtr EmscriptenPlatformAbstraction::DecodeBuffer( const Integration::ResourceType& resourceType, uint8_t * buffer, size_t bufferSize ) +{ + return Integration::BitmapPtr(); +} + +void EmscriptenPlatformAbstraction::CancelLoad(Integration::ResourceId id, Integration::ResourceTypeId typeId) +{ + DALI_ASSERT_ALWAYS("!Not Implemented"); +} + +void EmscriptenPlatformAbstraction::GetResources(Integration::ResourceCache& cache) +{ + while( !mResourceQueue.empty() ) + { + Integration::ResourceId resourceId = mResourceQueue.front().first; + Integration::BitmapPtr bitmapPtr = mResourceQueue.front().second; + + cache.LoadResponse( resourceId, + Integration::ResourceBitmap, + bitmapPtr, + Integration::RESOURCE_COMPLETELY_LOADED ); + mResourceQueue.pop(); + } +} + +bool EmscriptenPlatformAbstraction::IsLoading() +{ + EM_LOG("EmscriptenPlatformAbstraction::IsLoading"); + return false; +} + +const std::string& EmscriptenPlatformAbstraction::GetDefaultFontFamily() const +{ + EM_LOG("EmscriptenPlatformAbstraction::GetDefaultFontFamily"); + DALI_ASSERT_ALWAYS("!Not Implemented"); + return mGetDefaultFontFamilyResult; +} + +int EmscriptenPlatformAbstraction::GetDefaultFontSize() const +{ + EM_LOG("EmscriptenPlatformAbstraction::GetDefaultFontSize"); + return 12; +} + +void EmscriptenPlatformAbstraction::SetDpi (unsigned int /* dpiHorizontal*/, unsigned int /* dpiVertical */) +{ + +} + + +bool EmscriptenPlatformAbstraction::LoadFile( const std::string& filename, Dali::Vector< unsigned char >& buffer ) const +{ + EM_LOG("EmscriptenPlatformAbstraction::LoadFile"); + return false; +} + +bool EmscriptenPlatformAbstraction::SaveFile( const std::string& filename, const unsigned char * buffer, unsigned int numBytes ) const +{ + EM_LOG("EmscriptenPlatformAbstraction::SaveFile"); + + DALI_ASSERT_ALWAYS("!Unimplemented"); + return false; +} + +bool EmscriptenPlatformAbstraction::LoadShaderBinaryFile( const std::string& filename, Dali::Vector< unsigned char >& buffer ) const +{ + EM_LOG("EmscriptenPlatformAbstraction::LoadShaderBinaryFile"); + return false; +} + +bool EmscriptenPlatformAbstraction::SaveShaderBinaryFile( const std::string& filename, const unsigned char * buffer, unsigned int numBytes ) const +{ + EM_LOG("EmscriptenPlatformAbstraction::SaveShaderBinaryFile"); + + DALI_ASSERT_ALWAYS("!Unimplemented"); + return false; +} + +void EmscriptenPlatformAbstraction::JoinLoaderThreads() +{ + DALI_ASSERT_ALWAYS("!Unimplemented"); +} + +void EmscriptenPlatformAbstraction::UpdateDefaultsFromDevice() +{ + DALI_ASSERT_ALWAYS("!Unimplemented"); + mGetDefaultFontFamilyResult+=1.0f; +} + +void EmscriptenPlatformAbstraction::IncrementGetTimeResult(size_t milliseconds) +{ +} + +} // Dali diff --git a/platform-abstractions/emscripten/emscripten-platform-abstraction.h b/platform-abstractions/emscripten/emscripten-platform-abstraction.h new file mode 100644 index 000000000..2d67e4726 --- /dev/null +++ b/platform-abstractions/emscripten/emscripten-platform-abstraction.h @@ -0,0 +1,216 @@ +#ifndef __DALI_EMSCRIPTEN_PLATFORM_ABSTRACTION_H__ +#define __DALI_EMSCRIPTEN_PLATFORM_ABSTRACTION_H__ + +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include +#include +#include +#include + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +/** + * An Dali Platform abstraction using libSDL for Emscripten. + * + * Emscripten already wraps the SDL API so we can use this API to create windows/canvas in the browser + * + */ +class DALI_IMPORT_API EmscriptenPlatformAbstraction : public Dali::Integration::PlatformAbstraction +{ + +public: + + struct Resources + { + bool loaded; + Integration::ResourceId loadedId; + Integration::ResourceTypeId loadedType; + Integration::ResourcePointer loadedResource; + + bool loadFailed; + Integration::ResourceId loadFailedId; + Integration::ResourceFailure loadFailure; + + bool saved; + Integration::ResourceId savedId; + Integration::ResourceTypeId savedType; + + bool saveFailed; + Integration::ResourceId saveFailedId; + Integration::ResourceFailure saveFailure; + }; + + struct LoadFileResult + { + LoadFileResult() + : loadResult(false) + { + + } + + bool loadResult; + std::vector< unsigned char> buffer; + }; + + + /** + * Constructor + */ + EmscriptenPlatformAbstraction(); + + + /** + * Destructor + */ + virtual ~EmscriptenPlatformAbstraction(); + + /** + * @copydoc PlatformAbstraction::GetTimeMicroseconds() + */ + virtual void GetTimeMicroseconds(unsigned int &seconds, unsigned int µSeconds); + + void IncrementGetTimeResult(size_t milliseconds); + + /** + * @copydoc PlatformAbstraction::Suspend() + */ + virtual void Suspend(); + + /** + * @copydoc PlatformAbstraction::Resume() + */ + virtual void Resume(); + + /** + * @copydoc PlatformAbstraction::GetClosestImageSize() + */ + virtual ImageDimensions GetClosestImageSize( const std::string& filename, + ImageDimensions size, + FittingMode::Type fittingMode, + SamplingMode::Type samplingMode, + bool orientationCorrection ); + /** + * @copydoc PlatformAbstraction::GetClosestImageSize() + */ + virtual ImageDimensions GetClosestImageSize( Integration::ResourcePointer resourceBuffer, + ImageDimensions size, + FittingMode::Type fittingMode, + SamplingMode::Type samplingMode, + bool orientationCorrection ); + + /** + * @copydoc PlatformAbstraction::LoadResource() + */ + virtual void LoadResource(const Integration::ResourceRequest& request); + + virtual Integration::ResourcePointer LoadResourceSynchronously( const Integration::ResourceType& resourceType, const std::string& resourcePath ); + + /** + * @copydoc PlatformAbstraction::SaveResource() + */ + virtual void SaveResource(const Integration::ResourceRequest& request); + + /** + * @copydoc PlatformAbstraction::DecodeBuffer() + */ + virtual Integration::BitmapPtr DecodeBuffer( const Integration::ResourceType& resourceType, uint8_t * buffer, size_t bufferSize ); + + /** + * @copydoc PlatformAbstraction::CancelLoad() + */ + virtual void CancelLoad(Integration::ResourceId id, Integration::ResourceTypeId typeId); + + /** + * @copydoc PlatformAbstraction::GetResources() + */ + virtual void GetResources(Integration::ResourceCache& cache); + + /** + * @copydoc PlatformAbstraction::IsLoading() + */ + virtual bool IsLoading(); + + /** + * @copydoc PlatformAbstraction::GetDefaultFontFamily() + */ + virtual const std::string& GetDefaultFontFamily() const; + + /** + * @copydoc PlatformAbstraction::GetDefaultFontSize() + */ + virtual int GetDefaultFontSize() const; + + /** + * Sets horizontal and vertical pixels per inch value that is used by the display + * @param[in] dpiHorizontal horizontal dpi value + * @param[in] dpiVertical vertical dpi value + */ + virtual void SetDpi (unsigned int dpiHorizontal, unsigned int dpiVertical); + + /** + * @copydoc PlatformAbstraction::LoadFile() + */ + virtual bool LoadFile( const std::string& filename, Dali::Vector< unsigned char >& buffer ) const; + + /** + * @copydoc PlatformAbstraction::SaveFile() + */ + virtual bool SaveFile( const std::string& filename, const unsigned char * buffer, unsigned int numBytes ) const; + + /** + * @copydoc PlatformAbstraction::JoinLoaderThreads() + */ + virtual void JoinLoaderThreads(); + + /** + * @copydoc PlatformAbstraction::LoadShaderBinaryFile() + */ + virtual bool LoadShaderBinaryFile( const std::string& filename, Dali::Vector< unsigned char >& buffer ) const; + + /** + * @copydoc PlatformAbstraction::SaveShaderBinaryFile() + */ + virtual bool SaveShaderBinaryFile( const std::string& filename, const unsigned char * buffer, unsigned int numBytes ) const; + + /** + * @copydoc PlatformAbstraction::UpdateDefaultsFromDevice() + */ + virtual void UpdateDefaultsFromDevice(); + +private: + std::string mGetDefaultFontFamilyResult; + Resources mResources; + Vector2 mSize; + + LoadFileResult mLoadFileResult; + + typedef std::pair< Integration::ResourceId, Integration::BitmapPtr > ResourceIdBitmapPair; + std::queue mResourceQueue; + +}; + +} // Dali + +#endif /* __DALI_TET_PLATFORM_ABSTRACTION_H__ */ diff --git a/platform-abstractions/emscripten/file.list b/platform-abstractions/emscripten/file.list new file mode 100755 index 000000000..c64c830b8 --- /dev/null +++ b/platform-abstractions/emscripten/file.list @@ -0,0 +1,13 @@ +# Add local source files here: + +slp_platform_abstraction_src_files = \ + $(slp_platform_abstraction_src_dir)/emscripten-callbacks.cpp \ + $(slp_platform_abstraction_src_dir)/emscripten-platform-abstraction.cpp + +slp_assimp_src_files = + +slp_assimp_stub_src_files = + +# Add public headers here: + +# platform_abstraction_header_files = -- 2.34.1