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" 12 #include "SkImageSource.h" 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) { 31 auto src_img = GetResourceAsImage("images/mandrill_128.png"); 32 const char magic_str[] = "magic signature"; 33 34 const SkSerialImageProc sprocs[] = { 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); }, 38 }; 39 const SkDeserialImageProc dprocs[] = { 40 [](const void* data, size_t length, void*) -> sk_sp<SkImage> { 41 SK_ABORT("should not get called"); 42 return nullptr; 43 }, 44 [](const void* data, size_t length, void*) { 45 return SkImage::MakeFromEncoded(SkData::MakeWithCopy(data, length)); 46 }, 47 [](const void* data, size_t length, void* ctx) -> sk_sp<SkImage> { 48 State* state = (State*)ctx; 49 if (length != strlen(state->fStr)+1 || memcmp(data, state->fStr, length)) { 50 return nullptr; 51 } 52 return sk_ref_sp(state->fImg); 53 }, 54 }; 55 56 sk_sp<SkPicture> pic; 57 { 58 SkPictureRecorder rec; 59 SkCanvas* canvas = rec.beginRecording(128, 128); 60 canvas->drawImage(src_img, 0, 0, nullptr); 61 pic = rec.finishRecordingAsPicture(); 62 } 63 64 State state = { magic_str, src_img.get() }; 65 66 SkSerialProcs sproc; 67 sproc.fImageCtx = &state; 68 SkDeserialProcs dproc; 69 dproc.fImageCtx = &state; 70 71 for (size_t i = 0; i < SK_ARRAY_COUNT(sprocs); ++i) { 72 sproc.fImageProc = sprocs[i]; 73 auto data = pic->serialize(&sproc); 74 REPORTER_ASSERT(reporter, data); 75 76 dproc.fImageProc = dprocs[i]; 77 auto new_pic = SkPicture::MakeFromData(data.get(), &dproc); 78 REPORTER_ASSERT(reporter, data); 79 80 auto dst_img = picture_to_image(new_pic); 81 REPORTER_ASSERT(reporter, sk_tool_utils::equal_pixels(src_img.get(), dst_img.get())); 82 } 83 } 84 85 /////////////////////////////////////////////////////////////////////////////////////////////////// 86 87 static sk_sp<SkPicture> make_pic(const std::function<void(SkCanvas*)>& drawer) { 88 SkPictureRecorder rec; 89 drawer(rec.beginRecording(128, 128)); 90 return rec.finishRecordingAsPicture(); 91 } 92 93 static SkSerialProcs makes(SkSerialPictureProc proc, void* ctx = nullptr) { 94 SkSerialProcs procs; 95 procs.fPictureProc = proc; 96 procs.fPictureCtx = ctx; 97 return procs; 98 } 99 100 static SkDeserialProcs maked(SkDeserialPictureProc proc, const void* ctx = nullptr) { 101 SkDeserialProcs procs; 102 procs.fPictureProc = proc; 103 procs.fPictureCtx = const_cast<void*>(ctx); 104 return procs; 105 } 106 107 // packages the picture's point in the skdata, and records it in the ctx as an array 108 struct Context { 109 SkTDArray<SkPicture*> fArray; 110 SkPicture* fSkipMe = nullptr; 111 }; 112 113 static sk_sp<SkData> array_serial_proc(SkPicture* pic, void* ctx) { 114 Context* c = (Context*)ctx; 115 if (c->fSkipMe == pic) { 116 return nullptr; 117 } 118 *c->fArray.append() = pic; 119 return SkData::MakeWithCopy(&pic, sizeof(pic)); 120 } 121 122 static sk_sp<SkPicture> array_deserial_proc(const void* data, size_t size, void* ctx) { 123 SkASSERT(sizeof(SkPicture*) == size); 124 125 Context* c = (Context*)ctx; 126 SkPicture* pic; 127 memcpy(&pic, data, size); 128 129 int index = c->fArray.find(pic); 130 SkASSERT(index >= 0); 131 c->fArray.removeShuffle(index); 132 133 return sk_ref_sp(pic); 134 } 135 136 static void test_pictures(skiatest::Reporter* reporter, sk_sp<SkPicture> p0, int count, 137 bool skipRoot) { 138 Context ctx; 139 if (skipRoot) { 140 ctx.fSkipMe = p0.get(); 141 } 142 143 SkSerialProcs sprocs = makes(array_serial_proc, &ctx); 144 auto d0 = p0->serialize(&sprocs); 145 REPORTER_ASSERT(reporter, ctx.fArray.count() == count); 146 SkDeserialProcs dprocs = maked(array_deserial_proc, &ctx); 147 p0 = SkPicture::MakeFromData(d0.get(), &dprocs); 148 REPORTER_ASSERT(reporter, ctx.fArray.count() == 0); 149 } 150 151 DEF_TEST(serial_procs_picture, reporter) { 152 153 auto p1 = make_pic([](SkCanvas* c) { 154 // need to be large enough that drawPictures doesn't "unroll" us 155 for (int i = 0; i < 20; ++i) { 156 c->drawColor(SK_ColorRED); 157 } 158 }); 159 160 // now use custom serialization 161 auto p0 = make_pic([](SkCanvas* c) { c->drawColor(SK_ColorBLUE); }); 162 test_pictures(reporter, p0, 1, false); 163 164 // test inside effect 165 p0 = make_pic([p1](SkCanvas* c) { 166 SkPaint paint; 167 SkShader::TileMode tm = SkShader::kClamp_TileMode; 168 paint.setShader(SkShader::MakePictureShader(p1, tm, tm, nullptr, nullptr)); 169 c->drawPaint(paint); 170 }); 171 test_pictures(reporter, p0, 1, true); 172 173 // test nested picture 174 p0 = make_pic([p1](SkCanvas* c) { 175 c->drawColor(SK_ColorRED); 176 c->drawPicture(p1); 177 c->drawColor(SK_ColorBLUE); 178 }); 179 test_pictures(reporter, p0, 1, true); 180 } 181 182