From a30e4e7afc3b20d2dd6661063339b81a479feb6e Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Mon, 12 May 2014 20:47:52 -0700 Subject: [PATCH] Backed out 6 changesets (bug 1006198) for webgl assertions on a CLOSED TREE Backed out changeset 5be3c8c44eed (bug 1006198) Backed out changeset 4b15abd14f28 (bug 1006198) Backed out changeset bd11a10e7028 (bug 1006198) Backed out changeset e1044a8ab189 (bug 1006198) Backed out changeset 558abe22ab30 (bug 1006198) Backed out changeset 5462b9babaed (bug 1006198) --- content/canvas/src/WebGLContext.cpp | 17 ++- gfx/gl/GLReadTexImageHelper.cpp | 206 ++++++++++++++++++++++++---- gfx/gl/GLReadTexImageHelper.h | 2 + gfx/layers/CopyableCanvasLayer.cpp | 11 +- gfx/layers/LayerUtils.cpp | 101 ++++++++++++++ gfx/layers/LayerUtils.h | 21 +++ gfx/layers/composite/ImageHost.cpp | 4 - gfx/layers/moz.build | 1 + gfx/thebes/gfxUtils.cpp | 89 ++++++++++-- gfx/thebes/gfxUtils.h | 5 +- 10 files changed, 401 insertions(+), 56 deletions(-) create mode 100644 gfx/layers/LayerUtils.cpp create mode 100644 gfx/layers/LayerUtils.h diff --git a/content/canvas/src/WebGLContext.cpp b/content/canvas/src/WebGLContext.cpp index def321f93304..e64e20ebfa1e 100644 --- a/content/canvas/src/WebGLContext.cpp +++ b/content/canvas/src/WebGLContext.cpp @@ -1337,10 +1337,10 @@ WebGLContext::GetSurfaceSnapshot(bool* aPremultAlpha) if (!gl) return nullptr; - RefPtr surf = Factory::CreateDataSourceSurfaceWithStride(IntSize(mWidth, mHeight), - SurfaceFormat::B8G8R8A8, - mWidth * 4); - if (!surf) { + nsRefPtr surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight), + gfxImageFormat::ARGB32, + mWidth * 4, 0, false); + if (surf->CairoStatus() != 0) { return nullptr; } @@ -1348,7 +1348,7 @@ WebGLContext::GetSurfaceSnapshot(bool* aPremultAlpha) { ScopedBindFramebuffer autoFB(gl, 0); ClearBackbufferIfNeeded(); - ReadPixelsIntoDataSurface(gl, surf); + ReadPixelsIntoImageSurface(gl, surf); } if (aPremultAlpha) { @@ -1359,7 +1359,8 @@ WebGLContext::GetSurfaceSnapshot(bool* aPremultAlpha) if (aPremultAlpha) { *aPremultAlpha = false; } else { - gfxUtils::PremultiplyDataSurface(surf); + gfxUtils::PremultiplyImageSurface(surf); + surf->MarkDirty(); } } @@ -1372,12 +1373,14 @@ WebGLContext::GetSurfaceSnapshot(bool* aPremultAlpha) return nullptr; } + RefPtr source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surf); + Matrix m; m.Translate(0.0, mHeight); m.Scale(1.0, -1.0); dt->SetTransform(m); - dt->DrawSurface(surf, + dt->DrawSurface(source, Rect(0, 0, mWidth, mHeight), Rect(0, 0, mWidth, mHeight), DrawSurfaceOptions(), diff --git a/gfx/gl/GLReadTexImageHelper.cpp b/gfx/gl/GLReadTexImageHelper.cpp index 234d136affbe..7e4274af2efa 100644 --- a/gfx/gl/GLReadTexImageHelper.cpp +++ b/gfx/gl/GLReadTexImageHelper.cpp @@ -328,40 +328,44 @@ GuessAlignment(int width, int pixelSize, int rowStride) } void -ReadPixelsIntoDataSurface(GLContext* gl, DataSourceSurface* dest) { +ReadPixelsIntoImageSurface(GLContext* gl, gfxImageSurface* dest) { gl->MakeCurrent(); - MOZ_ASSERT(dest->GetSize().width != 0); - MOZ_ASSERT(dest->GetSize().height != 0); + MOZ_ASSERT(dest->GetSize() != gfxIntSize(0, 0)); - bool hasAlpha = dest->GetFormat() == SurfaceFormat::B8G8R8A8 || - dest->GetFormat() == SurfaceFormat::R8G8B8A8; + /* 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->GetFormat()) { - case SurfaceFormat::B8G8R8A8: - case SurfaceFormat::B8G8R8X8: + 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 SurfaceFormat::R8G8B8A8: - case SurfaceFormat::R8G8B8X8: - // Needs host (little) endian ABGR. - destFormat = LOCAL_GL_RGBA; - destType = LOCAL_GL_UNSIGNED_BYTE; - break; - case SurfaceFormat::R5G6B5: + + 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."); } - destPixelSize = BytesPerPixel(dest->GetFormat()); - MOZ_ASSERT(dest->GetSize().width * destPixelSize <= dest->Stride()); + MOZ_ASSERT(dest->Width() * destPixelSize <= dest->Stride()); GLenum readFormat = destFormat; GLenum readType = destType; @@ -369,15 +373,20 @@ ReadPixelsIntoDataSurface(GLContext* gl, DataSourceSurface* dest) { destFormat, destType, readFormat, readType); - RefPtr tempSurf; - DataSourceSurface* readSurf = dest; - int readAlignment = GuessAlignment(dest->GetSize().width, + nsAutoPtr tempSurf; + gfxImageSurface* readSurf = nullptr; + + // Figure out alignment. We don't need to know why, we just need it + // to be valid. + int readAlignment = GuessAlignment(dest->Width(), destPixelSize, dest->Stride()); - if (!readAlignment) { + if (!readAlignment) // Couldn't calculate a valid alignment. needsTempSurf = true; - } - if (needsTempSurf) { + + if (!needsTempSurf) { + readSurf = dest; + } else { if (gl->DebugMode()) { NS_WARNING("Needing intermediary surface for ReadPixels. This will be slow!"); } @@ -422,11 +431,154 @@ ReadPixelsIntoDataSurface(GLContext* gl, DataSourceSurface* dest) { } } + tempSurf = new gfxImageSurface(dest->GetSize(), + SurfaceFormatToImageFormat(readFormatGFX), + false); + readSurf = tempSurf; + } + 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 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 +} + +void +ReadPixelsIntoDataSurface(GLContext* gl, DataSourceSurface* dest) { + gl->MakeCurrent(); + MOZ_ASSERT(dest->GetSize().width != 0); + MOZ_ASSERT(dest->GetSize().height != 0); + + bool hasAlpha = dest->GetFormat() == SurfaceFormat::B8G8R8A8 || + dest->GetFormat() == SurfaceFormat::R8G8B8A8; + + int destPixelSize; + GLenum destFormat; + GLenum destType; + + switch (dest->GetFormat()) { + case SurfaceFormat::B8G8R8A8: + case SurfaceFormat::B8G8R8X8: + // Needs host (little) endian ARGB. + destFormat = LOCAL_GL_BGRA; + destType = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; + break; + case SurfaceFormat::R8G8B8A8: + case SurfaceFormat::R8G8B8X8: + // Needs host (little) endian ABGR. + destFormat = LOCAL_GL_RGBA; + destType = LOCAL_GL_UNSIGNED_BYTE; + break; + case SurfaceFormat::R5G6B5: + destFormat = LOCAL_GL_RGB; + destType = LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV; + break; + default: + MOZ_CRASH("Bad format."); + } + destPixelSize = BytesPerPixel(dest->GetFormat()); + MOZ_ASSERT(dest->GetSize().width * destPixelSize <= dest->Stride()); + + GLenum readFormat = destFormat; + GLenum readType = destType; + bool needsTempSurf = !GetActualReadFormats(gl, + destFormat, destType, + readFormat, readType); + + RefPtr tempSurf; + DataSourceSurface* readSurf = nullptr; + int readAlignment = 0; + if (needsTempSurf) { + if (gl->DebugMode()) { + NS_WARNING("Needing intermediary surface for ReadPixels. This will be slow!"); + } + SurfaceFormat readFormatGFX; + + // If needs temp surface, readFormat is always LOCAL_GL_RGBA + // and readType is always LOCAL_GL_UNSIGNED_BYTE + MOZ_ASSERT(readFormat == LOCAL_GL_RGBA); + MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE); + readFormatGFX = hasAlpha ? SurfaceFormat::R8G8B8A8 + : SurfaceFormat::R8G8B8X8; + readAlignment = 1; int32_t stride = dest->GetSize().width * BytesPerPixel(readFormatGFX); tempSurf = Factory::CreateDataSourceSurfaceWithStride(dest->GetSize(), readFormatGFX, stride); readSurf = tempSurf; + } else { + // Figure out alignment. We don't need to know why, we just need it + // to be valid. + readAlignment = GuessAlignment(dest->GetSize().width, + destPixelSize, + dest->Stride()); + readSurf = dest; } MOZ_ASSERT(readAlignment); MOZ_ASSERT(reinterpret_cast(readSurf->GetData()) % readAlignment == 0); @@ -553,6 +705,14 @@ ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, SurfaceFormat aFo return surf.forget(); } +void +ReadScreenIntoImageSurface(GLContext* gl, gfxImageSurface* dest) +{ + ScopedBindFramebuffer autoFB(gl, 0); + ReadPixelsIntoImageSurface(gl, dest); +} + + #define CLEANUP_IF_GLERROR_OCCURRED(x) \ if (DidGLErrorOccur(x)) { \ isurf = nullptr; \ diff --git a/gfx/gl/GLReadTexImageHelper.h b/gfx/gl/GLReadTexImageHelper.h index 9ea177778d96..29d0017a9c13 100644 --- a/gfx/gl/GLReadTexImageHelper.h +++ b/gfx/gl/GLReadTexImageHelper.h @@ -26,6 +26,8 @@ namespace gl { void ReadPixelsIntoDataSurface(GLContext* aGL, gfx::DataSourceSurface* aSurface); +void ReadPixelsIntoImageSurface(GLContext* aGL, gfxImageSurface* aSurface); +void ReadScreenIntoImageSurface(GLContext* aGL, gfxImageSurface* aSurface); TemporaryRef ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, gfx::SurfaceFormat aFormat); diff --git a/gfx/layers/CopyableCanvasLayer.cpp b/gfx/layers/CopyableCanvasLayer.cpp index 51f61697ec3c..c3c7c4b4781c 100644 --- a/gfx/layers/CopyableCanvasLayer.cpp +++ b/gfx/layers/CopyableCanvasLayer.cpp @@ -17,12 +17,11 @@ #include "gfxUtils.h" // for gfxUtils #include "gfx2DGlue.h" // for thebes --> moz2d transition #include "mozilla/gfx/BaseSize.h" // for BaseSize -#include "mozilla/gfx/Tools.h" #include "nsDebug.h" // for NS_ASSERTION, NS_WARNING, etc #include "nsISupportsImpl.h" // for gfxContext::AddRef, etc #include "nsRect.h" // for nsIntRect #include "nsSize.h" // for nsIntSize -#include "gfxUtils.h" +#include "LayerUtils.h" using namespace mozilla::gfx; using namespace mozilla::gl; @@ -127,7 +126,7 @@ CopyableCanvasLayer::UpdateTarget(DrawTarget* aDestTarget) Factory::CreateWrappingDataSourceSurface(destData, destStride, destSize, destFormat); mGLContext->Screen()->Readback(sharedSurf, data); if (needsPremult) { - gfxUtils::PremultiplyDataSurface(data); + PremultiplySurface(data); } aDestTarget->ReleaseBits(destData); return; @@ -145,7 +144,7 @@ CopyableCanvasLayer::UpdateTarget(DrawTarget* aDestTarget) // Readback handles Flush/MarkDirty. mGLContext->Screen()->Readback(sharedSurf, data); if (needsPremult) { - gfxUtils::PremultiplyDataSurface(data); + PremultiplySurface(data); } resultSurf = data; } @@ -171,9 +170,7 @@ CopyableCanvasLayer::GetTempSurface(const IntSize& aSize, aSize != mCachedTempSurface->GetSize() || aFormat != mCachedTempSurface->GetFormat()) { - // Create a surface aligned to 8 bytes since that's the highest alignment WebGL can handle. - uint32_t stride = GetAlignedStride<8>(aSize.width * BytesPerPixel(aFormat)); - mCachedTempSurface = Factory::CreateDataSourceSurfaceWithStride(aSize, aFormat, stride); + mCachedTempSurface = Factory::CreateDataSourceSurface(aSize, aFormat); } return mCachedTempSurface; diff --git a/gfx/layers/LayerUtils.cpp b/gfx/layers/LayerUtils.cpp new file mode 100644 index 000000000000..c6c7da9c238c --- /dev/null +++ b/gfx/layers/LayerUtils.cpp @@ -0,0 +1,101 @@ +/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "LayerUtils.h" +#include "PremultiplyTables.h" +#include "mozilla/Endian.h" + +namespace mozilla { +namespace layers { + +using namespace mozilla::gfx; + +static inline const uint8_t PremultiplyValue(uint8_t a, uint8_t v) { + return PremultiplyTable[a*256+v]; +} + +static inline const uint8_t UnpremultiplyValue(uint8_t a, uint8_t v) { + return UnpremultiplyTable[a*256+v]; +} + +#ifdef DEBUG +static bool IsLittleEndian() +{ + // Violate strict aliasing, because violating strict aliasing is how + // we always pack and unpack between uint32_t and uint8_t[]. + uint16_t testShort; + static const uint8_t testBytes[2] = { 0xAA, 0xBB }; + memcpy(&testShort, testBytes, sizeof(testBytes)); + return testShort == 0xBBAA; +} +#endif // DEBUG + +#ifdef MOZ_LITTLE_ENDIAN +#define ASSERT_ENDIAN() MOZ_ASSERT(IsLittleEndian(), "Defined as little endian, but actually big!") +#else +#define ASSERT_ENDIAN() MOZ_ASSERT(!IsLittleEndian(), "Defined as big endian, but actually little!") +#endif + +void +PremultiplySurface(DataSourceSurface* srcSurface, + DataSourceSurface* destSurface) +{ + if (!destSurface) + destSurface = srcSurface; + + IntSize srcSize = srcSurface->GetSize(); + MOZ_ASSERT(srcSurface->GetFormat() == destSurface->GetFormat() && + srcSize.width == destSurface->GetSize().width && + srcSize.height == destSurface->GetSize().height && + srcSurface->Stride() == destSurface->Stride(), + "Source and destination surfaces don't have identical characteristics"); + + MOZ_ASSERT(srcSurface->Stride() == srcSize.width * 4, + "Source surface stride isn't tightly packed"); + + // Only premultiply ARGB32 + if (srcSurface->GetFormat() != SurfaceFormat::B8G8R8A8) { + if (destSurface != srcSurface) { + memcpy(destSurface->GetData(), srcSurface->GetData(), + srcSurface->Stride() * srcSize.height); + } + return; + } + + uint8_t *src = srcSurface->GetData(); + uint8_t *dst = destSurface->GetData(); + + // Assert that our endian define is correct. + ASSERT_ENDIAN(); + + uint32_t dim = srcSize.width * srcSize.height; + for (uint32_t i = 0; i < dim; ++i) { +#ifdef MOZ_LITTLE_ENDIAN + uint8_t b = *src++; + uint8_t g = *src++; + uint8_t r = *src++; + uint8_t a = *src++; + + *dst++ = PremultiplyValue(a, b); + *dst++ = PremultiplyValue(a, g); + *dst++ = PremultiplyValue(a, r); + *dst++ = a; +#else + uint8_t a = *src++; + uint8_t r = *src++; + uint8_t g = *src++; + uint8_t b = *src++; + + *dst++ = a; + *dst++ = PremultiplyValue(a, r); + *dst++ = PremultiplyValue(a, g); + *dst++ = PremultiplyValue(a, b); +#endif + } +} + + +} +} diff --git a/gfx/layers/LayerUtils.h b/gfx/layers/LayerUtils.h new file mode 100644 index 000000000000..72c8fd497e3e --- /dev/null +++ b/gfx/layers/LayerUtils.h @@ -0,0 +1,21 @@ +/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_LAYERS_LAYERUTILS_H_ +#define MOZILLA_LAYERS_LAYERUTILS_H_ + +#include "mozilla/gfx/2D.h" + +namespace mozilla { +namespace layers { + +void +PremultiplySurface(gfx::DataSourceSurface* srcSurface, + gfx::DataSourceSurface* destSurface = nullptr); + +} +} + +#endif /* MOZILLA_LAYERS_LAYERUTILS_H_ */ diff --git a/gfx/layers/composite/ImageHost.cpp b/gfx/layers/composite/ImageHost.cpp index 6eef998b0e43..a8fe1d3fa33b 100644 --- a/gfx/layers/composite/ImageHost.cpp +++ b/gfx/layers/composite/ImageHost.cpp @@ -124,10 +124,6 @@ ImageHost::Composite(EffectChain& aEffectChain, } else { effect->mTextureCoords = Rect(0, 0, 1, 1); } - if (mFrontBuffer->GetFlags() & TextureFlags::NEEDS_Y_FLIP) { - effect->mTextureCoords.y = effect->mTextureCoords.YMost(); - effect->mTextureCoords.height = -effect->mTextureCoords.height; - } GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform); GetCompositor()->DrawDiagnostics(DiagnosticFlags::IMAGE | DiagnosticFlags::BIGIMAGE, diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build index ac7dbcb1adf7..78c8253dc61b 100644 --- a/gfx/layers/moz.build +++ b/gfx/layers/moz.build @@ -294,6 +294,7 @@ UNIFIED_SOURCES += [ 'LayerScope.cpp', 'LayersLogging.cpp', 'LayerSorter.cpp', + 'LayerUtils.cpp', 'opengl/CompositingRenderTargetOGL.cpp', 'opengl/CompositorOGL.cpp', 'opengl/OGLShaderProgram.cpp', diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp index 44f83a79834e..68d30ed2333d 100644 --- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -36,24 +36,34 @@ static const uint8_t UnpremultiplyValue(uint8_t a, uint8_t v) { } void -gfxUtils::PremultiplyDataSurface(DataSourceSurface *aSurface) +gfxUtils::PremultiplyImageSurface(gfxImageSurface *aSourceSurface, + gfxImageSurface *aDestSurface) { - // Only premultiply ARGB32 - if (aSurface->GetFormat() != SurfaceFormat::B8G8R8A8) { - return; - } + if (!aDestSurface) + aDestSurface = aSourceSurface; - DataSourceSurface::MappedSurface map; - if (!aSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map)) { - return; - } - MOZ_ASSERT(map.mStride == aSurface->GetSize().width * 4, + MOZ_ASSERT(aSourceSurface->Format() == aDestSurface->Format() && + aSourceSurface->Width() == aDestSurface->Width() && + aSourceSurface->Height() == aDestSurface->Height() && + aSourceSurface->Stride() == aDestSurface->Stride(), + "Source and destination surfaces don't have identical characteristics"); + + MOZ_ASSERT(aSourceSurface->Stride() == aSourceSurface->Width() * 4, "Source surface stride isn't tightly packed"); - uint8_t *src = map.mData; - uint8_t *dst = map.mData; + // Only premultiply ARGB32 + if (aSourceSurface->Format() != gfxImageFormat::ARGB32) { + if (aDestSurface != aSourceSurface) { + memcpy(aDestSurface->Data(), aSourceSurface->Data(), + aSourceSurface->Stride() * aSourceSurface->Height()); + } + return; + } - uint32_t dim = aSurface->GetSize().width * aSurface->GetSize().height; + uint8_t *src = aSourceSurface->Data(); + uint8_t *dst = aDestSurface->Data(); + + uint32_t dim = aSourceSurface->Width() * aSourceSurface->Height(); for (uint32_t i = 0; i < dim; ++i) { #ifdef IS_LITTLE_ENDIAN uint8_t b = *src++; @@ -77,8 +87,59 @@ gfxUtils::PremultiplyDataSurface(DataSourceSurface *aSurface) *dst++ = PremultiplyValue(a, b); #endif } +} - aSurface->Unmap(); +void +gfxUtils::UnpremultiplyImageSurface(gfxImageSurface *aSourceSurface, + gfxImageSurface *aDestSurface) +{ + if (!aDestSurface) + aDestSurface = aSourceSurface; + + MOZ_ASSERT(aSourceSurface->Format() == aDestSurface->Format() && + aSourceSurface->Width() == aDestSurface->Width() && + aSourceSurface->Height() == aDestSurface->Height(), + "Source and destination surfaces don't have identical characteristics"); + + // Only premultiply ARGB32 + if (aSourceSurface->Format() != gfxImageFormat::ARGB32) { + if (aDestSurface != aSourceSurface) { + aDestSurface->CopyFrom(aSourceSurface); + } + return; + } + + uint8_t *src = aSourceSurface->Data(); + uint8_t *dst = aDestSurface->Data(); + + for (int32_t i = 0; i < aSourceSurface->Height(); ++i) { + uint8_t *srcRow = src + (i * aSourceSurface->Stride()); + uint8_t *dstRow = dst + (i * aDestSurface->Stride()); + + for (int32_t j = 0; j < aSourceSurface->Width(); ++j) { +#ifdef IS_LITTLE_ENDIAN + uint8_t b = *srcRow++; + uint8_t g = *srcRow++; + uint8_t r = *srcRow++; + uint8_t a = *srcRow++; + + *dstRow++ = UnpremultiplyValue(a, b); + *dstRow++ = UnpremultiplyValue(a, g); + *dstRow++ = UnpremultiplyValue(a, r); + *dstRow++ = a; +#else + uint8_t a = *srcRow++; + uint8_t r = *srcRow++; + uint8_t g = *srcRow++; + uint8_t b = *srcRow++; + + *dstRow++ = a; + *dstRow++ = UnpremultiplyValue(a, r); + *dstRow++ = UnpremultiplyValue(a, g); + *dstRow++ = UnpremultiplyValue(a, b); +#endif + } + } } TemporaryRef diff --git a/gfx/thebes/gfxUtils.h b/gfx/thebes/gfxUtils.h index 25f79ed25d0e..3e68d7614c8b 100644 --- a/gfx/thebes/gfxUtils.h +++ b/gfx/thebes/gfxUtils.h @@ -40,7 +40,10 @@ public: * If the source is not gfxImageFormat::ARGB32, no operation is performed. If * aDestSurface is given, the data is copied over. */ - static void PremultiplyDataSurface(DataSourceSurface *aSurface); + static void PremultiplyImageSurface(gfxImageSurface *aSourceSurface, + gfxImageSurface *aDestSurface = nullptr); + static void UnpremultiplyImageSurface(gfxImageSurface *aSurface, + gfxImageSurface *aDestSurface = nullptr); static mozilla::TemporaryRef UnpremultiplyDataSurface(DataSourceSurface* aSurface); static void ConvertBGRAtoRGBA(gfxImageSurface *aSourceSurface,