зеркало из https://github.com/mozilla/gecko-dev.git
506 строки
14 KiB
C++
506 строки
14 KiB
C++
/* -*- 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.get()->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::SetTextureSourceProvider(TextureSourceProvider* aProvider)
|
|
{
|
|
mProvider = aProvider;
|
|
}
|
|
|
|
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 (!mProvider) {
|
|
// This can happen if we send textures to a compositable that isn't yet
|
|
// attached to a layer.
|
|
return;
|
|
}
|
|
|
|
if (!mTextureSource) {
|
|
mTextureSource = mProvider->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 (!mProvider) {
|
|
// This can happen if we send textures to a compositable that isn't yet
|
|
// attached to a layer.
|
|
return;
|
|
}
|
|
|
|
if (!mTextureSource) {
|
|
mTextureSource = mProvider->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();
|
|
}
|
|
|
|
}
|
|
}
|