1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "gtest/gtest.h"
8 
9 #include "mozilla/gfx/2D.h"
10 #include "skia/include/core/SkColorPriv.h"  // for SkPMSrcOver
11 #include "Common.h"
12 #include "Decoder.h"
13 #include "DecoderFactory.h"
14 #include "SourceBuffer.h"
15 #include "SurfaceFilters.h"
16 #include "SurfacePipe.h"
17 
18 using namespace mozilla;
19 using namespace mozilla::gfx;
20 using namespace mozilla::image;
21 
CreateTrivialBlendingDecoder()22 static already_AddRefed<image::Decoder> CreateTrivialBlendingDecoder() {
23   DecoderType decoderType = DecoderFactory::GetDecoderType("image/gif");
24   DecoderFlags decoderFlags = DefaultDecoderFlags();
25   SurfaceFlags surfaceFlags = DefaultSurfaceFlags();
26   auto sourceBuffer = MakeNotNull<RefPtr<SourceBuffer>>();
27   return DecoderFactory::CreateAnonymousDecoder(
28       decoderType, sourceBuffer, Nothing(), decoderFlags, surfaceFlags);
29 }
30 
31 template <typename Func>
WithBlendAnimationFilter(image::Decoder * aDecoder,const AnimationParams & aAnimParams,const IntSize & aOutputSize,Func aFunc)32 RawAccessFrameRef WithBlendAnimationFilter(image::Decoder* aDecoder,
33                                            const AnimationParams& aAnimParams,
34                                            const IntSize& aOutputSize,
35                                            Func aFunc) {
36   DecoderTestHelper decoderHelper(aDecoder);
37 
38   if (!aDecoder->HasAnimation()) {
39     decoderHelper.PostIsAnimated(aAnimParams.mTimeout);
40   }
41 
42   BlendAnimationConfig blendAnim{aDecoder};
43   SurfaceConfig surfaceSink{aDecoder, aOutputSize, SurfaceFormat::OS_RGBA,
44                             false, Some(aAnimParams)};
45 
46   auto func = [&](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
47     aFunc(aDecoder, aFilter);
48   };
49 
50   WithFilterPipeline(aDecoder, func, false, blendAnim, surfaceSink);
51 
52   RawAccessFrameRef current = aDecoder->GetCurrentFrameRef();
53   if (current) {
54     decoderHelper.PostFrameStop(Opacity::SOME_TRANSPARENCY);
55   }
56 
57   return current;
58 }
59 
AssertConfiguringBlendAnimationFilterFails(const IntRect & aFrameRect,const IntSize & aOutputSize)60 void AssertConfiguringBlendAnimationFilterFails(const IntRect& aFrameRect,
61                                                 const IntSize& aOutputSize) {
62   RefPtr<image::Decoder> decoder = CreateTrivialBlendingDecoder();
63   ASSERT_TRUE(decoder != nullptr);
64 
65   AnimationParams animParams{aFrameRect, FrameTimeout::FromRawMilliseconds(0),
66                              0, BlendMethod::SOURCE, DisposalMethod::KEEP};
67   BlendAnimationConfig blendAnim{decoder};
68   SurfaceConfig surfaceSink{decoder, aOutputSize, SurfaceFormat::OS_RGBA, false,
69                             Some(animParams)};
70   AssertConfiguringPipelineFails(decoder, blendAnim, surfaceSink);
71 }
72 
TEST(ImageBlendAnimationFilter,BlendFailsForNegativeFrameRect)73 TEST(ImageBlendAnimationFilter, BlendFailsForNegativeFrameRect)
74 {
75   // A negative frame rect size is disallowed.
76   AssertConfiguringBlendAnimationFilterFails(
77       IntRect(IntPoint(0, 0), IntSize(-1, -1)), IntSize(100, 100));
78 }
79 
TEST(ImageBlendAnimationFilter,WriteFullFirstFrame)80 TEST(ImageBlendAnimationFilter, WriteFullFirstFrame)
81 {
82   RefPtr<image::Decoder> decoder = CreateTrivialBlendingDecoder();
83   ASSERT_TRUE(decoder != nullptr);
84 
85   AnimationParams params{
86       IntRect(0, 0, 100, 100), FrameTimeout::FromRawMilliseconds(0),
87       /* aFrameNum */ 0, BlendMethod::SOURCE, DisposalMethod::KEEP};
88   RawAccessFrameRef frame0 = WithBlendAnimationFilter(
89       decoder, params, IntSize(100, 100),
90       [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
91         CheckWritePixels(aDecoder, aFilter, Some(IntRect(0, 0, 100, 100)));
92       });
93   EXPECT_EQ(IntRect(0, 0, 100, 100), frame0->GetDirtyRect());
94 }
95 
TEST(ImageBlendAnimationFilter,WritePartialFirstFrame)96 TEST(ImageBlendAnimationFilter, WritePartialFirstFrame)
97 {
98   RefPtr<image::Decoder> decoder = CreateTrivialBlendingDecoder();
99   ASSERT_TRUE(decoder != nullptr);
100 
101   AnimationParams params{
102       IntRect(25, 50, 50, 25), FrameTimeout::FromRawMilliseconds(0),
103       /* aFrameNum */ 0, BlendMethod::SOURCE, DisposalMethod::KEEP};
104   RawAccessFrameRef frame0 = WithBlendAnimationFilter(
105       decoder, params, IntSize(100, 100),
106       [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
107         CheckWritePixels(aDecoder, aFilter, Some(IntRect(0, 0, 100, 100)),
108                          Nothing(), Some(IntRect(25, 50, 50, 25)),
109                          Some(IntRect(25, 50, 50, 25)));
110       });
111   EXPECT_EQ(IntRect(0, 0, 100, 100), frame0->GetDirtyRect());
112 }
113 
TestWithBlendAnimationFilterClear(BlendMethod aBlendMethod)114 static void TestWithBlendAnimationFilterClear(BlendMethod aBlendMethod) {
115   RefPtr<image::Decoder> decoder = CreateTrivialBlendingDecoder();
116   ASSERT_TRUE(decoder != nullptr);
117 
118   AnimationParams params0{
119       IntRect(0, 0, 100, 100), FrameTimeout::FromRawMilliseconds(0),
120       /* aFrameNum */ 0, BlendMethod::SOURCE, DisposalMethod::KEEP};
121   RawAccessFrameRef frame0 = WithBlendAnimationFilter(
122       decoder, params0, IntSize(100, 100),
123       [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
124         auto result = aFilter->WritePixels<uint32_t>(
125             [&] { return AsVariant(BGRAColor::Green().AsPixel()); });
126         EXPECT_EQ(WriteState::FINISHED, result);
127       });
128   EXPECT_EQ(IntRect(0, 0, 100, 100), frame0->GetDirtyRect());
129 
130   AnimationParams params1{
131       IntRect(0, 40, 100, 20), FrameTimeout::FromRawMilliseconds(0),
132       /* aFrameNum */ 1, BlendMethod::SOURCE, DisposalMethod::CLEAR};
133   RawAccessFrameRef frame1 = WithBlendAnimationFilter(
134       decoder, params1, IntSize(100, 100),
135       [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
136         auto result = aFilter->WritePixels<uint32_t>(
137             [&] { return AsVariant(BGRAColor::Red().AsPixel()); });
138         EXPECT_EQ(WriteState::FINISHED, result);
139       });
140   EXPECT_EQ(IntRect(0, 40, 100, 20), frame1->GetDirtyRect());
141 
142   ASSERT_TRUE(frame1.get() != nullptr);
143 
144   RefPtr<SourceSurface> surface = frame1->GetSourceSurface();
145   EXPECT_TRUE(RowsAreSolidColor(surface, 0, 40, BGRAColor::Green()));
146   EXPECT_TRUE(RowsAreSolidColor(surface, 40, 20, BGRAColor::Red()));
147   EXPECT_TRUE(RowsAreSolidColor(surface, 60, 40, BGRAColor::Green()));
148 
149   AnimationParams params2{
150       IntRect(0, 50, 100, 20), FrameTimeout::FromRawMilliseconds(0),
151       /* aFrameNum */ 2, aBlendMethod, DisposalMethod::KEEP};
152   RawAccessFrameRef frame2 = WithBlendAnimationFilter(
153       decoder, params2, IntSize(100, 100),
154       [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
155         auto result = aFilter->WritePixels<uint32_t>(
156             [&] { return AsVariant(BGRAColor::Blue().AsPixel()); });
157         EXPECT_EQ(WriteState::FINISHED, result);
158       });
159 
160   ASSERT_TRUE(frame2.get() != nullptr);
161 
162   surface = frame2->GetSourceSurface();
163   EXPECT_TRUE(RowsAreSolidColor(surface, 0, 40, BGRAColor::Green()));
164   EXPECT_TRUE(RowsAreSolidColor(surface, 40, 10, BGRAColor::Transparent()));
165   EXPECT_TRUE(RowsAreSolidColor(surface, 50, 20, BGRAColor::Blue()));
166   EXPECT_TRUE(RowsAreSolidColor(surface, 70, 30, BGRAColor::Green()));
167 }
168 
TEST(ImageBlendAnimationFilter,ClearWithOver)169 TEST(ImageBlendAnimationFilter, ClearWithOver)
170 { TestWithBlendAnimationFilterClear(BlendMethod::OVER); }
171 
TEST(ImageBlendAnimationFilter,ClearWithSource)172 TEST(ImageBlendAnimationFilter, ClearWithSource)
173 { TestWithBlendAnimationFilterClear(BlendMethod::SOURCE); }
174 
TEST(ImageBlendAnimationFilter,KeepWithSource)175 TEST(ImageBlendAnimationFilter, KeepWithSource)
176 {
177   RefPtr<image::Decoder> decoder = CreateTrivialBlendingDecoder();
178   ASSERT_TRUE(decoder != nullptr);
179 
180   AnimationParams params0{
181       IntRect(0, 0, 100, 100), FrameTimeout::FromRawMilliseconds(0),
182       /* aFrameNum */ 0, BlendMethod::SOURCE, DisposalMethod::KEEP};
183   RawAccessFrameRef frame0 = WithBlendAnimationFilter(
184       decoder, params0, IntSize(100, 100),
185       [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
186         auto result = aFilter->WritePixels<uint32_t>(
187             [&] { return AsVariant(BGRAColor::Green().AsPixel()); });
188         EXPECT_EQ(WriteState::FINISHED, result);
189       });
190   EXPECT_EQ(IntRect(0, 0, 100, 100), frame0->GetDirtyRect());
191 
192   AnimationParams params1{
193       IntRect(0, 40, 100, 20), FrameTimeout::FromRawMilliseconds(0),
194       /* aFrameNum */ 1, BlendMethod::SOURCE, DisposalMethod::KEEP};
195   RawAccessFrameRef frame1 = WithBlendAnimationFilter(
196       decoder, params1, IntSize(100, 100),
197       [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
198         auto result = aFilter->WritePixels<uint32_t>(
199             [&] { return AsVariant(BGRAColor::Red().AsPixel()); });
200         EXPECT_EQ(WriteState::FINISHED, result);
201       });
202   EXPECT_EQ(IntRect(0, 40, 100, 20), frame1->GetDirtyRect());
203 
204   ASSERT_TRUE(frame1.get() != nullptr);
205 
206   RefPtr<SourceSurface> surface = frame1->GetSourceSurface();
207   EXPECT_TRUE(RowsAreSolidColor(surface, 0, 40, BGRAColor::Green()));
208   EXPECT_TRUE(RowsAreSolidColor(surface, 40, 20, BGRAColor::Red()));
209   EXPECT_TRUE(RowsAreSolidColor(surface, 60, 40, BGRAColor::Green()));
210 }
211 
TEST(ImageBlendAnimationFilter,KeepWithOver)212 TEST(ImageBlendAnimationFilter, KeepWithOver)
213 {
214   RefPtr<image::Decoder> decoder = CreateTrivialBlendingDecoder();
215   ASSERT_TRUE(decoder != nullptr);
216 
217   AnimationParams params0{
218       IntRect(0, 0, 100, 100), FrameTimeout::FromRawMilliseconds(0),
219       /* aFrameNum */ 0, BlendMethod::SOURCE, DisposalMethod::KEEP};
220   BGRAColor frameColor0(0, 0xFF, 0, 0x40);
221   RawAccessFrameRef frame0 = WithBlendAnimationFilter(
222       decoder, params0, IntSize(100, 100),
223       [&](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
224         auto result = aFilter->WritePixels<uint32_t>(
225             [&] { return AsVariant(frameColor0.AsPixel()); });
226         EXPECT_EQ(WriteState::FINISHED, result);
227       });
228   EXPECT_EQ(IntRect(0, 0, 100, 100), frame0->GetDirtyRect());
229 
230   AnimationParams params1{
231       IntRect(0, 40, 100, 20), FrameTimeout::FromRawMilliseconds(0),
232       /* aFrameNum */ 1, BlendMethod::OVER, DisposalMethod::KEEP};
233   BGRAColor frameColor1(0, 0, 0xFF, 0x80);
234   RawAccessFrameRef frame1 = WithBlendAnimationFilter(
235       decoder, params1, IntSize(100, 100),
236       [&](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
237         auto result = aFilter->WritePixels<uint32_t>(
238             [&] { return AsVariant(frameColor1.AsPixel()); });
239         EXPECT_EQ(WriteState::FINISHED, result);
240       });
241   EXPECT_EQ(IntRect(0, 40, 100, 20), frame1->GetDirtyRect());
242 
243   ASSERT_TRUE(frame1.get() != nullptr);
244 
245   BGRAColor blendedColor(0, 0x20, 0x80, 0xA0, true);  // already premultiplied
246   EXPECT_EQ(SkPMSrcOver(frameColor1.AsPixel(), frameColor0.AsPixel()),
247             blendedColor.AsPixel());
248 
249   RefPtr<SourceSurface> surface = frame1->GetSourceSurface();
250   EXPECT_TRUE(RowsAreSolidColor(surface, 0, 40, frameColor0));
251   EXPECT_TRUE(RowsAreSolidColor(surface, 40, 20, blendedColor));
252   EXPECT_TRUE(RowsAreSolidColor(surface, 60, 40, frameColor0));
253 }
254 
TEST(ImageBlendAnimationFilter,RestorePreviousWithOver)255 TEST(ImageBlendAnimationFilter, RestorePreviousWithOver)
256 {
257   RefPtr<image::Decoder> decoder = CreateTrivialBlendingDecoder();
258   ASSERT_TRUE(decoder != nullptr);
259 
260   AnimationParams params0{
261       IntRect(0, 0, 100, 100), FrameTimeout::FromRawMilliseconds(0),
262       /* aFrameNum */ 0, BlendMethod::SOURCE, DisposalMethod::KEEP};
263   BGRAColor frameColor0(0, 0xFF, 0, 0x40);
264   RawAccessFrameRef frame0 = WithBlendAnimationFilter(
265       decoder, params0, IntSize(100, 100),
266       [&](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
267         auto result = aFilter->WritePixels<uint32_t>(
268             [&] { return AsVariant(frameColor0.AsPixel()); });
269         EXPECT_EQ(WriteState::FINISHED, result);
270       });
271   EXPECT_EQ(IntRect(0, 0, 100, 100), frame0->GetDirtyRect());
272 
273   AnimationParams params1{
274       IntRect(0, 10, 100, 80), FrameTimeout::FromRawMilliseconds(0),
275       /* aFrameNum */ 1, BlendMethod::SOURCE, DisposalMethod::RESTORE_PREVIOUS};
276   BGRAColor frameColor1 = BGRAColor::Green();
277   RawAccessFrameRef frame1 = WithBlendAnimationFilter(
278       decoder, params1, IntSize(100, 100),
279       [&](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
280         auto result = aFilter->WritePixels<uint32_t>(
281             [&] { return AsVariant(frameColor1.AsPixel()); });
282         EXPECT_EQ(WriteState::FINISHED, result);
283       });
284   EXPECT_EQ(IntRect(0, 10, 100, 80), frame1->GetDirtyRect());
285 
286   AnimationParams params2{
287       IntRect(0, 40, 100, 20), FrameTimeout::FromRawMilliseconds(0),
288       /* aFrameNum */ 2, BlendMethod::OVER, DisposalMethod::KEEP};
289   BGRAColor frameColor2(0, 0, 0xFF, 0x80);
290   RawAccessFrameRef frame2 = WithBlendAnimationFilter(
291       decoder, params2, IntSize(100, 100),
292       [&](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
293         auto result = aFilter->WritePixels<uint32_t>(
294             [&] { return AsVariant(frameColor2.AsPixel()); });
295         EXPECT_EQ(WriteState::FINISHED, result);
296       });
297   EXPECT_EQ(IntRect(0, 10, 100, 80), frame2->GetDirtyRect());
298 
299   ASSERT_TRUE(frame2.get() != nullptr);
300 
301   BGRAColor blendedColor(0, 0x20, 0x80, 0xA0, true);  // already premultiplied
302   EXPECT_EQ(SkPMSrcOver(frameColor2.AsPixel(), frameColor0.AsPixel()),
303             blendedColor.AsPixel());
304 
305   RefPtr<SourceSurface> surface = frame2->GetSourceSurface();
306   EXPECT_TRUE(RowsAreSolidColor(surface, 0, 40, frameColor0));
307   EXPECT_TRUE(RowsAreSolidColor(surface, 40, 20, blendedColor));
308   EXPECT_TRUE(RowsAreSolidColor(surface, 60, 40, frameColor0));
309 }
310 
TEST(ImageBlendAnimationFilter,RestorePreviousWithSource)311 TEST(ImageBlendAnimationFilter, RestorePreviousWithSource)
312 {
313   RefPtr<image::Decoder> decoder = CreateTrivialBlendingDecoder();
314   ASSERT_TRUE(decoder != nullptr);
315 
316   AnimationParams params0{
317       IntRect(0, 0, 100, 100), FrameTimeout::FromRawMilliseconds(0),
318       /* aFrameNum */ 0, BlendMethod::SOURCE, DisposalMethod::KEEP};
319   BGRAColor frameColor0(0, 0xFF, 0, 0x40);
320   RawAccessFrameRef frame0 = WithBlendAnimationFilter(
321       decoder, params0, IntSize(100, 100),
322       [&](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
323         auto result = aFilter->WritePixels<uint32_t>(
324             [&] { return AsVariant(frameColor0.AsPixel()); });
325         EXPECT_EQ(WriteState::FINISHED, result);
326       });
327   EXPECT_EQ(IntRect(0, 0, 100, 100), frame0->GetDirtyRect());
328 
329   AnimationParams params1{
330       IntRect(0, 10, 100, 80), FrameTimeout::FromRawMilliseconds(0),
331       /* aFrameNum */ 1, BlendMethod::SOURCE, DisposalMethod::RESTORE_PREVIOUS};
332   BGRAColor frameColor1 = BGRAColor::Green();
333   RawAccessFrameRef frame1 = WithBlendAnimationFilter(
334       decoder, params1, IntSize(100, 100),
335       [&](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
336         auto result = aFilter->WritePixels<uint32_t>(
337             [&] { return AsVariant(frameColor1.AsPixel()); });
338         EXPECT_EQ(WriteState::FINISHED, result);
339       });
340   EXPECT_EQ(IntRect(0, 10, 100, 80), frame1->GetDirtyRect());
341 
342   AnimationParams params2{
343       IntRect(0, 40, 100, 20), FrameTimeout::FromRawMilliseconds(0),
344       /* aFrameNum */ 2, BlendMethod::SOURCE, DisposalMethod::KEEP};
345   BGRAColor frameColor2(0, 0, 0xFF, 0x80);
346   RawAccessFrameRef frame2 = WithBlendAnimationFilter(
347       decoder, params2, IntSize(100, 100),
348       [&](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
349         auto result = aFilter->WritePixels<uint32_t>(
350             [&] { return AsVariant(frameColor2.AsPixel()); });
351         EXPECT_EQ(WriteState::FINISHED, result);
352       });
353   EXPECT_EQ(IntRect(0, 10, 100, 80), frame2->GetDirtyRect());
354 
355   ASSERT_TRUE(frame2.get() != nullptr);
356 
357   RefPtr<SourceSurface> surface = frame2->GetSourceSurface();
358   EXPECT_TRUE(RowsAreSolidColor(surface, 0, 40, frameColor0));
359   EXPECT_TRUE(RowsAreSolidColor(surface, 40, 20, frameColor2));
360   EXPECT_TRUE(RowsAreSolidColor(surface, 60, 40, frameColor0));
361 }
362 
TEST(ImageBlendAnimationFilter,RestorePreviousClearWithSource)363 TEST(ImageBlendAnimationFilter, RestorePreviousClearWithSource)
364 {
365   RefPtr<image::Decoder> decoder = CreateTrivialBlendingDecoder();
366   ASSERT_TRUE(decoder != nullptr);
367 
368   AnimationParams params0{
369       IntRect(0, 0, 100, 100), FrameTimeout::FromRawMilliseconds(0),
370       /* aFrameNum */ 0, BlendMethod::SOURCE, DisposalMethod::KEEP};
371   BGRAColor frameColor0 = BGRAColor::Red();
372   RawAccessFrameRef frame0 = WithBlendAnimationFilter(
373       decoder, params0, IntSize(100, 100),
374       [&](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
375         auto result = aFilter->WritePixels<uint32_t>(
376             [&] { return AsVariant(frameColor0.AsPixel()); });
377         EXPECT_EQ(WriteState::FINISHED, result);
378       });
379   EXPECT_EQ(IntRect(0, 0, 100, 100), frame0->GetDirtyRect());
380 
381   AnimationParams params1{
382       IntRect(0, 0, 100, 20), FrameTimeout::FromRawMilliseconds(0),
383       /* aFrameNum */ 1, BlendMethod::SOURCE, DisposalMethod::CLEAR};
384   BGRAColor frameColor1 = BGRAColor::Blue();
385   RawAccessFrameRef frame1 = WithBlendAnimationFilter(
386       decoder, params1, IntSize(100, 100),
387       [&](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
388         auto result = aFilter->WritePixels<uint32_t>(
389             [&] { return AsVariant(frameColor1.AsPixel()); });
390         EXPECT_EQ(WriteState::FINISHED, result);
391       });
392   EXPECT_EQ(IntRect(0, 0, 100, 20), frame1->GetDirtyRect());
393 
394   AnimationParams params2{
395       IntRect(0, 10, 100, 80), FrameTimeout::FromRawMilliseconds(0),
396       /* aFrameNum */ 2, BlendMethod::SOURCE, DisposalMethod::RESTORE_PREVIOUS};
397   BGRAColor frameColor2 = BGRAColor::Green();
398   RawAccessFrameRef frame2 = WithBlendAnimationFilter(
399       decoder, params2, IntSize(100, 100),
400       [&](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
401         auto result = aFilter->WritePixels<uint32_t>(
402             [&] { return AsVariant(frameColor2.AsPixel()); });
403         EXPECT_EQ(WriteState::FINISHED, result);
404       });
405   EXPECT_EQ(IntRect(0, 0, 100, 90), frame2->GetDirtyRect());
406 
407   AnimationParams params3{
408       IntRect(0, 40, 100, 20), FrameTimeout::FromRawMilliseconds(0),
409       /* aFrameNum */ 3, BlendMethod::SOURCE, DisposalMethod::KEEP};
410   BGRAColor frameColor3 = BGRAColor::Blue();
411   RawAccessFrameRef frame3 = WithBlendAnimationFilter(
412       decoder, params3, IntSize(100, 100),
413       [&](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
414         auto result = aFilter->WritePixels<uint32_t>(
415             [&] { return AsVariant(frameColor3.AsPixel()); });
416         EXPECT_EQ(WriteState::FINISHED, result);
417       });
418   EXPECT_EQ(IntRect(0, 0, 100, 90), frame3->GetDirtyRect());
419 
420   ASSERT_TRUE(frame3.get() != nullptr);
421 
422   RefPtr<SourceSurface> surface = frame3->GetSourceSurface();
423   EXPECT_TRUE(RowsAreSolidColor(surface, 0, 20, BGRAColor::Transparent()));
424   EXPECT_TRUE(RowsAreSolidColor(surface, 20, 20, frameColor0));
425   EXPECT_TRUE(RowsAreSolidColor(surface, 40, 20, frameColor3));
426   EXPECT_TRUE(RowsAreSolidColor(surface, 60, 40, frameColor0));
427 }
428 
TEST(ImageBlendAnimationFilter,PartialOverlapFrameRect)429 TEST(ImageBlendAnimationFilter, PartialOverlapFrameRect)
430 {
431   RefPtr<image::Decoder> decoder = CreateTrivialBlendingDecoder();
432   ASSERT_TRUE(decoder != nullptr);
433 
434   AnimationParams params0{
435       IntRect(-10, -20, 110, 100), FrameTimeout::FromRawMilliseconds(0),
436       /* aFrameNum */ 0, BlendMethod::SOURCE, DisposalMethod::KEEP};
437   BGRAColor frameColor0 = BGRAColor::Red();
438   RawAccessFrameRef frame0 = WithBlendAnimationFilter(
439       decoder, params0, IntSize(100, 100),
440       [&](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
441         auto result = aFilter->WritePixels<uint32_t>(
442             [&] { return AsVariant(frameColor0.AsPixel()); });
443         EXPECT_EQ(WriteState::FINISHED, result);
444       });
445   EXPECT_EQ(IntRect(0, 0, 100, 100), frame0->GetDirtyRect());
446 
447   RefPtr<SourceSurface> surface = frame0->GetSourceSurface();
448   EXPECT_TRUE(RowsAreSolidColor(surface, 0, 80, frameColor0));
449   EXPECT_TRUE(RowsAreSolidColor(surface, 80, 20, BGRAColor::Transparent()));
450 }
451