diff options
Diffstat (limited to 'Source/WebCore/platform/graphics/openvg')
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 |