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