/*
* Copyright (C) 2009 Apple 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:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "JSWebGLRenderingContext.h"
#include "ExceptionCode.h"
#include "HTMLCanvasElement.h"
#include "HTMLImageElement.h"
#include "JSHTMLCanvasElement.h"
#include "JSHTMLImageElement.h"
#include "JSImageData.h"
#include "JSWebGLBuffer.h"
#include "JSFloat32Array.h"
#include "JSWebGLFramebuffer.h"
#include "JSInt32Array.h"
#include "JSWebGLProgram.h"
#include "JSWebGLRenderbuffer.h"
#include "JSWebGLShader.h"
#include "JSWebGLTexture.h"
#include "JSWebGLUniformLocation.h"
#include "JSUint8Array.h"
#include "JSWebKitCSSMatrix.h"
#include "NotImplemented.h"
#include "WebGLBuffer.h"
#include "Float32Array.h"
#include "WebGLFramebuffer.h"
#include "WebGLGetInfo.h"
#include "Int32Array.h"
#include "WebGLProgram.h"
#include "WebGLRenderingContext.h"
#include <runtime/Error.h>
#include <runtime/JSArray.h>
#include <wtf/FastMalloc.h>
#include <wtf/OwnFastMallocPtr.h>
#if ENABLE(VIDEO)
#include "HTMLVideoElement.h"
#include "JSHTMLVideoElement.h"
#endif
using namespace JSC;
namespace WebCore {
static JSValue toJS(ExecState* exec, JSDOMGlobalObject* globalObject, const WebGLGetInfo& info)
{
switch (info.getType()) {
case WebGLGetInfo::kTypeBool:
return jsBoolean(info.getBool());
case WebGLGetInfo::kTypeBoolArray: {
MarkedArgumentBuffer list;
const Vector<bool>& value = info.getBoolArray();
for (size_t ii = 0; ii < value.size(); ++ii)
list.append(jsBoolean(value[ii]));
return constructArray(exec, list);
}
case WebGLGetInfo::kTypeFloat:
return jsNumber(exec, info.getFloat());
case WebGLGetInfo::kTypeLong:
return jsNumber(exec, info.getLong());
case WebGLGetInfo::kTypeNull:
return jsNull();
case WebGLGetInfo::kTypeString:
return jsString(exec, info.getString());
case WebGLGetInfo::kTypeUnsignedLong:
return jsNumber(exec, info.getUnsignedLong());
case WebGLGetInfo::kTypeWebGLBuffer:
return toJS(exec, globalObject, info.getWebGLBuffer());
case WebGLGetInfo::kTypeWebGLFloatArray:
return toJS(exec, globalObject, info.getWebGLFloatArray());
case WebGLGetInfo::kTypeWebGLFramebuffer:
return toJS(exec, globalObject, info.getWebGLFramebuffer());
case WebGLGetInfo::kTypeWebGLIntArray:
return toJS(exec, globalObject, info.getWebGLIntArray());
// FIXME: implement WebGLObjectArray
// case WebGLGetInfo::kTypeWebGLObjectArray:
case WebGLGetInfo::kTypeWebGLProgram:
return toJS(exec, globalObject, info.getWebGLProgram());
case WebGLGetInfo::kTypeWebGLRenderbuffer:
return toJS(exec, globalObject, info.getWebGLRenderbuffer());
case WebGLGetInfo::kTypeWebGLTexture:
return toJS(exec, globalObject, info.getWebGLTexture());
case WebGLGetInfo::kTypeWebGLUnsignedByteArray:
return toJS(exec, globalObject, info.getWebGLUnsignedByteArray());
default:
notImplemented();
return jsUndefined();
}
}
enum ObjectType {
kBuffer, kRenderbuffer, kTexture, kVertexAttrib
};
static JSValue getObjectParameter(JSWebGLRenderingContext* obj, ExecState* exec, ObjectType objectType)
{
if (exec->argumentCount() != 2)
return throwSyntaxError(exec);
ExceptionCode ec = 0;
WebGLRenderingContext* context = static_cast<WebGLRenderingContext*>(obj->impl());
unsigned target = exec->argument(0).toInt32(exec);
if (exec->hadException())
return jsUndefined();
unsigned pname = exec->argument(1).toInt32(exec);
if (exec->hadException())
return jsUndefined();
WebGLGetInfo info;
switch (objectType) {
case kBuffer:
info = context->getBufferParameter(target, pname, ec);
break;
case kRenderbuffer:
info = context->getRenderbufferParameter(target, pname, ec);
break;
case kTexture:
info = context->getTexParameter(target, pname, ec);
break;
case kVertexAttrib:
// target => index
info = context->getVertexAttrib(target, pname, ec);
break;
default:
notImplemented();
break;
}
if (ec) {
setDOMException(exec, ec);
return jsUndefined();
}
return toJS(exec, obj->globalObject(), info);
}
enum WhichProgramCall {
kProgramParameter, kUniform
};
JSValue JSWebGLRenderingContext::getAttachedShaders(ExecState* exec)
{
if (exec->argumentCount() < 1)
return throwSyntaxError(exec);
ExceptionCode ec = 0;
WebGLRenderingContext* context = static_cast<WebGLRenderingContext*>(impl());
WebGLProgram* program = toWebGLProgram(exec->argument(0));
if (exec->hadException())
return jsUndefined();
Vector<WebGLShader*> shaders;
bool succeed = context->getAttachedShaders(program, shaders, ec);
if (ec) {
setDOMException(exec, ec);
return jsUndefined();
}
if (!succeed)
return jsUndefined();
MarkedArgumentBuffer list;
for (size_t ii = 0; ii < shaders.size(); ++ii)
list.append(toJS(exec, globalObject(), shaders[ii]));
return constructArray(exec, list);
}
JSValue JSWebGLRenderingContext::getBufferParameter(ExecState* exec)
{
return getObjectParameter(this, exec, kBuffer);
}
JSValue JSWebGLRenderingContext::getFramebufferAttachmentParameter(ExecState* exec)
{
if (exec->argumentCount() != 3)
return throwSyntaxError(exec);
ExceptionCode ec = 0;
WebGLRenderingContext* context = static_cast<WebGLRenderingContext*>(impl());
unsigned target = exec->argument(0).toInt32(exec);
if (exec->hadException())
return jsUndefined();
unsigned attachment = exec->argument(1).toInt32(exec);
if (exec->hadException())
return jsUndefined();
unsigned pname = exec->argument(2).toInt32(exec);
if (exec->hadException())
return jsUndefined();
WebGLGetInfo info = context->getFramebufferAttachmentParameter(target, attachment, pname, ec);
if (ec) {
setDOMException(exec, ec);
return jsUndefined();
}
return toJS(exec, globalObject(), info);
}
JSValue JSWebGLRenderingContext::getParameter(ExecState* exec)
{
if (exec->argumentCount() != 1)
return throwSyntaxError(exec);
ExceptionCode ec = 0;
WebGLRenderingContext* context = static_cast<WebGLRenderingContext*>(impl());
unsigned pname = exec->argument(0).toInt32(exec);
if (exec->hadException())
return jsUndefined();
WebGLGetInfo info = context->getParameter(pname, ec);
if (ec) {
setDOMException(exec, ec);
return jsUndefined();
}
return toJS(exec, globalObject(), info);
}
JSValue JSWebGLRenderingContext::getProgramParameter(ExecState* exec)
{
if (exec->argumentCount() != 2)
return throwSyntaxError(exec);
ExceptionCode ec = 0;
WebGLRenderingContext* context = static_cast<WebGLRenderingContext*>(impl());
WebGLProgram* program = toWebGLProgram(exec->argument(0));
unsigned pname = exec->argument(1).toInt32(exec);
if (exec->hadException())
return jsUndefined();
WebGLGetInfo info = context->getProgramParameter(program, pname, ec);
if (ec) {
setDOMException(exec, ec);
return jsUndefined();
}
return toJS(exec, globalObject(), info);
}
JSValue JSWebGLRenderingContext::getRenderbufferParameter(ExecState* exec)
{
return getObjectParameter(this, exec, kRenderbuffer);
}
JSValue JSWebGLRenderingContext::getShaderParameter(ExecState* exec)
{
if (exec->argumentCount() != 2)
return throwSyntaxError(exec);
ExceptionCode ec = 0;
WebGLRenderingContext* context = static_cast<WebGLRenderingContext*>(impl());
WebGLShader* shader = toWebGLShader(exec->argument(0));
unsigned pname = exec->argument(1).toInt32(exec);
if (exec->hadException())
return jsUndefined();
WebGLGetInfo info = context->getShaderParameter(shader, pname, ec);
if (ec) {
setDOMException(exec, ec);
return jsUndefined();
}
return toJS(exec, globalObject(), info);
}
JSValue JSWebGLRenderingContext::getTexParameter(ExecState* exec)
{
return getObjectParameter(this, exec, kTexture);
}
JSValue JSWebGLRenderingContext::getUniform(ExecState* exec)
{
if (exec->argumentCount() != 2)
return throwSyntaxError(exec);
ExceptionCode ec = 0;
WebGLRenderingContext* context = static_cast<WebGLRenderingContext*>(impl());
WebGLProgram* program = toWebGLProgram(exec->argument(0));
WebGLUniformLocation* loc = toWebGLUniformLocation(exec->argument(1));
if (exec->hadException())
return jsUndefined();
WebGLGetInfo info = context->getUniform(program, loc, ec);
if (ec) {
setDOMException(exec, ec);
return jsUndefined();
}
return toJS(exec, globalObject(), info);
}
JSValue JSWebGLRenderingContext::getVertexAttrib(ExecState* exec)
{
return getObjectParameter(this, exec, kVertexAttrib);
}
template<typename T, size_t inlineCapacity>
bool toVector(JSC::ExecState* exec, JSC::JSValue value, Vector<T, inlineCapacity>& vector)
{
if (!value.isObject())
return false;
JSC::JSObject* object = asObject(value);
int32_t length = object->get(exec, JSC::Identifier(exec, "length")).toInt32(exec);
vector.resize(length);
for (int32_t i = 0; i < length; ++i) {
JSC::JSValue v = object->get(exec, i);
if (exec->hadException())
return false;
vector[i] = static_cast<T>(v.toNumber(exec));
}
return true;
}
enum DataFunctionToCall {
f_uniform1v, f_uniform2v, f_uniform3v, f_uniform4v,
f_vertexAttrib1v, f_vertexAttrib2v, f_vertexAttrib3v, f_vertexAttrib4v
};
enum DataFunctionMatrixToCall {
f_uniformMatrix2fv, f_uniformMatrix3fv, f_uniformMatrix4fv
};
static bool functionForUniform(DataFunctionToCall f)
{
switch (f) {
case f_uniform1v:
case f_uniform2v:
case f_uniform3v:
case f_uniform4v:
return true;
break;
default: break;
}
return false;
}
static JSC::JSValue dataFunctionf(DataFunctionToCall f, JSC::ExecState* exec, WebGLRenderingContext* context)
{
if (exec->argumentCount() != 2)
return throwSyntaxError(exec);
WebGLUniformLocation* location = 0;
long index = -1;
if (functionForUniform(f))
location = toWebGLUniformLocation(exec->argument(0));
else
index = exec->argument(0).toInt32(exec);
if (exec->hadException())
return jsUndefined();
RefPtr<Float32Array> webGLArray = toFloat32Array(exec->argument(1));
if (exec->hadException())
return jsUndefined();
ExceptionCode ec = 0;
if (webGLArray) {
switch (f) {
case f_uniform1v:
context->uniform1fv(location, webGLArray.get(), ec);
break;
case f_uniform2v:
context->uniform2fv(location, webGLArray.get(), ec);
break;
case f_uniform3v:
context->uniform3fv(location, webGLArray.get(), ec);
break;
case f_uniform4v:
context->uniform4fv(location, webGLArray.get(), ec);
break;
case f_vertexAttrib1v:
context->vertexAttrib1fv(index, webGLArray.get());
break;
case f_vertexAttrib2v:
context->vertexAttrib2fv(index, webGLArray.get());
break;
case f_vertexAttrib3v:
context->vertexAttrib3fv(index, webGLArray.get());
break;
case f_vertexAttrib4v:
context->vertexAttrib4fv(index, webGLArray.get());
break;
}
setDOMException(exec, ec);
return jsUndefined();
}
Vector<float, 64> array;
if (!toVector(exec, exec->argument(1), array))
return throwTypeError(exec);
switch (f) {
case f_uniform1v:
context->uniform1fv(location, array.data(), array.size(), ec);
break;
case f_uniform2v:
context->uniform2fv(location, array.data(), array.size(), ec);
break;
case f_uniform3v:
context->uniform3fv(location, array.data(), array.size(), ec);
break;
case f_uniform4v:
context->uniform4fv(location, array.data(), array.size(), ec);
break;
case f_vertexAttrib1v:
context->vertexAttrib1fv(index, array.data(), array.size());
break;
case f_vertexAttrib2v:
context->vertexAttrib2fv(index, array.data(), array.size());
break;
case f_vertexAttrib3v:
context->vertexAttrib3fv(index, array.data(), array.size());
break;
case f_vertexAttrib4v:
context->vertexAttrib4fv(index, array.data(), array.size());
break;
}
setDOMException(exec, ec);
return jsUndefined();
}
static JSC::JSValue dataFunctioni(DataFunctionToCall f, JSC::ExecState* exec, WebGLRenderingContext* context)
{
if (exec->argumentCount() != 2)
return throwSyntaxError(exec);
WebGLUniformLocation* location = toWebGLUniformLocation(exec->argument(0));
if (exec->hadException())
return jsUndefined();
RefPtr<Int32Array> webGLArray = toInt32Array(exec->argument(1));
if (exec->hadException())
return jsUndefined();
ExceptionCode ec = 0;
if (webGLArray) {
switch (f) {
case f_uniform1v:
context->uniform1iv(location, webGLArray.get(), ec);
break;
case f_uniform2v:
context->uniform2iv(location, webGLArray.get(), ec);
break;
case f_uniform3v:
context->uniform3iv(location, webGLArray.get(), ec);
break;
case f_uniform4v:
context->uniform4iv(location, webGLArray.get(), ec);
break;
default:
break;
}
setDOMException(exec, ec);
return jsUndefined();
}
Vector<int, 64> array;
if (!toVector(exec, exec->argument(1), array))
return throwTypeError(exec);
switch (f) {
case f_uniform1v:
context->uniform1iv(location, array.data(), array.size(), ec);
break;
case f_uniform2v:
context->uniform2iv(location, array.data(), array.size(), ec);
break;
case f_uniform3v:
context->uniform3iv(location, array.data(), array.size(), ec);
break;
case f_uniform4v:
context->uniform4iv(location, array.data(), array.size(), ec);
break;
default:
break;
}
setDOMException(exec, ec);
return jsUndefined();
}
static JSC::JSValue dataFunctionMatrix(DataFunctionMatrixToCall f, JSC::ExecState* exec, WebGLRenderingContext* context)
{
if (exec->argumentCount() != 3)
return throwSyntaxError(exec);
WebGLUniformLocation* location = toWebGLUniformLocation(exec->argument(0));
if (exec->hadException())
return jsUndefined();
bool transpose = exec->argument(1).toBoolean(exec);
if (exec->hadException())
return jsUndefined();
RefPtr<Float32Array> webGLArray = toFloat32Array(exec->argument(2));
if (exec->hadException())
return jsUndefined();
ExceptionCode ec = 0;
if (webGLArray) {
switch (f) {
case f_uniformMatrix2fv:
context->uniformMatrix2fv(location, transpose, webGLArray.get(), ec);
break;
case f_uniformMatrix3fv:
context->uniformMatrix3fv(location, transpose, webGLArray.get(), ec);
break;
case f_uniformMatrix4fv:
context->uniformMatrix4fv(location, transpose, webGLArray.get(), ec);
break;
}
setDOMException(exec, ec);
return jsUndefined();
}
Vector<float, 64> array;
if (!toVector(exec, exec->argument(2), array))
return throwTypeError(exec);
switch (f) {
case f_uniformMatrix2fv:
context->uniformMatrix2fv(location, transpose, array.data(), array.size(), ec);
break;
case f_uniformMatrix3fv:
context->uniformMatrix3fv(location, transpose, array.data(), array.size(), ec);
break;
case f_uniformMatrix4fv:
context->uniformMatrix4fv(location, transpose, array.data(), array.size(), ec);
break;
}
setDOMException(exec, ec);
return jsUndefined();
}
JSC::JSValue JSWebGLRenderingContext::uniform1fv(JSC::ExecState* exec)
{
return dataFunctionf(f_uniform1v, exec, static_cast<WebGLRenderingContext*>(impl()));
}
JSC::JSValue JSWebGLRenderingContext::uniform1iv(JSC::ExecState* exec)
{
return dataFunctioni(f_uniform1v, exec, static_cast<WebGLRenderingContext*>(impl()));
}
JSC::JSValue JSWebGLRenderingContext::uniform2fv(JSC::ExecState* exec)
{
return dataFunctionf(f_uniform2v, exec, static_cast<WebGLRenderingContext*>(impl()));
}
JSC::JSValue JSWebGLRenderingContext::uniform2iv(JSC::ExecState* exec)
{
return dataFunctioni(f_uniform2v, exec, static_cast<WebGLRenderingContext*>(impl()));
}
JSC::JSValue JSWebGLRenderingContext::uniform3fv(JSC::ExecState* exec)
{
return dataFunctionf(f_uniform3v, exec, static_cast<WebGLRenderingContext*>(impl()));
}
JSC::JSValue JSWebGLRenderingContext::uniform3iv(JSC::ExecState* exec)
{
return dataFunctioni(f_uniform3v, exec, static_cast<WebGLRenderingContext*>(impl()));
}
JSC::JSValue JSWebGLRenderingContext::uniform4fv(JSC::ExecState* exec)
{
return dataFunctionf(f_uniform4v, exec, static_cast<WebGLRenderingContext*>(impl()));
}
JSC::JSValue JSWebGLRenderingContext::uniform4iv(JSC::ExecState* exec)
{
return dataFunctioni(f_uniform4v, exec, static_cast<WebGLRenderingContext*>(impl()));
}
JSC::JSValue JSWebGLRenderingContext::uniformMatrix2fv(JSC::ExecState* exec)
{
return dataFunctionMatrix(f_uniformMatrix2fv, exec, static_cast<WebGLRenderingContext*>(impl()));
}
JSC::JSValue JSWebGLRenderingContext::uniformMatrix3fv(JSC::ExecState* exec)
{
return dataFunctionMatrix(f_uniformMatrix3fv, exec, static_cast<WebGLRenderingContext*>(impl()));
}
JSC::JSValue JSWebGLRenderingContext::uniformMatrix4fv(JSC::ExecState* exec)
{
return dataFunctionMatrix(f_uniformMatrix4fv, exec, static_cast<WebGLRenderingContext*>(impl()));
}
JSC::JSValue JSWebGLRenderingContext::vertexAttrib1fv(JSC::ExecState* exec)
{
return dataFunctionf(f_vertexAttrib1v, exec, static_cast<WebGLRenderingContext*>(impl()));
}
JSC::JSValue JSWebGLRenderingContext::vertexAttrib2fv(JSC::ExecState* exec)
{
return dataFunctionf(f_vertexAttrib2v, exec, static_cast<WebGLRenderingContext*>(impl()));
}
JSC::JSValue JSWebGLRenderingContext::vertexAttrib3fv(JSC::ExecState* exec)
{
return dataFunctionf(f_vertexAttrib3v, exec, static_cast<WebGLRenderingContext*>(impl()));
}
JSC::JSValue JSWebGLRenderingContext::vertexAttrib4fv(JSC::ExecState* exec)
{
return dataFunctionf(f_vertexAttrib4v, exec, static_cast<WebGLRenderingContext*>(impl()));
}
} // namespace WebCore
#endif // ENABLE(3D_CANVAS)