xref: /aosp_15_r20/external/skia/tests/RecordOptsTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2014 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkBlendMode.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkColorFilter.h"
12 #include "include/core/SkImageFilter.h"
13 #include "include/core/SkImageInfo.h"
14 #include "include/core/SkPaint.h"
15 #include "include/core/SkPicture.h"
16 #include "include/core/SkPictureRecorder.h"
17 #include "include/core/SkRect.h"
18 #include "include/core/SkRefCnt.h"
19 #include "include/core/SkScalar.h"
20 #include "include/core/SkSurface.h"
21 #include "include/effects/SkImageFilters.h"
22 #include "src/core/SkRecord.h"
23 #include "src/core/SkRecordOpts.h"
24 #include "src/core/SkRecorder.h"
25 #include "src/core/SkRecords.h"
26 #include "tests/RecordTestUtils.h"
27 #include "tests/Test.h"
28 
29 #include <array>
30 #include <cstddef>
31 
32 static const int W = 1920, H = 1080;
33 
DEF_TEST(RecordOpts_NoopDraw,r)34 DEF_TEST(RecordOpts_NoopDraw, r) {
35     SkRecord record;
36     SkRecorder recorder(&record, W, H);
37 
38     recorder.drawRect(SkRect::MakeWH(200, 200), SkPaint());
39     recorder.drawRect(SkRect::MakeWH(300, 300), SkPaint());
40     recorder.drawRect(SkRect::MakeWH(100, 100), SkPaint());
41 
42     record.replace<SkRecords::NoOp>(1);  // NoOps should be allowed.
43 
44     SkRecordNoopSaveRestores(&record);
45 
46     REPORTER_ASSERT(r, 2 == count_instances_of_type<SkRecords::DrawRect>(record));
47 }
48 
DEF_TEST(RecordOpts_SingleNoopSaveRestore,r)49 DEF_TEST(RecordOpts_SingleNoopSaveRestore, r) {
50     SkRecord record;
51     SkRecorder recorder(&record, W, H);
52 
53     recorder.save();
54         recorder.clipRect(SkRect::MakeWH(200, 200));
55     recorder.restore();
56 
57     SkRecordNoopSaveRestores(&record);
58     for (int i = 0; i < 3; i++) {
59         assert_type<SkRecords::NoOp>(r, record, i);
60     }
61 }
62 
DEF_TEST(RecordOpts_NoopSaveRestores,r)63 DEF_TEST(RecordOpts_NoopSaveRestores, r) {
64     SkRecord record;
65     SkRecorder recorder(&record, W, H);
66 
67     // The second pass will clean up this pair after the first pass noops all the innards.
68     recorder.save();
69         // A simple pointless pair of save/restore.
70         recorder.save();
71         recorder.restore();
72 
73         // As long as we don't draw in there, everything is a noop.
74         recorder.save();
75             recorder.clipRect(SkRect::MakeWH(200, 200));
76             recorder.clipRect(SkRect::MakeWH(100, 100));
77         recorder.restore();
78     recorder.restore();
79 
80     SkRecordNoopSaveRestores(&record);
81     for (int index = 0; index < record.count(); index++) {
82         assert_type<SkRecords::NoOp>(r, record, index);
83     }
84 }
85 
DEF_TEST(RecordOpts_SaveSaveLayerRestoreRestore,r)86 DEF_TEST(RecordOpts_SaveSaveLayerRestoreRestore, r) {
87     SkRecord record;
88     SkRecorder recorder(&record, W, H);
89 
90     // A previous bug NoOp'd away the first 3 commands.
91     recorder.save();
92         recorder.saveLayer(nullptr, nullptr);
93         recorder.restore();
94     recorder.restore();
95 
96     SkRecordNoopSaveRestores(&record);
97     switch (record.count()) {
98         case 4:
99             assert_type<SkRecords::Save>     (r, record, 0);
100             assert_type<SkRecords::SaveLayer>(r, record, 1);
101             assert_type<SkRecords::Restore>  (r, record, 2);
102             assert_type<SkRecords::Restore>  (r, record, 3);
103             break;
104         case 2:
105             assert_type<SkRecords::SaveLayer>(r, record, 0);
106             assert_type<SkRecords::Restore>  (r, record, 1);
107             break;
108         case 0:
109             break;
110         default:
111             REPORTER_ASSERT(r, false);
112     }
113 }
114 
115 #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
assert_savelayer_restore(skiatest::Reporter * r,SkRecord * record,int i,bool shouldBeNoOped)116 static void assert_savelayer_restore(skiatest::Reporter* r,
117                                      SkRecord* record,
118                                      int i,
119                                      bool shouldBeNoOped) {
120     SkRecordNoopSaveLayerDrawRestores(record);
121     if (shouldBeNoOped) {
122         assert_type<SkRecords::NoOp>(r, *record, i);
123         assert_type<SkRecords::NoOp>(r, *record, i+1);
124     } else {
125         assert_type<SkRecords::SaveLayer>(r, *record, i);
126         assert_type<SkRecords::Restore>(r, *record, i+1);
127     }
128 }
129 
assert_savelayer_draw_restore(skiatest::Reporter * r,SkRecord * record,int i,bool shouldBeNoOped)130 static void assert_savelayer_draw_restore(skiatest::Reporter* r,
131                                           SkRecord* record,
132                                           int i,
133                                           bool shouldBeNoOped) {
134     SkRecordNoopSaveLayerDrawRestores(record);
135     if (shouldBeNoOped) {
136         assert_type<SkRecords::NoOp>(r, *record, i);
137         assert_type<SkRecords::NoOp>(r, *record, i+2);
138     } else {
139         assert_type<SkRecords::SaveLayer>(r, *record, i);
140         assert_type<SkRecords::Restore>(r, *record, i+2);
141     }
142 }
143 
DEF_TEST(RecordOpts_NoopSaveLayerDrawRestore,r)144 DEF_TEST(RecordOpts_NoopSaveLayerDrawRestore, r) {
145     SkRecord record;
146     SkRecorder recorder(&record, W, H);
147 
148     SkRect bounds = SkRect::MakeWH(100, 200);
149     SkRect   draw = SkRect::MakeWH(50, 60);
150 
151     SkPaint alphaOnlyLayerPaint, translucentLayerPaint, xfermodeLayerPaint;
152     alphaOnlyLayerPaint.setColor(0x03000000);  // Only alpha.
153     translucentLayerPaint.setColor(0x03040506);  // Not only alpha.
154     xfermodeLayerPaint.setBlendMode(SkBlendMode::kDstIn);  // Any effect will do.
155 
156     SkPaint opaqueDrawPaint, translucentDrawPaint;
157     opaqueDrawPaint.setColor(0xFF020202);  // Opaque.
158     translucentDrawPaint.setColor(0x0F020202);  // Not opaque.
159 
160     // SaveLayer/Restore removed: No paint = no point.
161     recorder.saveLayer(nullptr, nullptr);
162         recorder.drawRect(draw, opaqueDrawPaint);
163     recorder.restore();
164     assert_savelayer_draw_restore(r, &record, 0, true);
165 
166     // Bounds don't matter.
167     recorder.saveLayer(&bounds, nullptr);
168         recorder.drawRect(draw, opaqueDrawPaint);
169     recorder.restore();
170     assert_savelayer_draw_restore(r, &record, 3, true);
171 
172     // TODO(mtklein): test case with null draw paint
173 
174     // No change: layer paint isn't alpha-only.
175     recorder.saveLayer(nullptr, &translucentLayerPaint);
176         recorder.drawRect(draw, opaqueDrawPaint);
177     recorder.restore();
178     assert_savelayer_draw_restore(r, &record, 6, false);
179 
180     // No change: layer paint has an effect.
181     recorder.saveLayer(nullptr, &xfermodeLayerPaint);
182         recorder.drawRect(draw, opaqueDrawPaint);
183     recorder.restore();
184     assert_savelayer_draw_restore(r, &record, 9, false);
185 
186     // SaveLayer/Restore removed: we can fold in the alpha!
187     recorder.saveLayer(nullptr, &alphaOnlyLayerPaint);
188         recorder.drawRect(draw, translucentDrawPaint);
189     recorder.restore();
190     assert_savelayer_draw_restore(r, &record, 12, true);
191 
192     // SaveLayer/Restore removed: we can fold in the alpha!
193     recorder.saveLayer(nullptr, &alphaOnlyLayerPaint);
194         recorder.drawRect(draw, opaqueDrawPaint);
195     recorder.restore();
196     assert_savelayer_draw_restore(r, &record, 15, true);
197 
198     const SkRecords::DrawRect* drawRect = assert_type<SkRecords::DrawRect>(r, record, 16);
199     REPORTER_ASSERT(r, drawRect != nullptr);
200     REPORTER_ASSERT(r, drawRect->paint.getColor() == 0x03020202);
201 
202     // saveLayer w/ backdrop should NOT go away
203     sk_sp<SkImageFilter> filter(SkImageFilters::Blur(3, 3, nullptr));
204     recorder.saveLayer({ nullptr, nullptr, filter.get(), 0});
205         recorder.drawRect(draw, opaqueDrawPaint);
206     recorder.restore();
207     assert_savelayer_draw_restore(r, &record, 18, false);
208 }
209 #endif
210 
assert_merge_svg_opacity_and_filter_layers(skiatest::Reporter * r,SkRecord * record,int i,bool shouldBeNoOped)211 static void assert_merge_svg_opacity_and_filter_layers(skiatest::Reporter* r,
212                                                        SkRecord* record,
213                                                        int i,
214                                                        bool shouldBeNoOped) {
215     SkRecordMergeSvgOpacityAndFilterLayers(record);
216     if (shouldBeNoOped) {
217         assert_type<SkRecords::NoOp>(r, *record, i);
218         assert_type<SkRecords::NoOp>(r, *record, i + 6);
219     } else {
220         assert_type<SkRecords::SaveLayer>(r, *record, i);
221         assert_type<SkRecords::Restore>(r, *record, i + 6);
222     }
223 }
224 
DEF_TEST(RecordOpts_MergeSvgOpacityAndFilterLayers,r)225 DEF_TEST(RecordOpts_MergeSvgOpacityAndFilterLayers, r) {
226     SkRecord record;
227     SkRecorder recorder(&record, W, H);
228 
229     SkRect bounds = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(200));
230     SkRect clip = SkRect::MakeWH(SkIntToScalar(50), SkIntToScalar(60));
231 
232     SkPaint alphaOnlyLayerPaint;
233     alphaOnlyLayerPaint.setColor(0x03000000);  // Only alpha.
234     SkPaint translucentLayerPaint;
235     translucentLayerPaint.setColor(0x03040506);  // Not only alpha.
236     SkPaint xfermodePaint;
237     xfermodePaint.setBlendMode(SkBlendMode::kDstIn);
238     SkPaint colorFilterPaint;
239     colorFilterPaint.setColorFilter(
240         SkColorFilters::Blend(SK_ColorLTGRAY, SkBlendMode::kSrcIn));
241 
242     SkPaint opaqueFilterLayerPaint;
243     opaqueFilterLayerPaint.setColor(0xFF020202);  // Opaque.
244     SkPaint translucentFilterLayerPaint;
245     translucentFilterLayerPaint.setColor(0x0F020202);  // Not opaque.
246     sk_sp<SkPicture> shape;
247     {
248         SkPictureRecorder picRecorder;
249         SkCanvas* canvas = picRecorder.beginRecording(SkIntToScalar(100), SkIntToScalar(100));
250         SkPaint shapePaint;
251         shapePaint.setColor(SK_ColorWHITE);
252         canvas->drawRect(SkRect::MakeWH(SkIntToScalar(50), SkIntToScalar(50)), shapePaint);
253         shape = picRecorder.finishRecordingAsPicture();
254     }
255     translucentFilterLayerPaint.setImageFilter(SkImageFilters::Picture(shape));
256 
257     int index = 0;
258 
259     {
260         sk_sp<SkImageFilter> filter(SkImageFilters::Blur(3, 3, nullptr));
261         // first (null) should be optimized, 2nd should not
262         SkImageFilter* filters[] = { nullptr, filter.get() };
263 
264         // Any combination of these should cause the pattern to be optimized.
265         SkRect* firstBounds[] = { nullptr, &bounds };
266         SkPaint* firstPaints[] = { nullptr, &alphaOnlyLayerPaint };
267         SkRect* secondBounds[] = { nullptr, &bounds };
268         SkPaint* secondPaints[] = { &opaqueFilterLayerPaint, &translucentFilterLayerPaint };
269 
270         for (auto outerF : filters) {
271             bool outerNoOped = !outerF;
272             for (auto innerF : filters) {
273                 for (size_t i = 0; i < std::size(firstBounds); ++ i) {
274                     for (size_t j = 0; j < std::size(firstPaints); ++j) {
275                         for (size_t k = 0; k < std::size(secondBounds); ++k) {
276                             for (size_t m = 0; m < std::size(secondPaints); ++m) {
277                                 bool innerNoOped = !secondBounds[k] && !secondPaints[m] && !innerF;
278 
279                                 recorder.saveLayer({firstBounds[i], firstPaints[j], outerF, 0});
280                                 recorder.save();
281                                 recorder.clipRect(clip);
282                                 recorder.saveLayer({secondBounds[k], secondPaints[m], innerF, 0});
283                                 recorder.restore();
284                                 recorder.restore();
285                                 recorder.restore();
286                                 assert_merge_svg_opacity_and_filter_layers(r, &record, index,
287                                                                            outerNoOped);
288                             #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
289                                 assert_savelayer_restore(r, &record, index + 3, innerNoOped);
290                             #endif
291                                 index += 7;
292                             }
293                         }
294                     }
295                 }
296             }
297         }
298     }
299 
300     // These should cause the pattern to stay unoptimized:
301     struct {
302         SkPaint* firstPaint;
303         SkPaint* secondPaint;
304     } noChangeTests[] = {
305         // No change: nullptr filter layer paint not implemented.
306         { &alphaOnlyLayerPaint, nullptr },
307         // No change: layer paint is not alpha-only.
308         { &translucentLayerPaint, &opaqueFilterLayerPaint },
309         // No change: layer paint has an xfereffect.
310         { &xfermodePaint, &opaqueFilterLayerPaint },
311         // No change: filter layer paint has an xfereffect.
312         { &alphaOnlyLayerPaint, &xfermodePaint },
313         // No change: layer paint has a color filter.
314         { &colorFilterPaint, &opaqueFilterLayerPaint },
315         // No change: filter layer paint has a color filter (until the optimization accounts for
316         // constant color draws that can filter the color).
317         { &alphaOnlyLayerPaint, &colorFilterPaint }
318     };
319 
320     for (size_t i = 0; i < std::size(noChangeTests); ++i) {
321         recorder.saveLayer(nullptr, noChangeTests[i].firstPaint);
322         recorder.save();
323         recorder.clipRect(clip);
324         recorder.saveLayer(nullptr, noChangeTests[i].secondPaint);
325         recorder.restore();
326         recorder.restore();
327         recorder.restore();
328         assert_merge_svg_opacity_and_filter_layers(r, &record, index, false);
329         index += 7;
330     }
331 
332     // Test the folded alpha value.
333     recorder.saveLayer(nullptr, &alphaOnlyLayerPaint);
334     recorder.save();
335     recorder.clipRect(clip);
336     recorder.saveLayer(nullptr, &opaqueFilterLayerPaint);
337     recorder.restore();
338     recorder.restore();
339     recorder.restore();
340     assert_merge_svg_opacity_and_filter_layers(r, &record, index, true);
341 
342     const SkRecords::SaveLayer* saveLayer = assert_type<SkRecords::SaveLayer>(r, record, index + 3);
343     REPORTER_ASSERT(r, saveLayer != nullptr);
344     REPORTER_ASSERT(r, saveLayer->paint->getColor() == 0x03020202);
345 
346     index += 7;
347 
348     // Test that currently we do not fold alphas for patterns without the clip. This is just not
349     // implemented.
350     recorder.saveLayer(nullptr, &alphaOnlyLayerPaint);
351     recorder.saveLayer(nullptr, &opaqueFilterLayerPaint);
352     recorder.restore();
353     recorder.restore();
354     SkRecordMergeSvgOpacityAndFilterLayers(&record);
355     assert_type<SkRecords::SaveLayer>(r, record, index);
356     assert_type<SkRecords::SaveLayer>(r, record, index + 1);
357     assert_type<SkRecords::Restore>(r, record, index + 2);
358     assert_type<SkRecords::Restore>(r, record, index + 3);
359     index += 4;
360 }
361 
do_draw(SkCanvas * canvas,SkColor color,bool doLayer)362 static void do_draw(SkCanvas* canvas, SkColor color, bool doLayer) {
363     canvas->drawColor(SK_ColorWHITE);
364 
365     SkPaint p;
366     p.setColor(color);
367 
368     if (doLayer) {
369         canvas->saveLayer(nullptr, nullptr);
370         p.setBlendMode(SkBlendMode::kSrc);
371         canvas->drawPaint(p);
372         canvas->restore();
373     } else {
374         canvas->drawPaint(p);
375     }
376 }
377 
is_equal(SkSurface * a,SkSurface * b)378 static bool is_equal(SkSurface* a, SkSurface* b) {
379     const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
380     SkPMColor ca, cb;
381     a->readPixels(info, &ca, sizeof(SkPMColor), 0, 0);
382     b->readPixels(info, &cb, sizeof(SkPMColor), 0, 0);
383     return ca == cb;
384 }
385 
386 // Test drawing w/ and w/o a simple layer (no bounds or paint), so see that drawing ops
387 // that *should* draw the same in fact do.
388 //
389 // Perform this test twice : once directly, and once via a picture
390 //
do_savelayer_srcmode(skiatest::Reporter * r,SkColor color)391 static void do_savelayer_srcmode(skiatest::Reporter* r, SkColor color) {
392     for (int doPicture = 0; doPicture <= 1; ++doPicture) {
393         sk_sp<SkSurface> surf0 = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(10, 10));
394         sk_sp<SkSurface> surf1 = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(10, 10));
395         SkCanvas* c0 = surf0->getCanvas();
396         SkCanvas* c1 = surf1->getCanvas();
397 
398         SkPictureRecorder rec0, rec1;
399         if (doPicture) {
400             c0 = rec0.beginRecording(10, 10);
401             c1 = rec1.beginRecording(10, 10);
402         }
403 
404         do_draw(c0, color, false);
405         do_draw(c1, color, true);
406 
407         if (doPicture) {
408             surf0->getCanvas()->drawPicture(rec0.finishRecordingAsPicture());
409             surf1->getCanvas()->drawPicture(rec1.finishRecordingAsPicture());
410         }
411 
412         // we replicate the assert so we can see which line is reported if there is a failure
413         if (doPicture) {
414             REPORTER_ASSERT(r, is_equal(surf0.get(), surf1.get()));
415         } else {
416             REPORTER_ASSERT(r, is_equal(surf0.get(), surf1.get()));
417         }
418     }
419 }
420 
DEF_TEST(savelayer_srcmode_opaque,r)421 DEF_TEST(savelayer_srcmode_opaque, r) {
422     do_savelayer_srcmode(r, SK_ColorRED);
423 }
424 
DEF_TEST(savelayer_srcmode_alpha,r)425 DEF_TEST(savelayer_srcmode_alpha, r) {
426     do_savelayer_srcmode(r, 0x80FF0000);
427 }
428 
429