diff --git a/dom/canvas/WebGL2ContextBuffers.cpp b/dom/canvas/WebGL2ContextBuffers.cpp index 9eacbc36c512..bcba64d087ff 100644 --- a/dom/canvas/WebGL2ContextBuffers.cpp +++ b/dom/canvas/WebGL2ContextBuffers.cpp @@ -262,15 +262,4 @@ WebGL2Context::GetBufferSubData(GLenum target, GLintptr offset, GetBufferSubDataT(target, offset, data); } -void -WebGL2Context::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, - GLenum type, GLintptr offset) -{ - const char funcName[] = "readPixels"; - if (IsContextLost()) - return; - - ErrorInvalidOperation("%s: Not yet implemented.", funcName); -} - } // namespace mozilla diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h index 6b3db9e31e4f..8283c7ad7a0d 100644 --- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -539,9 +539,12 @@ public: void PixelStorei(GLenum pname, GLint param); void PolygonOffset(GLfloat factor, GLfloat units); protected: - bool DoReadPixelsAndConvert(GLint x, GLint y, GLsizei width, GLsizei height, - GLenum destFormat, GLenum destType, void* destBytes, - GLenum auxReadFormat, GLenum auxReadType); + bool ReadPixels_SharedPrecheck(ErrorResult* const out_error); + void ReadPixelsImpl(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, + GLenum type, void* data, uint32_t dataLen); + bool DoReadPixelsAndConvert(const webgl::FormatInfo* srcFormat, GLint x, GLint y, + GLsizei width, GLsizei height, GLenum format, + GLenum destType, void* dest); public: void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, diff --git a/dom/canvas/WebGLContextGL.cpp b/dom/canvas/WebGLContextGL.cpp index f0e5a67b8cf1..c5016bb34fdf 100644 --- a/dom/canvas/WebGLContextGL.cpp +++ b/dom/canvas/WebGLContextGL.cpp @@ -4,6 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "WebGLContext.h" +#include "WebGL2Context.h" #include "WebGLActiveInfo.h" #include "WebGLContextUtils.h" @@ -1214,28 +1215,37 @@ WebGLContext::PixelStorei(GLenum pname, GLint param) ErrorInvalidEnumInfo("pixelStorei: parameter", pname); } -bool -WebGLContext::DoReadPixelsAndConvert(GLint x, GLint y, GLsizei width, GLsizei height, - GLenum destFormat, GLenum destType, void* destBytes, - GLenum auxReadFormat, GLenum auxReadType) +static bool +IsNeedsANGLEWorkAround(const webgl::FormatInfo* format) { - GLenum readFormat = destFormat; - GLenum readType = destType; + switch (format->effectiveFormat) { + case webgl::EffectiveFormat::RGB16F: + case webgl::EffectiveFormat::RGBA16F: + return true; + default: + return false; + } +} + +bool +WebGLContext::DoReadPixelsAndConvert(const webgl::FormatInfo* srcFormat, GLint x, GLint y, + GLsizei width, GLsizei height, GLenum format, + GLenum destType, void* dest) +{ if (gl->WorkAroundDriverBugs() && gl->IsANGLE() && gl->Version() < 300 && // ANGLE ES2 doesn't support HALF_FLOAT reads properly. - readType == LOCAL_GL_FLOAT && - auxReadFormat == destFormat && - auxReadType == LOCAL_GL_HALF_FLOAT) + IsNeedsANGLEWorkAround(srcFormat)) { MOZ_RELEASE_ASSERT(!IsWebGL2()); // No SKIP_PIXELS, etc. + MOZ_ASSERT(!mBoundPixelPackBuffer); // Let's be real clear. - readType = auxReadType; + const GLenum readType = LOCAL_GL_HALF_FLOAT_OES; const char funcName[] = "readPixels"; - const auto readBytesPerPixel = webgl::BytesPerPixel({readFormat, readType}); - const auto destBytesPerPixel = webgl::BytesPerPixel({destFormat, destType}); + const auto readBytesPerPixel = webgl::BytesPerPixel({format, readType}); + const auto destBytesPerPixel = webgl::BytesPerPixel({format, destType}); uint32_t readStride; uint32_t readByteCount; @@ -1258,7 +1268,7 @@ WebGLContext::DoReadPixelsAndConvert(GLint x, GLint y, GLsizei width, GLsizei he gl::GLContext::LocalErrorScope errorScope(*gl); - gl->fReadPixels(x, y, width, height, readFormat, readType, readBuffer.get()); + gl->fReadPixels(x, y, width, height, format, readType, readBuffer.get()); const GLenum error = errorScope.GetError(); if (error == LOCAL_GL_OUT_OF_MEMORY) { @@ -1275,7 +1285,7 @@ WebGLContext::DoReadPixelsAndConvert(GLint x, GLint y, GLsizei width, GLsizei he destStride / sizeof(float)); const uint8_t* srcRow = (uint8_t*)readBuffer.get(); - uint8_t* dstRow = (uint8_t*)destBytes; + uint8_t* dstRow = (uint8_t*)dest; for (size_t j = 0; j < (size_t)height; j++) { auto src = (const uint16_t*)srcRow; @@ -1295,7 +1305,7 @@ WebGLContext::DoReadPixelsAndConvert(GLint x, GLint y, GLsizei width, GLsizei he return true; } - gl->fReadPixels(x, y, width, height, destFormat, destType, destBytes); + gl->fReadPixels(x, y, width, height, format, destType, dest); return true; } @@ -1393,6 +1403,68 @@ IsIntegerFormatAndTypeUnpackable(GLenum format, GLenum type) } } +static bool +GetJSScalarFromGLType(GLenum type, js::Scalar::Type* const out_scalarType) +{ + switch (type) { + case LOCAL_GL_BYTE: + *out_scalarType = js::Scalar::Int8; + return true; + + case LOCAL_GL_UNSIGNED_BYTE: + *out_scalarType = js::Scalar::Uint8; + return true; + + case LOCAL_GL_SHORT: + *out_scalarType = js::Scalar::Int16; + return true; + + case LOCAL_GL_HALF_FLOAT: + case LOCAL_GL_HALF_FLOAT_OES: + case LOCAL_GL_UNSIGNED_SHORT: + case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: + case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: + case LOCAL_GL_UNSIGNED_SHORT_5_6_5: + *out_scalarType = js::Scalar::Uint16; + return true; + + case LOCAL_GL_UNSIGNED_INT: + case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV: + case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV: + case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV: + case LOCAL_GL_UNSIGNED_INT_24_8: + *out_scalarType = js::Scalar::Uint32; + return true; + case LOCAL_GL_INT: + *out_scalarType = js::Scalar::Int32; + return true; + + case LOCAL_GL_FLOAT: + *out_scalarType = js::Scalar::Float32; + return true; + + default: + return false; + } +} + +bool +WebGLContext::ReadPixels_SharedPrecheck(ErrorResult* const out_error) +{ + if (IsContextLost()) + return false; + + if (mCanvasElement && + mCanvasElement->IsWriteOnly() && + !nsContentUtils::IsCallerChrome()) + { + GenerateWarning("readPixels: Not allowed"); + out_error->Throw(NS_ERROR_DOM_SECURITY_ERR); + return false; + } + + return true; +} bool WebGLContext::ValidatePackSize(const char* funcName, uint32_t width, uint32_t height, @@ -1443,132 +1515,44 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum const dom::Nullable& pixels, ErrorResult& out_error) { - const char funcName[] = "readPixels"; - if (IsContextLost()) + if (!ReadPixels_SharedPrecheck(&out_error)) return; - if (mCanvasElement && - mCanvasElement->IsWriteOnly() && - !nsContentUtils::IsCallerChrome()) - { - GenerateWarning("readPixels: Not allowed"); - out_error.Throw(NS_ERROR_DOM_SECURITY_ERR); + if (mBoundPixelPackBuffer) { + ErrorInvalidOperation("readPixels: PIXEL_PACK_BUFFER must be null."); return; } - if (width < 0 || height < 0) - return ErrorInvalidValue("readPixels: negative size passed"); - - if (pixels.IsNull()) - return ErrorInvalidValue("readPixels: null destination buffer"); - - if (!(IsWebGL2() && IsIntegerFormatAndTypeUnpackable(format, type)) && - !IsFormatAndTypeUnpackable(format, type, IsWebGL2())) { - return ErrorInvalidEnum("readPixels: Bad format or type."); - } - - int channels = 0; - - // Check the format param - switch (format) { - case LOCAL_GL_ALPHA: - case LOCAL_GL_LUMINANCE: - case LOCAL_GL_RED: - case LOCAL_GL_RED_INTEGER: - channels = 1; - break; - case LOCAL_GL_LUMINANCE_ALPHA: - case LOCAL_GL_RG: - case LOCAL_GL_RG_INTEGER: - channels = 2; - break; - case LOCAL_GL_RGB: - case LOCAL_GL_RGB_INTEGER: - channels = 3; - break; - case LOCAL_GL_RGBA: - case LOCAL_GL_RGBA_INTEGER: - channels = 4; - break; - default: - MOZ_CRASH("GFX: bad `format`"); - } - - - // Check the type param - int bytesPerPixel; - int requiredDataType; - switch (type) { - case LOCAL_GL_BYTE: - bytesPerPixel = 1*channels; - requiredDataType = js::Scalar::Int8; - break; - - case LOCAL_GL_UNSIGNED_BYTE: - bytesPerPixel = 1*channels; - requiredDataType = js::Scalar::Uint8; - break; - - case LOCAL_GL_SHORT: - bytesPerPixel = 2*channels; - requiredDataType = js::Scalar::Int16; - break; - - case LOCAL_GL_UNSIGNED_SHORT: - case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: - case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: - case LOCAL_GL_UNSIGNED_SHORT_5_6_5: - bytesPerPixel = 2; - requiredDataType = js::Scalar::Uint16; - break; - - case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV: - case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV: - case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV: - case LOCAL_GL_UNSIGNED_INT_24_8: - bytesPerPixel = 4; - requiredDataType = js::Scalar::Uint32; - break; - - case LOCAL_GL_UNSIGNED_INT: - bytesPerPixel = 4*channels; - requiredDataType = js::Scalar::Uint32; - break; - - case LOCAL_GL_INT: - bytesPerPixel = 4*channels; - requiredDataType = js::Scalar::Int32; - break; - - case LOCAL_GL_FLOAT: - bytesPerPixel = 4*channels; - requiredDataType = js::Scalar::Float32; - break; - - case LOCAL_GL_HALF_FLOAT: - case LOCAL_GL_HALF_FLOAT_OES: - bytesPerPixel = 2*channels; - requiredDataType = js::Scalar::Uint16; - break; - - default: - MOZ_CRASH("GFX: bad `type`"); - } - ////// + if (pixels.IsNull()) { + ErrorInvalidValue("readPixels: null destination buffer"); + return; + } + const auto& view = pixels.Value(); + ////// + + js::Scalar::Type reqScalarType; + if (!GetJSScalarFromGLType(type, &reqScalarType)) { + ErrorInvalidEnum("readPixels: Bad `type`."); + return; + } + + const js::Scalar::Type dataScalarType = JS_GetArrayBufferViewType(view.Obj()); + if (dataScalarType != reqScalarType) { + ErrorInvalidOperation("readPixels: `pixels` type does not match `type`."); + return; + } + + ////// + // Compute length and data. Don't reenter after this point, lest the // precomputed go out of sync with the instant length/data. view.ComputeLengthAndData(); void* data = view.DataAllowShared(); - size_t bytesAvailable = view.LengthAllowShared(); - js::Scalar::Type dataType = JS_GetArrayBufferViewType(view.Obj()); - - // Check the pixels param type - if (dataType != requiredDataType) - return ErrorInvalidOperation("readPixels: Mismatched type/pixels types"); + const auto dataLen = view.LengthAllowShared(); if (!data) { ErrorOutOfMemory("readPixels: buffer storage is null. Did we run out of memory?"); @@ -1576,21 +1560,162 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum return; } + ReadPixelsImpl(x, y, width, height, format, type, data, dataLen); +} + +void +WebGL2Context::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, + GLenum type, WebGLsizeiptr offset, ErrorResult& out_error) +{ + if (!ReadPixels_SharedPrecheck(&out_error)) + return; + + if (!mBoundPixelPackBuffer) { + ErrorInvalidOperation("readPixels: PIXEL_PACK_BUFFER must not be null."); + return; + } + ////// - uint32_t rowStride; - uint32_t bytesNeeded; - if (!ValidatePackSize(funcName, width, height, bytesPerPixel, &rowStride, - &bytesNeeded)) - { + if (offset < 0) { + ErrorInvalidValue("readPixels: offset must not be negative."); return; } - if (bytesNeeded > bytesAvailable) { - ErrorInvalidOperation("readPixels: buffer too small"); + { + const auto bytesPerType = webgl::BytesPerPixel({LOCAL_GL_RED, type}); + + if (offset % bytesPerType != 0) { + ErrorInvalidOperation("readPixels: `offset` must be divisible by the size" + " a `type` in bytes."); + return; + } + } + + ////// + + const auto bytesAvailable = mBoundPixelPackBuffer->ByteLength(); + const auto checkedBytesAfterOffset = CheckedUint32(bytesAvailable) - offset; + + uint32_t bytesAfterOffset = 0; + if (checkedBytesAfterOffset.isValid()) { + bytesAfterOffset = checkedBytesAfterOffset.value(); + } + + ReadPixelsImpl(x, y, width, height, format, type, (void*)offset, bytesAfterOffset); +} + +static bool +ValidateReadPixelsFormatAndType(const webgl::FormatInfo* srcFormat, + const webgl::PackingInfo& pi, gl::GLContext* gl, + WebGLContext* webgl) +{ + // Check the format and type params to assure they are an acceptable pair (as per spec) + GLenum mainFormat; + GLenum mainType; + + switch (srcFormat->componentType) { + case webgl::ComponentType::Float: + mainFormat = LOCAL_GL_RGBA; + mainType = LOCAL_GL_FLOAT; + break; + + case webgl::ComponentType::UInt: + mainFormat = LOCAL_GL_RGBA_INTEGER; + mainType = LOCAL_GL_UNSIGNED_INT; + break; + + case webgl::ComponentType::Int: + mainFormat = LOCAL_GL_RGBA_INTEGER; + mainType = LOCAL_GL_INT; + break; + + case webgl::ComponentType::NormInt: + case webgl::ComponentType::NormUInt: + mainFormat = LOCAL_GL_RGBA; + mainType = LOCAL_GL_UNSIGNED_BYTE; + break; + + default: + gfxCriticalNote << "Unhandled srcFormat->componentType: " + << (uint32_t)srcFormat->componentType; + webgl->ErrorInvalidOperation("readPixels: Unhandled srcFormat->componentType." + " Please file a bug!"); + return false; + } + + if (pi.format == mainFormat && pi.type == mainType) + return true; + + ////// + // OpenGL ES 3.0.4 p194 - When the internal format of the rendering surface is + // RGB10_A2, a third combination of format RGBA and type UNSIGNED_INT_2_10_10_10_REV + // is accepted. + + if (webgl->IsWebGL2() && + srcFormat->effectiveFormat == webgl::EffectiveFormat::RGB10_A2 && + pi.format == LOCAL_GL_RGBA && + pi.type == LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV) + { + return true; + } + + ////// + // OpenGL ES 2.0 $4.3.1 - IMPLEMENTATION_COLOR_READ_{TYPE/FORMAT} is a valid + // combination for glReadPixels()... + + // So yeah, we are actually checking that these are valid as /unpack/ formats, instead + // of /pack/ formats here, but it should cover the INVALID_ENUM cases. + if (!webgl->mFormatUsage->AreUnpackEnumsValid(pi.format, pi.type)) { + webgl->ErrorInvalidEnum("readPixels: Bad format and/or type."); + return false; + } + + // Only valid when pulled from: + // * GLES 2.0.25 p105: + // "table 3.4, excluding formats LUMINANCE and LUMINANCE_ALPHA." + // * GLES 3.0.4 p193: + // "table 3.2, excluding formats DEPTH_COMPONENT and DEPTH_STENCIL." + switch (pi.format) { + case LOCAL_GL_LUMINANCE: + case LOCAL_GL_LUMINANCE_ALPHA: + case LOCAL_GL_DEPTH_COMPONENT: + case LOCAL_GL_DEPTH_STENCIL: + webgl->ErrorInvalidEnum("readPixels: Invalid format: 0x%04x", pi.format); + return false; + } + + MOZ_ASSERT(gl->IsCurrent()); + if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) { + const auto auxFormat = gl->GetIntAs(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT); + const auto auxType = gl->GetIntAs(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE); + + if (auxFormat && auxType && + pi.format == auxFormat && pi.type == auxType) + { + return true; + } + } + + ////// + + webgl->ErrorInvalidOperation("readPixels: Invalid format or type."); + return false; +} + +void +WebGLContext::ReadPixelsImpl(GLint x, GLint y, GLsizei rawWidth, GLsizei rawHeight, + GLenum packFormat, GLenum packType, void* dest, + uint32_t dataLen) +{ + if (rawWidth < 0 || rawHeight < 0) { + ErrorInvalidValue("readPixels: negative size passed"); return; } + const uint32_t width(rawWidth); + const uint32_t height(rawHeight); + ////// MakeContextCurrent(); @@ -1601,58 +1726,34 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum if (!ValidateCurFBForRead("readPixels", &srcFormat, &srcWidth, &srcHeight)) return; - // Check the format and type params to assure they are an acceptable pair (as per spec) - auto srcType = srcFormat->format->componentType; - GLenum mainReadFormat; - GLenum mainReadType; - switch (srcType) { - case webgl::ComponentType::Float: - mainReadFormat = LOCAL_GL_RGBA; - mainReadType = LOCAL_GL_FLOAT; - break; - case webgl::ComponentType::UInt: - mainReadFormat = LOCAL_GL_RGBA_INTEGER; - mainReadType = LOCAL_GL_UNSIGNED_INT; - break; - case webgl::ComponentType::Int: - mainReadFormat = LOCAL_GL_RGBA_INTEGER; - mainReadType = LOCAL_GL_INT; - break; - default: - mainReadFormat = LOCAL_GL_RGBA; - mainReadType = LOCAL_GL_UNSIGNED_BYTE; - break; + ////// + + const webgl::PackingInfo pi = {packFormat, packType}; + if (!ValidateReadPixelsFormatAndType(srcFormat->format, pi, gl, this)) + return; + + uint8_t bytesPerPixel; + if (!webgl::GetBytesPerPixel(pi, &bytesPerPixel)) { + ErrorInvalidOperation("readPixels: Unsupported format and type."); + return; } - GLenum auxReadFormat = mainReadFormat; - GLenum auxReadType = mainReadType; + ////// - // OpenGL ES 2.0 $4.3.1 - IMPLEMENTATION_COLOR_READ_{TYPE/FORMAT} is a valid - // combination for glReadPixels(). - if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) { - gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT, - reinterpret_cast(&auxReadFormat)); - gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE, - reinterpret_cast(&auxReadType)); - } - - const bool mainMatches = (format == mainReadFormat && type == mainReadType); - const bool auxMatches = (format == auxReadFormat && type == auxReadType); - bool isValid = mainMatches || auxMatches; - - // OpenGL ES 3.0.4 p194 - When the internal format of the rendering surface is - // RGB10_A2, a third combination of format RGBA and type UNSIGNED_INT_2_10_10_10_REV - // is accepted. - if (srcFormat->format->effectiveFormat == webgl::EffectiveFormat::RGB10_A2 && - format == LOCAL_GL_RGBA && - type == LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV) + uint32_t rowStride; + uint32_t bytesNeeded; + if (!ValidatePackSize("readPixels", width, height, bytesPerPixel, &rowStride, + &bytesNeeded)) { - isValid = true; + return; } - if (!isValid) - return ErrorInvalidOperation("readPixels: Invalid format/type pair"); + if (bytesNeeded > dataLen) { + ErrorInvalidOperation("readPixels: buffer too small"); + return; + } + //////////////// // Now that the errors are out of the way, on to actually reading! uint32_t readX, readY; @@ -1662,8 +1763,8 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum Intersect(srcHeight, y, height, &readY, &writeY, &rwHeight); if (rwWidth == uint32_t(width) && rwHeight == uint32_t(height)) { - DoReadPixelsAndConvert(x, y, width, height, format, type, data, auxReadFormat, - auxReadType); + DoReadPixelsAndConvert(srcFormat->format, x, y, width, height, packFormat, + packType, dest); return; } @@ -1688,13 +1789,14 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum if (IsWebGL2()) { if (!mPixelStore_PackRowLength) { - gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mPixelStore_PackSkipPixels + width); + gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, + mPixelStore_PackSkipPixels + width); } gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels + writeX); gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows + writeY); - DoReadPixelsAndConvert(readX, readY, rwWidth, rwHeight, format, type, data, - auxReadFormat, auxReadType); + DoReadPixelsAndConvert(srcFormat->format, readX, readY, rwWidth, rwHeight, + packFormat, packType, dest); gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mPixelStore_PackRowLength); gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels); @@ -1702,11 +1804,11 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum } else { // I *did* say "hilariously slow". - uint8_t* row = (uint8_t*)data + writeX * bytesPerPixel; + uint8_t* row = (uint8_t*)dest + writeX * bytesPerPixel; row += writeY * rowStride; for (uint32_t j = 0; j < rwHeight; j++) { - DoReadPixelsAndConvert(readX, readY+j, rwWidth, 1, format, type, row, - auxReadFormat, auxReadType); + DoReadPixelsAndConvert(srcFormat->format, readX, readY+j, rwWidth, 1, + packFormat, packType, row); row += rowStride; } } diff --git a/dom/canvas/WebGLFormats.cpp b/dom/canvas/WebGLFormats.cpp index 9ce5c4f918d9..0a0ccff49549 100644 --- a/dom/canvas/WebGLFormats.cpp +++ b/dom/canvas/WebGLFormats.cpp @@ -440,24 +440,28 @@ FormatInfo::GetCopyDecayFormat(UnsizedFormat uf) const return FindOrNull(this->copyDecayFormats, uf); } -uint8_t -BytesPerPixel(const PackingInfo& packing) +bool +GetBytesPerPixel(const PackingInfo& packing, uint8_t* const out_bytes) { uint8_t bytesPerChannel; + switch (packing.type) { case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: case LOCAL_GL_UNSIGNED_SHORT_5_6_5: - return 2; + *out_bytes = 2; + return true; case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV: case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV: case LOCAL_GL_UNSIGNED_INT_24_8: case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV: - return 4; + *out_bytes = 4; + return true; case LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV: - return 8; + *out_bytes = 8; + return true; // Alright, that's all the fixed-size unpackTypes. @@ -480,11 +484,20 @@ BytesPerPixel(const PackingInfo& packing) break; default: - MOZ_CRASH("GFX: invalid PackingInfo"); + return false; } uint8_t channels; + switch (packing.format) { + case LOCAL_GL_RED: + case LOCAL_GL_RED_INTEGER: + case LOCAL_GL_LUMINANCE: + case LOCAL_GL_ALPHA: + case LOCAL_GL_DEPTH_COMPONENT: + channels = 1; + break; + case LOCAL_GL_RG: case LOCAL_GL_RG_INTEGER: case LOCAL_GL_LUMINANCE_ALPHA: @@ -493,23 +506,36 @@ BytesPerPixel(const PackingInfo& packing) case LOCAL_GL_RGB: case LOCAL_GL_RGB_INTEGER: + case LOCAL_GL_SRGB: channels = 3; break; + case LOCAL_GL_BGRA: case LOCAL_GL_RGBA: case LOCAL_GL_RGBA_INTEGER: + case LOCAL_GL_SRGB_ALPHA: channels = 4; break; default: - channels = 1; - break; + return false; } - return bytesPerChannel * channels; + *out_bytes = bytesPerChannel * channels; + return true; } +uint8_t +BytesPerPixel(const PackingInfo& packing) +{ + uint8_t ret; + if (MOZ_LIKELY(GetBytesPerPixel(packing, &ret))) + return ret; + gfxCriticalError() << "Bad `packing`: " << gfx::hexa(packing.format) << ", " + << gfx::hexa(packing.type); + MOZ_CRASH("Bad `packing`."); +} ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/dom/canvas/WebGLFormats.h b/dom/canvas/WebGLFormats.h index 7e29dfccd288..ff4046587d65 100644 --- a/dom/canvas/WebGLFormats.h +++ b/dom/canvas/WebGLFormats.h @@ -254,6 +254,7 @@ struct DriverUnpackInfo const FormatInfo* GetFormat(EffectiveFormat format); uint8_t BytesPerPixel(const PackingInfo& packing); +bool GetBytesPerPixel(const PackingInfo& packing, uint8_t* const out_bytes); /* GLint ComponentSize(const FormatInfo* format, GLenum component); GLenum ComponentType(const FormatInfo* format); diff --git a/gfx/gl/GLContext.h b/gfx/gl/GLContext.h index 041f16a05e13..227597491c3c 100644 --- a/gfx/gl/GLContext.h +++ b/gfx/gl/GLContext.h @@ -1228,6 +1228,14 @@ public: fGetIntegerv(pname, reinterpret_cast(params)); } + template + T GetIntAs(GLenum pname) { + static_assert(sizeof(T) == sizeof(GLint), "Invalid T."); + T ret = 0; + fGetIntegerv(pname, (GLint*)&ret); + return ret; + } + void fGetFloatv(GLenum pname, GLfloat* params) { BEFORE_GL_CALL; mSymbols.fGetFloatv(pname, params);