|
1 /* vim:set ts=2 sw=2 sts=2 et: */ |
|
2 /* Any copyright is dedicated to the Public Domain. |
|
3 * http://creativecommons.org/publicdomain/zero/1.0/ |
|
4 */ |
|
5 |
|
6 #include "gtest/gtest.h" |
|
7 #include "gmock/gmock.h" |
|
8 |
|
9 #include "mozilla/gfx/2D.h" |
|
10 #include "mozilla/gfx/Tools.h" |
|
11 #include "mozilla/layers/TextureClient.h" |
|
12 #include "mozilla/layers/TextureHost.h" |
|
13 #include "mozilla/RefPtr.h" |
|
14 #include "gfx2DGlue.h" |
|
15 #include "gfxImageSurface.h" |
|
16 #include "gfxTypes.h" |
|
17 #include "ImageContainer.h" |
|
18 #include "mozilla/layers/YCbCrImageDataSerializer.h" |
|
19 |
|
20 using namespace mozilla; |
|
21 using namespace mozilla::gfx; |
|
22 using namespace mozilla::layers; |
|
23 |
|
24 /* |
|
25 * This test performs the following actions: |
|
26 * - creates a surface |
|
27 * - initialize a texture client with it |
|
28 * - serilaizes the texture client |
|
29 * - deserializes the data into a texture host |
|
30 * - reads the surface from the texture host. |
|
31 * |
|
32 * The surface in the end should be equal to the inital one. |
|
33 * This test is run for different combinations of texture types and |
|
34 * image formats. |
|
35 */ |
|
36 |
|
37 namespace mozilla { |
|
38 namespace layers { |
|
39 |
|
40 // fills the surface with values betwee 0 and 100. |
|
41 void SetupSurface(gfxImageSurface* surface) { |
|
42 int bpp = gfxASurface::BytePerPixelFromFormat(surface->Format()); |
|
43 int stride = surface->Stride(); |
|
44 uint8_t val = 0; |
|
45 uint8_t* data = surface->Data(); |
|
46 for (int y = 0; y < surface->Height(); ++y) { |
|
47 for (int x = 0; x < surface->Height(); ++x) { |
|
48 for (int b = 0; b < bpp; ++b) { |
|
49 data[y*stride + x*bpp + b] = val; |
|
50 if (val == 100) { |
|
51 val = 0; |
|
52 } else { |
|
53 ++val; |
|
54 } |
|
55 } |
|
56 } |
|
57 } |
|
58 } |
|
59 |
|
60 // return true if two surfaces contain the same data |
|
61 void AssertSurfacesEqual(gfxImageSurface* surface1, |
|
62 gfxImageSurface* surface2) |
|
63 { |
|
64 ASSERT_EQ(surface1->GetSize(), surface2->GetSize()); |
|
65 ASSERT_EQ(surface1->Format(), surface2->Format()); |
|
66 |
|
67 uint8_t* data1 = surface1->Data(); |
|
68 uint8_t* data2 = surface2->Data(); |
|
69 int stride1 = surface1->Stride(); |
|
70 int stride2 = surface2->Stride(); |
|
71 int bpp = gfxASurface::BytePerPixelFromFormat(surface1->Format()); |
|
72 |
|
73 for (int y = 0; y < surface1->Height(); ++y) { |
|
74 for (int x = 0; x < surface1->Width(); ++x) { |
|
75 for (int b = 0; b < bpp; ++b) { |
|
76 ASSERT_EQ(data1[y*stride1 + x*bpp + b], |
|
77 data2[y*stride2 + x*bpp + b]); |
|
78 } |
|
79 } |
|
80 } |
|
81 } |
|
82 |
|
83 void AssertSurfacesEqual(SourceSurface* surface1, |
|
84 SourceSurface* surface2) |
|
85 { |
|
86 ASSERT_EQ(surface1->GetSize(), surface2->GetSize()); |
|
87 ASSERT_EQ(surface1->GetFormat(), surface2->GetFormat()); |
|
88 |
|
89 RefPtr<DataSourceSurface> dataSurface1 = surface1->GetDataSurface(); |
|
90 RefPtr<DataSourceSurface> dataSurface2 = surface2->GetDataSurface(); |
|
91 DataSourceSurface::MappedSurface map1; |
|
92 DataSourceSurface::MappedSurface map2; |
|
93 if (!dataSurface1->Map(DataSourceSurface::READ, &map1)) { |
|
94 return; |
|
95 } |
|
96 if (!dataSurface2->Map(DataSourceSurface::READ, &map2)) { |
|
97 dataSurface1->Unmap(); |
|
98 return; |
|
99 } |
|
100 uint8_t* data1 = map1.mData; |
|
101 uint8_t* data2 = map2.mData; |
|
102 int stride1 = map1.mStride; |
|
103 int stride2 = map2.mStride; |
|
104 int bpp = BytesPerPixel(surface1->GetFormat()); |
|
105 int width = surface1->GetSize().width; |
|
106 int height = surface1->GetSize().height; |
|
107 |
|
108 for (int y = 0; y < height; ++y) { |
|
109 for (int x = 0; x < width; ++x) { |
|
110 for (int b = 0; b < bpp; ++b) { |
|
111 ASSERT_EQ(data1[y*stride1 + x*bpp + b], |
|
112 data2[y*stride2 + x*bpp + b]); |
|
113 } |
|
114 } |
|
115 } |
|
116 |
|
117 dataSurface1->Unmap(); |
|
118 dataSurface2->Unmap(); |
|
119 } |
|
120 |
|
121 // Same as above, for YCbCr surfaces |
|
122 void AssertYCbCrSurfacesEqual(PlanarYCbCrData* surface1, |
|
123 PlanarYCbCrData* surface2) |
|
124 { |
|
125 ASSERT_EQ(surface1->mYSize, surface2->mYSize); |
|
126 ASSERT_EQ(surface1->mCbCrSize, surface2->mCbCrSize); |
|
127 ASSERT_EQ(surface1->mStereoMode, surface2->mStereoMode); |
|
128 ASSERT_EQ(surface1->mPicSize, surface2->mPicSize); |
|
129 |
|
130 for (int y = 0; y < surface1->mYSize.height; ++y) { |
|
131 for (int x = 0; x < surface1->mYSize.width; ++x) { |
|
132 ASSERT_EQ(surface1->mYChannel[y*surface1->mYStride + x*(1+surface1->mYSkip)], |
|
133 surface2->mYChannel[y*surface2->mYStride + x*(1+surface2->mYSkip)]); |
|
134 } |
|
135 } |
|
136 for (int y = 0; y < surface1->mCbCrSize.height; ++y) { |
|
137 for (int x = 0; x < surface1->mCbCrSize.width; ++x) { |
|
138 ASSERT_EQ(surface1->mCbChannel[y*surface1->mCbCrStride + x*(1+surface1->mCbSkip)], |
|
139 surface2->mCbChannel[y*surface2->mCbCrStride + x*(1+surface2->mCbSkip)]); |
|
140 ASSERT_EQ(surface1->mCrChannel[y*surface1->mCbCrStride + x*(1+surface1->mCrSkip)], |
|
141 surface2->mCrChannel[y*surface2->mCbCrStride + x*(1+surface2->mCrSkip)]); |
|
142 } |
|
143 } |
|
144 } |
|
145 |
|
146 // Run the test for a texture client and a surface |
|
147 void TestTextureClientSurface(TextureClient* texture, gfxImageSurface* surface) { |
|
148 |
|
149 // client allocation |
|
150 ASSERT_TRUE(texture->CanExposeDrawTarget()); |
|
151 texture->AllocateForSurface(ToIntSize(surface->GetSize())); |
|
152 ASSERT_TRUE(texture->IsAllocated()); |
|
153 |
|
154 ASSERT_TRUE(texture->Lock(OPEN_READ_WRITE)); |
|
155 // client painting |
|
156 RefPtr<DrawTarget> dt = texture->GetAsDrawTarget(); |
|
157 RefPtr<SourceSurface> source = |
|
158 gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surface); |
|
159 dt->CopySurface(source, IntRect(IntPoint(), source->GetSize()), IntPoint()); |
|
160 |
|
161 RefPtr<SourceSurface> snapshot = dt->Snapshot(); |
|
162 |
|
163 AssertSurfacesEqual(snapshot, source); |
|
164 |
|
165 dt = nullptr; // drop reference before calling Unlock() |
|
166 texture->Unlock(); |
|
167 |
|
168 // client serialization |
|
169 SurfaceDescriptor descriptor; |
|
170 ASSERT_TRUE(texture->ToSurfaceDescriptor(descriptor)); |
|
171 |
|
172 ASSERT_NE(descriptor.type(), SurfaceDescriptor::Tnull_t); |
|
173 |
|
174 // host deserialization |
|
175 RefPtr<TextureHost> host = CreateBackendIndependentTextureHost(descriptor, nullptr, |
|
176 texture->GetFlags()); |
|
177 |
|
178 ASSERT_TRUE(host.get() != nullptr); |
|
179 ASSERT_EQ(host->GetFlags(), texture->GetFlags()); |
|
180 |
|
181 // host read |
|
182 ASSERT_TRUE(host->Lock()); |
|
183 RefPtr<mozilla::gfx::DataSourceSurface> hostDataSurface = host->GetAsSurface(); |
|
184 host->Unlock(); |
|
185 |
|
186 nsRefPtr<gfxImageSurface> hostSurface = |
|
187 new gfxImageSurface(hostDataSurface->GetData(), |
|
188 ThebesIntSize(hostDataSurface->GetSize()), |
|
189 hostDataSurface->Stride(), |
|
190 SurfaceFormatToImageFormat(hostDataSurface->GetFormat())); |
|
191 AssertSurfacesEqual(surface, hostSurface.get()); |
|
192 } |
|
193 |
|
194 // Same as above, for YCbCr surfaces |
|
195 void TestTextureClientYCbCr(TextureClient* client, PlanarYCbCrData& ycbcrData) { |
|
196 |
|
197 // client allocation |
|
198 ASSERT_TRUE(client->AsTextureClientYCbCr() != nullptr); |
|
199 TextureClientYCbCr* texture = client->AsTextureClientYCbCr(); |
|
200 texture->AllocateForYCbCr(ycbcrData.mYSize, |
|
201 ycbcrData.mCbCrSize, |
|
202 ycbcrData.mStereoMode); |
|
203 ASSERT_TRUE(client->IsAllocated()); |
|
204 |
|
205 ASSERT_TRUE(client->Lock(OPEN_READ_WRITE)); |
|
206 // client painting |
|
207 texture->UpdateYCbCr(ycbcrData); |
|
208 client->Unlock(); |
|
209 |
|
210 // client serialization |
|
211 SurfaceDescriptor descriptor; |
|
212 ASSERT_TRUE(client->ToSurfaceDescriptor(descriptor)); |
|
213 |
|
214 ASSERT_NE(descriptor.type(), SurfaceDescriptor::Tnull_t); |
|
215 |
|
216 // host deserialization |
|
217 RefPtr<TextureHost> textureHost = CreateBackendIndependentTextureHost(descriptor, nullptr, |
|
218 client->GetFlags()); |
|
219 |
|
220 RefPtr<BufferTextureHost> host = static_cast<BufferTextureHost*>(textureHost.get()); |
|
221 |
|
222 ASSERT_TRUE(host.get() != nullptr); |
|
223 ASSERT_EQ(host->GetFlags(), client->GetFlags()); |
|
224 |
|
225 // host read |
|
226 ASSERT_TRUE(host->Lock()); |
|
227 |
|
228 // This will work iff the compositor is not BasicCompositor |
|
229 ASSERT_EQ(host->GetFormat(), mozilla::gfx::SurfaceFormat::YUV); |
|
230 |
|
231 YCbCrImageDataDeserializer yuvDeserializer(host->GetBuffer(), host->GetBufferSize()); |
|
232 ASSERT_TRUE(yuvDeserializer.IsValid()); |
|
233 PlanarYCbCrData data; |
|
234 data.mYChannel = yuvDeserializer.GetYData(); |
|
235 data.mCbChannel = yuvDeserializer.GetCbData(); |
|
236 data.mCrChannel = yuvDeserializer.GetCrData(); |
|
237 data.mYStride = yuvDeserializer.GetYStride(); |
|
238 data.mCbCrStride = yuvDeserializer.GetCbCrStride(); |
|
239 data.mStereoMode = yuvDeserializer.GetStereoMode(); |
|
240 data.mYSize = yuvDeserializer.GetYSize(); |
|
241 data.mCbCrSize = yuvDeserializer.GetCbCrSize(); |
|
242 data.mYSkip = 0; |
|
243 data.mCbSkip = 0; |
|
244 data.mCrSkip = 0; |
|
245 data.mPicSize = data.mYSize; |
|
246 data.mPicX = 0; |
|
247 data.mPicY = 0; |
|
248 |
|
249 AssertYCbCrSurfacesEqual(&ycbcrData, &data); |
|
250 host->Unlock(); |
|
251 } |
|
252 |
|
253 } // namespace |
|
254 } // namespace |
|
255 |
|
256 TEST(Layers, TextureSerialization) { |
|
257 // the test is run on all the following image formats |
|
258 gfxImageFormat formats[3] = { |
|
259 gfxImageFormat::ARGB32, |
|
260 gfxImageFormat::RGB24, |
|
261 gfxImageFormat::A8, |
|
262 }; |
|
263 |
|
264 for (int f = 0; f < 3; ++f) { |
|
265 RefPtr<gfxImageSurface> surface = new gfxImageSurface(gfxIntSize(400,300), formats[f]); |
|
266 SetupSurface(surface.get()); |
|
267 AssertSurfacesEqual(surface, surface); |
|
268 |
|
269 RefPtr<TextureClient> client |
|
270 = new MemoryTextureClient(nullptr, |
|
271 mozilla::gfx::ImageFormatToSurfaceFormat(surface->Format()), |
|
272 gfx::BackendType::CAIRO, |
|
273 TEXTURE_DEALLOCATE_CLIENT); |
|
274 |
|
275 TestTextureClientSurface(client, surface); |
|
276 |
|
277 // XXX - Test more texture client types. |
|
278 } |
|
279 } |
|
280 |
|
281 TEST(Layers, TextureYCbCrSerialization) { |
|
282 RefPtr<gfxImageSurface> ySurface = new gfxImageSurface(gfxIntSize(400,300), gfxImageFormat::A8); |
|
283 RefPtr<gfxImageSurface> cbSurface = new gfxImageSurface(gfxIntSize(200,150), gfxImageFormat::A8); |
|
284 RefPtr<gfxImageSurface> crSurface = new gfxImageSurface(gfxIntSize(200,150), gfxImageFormat::A8); |
|
285 SetupSurface(ySurface.get()); |
|
286 SetupSurface(cbSurface.get()); |
|
287 SetupSurface(crSurface.get()); |
|
288 |
|
289 PlanarYCbCrData clientData; |
|
290 clientData.mYChannel = ySurface->Data(); |
|
291 clientData.mCbChannel = cbSurface->Data(); |
|
292 clientData.mCrChannel = crSurface->Data(); |
|
293 clientData.mYSize = ySurface->GetSize().ToIntSize(); |
|
294 clientData.mPicSize = ySurface->GetSize().ToIntSize(); |
|
295 clientData.mCbCrSize = cbSurface->GetSize().ToIntSize(); |
|
296 clientData.mYStride = ySurface->Stride(); |
|
297 clientData.mCbCrStride = cbSurface->Stride(); |
|
298 clientData.mStereoMode = StereoMode::MONO; |
|
299 clientData.mYSkip = 0; |
|
300 clientData.mCbSkip = 0; |
|
301 clientData.mCrSkip = 0; |
|
302 clientData.mCrSkip = 0; |
|
303 clientData.mPicX = 0; |
|
304 clientData.mPicX = 0; |
|
305 |
|
306 RefPtr<TextureClient> client |
|
307 = new MemoryTextureClient(nullptr, |
|
308 mozilla::gfx::SurfaceFormat::YUV, |
|
309 gfx::BackendType::CAIRO, |
|
310 TEXTURE_DEALLOCATE_CLIENT); |
|
311 |
|
312 TestTextureClientYCbCr(client, clientData); |
|
313 |
|
314 // XXX - Test more texture client types. |
|
315 } |