michael@0: /* vim:set ts=2 sw=2 sts=2 et: */ michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: * http://creativecommons.org/publicdomain/zero/1.0/ michael@0: */ michael@0: michael@0: #include "gtest/gtest.h" michael@0: #include "gmock/gmock.h" michael@0: michael@0: #include "mozilla/gfx/2D.h" michael@0: #include "mozilla/gfx/Tools.h" michael@0: #include "mozilla/layers/TextureClient.h" michael@0: #include "mozilla/layers/TextureHost.h" michael@0: #include "mozilla/RefPtr.h" michael@0: #include "gfx2DGlue.h" michael@0: #include "gfxImageSurface.h" michael@0: #include "gfxTypes.h" michael@0: #include "ImageContainer.h" michael@0: #include "mozilla/layers/YCbCrImageDataSerializer.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::gfx; michael@0: using namespace mozilla::layers; michael@0: michael@0: /* michael@0: * This test performs the following actions: michael@0: * - creates a surface michael@0: * - initialize a texture client with it michael@0: * - serilaizes the texture client michael@0: * - deserializes the data into a texture host michael@0: * - reads the surface from the texture host. michael@0: * michael@0: * The surface in the end should be equal to the inital one. michael@0: * This test is run for different combinations of texture types and michael@0: * image formats. michael@0: */ michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: michael@0: // fills the surface with values betwee 0 and 100. michael@0: void SetupSurface(gfxImageSurface* surface) { michael@0: int bpp = gfxASurface::BytePerPixelFromFormat(surface->Format()); michael@0: int stride = surface->Stride(); michael@0: uint8_t val = 0; michael@0: uint8_t* data = surface->Data(); michael@0: for (int y = 0; y < surface->Height(); ++y) { michael@0: for (int x = 0; x < surface->Height(); ++x) { michael@0: for (int b = 0; b < bpp; ++b) { michael@0: data[y*stride + x*bpp + b] = val; michael@0: if (val == 100) { michael@0: val = 0; michael@0: } else { michael@0: ++val; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // return true if two surfaces contain the same data michael@0: void AssertSurfacesEqual(gfxImageSurface* surface1, michael@0: gfxImageSurface* surface2) michael@0: { michael@0: ASSERT_EQ(surface1->GetSize(), surface2->GetSize()); michael@0: ASSERT_EQ(surface1->Format(), surface2->Format()); michael@0: michael@0: uint8_t* data1 = surface1->Data(); michael@0: uint8_t* data2 = surface2->Data(); michael@0: int stride1 = surface1->Stride(); michael@0: int stride2 = surface2->Stride(); michael@0: int bpp = gfxASurface::BytePerPixelFromFormat(surface1->Format()); michael@0: michael@0: for (int y = 0; y < surface1->Height(); ++y) { michael@0: for (int x = 0; x < surface1->Width(); ++x) { michael@0: for (int b = 0; b < bpp; ++b) { michael@0: ASSERT_EQ(data1[y*stride1 + x*bpp + b], michael@0: data2[y*stride2 + x*bpp + b]); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void AssertSurfacesEqual(SourceSurface* surface1, michael@0: SourceSurface* surface2) michael@0: { michael@0: ASSERT_EQ(surface1->GetSize(), surface2->GetSize()); michael@0: ASSERT_EQ(surface1->GetFormat(), surface2->GetFormat()); michael@0: michael@0: RefPtr dataSurface1 = surface1->GetDataSurface(); michael@0: RefPtr dataSurface2 = surface2->GetDataSurface(); michael@0: DataSourceSurface::MappedSurface map1; michael@0: DataSourceSurface::MappedSurface map2; michael@0: if (!dataSurface1->Map(DataSourceSurface::READ, &map1)) { michael@0: return; michael@0: } michael@0: if (!dataSurface2->Map(DataSourceSurface::READ, &map2)) { michael@0: dataSurface1->Unmap(); michael@0: return; michael@0: } michael@0: uint8_t* data1 = map1.mData; michael@0: uint8_t* data2 = map2.mData; michael@0: int stride1 = map1.mStride; michael@0: int stride2 = map2.mStride; michael@0: int bpp = BytesPerPixel(surface1->GetFormat()); michael@0: int width = surface1->GetSize().width; michael@0: int height = surface1->GetSize().height; michael@0: michael@0: for (int y = 0; y < height; ++y) { michael@0: for (int x = 0; x < width; ++x) { michael@0: for (int b = 0; b < bpp; ++b) { michael@0: ASSERT_EQ(data1[y*stride1 + x*bpp + b], michael@0: data2[y*stride2 + x*bpp + b]); michael@0: } michael@0: } michael@0: } michael@0: michael@0: dataSurface1->Unmap(); michael@0: dataSurface2->Unmap(); michael@0: } michael@0: michael@0: // Same as above, for YCbCr surfaces michael@0: void AssertYCbCrSurfacesEqual(PlanarYCbCrData* surface1, michael@0: PlanarYCbCrData* surface2) michael@0: { michael@0: ASSERT_EQ(surface1->mYSize, surface2->mYSize); michael@0: ASSERT_EQ(surface1->mCbCrSize, surface2->mCbCrSize); michael@0: ASSERT_EQ(surface1->mStereoMode, surface2->mStereoMode); michael@0: ASSERT_EQ(surface1->mPicSize, surface2->mPicSize); michael@0: michael@0: for (int y = 0; y < surface1->mYSize.height; ++y) { michael@0: for (int x = 0; x < surface1->mYSize.width; ++x) { michael@0: ASSERT_EQ(surface1->mYChannel[y*surface1->mYStride + x*(1+surface1->mYSkip)], michael@0: surface2->mYChannel[y*surface2->mYStride + x*(1+surface2->mYSkip)]); michael@0: } michael@0: } michael@0: for (int y = 0; y < surface1->mCbCrSize.height; ++y) { michael@0: for (int x = 0; x < surface1->mCbCrSize.width; ++x) { michael@0: ASSERT_EQ(surface1->mCbChannel[y*surface1->mCbCrStride + x*(1+surface1->mCbSkip)], michael@0: surface2->mCbChannel[y*surface2->mCbCrStride + x*(1+surface2->mCbSkip)]); michael@0: ASSERT_EQ(surface1->mCrChannel[y*surface1->mCbCrStride + x*(1+surface1->mCrSkip)], michael@0: surface2->mCrChannel[y*surface2->mCbCrStride + x*(1+surface2->mCrSkip)]); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Run the test for a texture client and a surface michael@0: void TestTextureClientSurface(TextureClient* texture, gfxImageSurface* surface) { michael@0: michael@0: // client allocation michael@0: ASSERT_TRUE(texture->CanExposeDrawTarget()); michael@0: texture->AllocateForSurface(ToIntSize(surface->GetSize())); michael@0: ASSERT_TRUE(texture->IsAllocated()); michael@0: michael@0: ASSERT_TRUE(texture->Lock(OPEN_READ_WRITE)); michael@0: // client painting michael@0: RefPtr dt = texture->GetAsDrawTarget(); michael@0: RefPtr source = michael@0: gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surface); michael@0: dt->CopySurface(source, IntRect(IntPoint(), source->GetSize()), IntPoint()); michael@0: michael@0: RefPtr snapshot = dt->Snapshot(); michael@0: michael@0: AssertSurfacesEqual(snapshot, source); michael@0: michael@0: dt = nullptr; // drop reference before calling Unlock() michael@0: texture->Unlock(); michael@0: michael@0: // client serialization michael@0: SurfaceDescriptor descriptor; michael@0: ASSERT_TRUE(texture->ToSurfaceDescriptor(descriptor)); michael@0: michael@0: ASSERT_NE(descriptor.type(), SurfaceDescriptor::Tnull_t); michael@0: michael@0: // host deserialization michael@0: RefPtr host = CreateBackendIndependentTextureHost(descriptor, nullptr, michael@0: texture->GetFlags()); michael@0: michael@0: ASSERT_TRUE(host.get() != nullptr); michael@0: ASSERT_EQ(host->GetFlags(), texture->GetFlags()); michael@0: michael@0: // host read michael@0: ASSERT_TRUE(host->Lock()); michael@0: RefPtr hostDataSurface = host->GetAsSurface(); michael@0: host->Unlock(); michael@0: michael@0: nsRefPtr hostSurface = michael@0: new gfxImageSurface(hostDataSurface->GetData(), michael@0: ThebesIntSize(hostDataSurface->GetSize()), michael@0: hostDataSurface->Stride(), michael@0: SurfaceFormatToImageFormat(hostDataSurface->GetFormat())); michael@0: AssertSurfacesEqual(surface, hostSurface.get()); michael@0: } michael@0: michael@0: // Same as above, for YCbCr surfaces michael@0: void TestTextureClientYCbCr(TextureClient* client, PlanarYCbCrData& ycbcrData) { michael@0: michael@0: // client allocation michael@0: ASSERT_TRUE(client->AsTextureClientYCbCr() != nullptr); michael@0: TextureClientYCbCr* texture = client->AsTextureClientYCbCr(); michael@0: texture->AllocateForYCbCr(ycbcrData.mYSize, michael@0: ycbcrData.mCbCrSize, michael@0: ycbcrData.mStereoMode); michael@0: ASSERT_TRUE(client->IsAllocated()); michael@0: michael@0: ASSERT_TRUE(client->Lock(OPEN_READ_WRITE)); michael@0: // client painting michael@0: texture->UpdateYCbCr(ycbcrData); michael@0: client->Unlock(); michael@0: michael@0: // client serialization michael@0: SurfaceDescriptor descriptor; michael@0: ASSERT_TRUE(client->ToSurfaceDescriptor(descriptor)); michael@0: michael@0: ASSERT_NE(descriptor.type(), SurfaceDescriptor::Tnull_t); michael@0: michael@0: // host deserialization michael@0: RefPtr textureHost = CreateBackendIndependentTextureHost(descriptor, nullptr, michael@0: client->GetFlags()); michael@0: michael@0: RefPtr host = static_cast(textureHost.get()); michael@0: michael@0: ASSERT_TRUE(host.get() != nullptr); michael@0: ASSERT_EQ(host->GetFlags(), client->GetFlags()); michael@0: michael@0: // host read michael@0: ASSERT_TRUE(host->Lock()); michael@0: michael@0: // This will work iff the compositor is not BasicCompositor michael@0: ASSERT_EQ(host->GetFormat(), mozilla::gfx::SurfaceFormat::YUV); michael@0: michael@0: YCbCrImageDataDeserializer yuvDeserializer(host->GetBuffer(), host->GetBufferSize()); michael@0: ASSERT_TRUE(yuvDeserializer.IsValid()); michael@0: PlanarYCbCrData data; michael@0: data.mYChannel = yuvDeserializer.GetYData(); michael@0: data.mCbChannel = yuvDeserializer.GetCbData(); michael@0: data.mCrChannel = yuvDeserializer.GetCrData(); michael@0: data.mYStride = yuvDeserializer.GetYStride(); michael@0: data.mCbCrStride = yuvDeserializer.GetCbCrStride(); michael@0: data.mStereoMode = yuvDeserializer.GetStereoMode(); michael@0: data.mYSize = yuvDeserializer.GetYSize(); michael@0: data.mCbCrSize = yuvDeserializer.GetCbCrSize(); michael@0: data.mYSkip = 0; michael@0: data.mCbSkip = 0; michael@0: data.mCrSkip = 0; michael@0: data.mPicSize = data.mYSize; michael@0: data.mPicX = 0; michael@0: data.mPicY = 0; michael@0: michael@0: AssertYCbCrSurfacesEqual(&ycbcrData, &data); michael@0: host->Unlock(); michael@0: } michael@0: michael@0: } // namespace michael@0: } // namespace michael@0: michael@0: TEST(Layers, TextureSerialization) { michael@0: // the test is run on all the following image formats michael@0: gfxImageFormat formats[3] = { michael@0: gfxImageFormat::ARGB32, michael@0: gfxImageFormat::RGB24, michael@0: gfxImageFormat::A8, michael@0: }; michael@0: michael@0: for (int f = 0; f < 3; ++f) { michael@0: RefPtr surface = new gfxImageSurface(gfxIntSize(400,300), formats[f]); michael@0: SetupSurface(surface.get()); michael@0: AssertSurfacesEqual(surface, surface); michael@0: michael@0: RefPtr client michael@0: = new MemoryTextureClient(nullptr, michael@0: mozilla::gfx::ImageFormatToSurfaceFormat(surface->Format()), michael@0: gfx::BackendType::CAIRO, michael@0: TEXTURE_DEALLOCATE_CLIENT); michael@0: michael@0: TestTextureClientSurface(client, surface); michael@0: michael@0: // XXX - Test more texture client types. michael@0: } michael@0: } michael@0: michael@0: TEST(Layers, TextureYCbCrSerialization) { michael@0: RefPtr ySurface = new gfxImageSurface(gfxIntSize(400,300), gfxImageFormat::A8); michael@0: RefPtr cbSurface = new gfxImageSurface(gfxIntSize(200,150), gfxImageFormat::A8); michael@0: RefPtr crSurface = new gfxImageSurface(gfxIntSize(200,150), gfxImageFormat::A8); michael@0: SetupSurface(ySurface.get()); michael@0: SetupSurface(cbSurface.get()); michael@0: SetupSurface(crSurface.get()); michael@0: michael@0: PlanarYCbCrData clientData; michael@0: clientData.mYChannel = ySurface->Data(); michael@0: clientData.mCbChannel = cbSurface->Data(); michael@0: clientData.mCrChannel = crSurface->Data(); michael@0: clientData.mYSize = ySurface->GetSize().ToIntSize(); michael@0: clientData.mPicSize = ySurface->GetSize().ToIntSize(); michael@0: clientData.mCbCrSize = cbSurface->GetSize().ToIntSize(); michael@0: clientData.mYStride = ySurface->Stride(); michael@0: clientData.mCbCrStride = cbSurface->Stride(); michael@0: clientData.mStereoMode = StereoMode::MONO; michael@0: clientData.mYSkip = 0; michael@0: clientData.mCbSkip = 0; michael@0: clientData.mCrSkip = 0; michael@0: clientData.mCrSkip = 0; michael@0: clientData.mPicX = 0; michael@0: clientData.mPicX = 0; michael@0: michael@0: RefPtr client michael@0: = new MemoryTextureClient(nullptr, michael@0: mozilla::gfx::SurfaceFormat::YUV, michael@0: gfx::BackendType::CAIRO, michael@0: TEXTURE_DEALLOCATE_CLIENT); michael@0: michael@0: TestTextureClientYCbCr(client, clientData); michael@0: michael@0: // XXX - Test more texture client types. michael@0: }