diff options
Diffstat (limited to 'WebCore/html/canvas')
-rw-r--r-- | WebCore/html/canvas/CanvasRenderingContext2D.cpp | 78 | ||||
-rw-r--r-- | WebCore/html/canvas/CanvasRenderingContext2D.h | 2 | ||||
-rw-r--r-- | WebCore/html/canvas/CanvasStyle.cpp | 24 | ||||
-rw-r--r-- | WebCore/html/canvas/CanvasStyle.h | 2 | ||||
-rw-r--r-- | WebCore/html/canvas/Float32Array.idl | 3 | ||||
-rw-r--r-- | WebCore/html/canvas/Int16Array.idl | 3 | ||||
-rw-r--r-- | WebCore/html/canvas/Int32Array.idl | 3 | ||||
-rw-r--r-- | WebCore/html/canvas/Int8Array.idl | 3 | ||||
-rw-r--r-- | WebCore/html/canvas/Uint16Array.idl | 3 | ||||
-rw-r--r-- | WebCore/html/canvas/Uint32Array.idl | 3 | ||||
-rw-r--r-- | WebCore/html/canvas/Uint8Array.idl | 3 | ||||
-rw-r--r-- | WebCore/html/canvas/WebGLBuffer.cpp | 62 | ||||
-rw-r--r-- | WebCore/html/canvas/WebGLBuffer.h | 2 | ||||
-rw-r--r-- | WebCore/html/canvas/WebGLRenderingContext.cpp | 184 | ||||
-rw-r--r-- | WebCore/html/canvas/WebGLRenderingContext.h | 17 | ||||
-rw-r--r-- | WebCore/html/canvas/WebGLRenderingContext.idl | 6 | ||||
-rw-r--r-- | WebCore/html/canvas/WebGLTexture.cpp | 212 | ||||
-rw-r--r-- | WebCore/html/canvas/WebGLTexture.h | 57 |
18 files changed, 503 insertions, 164 deletions
diff --git a/WebCore/html/canvas/CanvasRenderingContext2D.cpp b/WebCore/html/canvas/CanvasRenderingContext2D.cpp index b3d212a..acd15d2 100644 --- a/WebCore/html/canvas/CanvasRenderingContext2D.cpp +++ b/WebCore/html/canvas/CanvasRenderingContext2D.cpp @@ -172,6 +172,9 @@ void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style) if (!style) return; + if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentColor(*style)) + return; + if (canvas()->originClean()) { if (CanvasPattern* pattern = style->canvasPattern()) { if (!pattern->originClean()) @@ -184,6 +187,7 @@ void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style) if (!c) return; state().m_strokeStyle->applyStrokeColor(c); + state().m_unparsedStrokeColor = String(); } CanvasStyle* CanvasRenderingContext2D::fillStyle() const @@ -195,6 +199,9 @@ void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> style) { if (!style) return; + + if (state().m_fillStyle && state().m_fillStyle->isEquivalentColor(*style)) + return; if (canvas()->originClean()) { if (CanvasPattern* pattern = style->canvasPattern()) { @@ -208,6 +215,7 @@ void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> style) if (!c) return; state().m_fillStyle->applyFillColor(c); + state().m_unparsedFillColor = String(); } float CanvasRenderingContext2D::lineWidth() const @@ -478,7 +486,10 @@ void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, flo void CanvasRenderingContext2D::setStrokeColor(const String& color) { + if (color == state().m_unparsedStrokeColor) + return; setStrokeStyle(CanvasStyle::create(color)); + state().m_unparsedStrokeColor = color; } void CanvasRenderingContext2D::setStrokeColor(float grayLevel) @@ -508,7 +519,10 @@ void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k void CanvasRenderingContext2D::setFillColor(const String& color) { + if (color == state().m_unparsedFillColor) + return; setFillStyle(CanvasStyle::create(color)); + state().m_unparsedFillColor = color; } void CanvasRenderingContext2D::setFillColor(float grayLevel) @@ -543,7 +557,12 @@ void CanvasRenderingContext2D::beginPath() void CanvasRenderingContext2D::closePath() { - m_path.closeSubpath(); + if (m_path.isEmpty()) + return; + + FloatRect boundRect = m_path.boundingRect(); + if (boundRect.width() || boundRect.height()) + m_path.closeSubpath(); } void CanvasRenderingContext2D::moveTo(float x, float y) @@ -561,9 +580,11 @@ void CanvasRenderingContext2D::lineTo(float x, float y) return; if (!state().m_invertibleCTM) return; + + FloatPoint p1 = FloatPoint(x, y); if (!m_path.hasCurrentPoint()) - m_path.moveTo(FloatPoint(x, y)); - else + m_path.moveTo(p1); + else if (p1 != m_path.currentPoint()) m_path.addLineTo(FloatPoint(x, y)); } @@ -575,7 +596,10 @@ void CanvasRenderingContext2D::quadraticCurveTo(float cpx, float cpy, float x, f return; if (!m_path.hasCurrentPoint()) m_path.moveTo(FloatPoint(cpx, cpy)); - m_path.addQuadCurveTo(FloatPoint(cpx, cpy), FloatPoint(x, y)); + + FloatPoint p1 = FloatPoint(x, y); + if (p1 != m_path.currentPoint()) + m_path.addQuadCurveTo(FloatPoint(cpx, cpy), p1); } void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y) @@ -586,22 +610,35 @@ void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x, return; if (!m_path.hasCurrentPoint()) m_path.moveTo(FloatPoint(cp1x, cp1y)); - m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), FloatPoint(x, y)); + + FloatPoint p1 = FloatPoint(x, y); + if (p1 != m_path.currentPoint()) + m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), p1); } -void CanvasRenderingContext2D::arcTo(float x0, float y0, float x1, float y1, float r, ExceptionCode& ec) +void CanvasRenderingContext2D::arcTo(float x1, float y1, float x2, float y2, float r, ExceptionCode& ec) { ec = 0; - if (!isfinite(x0) | !isfinite(y0) | !isfinite(x1) | !isfinite(y1) | !isfinite(r)) + if (!isfinite(x1) | !isfinite(y1) | !isfinite(x2) | !isfinite(y2) | !isfinite(r)) return; if (r < 0) { ec = INDEX_SIZE_ERR; return; } + if (!state().m_invertibleCTM) return; - m_path.addArcTo(FloatPoint(x0, y0), FloatPoint(x1, y1), r); + + FloatPoint p1 = FloatPoint(x1, y1); + FloatPoint p2 = FloatPoint(x2, y2); + + if (!m_path.hasCurrentPoint()) + m_path.moveTo(p1); + else if (p1 == m_path.currentPoint() || p1 == p2 || !r) + lineTo(x1, y1); + else + m_path.addArcTo(p1, p2, r); } void CanvasRenderingContext2D::arc(float x, float y, float r, float sa, float ea, bool anticlockwise, ExceptionCode& ec) @@ -646,10 +683,27 @@ static bool validateRectForCanvas(float& x, float& y, float& width, float& heigh void CanvasRenderingContext2D::rect(float x, float y, float width, float height) { - if (!validateRectForCanvas(x, y, width, height)) - return; if (!state().m_invertibleCTM) return; + + if (!isfinite(x) || !isfinite(y) || !isfinite(width) || !isfinite(height)) + return; + + if (!width && !height) { + m_path.moveTo(FloatPoint(x, y)); + return; + } + + if (width < 0) { + width = -width; + x -= width; + } + + if (height < 0) { + height = -height; + y -= height; + } + m_path.addRect(FloatRect(x, y, width, height)); } @@ -1126,6 +1180,8 @@ void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* sourceCanvas, const if (!sourceCanvas->originClean()) canvas()->setOriginTainted(); + sourceCanvas->makeRenderingResultsAvailable(); + c->drawImage(buffer->image(), DeviceColorSpace, destRect, sourceRect, state().m_globalComposite); willDraw(destRect); // This call comes after drawImage, since the buffer we draw into may be our own, and we need to make sure it is dirty. // FIXME: Arguably willDraw should become didDraw and occur after drawing calls and not before them to avoid problems like this. @@ -1344,7 +1400,7 @@ void CanvasRenderingContext2D::willDraw(const FloatRect& r, unsigned options) dirtyRect = ctm.mapRect(r); } - if (options & CanvasWillDrawApplyShadow) { + if (options & CanvasWillDrawApplyShadow && alphaChannel(state().m_shadowColor)) { // The shadow gets applied after transformation FloatRect shadowRect(dirtyRect); shadowRect.move(state().m_shadowOffset); diff --git a/WebCore/html/canvas/CanvasRenderingContext2D.h b/WebCore/html/canvas/CanvasRenderingContext2D.h index 43f3b35..2eac88d 100644 --- a/WebCore/html/canvas/CanvasRenderingContext2D.h +++ b/WebCore/html/canvas/CanvasRenderingContext2D.h @@ -214,6 +214,8 @@ namespace WebCore { struct State { State(); + String m_unparsedStrokeColor; + String m_unparsedFillColor; RefPtr<CanvasStyle> m_strokeStyle; RefPtr<CanvasStyle> m_fillStyle; float m_lineWidth; diff --git a/WebCore/html/canvas/CanvasStyle.cpp b/WebCore/html/canvas/CanvasStyle.cpp index 67e8201..1ae5236 100644 --- a/WebCore/html/canvas/CanvasStyle.cpp +++ b/WebCore/html/canvas/CanvasStyle.cpp @@ -33,6 +33,7 @@ #include "CanvasGradient.h" #include "CanvasPattern.h" #include "GraphicsContext.h" +#include <wtf/Assertions.h> #include <wtf/PassRefPtr.h> #if PLATFORM(CG) @@ -120,6 +121,29 @@ PassRefPtr<CanvasStyle> CanvasStyle::create(PassRefPtr<CanvasPattern> pattern) return adoptRef(new CanvasStyle(pattern)); } +bool CanvasStyle::isEquivalentColor(const CanvasStyle& other) const +{ + if (m_type != other.m_type) + return false; + + switch (m_type) { + case CanvasStyle::RGBA: + return m_rgba == other.m_rgba; + case CanvasStyle::CMYKA: + return m_cmyka.c == other.m_cmyka.c + && m_cmyka.m == other.m_cmyka.m + && m_cmyka.y == other.m_cmyka.y + && m_cmyka.k == other.m_cmyka.k + && m_cmyka.a == other.m_cmyka.a; + case CanvasStyle::Gradient: + case CanvasStyle::ImagePattern: + return false; + } + + ASSERT_NOT_REACHED(); + return false; +} + void CanvasStyle::applyStrokeColor(GraphicsContext* context) { if (!context) diff --git a/WebCore/html/canvas/CanvasStyle.h b/WebCore/html/canvas/CanvasStyle.h index 18e55cf..76ba6ef 100644 --- a/WebCore/html/canvas/CanvasStyle.h +++ b/WebCore/html/canvas/CanvasStyle.h @@ -55,6 +55,8 @@ namespace WebCore { void applyFillColor(GraphicsContext*); void applyStrokeColor(GraphicsContext*); + bool isEquivalentColor(const CanvasStyle&) const; + private: CanvasStyle(RGBA32 rgba); CanvasStyle(float grayLevel); diff --git a/WebCore/html/canvas/Float32Array.idl b/WebCore/html/canvas/Float32Array.idl index 651e74f..5a939ca 100644 --- a/WebCore/html/canvas/Float32Array.idl +++ b/WebCore/html/canvas/Float32Array.idl @@ -33,7 +33,8 @@ module html { HasNumericIndexGetter, HasCustomIndexSetter, GenerateNativeConverter, - CustomToJS + CustomToJS, + DontCheckEnums ] Float32Array : ArrayBufferView { const unsigned int BYTES_PER_ELEMENT = 4; diff --git a/WebCore/html/canvas/Int16Array.idl b/WebCore/html/canvas/Int16Array.idl index 095611b..02417f8 100644 --- a/WebCore/html/canvas/Int16Array.idl +++ b/WebCore/html/canvas/Int16Array.idl @@ -32,7 +32,8 @@ module html { HasNumericIndexGetter, HasCustomIndexSetter, GenerateNativeConverter, - CustomToJS + CustomToJS, + DontCheckEnums ] Int16Array : ArrayBufferView { const unsigned int BYTES_PER_ELEMENT = 2; diff --git a/WebCore/html/canvas/Int32Array.idl b/WebCore/html/canvas/Int32Array.idl index 9d1e8ee..6977d00 100644 --- a/WebCore/html/canvas/Int32Array.idl +++ b/WebCore/html/canvas/Int32Array.idl @@ -33,7 +33,8 @@ module html { HasNumericIndexGetter, HasCustomIndexSetter, GenerateNativeConverter, - CustomToJS + CustomToJS, + DontCheckEnums ] Int32Array : ArrayBufferView { const unsigned int BYTES_PER_ELEMENT = 4; diff --git a/WebCore/html/canvas/Int8Array.idl b/WebCore/html/canvas/Int8Array.idl index 43c83f5..4dba9e4 100644 --- a/WebCore/html/canvas/Int8Array.idl +++ b/WebCore/html/canvas/Int8Array.idl @@ -33,7 +33,8 @@ module html { HasNumericIndexGetter, HasCustomIndexSetter, GenerateNativeConverter, - CustomToJS + CustomToJS, + DontCheckEnums ] Int8Array : ArrayBufferView { const unsigned int BYTES_PER_ELEMENT = 1; diff --git a/WebCore/html/canvas/Uint16Array.idl b/WebCore/html/canvas/Uint16Array.idl index 4c369a0..de1e5e0 100644 --- a/WebCore/html/canvas/Uint16Array.idl +++ b/WebCore/html/canvas/Uint16Array.idl @@ -33,7 +33,8 @@ module html { HasNumericIndexGetter, HasCustomIndexSetter, GenerateNativeConverter, - CustomToJS + CustomToJS, + DontCheckEnums ] Uint16Array : ArrayBufferView { const unsigned int BYTES_PER_ELEMENT = 2; diff --git a/WebCore/html/canvas/Uint32Array.idl b/WebCore/html/canvas/Uint32Array.idl index 25f5b71..ce632dd 100644 --- a/WebCore/html/canvas/Uint32Array.idl +++ b/WebCore/html/canvas/Uint32Array.idl @@ -33,7 +33,8 @@ module html { HasNumericIndexGetter, HasCustomIndexSetter, GenerateNativeConverter, - CustomToJS + CustomToJS, + DontCheckEnums ] Uint32Array : ArrayBufferView { const unsigned int BYTES_PER_ELEMENT = 4; diff --git a/WebCore/html/canvas/Uint8Array.idl b/WebCore/html/canvas/Uint8Array.idl index 76b8cdd..c520844 100644 --- a/WebCore/html/canvas/Uint8Array.idl +++ b/WebCore/html/canvas/Uint8Array.idl @@ -33,7 +33,8 @@ module html { HasNumericIndexGetter, HasCustomIndexSetter, GenerateNativeConverter, - CustomToJS + CustomToJS, + DontCheckEnums ] Uint8Array : ArrayBufferView { const unsigned int BYTES_PER_ELEMENT = 1; diff --git a/WebCore/html/canvas/WebGLBuffer.cpp b/WebCore/html/canvas/WebGLBuffer.cpp index e71a12b..e449052 100644 --- a/WebCore/html/canvas/WebGLBuffer.cpp +++ b/WebCore/html/canvas/WebGLBuffer.cpp @@ -74,6 +74,35 @@ bool WebGLBuffer::associateBufferData(int size) return false; } +bool WebGLBuffer::associateBufferData(ArrayBuffer* array) +{ + if (!m_target) + return false; + if (!array) + return false; + + if (m_target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER) { + clearCachedMaxIndices(); + m_byteLength = array->byteLength(); + // We must always clone the incoming data because client-side + // modifications without calling bufferData or bufferSubData + // must never be able to change the validation results. + m_elementArrayBuffer = ArrayBuffer::create(array); + if (!m_elementArrayBuffer) { + m_byteLength = 0; + return false; + } + return true; + } + + if (m_target == GraphicsContext3D::ARRAY_BUFFER) { + m_byteLength = array->byteLength(); + return true; + } + + return false; +} + bool WebGLBuffer::associateBufferData(ArrayBufferView* array) { if (!m_target) @@ -103,6 +132,39 @@ bool WebGLBuffer::associateBufferData(ArrayBufferView* array) return false; } +bool WebGLBuffer::associateBufferSubData(long offset, ArrayBuffer* array) +{ + if (!m_target) + return false; + if (!array) + return false; + + if (m_target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER) { + clearCachedMaxIndices(); + + // We need to protect against integer overflow with these tests + if (offset < 0) + return false; + + unsigned long uoffset = static_cast<unsigned long>(offset); + if (uoffset > m_byteLength || array->byteLength() > m_byteLength - uoffset) + return false; + + if (!m_elementArrayBuffer) + return false; + + memcpy(static_cast<unsigned char*>(m_elementArrayBuffer->data()) + offset, + static_cast<unsigned char*>(array->data()), + array->byteLength()); + return true; + } + + if (m_target == GraphicsContext3D::ARRAY_BUFFER) + return array->byteLength() + offset <= m_byteLength; + + return false; +} + bool WebGLBuffer::associateBufferSubData(long offset, ArrayBufferView* array) { if (!m_target) diff --git a/WebCore/html/canvas/WebGLBuffer.h b/WebCore/html/canvas/WebGLBuffer.h index e1fec47..1280cf9 100644 --- a/WebCore/html/canvas/WebGLBuffer.h +++ b/WebCore/html/canvas/WebGLBuffer.h @@ -41,7 +41,9 @@ namespace WebCore { static PassRefPtr<WebGLBuffer> create(WebGLRenderingContext*); bool associateBufferData(int size); + bool associateBufferData(ArrayBuffer* array); bool associateBufferData(ArrayBufferView* array); + bool associateBufferSubData(long offset, ArrayBuffer* array); bool associateBufferSubData(long offset, ArrayBufferView* array); unsigned byteLength() const; diff --git a/WebCore/html/canvas/WebGLRenderingContext.cpp b/WebCore/html/canvas/WebGLRenderingContext.cpp index a82a4ac4..f567ac8 100644 --- a/WebCore/html/canvas/WebGLRenderingContext.cpp +++ b/WebCore/html/canvas/WebGLRenderingContext.cpp @@ -115,12 +115,12 @@ WebGLRenderingContext::WebGLRenderingContext(HTMLCanvasElement* passedCanvas, Pa m_context->getIntegerv(GraphicsContext3D::IMPLEMENTATION_COLOR_READ_TYPE, &implementationColorReadType); m_implementationColorReadType = implementationColorReadType; - int maxTextureSize = 0; - m_context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &maxTextureSize); - m_maxTextureSize = maxTextureSize; - int maxCubeMapTextureSize = 0; - m_context->getIntegerv(GraphicsContext3D::MAX_CUBE_MAP_TEXTURE_SIZE, &maxCubeMapTextureSize); - m_maxCubeMapTextureSize = maxCubeMapTextureSize; + m_maxTextureSize = 0; + m_context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &m_maxTextureSize); + m_maxTextureLevel = WebGLTexture::computeLevelCount(m_maxTextureSize, m_maxTextureSize); + m_maxCubeMapTextureSize = 0; + m_context->getIntegerv(GraphicsContext3D::MAX_CUBE_MAP_TEXTURE_SIZE, &m_maxCubeMapTextureSize); + m_maxCubeMapTextureLevel = WebGLTexture::computeLevelCount(m_maxCubeMapTextureSize, m_maxCubeMapTextureSize); if (!isGLES2Compliant()) { createFallbackBlackTextures1x1(); @@ -141,17 +141,23 @@ void WebGLRenderingContext::markContextChanged() RenderBox* renderBox = canvas()->renderBox(); if (renderBox && renderBox->hasLayer() && renderBox->layer()->hasAcceleratedCompositing()) renderBox->layer()->rendererContentChanged(); - else { #endif - if (!m_markedCanvasDirty) { - // Make sure the canvas's image buffer is allocated. - canvas()->buffer(); - canvas()->willDraw(FloatRect(0, 0, canvas()->width(), canvas()->height())); - m_markedCanvasDirty = true; - } -#if USE(ACCELERATED_COMPOSITING) + if (!m_markedCanvasDirty) { + // Make sure the canvas's image buffer is allocated. + canvas()->buffer(); + canvas()->willDraw(FloatRect(0, 0, canvas()->width(), canvas()->height())); + m_markedCanvasDirty = true; } -#endif +} + +bool WebGLRenderingContext::paintRenderingResultsToCanvas() +{ + if (m_markedCanvasDirty) { + m_markedCanvasDirty = false; + m_context->paintRenderingResultsToCanvas(this); + return true; + } + return false; } void WebGLRenderingContext::beginPaint() @@ -293,17 +299,20 @@ void WebGLRenderingContext::bindTexture(unsigned long target, WebGLTexture* text m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); return; } - if (target == GraphicsContext3D::TEXTURE_2D) + int maxLevel = 0; + if (target == GraphicsContext3D::TEXTURE_2D) { m_textureUnits[m_activeTextureUnit].m_texture2DBinding = texture; - else if (target == GraphicsContext3D::TEXTURE_CUBE_MAP) + maxLevel = m_maxTextureLevel; + } else if (target == GraphicsContext3D::TEXTURE_CUBE_MAP) { m_textureUnits[m_activeTextureUnit].m_textureCubeMapBinding = texture; - else { + maxLevel = m_maxCubeMapTextureLevel; + } else { m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); return; } m_context->bindTexture(target, texture); if (!isGLES2Compliant() && texture) - texture->setTarget(target); + texture->setTarget(target, maxLevel); cleanupAfterGraphicsCall(false); } @@ -349,22 +358,11 @@ void WebGLRenderingContext::blendFuncSeparate(unsigned long srcRGB, unsigned lon void WebGLRenderingContext::bufferData(unsigned long target, int size, unsigned long usage, ExceptionCode& ec) { UNUSED_PARAM(ec); - if (!isGLES2Compliant()) { - if (!validateBufferDataUsage(usage)) - return; - } - if (target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER && m_boundElementArrayBuffer) { - if (!m_boundElementArrayBuffer->associateBufferData(size)) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); - return; - } - } else if (target == GraphicsContext3D::ARRAY_BUFFER && m_boundArrayBuffer) { - if (!m_boundArrayBuffer->associateBufferData(size)) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); - return; - } - } else { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + WebGLBuffer* buffer = validateBufferDataParameters(target, usage); + if (!buffer) + return; + if (!buffer->associateBufferData(size)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return; } @@ -372,25 +370,29 @@ void WebGLRenderingContext::bufferData(unsigned long target, int size, unsigned cleanupAfterGraphicsCall(false); } -void WebGLRenderingContext::bufferData(unsigned long target, ArrayBufferView* data, unsigned long usage, ExceptionCode& ec) +void WebGLRenderingContext::bufferData(unsigned long target, ArrayBuffer* data, unsigned long usage, ExceptionCode& ec) { UNUSED_PARAM(ec); - if (!isGLES2Compliant()) { - if (!validateBufferDataUsage(usage)) - return; + WebGLBuffer* buffer = validateBufferDataParameters(target, usage); + if (!buffer) + return; + if (!buffer->associateBufferData(data)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; } - if (target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER && m_boundElementArrayBuffer) { - if (!m_boundElementArrayBuffer->associateBufferData(data)) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); - return; - } - } else if (target == GraphicsContext3D::ARRAY_BUFFER && m_boundArrayBuffer) { - if (!m_boundArrayBuffer->associateBufferData(data)) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); - return; - } - } else { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + + m_context->bufferData(target, data, usage); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::bufferData(unsigned long target, ArrayBufferView* data, unsigned long usage, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + WebGLBuffer* buffer = validateBufferDataParameters(target, usage); + if (!buffer) + return; + if (!buffer->associateBufferData(data)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return; } @@ -398,21 +400,29 @@ void WebGLRenderingContext::bufferData(unsigned long target, ArrayBufferView* da cleanupAfterGraphicsCall(false); } +void WebGLRenderingContext::bufferSubData(unsigned long target, long offset, ArrayBuffer* data, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + WebGLBuffer* buffer = validateBufferDataParameters(target, GraphicsContext3D::STATIC_DRAW); + if (!buffer) + return; + if (!buffer->associateBufferSubData(offset, data)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + + m_context->bufferSubData(target, offset, data); + cleanupAfterGraphicsCall(false); +} + void WebGLRenderingContext::bufferSubData(unsigned long target, long offset, ArrayBufferView* data, ExceptionCode& ec) { UNUSED_PARAM(ec); - if (target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER && m_boundElementArrayBuffer) { - if (!m_boundElementArrayBuffer->associateBufferSubData(offset, data)) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); - return; - } - } else if (target == GraphicsContext3D::ARRAY_BUFFER && m_boundArrayBuffer) { - if (!m_boundArrayBuffer->associateBufferSubData(offset, data)) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); - return; - } - } else { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + WebGLBuffer* buffer = validateBufferDataParameters(target, GraphicsContext3D::STATIC_DRAW); + if (!buffer) + return; + if (!buffer->associateBufferSubData(offset, data)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return; } @@ -507,10 +517,8 @@ void WebGLRenderingContext::copyTexImage2D(unsigned long target, long level, uns // FIXME: if the framebuffer is not complete, none of the below should be executed. WebGLTexture* tex = getTextureBinding(target); if (!isGLES2Compliant()) { - if (tex && !level) // only for level 0 - tex->setSize(target, width, height); if (tex) - tex->setInternalFormat(internalformat); + tex->setLevelInfo(target, level, internalformat, width, height, GraphicsContext3D::UNSIGNED_BYTE); } if (m_framebufferBinding && tex) m_framebufferBinding->onAttachedObjectChange(tex); @@ -1082,18 +1090,22 @@ void WebGLRenderingContext::frontFace(unsigned long mode) void WebGLRenderingContext::generateMipmap(unsigned long target) { + RefPtr<WebGLTexture> tex; if (!isGLES2Compliant()) { - RefPtr<WebGLTexture> tex = 0; if (target == GraphicsContext3D::TEXTURE_2D) tex = m_textureUnits[m_activeTextureUnit].m_texture2DBinding; else if (target == GraphicsContext3D::TEXTURE_CUBE_MAP) tex = m_textureUnits[m_activeTextureUnit].m_textureCubeMapBinding; - if (tex && tex->isNPOT()) { + if (tex && !tex->canGenerateMipmaps()) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); return; } } m_context->generateMipmap(target); + if (!isGLES2Compliant()) { + if (tex) + tex->generateMipmapLevelInfo(); + } cleanupAfterGraphicsCall(false); } @@ -1337,7 +1349,8 @@ WebGLGetInfo WebGLRenderingContext::getParameter(unsigned long pname, ExceptionC case GraphicsContext3D::MAX_VIEWPORT_DIMS: return getWebGLIntArrayParameter(pname); case GraphicsContext3D::NUM_COMPRESSED_TEXTURE_FORMATS: - return getLongParameter(pname); + // WebGL 1.0 specifies that there are no compressed texture formats. + return WebGLGetInfo(static_cast<long>(0)); case GraphicsContext3D::NUM_SHADER_BINARY_FORMATS: // FIXME: should we always return 0 for this? return getLongParameter(pname); @@ -2082,10 +2095,8 @@ void WebGLRenderingContext::texImage2DBase(unsigned target, unsigned level, unsi border, format, type, pixels); WebGLTexture* tex = getTextureBinding(target); if (!isGLES2Compliant()) { - if (tex && !level) // only for level 0 - tex->setSize(target, width, height); if (tex) - tex->setInternalFormat(internalformat); + tex->setLevelInfo(target, level, internalformat, width, height, type); } if (m_framebufferBinding && tex) m_framebufferBinding->onAttachedObjectChange(tex); @@ -3369,9 +3380,6 @@ bool WebGLRenderingContext::validateTexFuncParameters(unsigned long target, long if (!validateTexFuncFormatAndType(format, type)) return false; - if (isGLES2Compliant()) - return true; - if (width < 0 || height < 0 || level < 0) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return false; @@ -3379,7 +3387,7 @@ bool WebGLRenderingContext::validateTexFuncParameters(unsigned long target, long switch (target) { case GraphicsContext3D::TEXTURE_2D: - if (width > m_maxTextureSize || height > m_maxTextureSize) { + if (width > m_maxTextureSize || height > m_maxTextureSize || level > m_maxTextureLevel) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return false; } @@ -3390,7 +3398,7 @@ bool WebGLRenderingContext::validateTexFuncParameters(unsigned long target, long case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y: case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z: case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z: - if (width != height || width > m_maxCubeMapTextureSize) { + if (width != height || width > m_maxCubeMapTextureSize || level > m_maxCubeMapTextureLevel) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return false; } @@ -3592,16 +3600,32 @@ bool WebGLRenderingContext::validateUniformMatrixParameters(const WebGLUniformLo return true; } -bool WebGLRenderingContext::validateBufferDataUsage(unsigned long usage) +WebGLBuffer* WebGLRenderingContext::validateBufferDataParameters(unsigned long target, unsigned long usage) { + WebGLBuffer* buffer = 0; + switch (target) { + case GraphicsContext3D::ELEMENT_ARRAY_BUFFER: + buffer = m_boundElementArrayBuffer.get(); + break; + case GraphicsContext3D::ARRAY_BUFFER: + buffer = m_boundArrayBuffer.get(); + break; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return 0; + } + if (!buffer) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return 0; + } switch (usage) { case GraphicsContext3D::STREAM_DRAW: case GraphicsContext3D::STATIC_DRAW: case GraphicsContext3D::DYNAMIC_DRAW: - return true; + return buffer; } m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); - return false; + return 0; } void WebGLRenderingContext::vertexAttribfImpl(unsigned long index, int expectedSize, float v0, float v1, float v2, float v3) diff --git a/WebCore/html/canvas/WebGLRenderingContext.h b/WebCore/html/canvas/WebGLRenderingContext.h index d03d972..fa9ffdb 100644 --- a/WebCore/html/canvas/WebGLRenderingContext.h +++ b/WebCore/html/canvas/WebGLRenderingContext.h @@ -77,7 +77,9 @@ class WebKitCSSMatrix; void blendFuncSeparate(unsigned long srcRGB, unsigned long dstRGB, unsigned long srcAlpha, unsigned long dstAlpha); void bufferData(unsigned long target, int size, unsigned long usage, ExceptionCode&); + void bufferData(unsigned long target, ArrayBuffer* data, unsigned long usage, ExceptionCode&); void bufferData(unsigned long target, ArrayBufferView* data, unsigned long usage, ExceptionCode&); + void bufferSubData(unsigned long target, long offset, ArrayBuffer* data, ExceptionCode&); void bufferSubData(unsigned long target, long offset, ArrayBufferView* data, ExceptionCode&); unsigned long checkFramebufferStatus(unsigned long target); @@ -304,6 +306,10 @@ class WebKitCSSMatrix; void reshape(int width, int height); + // Return value true indicates canvas is updated during the call, + // false indicates no updates. + bool paintRenderingResultsToCanvas(); + // Helpers for notification about paint events. void beginPaint(); void endPaint(); @@ -410,8 +416,10 @@ class WebKitCSSMatrix; RefPtr<WebGLTexture> m_blackTexture2D; RefPtr<WebGLTexture> m_blackTextureCubeMap; - long m_maxTextureSize; - long m_maxCubeMapTextureSize; + int m_maxTextureSize; + int m_maxCubeMapTextureSize; + int m_maxTextureLevel; + int m_maxCubeMapTextureLevel; int m_packAlignment; int m_unpackAlignment; @@ -501,8 +509,9 @@ class WebKitCSSMatrix; bool validateUniformMatrixParameters(const WebGLUniformLocation* location, bool transpose, Float32Array* v, int mod); bool validateUniformMatrixParameters(const WebGLUniformLocation* location, bool transpose, void* v, int size, int mod); - // Helper function to validate usage for bufferData. - bool validateBufferDataUsage(unsigned long); + // Helper function to validate parameters for bufferData. + // Return the current bound buffer to target, or 0 if parameters are invalid. + WebGLBuffer* validateBufferDataParameters(unsigned long target, unsigned long usage); // Helper functions for vertexAttribNf{v}. void vertexAttribfImpl(unsigned long index, int expectedSize, float v0, float v1, float v2, float v3); diff --git a/WebCore/html/canvas/WebGLRenderingContext.idl b/WebCore/html/canvas/WebGLRenderingContext.idl index 1ea4c6d..654c7aa 100644 --- a/WebCore/html/canvas/WebGLRenderingContext.idl +++ b/WebCore/html/canvas/WebGLRenderingContext.idl @@ -28,7 +28,8 @@ module html { interface [ Conditional=3D_CANVAS, InterfaceUUID=98fb48ae-7216-489c-862b-8e1217fc4443, - ImplementationUUID=ab4f0781-152f-450e-9546-5b3987491a54 + ImplementationUUID=ab4f0781-152f-450e-9546-5b3987491a54, + DontCheckEnums ] WebGLRenderingContext : CanvasRenderingContext { /* ClearBufferMask */ @@ -477,9 +478,10 @@ module html { void blendEquationSeparate(in unsigned long modeRGB, in unsigned long modeAlpha); void blendFunc(in unsigned long sfactor, in unsigned long dfactor); void blendFuncSeparate(in unsigned long srcRGB, in unsigned long dstRGB, in unsigned long srcAlpha, in unsigned long dstAlpha); - + void bufferData(in unsigned long target, in ArrayBuffer data, in unsigned long usage) raises (DOMException); void bufferData(in unsigned long target, in ArrayBufferView data, in unsigned long usage) raises (DOMException); void bufferData(in unsigned long target, in long size, in unsigned long usage) raises (DOMException); + void bufferSubData(in unsigned long target, in long offset, in ArrayBuffer data) raises (DOMException); void bufferSubData(in unsigned long target, in long offset, in ArrayBufferView data) raises (DOMException); unsigned long checkFramebufferStatus(in unsigned long target); diff --git a/WebCore/html/canvas/WebGLTexture.cpp b/WebCore/html/canvas/WebGLTexture.cpp index 1cc7d5d..d832038 100644 --- a/WebCore/html/canvas/WebGLTexture.cpp +++ b/WebCore/html/canvas/WebGLTexture.cpp @@ -45,32 +45,39 @@ WebGLTexture::WebGLTexture(WebGLRenderingContext* ctx) , m_magFilter(GraphicsContext3D::LINEAR) , m_wrapS(GraphicsContext3D::REPEAT) , m_wrapT(GraphicsContext3D::REPEAT) - , m_internalFormat(0) , m_isNPOT(false) + , m_isComplete(false) , m_needToUseBlackTexture(false) { setObject(context()->graphicsContext3D()->createTexture()); - for (int ii = 0; ii < 6; ++ii) { - m_width[ii] = 0; - m_height[ii] = 0; - } } -void WebGLTexture::setTarget(unsigned long target) +void WebGLTexture::setTarget(unsigned long target, int maxLevel) { + if (!object()) + return; // Target is finalized the first time bindTexture() is called. if (m_target) return; switch (target) { case GraphicsContext3D::TEXTURE_2D: + m_target = target; + m_info.resize(1); + m_info[0].resize(maxLevel); + break; case GraphicsContext3D::TEXTURE_CUBE_MAP: m_target = target; + m_info.resize(6); + for (int ii = 0; ii < 6; ++ii) + m_info[ii].resize(maxLevel); break; } } void WebGLTexture::setParameteri(unsigned long pname, int param) { + if (!object() || !m_target) + return; switch (pname) { case GraphicsContext3D::TEXTURE_MIN_FILTER: switch (param) { @@ -113,88 +120,195 @@ void WebGLTexture::setParameteri(unsigned long pname, int param) default: return; } - updateNPOTStates(); + update(); } void WebGLTexture::setParameterf(unsigned long pname, float param) { + if (!object() || !m_target) + return; int iparam = static_cast<int>(param); setParameteri(pname, iparam); } -void WebGLTexture::setSize(unsigned long target, unsigned width, unsigned height) +void WebGLTexture::setLevelInfo(unsigned long target, int level, unsigned long internalFormat, int width, int height, unsigned long type) { - if (!width || !height) + if (!object() || !m_target) return; - int iTarget = -1; + // We assume level, internalFormat, width, height, and type have all been + // validated already. + int index = mapTargetToIndex(target); + if (index < 0) + return; + m_info[index][level].setInfo(internalFormat, width, height, type); + update(); +} + +void WebGLTexture::generateMipmapLevelInfo() +{ + if (!object() || !m_target) + return; + if (!canGenerateMipmaps()) + return; + if (m_isComplete) + return; + for (size_t ii = 0; ii < m_info.size(); ++ii) { + const LevelInfo& info0 = m_info[ii][0]; + int width = info0.width; + int height = info0.height; + int levelCount = computeLevelCount(width, height); + for (int level = 1; level < levelCount; ++level) { + width = std::max(1, width >> 1); + height = std::max(1, height >> 1); + LevelInfo& info = m_info[ii][level]; + info.setInfo(info0.internalFormat, width, height, info0.type); + } + } + m_isComplete = true; +} + +unsigned long WebGLTexture::getInternalFormat() const +{ + if (!object() || !m_target) + return 0; + return m_info[0][0].internalFormat; +} + +bool WebGLTexture::isNPOT(unsigned width, unsigned height) +{ + if (!width || !height) + return false; + if ((width & (width - 1)) || (height & (height - 1))) + return true; + return false; +} + +bool WebGLTexture::isNPOT() const +{ + if (!object()) + return false; + return m_isNPOT; +} + +bool WebGLTexture::needToUseBlackTexture() const +{ + if (!object()) + return false; + return m_needToUseBlackTexture; +} + +void WebGLTexture::_deleteObject(Platform3DObject object) +{ + context()->graphicsContext3D()->deleteTexture(object); +} + +int WebGLTexture::mapTargetToIndex(unsigned long target) +{ if (m_target == GraphicsContext3D::TEXTURE_2D) { if (target == GraphicsContext3D::TEXTURE_2D) - iTarget = 0; - } else if (m_target == GraphicsContext3D::TEXTURE_CUBE_MAP && width == height) { + return 0; + } else if (m_target == GraphicsContext3D::TEXTURE_CUBE_MAP) { switch (target) { case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X: - iTarget = 0; - break; + return 0; case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X: - iTarget = 1; - break; + return 1; case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y: - iTarget = 2; - break; + return 2; case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y: - iTarget = 3; - break; + return 3; case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z: - iTarget = 4; - break; + return 4; case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z: - iTarget = 5; - break; + return 5; } } - if (iTarget < 0) - return; - m_width[iTarget] = width; - m_height[iTarget] = height; - updateNPOTStates(); + return -1; } -bool WebGLTexture::isNPOT(unsigned width, unsigned height) +bool WebGLTexture::canGenerateMipmaps() { - if (!width || !height) + if (isNPOT()) return false; - if ((width & (width - 1)) || (height & (height - 1))) - return true; - return false; + const LevelInfo& first = m_info[0][0]; + for (size_t ii = 0; ii < m_info.size(); ++ii) { + const LevelInfo& info = m_info[ii][0]; + if (!info.valid + || info.width != first.width || info.height != first.height + || info.internalFormat != first.internalFormat || info.type != first.type) + return false; + } + return true; } -void WebGLTexture::_deleteObject(Platform3DObject object) +int WebGLTexture::computeLevelCount(int width, int height) { - context()->graphicsContext3D()->deleteTexture(object); + // return 1 + log2Floor(std::max(width, height)); + int n = std::max(width, height); + if (n <= 0) + return 0; + int log = 0; + int value = n; + for (int ii = 4; ii >= 0; --ii) { + int shift = (1 << ii); + int x = (value >> shift); + if (x) { + value = x; + log += shift; + } + } + ASSERT(value == 1); + return log + 1; } -void WebGLTexture::updateNPOTStates() +void WebGLTexture::update() { - int numTargets = 0; - if (m_target == GraphicsContext3D::TEXTURE_2D) - numTargets = 1; - else if (m_target == GraphicsContext3D::TEXTURE_CUBE_MAP) - numTargets = 6; m_isNPOT = false; - unsigned w0 = m_width[0], h0 = m_height[0]; - for (int ii = 0; ii < numTargets; ++ii) { - if (ii && (!m_width[ii] || !m_height[ii] || m_width[ii] != w0 || m_height[ii] != h0)) { - // We only set NPOT for complete cube map textures. - m_isNPOT = false; + for (size_t ii = 0; ii < m_info.size(); ++ii) { + if (isNPOT(m_info[ii][0].width, m_info[ii][0].height)) { + m_isNPOT = true; break; } - if (isNPOT(m_width[ii], m_height[ii])) - m_isNPOT = true; } + m_isComplete = true; + const LevelInfo& first = m_info[0][0]; + int levelCount = computeLevelCount(first.width, first.height); + if (levelCount < 1) + m_isComplete = false; + else { + for (size_t ii = 0; ii < m_info.size() && m_isComplete; ++ii) { + const LevelInfo& info0 = m_info[ii][0]; + if (!info0.valid + || info0.width != first.width || info0.height != first.height + || info0.internalFormat != first.internalFormat || info0.type != first.type) { + m_isComplete = false; + break; + } + int width = info0.width; + int height = info0.height; + for (int level = 1; level < levelCount; ++level) { + width = std::max(1, width >> 1); + height = std::max(1, height >> 1); + const LevelInfo& info = m_info[ii][level]; + if (!info.valid + || info.width != width || info.height != height + || info.internalFormat != info0.internalFormat || info.type != info0.type) { + m_isComplete = false; + break; + } + + } + } + } + m_needToUseBlackTexture = false; + // NPOT if (m_isNPOT && ((m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::LINEAR) || m_wrapS != GraphicsContext3D::CLAMP_TO_EDGE || m_wrapT != GraphicsContext3D::CLAMP_TO_EDGE)) m_needToUseBlackTexture = true; + // Completeness + if (!m_isComplete && m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::LINEAR) + m_needToUseBlackTexture = true; } } diff --git a/WebCore/html/canvas/WebGLTexture.h b/WebCore/html/canvas/WebGLTexture.h index 4d16b59..d4a32f0 100644 --- a/WebCore/html/canvas/WebGLTexture.h +++ b/WebCore/html/canvas/WebGLTexture.h @@ -30,6 +30,7 @@ #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> +#include <wtf/Vector.h> namespace WebCore { @@ -47,19 +48,26 @@ namespace WebCore { cubeMapRWrapModeInitialized = initialized; } - void setTarget(unsigned long); + void setTarget(unsigned long target, int maxLevel); void setParameteri(unsigned long pname, int param); void setParameterf(unsigned long pname, float param); - void setSize(unsigned long target, unsigned width, unsigned height); - void setInternalFormat(unsigned long internalformat) { m_internalFormat = internalformat; } - unsigned long getInternalFormat() const { return m_internalFormat; } + void setLevelInfo(unsigned long target, int level, unsigned long internalFormat, int width, int height, unsigned long type); + bool canGenerateMipmaps(); + // Generate all level information. + void generateMipmapLevelInfo(); + + unsigned long getInternalFormat() const; + + // Whether width/height is NotPowerOfTwo. static bool isNPOT(unsigned, unsigned); - bool isNPOT() const { return m_isNPOT; } + bool isNPOT() const; // Determine if texture sampling should always return [0, 0, 0, 1] (OpenGL ES 2.0 Sec 3.8.2). - bool needToUseBlackTexture() const { return m_needToUseBlackTexture; } + bool needToUseBlackTexture() const; + + static int computeLevelCount(int width, int height); protected: WebGLTexture(WebGLRenderingContext*); @@ -69,7 +77,9 @@ namespace WebCore { private: virtual bool isTexture() const { return true; } - void updateNPOTStates(); + void update(); + + int mapTargetToIndex(unsigned long); bool cubeMapRWrapModeInitialized; @@ -80,12 +90,37 @@ namespace WebCore { int m_wrapS; int m_wrapT; - unsigned long m_internalFormat; - - unsigned m_width[6]; - unsigned m_height[6]; + class LevelInfo { + public: + LevelInfo() + : valid(false) + , internalFormat(0) + , width(0) + , height(0) + , type(0) + { + } + + void setInfo(unsigned long internalFmt, int w, int h, unsigned long tp) + { + valid = true; + internalFormat = internalFmt; + width = w; + height = h; + type = tp; + } + + bool valid; + unsigned long internalFormat; + int width; + int height; + unsigned long type; + }; + + Vector<Vector<LevelInfo> > m_info; bool m_isNPOT; + bool m_isComplete; bool m_needToUseBlackTexture; }; |