Mike Reed | 60691a5 | 2017-12-05 15:11:24 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2017 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 | |
Mike Klein | c0bd9f9 | 2019-04-23 12:05:21 -0500 | [diff] [blame] | 8 | #include "include/core/SkCanvas.h" |
| 9 | #include "include/core/SkPicture.h" |
| 10 | #include "include/core/SkPictureRecorder.h" |
| 11 | #include "include/core/SkSerialProcs.h" |
| 12 | #include "include/core/SkSurface.h" |
Mike Klein | c0bd9f9 | 2019-04-23 12:05:21 -0500 | [diff] [blame] | 13 | #include "tests/Test.h" |
| 14 | #include "tools/Resources.h" |
| 15 | #include "tools/ToolUtils.h" |
Mike Reed | 60691a5 | 2017-12-05 15:11:24 -0500 | [diff] [blame] | 16 | |
| 17 | static sk_sp<SkImage> picture_to_image(sk_sp<SkPicture> pic) { |
| 18 | SkIRect r = pic->cullRect().round(); |
| 19 | auto surf = SkSurface::MakeRasterN32Premul(r.width(), r.height()); |
| 20 | surf->getCanvas()->drawPicture(pic); |
| 21 | return surf->makeImageSnapshot(); |
| 22 | } |
| 23 | |
| 24 | struct State { |
| 25 | const char* fStr; |
| 26 | SkImage* fImg; |
| 27 | }; |
| 28 | |
| 29 | DEF_TEST(serial_procs_image, reporter) { |
Hal Canary | c465d13 | 2017-12-08 10:21:31 -0500 | [diff] [blame] | 30 | auto src_img = GetResourceAsImage("images/mandrill_128.png"); |
Mike Reed | 60691a5 | 2017-12-05 15:11:24 -0500 | [diff] [blame] | 31 | const char magic_str[] = "magic signature"; |
| 32 | |
| 33 | const SkSerialImageProc sprocs[] = { |
Mike Reed | 64f7376 | 2017-12-08 10:31:52 -0500 | [diff] [blame] | 34 | [](SkImage* img, void* ctx) -> sk_sp<SkData> { return nullptr; }, |
| 35 | [](SkImage* img, void* ctx) { return img->encodeToData(); }, |
| 36 | [](SkImage* img, void* ctx) { return SkData::MakeWithCString(((State*)ctx)->fStr); }, |
Mike Reed | 60691a5 | 2017-12-05 15:11:24 -0500 | [diff] [blame] | 37 | }; |
| 38 | const SkDeserialImageProc dprocs[] = { |
| 39 | [](const void* data, size_t length, void*) -> sk_sp<SkImage> { |
Mike Reed | 60691a5 | 2017-12-05 15:11:24 -0500 | [diff] [blame] | 40 | return nullptr; |
| 41 | }, |
Mike Reed | 64f7376 | 2017-12-08 10:31:52 -0500 | [diff] [blame] | 42 | [](const void* data, size_t length, void*) { |
Mike Reed | 60691a5 | 2017-12-05 15:11:24 -0500 | [diff] [blame] | 43 | return SkImage::MakeFromEncoded(SkData::MakeWithCopy(data, length)); |
| 44 | }, |
| 45 | [](const void* data, size_t length, void* ctx) -> sk_sp<SkImage> { |
| 46 | State* state = (State*)ctx; |
John Stiles | c1c3c6d | 2020-08-15 23:22:53 -0400 | [diff] [blame] | 47 | if (length != strlen(state->fStr)+1 || 0 != memcmp(data, state->fStr, length)) { |
Mike Reed | 60691a5 | 2017-12-05 15:11:24 -0500 | [diff] [blame] | 48 | return nullptr; |
| 49 | } |
| 50 | return sk_ref_sp(state->fImg); |
| 51 | }, |
| 52 | }; |
| 53 | |
| 54 | sk_sp<SkPicture> pic; |
| 55 | { |
| 56 | SkPictureRecorder rec; |
| 57 | SkCanvas* canvas = rec.beginRecording(128, 128); |
Mike Reed | 039f136 | 2021-01-27 21:21:08 -0500 | [diff] [blame] | 58 | canvas->drawImage(src_img, 0, 0); |
Mike Reed | 60691a5 | 2017-12-05 15:11:24 -0500 | [diff] [blame] | 59 | pic = rec.finishRecordingAsPicture(); |
| 60 | } |
| 61 | |
| 62 | State state = { magic_str, src_img.get() }; |
| 63 | |
| 64 | SkSerialProcs sproc; |
| 65 | sproc.fImageCtx = &state; |
| 66 | SkDeserialProcs dproc; |
| 67 | dproc.fImageCtx = &state; |
| 68 | |
| 69 | for (size_t i = 0; i < SK_ARRAY_COUNT(sprocs); ++i) { |
| 70 | sproc.fImageProc = sprocs[i]; |
Mike Reed | 47fdf6c | 2017-12-20 14:12:07 -0500 | [diff] [blame] | 71 | auto data = pic->serialize(&sproc); |
Mike Reed | 60691a5 | 2017-12-05 15:11:24 -0500 | [diff] [blame] | 72 | REPORTER_ASSERT(reporter, data); |
| 73 | |
| 74 | dproc.fImageProc = dprocs[i]; |
Mike Reed | 47fdf6c | 2017-12-20 14:12:07 -0500 | [diff] [blame] | 75 | auto new_pic = SkPicture::MakeFromData(data.get(), &dproc); |
Mike Reed | 60691a5 | 2017-12-05 15:11:24 -0500 | [diff] [blame] | 76 | REPORTER_ASSERT(reporter, data); |
| 77 | |
| 78 | auto dst_img = picture_to_image(new_pic); |
Mike Klein | ea3f014 | 2019-03-20 11:12:10 -0500 | [diff] [blame] | 79 | REPORTER_ASSERT(reporter, ToolUtils::equal_pixels(src_img.get(), dst_img.get())); |
Mike Reed | 60691a5 | 2017-12-05 15:11:24 -0500 | [diff] [blame] | 80 | } |
| 81 | } |
| 82 | |
Mike Reed | 45ab630 | 2017-12-13 11:38:57 -0500 | [diff] [blame] | 83 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
| 84 | |
| 85 | static sk_sp<SkPicture> make_pic(const std::function<void(SkCanvas*)>& drawer) { |
| 86 | SkPictureRecorder rec; |
| 87 | drawer(rec.beginRecording(128, 128)); |
| 88 | return rec.finishRecordingAsPicture(); |
| 89 | } |
| 90 | |
| 91 | static SkSerialProcs makes(SkSerialPictureProc proc, void* ctx = nullptr) { |
| 92 | SkSerialProcs procs; |
| 93 | procs.fPictureProc = proc; |
| 94 | procs.fPictureCtx = ctx; |
| 95 | return procs; |
| 96 | } |
| 97 | |
| 98 | static SkDeserialProcs maked(SkDeserialPictureProc proc, const void* ctx = nullptr) { |
| 99 | SkDeserialProcs procs; |
| 100 | procs.fPictureProc = proc; |
| 101 | procs.fPictureCtx = const_cast<void*>(ctx); |
| 102 | return procs; |
| 103 | } |
| 104 | |
| 105 | // packages the picture's point in the skdata, and records it in the ctx as an array |
| 106 | struct Context { |
| 107 | SkTDArray<SkPicture*> fArray; |
| 108 | SkPicture* fSkipMe = nullptr; |
| 109 | }; |
| 110 | |
| 111 | static sk_sp<SkData> array_serial_proc(SkPicture* pic, void* ctx) { |
| 112 | Context* c = (Context*)ctx; |
| 113 | if (c->fSkipMe == pic) { |
| 114 | return nullptr; |
| 115 | } |
| 116 | *c->fArray.append() = pic; |
| 117 | return SkData::MakeWithCopy(&pic, sizeof(pic)); |
| 118 | } |
| 119 | |
| 120 | static sk_sp<SkPicture> array_deserial_proc(const void* data, size_t size, void* ctx) { |
| 121 | SkASSERT(sizeof(SkPicture*) == size); |
| 122 | |
| 123 | Context* c = (Context*)ctx; |
| 124 | SkPicture* pic; |
| 125 | memcpy(&pic, data, size); |
| 126 | |
| 127 | int index = c->fArray.find(pic); |
| 128 | SkASSERT(index >= 0); |
| 129 | c->fArray.removeShuffle(index); |
| 130 | |
| 131 | return sk_ref_sp(pic); |
| 132 | } |
| 133 | |
| 134 | static void test_pictures(skiatest::Reporter* reporter, sk_sp<SkPicture> p0, int count, |
| 135 | bool skipRoot) { |
| 136 | Context ctx; |
| 137 | if (skipRoot) { |
| 138 | ctx.fSkipMe = p0.get(); |
| 139 | } |
| 140 | |
Mike Reed | 47fdf6c | 2017-12-20 14:12:07 -0500 | [diff] [blame] | 141 | SkSerialProcs sprocs = makes(array_serial_proc, &ctx); |
| 142 | auto d0 = p0->serialize(&sprocs); |
Mike Reed | 45ab630 | 2017-12-13 11:38:57 -0500 | [diff] [blame] | 143 | REPORTER_ASSERT(reporter, ctx.fArray.count() == count); |
Mike Reed | 47fdf6c | 2017-12-20 14:12:07 -0500 | [diff] [blame] | 144 | SkDeserialProcs dprocs = maked(array_deserial_proc, &ctx); |
| 145 | p0 = SkPicture::MakeFromData(d0.get(), &dprocs); |
Mike Reed | 45ab630 | 2017-12-13 11:38:57 -0500 | [diff] [blame] | 146 | REPORTER_ASSERT(reporter, ctx.fArray.count() == 0); |
| 147 | } |
| 148 | |
| 149 | DEF_TEST(serial_procs_picture, reporter) { |
| 150 | |
| 151 | auto p1 = make_pic([](SkCanvas* c) { |
| 152 | // need to be large enough that drawPictures doesn't "unroll" us |
| 153 | for (int i = 0; i < 20; ++i) { |
| 154 | c->drawColor(SK_ColorRED); |
| 155 | } |
| 156 | }); |
| 157 | |
| 158 | // now use custom serialization |
| 159 | auto p0 = make_pic([](SkCanvas* c) { c->drawColor(SK_ColorBLUE); }); |
| 160 | test_pictures(reporter, p0, 1, false); |
| 161 | |
| 162 | // test inside effect |
| 163 | p0 = make_pic([p1](SkCanvas* c) { |
| 164 | SkPaint paint; |
Mike Reed | fae8fce | 2019-04-03 10:27:45 -0400 | [diff] [blame] | 165 | SkTileMode tm = SkTileMode::kClamp; |
Mike Reed | 10a5ff2 | 2021-03-18 17:18:58 -0400 | [diff] [blame] | 166 | paint.setShader(p1->makeShader(tm, tm, SkFilterMode::kNearest)); |
Mike Reed | 45ab630 | 2017-12-13 11:38:57 -0500 | [diff] [blame] | 167 | c->drawPaint(paint); |
| 168 | }); |
| 169 | test_pictures(reporter, p0, 1, true); |
| 170 | |
| 171 | // test nested picture |
| 172 | p0 = make_pic([p1](SkCanvas* c) { |
| 173 | c->drawColor(SK_ColorRED); |
| 174 | c->drawPicture(p1); |
| 175 | c->drawColor(SK_ColorBLUE); |
| 176 | }); |
| 177 | test_pictures(reporter, p0, 1, true); |
| 178 | } |
| 179 | |
Mike Reed | 32ade4c | 2018-09-05 10:51:46 -0400 | [diff] [blame] | 180 | static sk_sp<SkPicture> make_picture(sk_sp<SkTypeface> tf0, sk_sp<SkTypeface> tf1) { |
| 181 | SkPictureRecorder rec; |
| 182 | SkCanvas* canvas = rec.beginRecording(100, 100); |
| 183 | SkPaint paint; |
Hal Canary | 3560ea7 | 2019-01-08 13:01:58 -0500 | [diff] [blame] | 184 | SkFont font; |
| 185 | font.setTypeface(tf0); canvas->drawString("hello", 0, 0, font, paint); |
| 186 | font.setTypeface(tf1); canvas->drawString("hello", 0, 0, font, paint); |
| 187 | font.setTypeface(tf0); canvas->drawString("hello", 0, 0, font, paint); |
| 188 | font.setTypeface(tf1); canvas->drawString("hello", 0, 0, font, paint); |
Mike Reed | 32ade4c | 2018-09-05 10:51:46 -0400 | [diff] [blame] | 189 | return rec.finishRecordingAsPicture(); |
| 190 | } |
| 191 | |
| 192 | DEF_TEST(serial_typeface, reporter) { |
| 193 | auto tf0 = MakeResourceAsTypeface("fonts/hintgasp.ttf"); |
| 194 | auto tf1 = MakeResourceAsTypeface("fonts/Roboto2-Regular_NoEmbed.ttf"); |
| 195 | if (!tf0 || !tf1 || tf0.get() == tf1.get()) { |
| 196 | return; // need two different typefaces for this test to make sense. |
| 197 | } |
| 198 | |
Mike Reed | 32ade4c | 2018-09-05 10:51:46 -0400 | [diff] [blame] | 199 | auto pic = make_picture(tf0, tf1); |
Mike Reed | 32ade4c | 2018-09-05 10:51:46 -0400 | [diff] [blame] | 200 | |
| 201 | int counter = 0; |
| 202 | SkSerialProcs procs; |
| 203 | procs.fTypefaceProc = [](SkTypeface* tf, void* ctx) -> sk_sp<SkData> { |
| 204 | *(int*)ctx += 1; |
| 205 | return nullptr; |
| 206 | }; |
| 207 | procs.fTypefaceCtx = &counter; |
| 208 | auto data = pic->serialize(&procs); |
| 209 | |
| 210 | // The picture has 2 references to each typeface, but we want the serialized picture to |
| 211 | // only have written the data 1 time per typeface. |
| 212 | REPORTER_ASSERT(reporter, counter == 2); |
| 213 | } |
| 214 | |