diff options
Diffstat (limited to 'WebKit/chromium/src/GraphicsContext3D.cpp')
| -rw-r--r-- | WebKit/chromium/src/GraphicsContext3D.cpp | 2146 |
1 files changed, 2146 insertions, 0 deletions
diff --git a/WebKit/chromium/src/GraphicsContext3D.cpp b/WebKit/chromium/src/GraphicsContext3D.cpp new file mode 100644 index 0000000..c9ba5a1 --- /dev/null +++ b/WebKit/chromium/src/GraphicsContext3D.cpp @@ -0,0 +1,2146 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(3D_CANVAS) + +#include "GraphicsContext3D.h" + +#include "CachedImage.h" +#include "CString.h" +#include "HTMLCanvasElement.h" +#include "HTMLImageElement.h" +#include "ImageBuffer.h" +#include "ImageData.h" +#include "NotImplemented.h" +#include "WebGLBuffer.h" +#include "WebGLByteArray.h" +#include "WebGLFloatArray.h" +#include "WebGLFramebuffer.h" +#include "WebGLIntArray.h" +#include "WebGLProgram.h" +#include "WebGLRenderbuffer.h" +#include "WebGLRenderingContext.h" +#include "WebGLShader.h" +#include "WebGLTexture.h" +#include "WebGLUnsignedByteArray.h" + +#include <stdio.h> +#include <wtf/FastMalloc.h> + +#if PLATFORM(WIN_OS) +#include <windows.h> +#endif + +#include "GL/glew.h" + +#if PLATFORM(CG) +#include "GraphicsContext.h" +#include <CoreGraphics/CGContext.h> +#include <CoreGraphics/CGBitmapContext.h> +#include <CoreGraphics/CGImage.h> +#include <OpenGL/OpenGL.h> +#else +#define FLIP_FRAMEBUFFER_VERTICALLY +#endif + +#if PLATFORM(SKIA) +#include "NativeImageSkia.h" +#endif + +#if PLATFORM(DARWIN) +#define USE_TEXTURE_RECTANGLE_FOR_FRAMEBUFFER +#endif + +#if PLATFORM(LINUX) +#include <dlfcn.h> +#include "GL/glxew.h" +#endif + +using namespace std; + +namespace WebCore { + +// GraphicsContext3DInternal ----------------------------------------------------- + +// Uncomment this to render to a separate window for debugging +// #define RENDER_TO_DEBUGGING_WINDOW + +#define EXTRACT(val) (!val ? 0 : val->object()) + +class GraphicsContext3DInternal { +public: + GraphicsContext3DInternal(); + ~GraphicsContext3DInternal(); + + bool makeContextCurrent(); + + PlatformGraphicsContext3D platformGraphicsContext3D() const; + Platform3DObject platformTexture() const; + + void reshape(int width, int height); + + void beginPaint(WebGLRenderingContext* context); + + bool validateTextureTarget(int target); + bool validateTextureParameter(int param); + + void activeTexture(unsigned long texture); + void bindBuffer(unsigned long target, + WebGLBuffer* buffer); + void bindTexture(unsigned long target, + WebGLTexture* texture); + void bufferDataImpl(unsigned long target, int size, const void* data, unsigned long usage); + void disableVertexAttribArray(unsigned long index); + void enableVertexAttribArray(unsigned long index); + unsigned long getError(); + void vertexAttribPointer(unsigned long indx, int size, int type, bool normalized, + unsigned long stride, unsigned long offset); + void viewportImpl(long x, long y, unsigned long width, unsigned long height); + + void synthesizeGLError(unsigned long error); + +private: + unsigned int m_texture; + unsigned int m_fbo; + unsigned int m_depthBuffer; + unsigned int m_cachedWidth, m_cachedHeight; + +#ifdef FLIP_FRAMEBUFFER_VERTICALLY + unsigned char* m_scanline; + void flipVertically(unsigned char* framebuffer, + unsigned int width, + unsigned int height); +#endif + + // Note: we aren't currently using this information, but we will + // need to in order to verify that all enabled vertex arrays have + // a valid buffer bound -- to avoid crashes on certain cards. + unsigned int m_boundArrayBuffer; + class VertexAttribPointerState { + public: + VertexAttribPointerState(); + + bool enabled; + unsigned long buffer; + unsigned long indx; + int size; + int type; + bool normalized; + unsigned long stride; + unsigned long offset; + }; + + enum { + NumTrackedPointerStates = 2 + }; + VertexAttribPointerState m_vertexAttribPointerState[NumTrackedPointerStates]; + + // Errors raised by synthesizeGLError(). + ListHashSet<unsigned long> m_syntheticErrors; + +#if PLATFORM(SKIA) + // If the width and height of the Canvas's backing store don't + // match those that we were given in the most recent call to + // reshape(), then we need an intermediate bitmap to read back the + // frame buffer into. This seems to happen when CSS styles are + // used to resize the Canvas. + SkBitmap* m_resizingBitmap; +#endif + +#if PLATFORM(WIN_OS) + HWND m_canvasWindow; + HDC m_canvasDC; + HGLRC m_contextObj; +#elif PLATFORM(CG) + CGLPBufferObj m_pbuffer; + CGLContextObj m_contextObj; + unsigned char* m_renderOutput; + CGContextRef m_cgContext; +#elif PLATFORM(LINUX) + Display* m_display; + GLXContext m_contextObj; + GLXPbuffer m_pbuffer; + // In order to avoid problems caused by linking against libGL, we + // dynamically look up all the symbols we need. + // http://code.google.com/p/chromium/issues/detail?id=16800 + void* m_libGL; + PFNGLXCHOOSEFBCONFIGPROC m_glXChooseFBConfig; + PFNGLXCREATENEWCONTEXTPROC m_glXCreateNewContext; + PFNGLXCREATEPBUFFERPROC m_glXCreatePbuffer; + PFNGLXDESTROYPBUFFERPROC m_glXDestroyPbuffer; + typedef Bool (* PFNGLXMAKECURRENTPROC)(Display* dpy, GLXDrawable drawable, GLXContext ctx); + PFNGLXMAKECURRENTPROC m_glXMakeCurrent; + typedef void (* PFNGLXDESTROYCONTEXTPROC)(Display* dpy, GLXContext ctx); + PFNGLXDESTROYCONTEXTPROC m_glXDestroyContext; + typedef GLXContext (* PFNGLXGETCURRENTCONTEXTPROC)(void); + PFNGLXGETCURRENTCONTEXTPROC m_glXGetCurrentContext; +#else + #error Must port GraphicsContext3D to your platform +#endif +}; + +GraphicsContext3DInternal::VertexAttribPointerState::VertexAttribPointerState() + : enabled(false) + , buffer(0) + , indx(0) + , size(0) + , type(0) + , normalized(false) + , stride(0) + , offset(0) +{ +} + +#if PLATFORM(LINUX) +static void* tryLoad(const char* libName) +{ + // We use RTLD_GLOBAL semantics so that GLEW initialization works; + // GLEW expects to be able to open the current process's handle + // and do dlsym's of GL entry points from there. + return dlopen(libName, RTLD_LAZY | RTLD_GLOBAL); +} +#endif + +GraphicsContext3DInternal::GraphicsContext3DInternal() + : m_texture(0) + , m_fbo(0) + , m_depthBuffer(0) +#ifdef FLIP_FRAMEBUFFER_VERTICALLY + , m_scanline(0) +#endif + , m_boundArrayBuffer(0) +#if PLATFORM(SKIA) + , m_resizingBitmap(0) +#endif +#if PLATFORM(WIN_OS) + , m_canvasWindow(0) + , m_canvasDC(0) + , m_contextObj(0) +#elif PLATFORM(CG) + , m_pbuffer(0) + , m_contextObj(0) + , m_renderOutput(0) + , m_cgContext(0) +#elif PLATFORM(LINUX) + , m_display(0) + , m_contextObj(0) + , m_pbuffer(0) + , m_glXChooseFBConfig(0) + , m_glXCreateNewContext(0) + , m_glXCreatePbuffer(0) + , m_glXDestroyPbuffer(0) + , m_glXMakeCurrent(0) + , m_glXDestroyContext(0) + , m_glXGetCurrentContext(0) +#else +#error Must port to your platform +#endif +{ +#if PLATFORM(WIN_OS) + WNDCLASS wc; + if (!GetClassInfo(GetModuleHandle(0), L"CANVASGL", &wc)) { + ZeroMemory(&wc, sizeof(WNDCLASS)); + wc.style = CS_OWNDC; + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = DefWindowProc; + wc.lpszClassName = L"CANVASGL"; + + if (!RegisterClass(&wc)) { + printf("GraphicsContext3D: RegisterClass failed\n"); + return; + } + } + + m_canvasWindow = CreateWindow(L"CANVASGL", L"CANVASGL", + WS_CAPTION, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, 0, 0, GetModuleHandle(0), 0); + if (!m_canvasWindow) { + printf("GraphicsContext3DInternal: CreateWindow failed\n"); + return; + } + + // get the device context + m_canvasDC = GetDC(m_canvasWindow); + if (!m_canvasDC) { + printf("GraphicsContext3DInternal: GetDC failed\n"); + return; + } + + // find default pixel format + PIXELFORMATDESCRIPTOR pfd; + ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR)); + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL +#ifdef RENDER_TO_DEBUGGING_WINDOW + | PFD_DOUBLEBUFFER +#endif // RENDER_TO_DEBUGGING_WINDOW + ; + int pixelformat = ChoosePixelFormat(m_canvasDC, &pfd); + + // set the pixel format for the dc + if (!SetPixelFormat(m_canvasDC, pixelformat, &pfd)) { + printf("GraphicsContext3D: SetPixelFormat failed\n"); + return; + } + + // create rendering context + m_contextObj = wglCreateContext(m_canvasDC); + if (!m_contextObj) { + printf("GraphicsContext3D: wglCreateContext failed\n"); + return; + } + + if (!wglMakeCurrent(m_canvasDC, m_contextObj)) { + printf("GraphicsContext3D: wglMakeCurrent failed\n"); + return; + } + +#ifdef RENDER_TO_DEBUGGING_WINDOW + typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC) (int interval); + PFNWGLSWAPINTERVALEXTPROC setSwapInterval = 0; + setSwapInterval = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT"); + if (setSwapInterval) + setSwapInterval(1); +#endif // RENDER_TO_DEBUGGING_WINDOW + +#elif PLATFORM(CG) + // Create a 1x1 pbuffer and associated context to bootstrap things + CGLPixelFormatAttribute attribs[] = { + (CGLPixelFormatAttribute) kCGLPFAPBuffer, + (CGLPixelFormatAttribute) 0 + }; + CGLPixelFormatObj pixelFormat; + GLint numPixelFormats; + if (CGLChoosePixelFormat(attribs, &pixelFormat, &numPixelFormats) != kCGLNoError) { + printf("GraphicsContext3D: error choosing pixel format\n"); + return; + } + if (!pixelFormat) { + printf("GraphicsContext3D: no pixel format selected\n"); + return; + } + CGLContextObj context; + CGLError res = CGLCreateContext(pixelFormat, 0, &context); + CGLDestroyPixelFormat(pixelFormat); + if (res != kCGLNoError) { + printf("GraphicsContext3D: error creating context\n"); + return; + } + CGLPBufferObj pbuffer; + if (CGLCreatePBuffer(1, 1, GL_TEXTURE_2D, GL_RGBA, 0, &pbuffer) != kCGLNoError) { + CGLDestroyContext(context); + printf("GraphicsContext3D: error creating pbuffer\n"); + return; + } + if (CGLSetPBuffer(context, pbuffer, 0, 0, 0) != kCGLNoError) { + CGLDestroyContext(context); + CGLDestroyPBuffer(pbuffer); + printf("GraphicsContext3D: error attaching pbuffer to context\n"); + return; + } + if (CGLSetCurrentContext(context) != kCGLNoError) { + CGLDestroyContext(context); + CGLDestroyPBuffer(pbuffer); + printf("GraphicsContext3D: error making context current\n"); + return; + } + m_pbuffer = pbuffer; + m_contextObj = context; +#elif PLATFORM(LINUX) + m_display = XOpenDisplay(0); + if (!m_display) { + printf("GraphicsContext3D: error opening X display\n"); + return; + } + + const char* libNames[] = { + "/usr/lib/libGL.so.1", + "/usr/lib32/libGL.so.1", + "/usr/lib64/libGL.so.1", + }; + for (int i = 0; i < sizeof(libNames) / sizeof(const char*); i++) { + m_libGL = tryLoad(libNames[i]); + if (m_libGL) + break; + } + if (!m_libGL) { + printf("GraphicsContext3D: error opening libGL.so.1\n"); + printf("GraphicsContext3D: tried:"); + for (int i = 0; i < sizeof(libNames) / sizeof(const char*); i++) + printf(" %s", libNames[i]); + return; + } + m_glXChooseFBConfig = (PFNGLXCHOOSEFBCONFIGPROC) dlsym(m_libGL, "glXChooseFBConfig"); + m_glXCreateNewContext = (PFNGLXCREATENEWCONTEXTPROC) dlsym(m_libGL, "glXCreateNewContext"); + m_glXCreatePbuffer = (PFNGLXCREATEPBUFFERPROC) dlsym(m_libGL, "glXCreatePbuffer"); + m_glXDestroyPbuffer = (PFNGLXDESTROYPBUFFERPROC) dlsym(m_libGL, "glXDestroyPbuffer"); + m_glXMakeCurrent = (PFNGLXMAKECURRENTPROC) dlsym(m_libGL, "glXMakeCurrent"); + m_glXDestroyContext = (PFNGLXDESTROYCONTEXTPROC) dlsym(m_libGL, "glXDestroyContext"); + m_glXGetCurrentContext = (PFNGLXGETCURRENTCONTEXTPROC) dlsym(m_libGL, "glXGetCurrentContext"); + if (!m_glXChooseFBConfig || !m_glXCreateNewContext || !m_glXCreatePbuffer + || !m_glXDestroyPbuffer || !m_glXMakeCurrent || !m_glXDestroyContext + || !m_glXGetCurrentContext) { + printf("GraphicsContext3D: error looking up bootstrapping entry points\n"); + return; + } + int configAttrs[] = { + GLX_DRAWABLE_TYPE, + GLX_PBUFFER_BIT, + GLX_RENDER_TYPE, + GLX_RGBA_BIT, + GLX_DOUBLEBUFFER, + 0, + 0 + }; + int nelements = 0; + GLXFBConfig* config = m_glXChooseFBConfig(m_display, 0, configAttrs, &nelements); + if (!config) { + printf("GraphicsContext3D: glXChooseFBConfig failed\n"); + return; + } + if (!nelements) { + printf("GraphicsContext3D: glXChooseFBConfig returned 0 elements\n"); + XFree(config); + return; + } + GLXContext context = m_glXCreateNewContext(m_display, config[0], GLX_RGBA_TYPE, 0, True); + if (!context) { + printf("GraphicsContext3D: glXCreateNewContext failed\n"); + XFree(config); + return; + } + int pbufferAttrs[] = { + GLX_PBUFFER_WIDTH, + 1, + GLX_PBUFFER_HEIGHT, + 1, + 0 + }; + GLXPbuffer pbuffer = m_glXCreatePbuffer(m_display, config[0], pbufferAttrs); + XFree(config); + if (!pbuffer) { + printf("GraphicsContext3D: glxCreatePbuffer failed\n"); + return; + } + if (!m_glXMakeCurrent(m_display, pbuffer, context)) { + printf("GraphicsContext3D: glXMakeCurrent failed\n"); + return; + } + m_contextObj = context; + m_pbuffer = pbuffer; +#else +#error Must port to your platform +#endif + + static bool initializedGLEW = false; + if (!initializedGLEW) { + // Initialize GLEW and check for GL 2.0 support by the drivers. + GLenum glewInitResult = glewInit(); + if (glewInitResult != GLEW_OK) { + printf("GraphicsContext3D: GLEW initialization failed\n"); + return; + } + if (!glewIsSupported("GL_VERSION_2_0")) { + printf("GraphicsContext3D: OpenGL 2.0 not supported\n"); + return; + } + initializedGLEW = true; + } +} + +GraphicsContext3DInternal::~GraphicsContext3DInternal() +{ + makeContextCurrent(); +#ifndef RENDER_TO_DEBUGGING_WINDOW + glDeleteRenderbuffersEXT(1, &m_depthBuffer); + glDeleteTextures(1, &m_texture); +#ifdef FLIP_FRAMEBUFFER_VERTICALLY + if (m_scanline) + delete[] m_scanline; +#endif + glDeleteFramebuffersEXT(1, &m_fbo); +#endif // !RENDER_TO_DEBUGGING_WINDOW +#if PLATFORM(SKIA) + if (m_resizingBitmap) + delete m_resizingBitmap; +#endif +#if PLATFORM(WIN_OS) + wglMakeCurrent(0, 0); + wglDeleteContext(m_contextObj); + ReleaseDC(m_canvasWindow, m_canvasDC); + DestroyWindow(m_canvasWindow); +#elif PLATFORM(CG) + CGLSetCurrentContext(0); + CGLDestroyContext(m_contextObj); + CGLDestroyPBuffer(m_pbuffer); + if (m_cgContext) + CGContextRelease(m_cgContext); + if (m_renderOutput) + delete[] m_renderOutput; +#elif PLATFORM(LINUX) + m_glXMakeCurrent(m_display, 0, 0); + m_glXDestroyContext(m_display, m_contextObj); + m_glXDestroyPbuffer(m_display, m_pbuffer); + XCloseDisplay(m_display); + dlclose(m_libGL); +#else +#error Must port to your platform +#endif + m_contextObj = 0; +} + +bool GraphicsContext3DInternal::makeContextCurrent() +{ +#if PLATFORM(WIN_OS) + if (wglGetCurrentContext() != m_contextObj) + if (wglMakeCurrent(m_canvasDC, m_contextObj)) + return true; +#elif PLATFORM(CG) + if (CGLGetCurrentContext() != m_contextObj) + if (CGLSetCurrentContext(m_contextObj) == kCGLNoError) + return true; +#elif PLATFORM(LINUX) + if (m_glXGetCurrentContext() != m_contextObj) + if (m_glXMakeCurrent(m_display, m_pbuffer, m_contextObj)) + return true; +#else +#error Must port to your platform +#endif + return false; +} + +PlatformGraphicsContext3D GraphicsContext3DInternal::platformGraphicsContext3D() const +{ + return m_contextObj; +} + +Platform3DObject GraphicsContext3DInternal::platformTexture() const +{ + return m_texture; +} + +static int createTextureObject(GLenum target) +{ + GLuint texture = 0; + glGenTextures(1, &texture); + glBindTexture(target, texture); + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + return texture; +} + +void GraphicsContext3DInternal::reshape(int width, int height) +{ +#ifdef RENDER_TO_DEBUGGING_WINDOW + SetWindowPos(m_canvasWindow, HWND_TOP, 0, 0, width, height, + SWP_NOMOVE); + ShowWindow(m_canvasWindow, SW_SHOW); +#endif + + m_cachedWidth = width; + m_cachedHeight = height; + makeContextCurrent(); + +#ifndef RENDER_TO_DEBUGGING_WINDOW +#ifdef USE_TEXTURE_RECTANGLE_FOR_FRAMEBUFFER + // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on Mac OS X + GLenum target = GL_TEXTURE_RECTANGLE_ARB; +#else + GLenum target = GL_TEXTURE_2D; +#endif + if (!m_texture) { + // Generate the texture object + m_texture = createTextureObject(target); + // Generate the framebuffer object + glGenFramebuffersEXT(1, &m_fbo); + // Generate the depth buffer + glGenRenderbuffersEXT(1, &m_depthBuffer); + } + + // Reallocate the color and depth buffers + glBindTexture(target, m_texture); + glTexImage2D(target, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glBindTexture(target, 0); + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_depthBuffer); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width, height); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, target, m_texture, 0); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_depthBuffer); + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { + printf("GraphicsContext3D: framebuffer was incomplete\n"); + + // FIXME: cleanup. + notImplemented(); + } +#endif // RENDER_TO_DEBUGGING_WINDOW + +#ifdef FLIP_FRAMEBUFFER_VERTICALLY + if (m_scanline) { + delete[] m_scanline; + m_scanline = 0; + } + m_scanline = new unsigned char[width * 4]; +#endif // FLIP_FRAMEBUFFER_VERTICALLY + + glClear(GL_COLOR_BUFFER_BIT); + viewportImpl(0, 0, width, height); + +#if PLATFORM(CG) + // Need to reallocate the client-side backing store. + // FIXME: make this more efficient. + if (m_cgContext) { + CGContextRelease(m_cgContext); + m_cgContext = 0; + } + if (m_renderOutput) { + delete[] m_renderOutput; + m_renderOutput = 0; + } + int rowBytes = width * 4; + m_renderOutput = new unsigned char[height * rowBytes]; + CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + m_cgContext = CGBitmapContextCreate(m_renderOutput, width, height, 8, rowBytes, + colorSpace, kCGImageAlphaPremultipliedLast); + CGColorSpaceRelease(colorSpace); +#endif // PLATFORM(CG) +} + +#ifdef FLIP_FRAMEBUFFER_VERTICALLY +void GraphicsContext3DInternal::flipVertically(unsigned char* framebuffer, + unsigned int width, + unsigned int height) +{ + unsigned char* scanline = m_scanline; + if (!scanline) + return; + unsigned int rowBytes = width * 4; + unsigned int count = height / 2; + for (unsigned int i = 0; i < count; i++) { + unsigned char* rowA = framebuffer + i * rowBytes; + unsigned char* rowB = framebuffer + (height - i - 1) * rowBytes; + // FIXME: this is where the multiplication of the alpha + // channel into the color buffer will need to occur if the + // user specifies the "premultiplyAlpha" flag in the context + // creation attributes. + memcpy(scanline, rowB, rowBytes); + memcpy(rowB, rowA, rowBytes); + memcpy(rowA, scanline, rowBytes); + } +} +#endif + +void GraphicsContext3DInternal::beginPaint(WebGLRenderingContext* context) +{ + makeContextCurrent(); + +#ifdef RENDER_TO_DEBUGGING_WINDOW + SwapBuffers(m_canvasDC); +#else + // Earlier versions of this code used the GPU to flip the + // framebuffer vertically before reading it back for compositing + // via software. This code was quite complicated, used a lot of + // GPU memory, and didn't provide an obvious speedup. Since this + // vertical flip is only a temporary solution anyway until Chrome + // is fully GPU composited, it wasn't worth the complexity. + + HTMLCanvasElement* canvas = context->canvas(); + ImageBuffer* imageBuffer = canvas->buffer(); + unsigned char* pixels = 0; +#if PLATFORM(SKIA) + const SkBitmap* canvasBitmap = imageBuffer->context()->platformContext()->bitmap(); + const SkBitmap* readbackBitmap = 0; + ASSERT(canvasBitmap->config() == SkBitmap::kARGB_8888_Config); + if (canvasBitmap->width() == m_cachedWidth && canvasBitmap->height() == m_cachedHeight) { + // This is the fastest and most common case. We read back + // directly into the canvas's backing store. + readbackBitmap = canvasBitmap; + if (m_resizingBitmap) { + delete m_resizingBitmap; + m_resizingBitmap = 0; + } + } else { + // We need to allocate a temporary bitmap for reading back the + // pixel data. We will then use Skia to rescale this bitmap to + // the size of the canvas's backing store. + if (m_resizingBitmap && (m_resizingBitmap->width() != m_cachedWidth || m_resizingBitmap->height() != m_cachedHeight)) { + delete m_resizingBitmap; + m_resizingBitmap = 0; + } + if (!m_resizingBitmap) { + m_resizingBitmap = new SkBitmap(); + m_resizingBitmap->setConfig(SkBitmap::kARGB_8888_Config, + m_cachedWidth, + m_cachedHeight); + if (!m_resizingBitmap->allocPixels()) { + delete m_resizingBitmap; + m_resizingBitmap = 0; + return; + } + } + readbackBitmap = m_resizingBitmap; + } + + // Read back the frame buffer. + SkAutoLockPixels bitmapLock(*readbackBitmap); + pixels = static_cast<unsigned char*>(readbackBitmap->getPixels()); + glReadPixels(0, 0, m_cachedWidth, m_cachedHeight, GL_BGRA, GL_UNSIGNED_BYTE, pixels); +#elif PLATFORM(CG) + if (m_renderOutput) { + ASSERT(CGBitmapContextGetWidth(m_cgContext) == m_cachedWidth); + ASSERT(CGBitmapContextGetHeight(m_cgContext) == m_cachedHeight); + pixels = m_renderOutput; + glReadPixels(0, 0, m_cachedWidth, m_cachedHeight, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + } +#else +#error Must port to your platform +#endif + +#ifdef FLIP_FRAMEBUFFER_VERTICALLY + if (pixels) + flipVertically(pixels, m_cachedWidth, m_cachedHeight); +#endif + +#if PLATFORM(SKIA) + if (m_resizingBitmap) { + // We need to draw the resizing bitmap into the canvas's backing store. + SkCanvas canvas(*canvasBitmap); + SkRect dst; + dst.set(0, 0, canvasBitmap->width(), canvasBitmap->height()); + canvas.drawBitmapRect(*m_resizingBitmap, 0, dst); + } +#elif PLATFORM(CG) + if (m_renderOutput) { + CGImageRef cgImage = CGBitmapContextCreateImage(m_cgContext); + // CSS styling may cause the canvas's content to be resized on + // the page. Go back to the Canvas to figure out the correct + // width and height to draw. + CGRect rect = CGRectMake(0, 0, + context->canvas()->width(), + context->canvas()->height()); + // We want to completely overwrite the previous frame's + // rendering results. + CGContextSetBlendMode(imageBuffer->context()->platformContext(), + kCGBlendModeCopy); + CGContextDrawImage(imageBuffer->context()->platformContext(), + rect, cgImage); + CGImageRelease(cgImage); + } +#else +#error Must port to your platform +#endif + +#endif // RENDER_TO_DEBUGGING_WINDOW +} + +void GraphicsContext3DInternal::activeTexture(unsigned long texture) +{ + // FIXME: query number of textures available. + if (texture < GL_TEXTURE0 || texture > GL_TEXTURE0+32) + // FIXME: raise exception. + return; + + makeContextCurrent(); + glActiveTexture(texture); +} + +void GraphicsContext3DInternal::bindBuffer(unsigned long target, + WebGLBuffer* buffer) +{ + makeContextCurrent(); + GLuint bufID = EXTRACT(buffer); + if (target == GL_ARRAY_BUFFER) + m_boundArrayBuffer = bufID; + glBindBuffer(target, bufID); +} + +// If we didn't have to hack GL_TEXTURE_WRAP_R for cube maps, +// we could just use: +// GL_SAME_METHOD_2_X2(BindTexture, bindTexture, unsigned long, WebGLTexture*) +void GraphicsContext3DInternal::bindTexture(unsigned long target, + WebGLTexture* texture) +{ + makeContextCurrent(); + unsigned int textureObject = EXTRACT(texture); + + glBindTexture(target, textureObject); + + // FIXME: GL_TEXTURE_WRAP_R isn't exposed in the OpenGL ES 2.0 + // API. On desktop OpenGL implementations it seems necessary to + // set this wrap mode to GL_CLAMP_TO_EDGE to get correct behavior + // of cube maps. + if (texture) { + if (target == GL_TEXTURE_CUBE_MAP) { + if (!texture->isCubeMapRWrapModeInitialized()) { + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + texture->setCubeMapRWrapModeInitialized(true); + } + } else + texture->setCubeMapRWrapModeInitialized(false); + } +} + +void GraphicsContext3DInternal::bufferDataImpl(unsigned long target, int size, const void* data, unsigned long usage) +{ + makeContextCurrent(); + // FIXME: make this verification more efficient. + GLint binding = 0; + GLenum binding_target = GL_ARRAY_BUFFER_BINDING; + if (target == GL_ELEMENT_ARRAY_BUFFER) + binding_target = GL_ELEMENT_ARRAY_BUFFER_BINDING; + glGetIntegerv(binding_target, &binding); + if (binding <= 0) { + // FIXME: raise exception. + // LogMessagef(("bufferData: no buffer bound")); + return; + } + + glBufferData(target, + size, + data, + usage); +} + +void GraphicsContext3DInternal::disableVertexAttribArray(unsigned long index) +{ + makeContextCurrent(); + if (index < NumTrackedPointerStates) + m_vertexAttribPointerState[index].enabled = false; + glDisableVertexAttribArray(index); +} + +void GraphicsContext3DInternal::enableVertexAttribArray(unsigned long index) +{ + makeContextCurrent(); + if (index < NumTrackedPointerStates) + m_vertexAttribPointerState[index].enabled = true; + glEnableVertexAttribArray(index); +} + +unsigned long GraphicsContext3DInternal::getError() +{ + if (m_syntheticErrors.size() > 0) { + ListHashSet<unsigned long>::iterator iter = m_syntheticErrors.begin(); + unsigned long err = *iter; + m_syntheticErrors.remove(iter); + return err; + } + + makeContextCurrent(); + return glGetError(); +} + +void GraphicsContext3DInternal::vertexAttribPointer(unsigned long indx, int size, int type, bool normalized, + unsigned long stride, unsigned long offset) +{ + makeContextCurrent(); + + if (m_boundArrayBuffer <= 0) { + // FIXME: raise exception. + // LogMessagef(("bufferData: no buffer bound")); + return; + } + + if (indx < NumTrackedPointerStates) { + VertexAttribPointerState& state = m_vertexAttribPointerState[indx]; + state.buffer = m_boundArrayBuffer; + state.indx = indx; + state.size = size; + state.type = type; + state.normalized = normalized; + state.stride = stride; + state.offset = offset; + } + + glVertexAttribPointer(indx, size, type, normalized, stride, + reinterpret_cast<void*>(static_cast<intptr_t>(offset))); +} + +void GraphicsContext3DInternal::viewportImpl(long x, long y, unsigned long width, unsigned long height) +{ + glViewport(x, y, width, height); +} + +void GraphicsContext3DInternal::synthesizeGLError(unsigned long error) +{ + m_syntheticErrors.add(error); +} + +// GraphicsContext3D ----------------------------------------------------- + +/* Helper macros for when we're just wrapping a gl method, so that + * we can avoid having to type this 500 times. Note that these MUST + * NOT BE USED if we need to check any of the parameters. + */ + +#define GL_SAME_METHOD_0(glname, name) \ +void GraphicsContext3D::name() \ +{ \ + makeContextCurrent(); \ + gl##glname(); \ +} + +#define GL_SAME_METHOD_1(glname, name, t1) \ +void GraphicsContext3D::name(t1 a1) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1); \ +} + +#define GL_SAME_METHOD_1_X(glname, name, t1) \ +void GraphicsContext3D::name(t1 a1) \ +{ \ + makeContextCurrent(); \ + gl##glname(EXTRACT(a1)); \ +} + +#define GL_SAME_METHOD_2(glname, name, t1, t2) \ +void GraphicsContext3D::name(t1 a1, t2 a2) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1, a2); \ +} + +#define GL_SAME_METHOD_2_X12(glname, name, t1, t2) \ +void GraphicsContext3D::name(t1 a1, t2 a2) \ +{ \ + makeContextCurrent(); \ + gl##glname(EXTRACT(a1), EXTRACT(a2)); \ +} + +#define GL_SAME_METHOD_2_X2(glname, name, t1, t2) \ +void GraphicsContext3D::name(t1 a1, t2 a2) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1, EXTRACT(a2)); \ +} + +#define GL_SAME_METHOD_3(glname, name, t1, t2, t3) \ +void GraphicsContext3D::name(t1 a1, t2 a2, t3 a3) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1, a2, a3); \ +} + +#define GL_SAME_METHOD_3_X12(glname, name, t1, t2, t3) \ +void GraphicsContext3D::name(t1 a1, t2 a2, t3 a3) \ +{ \ + makeContextCurrent(); \ + gl##glname(EXTRACT(a1), EXTRACT(a2), a3); \ +} + +#define GL_SAME_METHOD_3_X2(glname, name, t1, t2, t3) \ +void GraphicsContext3D::name(t1 a1, t2 a2, t3 a3) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1, EXTRACT(a2), a3); \ +} + +#define GL_SAME_METHOD_4(glname, name, t1, t2, t3, t4) \ +void GraphicsContext3D::name(t1 a1, t2 a2, t3 a3, t4 a4) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1, a2, a3, a4); \ +} + +#define GL_SAME_METHOD_4_X4(glname, name, t1, t2, t3, t4) \ +void GraphicsContext3D::name(t1 a1, t2 a2, t3 a3, t4 a4) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1, a2, a3, EXTRACT(a4)); \ +} + +#define GL_SAME_METHOD_5(glname, name, t1, t2, t3, t4, t5) \ +void GraphicsContext3D::name(t1 a1, t2 a2, t3 a3, t4 a4, t5 a5) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1, a2, a3, a4, a5); \ +} + +#define GL_SAME_METHOD_5_X4(glname, name, t1, t2, t3, t4, t5) \ +void GraphicsContext3D::name(t1 a1, t2 a2, t3 a3, t4 a4, t5 a5) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1, a2, a3, EXTRACT(a4), a5); \ +} + +#define GL_SAME_METHOD_6(glname, name, t1, t2, t3, t4, t5, t6) \ +void GraphicsContext3D::name(t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1, a2, a3, a4, a5, a6); \ +} + +#define GL_SAME_METHOD_8(glname, name, t1, t2, t3, t4, t5, t6, t7, t8) \ +void GraphicsContext3D::name(t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7, t8 a8) \ +{ \ + makeContextCurrent(); \ + gl##glname(a1, a2, a3, a4, a5, a6, a7, a8); \ +} + +PassOwnPtr<GraphicsContext3D> GraphicsContext3D::create() +{ + PassOwnPtr<GraphicsContext3D> context = new GraphicsContext3D(); + // FIXME: add error checking + return context; +} + +GraphicsContext3D::GraphicsContext3D() + : m_currentWidth(0) + , m_currentHeight(0) + , m_internal(new GraphicsContext3DInternal()) +{ +} + +GraphicsContext3D::~GraphicsContext3D() +{ +} + +PlatformGraphicsContext3D GraphicsContext3D::platformGraphicsContext3D() const +{ + return m_internal->platformGraphicsContext3D(); +} + +Platform3DObject GraphicsContext3D::platformTexture() const +{ + return m_internal->platformTexture(); +} + +void GraphicsContext3D::makeContextCurrent() +{ + m_internal->makeContextCurrent(); +} + +void GraphicsContext3D::reshape(int width, int height) +{ + if (width == m_currentWidth && height == m_currentHeight) + return; + + m_currentWidth = width; + m_currentHeight = height; + + m_internal->reshape(width, height); +} + +void GraphicsContext3D::beginPaint(WebGLRenderingContext* context) +{ + m_internal->beginPaint(context); +} + +void GraphicsContext3D::endPaint() +{ +} + +int GraphicsContext3D::sizeInBytes(int type) +{ + switch (type) { + case GL_BYTE: + return sizeof(GLbyte); + case GL_UNSIGNED_BYTE: + return sizeof(GLubyte); + case GL_SHORT: + return sizeof(GLshort); + case GL_UNSIGNED_SHORT: + return sizeof(GLushort); + case GL_INT: + return sizeof(GLint); + case GL_UNSIGNED_INT: + return sizeof(GLuint); + case GL_FLOAT: + return sizeof(GLfloat); + default: // FIXME: default cases are discouraged in WebKit. + return 0; + } +} + +unsigned GraphicsContext3D::createBuffer() +{ + makeContextCurrent(); + GLuint o; + glGenBuffers(1, &o); + return o; +} + +unsigned GraphicsContext3D::createFramebuffer() +{ + makeContextCurrent(); + GLuint o = 0; + glGenFramebuffersEXT(1, &o); + return o; +} + +unsigned GraphicsContext3D::createProgram() +{ + makeContextCurrent(); + return glCreateProgram(); +} + +unsigned GraphicsContext3D::createRenderbuffer() +{ + makeContextCurrent(); + GLuint o; + glGenRenderbuffersEXT(1, &o); + return o; +} + +unsigned GraphicsContext3D::createShader(unsigned long type) +{ + makeContextCurrent(); + return glCreateShader((type == FRAGMENT_SHADER) ? GL_FRAGMENT_SHADER : GL_VERTEX_SHADER); +} + +unsigned GraphicsContext3D::createTexture() +{ + makeContextCurrent(); + GLuint o; + glGenTextures(1, &o); + return o; +} + +void GraphicsContext3D::deleteBuffer(unsigned buffer) +{ + makeContextCurrent(); + glDeleteBuffers(1, &buffer); +} + +void GraphicsContext3D::deleteFramebuffer(unsigned framebuffer) +{ + makeContextCurrent(); + glDeleteFramebuffersEXT(1, &framebuffer); +} + +void GraphicsContext3D::deleteProgram(unsigned program) +{ + makeContextCurrent(); + glDeleteProgram(program); +} + +void GraphicsContext3D::deleteRenderbuffer(unsigned renderbuffer) +{ + makeContextCurrent(); + glDeleteRenderbuffersEXT(1, &renderbuffer); +} + +void GraphicsContext3D::deleteShader(unsigned shader) +{ + makeContextCurrent(); + glDeleteShader(shader); +} + +void GraphicsContext3D::deleteTexture(unsigned texture) +{ + makeContextCurrent(); + glDeleteTextures(1, &texture); +} + +void GraphicsContext3D::activeTexture(unsigned long texture) +{ + m_internal->activeTexture(texture); +} + +GL_SAME_METHOD_2_X12(AttachShader, attachShader, WebGLProgram*, WebGLShader*) + +void GraphicsContext3D::bindAttribLocation(WebGLProgram* program, + unsigned long index, + const String& name) +{ + if (!program) + return; + makeContextCurrent(); + glBindAttribLocation(EXTRACT(program), index, name.utf8().data()); +} + +void GraphicsContext3D::bindBuffer(unsigned long target, + WebGLBuffer* buffer) +{ + m_internal->bindBuffer(target, buffer); +} + +GL_SAME_METHOD_2_X2(BindFramebufferEXT, bindFramebuffer, unsigned long, WebGLFramebuffer*) + +GL_SAME_METHOD_2_X2(BindRenderbufferEXT, bindRenderbuffer, unsigned long, WebGLRenderbuffer*) + +// If we didn't have to hack GL_TEXTURE_WRAP_R for cube maps, +// we could just use: +// GL_SAME_METHOD_2_X2(BindTexture, bindTexture, unsigned long, WebGLTexture*) +void GraphicsContext3D::bindTexture(unsigned long target, + WebGLTexture* texture) +{ + m_internal->bindTexture(target, texture); +} + +GL_SAME_METHOD_4(BlendColor, blendColor, double, double, double, double) + +GL_SAME_METHOD_1(BlendEquation, blendEquation, unsigned long) + +GL_SAME_METHOD_2(BlendEquationSeparate, blendEquationSeparate, unsigned long, unsigned long) + +GL_SAME_METHOD_2(BlendFunc, blendFunc, unsigned long, unsigned long) + +GL_SAME_METHOD_4(BlendFuncSeparate, blendFuncSeparate, unsigned long, unsigned long, unsigned long, unsigned long) + +void GraphicsContext3D::bufferData(unsigned long target, int size, unsigned long usage) +{ + m_internal->bufferDataImpl(target, size, 0, usage); +} + +void GraphicsContext3D::bufferData(unsigned long target, WebGLArray* array, unsigned long usage) +{ + m_internal->bufferDataImpl(target, array->byteLength(), array->baseAddress(), usage); +} + +void GraphicsContext3D::bufferSubData(unsigned long target, long offset, WebGLArray* array) +{ + if (!array || !array->length()) + return; + + makeContextCurrent(); + // FIXME: make this verification more efficient. + GLint binding = 0; + GLenum binding_target = GL_ARRAY_BUFFER_BINDING; + if (target == GL_ELEMENT_ARRAY_BUFFER) + binding_target = GL_ELEMENT_ARRAY_BUFFER_BINDING; + glGetIntegerv(binding_target, &binding); + if (binding <= 0) { + // FIXME: raise exception. + // LogMessagef(("bufferSubData: no buffer bound")); + return; + } + glBufferSubData(target, offset, array->byteLength(), array->baseAddress()); +} + +unsigned long GraphicsContext3D::checkFramebufferStatus(unsigned long target) +{ + makeContextCurrent(); + return glCheckFramebufferStatusEXT(target); +} + +GL_SAME_METHOD_1(Clear, clear, unsigned long) + +GL_SAME_METHOD_4(ClearColor, clearColor, double, double, double, double) + +GL_SAME_METHOD_1(ClearDepth, clearDepth, double) + +GL_SAME_METHOD_1(ClearStencil, clearStencil, long) + +GL_SAME_METHOD_4(ColorMask, colorMask, bool, bool, bool, bool) + +GL_SAME_METHOD_1_X(CompileShader, compileShader, WebGLShader*) + +GL_SAME_METHOD_8(CopyTexImage2D, copyTexImage2D, unsigned long, long, unsigned long, long, long, unsigned long, unsigned long, long) + +GL_SAME_METHOD_8(CopyTexSubImage2D, copyTexSubImage2D, unsigned long, long, long, long, long, long, unsigned long, unsigned long) + +GL_SAME_METHOD_1(CullFace, cullFace, unsigned long) + +GL_SAME_METHOD_1(DepthFunc, depthFunc, unsigned long) + +GL_SAME_METHOD_1(DepthMask, depthMask, bool) + +GL_SAME_METHOD_2(DepthRange, depthRange, double, double) + +void GraphicsContext3D::detachShader(WebGLProgram* program, WebGLShader* shader) +{ + if (!program || !shader) + return; + + makeContextCurrent(); + glDetachShader(EXTRACT(program), EXTRACT(shader)); +} + +GL_SAME_METHOD_1(Disable, disable, unsigned long) + +void GraphicsContext3D::disableVertexAttribArray(unsigned long index) +{ + m_internal->disableVertexAttribArray(index); +} + +void GraphicsContext3D::drawArrays(unsigned long mode, long first, long count) +{ + switch (mode) { + case GL_TRIANGLES: + case GL_TRIANGLE_STRIP: + case GL_TRIANGLE_FAN: + case GL_POINTS: + case GL_LINE_STRIP: + case GL_LINE_LOOP: + case GL_LINES: + break; + default: // FIXME: default cases are discouraged in WebKit. + // FIXME: output log message, raise exception. + // LogMessage(NS_LITERAL_CSTRING("drawArrays: invalid mode")); + // return NS_ERROR_DOM_SYNTAX_ERR; + return; + } + + if (first+count < first || first+count < count) { + // FIXME: output log message, raise exception. + // LogMessage(NS_LITERAL_CSTRING("drawArrays: overflow in first+count")); + // return NS_ERROR_INVALID_ARG; + return; + } + + // FIXME: validate against currently bound buffer. + // if (!ValidateBuffers(first+count)) + // return NS_ERROR_INVALID_ARG; + + makeContextCurrent(); + glDrawArrays(mode, first, count); +} + +void GraphicsContext3D::drawElements(unsigned long mode, unsigned long count, unsigned long type, long offset) +{ + makeContextCurrent(); + // FIXME: make this verification more efficient. + GLint binding = 0; + GLenum binding_target = GL_ELEMENT_ARRAY_BUFFER_BINDING; + glGetIntegerv(binding_target, &binding); + if (binding <= 0) { + // FIXME: raise exception. + // LogMessagef(("bufferData: no buffer bound")); + return; + } + glDrawElements(mode, count, type, + reinterpret_cast<void*>(static_cast<intptr_t>(offset))); +} + +GL_SAME_METHOD_1(Enable, enable, unsigned long) + +void GraphicsContext3D::enableVertexAttribArray(unsigned long index) +{ + m_internal->enableVertexAttribArray(index); +} + +GL_SAME_METHOD_0(Finish, finish) + +GL_SAME_METHOD_0(Flush, flush) + +GL_SAME_METHOD_4_X4(FramebufferRenderbufferEXT, framebufferRenderbuffer, unsigned long, unsigned long, unsigned long, WebGLRenderbuffer*) + +GL_SAME_METHOD_5_X4(FramebufferTexture2DEXT, framebufferTexture2D, unsigned long, unsigned long, unsigned long, WebGLTexture*, long) + +GL_SAME_METHOD_1(FrontFace, frontFace, unsigned long) + +void GraphicsContext3D::generateMipmap(unsigned long target) +{ + makeContextCurrent(); + if (glGenerateMipmapEXT) + glGenerateMipmapEXT(target); + // FIXME: provide alternative code path? This will be unpleasant + // to implement if glGenerateMipmapEXT is not available -- it will + // require a texture readback and re-upload. +} + +bool GraphicsContext3D::getActiveAttrib(WebGLProgram* program, unsigned long index, ActiveInfo& info) +{ + if (!program) { + synthesizeGLError(INVALID_VALUE); + return false; + } + GLint maxNameLength = -1; + glGetProgramiv(EXTRACT(program), GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxNameLength); + if (maxNameLength < 0) + return false; + GLchar* name = 0; + if (!tryFastMalloc(maxNameLength * sizeof(GLchar)).getValue(name)) { + synthesizeGLError(OUT_OF_MEMORY); + return false; + } + GLsizei length = 0; + GLint size = -1; + GLenum type = 0; + glGetActiveAttrib(EXTRACT(program), index, maxNameLength, + &length, &size, &type, name); + if (size < 0) { + fastFree(name); + return false; + } + info.name = String(name, length); + info.type = type; + info.size = size; + fastFree(name); + return true; +} + +bool GraphicsContext3D::getActiveUniform(WebGLProgram* program, unsigned long index, ActiveInfo& info) +{ + if (!program) { + synthesizeGLError(INVALID_VALUE); + return false; + } + GLint maxNameLength = -1; + glGetProgramiv(EXTRACT(program), GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxNameLength); + if (maxNameLength < 0) + return false; + GLchar* name = 0; + if (!tryFastMalloc(maxNameLength * sizeof(GLchar)).getValue(name)) { + synthesizeGLError(OUT_OF_MEMORY); + return false; + } + GLsizei length = 0; + GLint size = -1; + GLenum type = 0; + glGetActiveUniform(EXTRACT(program), index, maxNameLength, + &length, &size, &type, name); + if (size < 0) { + fastFree(name); + return false; + } + info.name = String(name, length); + info.type = type; + info.size = size; + fastFree(name); + return true; +} + +int GraphicsContext3D::getAttribLocation(WebGLProgram* program, const String& name) +{ + if (!program) + return -1; + + makeContextCurrent(); + return glGetAttribLocation(EXTRACT(program), name.utf8().data()); +} + +void GraphicsContext3D::getBooleanv(unsigned long pname, unsigned char* value) +{ + makeContextCurrent(); + glGetBooleanv(pname, value); +} + +void GraphicsContext3D::getBufferParameteriv(unsigned long target, unsigned long pname, int* value) +{ + makeContextCurrent(); + glGetBufferParameteriv(target, pname, value); +} + +unsigned long GraphicsContext3D::getError() +{ + return m_internal->getError(); +} + +void GraphicsContext3D::getFloatv(unsigned long pname, float* value) +{ + makeContextCurrent(); + glGetFloatv(pname, value); +} + +void GraphicsContext3D::getFramebufferAttachmentParameteriv(unsigned long target, + unsigned long attachment, + unsigned long pname, + int* value) +{ + makeContextCurrent(); + glGetFramebufferAttachmentParameterivEXT(target, attachment, pname, value); +} + +void GraphicsContext3D::getIntegerv(unsigned long pname, int* value) +{ + makeContextCurrent(); + glGetIntegerv(pname, value); +} + +void GraphicsContext3D::getProgramiv(WebGLProgram* program, + unsigned long pname, + int* value) +{ + makeContextCurrent(); + glGetProgramiv(EXTRACT(program), pname, value); +} + +String GraphicsContext3D::getProgramInfoLog(WebGLProgram* program) +{ + makeContextCurrent(); + GLuint programID = EXTRACT(program); + GLint logLength; + glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &logLength); + if (!logLength) + return String(); + GLchar* log = 0; + if (!tryFastMalloc(logLength * sizeof(GLchar)).getValue(log)) + return String(); + GLsizei returnedLogLength; + glGetProgramInfoLog(programID, logLength, &returnedLogLength, log); + ASSERT(logLength == returnedLogLength + 1); + String res = String(log, returnedLogLength); + fastFree(log); + return res; +} + +void GraphicsContext3D::getRenderbufferParameteriv(unsigned long target, + unsigned long pname, + int* value) +{ + makeContextCurrent(); + glGetRenderbufferParameterivEXT(target, pname, value); +} + +void GraphicsContext3D::getShaderiv(WebGLShader* shader, + unsigned long pname, + int* value) +{ + makeContextCurrent(); + glGetShaderiv(EXTRACT(shader), pname, value); +} + +String GraphicsContext3D::getShaderInfoLog(WebGLShader* shader) +{ + makeContextCurrent(); + GLuint shaderID = EXTRACT(shader); + GLint logLength; + glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &logLength); + if (!logLength) + return String(); + GLchar* log = 0; + if (!tryFastMalloc(logLength * sizeof(GLchar)).getValue(log)) + return String(); + GLsizei returnedLogLength; + glGetShaderInfoLog(shaderID, logLength, &returnedLogLength, log); + ASSERT(logLength == returnedLogLength + 1); + String res = String(log, returnedLogLength); + fastFree(log); + return res; +} + +String GraphicsContext3D::getShaderSource(WebGLShader* shader) +{ + makeContextCurrent(); + GLuint shaderID = EXTRACT(shader); + GLint logLength; + glGetShaderiv(shaderID, GL_SHADER_SOURCE_LENGTH, &logLength); + if (!logLength) + return String(); + GLchar* log = 0; + if (!tryFastMalloc(logLength * sizeof(GLchar)).getValue(log)) + return String(); + GLsizei returnedLogLength; + glGetShaderSource(shaderID, logLength, &returnedLogLength, log); + ASSERT(logLength == returnedLogLength + 1); + String res = String(log, returnedLogLength); + fastFree(log); + return res; +} + +String GraphicsContext3D::getString(unsigned long name) +{ + makeContextCurrent(); + return String(reinterpret_cast<const char*>(glGetString(name))); +} + +void GraphicsContext3D::getTexParameterfv(unsigned long target, unsigned long pname, float* value) +{ + makeContextCurrent(); + glGetTexParameterfv(target, pname, value); +} + +void GraphicsContext3D::getTexParameteriv(unsigned long target, unsigned long pname, int* value) +{ + makeContextCurrent(); + glGetTexParameteriv(target, pname, value); +} + +void GraphicsContext3D::getUniformfv(WebGLProgram* program, long location, float* value) +{ + makeContextCurrent(); + glGetUniformfv(EXTRACT(program), location, value); +} + +void GraphicsContext3D::getUniformiv(WebGLProgram* program, long location, int* value) +{ + makeContextCurrent(); + glGetUniformiv(EXTRACT(program), location, value); +} + +long GraphicsContext3D::getUniformLocation(WebGLProgram* program, const String& name) +{ + if (!program) + return -1; + + makeContextCurrent(); + return glGetUniformLocation(EXTRACT(program), name.utf8().data()); +} + +void GraphicsContext3D::getVertexAttribfv(unsigned long index, + unsigned long pname, + float* value) +{ + makeContextCurrent(); + glGetVertexAttribfv(index, pname, value); +} + +void GraphicsContext3D::getVertexAttribiv(unsigned long index, + unsigned long pname, + int* value) +{ + makeContextCurrent(); + glGetVertexAttribiv(index, pname, value); +} + +long GraphicsContext3D::getVertexAttribOffset(unsigned long index, unsigned long pname) +{ + // FIXME: implement. + notImplemented(); + return 0; +} + +GL_SAME_METHOD_2(Hint, hint, unsigned long, unsigned long); + +bool GraphicsContext3D::isBuffer(WebGLBuffer* buffer) +{ + makeContextCurrent(); + return glIsBuffer(EXTRACT(buffer)); +} + +bool GraphicsContext3D::isEnabled(unsigned long cap) +{ + makeContextCurrent(); + return glIsEnabled(cap); +} + +bool GraphicsContext3D::isFramebuffer(WebGLFramebuffer* framebuffer) +{ + makeContextCurrent(); + return glIsFramebufferEXT(EXTRACT(framebuffer)); +} + +bool GraphicsContext3D::isProgram(WebGLProgram* program) +{ + makeContextCurrent(); + return glIsProgram(EXTRACT(program)); +} + +bool GraphicsContext3D::isRenderbuffer(WebGLRenderbuffer* renderbuffer) +{ + makeContextCurrent(); + return glIsRenderbufferEXT(EXTRACT(renderbuffer)); +} + +bool GraphicsContext3D::isShader(WebGLShader* shader) +{ + makeContextCurrent(); + return glIsShader(EXTRACT(shader)); +} + +bool GraphicsContext3D::isTexture(WebGLTexture* texture) +{ + makeContextCurrent(); + return glIsTexture(EXTRACT(texture)); +} + +GL_SAME_METHOD_1(LineWidth, lineWidth, double) + +GL_SAME_METHOD_1_X(LinkProgram, linkProgram, WebGLProgram*) + +void GraphicsContext3D::pixelStorei(unsigned long pname, long param) +{ + if (pname != GL_PACK_ALIGNMENT && pname != GL_UNPACK_ALIGNMENT) { + // FIXME: Create a fake GL error and throw an exception. + return; + } + + makeContextCurrent(); + glPixelStorei(pname, param); +} + +GL_SAME_METHOD_2(PolygonOffset, polygonOffset, double, double) + +PassRefPtr<WebGLArray> GraphicsContext3D::readPixels(long x, long y, + unsigned long width, unsigned long height, + unsigned long format, unsigned long type) { + // FIXME: support more pixel formats and types. + if (!((format == GL_RGBA) && (type == GL_UNSIGNED_BYTE))) + return 0; + + // FIXME: take into account pack alignment. + RefPtr<WebGLUnsignedByteArray> array = WebGLUnsignedByteArray::create(width * height * 4); + glReadPixels(x, y, width, height, format, type, array->baseAddress()); + return array; +} + +void GraphicsContext3D::releaseShaderCompiler() +{ +} + +GL_SAME_METHOD_4(RenderbufferStorageEXT, renderbufferStorage, unsigned long, unsigned long, unsigned long, unsigned long) + +GL_SAME_METHOD_2(SampleCoverage, sampleCoverage, double, bool) + +GL_SAME_METHOD_4(Scissor, scissor, long, long, unsigned long, unsigned long) + +void GraphicsContext3D::shaderSource(WebGLShader* shader, const String& source) +{ + makeContextCurrent(); + CString str = source.utf8(); + const char* data = str.data(); + GLint length = str.length(); + glShaderSource(EXTRACT(shader), 1, &data, &length); +} + +GL_SAME_METHOD_3(StencilFunc, stencilFunc, unsigned long, long, unsigned long) + +GL_SAME_METHOD_4(StencilFuncSeparate, stencilFuncSeparate, unsigned long, unsigned long, long, unsigned long) + +GL_SAME_METHOD_1(StencilMask, stencilMask, unsigned long) + +GL_SAME_METHOD_2(StencilMaskSeparate, stencilMaskSeparate, unsigned long, unsigned long) + +GL_SAME_METHOD_3(StencilOp, stencilOp, unsigned long, unsigned long, unsigned long) + +GL_SAME_METHOD_4(StencilOpSeparate, stencilOpSeparate, unsigned long, unsigned long, unsigned long, unsigned long) + +void GraphicsContext3D::synthesizeGLError(unsigned long error) +{ + m_internal->synthesizeGLError(error); +} + +int GraphicsContext3D::texImage2D(unsigned target, + unsigned level, + unsigned internalformat, + unsigned width, + unsigned height, + unsigned border, + unsigned format, + unsigned type, + WebGLArray* pixels) +{ + // FIXME: must do validation similar to JOGL's to ensure that + // the incoming array is of the appropriate length. + glTexImage2D(target, + level, + internalformat, + width, + height, + border, + format, + type, + pixels->baseAddress()); + return 0; +} + +int GraphicsContext3D::texImage2D(unsigned target, + unsigned level, + unsigned internalformat, + unsigned width, + unsigned height, + unsigned border, + unsigned format, + unsigned type, + ImageData* pixels) +{ + // FIXME: implement. + notImplemented(); + return -1; +} + +// Remove premultiplied alpha from color channels. +// FIXME: this is lossy. Must retrieve original values from HTMLImageElement. +static void unmultiplyAlpha(unsigned char* rgbaData, int numPixels) +{ + for (int j = 0; j < numPixels; j++) { + float b = rgbaData[4*j+0] / 255.0f; + float g = rgbaData[4*j+1] / 255.0f; + float r = rgbaData[4*j+2] / 255.0f; + float a = rgbaData[4*j+3] / 255.0f; + if (a > 0.0f) { + b /= a; + g /= a; + r /= a; + b = (b > 1.0f) ? 1.0f : b; + g = (g > 1.0f) ? 1.0f : g; + r = (r > 1.0f) ? 1.0f : r; + rgbaData[4*j+0] = (unsigned char) (b * 255.0f); + rgbaData[4*j+1] = (unsigned char) (g * 255.0f); + rgbaData[4*j+2] = (unsigned char) (r * 255.0f); + } + } +} + +// FIXME: this must be changed to refer to the original image data +// rather than unmultiplying the alpha channel. +static int texImage2DHelper(unsigned target, unsigned level, + int width, int height, + int rowBytes, + bool flipY, + bool premultiplyAlpha, + GLenum format, + bool skipAlpha, + unsigned char* pixels) +{ + ASSERT(format == GL_RGBA || format == GL_BGRA); + GLint internalFormat = GL_RGBA8; + if (skipAlpha) { + internalFormat = GL_RGB8; + // Ignore the alpha channel + premultiplyAlpha = true; + } + if (flipY) { + // Need to flip images vertically. To avoid making a copy of + // the entire image, we perform a ton of glTexSubImage2D + // calls. FIXME: should rethink this strategy for efficiency. + glTexImage2D(target, level, internalFormat, + width, + height, + 0, + format, + GL_UNSIGNED_BYTE, + 0); + unsigned char* row = 0; + bool allocatedRow = false; + if (!premultiplyAlpha) { + row = new unsigned char[rowBytes]; + allocatedRow = true; + } + for (int i = 0; i < height; i++) { + if (premultiplyAlpha) + row = pixels + (rowBytes * i); + else { + memcpy(row, pixels + (rowBytes * i), rowBytes); + unmultiplyAlpha(row, width); + } + glTexSubImage2D(target, level, 0, height - i - 1, + width, 1, + format, + GL_UNSIGNED_BYTE, + row); + } + if (allocatedRow) + delete[] row; + } else { + // The pixels of cube maps' faces are defined with a top-down + // scanline ordering, unlike GL_TEXTURE_2D, so when uploading + // these, the above vertical flip is the wrong thing to do. + if (premultiplyAlpha) + glTexImage2D(target, level, internalFormat, + width, + height, + 0, + format, + GL_UNSIGNED_BYTE, + pixels); + else { + glTexImage2D(target, level, internalFormat, + width, + height, + 0, + format, + GL_UNSIGNED_BYTE, + 0); + unsigned char* row = new unsigned char[rowBytes]; + for (int i = 0; i < height; i++) { + memcpy(row, pixels + (rowBytes * i), rowBytes); + unmultiplyAlpha(row, width); + glTexSubImage2D(target, level, 0, i, + width, 1, + format, + GL_UNSIGNED_BYTE, + row); + } + delete[] row; + } + } + return 0; +} + +int GraphicsContext3D::texImage2D(unsigned target, unsigned level, Image* image, + bool flipY, bool premultiplyAlpha) +{ + ASSERT(image); + + int res = -1; +#if PLATFORM(SKIA) + NativeImageSkia* skiaImage = image->nativeImageForCurrentFrame(); + if (!skiaImage) { + ASSERT_NOT_REACHED(); + return -1; + } + SkBitmap::Config skiaConfig = skiaImage->config(); + // FIXME: must support more image configurations. + if (skiaConfig != SkBitmap::kARGB_8888_Config) { + ASSERT_NOT_REACHED(); + return -1; + } + SkBitmap& skiaImageRef = *skiaImage; + SkAutoLockPixels lock(skiaImageRef); + int width = skiaImage->width(); + int height = skiaImage->height(); + unsigned char* pixels = + reinterpret_cast<unsigned char*>(skiaImage->getPixels()); + int rowBytes = skiaImage->rowBytes(); + res = texImage2DHelper(target, level, + width, height, + rowBytes, + flipY, premultiplyAlpha, + GL_BGRA, + false, + pixels); +#elif PLATFORM(CG) + CGImageRef cgImage = image->nativeImageForCurrentFrame(); + if (!cgImage) { + ASSERT_NOT_REACHED(); + return -1; + } + int width = CGImageGetWidth(cgImage); + int height = CGImageGetHeight(cgImage); + int rowBytes = width * 4; + CGImageAlphaInfo info = CGImageGetAlphaInfo(cgImage); + bool skipAlpha = (info == kCGImageAlphaNone + || info == kCGImageAlphaNoneSkipLast + || info == kCGImageAlphaNoneSkipFirst); + unsigned char* imageData = new unsigned char[height * rowBytes]; + CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + CGContextRef tmpContext = CGBitmapContextCreate(imageData, width, height, 8, rowBytes, + colorSpace, + kCGImageAlphaPremultipliedLast); + CGColorSpaceRelease(colorSpace); + CGContextDrawImage(tmpContext, + CGRectMake(0, 0, static_cast<CGFloat>(width), static_cast<CGFloat>(height)), + cgImage); + CGContextRelease(tmpContext); + res = texImage2DHelper(target, level, width, height, rowBytes, + flipY, premultiplyAlpha, GL_RGBA, skipAlpha, imageData); + delete[] imageData; +#else +#error Must port to your platform +#endif + return res; +} + +int GraphicsContext3D::texImage2D(unsigned target, unsigned level, HTMLVideoElement* video, + bool flipY, bool premultiplyAlpha) +{ + // FIXME: implement. + notImplemented(); + return -1; +} + +GL_SAME_METHOD_3(TexParameterf, texParameterf, unsigned, unsigned, float); + +GL_SAME_METHOD_3(TexParameteri, texParameteri, unsigned, unsigned, int); + +int GraphicsContext3D::texSubImage2D(unsigned target, + unsigned level, + unsigned xoffset, + unsigned yoffset, + unsigned width, + unsigned height, + unsigned format, + unsigned type, + WebGLArray* pixels) +{ + // FIXME: implement. + notImplemented(); + return -1; +} + +int GraphicsContext3D::texSubImage2D(unsigned target, + unsigned level, + unsigned xoffset, + unsigned yoffset, + unsigned width, + unsigned height, + unsigned format, + unsigned type, + ImageData* pixels) +{ + // FIXME: implement. + notImplemented(); + return -1; +} + +int GraphicsContext3D::texSubImage2D(unsigned target, + unsigned level, + unsigned xoffset, + unsigned yoffset, + unsigned width, + unsigned height, + Image* image, + bool flipY, + bool premultiplyAlpha) +{ + // FIXME: implement. + notImplemented(); + return -1; +} + +int GraphicsContext3D::texSubImage2D(unsigned target, + unsigned level, + unsigned xoffset, + unsigned yoffset, + unsigned width, + unsigned height, + HTMLVideoElement* video, + bool flipY, + bool premultiplyAlpha) +{ + // FIXME: implement. + notImplemented(); + return -1; +} + +GL_SAME_METHOD_2(Uniform1f, uniform1f, long, float) + +void GraphicsContext3D::uniform1fv(long location, float* v, int size) +{ + makeContextCurrent(); + glUniform1fv(location, size, v); +} + +GL_SAME_METHOD_2(Uniform1i, uniform1i, long, int) + +void GraphicsContext3D::uniform1iv(long location, int* v, int size) +{ + makeContextCurrent(); + glUniform1iv(location, size, v); +} + +GL_SAME_METHOD_3(Uniform2f, uniform2f, long, float, float) + +void GraphicsContext3D::uniform2fv(long location, float* v, int size) +{ + makeContextCurrent(); + glUniform2fv(location, size, v); +} + +GL_SAME_METHOD_3(Uniform2i, uniform2i, long, int, int) + +void GraphicsContext3D::uniform2iv(long location, int* v, int size) +{ + makeContextCurrent(); + glUniform2iv(location, size, v); +} + +GL_SAME_METHOD_4(Uniform3f, uniform3f, long, float, float, float) + +void GraphicsContext3D::uniform3fv(long location, float* v, int size) +{ + makeContextCurrent(); + glUniform3fv(location, size, v); +} + +GL_SAME_METHOD_4(Uniform3i, uniform3i, long, int, int, int) + +void GraphicsContext3D::uniform3iv(long location, int* v, int size) +{ + makeContextCurrent(); + glUniform3iv(location, size, v); +} + +GL_SAME_METHOD_5(Uniform4f, uniform4f, long, float, float, float, float) + +void GraphicsContext3D::uniform4fv(long location, float* v, int size) +{ + makeContextCurrent(); + glUniform4fv(location, size, v); +} + +GL_SAME_METHOD_5(Uniform4i, uniform4i, long, int, int, int, int) + +void GraphicsContext3D::uniform4iv(long location, int* v, int size) +{ + makeContextCurrent(); + glUniform4iv(location, size, v); +} + +void GraphicsContext3D::uniformMatrix2fv(long location, bool transpose, float* value, int size) +{ + makeContextCurrent(); + glUniformMatrix2fv(location, size, transpose, value); +} + +void GraphicsContext3D::uniformMatrix3fv(long location, bool transpose, float* value, int size) +{ + makeContextCurrent(); + glUniformMatrix3fv(location, size, transpose, value); +} + +void GraphicsContext3D::uniformMatrix4fv(long location, bool transpose, float* value, int size) +{ + makeContextCurrent(); + glUniformMatrix4fv(location, size, transpose, value); +} + +GL_SAME_METHOD_1_X(UseProgram, useProgram, WebGLProgram*) + +GL_SAME_METHOD_1_X(ValidateProgram, validateProgram, WebGLProgram*) + +GL_SAME_METHOD_2(VertexAttrib1f, vertexAttrib1f, unsigned long, float) + +void GraphicsContext3D::vertexAttrib1fv(unsigned long indx, float* values) +{ + makeContextCurrent(); + glVertexAttrib1fv(indx, values); +} + +GL_SAME_METHOD_3(VertexAttrib2f, vertexAttrib2f, unsigned long, float, float) + +void GraphicsContext3D::vertexAttrib2fv(unsigned long indx, float* values) +{ + makeContextCurrent(); + glVertexAttrib2fv(indx, values); +} + +GL_SAME_METHOD_4(VertexAttrib3f, vertexAttrib3f, unsigned long, float, float, float) + +void GraphicsContext3D::vertexAttrib3fv(unsigned long indx, float* values) +{ + makeContextCurrent(); + glVertexAttrib3fv(indx, values); +} + +GL_SAME_METHOD_5(VertexAttrib4f, vertexAttrib4f, unsigned long, float, float, float, float) + +void GraphicsContext3D::vertexAttrib4fv(unsigned long indx, float* values) +{ + makeContextCurrent(); + glVertexAttrib4fv(indx, values); +} + +void GraphicsContext3D::vertexAttribPointer(unsigned long indx, int size, int type, bool normalized, + unsigned long stride, unsigned long offset) +{ + m_internal->vertexAttribPointer(indx, size, type, normalized, stride, offset); +} + +void GraphicsContext3D::viewport(long x, long y, unsigned long width, unsigned long height) +{ + makeContextCurrent(); + m_internal->viewportImpl(x, y, width, height); +} + +} + +#endif // ENABLE(3D_CANVAS) |
