bool ReadbackSharedSurface(SharedSurface* src, gfx::DrawTarget* dst) { AutoLockBits lock(dst); uint8_t* dstBytes; gfx::IntSize dstSize; int32_t dstStride; gfx::SurfaceFormat dstFormat; if (!lock.Lock(&dstBytes, &dstSize, &dstStride, &dstFormat)) return false; const bool isDstRGBA = (dstFormat == gfx::SurfaceFormat::R8G8B8A8 || dstFormat == gfx::SurfaceFormat::R8G8B8X8); MOZ_ASSERT_IF(!isDstRGBA, dstFormat == gfx::SurfaceFormat::B8G8R8A8 || dstFormat == gfx::SurfaceFormat::B8G8R8X8); size_t width = src->mSize.width; size_t height = src->mSize.height; MOZ_ASSERT(width == (size_t)dstSize.width); MOZ_ASSERT(height == (size_t)dstSize.height); GLenum readGLFormat; GLenum readType; { ScopedReadbackFB autoReadback(src); // We have a source FB, now we need a format. GLenum dstGLFormat = isDstRGBA ? LOCAL_GL_BGRA : LOCAL_GL_RGBA; GLenum dstType = LOCAL_GL_UNSIGNED_BYTE; // We actually don't care if they match, since we can handle // any read{Format,Type} we get. GLContext* gl = src->mGL; GetActualReadFormats(gl, dstGLFormat, dstType, &readGLFormat, &readType); MOZ_ASSERT(readGLFormat == LOCAL_GL_RGBA || readGLFormat == LOCAL_GL_BGRA); MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE); // ReadPixels from the current FB into lockedBits. { size_t alignment = 8; if (dstStride % 4 == 0) alignment = 4; ScopedPackAlignment autoAlign(gl, alignment); gl->raw_fReadPixels(0, 0, width, height, readGLFormat, readType, dstBytes); } } const bool isReadRGBA = readGLFormat == LOCAL_GL_RGBA; if (isReadRGBA != isDstRGBA) { for (size_t j = 0; j < height; ++j) { uint8_t* rowItr = dstBytes + j*dstStride; uint8_t* rowEnd = rowItr + 4*width; while (rowItr != rowEnd) { Swap(rowItr[0], rowItr[2]); rowItr += 4; } } } return true; }
static already_AddRefed<TextureClient> TexClientFromReadback(SharedSurface* src, ISurfaceAllocator* allocator, TextureFlags baseFlags, LayersBackend layersBackend) { auto backendType = gfx::BackendType::CAIRO; TexClientFactory factory(allocator, src->mHasAlpha, src->mSize, backendType, baseFlags, layersBackend); RefPtr<TextureClient> texClient; { gl::ScopedReadbackFB autoReadback(src); // We have a source FB, now we need a format. GLenum destFormat = LOCAL_GL_BGRA; GLenum destType = LOCAL_GL_UNSIGNED_BYTE; GLenum readFormat; GLenum readType; // We actually don't care if they match, since we can handle // any read{Format,Type} we get. auto gl = src->mGL; GetActualReadFormats(gl, destFormat, destType, &readFormat, &readType); MOZ_ASSERT(readFormat == LOCAL_GL_RGBA || readFormat == LOCAL_GL_BGRA); MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE); // With a format and type, we can create texClient. if (readFormat == LOCAL_GL_BGRA && readType == LOCAL_GL_UNSIGNED_BYTE) { // 0xAARRGGBB // In Lendian: [BB, GG, RR, AA] texClient = factory.CreateB8G8R8AX8(); } else if (readFormat == LOCAL_GL_RGBA && readType == LOCAL_GL_UNSIGNED_BYTE) { // [RR, GG, BB, AA] texClient = factory.CreateR8G8B8AX8(); } else { MOZ_CRASH("GFX: Bad `read{Format,Type}`."); } MOZ_ASSERT(texClient); if (!texClient) return nullptr; // With a texClient, we can lock for writing. TextureClientAutoLock autoLock(texClient, OpenMode::OPEN_WRITE); DebugOnly<bool> succeeded = autoLock.Succeeded(); MOZ_ASSERT(succeeded, "texture should have locked"); MappedTextureData mapped; texClient->BorrowMappedData(mapped); // ReadPixels from the current FB into mapped.data. auto width = src->mSize.width; auto height = src->mSize.height; { ScopedPackAlignment autoAlign(gl, 4); MOZ_ASSERT(mapped.stride/4 == mapped.size.width); gl->raw_fReadPixels(0, 0, width, height, readFormat, readType, mapped.data); } // RB_SWAPPED doesn't work with D3D11. (bug 1051010) // RB_SWAPPED doesn't work with Basic. (bug ???????) // RB_SWAPPED doesn't work with D3D9. (bug ???????) bool layersNeedsManualSwap = layersBackend == LayersBackend::LAYERS_BASIC || layersBackend == LayersBackend::LAYERS_D3D9 || layersBackend == LayersBackend::LAYERS_D3D11; if (texClient->HasFlags(TextureFlags::RB_SWAPPED) && layersNeedsManualSwap) { size_t pixels = width * height; uint8_t* itr = mapped.data; for (size_t i = 0; i < pixels; i++) { SwapRB_R8G8B8A8(itr); itr += 4; } texClient->RemoveFlags(TextureFlags::RB_SWAPPED); } } return texClient.forget(); }
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: le[RGBA] * BGRA+UInt: le[BGRA] * BGRA+UIntRev: 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->Stride() == dest->Width() * destPixelSize); GLenum readFormat = destFormat; GLenum readType = destType; bool needsTempSurf = !GetActualReadFormats(gl, destFormat, destType, readFormat, readType); nsAutoPtr<gfxImageSurface> tempSurf; gfxImageSurface* readSurf = nullptr; int readPixelSize = 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(readPixelSize == 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); readPixelSize = 4; break; } case LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV: { MOZ_ASSERT(readFormat == LOCAL_GL_BGRA); readPixelSize = 4; break; } case LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV: { MOZ_ASSERT(readFormat == LOCAL_GL_RGB); readPixelSize = 2; break; } default: { MOZ_CRASH("Bad read type."); } } tempSurf = new gfxImageSurface(dest->GetSize(), SurfaceFormatToImageFormat(readFormatGFX), false); readSurf = tempSurf; } else { readPixelSize = destPixelSize; readSurf = dest; } MOZ_ASSERT(readPixelSize); GLint currentPackAlignment = 0; gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, ¤tPackAlignment); if (currentPackAlignment != readPixelSize) gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readPixelSize); GLsizei width = dest->Width(); GLsizei height = dest->Height(); readSurf->Flush(); gl->fReadPixels(0, 0, width, height, readFormat, readType, readSurf->Data()); readSurf->MarkDirty(); if (currentPackAlignment != readPixelSize) 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); 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 }