summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/graphics/openvg
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/graphics/openvg')
-rw-r--r--Source/WebCore/platform/graphics/openvg/EGLDisplayOpenVG.cpp455
-rw-r--r--Source/WebCore/platform/graphics/openvg/EGLDisplayOpenVG.h90
-rw-r--r--Source/WebCore/platform/graphics/openvg/EGLUtils.h71
-rw-r--r--Source/WebCore/platform/graphics/openvg/GraphicsContextOpenVG.cpp523
-rw-r--r--Source/WebCore/platform/graphics/openvg/ImageOpenVG.cpp199
-rw-r--r--Source/WebCore/platform/graphics/openvg/PainterOpenVG.cpp1242
-rw-r--r--Source/WebCore/platform/graphics/openvg/PainterOpenVG.h141
-rw-r--r--Source/WebCore/platform/graphics/openvg/PathOpenVG.cpp497
-rw-r--r--Source/WebCore/platform/graphics/openvg/PlatformPathOpenVG.h53
-rw-r--r--Source/WebCore/platform/graphics/openvg/SharedResourceOpenVG.cpp45
-rw-r--r--Source/WebCore/platform/graphics/openvg/SharedResourceOpenVG.h33
-rw-r--r--Source/WebCore/platform/graphics/openvg/SurfaceOpenVG.cpp271
-rw-r--r--Source/WebCore/platform/graphics/openvg/SurfaceOpenVG.h157
-rw-r--r--Source/WebCore/platform/graphics/openvg/TiledImageOpenVG.cpp177
-rw-r--r--Source/WebCore/platform/graphics/openvg/TiledImageOpenVG.h69
-rw-r--r--Source/WebCore/platform/graphics/openvg/VGUtils.cpp235
-rw-r--r--Source/WebCore/platform/graphics/openvg/VGUtils.h115
17 files changed, 4373 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/openvg/EGLDisplayOpenVG.cpp b/Source/WebCore/platform/graphics/openvg/EGLDisplayOpenVG.cpp
new file mode 100644
index 0000000..d681d75
--- /dev/null
+++ b/Source/WebCore/platform/graphics/openvg/EGLDisplayOpenVG.cpp
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "EGLDisplayOpenVG.h"
+
+#include "EGLUtils.h"
+#include "IntSize.h"
+#include "SurfaceOpenVG.h"
+
+#include <wtf/Assertions.h>
+#include <wtf/StdLibExtras.h>
+
+namespace WebCore {
+
+// Need to typedef this, otherwise DEFINE_STATIC_LOCAL() doesn't swallow it.
+typedef HashMap<EGLDisplay, EGLDisplayOpenVG*> EGLDisplayManagerMap;
+
+// File-static variables.
+static EGLDisplayManagerMap& displayManagers()
+{
+ DEFINE_STATIC_LOCAL(EGLDisplayManagerMap, managers, ());
+ return managers;
+}
+
+static EGLDisplayOpenVG* s_current = 0;
+
+// Static class members.
+
+SurfaceOpenVG* EGLDisplayOpenVG::currentSurface()
+{
+ EGLDisplayManagerMap& managers = displayManagers();
+ EGLDisplay currentDisplay = eglGetCurrentDisplay();
+
+ if (currentDisplay == EGL_NO_DISPLAY || !managers.contains(currentDisplay))
+ return 0;
+
+ EGLDisplayOpenVG* displayManager = managers.get(currentDisplay);
+ EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW);
+
+ if (currentSurface == EGL_NO_SURFACE || !displayManager->m_platformSurfaces.contains(currentSurface))
+ return 0;
+
+ return displayManager->m_platformSurfaces.get(currentSurface);
+}
+
+void EGLDisplayOpenVG::registerPlatformSurface(SurfaceOpenVG* platformSurface)
+{
+ EGLDisplayOpenVG* displayManager = EGLDisplayOpenVG::forDisplay(platformSurface->eglDisplay());
+ displayManager->m_platformSurfaces.set(platformSurface->eglSurface(), platformSurface);
+}
+
+void EGLDisplayOpenVG::unregisterPlatformSurface(SurfaceOpenVG* platformSurface)
+{
+ EGLDisplayOpenVG* displayManager = EGLDisplayOpenVG::forDisplay(platformSurface->eglDisplay());
+ displayManager->m_platformSurfaces.remove(platformSurface->eglSurface());
+}
+
+void EGLDisplayOpenVG::setCurrentDisplay(const EGLDisplay& display)
+{
+ s_current = EGLDisplayOpenVG::forDisplay(display);
+}
+
+EGLDisplayOpenVG* EGLDisplayOpenVG::current()
+{
+ if (!s_current) {
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ eglInitialize(display, 0, 0);
+ ASSERT_EGL_NO_ERROR();
+
+ s_current = EGLDisplayOpenVG::forDisplay(display);
+ }
+ return s_current;
+}
+
+EGLDisplayOpenVG* EGLDisplayOpenVG::forDisplay(const EGLDisplay& display)
+{
+ EGLDisplayManagerMap& managers = displayManagers();
+
+ if (!managers.contains(display))
+ managers.set(display, new EGLDisplayOpenVG(display));
+
+ return managers.get(display);
+}
+
+
+// Object/instance members.
+
+EGLDisplayOpenVG::EGLDisplayOpenVG(const EGLDisplay& display)
+ : m_display(display)
+ , m_sharedPlatformSurface(0)
+ , m_pbufferConfigId(0)
+ , m_windowConfigId(0)
+{
+ eglBindAPI(EGL_OPENVG_API);
+ ASSERT_EGL_NO_ERROR();
+}
+
+EGLDisplayOpenVG::~EGLDisplayOpenVG()
+{
+ eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ ASSERT_EGL_NO_ERROR();
+
+ delete m_sharedPlatformSurface;
+
+ HashMap<EGLSurface, EGLint>::const_iterator end = m_surfaceConfigIds.end();
+ for (HashMap<EGLSurface, EGLint>::const_iterator it = m_surfaceConfigIds.begin(); it != end; ++it)
+ destroySurface((*it).first);
+
+ eglTerminate(m_display);
+ ASSERT_EGL_NO_ERROR();
+}
+
+void EGLDisplayOpenVG::setDefaultPbufferConfig(const EGLConfig& config)
+{
+ EGLint configId;
+ EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &configId);
+ ASSERT(success == EGL_TRUE);
+ ASSERT(configId != EGL_BAD_ATTRIBUTE);
+
+ m_pbufferConfigId = configId;
+}
+
+EGLConfig EGLDisplayOpenVG::defaultPbufferConfig()
+{
+ EGLConfig config;
+ EGLint numConfigs;
+
+ // Hopefully the client will have set the pbuffer config of its choice
+ // by now - if not, use a 32-bit generic one as default.
+ if (!m_pbufferConfigId) {
+ static const EGLint configAttribs[] = {
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_ALPHA_MASK_SIZE, 1,
+ EGL_LUMINANCE_SIZE, EGL_DONT_CARE,
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
+ EGL_NONE
+ };
+ eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs);
+ } else {
+ const EGLint configAttribs[] = {
+ EGL_CONFIG_ID, m_pbufferConfigId,
+ EGL_NONE
+ };
+ eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs);
+ }
+
+ ASSERT_EGL_NO_ERROR();
+ ASSERT(numConfigs == 1);
+ return config;
+}
+
+void EGLDisplayOpenVG::setDefaultWindowConfig(const EGLConfig& config)
+{
+ EGLint configId;
+ EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &configId);
+ ASSERT(success == EGL_TRUE);
+ ASSERT(configId != EGL_BAD_ATTRIBUTE);
+
+ m_windowConfigId = configId;
+}
+
+EGLConfig EGLDisplayOpenVG::defaultWindowConfig()
+{
+ EGLConfig config;
+ EGLint numConfigs;
+
+ // Hopefully the client will have set the window config of its choice
+ // by now - if not, use a 32-bit generic one as default.
+ if (!m_windowConfigId) {
+ static const EGLint configAttribs[] = {
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_ALPHA_MASK_SIZE, 1,
+ EGL_LUMINANCE_SIZE, EGL_DONT_CARE,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
+ EGL_NONE
+ };
+ eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs);
+ } else {
+ const EGLint configAttribs[] = {
+ EGL_CONFIG_ID, m_windowConfigId,
+ EGL_NONE
+ };
+ eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs);
+ }
+
+ ASSERT_EGL_NO_ERROR();
+ ASSERT(numConfigs == 1);
+ return config;
+}
+
+SurfaceOpenVG* EGLDisplayOpenVG::sharedPlatformSurface()
+{
+ if (!m_sharedPlatformSurface) {
+ // The shared surface doesn't need to be drawn on, it just exists so
+ // that we can always make the shared context current (which in turn is
+ // the owner of long-living resources such as images, paths and fonts).
+ // We'll just make the shared surface as small as possible: 1x1 pixel.
+ EGLConfig config = defaultPbufferConfig();
+ EGLSurface surface = createPbufferSurface(IntSize(1, 1), config);
+
+ EGLContext context = eglCreateContext(m_display, config, EGL_NO_CONTEXT, 0);
+ ASSERT_EGL_NO_ERROR();
+ m_contexts.set(m_surfaceConfigIds.get(surface), context);
+
+ m_sharedPlatformSurface = new SurfaceOpenVG;
+ m_sharedPlatformSurface->m_eglDisplay = m_display;
+ m_sharedPlatformSurface->m_eglSurface = surface;
+ m_sharedPlatformSurface->m_eglContext = context;
+ m_platformSurfaces.set(surface, m_sharedPlatformSurface); // a.k.a. registerPlatformSurface()
+ }
+ return m_sharedPlatformSurface;
+}
+
+EGLSurface EGLDisplayOpenVG::createPbufferSurface(const IntSize& size, const EGLConfig& config, EGLint* errorCode)
+{
+ const EGLint attribList[] = {
+ EGL_WIDTH, size.width(),
+ EGL_HEIGHT, size.height(),
+ EGL_NONE
+ };
+ EGLSurface surface = eglCreatePbufferSurface(m_display, config, attribList);
+
+ if (errorCode)
+ *errorCode = eglGetError();
+ else
+ ASSERT_EGL_NO_ERROR();
+
+ if (surface == EGL_NO_SURFACE)
+ return EGL_NO_SURFACE;
+
+ EGLint surfaceConfigId;
+ EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &surfaceConfigId);
+ ASSERT(success == EGL_TRUE);
+ ASSERT(surfaceConfigId != EGL_BAD_ATTRIBUTE);
+
+ ASSERT(!m_surfaceConfigIds.contains(surface));
+ m_surfaceConfigIds.set(surface, surfaceConfigId);
+ return surface;
+}
+
+EGLSurface EGLDisplayOpenVG::createPbufferFromClientBuffer(
+ EGLClientBuffer clientBuffer, EGLenum bufferType, const EGLConfig& config, EGLint* errorCode)
+{
+ EGLSurface surface = eglCreatePbufferFromClientBuffer(m_display,
+ bufferType, clientBuffer, config, 0 /* attribList */);
+
+ if (errorCode)
+ *errorCode = eglGetError();
+ else
+ ASSERT_EGL_NO_ERROR();
+
+ if (surface == EGL_NO_SURFACE)
+ return EGL_NO_SURFACE;
+
+ EGLint surfaceConfigId;
+ EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &surfaceConfigId);
+ ASSERT(success == EGL_TRUE);
+ ASSERT(surfaceConfigId != EGL_BAD_ATTRIBUTE);
+
+ ASSERT(!m_surfaceConfigIds.contains(surface));
+ m_surfaceConfigIds.set(surface, surfaceConfigId);
+ return surface;
+}
+
+EGLSurface EGLDisplayOpenVG::surfaceForWindow(EGLNativeWindowType wId, const EGLConfig& config)
+{
+ if (m_windowSurfaces.contains(wId))
+ return m_windowSurfaces.get(wId);
+
+ EGLSurface surface = eglCreateWindowSurface(m_display, config, wId, 0);
+ ASSERT_EGL_NO_ERROR();
+
+ EGLint surfaceConfigId;
+ EGLBoolean success = eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &surfaceConfigId);
+ ASSERT(success == EGL_TRUE);
+ ASSERT(surfaceConfigId != EGL_BAD_ATTRIBUTE);
+
+ ASSERT(!m_surfaceConfigIds.contains(surface));
+ m_surfaceConfigIds.set(surface, surfaceConfigId);
+ return surface;
+}
+
+bool EGLDisplayOpenVG::surfacesCompatible(const EGLSurface& surface, const EGLSurface& otherSurface)
+{
+ if (surface == EGL_NO_SURFACE || otherSurface == EGL_NO_SURFACE)
+ return false;
+
+ // Currently, we assume that all surfaces known to this object are
+ // context-compatible to each other (which is reasonable to assume,
+ // otherwise eglCreateContext() would fail with EGL_BAD_MATCH for shared
+ // context compatibility anyways.
+ return m_surfaceConfigIds.contains(surface) && m_surfaceConfigIds.contains(otherSurface);
+}
+
+void EGLDisplayOpenVG::destroySurface(const EGLSurface& surface)
+{
+ ASSERT(surface != EGL_NO_SURFACE);
+
+ if (eglGetCurrentSurface(EGL_DRAW) == surface) {
+ eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ ASSERT_EGL_NO_ERROR();
+ }
+
+ // Destroy the context associated to the surface, if we already created one.
+ if (m_surfaceConfigIds.contains(surface)) {
+ EGLint surfaceConfigId = m_surfaceConfigIds.take(surface); // take = get and remove
+ bool isContextReferenced = false;
+
+ if (m_compatibleConfigIds.contains(surfaceConfigId))
+ surfaceConfigId = m_compatibleConfigIds.get(surfaceConfigId);
+
+ HashMap<EGLSurface, EGLint>::iterator end = m_surfaceConfigIds.end();
+
+ // ...but only if there's no other surfaces associated to that context.
+ for (HashMap<EGLSurface, EGLint>::iterator it = m_surfaceConfigIds.begin(); it != end; ++it) {
+ if ((*it).second == surfaceConfigId) {
+ isContextReferenced = true;
+ break;
+ }
+ }
+ if (!isContextReferenced && m_contexts.contains(surfaceConfigId)) {
+ EGLContext context = m_contexts.take(surfaceConfigId);
+ eglDestroyContext(m_display, context);
+ ASSERT_EGL_NO_ERROR();
+ }
+ }
+
+ m_platformSurfaces.remove(surface);
+
+ HashMap<EGLNativeWindowType, EGLSurface>::iterator end = m_windowSurfaces.end();
+ for (HashMap<EGLNativeWindowType, EGLSurface>::iterator it = m_windowSurfaces.begin(); it != end; ++it) {
+ if ((*it).second == surface) {
+ m_windowSurfaces.remove(it);
+ break;
+ }
+ }
+
+ eglDestroySurface(m_display, surface);
+ ASSERT_EGL_NO_ERROR();
+}
+
+EGLContext EGLDisplayOpenVG::contextForSurface(const EGLSurface& surface)
+{
+ ASSERT(surface != EGL_NO_SURFACE);
+
+ if (m_platformSurfaces.contains(surface))
+ return m_platformSurfaces.get(surface)->eglContext();
+
+ eglBindAPI(EGL_OPENVG_API);
+ ASSERT_EGL_NO_ERROR();
+
+ EGLint surfaceConfigId;
+
+ if (m_surfaceConfigIds.contains(surface))
+ surfaceConfigId = m_surfaceConfigIds.get(surface);
+ else {
+ // Retrieve the same EGL config for context creation that was used to
+ // create the the EGL surface.
+ EGLBoolean success = eglQuerySurface(m_display, surface, EGL_CONFIG_ID, &surfaceConfigId);
+ ASSERT(success == EGL_TRUE);
+ ASSERT(surfaceConfigId != EGL_BAD_ATTRIBUTE);
+
+ m_surfaceConfigIds.set(surface, surfaceConfigId);
+ }
+
+ if (m_compatibleConfigIds.contains(surfaceConfigId))
+ surfaceConfigId = m_compatibleConfigIds.get(surfaceConfigId);
+
+ if (m_contexts.contains(surfaceConfigId))
+ return m_contexts.get(surfaceConfigId);
+
+ if (!m_sharedPlatformSurface) // shared context has not been created yet
+ sharedPlatformSurface(); // creates the shared surface & context
+
+ EGLDisplay currentDisplay = eglGetCurrentDisplay();
+ EGLSurface currentReadSurface = eglGetCurrentSurface(EGL_READ);
+ EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW);
+ EGLContext currentContext = eglGetCurrentContext();
+
+ // Before creating a new context, let's try whether an existing one
+ // is compatible with the surface. EGL doesn't give us a different way
+ // to check context/surface compatibility than trying it out, so let's
+ // do just that.
+ HashMap<EGLint, EGLContext>::iterator end = m_contexts.end();
+
+ for (HashMap<EGLint, EGLContext>::iterator it = m_contexts.begin(); it != end; ++it) {
+ eglMakeCurrent(m_display, surface, surface, (*it).second);
+ if (eglGetError() == EGL_SUCCESS) {
+ // Restore previous surface/context.
+ if (currentContext != EGL_NO_CONTEXT) {
+ eglMakeCurrent(currentDisplay, currentReadSurface, currentDrawSurface, currentContext);
+ ASSERT_EGL_NO_ERROR();
+ }
+ // Cool, surface is compatible to one of our existing contexts.
+ m_compatibleConfigIds.set(surfaceConfigId, (*it).first);
+ return (*it).second;
+ }
+ }
+ // Restore previous surface/context.
+ if (currentContext != EGL_NO_CONTEXT) {
+ eglMakeCurrent(currentDisplay, currentReadSurface, currentDrawSurface, currentContext);
+ ASSERT_EGL_NO_ERROR();
+ }
+
+ EGLConfig config;
+ EGLint numConfigs;
+
+ const EGLint configAttribs[] = {
+ EGL_CONFIG_ID, surfaceConfigId,
+ EGL_NONE
+ };
+
+ eglChooseConfig(m_display, configAttribs, &config, 1, &numConfigs);
+ ASSERT_EGL_NO_ERROR();
+ ASSERT(numConfigs == 1);
+
+ // We share all of the images and paths amongst the different contexts,
+ // so that they can be used in all of them. Resources that are created
+ // while m_sharedPlatformSurface->context() is current will be
+ // accessible from all other contexts, but are not restricted to the
+ // lifetime of those contexts.
+ EGLContext context = eglCreateContext(m_display, config, m_sharedPlatformSurface->eglContext(), 0);
+ ASSERT_EGL_NO_ERROR();
+
+ ASSERT(!m_contexts.contains(surfaceConfigId));
+ m_contexts.set(surfaceConfigId, context);
+ return context;
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/openvg/EGLDisplayOpenVG.h b/Source/WebCore/platform/graphics/openvg/EGLDisplayOpenVG.h
new file mode 100644
index 0000000..0dff6c9
--- /dev/null
+++ b/Source/WebCore/platform/graphics/openvg/EGLDisplayOpenVG.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef EGLDisplayOpenVG_h
+#define EGLDisplayOpenVG_h
+
+#include <egl.h>
+#include <wtf/HashMap.h>
+
+namespace WebCore {
+
+class IntSize;
+class SurfaceOpenVG;
+
+class EGLDisplayOpenVG {
+public:
+ friend class SurfaceOpenVG;
+
+ static SurfaceOpenVG* currentSurface();
+ static void setCurrentDisplay(const EGLDisplay&);
+ static EGLDisplayOpenVG* current();
+ static EGLDisplayOpenVG* forDisplay(const EGLDisplay&);
+
+ void setDefaultPbufferConfig(const EGLConfig&);
+ EGLConfig defaultPbufferConfig();
+ void setDefaultWindowConfig(const EGLConfig&);
+ EGLConfig defaultWindowConfig();
+
+ EGLDisplay display() const { return m_display; }
+ SurfaceOpenVG* sharedPlatformSurface();
+
+ /** Creates a pbuffer surface using the given config. If no surface
+ * could be created, EGL_NO_SURFACE is returned and errors can be
+ * checked with the value that is written to the errorCode parameter
+ * If no surface could be created and errorCode is zero, this method
+ * will trigger an assertion by itself. */
+ EGLSurface createPbufferSurface(const IntSize&, const EGLConfig&, EGLint* errorCode = 0);
+ EGLSurface createPbufferFromClientBuffer(EGLClientBuffer, EGLenum bufferType, const EGLConfig&, EGLint* errorCode = 0);
+
+ EGLSurface surfaceForWindow(EGLNativeWindowType, const EGLConfig&);
+
+ bool surfacesCompatible(const EGLSurface&, const EGLSurface&);
+
+ /** Destroy the surface and its corresponding context (unless another
+ * surface is still using the same context, in which case the context
+ * is not destroyed). */
+ void destroySurface(const EGLSurface&);
+
+ /** Return the context corresponding to the surface.
+ * If no corresponding context exists, one is created automatically. */
+ EGLContext contextForSurface(const EGLSurface&);
+
+private:
+ static void registerPlatformSurface(SurfaceOpenVG*);
+ static void unregisterPlatformSurface(SurfaceOpenVG*);
+
+ EGLDisplayOpenVG(const EGLDisplay& display);
+ ~EGLDisplayOpenVG();
+
+ EGLDisplay m_display;
+ SurfaceOpenVG* m_sharedPlatformSurface;
+ EGLint m_pbufferConfigId;
+ EGLint m_windowConfigId;
+
+ HashMap<EGLSurface, SurfaceOpenVG*> m_platformSurfaces;
+ HashMap<EGLNativeWindowType, EGLSurface> m_windowSurfaces;
+ HashMap<EGLSurface, EGLint> m_surfaceConfigIds;
+ HashMap<EGLint, EGLint> m_compatibleConfigIds;
+ HashMap<EGLint, EGLContext> m_contexts;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/openvg/EGLUtils.h b/Source/WebCore/platform/graphics/openvg/EGLUtils.h
new file mode 100644
index 0000000..6f5d793
--- /dev/null
+++ b/Source/WebCore/platform/graphics/openvg/EGLUtils.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef EGLUtils_h
+#define EGLUtils_h
+
+#include <egl.h>
+#include <wtf/Assertions.h>
+
+static inline const char* toEGLErrorConstant(EGLint error)
+{
+ switch (error) {
+ case EGL_NOT_INITIALIZED:
+ return "EGL_NOT_INITIALIZED";
+ case EGL_BAD_ACCESS:
+ return "EGL_BAD_ACCESS";
+ case EGL_BAD_ALLOC:
+ return "EGL_BAD_ALLOC";
+ case EGL_BAD_ATTRIBUTE:
+ return "EGL_BAD_ATTRIBUTE";
+ case EGL_BAD_CONTEXT:
+ return "EGL_BAD_CONTEXT";
+ case EGL_BAD_CONFIG:
+ return "EGL_BAD_CONFIG";
+ case EGL_BAD_CURRENT_SURFACE:
+ return "EGL_BAD_CURRENT_SURFACE";
+ case EGL_BAD_DISPLAY:
+ return "EGL_BAD_DISPLAY";
+ case EGL_BAD_SURFACE:
+ return "EGL_BAD_SURFACE";
+ case EGL_BAD_MATCH:
+ return "EGL_BAD_MATCH";
+ case EGL_BAD_PARAMETER:
+ return "EGL_BAD_PARAMETER";
+ case EGL_BAD_NATIVE_PIXMAP:
+ return "EGL_BAD_NATIVE_PIXMAP";
+ case EGL_BAD_NATIVE_WINDOW:
+ return "EGL_BAD_NATIVE_WINDOW";
+ case EGL_CONTEXT_LOST:
+ return "EGL_CONTEXT_LOST";
+ default:
+ return "UNKNOWN_ERROR";
+ }
+}
+
+#if ASSERT_DISABLED
+#define ASSERT_EGL_NO_ERROR() ((void)0)
+#else
+#define ASSERT_EGL_NO_ERROR() do { \
+ EGLint eglErrorCode = eglGetError(); \
+ ASSERT_WITH_MESSAGE(eglErrorCode == EGL_SUCCESS, "Found %s", toEGLErrorConstant(eglErrorCode)); \
+} while (0)
+#endif
+
+#endif
diff --git a/Source/WebCore/platform/graphics/openvg/GraphicsContextOpenVG.cpp b/Source/WebCore/platform/graphics/openvg/GraphicsContextOpenVG.cpp
new file mode 100644
index 0000000..6466a9c
--- /dev/null
+++ b/Source/WebCore/platform/graphics/openvg/GraphicsContextOpenVG.cpp
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "GraphicsContext.h"
+
+#include "AffineTransform.h"
+#include "KURL.h"
+#include "NotImplemented.h"
+#include "PainterOpenVG.h"
+#include "SurfaceOpenVG.h"
+
+#include <wtf/Assertions.h>
+#include <wtf/MathExtras.h>
+#include <wtf/UnusedParam.h>
+#include <wtf/Vector.h>
+
+#if PLATFORM(EGL)
+#include "EGLDisplayOpenVG.h"
+#include "EGLUtils.h"
+#include <egl.h>
+#endif
+
+namespace WebCore {
+
+// typedef'ing doesn't work, let's inherit from PainterOpenVG instead
+class GraphicsContextPlatformPrivate : public PainterOpenVG {
+public:
+ GraphicsContextPlatformPrivate(SurfaceOpenVG* surface)
+ : PainterOpenVG(surface)
+ {
+ }
+};
+
+void GraphicsContext::platformInit(SurfaceOpenVG* surface)
+{
+ m_data = surface ? new GraphicsContextPlatformPrivate(surface) : 0;
+ setPaintingDisabled(!surface);
+}
+
+void GraphicsContext::platformDestroy()
+{
+ delete m_data;
+}
+
+PlatformGraphicsContext* GraphicsContext::platformContext() const
+{
+ if (paintingDisabled())
+ return 0;
+
+ return m_data->baseSurface();
+}
+
+AffineTransform GraphicsContext::getCTM() const
+{
+ if (paintingDisabled())
+ return AffineTransform();
+
+ return m_data->transformation();
+}
+
+void GraphicsContext::savePlatformState()
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->save();
+}
+
+void GraphicsContext::restorePlatformState()
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->restore();
+}
+
+void GraphicsContext::drawRect(const IntRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->drawRect(rect);
+}
+
+void GraphicsContext::drawLine(const IntPoint& from, const IntPoint& to)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->drawLine(from, to);
+}
+
+/**
+ * Draw the largest ellipse that fits into the given rectangle.
+ */
+void GraphicsContext::drawEllipse(const IntRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->drawEllipse(rect);
+}
+
+void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->drawArc(rect, startAngle, angleSpan, VG_STROKE_PATH);
+}
+
+void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, bool shouldAntialias)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->drawPolygon(numPoints, points);
+
+ UNUSED_PARAM(shouldAntialias); // FIXME
+}
+
+void GraphicsContext::fillPath(const Path& path)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->drawPath(path, VG_FILL_PATH, m_state.fillRule);
+}
+
+void GraphicsContext::strokePath(const Path& path)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->drawPath(path, VG_STROKE_PATH, m_state.fillRule);
+}
+
+void GraphicsContext::fillRect(const FloatRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->drawRect(rect, VG_FILL_PATH);
+}
+
+void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
+{
+ if (paintingDisabled())
+ return;
+
+ Color oldColor = m_data->fillColor();
+ m_data->setFillColor(color);
+ m_data->drawRect(rect, VG_FILL_PATH);
+ m_data->setFillColor(oldColor);
+
+ UNUSED_PARAM(colorSpace); // FIXME
+}
+
+void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace)
+{
+ if (paintingDisabled())
+ return;
+
+ Color oldColor = m_data->fillColor();
+ m_data->setFillColor(color);
+ m_data->drawRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight, VG_FILL_PATH);
+ m_data->setFillColor(oldColor);
+
+ UNUSED_PARAM(colorSpace); // FIXME
+}
+
+void GraphicsContext::clip(const FloatRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->intersectClipRect(rect);
+}
+
+void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->clipPath(path, PainterOpenVG::IntersectClip, clipRule);
+}
+
+void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
+{
+ if (paintingDisabled())
+ return;
+
+ if (rects.isEmpty())
+ return;
+
+ // FIXME: We just unite all focus ring rects into one for now.
+ // We should outline the edge of the full region.
+ offset += (width - 1) / 2;
+ IntRect finalFocusRect;
+
+ for (unsigned i = 0; i < rects.size(); i++) {
+ IntRect focusRect = rects[i];
+ focusRect.inflate(offset);
+ finalFocusRect.unite(focusRect);
+ }
+
+ StrokeStyle oldStyle = m_data->strokeStyle();
+ Color oldStrokeColor = m_data->strokeColor();
+ m_data->setStrokeStyle(DashedStroke);
+ m_data->setStrokeColor(color);
+ strokeRect(FloatRect(finalFocusRect), 1.f);
+ m_data->setStrokeStyle(oldStyle);
+ m_data->setStrokeColor(oldStrokeColor);
+}
+
+void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool printing)
+{
+ if (paintingDisabled())
+ return;
+
+ if (width <= 0)
+ return;
+
+ StrokeStyle oldStyle = m_data->strokeStyle();
+ m_data->setStrokeStyle(SolidStroke);
+ drawLine(origin, origin + IntSize(width, 0));
+ m_data->setStrokeStyle(oldStyle);
+
+ UNUSED_PARAM(printing);
+}
+
+void GraphicsContext::drawLineForTextChecking(const IntPoint& origin, int width, TextCheckingLineStyle style)
+{
+ if (paintingDisabled())
+ return;
+
+ notImplemented();
+ UNUSED_PARAM(origin);
+ UNUSED_PARAM(width);
+ UNUSED_PARAM(style);
+}
+
+FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect)
+{
+ if (paintingDisabled())
+ return FloatRect();
+
+ return FloatRect(enclosingIntRect(m_data->transformation().mapRect(rect)));
+}
+
+void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace colorSpace)
+{
+ if (paintingDisabled())
+ return;
+
+ notImplemented();
+ UNUSED_PARAM(size);
+ UNUSED_PARAM(blur);
+ UNUSED_PARAM(color);
+ UNUSED_PARAM(colorSpace);
+}
+
+void GraphicsContext::clearPlatformShadow()
+{
+ if (paintingDisabled())
+ return;
+
+ notImplemented();
+}
+
+void GraphicsContext::beginTransparencyLayer(float opacity)
+{
+ if (paintingDisabled())
+ return;
+
+ notImplemented();
+ UNUSED_PARAM(opacity);
+}
+
+void GraphicsContext::endTransparencyLayer()
+{
+ if (paintingDisabled())
+ return;
+
+ notImplemented();
+}
+
+void GraphicsContext::clearRect(const FloatRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ CompositeOperator op = m_data->compositeOperation();
+ m_data->setCompositeOperation(CompositeClear);
+ m_data->drawRect(rect, VG_FILL_PATH);
+ m_data->setCompositeOperation(op);
+}
+
+void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
+{
+ if (paintingDisabled())
+ return;
+
+ float oldThickness = m_data->strokeThickness();
+ m_data->setStrokeThickness(lineWidth);
+ m_data->drawRect(rect, VG_STROKE_PATH);
+ m_data->setStrokeThickness(oldThickness);
+}
+
+void GraphicsContext::setLineCap(LineCap lc)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->setLineCap(lc);
+}
+
+void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->setLineDash(dashes, dashOffset);
+}
+
+void GraphicsContext::setLineJoin(LineJoin lj)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->setLineJoin(lj);
+}
+
+void GraphicsContext::setMiterLimit(float limit)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->setMiterLimit(limit);
+}
+
+void GraphicsContext::setAlpha(float opacity)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->setOpacity(opacity);
+}
+
+void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->setCompositeOperation(op);
+}
+
+void GraphicsContext::clip(const Path& path)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->clipPath(path, PainterOpenVG::IntersectClip, m_state.fillRule);
+}
+
+void GraphicsContext::canvasClip(const Path& path)
+{
+ clip(path);
+}
+
+void GraphicsContext::clipOut(const Path& path)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->clipPath(path, PainterOpenVG::SubtractClip, m_state.fillRule);
+}
+
+void GraphicsContext::scale(const FloatSize& scaleFactors)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->scale(scaleFactors);
+}
+
+void GraphicsContext::rotate(float radians)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->rotate(radians);
+}
+
+void GraphicsContext::translate(float dx, float dy)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->translate(dx, dy);
+}
+
+void GraphicsContext::clipOut(const IntRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ Path path;
+ path.addRect(rect);
+ m_data->clipPath(path, PainterOpenVG::SubtractClip, m_state.fillRule);
+}
+
+void GraphicsContext::clipToImageBuffer(const FloatRect& rect, const ImageBuffer* imageBuffer)
+{
+ if (paintingDisabled())
+ return;
+
+ notImplemented();
+ UNUSED_PARAM(rect);
+ UNUSED_PARAM(imageBuffer);
+}
+
+void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
+{
+ if (paintingDisabled())
+ return;
+
+ Path path;
+ path.addEllipse(rect);
+ path.addEllipse(FloatRect(rect.x() + thickness, rect.y() + thickness,
+ rect.width() - (thickness * 2), rect.height() - (thickness * 2)));
+
+ m_data->clipPath(path, PainterOpenVG::IntersectClip, m_state.fillRule);
+}
+
+void GraphicsContext::concatCTM(const AffineTransform& transformation)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->concatTransformation(transformation);
+}
+
+void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
+{
+ notImplemented();
+ UNUSED_PARAM(link);
+ UNUSED_PARAM(destRect);
+}
+
+void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->setStrokeColor(color);
+
+ UNUSED_PARAM(colorSpace); // FIXME
+}
+
+void GraphicsContext::setPlatformStrokeStyle(StrokeStyle strokeStyle)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->setStrokeStyle(strokeStyle);
+}
+
+void GraphicsContext::setPlatformStrokeThickness(float thickness)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->setStrokeThickness(thickness);
+}
+
+void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->setFillColor(color);
+
+ UNUSED_PARAM(colorSpace); // FIXME
+}
+
+void GraphicsContext::setPlatformShouldAntialias(bool enable)
+{
+ if (paintingDisabled())
+ return;
+
+ m_data->setAntialiasingEnabled(enable);
+}
+
+void GraphicsContext::setImageInterpolationQuality(InterpolationQuality)
+{
+ notImplemented();
+}
+
+InterpolationQuality GraphicsContext::imageInterpolationQuality() const
+{
+ notImplemented();
+ return InterpolationDefault;
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/openvg/ImageOpenVG.cpp b/Source/WebCore/platform/graphics/openvg/ImageOpenVG.cpp
new file mode 100644
index 0000000..4c1932a
--- /dev/null
+++ b/Source/WebCore/platform/graphics/openvg/ImageOpenVG.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "Image.h"
+
+#include "AffineTransform.h"
+#include "BitmapImage.h"
+#include "FloatRect.h"
+#include "GraphicsContext.h"
+#include "ImageDecoder.h"
+#include "ImageObserver.h"
+#include "IntSize.h"
+#include "NotImplemented.h"
+#include "PainterOpenVG.h"
+#include "SurfaceOpenVG.h"
+#include "TiledImageOpenVG.h"
+#include "VGUtils.h"
+
+#include <wtf/MathExtras.h>
+
+namespace WebCore {
+
+bool FrameData::clear(bool clearMetadata)
+{
+ if (clearMetadata)
+ m_haveMetadata = false;
+
+ if (m_frame) {
+ delete m_frame;
+ m_frame = 0;
+ return true;
+ }
+ return false;
+}
+
+BitmapImage::BitmapImage(TiledImageOpenVG* tiledImage, ImageObserver* observer)
+ : Image(observer)
+ , m_size(tiledImage->size())
+ , m_currentFrame(0)
+ , m_frames(1)
+ , m_frameTimer(0)
+ , m_repetitionCount(cAnimationNone)
+ , m_repetitionCountStatus(Unknown)
+ , m_repetitionsComplete(0)
+ , m_desiredFrameStartTime(0)
+ , m_isSolidColor(false)
+ , m_checkedForSolidColor(false)
+ , m_animationFinished(false)
+ , m_allDataReceived(false)
+ , m_haveSize(true)
+ , m_sizeAvailable(true)
+ , m_hasUniformFrameSize(true)
+ , m_haveFrameCount(true)
+ , m_frameCount(1)
+{
+ initPlatformData();
+
+ ASSERT(m_size.width() > 0);
+ ASSERT(m_size.height() > 0);
+
+ m_decodedSize = m_size.width() * m_size.height() * 4;
+
+ m_frames[0].m_frame = tiledImage;
+ m_frames[0].m_hasAlpha = true;
+ m_frames[0].m_isComplete = true;
+ m_frames[0].m_haveMetadata = true;
+ checkForSolidColor();
+}
+
+void BitmapImage::checkForSolidColor()
+{
+ TiledImageOpenVG* tiledImage = 0;
+
+ if (m_frameCount == 1 && m_size.width() == 1 && m_size.height() == 1)
+ tiledImage = nativeImageForCurrentFrame();
+
+ if (tiledImage) {
+ m_isSolidColor = true;
+ RGBA32 pixel;
+ vgGetImageSubData(tiledImage->tile(0, 0), &pixel, 0, VG_sARGB_8888, 0, 0, 1, 1);
+ ASSERT_VG_NO_ERROR();
+ m_solidColor.setRGB(pixel);
+ } else
+ m_isSolidColor = false;
+
+ m_checkedForSolidColor = true;
+}
+
+void BitmapImage::initPlatformData()
+{
+}
+
+void BitmapImage::invalidatePlatformData()
+{
+}
+
+#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
+static void adjustSourceRectForDownSampling(FloatRect& srcRect, const IntSize& origSize, const IntSize& scaledSize)
+{
+ // We assume down-sampling zoom rates in X direction and in Y direction are same.
+ if (origSize.width() == scaledSize.width())
+ return;
+
+ // Image has been down sampled.
+ double rate = static_cast<double>(scaledSize.width()) / origSize.width();
+ double temp = srcRect.right() * rate;
+ srcRect.setX(srcRect.x() * rate);
+ srcRect.setWidth(temp - srcRect.x());
+ temp = srcRect.bottom() * rate;
+ srcRect.setY(srcRect.y() * rate);
+ srcRect.setHeight(temp - srcRect.y());
+}
+#endif
+
+void BitmapImage::draw(GraphicsContext* context, const FloatRect& dst, const FloatRect& src, ColorSpace styleColorSpace, CompositeOperator op)
+{
+ if (dst.isEmpty() || src.isEmpty())
+ return;
+
+ NativeImagePtr image = nativeImageForCurrentFrame();
+ if (!image)
+ return;
+
+ startAnimation();
+
+ if (mayFillWithSolidColor()) {
+ fillWithSolidColor(context, dst, solidColor(), styleColorSpace, op);
+ return;
+ }
+
+ context->save();
+
+ // Set the compositing operation.
+ if (op == CompositeSourceOver && !frameHasAlphaAtIndex(m_currentFrame))
+ context->setCompositeOperation(CompositeCopy);
+ else
+ context->setCompositeOperation(op);
+
+ FloatRect srcRectLocal(src);
+#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
+ adjustSourceRectForDownSampling(srcRectLocal, size(), image->size());
+#endif
+
+ context->platformContext()->activePainter()->drawImage(image, dst, srcRectLocal);
+ context->restore();
+
+ if (imageObserver())
+ imageObserver()->didDraw(this);
+}
+
+void Image::drawPattern(GraphicsContext* context, const FloatRect& src,
+ const AffineTransform& patternTransformation,
+ const FloatPoint& phase, ColorSpace styleColorSpace,
+ CompositeOperator op, const FloatRect& dst)
+{
+ if (dst.isEmpty() || src.isEmpty())
+ return;
+
+ NativeImagePtr image = nativeImageForCurrentFrame();
+ if (!image)
+ return;
+
+ startAnimation();
+
+ if (mayFillWithSolidColor()) {
+ fillWithSolidColor(context, dst, solidColor(), styleColorSpace, op);
+ return;
+ }
+
+ notImplemented();
+
+ if (imageObserver())
+ imageObserver()->didDraw(this);
+}
+
+PassRefPtr<Image> Image::loadPlatformResource(char const* name)
+{
+ notImplemented();
+ return 0;
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/openvg/PainterOpenVG.cpp b/Source/WebCore/platform/graphics/openvg/PainterOpenVG.cpp
new file mode 100644
index 0000000..3e2b92c
--- /dev/null
+++ b/Source/WebCore/platform/graphics/openvg/PainterOpenVG.cpp
@@ -0,0 +1,1242 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "PainterOpenVG.h"
+
+#include "AffineTransform.h"
+#include "Color.h"
+#include "DashArray.h"
+#include "FloatPoint.h"
+#include "FloatQuad.h"
+#include "FloatRect.h"
+#include "IntRect.h"
+#include "IntSize.h"
+#include "NotImplemented.h"
+#include "PlatformPathOpenVG.h"
+#include "SurfaceOpenVG.h"
+#include "TiledImageOpenVG.h"
+#include "VGUtils.h"
+
+#if PLATFORM(EGL)
+#include "EGLUtils.h"
+#endif
+
+#include <vgu.h>
+
+#include <wtf/Assertions.h>
+#include <wtf/MathExtras.h>
+
+namespace WebCore {
+
+static bool isNonRotatedAffineTransformation(const AffineTransform& t)
+{
+ return t.b() <= FLT_EPSILON && t.c() <= FLT_EPSILON;
+}
+
+static VGCapStyle toVGCapStyle(LineCap lineCap)
+{
+ switch (lineCap) {
+ case RoundCap:
+ return VG_CAP_ROUND;
+ case SquareCap:
+ return VG_CAP_SQUARE;
+ case ButtCap:
+ default:
+ return VG_CAP_BUTT;
+ }
+}
+
+static VGJoinStyle toVGJoinStyle(LineJoin lineJoin)
+{
+ switch (lineJoin) {
+ case RoundJoin:
+ return VG_JOIN_ROUND;
+ case BevelJoin:
+ return VG_JOIN_BEVEL;
+ case MiterJoin:
+ default:
+ return VG_JOIN_MITER;
+ }
+}
+
+static VGFillRule toVGFillRule(WindRule fillRule)
+{
+ return fillRule == RULE_EVENODD ? VG_EVEN_ODD : VG_NON_ZERO;
+}
+
+static VGuint colorToVGColor(const Color& color)
+{
+ VGuint vgColor = color.red();
+ vgColor = (vgColor << 8) | color.green();
+ vgColor = (vgColor << 8) | color.blue();
+ vgColor = (vgColor << 8) | color.alpha();
+ return vgColor;
+}
+
+static void setVGSolidColor(VGPaintMode paintMode, const Color& color)
+{
+ VGPaint paint = vgCreatePaint();
+ vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
+ vgSetColor(paint, colorToVGColor(color));
+ vgSetPaint(paint, paintMode);
+ vgDestroyPaint(paint);
+ ASSERT_VG_NO_ERROR();
+}
+
+
+struct PlatformPainterState {
+ AffineTransform surfaceTransformation;
+ CompositeOperator compositeOperation;
+ float opacity;
+
+ bool scissoringEnabled;
+ FloatRect scissorRect;
+#ifdef OPENVG_VERSION_1_1
+ bool maskingChangedAndEnabled;
+ VGMaskLayer mask;
+#endif
+
+ Color fillColor;
+ StrokeStyle strokeStyle;
+ Color strokeColor;
+ float strokeThickness;
+ LineCap strokeLineCap;
+ LineJoin strokeLineJoin;
+ float strokeMiterLimit;
+ DashArray strokeDashArray;
+ float strokeDashOffset;
+
+ TextDrawingModeFlags textDrawingMode;
+ bool antialiasingEnabled;
+
+ PlatformPainterState()
+ : compositeOperation(CompositeSourceOver)
+ , opacity(1.0)
+ , scissoringEnabled(false)
+#ifdef OPENVG_VERSION_1_1
+ , maskingChangedAndEnabled(false)
+ , mask(VG_INVALID_HANDLE)
+#endif
+ , fillColor(Color::black)
+ , strokeStyle(NoStroke)
+ , strokeThickness(0.0)
+ , strokeLineCap(ButtCap)
+ , strokeLineJoin(MiterJoin)
+ , strokeMiterLimit(4.0)
+ , strokeDashOffset(0.0)
+ , textDrawingMode(TextModeFill)
+ , antialiasingEnabled(true)
+ {
+ }
+
+ ~PlatformPainterState()
+ {
+#ifdef OPENVG_VERSION_1_1
+ if (maskingChangedAndEnabled && mask != VG_INVALID_HANDLE) {
+ vgDestroyMaskLayer(mask);
+ ASSERT_VG_NO_ERROR();
+ mask = VG_INVALID_HANDLE;
+ }
+#endif
+ }
+
+ PlatformPainterState(const PlatformPainterState& state)
+ {
+ surfaceTransformation = state.surfaceTransformation;
+
+ scissoringEnabled = state.scissoringEnabled;
+ scissorRect = state.scissorRect;
+#ifdef OPENVG_VERSION_1_1
+ maskingChangedAndEnabled = false;
+ mask = state.mask;
+#endif
+ copyPaintState(&state);
+ }
+
+ inline bool maskingEnabled()
+ {
+ return maskingChangedAndEnabled || mask != VG_INVALID_HANDLE;
+ }
+
+ void copyPaintState(const PlatformPainterState* other)
+ {
+ compositeOperation = other->compositeOperation;
+ opacity = other->opacity;
+
+ fillColor = other->fillColor;
+ strokeStyle = other->strokeStyle;
+ strokeColor = other->strokeColor;
+ strokeThickness = other->strokeThickness;
+ strokeLineCap = other->strokeLineCap;
+ strokeLineJoin = other->strokeLineJoin;
+ strokeMiterLimit = other->strokeMiterLimit;
+ strokeDashArray = other->strokeDashArray;
+ strokeDashOffset = other->strokeDashOffset;
+
+ textDrawingMode = other->textDrawingMode;
+ antialiasingEnabled = other->antialiasingEnabled;
+ }
+
+ void applyState(PainterOpenVG* painter)
+ {
+ ASSERT(painter);
+
+ setVGSolidColor(VG_FILL_PATH, fillColor);
+ setVGSolidColor(VG_STROKE_PATH, strokeColor);
+
+ vgSetf(VG_STROKE_LINE_WIDTH, strokeThickness);
+ vgSeti(VG_STROKE_CAP_STYLE, toVGCapStyle(strokeLineCap));
+ vgSeti(VG_STROKE_JOIN_STYLE, toVGJoinStyle(strokeLineJoin));
+ vgSetf(VG_STROKE_MITER_LIMIT, strokeMiterLimit);
+
+ if (antialiasingEnabled)
+ vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_FASTER);
+ else
+ vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_NONANTIALIASED);
+
+ applyBlending(painter);
+ applyStrokeStyle();
+
+ applyTransformation(painter);
+ applyScissorRect();
+
+#ifdef OPENVG_VERSION_1_1
+ if (maskingEnabled()) {
+ vgSeti(VG_MASKING, VG_TRUE);
+ if (mask != VG_INVALID_HANDLE)
+ vgMask(mask, VG_SET_MASK, 0, 0, painter->surface()->width(), painter->surface()->height());
+ } else
+ vgSeti(VG_MASKING, VG_FALSE);
+#endif
+ ASSERT_VG_NO_ERROR();
+ }
+
+ void applyBlending(PainterOpenVG* painter)
+ {
+ VGBlendMode blendMode = VG_BLEND_SRC_OVER;
+
+ switch (compositeOperation) {
+ case CompositeClear: {
+ // Clear means "set to fully transparent regardless of SRC".
+ // We implement that by multiplying DST with white color
+ // (= no changes) and an alpha of 1.0 - opacity, so the destination
+ // pixels will be fully transparent when opacity == 1.0 and
+ // unchanged when opacity == 0.0.
+ blendMode = VG_BLEND_DST_IN;
+ const VGfloat values[] = { 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 - opacity };
+ vgSetfv(VG_COLOR_TRANSFORM_VALUES, 8, values);
+ vgSeti(VG_COLOR_TRANSFORM, VG_TRUE);
+ ASSERT_VG_NO_ERROR();
+ break;
+ }
+ case CompositeCopy:
+ blendMode = VG_BLEND_SRC;
+ break;
+ case CompositeSourceOver:
+ blendMode = VG_BLEND_SRC_OVER;
+ break;
+ case CompositeSourceIn:
+ blendMode = VG_BLEND_SRC_IN;
+ break;
+ case CompositeSourceOut:
+ notImplemented();
+ break;
+ case CompositeSourceAtop:
+ notImplemented();
+ break;
+ case CompositeDestinationOver:
+ blendMode = VG_BLEND_DST_OVER;
+ break;
+ case CompositeDestinationIn:
+ blendMode = VG_BLEND_DST_IN;
+ break;
+ case CompositeDestinationOut:
+ notImplemented();
+ break;
+ case CompositeDestinationAtop:
+ notImplemented();
+ break;
+ case CompositeXOR:
+ notImplemented();
+ break;
+ case CompositePlusDarker:
+ blendMode = VG_BLEND_DARKEN;
+ break;
+ case CompositeHighlight:
+ notImplemented();
+ break;
+ case CompositePlusLighter:
+ blendMode = VG_BLEND_LIGHTEN;
+ break;
+ }
+
+ if (compositeOperation != CompositeClear) {
+ if (opacity >= (1.0 - FLT_EPSILON))
+ vgSeti(VG_COLOR_TRANSFORM, VG_FALSE);
+ else if (blendMode == VG_BLEND_SRC) {
+ blendMode = VG_BLEND_SRC_OVER;
+ VGfloat values[] = { 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, opacity };
+ vgSetfv(VG_COLOR_TRANSFORM_VALUES, 8, values);
+ vgSeti(VG_COLOR_TRANSFORM, VG_TRUE);
+ } else {
+ VGfloat values[] = { 1.0, 1.0, 1.0, opacity, 0.0, 0.0, 0.0, 0.0 };
+ vgSetfv(VG_COLOR_TRANSFORM_VALUES, 8, values);
+ vgSeti(VG_COLOR_TRANSFORM, VG_TRUE);
+ }
+ ASSERT_VG_NO_ERROR();
+ }
+
+ vgSeti(VG_BLEND_MODE, blendMode);
+ ASSERT_VG_NO_ERROR();
+ }
+
+ void applyTransformation(PainterOpenVG* painter)
+ {
+ // There are *five* separate transforms that can be applied to OpenVG as of 1.1
+ // but it is not clear that we need to set them separately. Instead we set them
+ // all right here and let this be a call to essentially set the world transformation!
+ VGMatrix vgMatrix(surfaceTransformation);
+ const VGfloat* vgFloatArray = vgMatrix.toVGfloat();
+
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
+ vgLoadMatrix(vgFloatArray);
+ ASSERT_VG_NO_ERROR();
+
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
+ vgLoadMatrix(vgFloatArray);
+ ASSERT_VG_NO_ERROR();
+
+#ifdef OPENVG_VERSION_1_1
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE);
+ vgLoadMatrix(vgFloatArray);
+ ASSERT_VG_NO_ERROR();
+#endif
+ }
+
+ void applyScissorRect()
+ {
+ if (scissoringEnabled) {
+ vgSeti(VG_SCISSORING, VG_TRUE);
+ vgSetfv(VG_SCISSOR_RECTS, 4, VGRect(scissorRect).toVGfloat());
+ } else
+ vgSeti(VG_SCISSORING, VG_FALSE);
+
+ ASSERT_VG_NO_ERROR();
+ }
+
+ void applyStrokeStyle()
+ {
+ if (strokeStyle == DottedStroke) {
+ VGfloat vgFloatArray[2] = { 1.0, 1.0 };
+ vgSetfv(VG_STROKE_DASH_PATTERN, 2, vgFloatArray);
+ vgSetf(VG_STROKE_DASH_PHASE, 0.0);
+ } else if (strokeStyle == DashedStroke) {
+ if (!strokeDashArray.size()) {
+ VGfloat vgFloatArray[2] = { 4.0, 3.0 };
+ vgSetfv(VG_STROKE_DASH_PATTERN, 2, vgFloatArray);
+ } else {
+ Vector<VGfloat> vgFloatArray(strokeDashArray.size());
+ for (int i = 0; i < strokeDashArray.size(); ++i)
+ vgFloatArray[i] = strokeDashArray[i];
+
+ vgSetfv(VG_STROKE_DASH_PATTERN, vgFloatArray.size(), vgFloatArray.data());
+ }
+ vgSetf(VG_STROKE_DASH_PHASE, strokeDashOffset);
+ } else {
+ vgSetfv(VG_STROKE_DASH_PATTERN, 0, 0);
+ vgSetf(VG_STROKE_DASH_PHASE, 0.0);
+ }
+
+ ASSERT_VG_NO_ERROR();
+ }
+
+ inline bool strokeDisabled() const
+ {
+ return (compositeOperation == CompositeSourceOver
+ && (strokeStyle == NoStroke || !strokeColor.alpha()));
+ }
+
+ inline bool fillDisabled() const
+ {
+ return (compositeOperation == CompositeSourceOver && !fillColor.alpha());
+ }
+
+ void saveMaskIfNecessary(PainterOpenVG* painter)
+ {
+#ifdef OPENVG_VERSION_1_1
+ if (maskingChangedAndEnabled) {
+ if (mask != VG_INVALID_HANDLE) {
+ vgDestroyMaskLayer(mask);
+ ASSERT_VG_NO_ERROR();
+ }
+ mask = vgCreateMaskLayer(painter->surface()->width(), painter->surface()->height());
+ ASSERT(mask != VG_INVALID_HANDLE);
+ vgCopyMask(mask, 0, 0, 0, 0, painter->surface()->width(), painter->surface()->height());
+ ASSERT_VG_NO_ERROR();
+ }
+#endif
+ }
+};
+
+
+PainterOpenVG::PainterOpenVG()
+ : m_state(0)
+ , m_surface(0)
+{
+}
+
+PainterOpenVG::PainterOpenVG(SurfaceOpenVG* surface)
+ : m_state(0)
+ , m_surface(0)
+{
+ ASSERT(surface);
+ begin(surface);
+}
+
+PainterOpenVG::~PainterOpenVG()
+{
+ end();
+}
+
+void PainterOpenVG::begin(SurfaceOpenVG* surface)
+{
+ if (surface == m_surface)
+ return;
+
+ ASSERT(surface);
+ ASSERT(!m_state);
+
+ m_surface = surface;
+
+ m_stateStack.append(new PlatformPainterState());
+ m_state = m_stateStack.last();
+
+ m_surface->setActivePainter(this);
+ m_surface->makeCurrent();
+}
+
+void PainterOpenVG::end()
+{
+ if (!m_surface)
+ return;
+
+ m_surface->setActivePainter(0);
+ m_surface = 0;
+
+ destroyPainterStates();
+}
+
+void PainterOpenVG::destroyPainterStates()
+{
+ PlatformPainterState* state = 0;
+ while (!m_stateStack.isEmpty()) {
+ state = m_stateStack.last();
+ m_stateStack.removeLast();
+ delete state;
+ }
+ m_state = 0;
+}
+
+// Called by friend SurfaceOpenVG, private otherwise.
+void PainterOpenVG::applyState()
+{
+ ASSERT(m_state);
+ m_state->applyState(this);
+}
+
+/**
+ * Copy the current back buffer image onto the surface.
+ *
+ * Call this method when all painting operations have been completed,
+ * otherwise the surface won't visibly change.
+ */
+void PainterOpenVG::blitToSurface()
+{
+ ASSERT(m_state); // implies m_surface
+ m_surface->flush();
+}
+
+AffineTransform PainterOpenVG::transformation() const
+{
+ ASSERT(m_state);
+ return m_state->surfaceTransformation;
+}
+
+void PainterOpenVG::concatTransformation(const AffineTransform& transformation)
+{
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ // We do the multiplication ourself using WebCore's AffineTransform rather
+ // than offloading this to VG via vgMultMatrix() to keep things simple and
+ // so we can maintain state ourselves.
+ m_state->surfaceTransformation.multLeft(transformation);
+ m_state->applyTransformation(this);
+}
+
+void PainterOpenVG::setTransformation(const AffineTransform& transformation)
+{
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ m_state->surfaceTransformation = transformation;
+ m_state->applyTransformation(this);
+}
+
+void PainterOpenVG::transformPath(VGPath dst, VGPath src, const AffineTransform& transformation)
+{
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
+
+ // Save the transform state
+ VGfloat currentMatrix[9];
+ vgGetMatrix(currentMatrix);
+ ASSERT_VG_NO_ERROR();
+
+ // Load the new transform
+ vgLoadMatrix(VGMatrix(transformation).toVGfloat());
+ ASSERT_VG_NO_ERROR();
+
+ // Apply the new transform
+ vgTransformPath(dst, src);
+ ASSERT_VG_NO_ERROR();
+
+ // Restore the transform state
+ vgLoadMatrix(currentMatrix);
+ ASSERT_VG_NO_ERROR();
+}
+
+CompositeOperator PainterOpenVG::compositeOperation() const
+{
+ ASSERT(m_state);
+ return m_state->compositeOperation;
+}
+
+void PainterOpenVG::setCompositeOperation(CompositeOperator op)
+{
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ m_state->compositeOperation = op;
+ m_state->applyBlending(this);
+}
+
+float PainterOpenVG::opacity() const
+{
+ ASSERT(m_state);
+ return m_state->opacity;
+}
+
+void PainterOpenVG::setOpacity(float opacity)
+{
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ m_state->opacity = opacity;
+ m_state->applyBlending(this);
+}
+
+float PainterOpenVG::strokeThickness() const
+{
+ ASSERT(m_state);
+ return m_state->strokeThickness;
+}
+
+void PainterOpenVG::setStrokeThickness(float thickness)
+{
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ m_state->strokeThickness = thickness;
+ vgSetf(VG_STROKE_LINE_WIDTH, thickness);
+ ASSERT_VG_NO_ERROR();
+}
+
+StrokeStyle PainterOpenVG::strokeStyle() const
+{
+ ASSERT(m_state);
+ return m_state->strokeStyle;
+}
+
+void PainterOpenVG::setStrokeStyle(StrokeStyle style)
+{
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ m_state->strokeStyle = style;
+ m_state->applyStrokeStyle();
+}
+
+void PainterOpenVG::setLineDash(const DashArray& dashArray, float dashOffset)
+{
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ m_state->strokeDashArray = dashArray;
+ m_state->strokeDashOffset = dashOffset;
+ m_state->applyStrokeStyle();
+}
+
+void PainterOpenVG::setLineCap(LineCap lineCap)
+{
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ m_state->strokeLineCap = lineCap;
+ vgSeti(VG_STROKE_CAP_STYLE, toVGCapStyle(lineCap));
+ ASSERT_VG_NO_ERROR();
+}
+
+void PainterOpenVG::setLineJoin(LineJoin lineJoin)
+{
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ m_state->strokeLineJoin = lineJoin;
+ vgSeti(VG_STROKE_JOIN_STYLE, toVGJoinStyle(lineJoin));
+ ASSERT_VG_NO_ERROR();
+}
+
+void PainterOpenVG::setMiterLimit(float miterLimit)
+{
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ m_state->strokeMiterLimit = miterLimit;
+ vgSetf(VG_STROKE_MITER_LIMIT, miterLimit);
+ ASSERT_VG_NO_ERROR();
+}
+
+Color PainterOpenVG::strokeColor() const
+{
+ ASSERT(m_state);
+ return m_state->strokeColor;
+}
+
+void PainterOpenVG::setStrokeColor(const Color& color)
+{
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ m_state->strokeColor = color;
+ setVGSolidColor(VG_STROKE_PATH, color);
+}
+
+Color PainterOpenVG::fillColor() const
+{
+ ASSERT(m_state);
+ return m_state->fillColor;
+}
+
+void PainterOpenVG::setFillColor(const Color& color)
+{
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ m_state->fillColor = color;
+ setVGSolidColor(VG_FILL_PATH, color);
+}
+
+TextDrawingModeFlags PainterOpenVG::textDrawingMode() const
+{
+ ASSERT(m_state);
+ return m_state->textDrawingMode;
+}
+
+void PainterOpenVG::setTextDrawingMode(TextDrawingModeFlags mode)
+{
+ ASSERT(m_state);
+ m_state->textDrawingMode = mode;
+}
+
+bool PainterOpenVG::antialiasingEnabled() const
+{
+ ASSERT(m_state);
+ return m_state->antialiasingEnabled;
+}
+
+void PainterOpenVG::setAntialiasingEnabled(bool enabled)
+{
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ m_state->antialiasingEnabled = enabled;
+
+ if (enabled)
+ vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_FASTER);
+ else
+ vgSeti(VG_RENDERING_QUALITY, VG_RENDERING_QUALITY_NONANTIALIASED);
+}
+
+void PainterOpenVG::scale(const FloatSize& scaleFactors)
+{
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ AffineTransform transformation = m_state->surfaceTransformation;
+ transformation.scaleNonUniform(scaleFactors.width(), scaleFactors.height());
+ setTransformation(transformation);
+}
+
+void PainterOpenVG::rotate(float radians)
+{
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ AffineTransform transformation = m_state->surfaceTransformation;
+ transformation.rotate(rad2deg(radians));
+ setTransformation(transformation);
+}
+
+void PainterOpenVG::translate(float dx, float dy)
+{
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ AffineTransform transformation = m_state->surfaceTransformation;
+ transformation.translate(dx, dy);
+ setTransformation(transformation);
+}
+
+void PainterOpenVG::drawPath(const Path& path, VGbitfield specifiedPaintModes, WindRule fillRule)
+{
+ ASSERT(m_state);
+
+ VGbitfield paintModes = 0;
+ if (!m_state->strokeDisabled())
+ paintModes |= VG_STROKE_PATH;
+ if (!m_state->fillDisabled())
+ paintModes |= VG_FILL_PATH;
+
+ paintModes &= specifiedPaintModes;
+
+ if (!paintModes)
+ return;
+
+ m_surface->makeCurrent();
+
+ vgSeti(VG_FILL_RULE, toVGFillRule(fillRule));
+ vgDrawPath(path.platformPath()->vgPath(), paintModes);
+ ASSERT_VG_NO_ERROR();
+}
+
+void PainterOpenVG::intersectScissorRect(const FloatRect& rect)
+{
+ // Scissor rectangles are defined by float values, but e.g. painting
+ // something red to a float-clipped rectangle and then painting something
+ // white to the same rectangle will leave some red remnants as it is
+ // rendered to full pixels in between. Also, some OpenVG implementations
+ // are likely to clip to integer coordinates anyways because of the above
+ // effect. So considering the above (and confirming through tests) the
+ // visual result is better if we clip to the enclosing integer rectangle
+ // rather than the exact float rectangle for scissoring.
+ if (m_state->scissoringEnabled)
+ m_state->scissorRect.intersect(FloatRect(enclosingIntRect(rect)));
+ else {
+ m_state->scissoringEnabled = true;
+ m_state->scissorRect = FloatRect(enclosingIntRect(rect));
+ }
+
+ m_state->applyScissorRect();
+}
+
+void PainterOpenVG::intersectClipRect(const FloatRect& rect)
+{
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ if (m_state->surfaceTransformation.isIdentity()) {
+ // No transformation required, skip all the complex stuff.
+ intersectScissorRect(rect);
+ return;
+ }
+
+ // Check if the actual destination rectangle is still rectilinear (can be
+ // represented as FloatRect) so we could apply scissoring instead of
+ // (potentially more expensive) path clipping. Note that scissoring is not
+ // subject to transformations, so we need to do the transformation to
+ // surface coordinates by ourselves.
+ FloatQuad effectiveScissorQuad = m_state->surfaceTransformation.mapQuad(FloatQuad(rect));
+
+ if (effectiveScissorQuad.isRectilinear())
+ intersectScissorRect(effectiveScissorQuad.boundingBox());
+ else {
+ // The transformed scissorRect cannot be represented as FloatRect
+ // anymore, so we need to perform masking instead.
+ Path scissorRectPath;
+ scissorRectPath.addRect(rect);
+ clipPath(scissorRectPath, PainterOpenVG::IntersectClip);
+ }
+}
+
+void PainterOpenVG::clipPath(const Path& path, PainterOpenVG::ClipOperation maskOp, WindRule clipRule)
+{
+#ifdef OPENVG_VERSION_1_1
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ if (m_state->mask != VG_INVALID_HANDLE && !m_state->maskingChangedAndEnabled) {
+ // The parent's mask has been inherited - dispose the handle so that
+ // it won't be overwritten.
+ m_state->maskingChangedAndEnabled = true;
+ m_state->mask = VG_INVALID_HANDLE;
+ } else if (!m_state->maskingEnabled()) {
+ // None of the parent painter states had a mask enabled yet.
+ m_state->maskingChangedAndEnabled = true;
+ vgSeti(VG_MASKING, VG_TRUE);
+ // Make sure not to inherit previous mask state from previously written
+ // (but disabled) masks. For VG_FILL_MASK the first argument is ignored,
+ // we pass VG_INVALID_HANDLE which is what the OpenVG spec suggests.
+ vgMask(VG_INVALID_HANDLE, VG_FILL_MASK, 0, 0, m_surface->width(), m_surface->height());
+ }
+
+ // Intersect the path from the mask, or subtract it from there.
+ // (In either case we always decrease the visible area, never increase it,
+ // which means masking never has to modify scissor rectangles.)
+ vgSeti(VG_FILL_RULE, toVGFillRule(clipRule));
+ vgRenderToMask(path.platformPath()->vgPath(), VG_FILL_PATH, (VGMaskOperation) maskOp);
+ ASSERT_VG_NO_ERROR();
+#else
+ notImplemented();
+#endif
+}
+
+void PainterOpenVG::drawRect(const FloatRect& rect, VGbitfield specifiedPaintModes)
+{
+ ASSERT(m_state);
+
+ VGbitfield paintModes = 0;
+ if (!m_state->strokeDisabled())
+ paintModes |= VG_STROKE_PATH;
+ if (!m_state->fillDisabled())
+ paintModes |= VG_FILL_PATH;
+
+ paintModes &= specifiedPaintModes;
+
+ if (!paintModes)
+ return;
+
+ m_surface->makeCurrent();
+
+ VGPath path = vgCreatePath(
+ VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
+ 1.0 /* scale */, 0.0 /* bias */,
+ 5 /* expected number of segments */,
+ 5 /* expected number of total coordinates */,
+ VG_PATH_CAPABILITY_APPEND_TO);
+ ASSERT_VG_NO_ERROR();
+
+ if (vguRect(path, rect.x(), rect.y(), rect.width(), rect.height()) == VGU_NO_ERROR) {
+ vgDrawPath(path, paintModes);
+ ASSERT_VG_NO_ERROR();
+ }
+
+ vgDestroyPath(path);
+ ASSERT_VG_NO_ERROR();
+}
+
+void PainterOpenVG::drawRoundedRect(const FloatRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, VGbitfield specifiedPaintModes)
+{
+ ASSERT(m_state);
+
+ VGbitfield paintModes = 0;
+ if (!m_state->strokeDisabled())
+ paintModes |= VG_STROKE_PATH;
+ if (!m_state->fillDisabled())
+ paintModes |= VG_FILL_PATH;
+
+ paintModes &= specifiedPaintModes;
+
+ if (!paintModes)
+ return;
+
+ m_surface->makeCurrent();
+
+ VGPath path = vgCreatePath(
+ VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
+ 1.0 /* scale */, 0.0 /* bias */,
+ 10 /* expected number of segments */,
+ 25 /* expected number of total coordinates */,
+ VG_PATH_CAPABILITY_APPEND_TO);
+ ASSERT_VG_NO_ERROR();
+
+ // clamp corner arc sizes
+ FloatSize clampedTopLeft = FloatSize(topLeft).shrunkTo(rect.size()).expandedTo(FloatSize());
+ FloatSize clampedTopRight = FloatSize(topRight).shrunkTo(rect.size()).expandedTo(FloatSize());
+ FloatSize clampedBottomLeft = FloatSize(bottomLeft).shrunkTo(rect.size()).expandedTo(FloatSize());
+ FloatSize clampedBottomRight = FloatSize(bottomRight).shrunkTo(rect.size()).expandedTo(FloatSize());
+
+ // As OpenVG's coordinate system is flipped in comparison to WebKit's,
+ // we have to specify the opposite value for the "clockwise" value.
+ static const VGubyte pathSegments[] = {
+ VG_MOVE_TO_ABS,
+ VG_HLINE_TO_REL,
+ VG_SCCWARC_TO_REL,
+ VG_VLINE_TO_REL,
+ VG_SCCWARC_TO_REL,
+ VG_HLINE_TO_REL,
+ VG_SCCWARC_TO_REL,
+ VG_VLINE_TO_REL,
+ VG_SCCWARC_TO_REL,
+ VG_CLOSE_PATH
+ };
+ // Also, the rounded rectangle path proceeds from the top to the bottom,
+ // requiring height distances and clamped radius sizes to be flipped.
+ const VGfloat pathData[] = {
+ rect.x() + clampedTopLeft.width(), rect.y(),
+ rect.width() - clampedTopLeft.width() - clampedTopRight.width(),
+ clampedTopRight.width(), clampedTopRight.height(), 0, clampedTopRight.width(), clampedTopRight.height(),
+ rect.height() - clampedTopRight.height() - clampedBottomRight.height(),
+ clampedBottomRight.width(), clampedBottomRight.height(), 0, -clampedBottomRight.width(), clampedBottomRight.height(),
+ -(rect.width() - clampedBottomLeft.width() - clampedBottomRight.width()),
+ clampedBottomLeft.width(), clampedBottomLeft.height(), 0, -clampedBottomLeft.width(), -clampedBottomLeft.height(),
+ -(rect.height() - clampedTopLeft.height() - clampedBottomLeft.height()),
+ clampedTopLeft.width(), clampedTopLeft.height(), 0, clampedTopLeft.width(), -clampedTopLeft.height(),
+ };
+
+ vgAppendPathData(path, 10, pathSegments, pathData);
+ vgDrawPath(path, paintModes);
+ vgDestroyPath(path);
+ ASSERT_VG_NO_ERROR();
+}
+
+void PainterOpenVG::drawLine(const IntPoint& from, const IntPoint& to)
+{
+ ASSERT(m_state);
+
+ if (m_state->strokeDisabled())
+ return;
+
+ m_surface->makeCurrent();
+
+ VGPath path = vgCreatePath(
+ VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
+ 1.0 /* scale */, 0.0 /* bias */,
+ 2 /* expected number of segments */,
+ 4 /* expected number of total coordinates */,
+ VG_PATH_CAPABILITY_APPEND_TO);
+ ASSERT_VG_NO_ERROR();
+
+ VGUErrorCode errorCode;
+
+ // Try to align lines to pixels, centering them between pixels for odd thickness values.
+ if (fmod(m_state->strokeThickness + 0.5, 2.0) < 1.0)
+ errorCode = vguLine(path, from.x(), from.y(), to.x(), to.y());
+ else if ((to.y() - from.y()) > (to.x() - from.x())) // more vertical than horizontal
+ errorCode = vguLine(path, from.x() + 0.5, from.y(), to.x() + 0.5, to.y());
+ else
+ errorCode = vguLine(path, from.x(), from.y() + 0.5, to.x(), to.y() + 0.5);
+
+ if (errorCode == VGU_NO_ERROR) {
+ vgDrawPath(path, VG_STROKE_PATH);
+ ASSERT_VG_NO_ERROR();
+ }
+
+ vgDestroyPath(path);
+ ASSERT_VG_NO_ERROR();
+}
+
+void PainterOpenVG::drawArc(const IntRect& rect, int startAngle, int angleSpan, VGbitfield specifiedPaintModes)
+{
+ ASSERT(m_state);
+
+ VGbitfield paintModes = 0;
+ if (!m_state->strokeDisabled())
+ paintModes |= VG_STROKE_PATH;
+ if (!m_state->fillDisabled())
+ paintModes |= VG_FILL_PATH;
+
+ paintModes &= specifiedPaintModes;
+
+ if (!paintModes)
+ return;
+
+ m_surface->makeCurrent();
+
+ VGPath path = vgCreatePath(
+ VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
+ 1.0 /* scale */, 0.0 /* bias */,
+ 2 /* expected number of segments */,
+ 4 /* expected number of total coordinates */,
+ VG_PATH_CAPABILITY_APPEND_TO);
+ ASSERT_VG_NO_ERROR();
+
+ if (vguArc(path, rect.x() + rect.width() / 2.0, rect.y() + rect.height() / 2.0, rect.width(), rect.height(), -startAngle, -angleSpan, VGU_ARC_OPEN) == VGU_NO_ERROR) {
+ vgDrawPath(path, VG_STROKE_PATH);
+ ASSERT_VG_NO_ERROR();
+ }
+
+ vgDestroyPath(path);
+ ASSERT_VG_NO_ERROR();
+}
+
+void PainterOpenVG::drawEllipse(const IntRect& rect, VGbitfield specifiedPaintModes)
+{
+ ASSERT(m_state);
+
+ VGbitfield paintModes = 0;
+ if (!m_state->strokeDisabled())
+ paintModes |= VG_STROKE_PATH;
+ if (!m_state->fillDisabled())
+ paintModes |= VG_FILL_PATH;
+
+ paintModes &= specifiedPaintModes;
+
+ if (!paintModes)
+ return;
+
+ m_surface->makeCurrent();
+
+ VGPath path = vgCreatePath(
+ VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
+ 1.0 /* scale */, 0.0 /* bias */,
+ 4 /* expected number of segments */,
+ 12 /* expected number of total coordinates */,
+ VG_PATH_CAPABILITY_APPEND_TO);
+ ASSERT_VG_NO_ERROR();
+
+ if (vguEllipse(path, rect.x() + rect.width() / 2.0, rect.y() + rect.height() / 2.0, rect.width(), rect.height()) == VGU_NO_ERROR) {
+ vgDrawPath(path, paintModes);
+ ASSERT_VG_NO_ERROR();
+ }
+
+ vgDestroyPath(path);
+ ASSERT_VG_NO_ERROR();
+}
+
+void PainterOpenVG::drawPolygon(size_t numPoints, const FloatPoint* points, VGbitfield specifiedPaintModes)
+{
+ ASSERT(m_state);
+
+ VGbitfield paintModes = 0;
+ if (!m_state->strokeDisabled())
+ paintModes |= VG_STROKE_PATH;
+ if (!m_state->fillDisabled())
+ paintModes |= VG_FILL_PATH;
+
+ paintModes &= specifiedPaintModes;
+
+ if (!paintModes)
+ return;
+
+ m_surface->makeCurrent();
+
+ // Path segments: all points + "close path".
+ const VGint numSegments = numPoints + 1;
+ const VGint numCoordinates = numPoints * 2;
+
+ VGPath path = vgCreatePath(
+ VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
+ 1.0 /* scale */, 0.0 /* bias */,
+ numSegments /* expected number of segments */,
+ numCoordinates /* expected number of total coordinates */,
+ VG_PATH_CAPABILITY_APPEND_TO);
+ ASSERT_VG_NO_ERROR();
+
+ Vector<VGfloat> vgPoints(numCoordinates);
+ for (int i = 0; i < numPoints; ++i) {
+ vgPoints[i*2] = points[i].x();
+ vgPoints[i*2 + 1] = points[i].y();
+ }
+
+ if (vguPolygon(path, vgPoints.data(), numPoints, VG_TRUE /* closed */) == VGU_NO_ERROR) {
+ vgDrawPath(path, paintModes);
+ ASSERT_VG_NO_ERROR();
+ }
+
+ vgDestroyPath(path);
+ ASSERT_VG_NO_ERROR();
+}
+
+void PainterOpenVG::drawImage(TiledImageOpenVG* tiledImage, const FloatRect& dst, const FloatRect& src)
+{
+ ASSERT(m_state);
+ m_surface->makeCurrent();
+
+ // If buffers can be larger than the maximum OpenVG image sizes,
+ // we split them into tiles.
+ IntRect drawnTiles = tiledImage->tilesInRect(src);
+ AffineTransform srcToDstTransformation = makeMapBetweenRects(
+ FloatRect(FloatPoint(0.0, 0.0), src.size()), dst);
+ srcToDstTransformation.translate(-src.x(), -src.y());
+
+ for (int yIndex = drawnTiles.y(); yIndex < drawnTiles.bottom(); ++yIndex) {
+ for (int xIndex = drawnTiles.x(); xIndex < drawnTiles.right(); ++xIndex) {
+ // The srcTile rectangle is an aligned tile cropped by the src rectangle.
+ FloatRect tile(tiledImage->tileRect(xIndex, yIndex));
+ FloatRect srcTile = intersection(src, tile);
+
+ save();
+
+ // If the image is drawn in full, all we need is the proper transformation
+ // in order to get it drawn at the right spot on the surface.
+ concatTransformation(AffineTransform(srcToDstTransformation).translate(tile.x(), tile.y()));
+
+ // If only a part of the tile is drawn, we also need to clip the surface.
+ if (srcTile != tile) {
+ // Put boundaries relative to tile origin, as we already
+ // translated to (x, y) with the transformation matrix.
+ srcTile.move(-tile.x(), -tile.y());
+ intersectClipRect(srcTile);
+ }
+
+ VGImage image = tiledImage->tile(xIndex, yIndex);
+ if (image != VG_INVALID_HANDLE) {
+ vgDrawImage(image);
+ ASSERT_VG_NO_ERROR();
+ }
+
+ restore();
+ }
+ }
+}
+
+#ifdef OPENVG_VERSION_1_1
+void PainterOpenVG::drawText(VGFont vgFont, Vector<VGuint>& characters, VGfloat* adjustmentsX, VGfloat* adjustmentsY, const FloatPoint& point)
+{
+ ASSERT(m_state);
+
+ VGbitfield paintModes = 0;
+
+ if (m_state->textDrawingMode & TextModeClip)
+ return; // unsupported for every port except CG at the time of writing
+ if (m_state->textDrawingMode & TextModeFill && !m_state->fillDisabled())
+ paintModes |= VG_FILL_PATH;
+ if (m_state->textDrawingMode & TextModeStroke && !m_state->strokeDisabled())
+ paintModes |= VG_STROKE_PATH;
+
+ m_surface->makeCurrent();
+
+ FloatPoint effectivePoint = m_state->surfaceTransformation.mapPoint(point);
+ FloatPoint p = point;
+ AffineTransform* originalTransformation = 0;
+
+ // In case the font isn't drawn at a pixel-exact baseline and we can easily
+ // fix that (which is the case for non-rotated affine transforms), let's
+ // align the starting point to the pixel boundary in order to prevent
+ // font rendering issues such as glyphs that appear off by a pixel.
+ // This causes us to have inconsistent spacing between baselines in a
+ // larger paragraph, but that seems to be the least of all evils.
+ if ((fmod(effectivePoint.x() + 0.01, 1.0) > 0.02 || fmod(effectivePoint.y() + 0.01, 1.0) > 0.02)
+ && isNonRotatedAffineTransformation(m_state->surfaceTransformation))
+ {
+ originalTransformation = new AffineTransform(m_state->surfaceTransformation);
+ setTransformation(AffineTransform(
+ m_state->surfaceTransformation.a(), 0,
+ 0, m_state->surfaceTransformation.d(),
+ roundf(effectivePoint.x()), roundf(effectivePoint.y())));
+ p = FloatPoint();
+ }
+
+ const VGfloat vgPoint[2] = { p.x(), p.y() };
+ vgSetfv(VG_GLYPH_ORIGIN, 2, vgPoint);
+ ASSERT_VG_NO_ERROR();
+
+ vgDrawGlyphs(vgFont, characters.size(), characters.data(),
+ adjustmentsX, adjustmentsY, paintModes, VG_TRUE /* allow autohinting */);
+ ASSERT_VG_NO_ERROR();
+
+ if (originalTransformation) {
+ setTransformation(*originalTransformation);
+ delete originalTransformation;
+ }
+}
+#endif
+
+TiledImageOpenVG* PainterOpenVG::asNewNativeImage(const IntRect& src, VGImageFormat format)
+{
+ ASSERT(m_state);
+ m_surface->sharedSurface()->makeCurrent();
+
+ const IntSize vgMaxImageSize(vgGeti(VG_MAX_IMAGE_WIDTH), vgGeti(VG_MAX_IMAGE_HEIGHT));
+ ASSERT_VG_NO_ERROR();
+
+ const IntRect rect = intersection(src, IntRect(0, 0, m_surface->width(), m_surface->height()));
+ TiledImageOpenVG* tiledImage = new TiledImageOpenVG(rect.size(), vgMaxImageSize);
+
+ const int numColumns = tiledImage->numColumns();
+ const int numRows = tiledImage->numRows();
+
+ // Create the images as resources of the shared surface/context.
+ for (int yIndex = 0; yIndex < numRows; ++yIndex) {
+ for (int xIndex = 0; xIndex < numColumns; ++xIndex) {
+ IntRect tileRect = tiledImage->tileRect(xIndex, yIndex);
+ VGImage image = vgCreateImage(format, tileRect.width(), tileRect.height(), VG_IMAGE_QUALITY_FASTER);
+ ASSERT_VG_NO_ERROR();
+
+ tiledImage->setTile(xIndex, yIndex, image);
+ }
+ }
+
+ // Fill the image contents with our own surface/context being current.
+ m_surface->makeCurrent();
+
+ for (int yIndex = 0; yIndex < numRows; ++yIndex) {
+ for (int xIndex = 0; xIndex < numColumns; ++xIndex) {
+ IntRect tileRect = tiledImage->tileRect(xIndex, yIndex);
+
+ vgGetPixels(tiledImage->tile(xIndex, yIndex), 0, 0,
+ rect.x() + tileRect.x(), rect.y() + tileRect.y(),
+ tileRect.width(), tileRect.height());
+ ASSERT_VG_NO_ERROR();
+ }
+ }
+
+ return tiledImage;
+}
+
+void PainterOpenVG::save(PainterOpenVG::SaveMode saveMode)
+{
+ ASSERT(m_state);
+
+ // If the underlying context/surface was switched away by someone without
+ // telling us, it might not correspond to the one assigned to this painter.
+ // Switch back so we can save the state properly. (Should happen rarely.)
+ // Use DontSaveOrApplyPainterState mode in order to avoid recursion.
+ m_surface->makeCurrent(SurfaceOpenVG::DontSaveOrApplyPainterState);
+
+ if (saveMode == PainterOpenVG::CreateNewState) {
+ m_state->saveMaskIfNecessary(this);
+ PlatformPainterState* state = new PlatformPainterState(*m_state);
+ m_stateStack.append(state);
+ m_state = m_stateStack.last();
+ } else if (saveMode == PainterOpenVG::CreateNewStateWithPaintStateOnly) {
+ m_state->saveMaskIfNecessary(this);
+ PlatformPainterState* state = new PlatformPainterState();
+ state->copyPaintState(m_state);
+ m_stateStack.append(state);
+ m_state = m_stateStack.last();
+ } else // if (saveMode == PainterOpenVG::KeepCurrentState)
+ m_state->saveMaskIfNecessary(this);
+}
+
+void PainterOpenVG::restore()
+{
+ ASSERT(m_stateStack.size() >= 2);
+ m_surface->makeCurrent(SurfaceOpenVG::DontApplyPainterState);
+
+ PlatformPainterState* state = m_stateStack.last();
+ m_stateStack.removeLast();
+ delete state;
+
+ m_state = m_stateStack.last();
+ m_state->applyState(this);
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/openvg/PainterOpenVG.h b/Source/WebCore/platform/graphics/openvg/PainterOpenVG.h
new file mode 100644
index 0000000..32f1fe5
--- /dev/null
+++ b/Source/WebCore/platform/graphics/openvg/PainterOpenVG.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PainterOpenVG_h
+#define PainterOpenVG_h
+
+#include "Color.h"
+#include "GraphicsContext.h"
+
+#include <openvg.h>
+
+#include <wtf/Noncopyable.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class AffineTransform;
+class FloatPoint;
+class FloatRect;
+class IntRect;
+class IntSize;
+class Path;
+class SurfaceOpenVG;
+class TiledImageOpenVG;
+
+struct PlatformPainterState;
+
+class PainterOpenVG : public Noncopyable {
+public:
+ friend class SurfaceOpenVG;
+ friend struct PlatformPainterState;
+
+ enum SaveMode {
+ CreateNewState,
+ KeepCurrentState,
+ CreateNewStateWithPaintStateOnly // internal usage only, do not use outside PainterOpenVG
+ };
+ enum ClipOperation {
+ IntersectClip = VG_INTERSECT_MASK,
+ SubtractClip = VG_SUBTRACT_MASK
+ };
+
+ PainterOpenVG();
+ PainterOpenVG(SurfaceOpenVG*);
+ ~PainterOpenVG();
+
+ void begin(SurfaceOpenVG*);
+ void end();
+
+ AffineTransform transformation() const;
+ void setTransformation(const AffineTransform&);
+ void concatTransformation(const AffineTransform&);
+
+ static void transformPath(VGPath dst, VGPath src, const AffineTransform&);
+
+ CompositeOperator compositeOperation() const;
+ void setCompositeOperation(CompositeOperator);
+ float opacity() const;
+ void setOpacity(float);
+
+ float strokeThickness() const;
+ void setStrokeThickness(float);
+ StrokeStyle strokeStyle() const;
+ void setStrokeStyle(StrokeStyle);
+
+ void setLineDash(const DashArray&, float dashOffset);
+ void setLineCap(LineCap);
+ void setLineJoin(LineJoin);
+ void setMiterLimit(float);
+
+ Color strokeColor() const;
+ void setStrokeColor(const Color&);
+
+ Color fillColor() const;
+ void setFillColor(const Color&);
+
+ int textDrawingMode() const;
+ void setTextDrawingMode(int mode);
+
+ bool antialiasingEnabled() const;
+ void setAntialiasingEnabled(bool);
+
+ void drawRect(const FloatRect&, VGbitfield paintModes = (VG_STROKE_PATH | VG_FILL_PATH));
+ void drawRoundedRect(const FloatRect&, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, VGbitfield paintModes = (VG_STROKE_PATH | VG_FILL_PATH));
+ void drawLine(const IntPoint& from, const IntPoint& to);
+ void drawArc(const IntRect& ellipseBounds, int startAngle, int angleSpan, VGbitfield paintModes = (VG_STROKE_PATH | VG_FILL_PATH));
+ void drawEllipse(const IntRect& bounds, VGbitfield paintModes = (VG_STROKE_PATH | VG_FILL_PATH));
+ void drawPolygon(size_t numPoints, const FloatPoint* points, VGbitfield paintModes = (VG_STROKE_PATH | VG_FILL_PATH));
+ void drawImage(TiledImageOpenVG*, const FloatRect& dst, const FloatRect& src);
+#ifdef OPENVG_VERSION_1_1
+ void drawText(VGFont, Vector<VGuint>& characters, VGfloat* adjustmentsX, VGfloat* adjustmentsY, const FloatPoint&);
+#endif
+
+ void scale(const FloatSize& scaleFactors);
+ void rotate(float radians);
+ void translate(float dx, float dy);
+
+ void drawPath(const Path&, VGbitfield paintModes = (VG_STROKE_PATH | VG_FILL_PATH), WindRule fillRule = RULE_NONZERO);
+
+ void intersectClipRect(const FloatRect&);
+ void clipPath(const Path&, PainterOpenVG::ClipOperation, WindRule clipRule = RULE_NONZERO);
+
+ TiledImageOpenVG* asNewNativeImage(const IntRect& src, VGImageFormat);
+
+ void save(PainterOpenVG::SaveMode saveMode = CreateNewState);
+ void restore();
+
+ SurfaceOpenVG* surface() { return m_surface; }
+ void blitToSurface();
+
+private:
+ void destroyPainterStates();
+ void applyState();
+
+ void intersectScissorRect(const FloatRect&);
+
+private:
+ Vector<PlatformPainterState*> m_stateStack;
+ PlatformPainterState* m_state;
+ SurfaceOpenVG* m_surface;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/openvg/PathOpenVG.cpp b/Source/WebCore/platform/graphics/openvg/PathOpenVG.cpp
new file mode 100644
index 0000000..39a4b06
--- /dev/null
+++ b/Source/WebCore/platform/graphics/openvg/PathOpenVG.cpp
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "Path.h"
+
+#include "AffineTransform.h"
+#include "FloatRect.h"
+#include "GraphicsContext.h"
+#include "NotImplemented.h"
+#include "PainterOpenVG.h"
+#include "PlatformPathOpenVG.h"
+#include "PlatformString.h"
+#include "StrokeStyleApplier.h"
+#include "VGUtils.h"
+
+#include <openvg.h>
+#include <wtf/MathExtras.h>
+
+#define WEBKIT_VG_PATH_CAPABILITIES VG_PATH_CAPABILITY_ALL
+
+#define FUZZY_COMPARE(number, reference, delta) \
+ (number >= (reference - delta) && number <= (reference + delta))
+
+namespace WebCore {
+
+PlatformPathOpenVG::PlatformPathOpenVG()
+ : SharedResourceOpenVG()
+{
+ createPath();
+}
+
+PlatformPathOpenVG::PlatformPathOpenVG(const PlatformPathOpenVG& other)
+ : SharedResourceOpenVG()
+ , m_currentPoint(other.m_currentPoint)
+ , m_subpathStartPoint(other.m_subpathStartPoint)
+{
+ createPath();
+ // makeCompatibleContextCurrent() is called by createPath(), so not necessary here.
+ vgAppendPath(m_vgPath, other.m_vgPath);
+ ASSERT_VG_NO_ERROR();
+}
+
+PlatformPathOpenVG& PlatformPathOpenVG::operator=(const PlatformPathOpenVG& other)
+{
+ if (&other != this) {
+ clear();
+ // makeCompatibleContextCurrent() is called by clear(), so not necessary here.
+ vgAppendPath(m_vgPath, other.m_vgPath);
+ ASSERT_VG_NO_ERROR();
+ }
+ return *this;
+}
+
+PlatformPathOpenVG::~PlatformPathOpenVG()
+{
+ makeCompatibleContextCurrent();
+
+ vgDestroyPath(m_vgPath);
+ ASSERT_VG_NO_ERROR();
+}
+
+void PlatformPathOpenVG::clear()
+{
+ makeCompatibleContextCurrent();
+
+ vgClearPath(m_vgPath, WEBKIT_VG_PATH_CAPABILITIES);
+ ASSERT_VG_NO_ERROR();
+
+ m_subpathStartPoint.setX(0);
+ m_subpathStartPoint.setY(0);
+ m_currentPoint = m_subpathStartPoint;
+}
+
+void PlatformPathOpenVG::createPath()
+{
+ makeSharedContextCurrent();
+
+ m_vgPath = vgCreatePath(
+ VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
+ 1.0 /* scale */, 0.0 /* bias */,
+ 0 /* expected number of segments */,
+ 0 /* expected number of total coordinates */,
+ WEBKIT_VG_PATH_CAPABILITIES);
+ ASSERT_VG_NO_ERROR();
+}
+
+
+Path::Path()
+{
+ m_path = new PlatformPathOpenVG();
+}
+
+Path::~Path()
+{
+ delete m_path;
+}
+
+Path::Path(const Path& other)
+{
+ m_path = new PlatformPathOpenVG(*(other.m_path));
+}
+
+Path& Path::operator=(const Path& other)
+{
+ *m_path = *(other.m_path);
+ return *this;
+}
+
+FloatPoint Path::currentPoint() const
+{
+ // FIXME: is this the way to return the current point of the subpath?
+ return m_currentPoint;
+}
+
+
+bool Path::contains(const FloatPoint& point, WindRule rule) const
+{
+ notImplemented();
+
+ // OpenVG has no path-contains function, so for now we approximate by
+ // using the bounding rect of the path.
+ return boundingRect().contains(point);
+}
+
+bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
+{
+ notImplemented();
+
+ // OpenVG has no path-contains function, so for now we approximate by
+ // using the stroke bounding rect of the path.
+ return (const_cast<Path*>(this))->strokeBoundingRect().contains(point);
+}
+
+void Path::translate(const FloatSize& size)
+{
+ AffineTransform transformation;
+ transformation.translate(size.width(), size.height());
+ transform(transformation);
+}
+
+FloatRect Path::boundingRect() const
+{
+ VGfloat minX;
+ VGfloat minY;
+ VGfloat width;
+ VGfloat height;
+
+ m_path->makeCompatibleContextCurrent();
+ vgPathBounds(m_path->vgPath(), &minX, &minY, &width, &height);
+ ASSERT_VG_NO_ERROR();
+
+ return FloatRect(FloatPoint(minX, minY), FloatSize(width, height));
+}
+
+FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier)
+{
+ notImplemented();
+
+ // vgPathBounds() ignores stroke parameters, and we don't currently have
+ // an approximation that takes stroke parameters into account.
+ return boundingRect();
+}
+
+void Path::moveTo(const FloatPoint& point)
+{
+ static const VGubyte pathSegments[] = { VG_MOVE_TO_ABS };
+ const VGfloat pathData[] = { point.x(), point.y() };
+
+ m_path->makeCompatibleContextCurrent();
+ vgAppendPathData(m_path->vgPath(), 1, pathSegments, pathData);
+ ASSERT_VG_NO_ERROR();
+
+ m_path->m_currentPoint = m_path->m_subpathStartPoint = point;
+}
+
+void Path::addLineTo(const FloatPoint& point)
+{
+ static const VGubyte pathSegments[] = { VG_LINE_TO_ABS };
+ const VGfloat pathData[] = { point.x(), point.y() };
+
+ m_path->makeCompatibleContextCurrent();
+ vgAppendPathData(m_path->vgPath(), 1, pathSegments, pathData);
+ ASSERT_VG_NO_ERROR();
+
+ m_path->m_currentPoint = point;
+}
+
+void Path::addQuadCurveTo(const FloatPoint& controlPoint, const FloatPoint& endPoint)
+{
+ static const VGubyte pathSegments[] = { VG_QUAD_TO_ABS };
+ const VGfloat pathData[] = { controlPoint.x(), controlPoint.y(), endPoint.x(), endPoint.y() };
+
+ m_path->makeCompatibleContextCurrent();
+ vgAppendPathData(m_path->vgPath(), 1, pathSegments, pathData);
+ ASSERT_VG_NO_ERROR();
+
+ m_path->m_currentPoint = endPoint;
+}
+
+void Path::addBezierCurveTo(const FloatPoint& controlPoint1, const FloatPoint& controlPoint2, const FloatPoint& endPoint)
+{
+ static const VGubyte pathSegments[] = { VG_CUBIC_TO_ABS };
+ const VGfloat pathData[] = { controlPoint1.x(), controlPoint1.y(), controlPoint2.x(), controlPoint2.y(), endPoint.x(), endPoint.y() };
+
+ m_path->makeCompatibleContextCurrent();
+ vgAppendPathData(m_path->vgPath(), 1, pathSegments, pathData);
+ ASSERT_VG_NO_ERROR();
+
+ m_path->m_currentPoint = endPoint;
+}
+
+void Path::addArcTo(const FloatPoint& point1, const FloatPoint& point2, float radius)
+{
+ // See http://philip.html5.org/tests/canvas/suite/tests/spec.html#arcto.
+
+ const FloatPoint& point0 = m_path->m_currentPoint;
+ if (!radius || point0 == point1 || point1 == point2) {
+ addLineTo(point1);
+ return;
+ }
+
+ FloatSize v01 = point0 - point1;
+ FloatSize v21 = point2 - point1;
+
+ // sin(A - B) = sin(A) * cos(B) - sin(B) * cos(A)
+ double cross = v01.width() * v21.height() - v01.height() * v21.width();
+
+ if (fabs(cross) < 1E-10) {
+ // on one line
+ addLineTo(point1);
+ return;
+ }
+
+ double d01 = hypot(v01.width(), v01.height());
+ double d21 = hypot(v21.width(), v21.height());
+ double angle = (piDouble - fabs(asin(cross / (d01 * d21)))) * 0.5;
+ double span = radius * tan(angle);
+ double rate = span / d01;
+ FloatPoint startPoint = FloatPoint(point1.x() + v01.width() * rate,
+ point1.y() + v01.height() * rate);
+ rate = span / d21;
+ FloatPoint endPoint = FloatPoint(point1.x() + v21.width() * rate,
+ point1.y() + v21.height() * rate);
+
+ // Fa: large arc flag, makes the difference between SCWARC_TO and LCWARC_TO
+ // respectively SCCWARC_TO and LCCWARC_TO arcs. We always use small
+ // arcs for arcTo(), as the arc is defined as the "shortest arc" of the
+ // circle specified in HTML 5.
+
+ // Fs: sweep flag, specifying whether the arc is drawn in increasing (true)
+ // or decreasing (0) direction.
+ const bool anticlockwise = cross < 0;
+
+ // Translate the large arc and sweep flags into an OpenVG segment command.
+ const VGubyte segmentCommand = anticlockwise ? VG_SCCWARC_TO_ABS : VG_SCWARC_TO_ABS;
+
+ const VGubyte pathSegments[] = {
+ VG_LINE_TO_ABS,
+ segmentCommand
+ };
+ const VGfloat pathData[] = {
+ startPoint.x(), startPoint.y(),
+ radius, radius, 0, endPoint.x(), endPoint.y()
+ };
+
+ m_path->makeCompatibleContextCurrent();
+ vgAppendPathData(m_path->vgPath(), 2, pathSegments, pathData);
+ ASSERT_VG_NO_ERROR();
+
+ m_path->m_currentPoint = endPoint;
+}
+
+void Path::closeSubpath()
+{
+ static const VGubyte pathSegments[] = { VG_CLOSE_PATH };
+ // pathData must not be 0, but certain compilers also don't create
+ // zero-size arrays. So let's use a random aligned value (sizeof(VGfloat)),
+ // it won't be accessed anyways as VG_CLOSE_PATH doesn't take coordinates.
+ static const VGfloat* pathData = reinterpret_cast<VGfloat*>(sizeof(VGfloat));
+
+ m_path->makeCompatibleContextCurrent();
+ vgAppendPathData(m_path->vgPath(), 1, pathSegments, pathData);
+ ASSERT_VG_NO_ERROR();
+
+ m_path->m_currentPoint = m_path->m_subpathStartPoint;
+}
+
+void Path::addArc(const FloatPoint& center, float radius, float startAngle, float endAngle, bool anticlockwise)
+{
+ // The OpenVG spec says nothing about inf as radius or start/end angle.
+ // WebKit seems to pass those (e.g. https://bugs.webkit.org/show_bug.cgi?id=16449),
+ // so abort instead of risking undefined behavior.
+ if (!isfinite(radius) || !isfinite(startAngle) || !isfinite(endAngle))
+ return;
+
+ // For some reason, the HTML 5 spec defines the angle as going clockwise
+ // from the positive X axis instead of going standard anticlockwise.
+ // So let's make it a proper angle in order to keep sanity.
+ startAngle = fmod((2.0 * piDouble) - startAngle, 2.0 * piDouble);
+ endAngle = fmod((2.0 * piDouble) - endAngle, 2.0 * piDouble);
+
+ // Make it so that endAngle > startAngle. fmod() above takes care of
+ // keeping the difference below 360 degrees.
+ if (endAngle <= startAngle)
+ endAngle += 2.0 * piDouble;
+
+ const VGfloat angleDelta = anticlockwise
+ ? (endAngle - startAngle)
+ : (startAngle - endAngle + (2.0 * piDouble));
+
+ // OpenVG uses endpoint parameterization while this method receives its
+ // values in center parameterization. It lacks an ellipse rotation
+ // parameter so we use 0 for that, and also the radius is only a single
+ // value which makes for rh == rv. In order to convert from endpoint to
+ // center parameterization, we use the formulas from the OpenVG/SVG specs:
+
+ // (x,y) = (cos rot, -sin rot; sin rot, -cos rot) * (rh * cos angle, rv * sin angle) + (center.x, center.y)
+ // rot is 0, which simplifies this a bit:
+ // (x,y) = (1, 0; 0, -1) * (rh * cos angle, rv * sin angle) + (center.x, center.y)
+ // = (1 * rh * cos angle + 0 * rv * sin angle, 0 * rh * cos angle + -1 * rv * sin angle) + (center.x, center.y)
+ // = (rh * cos angle, -rv * sin angle) + (center.x, center.y)
+ // (Set angle = {startAngle, endAngle} to retrieve the respective endpoints.)
+
+ const VGfloat startX = radius * cos(startAngle) + center.x();
+ const VGfloat startY = -radius * sin(startAngle) + center.y();
+ const VGfloat endX = radius * cos(endAngle) + center.x();
+ const VGfloat endY = -radius * sin(endAngle) + center.y();
+
+ // Fa: large arc flag, makes the difference between SCWARC_TO and LCWARC_TO
+ // respectively SCCWARC_TO and LCCWARC_TO arcs.
+ const bool largeArc = (angleDelta > piDouble);
+
+ // Fs: sweep flag, specifying whether the arc is drawn in increasing (true)
+ // or decreasing (0) direction. No need to calculate this value, as it
+ // we already get it passed as a parameter (Fs == !anticlockwise).
+
+ // Translate the large arc and sweep flags into an OpenVG segment command.
+ // As OpenVG thinks of everything upside down, we need to reverse the
+ // anticlockwise parameter in order to get the specified rotation.
+ const VGubyte segmentCommand = !anticlockwise
+ ? (largeArc ? VG_LCCWARC_TO_ABS : VG_SCCWARC_TO_ABS)
+ : (largeArc ? VG_LCWARC_TO_ABS : VG_SCWARC_TO_ABS);
+
+ // So now, we've got all the parameters in endpoint parameterization format
+ // as OpenVG requires it. Which means we can just pass it like this.
+ const VGubyte pathSegments[] = {
+ hasCurrentPoint() ? VG_LINE_TO_ABS : VG_MOVE_TO_ABS,
+ segmentCommand
+ };
+ const VGfloat pathData[] = {
+ startX, startY,
+ radius, radius, 0, endX, endY
+ };
+
+ m_path->makeCompatibleContextCurrent();
+ vgAppendPathData(m_path->vgPath(), 2, pathSegments, pathData);
+ ASSERT_VG_NO_ERROR();
+
+ m_path->m_currentPoint.setX(endX);
+ m_path->m_currentPoint.setY(endY);
+}
+
+void Path::addRect(const FloatRect& rect)
+{
+ static const VGubyte pathSegments[] = {
+ VG_MOVE_TO_ABS,
+ VG_HLINE_TO_REL,
+ VG_VLINE_TO_REL,
+ VG_HLINE_TO_REL,
+ VG_CLOSE_PATH
+ };
+ const VGfloat pathData[] = {
+ rect.x(), rect.y(),
+ rect.width(),
+ rect.height(),
+ -rect.width()
+ };
+
+ m_path->makeCompatibleContextCurrent();
+ vgAppendPathData(m_path->vgPath(), 5, pathSegments, pathData);
+ ASSERT_VG_NO_ERROR();
+
+ m_path->m_currentPoint = m_path->m_subpathStartPoint = rect.location();
+}
+
+void Path::addEllipse(const FloatRect& rect)
+{
+ static const VGubyte pathSegments[] = {
+ VG_MOVE_TO_ABS,
+ VG_SCCWARC_TO_REL,
+ VG_SCCWARC_TO_REL,
+ VG_CLOSE_PATH
+ };
+ const VGfloat pathData[] = {
+ rect.x() + rect.width() / 2.0, rect.y(),
+ rect.width() / 2.0, rect.height() / 2.0, 0, 0, rect.height(),
+ rect.width() / 2.0, rect.height() / 2.0, 0, 0, -rect.height()
+ };
+
+ m_path->makeCompatibleContextCurrent();
+ vgAppendPathData(m_path->vgPath(), 4, pathSegments, pathData);
+ ASSERT_VG_NO_ERROR();
+}
+
+void Path::clear()
+{
+ m_path->clear();
+}
+
+bool Path::isEmpty() const
+{
+ m_path->makeCompatibleContextCurrent();
+ return !vgGetParameteri(m_path->vgPath(), VG_PATH_NUM_SEGMENTS);
+}
+
+bool Path::hasCurrentPoint() const
+{
+ m_path->makeCompatibleContextCurrent();
+ return vgGetParameteri(m_path->vgPath(), VG_PATH_NUM_SEGMENTS) > 0;
+}
+
+void Path::apply(void* info, PathApplierFunction function) const
+{
+ // OpenVG provides no means to retrieve path segment information.
+ // This is *very* unfortunate, we might need to store the segments in
+ // memory if we want to implement this function properly.
+ // See http://www.khronos.org/message_boards/viewtopic.php?f=6&t=1887
+ notImplemented();
+}
+
+void Path::transform(const AffineTransform& transformation)
+{
+ PlatformPathOpenVG* dst = new PlatformPathOpenVG();
+ // dst->makeCompatibleContextCurrent() is called by the platform path
+ // constructor, therefore not necessary to call it again here.
+ PainterOpenVG::transformPath(dst->vgPath(), m_path->vgPath(), transformation);
+ delete m_path;
+ m_path = dst;
+
+ m_path->m_currentPoint = transformation.mapPoint(m_path->m_currentPoint);
+ m_path->m_subpathStartPoint = transformation.mapPoint(m_path->m_subpathStartPoint);
+}
+
+
+// Path::length(), Path::pointAtLength() and Path::normalAngleAtLength() are
+// reimplemented here instead of in Path.cpp, because OpenVG has its own
+// functions and Path::apply() doesn't really work as long as we rely on VGPath
+// as primary path storage.
+
+float Path::length()
+{
+ m_path->makeCompatibleContextCurrent();
+ VGfloat length = vgPathLength(m_path->vgPath(), 0, vgGetParameteri(m_path->vgPath(), VG_PATH_NUM_SEGMENTS));
+ ASSERT_VG_NO_ERROR();
+ return length;
+}
+
+FloatPoint Path::pointAtLength(float length, bool& ok)
+{
+ VGfloat x = 0, y = 0;
+ m_path->makeCompatibleContextCurrent();
+
+ vgPointAlongPath(m_path->vgPath(), 0, vgGetParameteri(m_path->vgPath(), VG_PATH_NUM_SEGMENTS),
+ length, &x, &y, 0, 0);
+ ok = (vgGetError() == VG_NO_ERROR);
+ return FloatPoint(x, y);
+}
+
+float Path::normalAngleAtLength(float length, bool& ok)
+{
+ VGfloat tangentX, tangentY;
+ m_path->makeCompatibleContextCurrent();
+
+ vgPointAlongPath(m_path->vgPath(), 0, vgGetParameteri(m_path->vgPath(), VG_PATH_NUM_SEGMENTS),
+ length, 0, 0, &tangentX, &tangentY);
+ ok = (vgGetError() == VG_NO_ERROR);
+ return atan2f(tangentY, tangentX) * 180.0 / piFloat; // convert to degrees
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/openvg/PlatformPathOpenVG.h b/Source/WebCore/platform/graphics/openvg/PlatformPathOpenVG.h
new file mode 100644
index 0000000..286da53
--- /dev/null
+++ b/Source/WebCore/platform/graphics/openvg/PlatformPathOpenVG.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PlatformPathOpenVG_h
+#define PlatformPathOpenVG_h
+
+#include "FloatPoint.h"
+#include "SharedResourceOpenVG.h"
+
+#include <openvg.h>
+
+namespace WebCore {
+
+class PlatformPathOpenVG : public SharedResourceOpenVG {
+public:
+ PlatformPathOpenVG();
+ PlatformPathOpenVG(const PlatformPathOpenVG&);
+ ~PlatformPathOpenVG();
+
+ PlatformPathOpenVG& operator=(const PlatformPathOpenVG&);
+
+ VGPath vgPath() { return m_vgPath; }
+ void clear();
+
+public:
+ FloatPoint m_currentPoint;
+ FloatPoint m_subpathStartPoint;
+
+private:
+ void createPath();
+
+ VGPath m_vgPath;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/openvg/SharedResourceOpenVG.cpp b/Source/WebCore/platform/graphics/openvg/SharedResourceOpenVG.cpp
new file mode 100644
index 0000000..a843db5
--- /dev/null
+++ b/Source/WebCore/platform/graphics/openvg/SharedResourceOpenVG.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "SharedResourceOpenVG.h"
+
+#include "SurfaceOpenVG.h"
+
+#if PLATFORM(EGL)
+#include "EGLDisplayOpenVG.h"
+#endif
+
+namespace WebCore {
+
+void SharedResourceOpenVG::makeSharedContextCurrent()
+{
+#if PLATFORM(EGL)
+ EGLDisplayOpenVG::current()->sharedPlatformSurface()->makeCurrent();
+#endif
+}
+
+void SharedResourceOpenVG::makeCompatibleContextCurrent()
+{
+#if PLATFORM(EGL)
+ EGLDisplayOpenVG::current()->sharedPlatformSurface()->makeCompatibleCurrent();
+#endif
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/openvg/SharedResourceOpenVG.h b/Source/WebCore/platform/graphics/openvg/SharedResourceOpenVG.h
new file mode 100644
index 0000000..436ae90
--- /dev/null
+++ b/Source/WebCore/platform/graphics/openvg/SharedResourceOpenVG.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef SharedResourceOpenVG_h
+#define SharedResourceOpenVG_h
+
+namespace WebCore {
+
+class SharedResourceOpenVG {
+public:
+ void makeSharedContextCurrent();
+ void makeCompatibleContextCurrent();
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/openvg/SurfaceOpenVG.cpp b/Source/WebCore/platform/graphics/openvg/SurfaceOpenVG.cpp
new file mode 100644
index 0000000..6700c6f
--- /dev/null
+++ b/Source/WebCore/platform/graphics/openvg/SurfaceOpenVG.cpp
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "SurfaceOpenVG.h"
+
+#include "IntSize.h"
+#include "PainterOpenVG.h"
+
+#if PLATFORM(EGL)
+#include "EGLDisplayOpenVG.h"
+#include "EGLUtils.h"
+#endif
+
+#include <wtf/Assertions.h>
+
+namespace WebCore {
+
+PainterOpenVG* SurfaceOpenVG::s_currentPainter = 0;
+
+SurfaceOpenVG* SurfaceOpenVG::currentSurface()
+{
+#if PLATFORM(EGL)
+ return EGLDisplayOpenVG::currentSurface();
+#else
+ ASSERT_NOT_REACHED();
+ return 0;
+#endif
+}
+
+#if PLATFORM(EGL)
+SurfaceOpenVG::SurfaceOpenVG(const IntSize& size, const EGLDisplay& display, EGLConfig* confPtr, EGLint* errorCode)
+ : m_activePainter(0)
+ , m_eglDisplay(display)
+ , m_eglSurface(EGL_NO_SURFACE)
+ , m_eglContext(EGL_NO_CONTEXT)
+{
+ ASSERT(m_eglDisplay != EGL_NO_DISPLAY);
+
+ EGLDisplayOpenVG* displayManager = EGLDisplayOpenVG::forDisplay(m_eglDisplay);
+ EGLConfig config = confPtr ? (*confPtr) : displayManager->defaultPbufferConfig();
+ m_eglSurface = displayManager->createPbufferSurface(size, config, errorCode);
+
+ if (m_eglSurface == EGL_NO_SURFACE)
+ return;
+
+ m_eglContext = displayManager->contextForSurface(m_eglSurface);
+ EGLDisplayOpenVG::registerPlatformSurface(this);
+}
+
+SurfaceOpenVG::SurfaceOpenVG(EGLClientBuffer buffer, EGLenum bufferType, const EGLDisplay& display, EGLConfig* confPtr, EGLint* errorCode)
+ : m_activePainter(0)
+ , m_eglDisplay(display)
+ , m_eglSurface(EGL_NO_SURFACE)
+ , m_eglContext(EGL_NO_CONTEXT)
+{
+ ASSERT(m_eglDisplay != EGL_NO_DISPLAY);
+
+ EGLDisplayOpenVG* displayManager = EGLDisplayOpenVG::forDisplay(m_eglDisplay);
+ EGLConfig config = confPtr ? (*confPtr) : displayManager->defaultPbufferConfig();
+ m_eglSurface = displayManager->createPbufferFromClientBuffer(buffer, bufferType, config, errorCode);
+
+ if (m_eglSurface == EGL_NO_SURFACE)
+ return;
+
+ m_eglContext = displayManager->contextForSurface(m_eglSurface);
+ EGLDisplayOpenVG::registerPlatformSurface(this);
+}
+
+SurfaceOpenVG::SurfaceOpenVG(EGLNativeWindowType window, const EGLDisplay& display, EGLConfig* confPtr)
+ : m_activePainter(0)
+ , m_eglDisplay(display)
+ , m_eglSurface(EGL_NO_SURFACE)
+ , m_eglContext(EGL_NO_CONTEXT)
+{
+ ASSERT(m_eglDisplay != EGL_NO_DISPLAY);
+
+ EGLDisplayOpenVG* displayManager = EGLDisplayOpenVG::forDisplay(m_eglDisplay);
+ EGLConfig config = confPtr ? (*confPtr) : displayManager->defaultWindowConfig();
+ m_eglSurface = displayManager->surfaceForWindow(window, config);
+ ASSERT(m_eglSurface != EGL_NO_SURFACE);
+
+ m_eglContext = displayManager->contextForSurface(m_eglSurface);
+ EGLDisplayOpenVG::registerPlatformSurface(this);
+}
+
+// Constructor only accessible to EGLDisplayOpenVG for shared context
+// initialization. The parameter types might define to void* like in the
+// window surface constructor, so it can't be overloaded with all the required
+// arguments and EGLDisplayOpenVG basically implements the constructor
+// by itself.
+SurfaceOpenVG::SurfaceOpenVG()
+ : m_activePainter(0)
+ , m_eglDisplay(EGL_NO_DISPLAY)
+ , m_eglSurface(EGL_NO_SURFACE)
+ , m_eglContext(EGL_NO_CONTEXT)
+{
+}
+#endif
+
+SurfaceOpenVG::~SurfaceOpenVG()
+{
+ if (!isValid())
+ return;
+
+ if (m_activePainter && this == m_activePainter->baseSurface())
+ m_activePainter->end();
+
+#if PLATFORM(EGL)
+ EGLDisplayOpenVG::forDisplay(m_eglDisplay)->destroySurface(m_eglSurface);
+ EGLDisplayOpenVG::unregisterPlatformSurface(this);
+#else
+ ASSERT_NOT_REACHED();
+#endif
+}
+
+bool SurfaceOpenVG::isValid() const
+{
+#if PLATFORM(EGL)
+ return (m_eglSurface != EGL_NO_SURFACE);
+#else
+ ASSERT_NOT_REACHED();
+ return false;
+#endif
+}
+
+int SurfaceOpenVG::width() const
+{
+#if PLATFORM(EGL)
+ ASSERT(m_eglSurface != EGL_NO_SURFACE);
+
+ EGLint width;
+ eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_WIDTH, &width);
+ ASSERT_EGL_NO_ERROR();
+ return width;
+#else
+ ASSERT_NOT_REACHED();
+ return 0;
+#endif
+}
+
+int SurfaceOpenVG::height() const
+{
+#if PLATFORM(EGL)
+ ASSERT(m_eglSurface != EGL_NO_SURFACE);
+
+ EGLint height;
+ eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_HEIGHT, &height);
+ ASSERT_EGL_NO_ERROR();
+ return height;
+#else
+ ASSERT_NOT_REACHED();
+ return 0;
+#endif
+}
+
+SurfaceOpenVG* SurfaceOpenVG::sharedSurface() const
+{
+#if PLATFORM(EGL)
+ ASSERT(m_eglSurface != EGL_NO_SURFACE);
+ return EGLDisplayOpenVG::forDisplay(m_eglDisplay)->sharedPlatformSurface();
+#else
+ ASSERT_NOT_REACHED();
+ return 0;
+#endif
+}
+
+void SurfaceOpenVG::makeCurrent(MakeCurrentMode mode)
+{
+#if PLATFORM(EGL)
+ ASSERT(m_eglSurface != EGL_NO_SURFACE);
+
+ eglBindAPI(EGL_OPENVG_API);
+ ASSERT_EGL_NO_ERROR();
+ EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW);
+ ASSERT_EGL_NO_ERROR();
+
+ if (currentSurface != m_eglSurface) {
+ // Save other context before switching over.
+ if (s_currentPainter && mode != DontSaveOrApplyPainterState
+ && s_currentPainter->surface()->m_eglSurface == currentSurface)
+ s_currentPainter->save(PainterOpenVG::KeepCurrentState);
+
+ eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
+ ASSERT_EGL_NO_ERROR();
+ s_currentPainter = 0;
+ }
+#endif
+
+ if (m_activePainter && mode == ApplyPainterStateOnSurfaceSwitch
+ && s_currentPainter != m_activePainter) {
+ m_activePainter->applyState();
+ s_currentPainter = m_activePainter;
+ }
+}
+
+void SurfaceOpenVG::makeCompatibleCurrent()
+{
+#if PLATFORM(EGL)
+ ASSERT(m_eglSurface != EGL_NO_SURFACE);
+
+ eglBindAPI(EGL_OPENVG_API);
+ ASSERT_EGL_NO_ERROR();
+ EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW);
+ ASSERT_EGL_NO_ERROR();
+
+ if (currentSurface == m_eglSurface) {
+ if (m_activePainter && s_currentPainter != m_activePainter) {
+ m_activePainter->applyState();
+ s_currentPainter = m_activePainter;
+ }
+ } else if (!EGLDisplayOpenVG::forDisplay(m_eglDisplay)->surfacesCompatible(currentSurface, m_eglSurface)) {
+ // Save other context before switching over.
+ if (s_currentPainter && s_currentPainter->surface()->m_eglSurface == currentSurface)
+ s_currentPainter->save(PainterOpenVG::KeepCurrentState);
+
+ eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
+ ASSERT_EGL_NO_ERROR();
+ s_currentPainter = 0;
+ }
+ // else: surfaces compatible, no need to switch contexts
+#endif
+}
+
+void SurfaceOpenVG::flush()
+{
+#if PLATFORM(EGL)
+ ASSERT(m_eglSurface != EGL_NO_SURFACE);
+
+ eglSwapBuffers(m_eglDisplay, m_eglSurface);
+ ASSERT_EGL_NO_ERROR();
+#endif
+}
+
+void SurfaceOpenVG::setActivePainter(PainterOpenVG* painter)
+{
+ ASSERT(isValid());
+
+ // If painter is non-zero, we want to make sure there was no previous painter set.
+ ASSERT(!painter || !m_activePainter);
+
+ // Make sure a disabled painter isn't marked as global current painter anymore.
+ if (!painter && s_currentPainter == m_activePainter)
+ s_currentPainter = 0;
+
+ m_activePainter = painter;
+}
+
+PainterOpenVG* SurfaceOpenVG::activePainter()
+{
+ ASSERT(isValid());
+ return m_activePainter;
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/openvg/SurfaceOpenVG.h b/Source/WebCore/platform/graphics/openvg/SurfaceOpenVG.h
new file mode 100644
index 0000000..46d1646
--- /dev/null
+++ b/Source/WebCore/platform/graphics/openvg/SurfaceOpenVG.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef SurfaceOpenVG_h
+#define SurfaceOpenVG_h
+
+#if PLATFORM(EGL)
+#include <egl.h>
+#endif
+
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+#if PLATFORM(EGL)
+class EGLDisplayOpenVG;
+#endif
+class PainterOpenVG;
+class IntSize;
+
+/**
+ * SurfaceOpenVG provides the functionality of surfaces and contexts that are
+ * underlying the OpenVG implementation. In the vast majority of cases, that
+ * underlying technology is EGL, but OpenVG doesn't depend on EGL per se.
+ * Wrapping surface/context functionality into a separate class avoids lots
+ * of #ifdefs and should make it easy to add different surface/context
+ * implementations than EGL.
+ */
+class SurfaceOpenVG : public Noncopyable {
+public:
+ enum MakeCurrentMode {
+ ApplyPainterStateOnSurfaceSwitch,
+ DontApplyPainterState,
+ DontSaveOrApplyPainterState
+ };
+
+ static SurfaceOpenVG* currentSurface();
+
+#if PLATFORM(EGL)
+ friend class EGLDisplayOpenVG;
+
+ /**
+ * Create a new EGL pbuffer surface with the specified size and config on
+ * the given display. If config is not specified, the display's default
+ * pbuffer config is used.
+ *
+ * This constructor will trigger an assertion if creation of the surface
+ * fails, unless you pledge to manually process the error code by passing
+ * a non-zero pointer as errorCode parameter. The error code returned by
+ * eglGetError() will be written to that variable.
+ */
+ SurfaceOpenVG(const IntSize& size, const EGLDisplay& display, EGLConfig* config = 0, EGLint* errorCode = 0);
+
+ /**
+ * Create a new EGL pbuffer surface that will be bound to the given
+ * client buffer (read: VGImage), with the specified config on the
+ * given display. If config is not specified, the display's default
+ * pbuffer config is used.
+ *
+ * After the surface is created, you will only be able to access the
+ * client buffer image if the surface is not current. The recommended way
+ * to ensure this is to call surface->sharedSurface()->makeCurrent() if you
+ * simply want to access the image's pixel contents, or if you intend to
+ * draw the image directly, making the draw target surface current.
+ *
+ * This constructor will trigger an assertion if creation of the surface
+ * fails, unless you pledge to manually process the error code by passing
+ * a non-zero pointer as errorCode parameter. The error code returned by
+ * eglGetError() will be written to that variable.
+ */
+ SurfaceOpenVG(EGLClientBuffer buffer, EGLenum bufferType,
+ const EGLDisplay& display, EGLConfig* config = 0, EGLint* errorCode = 0);
+
+ /**
+ * Create a new EGL window surface with the specified native window handle
+ * and config on the given display. If config is not specified, the
+ * display's default window config is used.
+ */
+ SurfaceOpenVG(EGLNativeWindowType window, const EGLDisplay& display, EGLConfig* config = 0);
+
+ EGLDisplay eglDisplay() const { return m_eglDisplay; }
+ EGLSurface eglSurface() const { return m_eglSurface; }
+ EGLContext eglContext() const { return m_eglContext; }
+#endif
+
+ ~SurfaceOpenVG();
+
+ /**
+ * If a surface is invalid (could not be created), all method calls will
+ * crash horribly.
+ */
+ bool isValid() const;
+
+ int width() const;
+ int height() const;
+
+ SurfaceOpenVG* sharedSurface() const;
+
+ /**
+ * Make the associated GL/EGL context the current one, so that subsequent
+ * OpenVG commands apply to it.
+ */
+ void makeCurrent(MakeCurrentMode mode = ApplyPainterStateOnSurfaceSwitch);
+
+ /**
+ * Make a surface/context combination current that is "compatible"
+ * (i.e. can access its shared resources) to the given one. If no
+ * surface/context is current, the given one is made current.
+ *
+ * This method is meant to avoid context changes if they're not
+ * necessary, particularly tailored for the case where something
+ * compatible to the shared surface is requested while actual painting
+ * happens on another surface.
+ */
+ void makeCompatibleCurrent();
+
+ /**
+ * Empty the OpenVG pipeline and make sure all the performed paint
+ * operations show up on the surface as actual drawn pixels.
+ */
+ void flush();
+
+ void setActivePainter(PainterOpenVG*);
+ PainterOpenVG* activePainter();
+
+private:
+ PainterOpenVG* m_activePainter;
+ static PainterOpenVG* s_currentPainter; // global currently active painter
+
+#if PLATFORM(EGL)
+ SurfaceOpenVG(); // for EGLDisplayOpenVG
+
+ EGLDisplay m_eglDisplay;
+ EGLSurface m_eglSurface;
+ EGLContext m_eglContext;
+#endif
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/openvg/TiledImageOpenVG.cpp b/Source/WebCore/platform/graphics/openvg/TiledImageOpenVG.cpp
new file mode 100644
index 0000000..64d94c9
--- /dev/null
+++ b/Source/WebCore/platform/graphics/openvg/TiledImageOpenVG.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "TiledImageOpenVG.h"
+
+#include "FloatRect.h"
+#include "IntRect.h"
+#include "VGUtils.h"
+
+namespace WebCore {
+
+TiledImageOpenVG::TiledImageOpenVG(const IntSize& size, const IntSize& tileSize)
+ : SharedResourceOpenVG()
+ , m_size(size)
+ , m_maxTileSize(tileSize)
+ , m_numColumns((m_size.width() - 1) / m_maxTileSize.width() + 1)
+ , m_tiles(((m_size.height() - 1) / m_maxTileSize.height() + 1) * m_numColumns, VG_INVALID_HANDLE)
+{
+}
+
+TiledImageOpenVG::TiledImageOpenVG(const TiledImageOpenVG& other)
+ : SharedResourceOpenVG()
+ , m_size(other.m_size)
+ , m_maxTileSize(other.m_maxTileSize)
+ , m_numColumns(other.m_numColumns)
+ , m_tiles(other.m_tiles)
+{
+ detachTiles();
+}
+
+TiledImageOpenVG& TiledImageOpenVG::operator=(const TiledImageOpenVG& other)
+{
+ if (&other != this) {
+ destroyTiles();
+
+ m_size = other.m_size;
+ m_maxTileSize = other.m_maxTileSize;
+ m_numColumns = other.m_numColumns;
+ m_tiles = other.m_tiles;
+
+ detachTiles();
+ }
+ return *this;
+}
+
+TiledImageOpenVG::~TiledImageOpenVG()
+{
+ destroyTiles();
+}
+
+int TiledImageOpenVG::numTiles() const
+{
+ return m_tiles.size();
+}
+
+int TiledImageOpenVG::numColumns() const
+{
+ return m_numColumns;
+}
+
+int TiledImageOpenVG::numRows() const
+{
+ return m_tiles.size() / m_numColumns;
+}
+
+void TiledImageOpenVG::setTile(int xIndex, int yIndex, VGImage image)
+{
+ ASSERT(xIndex < m_numColumns);
+ int i = (yIndex * m_numColumns) + xIndex;
+ ASSERT(i < m_tiles.size());
+ m_tiles.at(i) = image;
+}
+
+IntRect TiledImageOpenVG::tilesInRect(const FloatRect& rect) const
+{
+ int leftIndex = static_cast<int>(rect.x()) / m_maxTileSize.width();
+ int topIndex = static_cast<int>(rect.y()) / m_maxTileSize.height();
+
+ if (leftIndex < 0)
+ leftIndex = 0;
+ if (topIndex < 0)
+ topIndex = 0;
+
+ // Round rect edges up to get the outer pixel boundaries.
+ int rightIndex = (static_cast<int>(ceil(rect.right())) - 1) / m_maxTileSize.width();
+ int bottomIndex = (static_cast<int>(ceil(rect.bottom())) - 1) / m_maxTileSize.height();
+ int columns = (rightIndex - leftIndex) + 1;
+ int rows = (bottomIndex - topIndex) + 1;
+
+ return IntRect(leftIndex, topIndex,
+ (columns <= m_numColumns) ? columns : m_numColumns,
+ (rows <= (m_tiles.size() / m_numColumns)) ? rows : (m_tiles.size() / m_numColumns));
+}
+
+VGImage TiledImageOpenVG::tile(int xIndex, int yIndex) const
+{
+ ASSERT(xIndex < m_numColumns);
+ int i = (yIndex * m_numColumns) + xIndex;
+ ASSERT(i < m_tiles.size());
+ return m_tiles.at(i);
+}
+
+IntRect TiledImageOpenVG::tileRect(int xIndex, int yIndex) const
+{
+ ASSERT(xIndex < m_numColumns);
+ ASSERT((yIndex * m_numColumns) + xIndex < m_tiles.size());
+
+ int x = xIndex * m_maxTileSize.width();
+ int y = yIndex * m_maxTileSize.height();
+
+ return IntRect(x, y,
+ ((m_maxTileSize.width() < m_size.width() - x) ? m_maxTileSize.width() : (m_size.width() - x)),
+ ((m_maxTileSize.height() < m_size.height() - y) ? m_maxTileSize.height() : (m_size.height() - y)));
+}
+
+void TiledImageOpenVG::detachTiles()
+{
+ makeSharedContextCurrent(); // because we create new images
+
+ int numTiles = m_tiles.size();
+ VGImage newTile, originalTile;
+
+ for (int i = 0; i < numTiles; ++i) {
+ originalTile = m_tiles.at(i);
+
+ if (originalTile == VG_INVALID_HANDLE)
+ continue;
+
+ VGImageFormat format = (VGImageFormat) vgGetParameteri(originalTile, VG_IMAGE_FORMAT);
+ VGint width = vgGetParameteri(originalTile, VG_IMAGE_WIDTH);
+ VGint height = vgGetParameteri(originalTile, VG_IMAGE_HEIGHT);
+ ASSERT_VG_NO_ERROR();
+
+ newTile = vgCreateImage(format, width, height, VG_IMAGE_QUALITY_FASTER);
+ ASSERT_VG_NO_ERROR();
+
+ vgCopyImage(newTile, 0, 0, originalTile, 0, 0, width, height, VG_FALSE /* dither */);
+ ASSERT_VG_NO_ERROR();
+
+ m_tiles.at(i) = newTile;
+ }
+}
+
+void TiledImageOpenVG::destroyTiles()
+{
+ makeCompatibleContextCurrent();
+
+ Vector<VGImage>::const_iterator it = m_tiles.begin();
+ Vector<VGImage>::const_iterator end = m_tiles.end();
+
+ for (; it != end; ++it) {
+ if (*it != VG_INVALID_HANDLE)
+ vgDestroyImage(*it);
+ }
+ ASSERT_VG_NO_ERROR();
+
+ m_tiles.fill(VG_INVALID_HANDLE);
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/openvg/TiledImageOpenVG.h b/Source/WebCore/platform/graphics/openvg/TiledImageOpenVG.h
new file mode 100644
index 0000000..c8f55d2
--- /dev/null
+++ b/Source/WebCore/platform/graphics/openvg/TiledImageOpenVG.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef TiledImageOpenVG_h
+#define TiledImageOpenVG_h
+
+#include "IntRect.h"
+#include "IntSize.h"
+#include "SharedResourceOpenVG.h"
+
+#include <openvg.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class FloatRect;
+
+class TiledImageOpenVG : public SharedResourceOpenVG {
+public:
+ TiledImageOpenVG(const IntSize& size, const IntSize& tileSize);
+ TiledImageOpenVG(const TiledImageOpenVG&);
+ ~TiledImageOpenVG();
+
+ TiledImageOpenVG& operator=(const TiledImageOpenVG&);
+
+ const IntSize& size() const { return m_size; }
+ const IntSize& maxTileSize() const { return m_maxTileSize; }
+
+ int numTiles() const;
+ int numColumns() const;
+ int numRows() const;
+
+ IntRect tilesInRect(const FloatRect&) const;
+
+ void setTile(int xIndex, int yIndex, VGImage);
+ VGImage tile(int xIndex, int yIndex) const;
+ IntRect tileRect(int xIndex, int yIndex) const;
+
+private:
+ void detachTiles();
+ void destroyTiles();
+
+ IntSize m_size;
+ IntSize m_maxTileSize;
+
+ int m_numColumns;
+
+ Vector<VGImage> m_tiles;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/openvg/VGUtils.cpp b/Source/WebCore/platform/graphics/openvg/VGUtils.cpp
new file mode 100644
index 0000000..2559aaf
--- /dev/null
+++ b/Source/WebCore/platform/graphics/openvg/VGUtils.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "VGUtils.h"
+
+#include "AffineTransform.h"
+#include "FloatRect.h"
+#include "TransformationMatrix.h"
+
+namespace WebCore {
+
+VGMatrix::VGMatrix(const VGfloat data[9])
+{
+ m_data[0] = data[0];
+ m_data[1] = data[1];
+ m_data[2] = data[2];
+ m_data[3] = data[3];
+ m_data[4] = data[4];
+ m_data[5] = data[5];
+ m_data[6] = data[6];
+ m_data[7] = data[7];
+ m_data[8] = data[8];
+}
+
+VGMatrix::VGMatrix(const AffineTransform& transformation)
+{
+ m_data[0] = transformation.a();
+ m_data[1] = transformation.b();
+ m_data[2] = 0;
+ m_data[3] = transformation.c();
+ m_data[4] = transformation.d();
+ m_data[5] = 0;
+ m_data[6] = transformation.e();
+ m_data[7] = transformation.f();
+ m_data[8] = 1;
+}
+
+VGMatrix::VGMatrix(const TransformationMatrix& matrix)
+{
+ m_data[0] = matrix.m11();
+ m_data[1] = matrix.m12();
+ m_data[2] = matrix.m14();
+ m_data[3] = matrix.m21();
+ m_data[4] = matrix.m22();
+ m_data[5] = matrix.m24();
+ m_data[6] = matrix.m41();
+ m_data[7] = matrix.m42();
+ m_data[8] = matrix.m44();
+}
+
+VGMatrix::operator AffineTransform() const
+{
+ AffineTransform transformation(
+ m_data[0], m_data[1],
+ m_data[3], m_data[4],
+ m_data[6], m_data[7]);
+ return transformation;
+}
+
+VGMatrix::operator TransformationMatrix() const
+{
+ TransformationMatrix matrix(
+ m_data[0], m_data[1], 0, m_data[2],
+ m_data[3], m_data[4], 0, m_data[5],
+ 0, 0, 1, 0,
+ m_data[6], m_data[7], 0, m_data[8]);
+ return matrix;
+}
+
+AffineTransform::operator VGMatrix() const
+{
+ return VGMatrix(*this);
+}
+
+TransformationMatrix::operator VGMatrix() const
+{
+ return VGMatrix(*this);
+}
+
+VGRect::VGRect(const VGfloat data[4])
+{
+ m_data[0] = data[0];
+ m_data[1] = data[1];
+ m_data[2] = data[2];
+ m_data[3] = data[3];
+}
+
+VGRect::VGRect(const FloatRect& rect)
+{
+ m_data[0] = rect.x();
+ m_data[1] = rect.y();
+ m_data[2] = rect.width();
+ m_data[3] = rect.height();
+}
+
+VGRect::operator FloatRect() const
+{
+ return FloatRect(m_data[0], m_data[1], m_data[2], m_data[3]);
+}
+
+FloatRect::operator VGRect() const
+{
+ return VGRect(*this);
+}
+
+int VGUtils::bytesForImage(VGImageFormat format, VGint width, VGint height)
+{
+ return width * height * imageFormatBitsPerPixel(format) / 8;
+}
+
+int VGUtils::bytesForImageScanline(VGImageFormat format, VGint width)
+{
+ int bits = width * imageFormatBitsPerPixel(format);
+ if (bits % 8 > 1) // If unaligned, round up to the next byte.
+ bits += 8 - (bits % 8);
+
+ return bits / 8;
+}
+
+int VGUtils::imageFormatBitsPerPixel(VGImageFormat format)
+{
+ switch (format) {
+ case VG_sRGBX_8888:
+ case VG_sRGBA_8888:
+ case VG_sRGBA_8888_PRE:
+ case VG_lRGBX_8888:
+ case VG_lRGBA_8888:
+ case VG_lRGBA_8888_PRE:
+ case VG_sXRGB_8888:
+ case VG_sARGB_8888:
+ case VG_sARGB_8888_PRE:
+ case VG_lXRGB_8888:
+ case VG_lARGB_8888:
+ case VG_lARGB_8888_PRE:
+ case VG_sBGRX_8888:
+ case VG_sBGRA_8888:
+ case VG_sBGRA_8888_PRE:
+ case VG_lBGRX_8888:
+ case VG_lBGRA_8888:
+ case VG_lBGRA_8888_PRE:
+ case VG_sXBGR_8888:
+ case VG_sABGR_8888:
+ case VG_sABGR_8888_PRE:
+ case VG_lXBGR_8888:
+ case VG_lABGR_8888:
+ case VG_lABGR_8888_PRE:
+ return 32;
+
+ case VG_sRGB_565:
+ case VG_sRGBA_5551:
+ case VG_sRGBA_4444:
+ case VG_sARGB_1555:
+ case VG_sARGB_4444:
+ case VG_sBGR_565:
+ case VG_sBGRA_5551:
+ case VG_sBGRA_4444:
+ case VG_sABGR_1555:
+ case VG_sABGR_4444:
+ return 16;
+
+ case VG_sL_8:
+ case VG_lL_8:
+ case VG_A_8:
+ return 8;
+
+ case VG_A_4:
+ return 4;
+
+ case VG_BW_1:
+ case VG_A_1:
+ return 1;
+
+ default: // Will only happen when OpenVG extends the enum and we don't.
+ ASSERT(false);
+ return 0;
+ }
+}
+
+#ifndef WTF_PLATFORM_BIG_ENDIAN
+VGImageFormat VGUtils::endianAwareImageFormat(VGImageFormat bigEndianFormat)
+{
+ switch (bigEndianFormat) {
+ case VG_sRGBX_8888: return VG_sXBGR_8888;
+ case VG_sRGBA_8888: return VG_sABGR_8888;
+ case VG_sRGBA_8888_PRE: return VG_sABGR_8888_PRE;
+ case VG_lRGBX_8888: return VG_lXBGR_8888;
+ case VG_lRGBA_8888: return VG_lABGR_8888;
+ case VG_lRGBA_8888_PRE: return VG_lABGR_8888_PRE;
+ case VG_sXRGB_8888: return VG_sBGRX_8888;
+ case VG_sARGB_8888: return VG_sBGRA_8888;
+ case VG_sARGB_8888_PRE: return VG_sBGRA_8888_PRE;
+ case VG_lXRGB_8888: return VG_lBGRX_8888;
+ case VG_lARGB_8888: return VG_lBGRA_8888;
+ case VG_lARGB_8888_PRE: return VG_lBGRA_8888_PRE;
+ case VG_sBGRX_8888: return VG_sXRGB_8888;
+ case VG_sBGRA_8888: return VG_sARGB_8888;
+ case VG_sBGRA_8888_PRE: return VG_sARGB_8888_PRE;
+ case VG_lBGRX_8888: return VG_lXRGB_8888;
+ case VG_lBGRA_8888: return VG_lARGB_8888;
+ case VG_lBGRA_8888_PRE: return VG_lARGB_8888_PRE;
+ case VG_sXBGR_8888: return VG_sRGBX_8888;
+ case VG_sABGR_8888: return VG_sRGBA_8888;
+ case VG_sABGR_8888_PRE: return VG_sRGBA_8888_PRE;
+ case VG_lXBGR_8888: return VG_lRGBX_8888;
+ case VG_lABGR_8888: return VG_lRGBA_8888;
+ case VG_lABGR_8888_PRE: return VG_lRGBA_8888_PRE;
+ default: ASSERT(false);
+ return (VGImageFormat) 0;
+ }
+}
+#else
+VGImageFormat VGUtils::endianAwareImageFormat(VGImageFormat bigEndianFormat)
+{
+ return bigEndianFormat;
+}
+#endif
+
+}
diff --git a/Source/WebCore/platform/graphics/openvg/VGUtils.h b/Source/WebCore/platform/graphics/openvg/VGUtils.h
new file mode 100644
index 0000000..06d5fea
--- /dev/null
+++ b/Source/WebCore/platform/graphics/openvg/VGUtils.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef VGUtils_h
+#define VGUtils_h
+
+#include <openvg.h>
+#include <wtf/Assertions.h>
+
+static inline const char* toVGErrorConstant(VGErrorCode error)
+{
+ switch (error) {
+ case VG_BAD_HANDLE_ERROR:
+ return "VG_BAD_HANDLE_ERROR";
+ case VG_ILLEGAL_ARGUMENT_ERROR:
+ return "VG_ILLEGAL_ARGUMENT_ERROR";
+ case VG_OUT_OF_MEMORY_ERROR:
+ return "VG_OUT_OF_MEMORY_ERROR";
+ case VG_PATH_CAPABILITY_ERROR:
+ return "VG_PATH_CAPABILITY_ERROR";
+ case VG_UNSUPPORTED_IMAGE_FORMAT_ERROR:
+ return "VG_UNSUPPORTED_IMAGE_FORMAT_ERROR";
+ case VG_UNSUPPORTED_PATH_FORMAT_ERROR:
+ return "VG_UNSUPPORTED_PATH_FORMAT_ERROR";
+ case VG_IMAGE_IN_USE_ERROR:
+ return "VG_IMAGE_IN_USE_ERROR";
+ case VG_NO_CONTEXT_ERROR:
+ return "VG_NO_CONTEXT_ERROR";
+ default:
+ return "UNKNOWN_ERROR";
+ }
+}
+
+#if ASSERT_DISABLED
+#define ASSERT_VG_NO_ERROR() ((void)0)
+#else
+#define ASSERT_VG_NO_ERROR() do { \
+ VGErrorCode vgErrorCode = vgGetError(); \
+ ASSERT_WITH_MESSAGE(vgErrorCode == VG_NO_ERROR, "Found %s", toVGErrorConstant(vgErrorCode)); \
+} while (0)
+#endif
+
+
+namespace WebCore {
+
+class AffineTransform;
+class FloatRect;
+class TransformationMatrix;
+
+class VGMatrix {
+public:
+ VGMatrix(const VGfloat data[9]);
+ VGMatrix(const AffineTransform&);
+ VGMatrix(const TransformationMatrix&);
+ const VGfloat* toVGfloat() const { return m_data; }
+ operator AffineTransform() const;
+ operator TransformationMatrix() const;
+private:
+ VGfloat m_data[9];
+};
+
+class VGRect {
+public:
+ VGRect(const VGfloat data[4]);
+ VGRect(const FloatRect&);
+ const VGfloat* toVGfloat() const { return m_data; }
+ operator FloatRect() const;
+private:
+ VGfloat m_data[4];
+};
+
+class VGUtils {
+public:
+ static int bytesForImage(VGImageFormat, VGint width, VGint height);
+ static int bytesForImageScanline(VGImageFormat, VGint width);
+ static int imageFormatBitsPerPixel(VGImageFormat);
+
+ /**
+ * Return a flipped VGImageFormat if the platform is little endian
+ * (e.g. VG_ABGR_8888 for a given VG_RGBA_8888), or return the image format
+ * as is if the platform is big endian.
+ *
+ * OpenVG itself is indifferent to endianness, it will always work on a
+ * single machine word with the bytes going from left to right as specified
+ * in the image format, no matter which one of the bytes is most or least
+ * significant.
+ *
+ * However, if you interface with vgImageSubData()/vgGetImageSubData()
+ * using a byte array then you want to make sure the byte order is
+ * appropriate for the given platform (otherwise the byte indexes need
+ * to be swapped depending on endianness). So, use this function when
+ * interfacing with byte arrays, and don't use it otherwise.
+ */
+ static VGImageFormat endianAwareImageFormat(VGImageFormat bigEndianFormat);
+};
+
+}
+
+#endif