summaryrefslogtreecommitdiffstats
path: root/gfx/layers/TextureDIB.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/TextureDIB.cpp')
-rw-r--r--gfx/layers/TextureDIB.cpp505
1 files changed, 505 insertions, 0 deletions
diff --git a/gfx/layers/TextureDIB.cpp b/gfx/layers/TextureDIB.cpp
new file mode 100644
index 000000000..79a9469bf
--- /dev/null
+++ b/gfx/layers/TextureDIB.cpp
@@ -0,0 +1,505 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "TextureDIB.h"
+#include "gfx2DGlue.h"
+#include "mozilla/gfx/DataSurfaceHelpers.h" // For BufferSizeFromDimensions
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+/**
+ * Can only be drawn into through Cairo.
+ * The coresponding TextureHost depends on the compositor
+ */
+class MemoryDIBTextureData : public DIBTextureData
+{
+public:
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+ static
+ DIBTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) override
+ {
+ mSurface = nullptr;
+ }
+
+ MemoryDIBTextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfxWindowsSurface* aSurface)
+ : DIBTextureData(aSize, aFormat, aSurface)
+ {
+ MOZ_COUNT_CTOR(MemoryDIBTextureData);
+ }
+
+ virtual ~MemoryDIBTextureData()
+ {
+ MOZ_COUNT_DTOR(MemoryDIBTextureData);
+ }
+};
+
+/**
+ * Can only be drawn into through Cairo.
+ * The coresponding TextureHost depends on the compositor
+ */
+class ShmemDIBTextureData : public DIBTextureData
+{
+public:
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+ static
+ DIBTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ LayersIPCChannel* aAllocator);
+
+ void DeallocateData()
+ {
+ if (mSurface) {
+ ::DeleteObject(mBitmap);
+ ::DeleteDC(mDC);
+ ::CloseHandle(mFileMapping);
+ mBitmap = NULL;
+ mDC = NULL;
+ mFileMapping = NULL;
+ mSurface = nullptr;
+ }
+ }
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) override
+ {
+ DeallocateData();
+ }
+
+ ShmemDIBTextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfxWindowsSurface* aSurface,
+ HANDLE aFileMapping, HANDLE aHostHandle,
+ HDC aDC, HBITMAP aBitmap)
+ : DIBTextureData(aSize, aFormat, aSurface)
+ , mFileMapping(aFileMapping)
+ , mHostHandle(aHostHandle)
+ , mDC(aDC)
+ , mBitmap(aBitmap)
+ {
+ MOZ_COUNT_CTOR(ShmemDIBTextureData);
+ }
+
+ virtual ~ShmemDIBTextureData()
+ {
+ MOZ_COUNT_DTOR(ShmemDIBTextureData);
+
+ // The host side has its own references and handles to this data, we can
+ // safely clear ours.
+ DeallocateData();
+ }
+
+ HANDLE mFileMapping;
+ HANDLE mHostHandle;
+ HDC mDC;
+ HBITMAP mBitmap;
+};
+
+void
+DIBTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSize;
+ aInfo.format = mFormat;
+ aInfo.hasIntermediateBuffer = true;
+ aInfo.hasSynchronization = false;
+ aInfo.supportsMoz2D = true;
+ aInfo.canExposeMappedData = false;
+}
+
+already_AddRefed<gfx::DrawTarget>
+DIBTextureData::BorrowDrawTarget()
+{
+ return gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mSurface, mSize);
+}
+
+DIBTextureData*
+DIBTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ LayersIPCChannel* aAllocator)
+{
+ if (!aAllocator) {
+ return nullptr;
+ }
+ if (aFormat == gfx::SurfaceFormat::UNKNOWN) {
+ return nullptr;
+ }
+ if (aAllocator->IsSameProcess()) {
+ return MemoryDIBTextureData::Create(aSize, aFormat);
+ } else {
+ return ShmemDIBTextureData::Create(aSize, aFormat, aAllocator);
+ }
+}
+
+TextureData*
+MemoryDIBTextureData::CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const
+{
+ if (!aAllocator) {
+ return nullptr;
+ }
+ return MemoryDIBTextureData::Create(mSize, mFormat);
+}
+
+bool
+MemoryDIBTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ MOZ_ASSERT(mSurface);
+ // The host will release this ref when it receives the surface descriptor.
+ // We AddRef in case we die before the host receives the pointer.
+ aOutDescriptor = SurfaceDescriptorDIB(reinterpret_cast<uintptr_t>(mSurface.get()));
+ mSurface->AddRef();
+ return true;
+}
+
+DIBTextureData*
+MemoryDIBTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat)
+{
+ RefPtr<gfxWindowsSurface> surface
+ = new gfxWindowsSurface(aSize, SurfaceFormatToImageFormat(aFormat));
+ if (!surface || surface->CairoStatus()) {
+ NS_WARNING("Could not create DIB surface");
+ return nullptr;
+ }
+
+ return new MemoryDIBTextureData(aSize, aFormat, surface);
+}
+
+bool
+MemoryDIBTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+ RefPtr<gfxImageSurface> imgSurf = mSurface->GetAsImageSurface();
+
+ RefPtr<DataSourceSurface> srcSurf = aSurface->GetDataSurface();
+
+ if (!srcSurf) {
+ gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (DIB).";
+ return false;
+ }
+
+ DataSourceSurface::MappedSurface sourceMap;
+ if (!srcSurf->Map(gfx::DataSourceSurface::READ, &sourceMap)) {
+ gfxCriticalError() << "Failed to map source surface for UpdateFromSurface.";
+ return false;
+ }
+
+ for (int y = 0; y < srcSurf->GetSize().height; y++) {
+ memcpy(imgSurf->Data() + imgSurf->Stride() * y,
+ sourceMap.mData + sourceMap.mStride * y,
+ srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat()));
+ }
+
+ srcSurf->Unmap();
+ return true;
+}
+
+TextureData*
+ShmemDIBTextureData::CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const
+{
+ if (!aAllocator) {
+ return nullptr;
+ }
+ return ShmemDIBTextureData::Create(mSize, mFormat, aAllocator);
+}
+
+bool
+ShmemDIBTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+
+ RefPtr<DataSourceSurface> srcSurf = aSurface->GetDataSurface();
+
+ if (!srcSurf) {
+ gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (DTD).";
+ return false;
+ }
+
+ DataSourceSurface::MappedSurface sourceMap;
+ if (!srcSurf->Map(gfx::DataSourceSurface::READ, &sourceMap)) {
+ gfxCriticalError() << "Failed to map source surface for UpdateFromSurface.";
+ return false;
+ }
+
+ GdiFlush();
+
+ uint32_t stride = mSize.width * BytesPerPixel(mFormat);
+ uint8_t* data = (uint8_t*)::MapViewOfFile(mFileMapping, FILE_MAP_WRITE, 0, 0, stride * mSize.height);
+
+ if (!data) {
+ gfxCriticalError() << "Failed to map view of file for UpdateFromSurface.";
+ srcSurf->Unmap();
+ return false;
+ }
+
+ for (int y = 0; y < srcSurf->GetSize().height; y++) {
+ memcpy(data + stride * y,
+ sourceMap.mData + sourceMap.mStride * y,
+ srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat()));
+ }
+
+ ::UnmapViewOfFile(data);
+
+ srcSurf->Unmap();
+ return true;
+}
+
+bool
+ShmemDIBTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ if (mFormat == gfx::SurfaceFormat::UNKNOWN) {
+ return false;
+ }
+
+ ::GdiFlush();
+ aOutDescriptor = SurfaceDescriptorFileMapping((WindowsHandle)mHostHandle, mFormat, mSize);
+ return true;
+}
+
+DIBTextureData*
+ShmemDIBTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ LayersIPCChannel* aAllocator)
+{
+ MOZ_ASSERT(aAllocator->GetParentPid() != base::ProcessId());
+
+ DWORD mapSize = aSize.width * aSize.height * BytesPerPixel(aFormat);
+ HANDLE fileMapping = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, mapSize, NULL);
+
+ if (!fileMapping) {
+ gfxCriticalError() << "Failed to create memory file mapping for " << mapSize << " bytes.";
+ return nullptr;
+ }
+
+ BITMAPV4HEADER header;
+ memset(&header, 0, sizeof(BITMAPV4HEADER));
+ header.bV4Size = sizeof(BITMAPV4HEADER);
+ header.bV4Width = aSize.width;
+ header.bV4Height = -LONG(aSize.height); // top-to-buttom DIB
+ header.bV4Planes = 1;
+ header.bV4BitCount = 32;
+ header.bV4V4Compression = BI_BITFIELDS;
+ header.bV4RedMask = 0x00FF0000;
+ header.bV4GreenMask = 0x0000FF00;
+ header.bV4BlueMask = 0x000000FF;
+
+ HDC nulldc = ::GetDC(NULL);
+
+ HDC dc = ::CreateCompatibleDC(nulldc);
+
+ ::ReleaseDC(nullptr, nulldc);
+
+ if (!dc) {
+ ::CloseHandle(fileMapping);
+ gfxCriticalError() << "Failed to create DC for bitmap.";
+ return nullptr;
+ }
+
+ void* bits;
+ HBITMAP bitmap = ::CreateDIBSection(dc, (BITMAPINFO*)&header,
+ DIB_RGB_COLORS, &bits,
+ fileMapping, 0);
+
+ if (!bitmap) {
+ gfxCriticalError() << "Failed to create DIB section for a bitmap of size "
+ << aSize << " and mapSize " << mapSize;
+ ::CloseHandle(fileMapping);
+ ::DeleteDC(dc);
+ return nullptr;
+ }
+
+ ::SelectObject(dc, bitmap);
+
+ RefPtr<gfxWindowsSurface> surface = new gfxWindowsSurface(dc, 0);
+ if (surface->CairoStatus())
+ {
+ ::DeleteObject(bitmap);
+ ::DeleteDC(dc);
+ ::CloseHandle(fileMapping);
+ gfxCriticalError() << "Could not create surface, status: "
+ << surface->CairoStatus();
+ return nullptr;
+ }
+
+ HANDLE hostHandle = NULL;
+
+ if (!ipc::DuplicateHandle(fileMapping, aAllocator->GetParentPid(),
+ &hostHandle, 0, DUPLICATE_SAME_ACCESS)) {
+ gfxCriticalError() << "Failed to duplicate handle to parent process for surface.";
+ ::DeleteObject(bitmap);
+ ::DeleteDC(dc);
+ ::CloseHandle(fileMapping);
+ return nullptr;
+ }
+
+ return new ShmemDIBTextureData(aSize, aFormat, surface,
+ fileMapping, hostHandle,
+ dc, bitmap);
+}
+
+
+bool
+TextureHostDirectUpload::Lock()
+{
+ MOZ_ASSERT(!mIsLocked);
+ mIsLocked = true;
+ return true;
+}
+
+void
+TextureHostDirectUpload::Unlock()
+{
+ MOZ_ASSERT(mIsLocked);
+ mIsLocked = false;
+}
+
+void
+TextureHostDirectUpload::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = aCompositor;
+}
+
+void
+TextureHostDirectUpload::DeallocateDeviceData()
+{
+ if (mTextureSource) {
+ mTextureSource->DeallocateDeviceData();
+ }
+}
+
+bool
+TextureHostDirectUpload::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ if (!mTextureSource) {
+ Updated();
+ }
+
+ aTexture = mTextureSource;
+ return !!aTexture;
+}
+
+DIBTextureHost::DIBTextureHost(TextureFlags aFlags,
+ const SurfaceDescriptorDIB& aDescriptor)
+ : TextureHostDirectUpload(aFlags, SurfaceFormat::B8G8R8X8, IntSize())
+{
+ // We added an extra ref for transport, so we shouldn't AddRef now.
+ mSurface =
+ dont_AddRef(reinterpret_cast<gfxWindowsSurface*>(aDescriptor.surface()));
+ MOZ_ASSERT(mSurface);
+
+ mSize = mSurface->GetSize();
+ mFormat = mSurface->GetSurfaceFormat();
+}
+
+void
+DIBTextureHost::UpdatedInternal(const nsIntRegion* aRegion)
+{
+ if (!mCompositor) {
+ // This can happen if we send textures to a compositable that isn't yet
+ // attached to a layer.
+ return;
+ }
+
+ if (!mTextureSource) {
+ mTextureSource = mCompositor->CreateDataTextureSource(mFlags);
+ }
+
+ if (mSurface->CairoStatus()) {
+ gfxWarning() << "Bad Cairo surface internal update " << mSurface->CairoStatus();
+ mTextureSource = nullptr;
+ return;
+ }
+ RefPtr<gfxImageSurface> imgSurf = mSurface->GetAsImageSurface();
+
+ RefPtr<DataSourceSurface> surf = Factory::CreateWrappingDataSourceSurface(imgSurf->Data(), imgSurf->Stride(), mSize, mFormat);
+
+ if (!surf || !mTextureSource->Update(surf, const_cast<nsIntRegion*>(aRegion))) {
+ mTextureSource = nullptr;
+ }
+
+ ReadUnlock();
+}
+
+TextureHostFileMapping::TextureHostFileMapping(TextureFlags aFlags,
+ const SurfaceDescriptorFileMapping& aDescriptor)
+ : TextureHostDirectUpload(aFlags, aDescriptor.format(), aDescriptor.size())
+ , mFileMapping((HANDLE)aDescriptor.handle())
+{
+}
+
+TextureHostFileMapping::~TextureHostFileMapping()
+{
+ ::CloseHandle(mFileMapping);
+}
+
+UserDataKey kFileMappingKey;
+
+static void UnmapFileData(void* aData)
+{
+ MOZ_ASSERT(aData);
+ ::UnmapViewOfFile(aData);
+}
+
+void
+TextureHostFileMapping::UpdatedInternal(const nsIntRegion* aRegion)
+{
+ if (!mCompositor) {
+ // This can happen if we send textures to a compositable that isn't yet
+ // attached to a layer.
+ return;
+ }
+
+ if (!mTextureSource) {
+ mTextureSource = mCompositor->CreateDataTextureSource(mFlags);
+ }
+
+ uint8_t* data = nullptr;
+ int32_t totalBytes = BufferSizeFromDimensions(mSize.width, mSize.height, BytesPerPixel(mFormat));
+ if (totalBytes > 0) {
+ data = (uint8_t*)::MapViewOfFile(mFileMapping, FILE_MAP_READ, 0, 0, totalBytes);
+ }
+
+ if (data) {
+ RefPtr<DataSourceSurface> surf = Factory::CreateWrappingDataSourceSurface(data, mSize.width * BytesPerPixel(mFormat), mSize, mFormat);
+ if (surf) {
+ surf->AddUserData(&kFileMappingKey, data, UnmapFileData);
+ if (!mTextureSource->Update(surf, const_cast<nsIntRegion*>(aRegion))) {
+ mTextureSource = nullptr;
+ }
+ } else {
+ mTextureSource = nullptr;
+ }
+ } else {
+ mTextureSource = nullptr;
+ }
+
+ ReadUnlock();
+}
+
+}
+}