void ReadPixelsIntoImageSurface(GLContext* gl, gfxImageSurface* dest) { gl->MakeCurrent(); MOZ_ASSERT(dest->GetSize() != gfxIntSize(0, 0)); /* gfxImageFormat::ARGB32: * RGBA+UByte: be[RGBA], le[ABGR] * RGBA+UInt: be[ABGR], le[RGBA] * BGRA+UInt: be[ARGB], le[BGRA] * BGRA+UIntRev: be[BGRA], le[ARGB] * * gfxImageFormat::RGB16_565: * RGB+UShort: le[rrrrrggg,gggbbbbb] */ bool hasAlpha = dest->Format() == gfxImageFormat::ARGB32; int destPixelSize; GLenum destFormat; GLenum destType; switch (dest->Format()) { case gfxImageFormat::RGB24: // XRGB case gfxImageFormat::ARGB32: destPixelSize = 4; // Needs host (little) endian ARGB. destFormat = LOCAL_GL_BGRA; destType = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; break; case gfxImageFormat::RGB16_565: destPixelSize = 2; destFormat = LOCAL_GL_RGB; destType = LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV; break; default: MOZ_CRASH("Bad format."); } MOZ_ASSERT(dest->Width() * destPixelSize <= dest->Stride()); GLenum readFormat = destFormat; GLenum readType = destType; bool needsTempSurf = !GetActualReadFormats(gl, destFormat, destType, readFormat, readType); nsAutoPtr<gfxImageSurface> tempSurf; gfxImageSurface* readSurf = nullptr; int readAlignment = 0; if (needsTempSurf) { if (gl->DebugMode()) { NS_WARNING("Needing intermediary surface for ReadPixels. This will be slow!"); } SurfaceFormat readFormatGFX; switch (readFormat) { case LOCAL_GL_RGBA: case LOCAL_GL_BGRA: { readFormatGFX = hasAlpha ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8; break; } case LOCAL_GL_RGB: { MOZ_ASSERT(destPixelSize == 2); MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV); readFormatGFX = SurfaceFormat::R5G6B5; break; } default: { MOZ_CRASH("Bad read format."); } } switch (readType) { case LOCAL_GL_UNSIGNED_BYTE: { MOZ_ASSERT(readFormat == LOCAL_GL_RGBA); readAlignment = 1; break; } case LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV: { MOZ_ASSERT(readFormat == LOCAL_GL_BGRA); readAlignment = 4; break; } case LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV: { MOZ_ASSERT(readFormat == LOCAL_GL_RGB); readAlignment = 2; break; } default: { MOZ_CRASH("Bad read type."); } } tempSurf = new gfxImageSurface(dest->GetSize(), SurfaceFormatToImageFormat(readFormatGFX), false); readSurf = tempSurf; } else { // Figure out alignment. We don't need to know why, we just need it // to be valid. readAlignment = GuessAlignment(dest->Width(), destPixelSize, dest->Stride()); readSurf = dest; } MOZ_ASSERT(readAlignment); GLint currentPackAlignment = 0; gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, ¤tPackAlignment); if (currentPackAlignment != readAlignment) gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readAlignment); GLsizei width = dest->Width(); GLsizei height = dest->Height(); readSurf->Flush(); gl->fReadPixels(0, 0, width, height, readFormat, readType, readSurf->Data()); readSurf->MarkDirty(); if (currentPackAlignment != readAlignment) gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment); if (readSurf != dest) { MOZ_ASSERT(readFormat == LOCAL_GL_RGBA); MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE); // So we just copied in RGBA in big endian, or le: 0xAABBGGRR. // We want 0xAARRGGBB, so swap R and B: dest->Flush(); RefPtr<DataSourceSurface> readDSurf = Factory::CreateWrappingDataSourceSurface(readSurf->Data(), readSurf->Stride(), ToIntSize(readSurf->GetSize()), ImageFormatToSurfaceFormat(readSurf->Format())); SwapRAndBComponents(readDSurf); dest->MarkDirty(); gfxContext ctx(dest); ctx.SetOperator(gfxContext::OPERATOR_SOURCE); ctx.SetSource(readSurf); ctx.Paint(); } // Check if GL is giving back 1.0 alpha for // RGBA reads to RGBA images from no-alpha buffers. #ifdef XP_MACOSX if (gl->WorkAroundDriverBugs() && gl->Vendor() == gl::GLVendor::NVIDIA && dest->Format() == gfxImageFormat::ARGB32 && width && height) { GLint alphaBits = 0; gl->fGetIntegerv(LOCAL_GL_ALPHA_BITS, &alphaBits); if (!alphaBits) { const uint32_t alphaMask = gfxPackedPixelNoPreMultiply(0xff,0,0,0); MOZ_ASSERT(dest->Width() * destPixelSize == dest->Stride()); dest->Flush(); uint32_t* itr = (uint32_t*)dest->Data(); uint32_t testPixel = *itr; if ((testPixel & alphaMask) != alphaMask) { // We need to set the alpha channel to 1.0 manually. uint32_t* itrEnd = itr + width*height; // Stride is guaranteed to be width*4. for (; itr != itrEnd; itr++) { *itr |= alphaMask; } } dest->MarkDirty(); } } #endif }
/*static*/ bool TexUnpackSurface::UploadDataSurface(bool isSubImage, WebGLContext* webgl, TexImageTarget target, GLint level, const webgl::DriverUnpackInfo* dui, GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width, GLsizei height, gfx::DataSourceSurface* surf, bool isSurfAlphaPremult, GLenum* const out_glError) { gl::GLContext* gl = webgl->GL(); MOZ_ASSERT(gl->IsCurrent()); *out_glError = 0; if (isSurfAlphaPremult != webgl->mPixelStore_PremultiplyAlpha) return false; gl::OriginPos srcOrigin, dstOrigin; OriginsForDOM(webgl, &srcOrigin, &dstOrigin); if (srcOrigin != dstOrigin) return false; // This differs from the raw-data upload in that we choose how we do the unpack. // (alignment, etc.) // Uploading RGBX as RGBA and blitting to RGB is faster than repacking RGBX into // RGB on the CPU. However, this is optimization is out-of-scope for now. static const webgl::DriverUnpackInfo kInfoBGRA = { LOCAL_GL_BGRA, LOCAL_GL_BGRA, LOCAL_GL_UNSIGNED_BYTE, }; const webgl::DriverUnpackInfo* chosenDUI = nullptr; switch (surf->GetFormat()) { case gfx::SurfaceFormat::B8G8R8A8: if (SupportsBGRA(gl) && dui->internalFormat == LOCAL_GL_RGBA && dui->unpackFormat == LOCAL_GL_RGBA && dui->unpackType == LOCAL_GL_UNSIGNED_BYTE) { chosenDUI = &kInfoBGRA; } break; case gfx::SurfaceFormat::R8G8B8A8: if (dui->unpackFormat == LOCAL_GL_RGBA && dui->unpackType == LOCAL_GL_UNSIGNED_BYTE) { chosenDUI = dui; } break; case gfx::SurfaceFormat::R5G6B5_UINT16: if (dui->unpackFormat == LOCAL_GL_RGB && dui->unpackType == LOCAL_GL_UNSIGNED_SHORT_5_6_5) { chosenDUI = dui; } break; default: break; } if (!chosenDUI) return false; gfx::DataSourceSurface::ScopedMap map(surf, gfx::DataSourceSurface::MapType::READ); if (!map.IsMapped()) return false; const webgl::PackingInfo pi = {chosenDUI->unpackFormat, chosenDUI->unpackType}; const auto bytesPerPixel = webgl::BytesPerPixel(pi); const size_t bytesPerRow = width * bytesPerPixel; const GLint kMaxUnpackAlignment = 8; size_t unpackAlignment; if (!GuessAlignment(map.GetData(), bytesPerRow, map.GetStride(), kMaxUnpackAlignment, &unpackAlignment)) { return false; // TODO: Consider using UNPACK_ settings to set the stride based on the too-large // alignment used for some SourceSurfaces. (D2D allegedy likes alignment=16) } MOZ_ALWAYS_TRUE( webgl->gl->MakeCurrent() ); ScopedUnpackReset scopedReset(webgl); gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, unpackAlignment); const GLsizei depth = 1; GLenum error = DoTexOrSubImage(isSubImage, gl, target.get(), level, chosenDUI, xOffset, yOffset, zOffset, width, height, depth, map.GetData()); if (error) { *out_glError = error; return false; } return true; }