1/* 2 * Copyright 2013 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 "Resources.h" 9#include "SkAnnotationKeys.h" 10#include "SkCanvas.h" 11#include "SkFixed.h" 12#include "SkFontDescriptor.h" 13#include "SkImage.h" 14#include "SkImageSource.h" 15#include "SkMakeUnique.h" 16#include "SkMallocPixelRef.h" 17#include "SkMatrixPriv.h" 18#include "SkOSFile.h" 19#include "SkReadBuffer.h" 20#include "SkPictureRecorder.h" 21#include "SkShaderBase.h" 22#include "SkTableColorFilter.h" 23#include "SkTemplates.h" 24#include "SkTypeface.h" 25#include "SkWriteBuffer.h" 26#include "SkXfermodeImageFilter.h" 27#include "sk_tool_utils.h" 28#include "Test.h" 29 30static const uint32_t kArraySize = 64; 31static const int kBitmapSize = 256; 32 33class SerializationTest { 34public: 35 36template<typename T> 37static void TestAlignment(T* testObj, skiatest::Reporter* reporter) { 38 // Test memory read/write functions directly 39 unsigned char dataWritten[1024]; 40 size_t bytesWrittenToMemory = testObj->writeToMemory(dataWritten); 41 REPORTER_ASSERT(reporter, SkAlign4(bytesWrittenToMemory) == bytesWrittenToMemory); 42 size_t bytesReadFromMemory = testObj->readFromMemory(dataWritten, bytesWrittenToMemory); 43 REPORTER_ASSERT(reporter, SkAlign4(bytesReadFromMemory) == bytesReadFromMemory); 44} 45}; 46 47template<typename T> struct SerializationUtils { 48 // Generic case for flattenables 49 static void Write(SkWriteBuffer& writer, const T* flattenable) { 50 writer.writeFlattenable(flattenable); 51 } 52 static void Read(SkReadBuffer& reader, T** flattenable) { 53 *flattenable = (T*)reader.readFlattenable(T::GetFlattenableType()); 54 } 55}; 56 57template<> struct SerializationUtils<SkMatrix> { 58 static void Write(SkWriteBuffer& writer, const SkMatrix* matrix) { 59 writer.writeMatrix(*matrix); 60 } 61 static void Read(SkReadBuffer& reader, SkMatrix* matrix) { 62 reader.readMatrix(matrix); 63 } 64}; 65 66template<> struct SerializationUtils<SkPath> { 67 static void Write(SkWriteBuffer& writer, const SkPath* path) { 68 writer.writePath(*path); 69 } 70 static void Read(SkReadBuffer& reader, SkPath* path) { 71 reader.readPath(path); 72 } 73}; 74 75template<> struct SerializationUtils<SkRegion> { 76 static void Write(SkWriteBuffer& writer, const SkRegion* region) { 77 writer.writeRegion(*region); 78 } 79 static void Read(SkReadBuffer& reader, SkRegion* region) { 80 reader.readRegion(region); 81 } 82}; 83 84template<> struct SerializationUtils<SkString> { 85 static void Write(SkWriteBuffer& writer, const SkString* string) { 86 writer.writeString(string->c_str()); 87 } 88 static void Read(SkReadBuffer& reader, SkString* string) { 89 reader.readString(string); 90 } 91}; 92 93template<> struct SerializationUtils<unsigned char> { 94 static void Write(SkWriteBuffer& writer, unsigned char* data, uint32_t arraySize) { 95 writer.writeByteArray(data, arraySize); 96 } 97 static bool Read(SkReadBuffer& reader, unsigned char* data, uint32_t arraySize) { 98 return reader.readByteArray(data, arraySize); 99 } 100}; 101 102template<> struct SerializationUtils<SkColor> { 103 static void Write(SkWriteBuffer& writer, SkColor* data, uint32_t arraySize) { 104 writer.writeColorArray(data, arraySize); 105 } 106 static bool Read(SkReadBuffer& reader, SkColor* data, uint32_t arraySize) { 107 return reader.readColorArray(data, arraySize); 108 } 109}; 110 111template<> struct SerializationUtils<SkColor4f> { 112 static void Write(SkWriteBuffer& writer, SkColor4f* data, uint32_t arraySize) { 113 writer.writeColor4fArray(data, arraySize); 114 } 115 static bool Read(SkReadBuffer& reader, SkColor4f* data, uint32_t arraySize) { 116 return reader.readColor4fArray(data, arraySize); 117 } 118}; 119 120template<> struct SerializationUtils<int32_t> { 121 static void Write(SkWriteBuffer& writer, int32_t* data, uint32_t arraySize) { 122 writer.writeIntArray(data, arraySize); 123 } 124 static bool Read(SkReadBuffer& reader, int32_t* data, uint32_t arraySize) { 125 return reader.readIntArray(data, arraySize); 126 } 127}; 128 129template<> struct SerializationUtils<SkPoint> { 130 static void Write(SkWriteBuffer& writer, SkPoint* data, uint32_t arraySize) { 131 writer.writePointArray(data, arraySize); 132 } 133 static bool Read(SkReadBuffer& reader, SkPoint* data, uint32_t arraySize) { 134 return reader.readPointArray(data, arraySize); 135 } 136}; 137 138template<> struct SerializationUtils<SkScalar> { 139 static void Write(SkWriteBuffer& writer, SkScalar* data, uint32_t arraySize) { 140 writer.writeScalarArray(data, arraySize); 141 } 142 static bool Read(SkReadBuffer& reader, SkScalar* data, uint32_t arraySize) { 143 return reader.readScalarArray(data, arraySize); 144 } 145}; 146 147template<typename T, bool testInvalid> struct SerializationTestUtils { 148 static void InvalidateData(unsigned char* data) {} 149}; 150 151template<> struct SerializationTestUtils<SkString, true> { 152 static void InvalidateData(unsigned char* data) { 153 data[3] |= 0x80; // Reverse sign of 1st integer 154 } 155}; 156 157template<typename T, bool testInvalid> 158static void TestObjectSerializationNoAlign(T* testObj, skiatest::Reporter* reporter) { 159 SkBinaryWriteBuffer writer; 160 SerializationUtils<T>::Write(writer, testObj); 161 size_t bytesWritten = writer.bytesWritten(); 162 REPORTER_ASSERT(reporter, SkAlign4(bytesWritten) == bytesWritten); 163 164 unsigned char dataWritten[1024]; 165 writer.writeToMemory(dataWritten); 166 167 SerializationTestUtils<T, testInvalid>::InvalidateData(dataWritten); 168 169 // Make sure this fails when it should (test with smaller size, but still multiple of 4) 170 SkReadBuffer buffer(dataWritten, bytesWritten - 4); 171 T obj; 172 SerializationUtils<T>::Read(buffer, &obj); 173 REPORTER_ASSERT(reporter, !buffer.isValid()); 174 175 // Make sure this succeeds when it should 176 SkReadBuffer buffer2(dataWritten, bytesWritten); 177 size_t offsetBefore = buffer2.offset(); 178 T obj2; 179 SerializationUtils<T>::Read(buffer2, &obj2); 180 size_t offsetAfter = buffer2.offset(); 181 // This should have succeeded, since there are enough bytes to read this 182 REPORTER_ASSERT(reporter, buffer2.isValid() == !testInvalid); 183 // Note: This following test should always succeed, regardless of whether the buffer is valid, 184 // since if it is invalid, it will simply skip to the end, as if it had read the whole buffer. 185 REPORTER_ASSERT(reporter, offsetAfter - offsetBefore == bytesWritten); 186} 187 188template<typename T> 189static void TestObjectSerialization(T* testObj, skiatest::Reporter* reporter) { 190 TestObjectSerializationNoAlign<T, false>(testObj, reporter); 191 SerializationTest::TestAlignment(testObj, reporter); 192} 193 194template<typename T> 195static T* TestFlattenableSerialization(T* testObj, bool shouldSucceed, 196 skiatest::Reporter* reporter) { 197 SkBinaryWriteBuffer writer; 198 SerializationUtils<T>::Write(writer, testObj); 199 size_t bytesWritten = writer.bytesWritten(); 200 REPORTER_ASSERT(reporter, SkAlign4(bytesWritten) == bytesWritten); 201 202 SkASSERT(bytesWritten <= 4096); 203 unsigned char dataWritten[4096]; 204 writer.writeToMemory(dataWritten); 205 206 // Make sure this fails when it should (test with smaller size, but still multiple of 4) 207 SkReadBuffer buffer(dataWritten, bytesWritten - 4); 208 T* obj = nullptr; 209 SerializationUtils<T>::Read(buffer, &obj); 210 REPORTER_ASSERT(reporter, !buffer.isValid()); 211 REPORTER_ASSERT(reporter, nullptr == obj); 212 213 // Make sure this succeeds when it should 214 SkReadBuffer buffer2(dataWritten, bytesWritten); 215 const unsigned char* peekBefore = static_cast<const unsigned char*>(buffer2.skip(0)); 216 T* obj2 = nullptr; 217 SerializationUtils<T>::Read(buffer2, &obj2); 218 const unsigned char* peekAfter = static_cast<const unsigned char*>(buffer2.skip(0)); 219 if (shouldSucceed) { 220 // This should have succeeded, since there are enough bytes to read this 221 REPORTER_ASSERT(reporter, buffer2.isValid()); 222 REPORTER_ASSERT(reporter, static_cast<size_t>(peekAfter - peekBefore) == bytesWritten); 223 REPORTER_ASSERT(reporter, obj2); 224 } else { 225 // If the deserialization was supposed to fail, make sure it did 226 REPORTER_ASSERT(reporter, !buffer.isValid()); 227 REPORTER_ASSERT(reporter, nullptr == obj2); 228 } 229 230 return obj2; // Return object to perform further validity tests on it 231} 232 233template<typename T> 234static void TestArraySerialization(T* data, skiatest::Reporter* reporter) { 235 SkBinaryWriteBuffer writer; 236 SerializationUtils<T>::Write(writer, data, kArraySize); 237 size_t bytesWritten = writer.bytesWritten(); 238 // This should write the length (in 4 bytes) and the array 239 REPORTER_ASSERT(reporter, (4 + kArraySize * sizeof(T)) == bytesWritten); 240 241 unsigned char dataWritten[2048]; 242 writer.writeToMemory(dataWritten); 243 244 // Make sure this fails when it should 245 SkReadBuffer buffer(dataWritten, bytesWritten); 246 T dataRead[kArraySize]; 247 bool success = SerializationUtils<T>::Read(buffer, dataRead, kArraySize / 2); 248 // This should have failed, since the provided size was too small 249 REPORTER_ASSERT(reporter, !success); 250 251 // Make sure this succeeds when it should 252 SkReadBuffer buffer2(dataWritten, bytesWritten); 253 success = SerializationUtils<T>::Read(buffer2, dataRead, kArraySize); 254 // This should have succeeded, since there are enough bytes to read this 255 REPORTER_ASSERT(reporter, success); 256} 257 258static void TestBitmapSerialization(const SkBitmap& validBitmap, 259 const SkBitmap& invalidBitmap, 260 bool shouldSucceed, 261 skiatest::Reporter* reporter) { 262 sk_sp<SkImage> validImage(SkImage::MakeFromBitmap(validBitmap)); 263 sk_sp<SkImageFilter> validBitmapSource(SkImageSource::Make(std::move(validImage))); 264 sk_sp<SkImage> invalidImage(SkImage::MakeFromBitmap(invalidBitmap)); 265 sk_sp<SkImageFilter> invalidBitmapSource(SkImageSource::Make(std::move(invalidImage))); 266 sk_sp<SkImageFilter> xfermodeImageFilter( 267 SkXfermodeImageFilter::Make(SkBlendMode::kSrcOver, 268 std::move(invalidBitmapSource), 269 std::move(validBitmapSource), nullptr)); 270 271 sk_sp<SkImageFilter> deserializedFilter( 272 TestFlattenableSerialization<SkImageFilter>( 273 xfermodeImageFilter.get(), shouldSucceed, reporter)); 274 275 // Try to render a small bitmap using the invalid deserialized filter 276 // to make sure we don't crash while trying to render it 277 if (shouldSucceed) { 278 SkBitmap bitmap; 279 bitmap.allocN32Pixels(24, 24); 280 SkCanvas canvas(bitmap); 281 canvas.clear(0x00000000); 282 SkPaint paint; 283 paint.setImageFilter(deserializedFilter); 284 canvas.clipRect(SkRect::MakeXYWH(0, 0, SkIntToScalar(24), SkIntToScalar(24))); 285 canvas.drawBitmap(bitmap, 0, 0, &paint); 286 } 287} 288 289static void TestColorFilterSerialization(skiatest::Reporter* reporter) { 290 uint8_t table[256]; 291 for (int i = 0; i < 256; ++i) { 292 table[i] = (i * 41) % 256; 293 } 294 auto colorFilter(SkTableColorFilter::Make(table)); 295 sk_sp<SkColorFilter> copy( 296 TestFlattenableSerialization<SkColorFilter>(colorFilter.get(), true, reporter)); 297} 298 299static SkBitmap draw_picture(SkPicture& picture) { 300 SkBitmap bitmap; 301 bitmap.allocN32Pixels(SkScalarCeilToInt(picture.cullRect().width()), 302 SkScalarCeilToInt(picture.cullRect().height())); 303 SkCanvas canvas(bitmap); 304 picture.playback(&canvas); 305 return bitmap; 306} 307 308static void compare_bitmaps(skiatest::Reporter* reporter, 309 const SkBitmap& b1, const SkBitmap& b2) { 310 REPORTER_ASSERT(reporter, b1.width() == b2.width()); 311 REPORTER_ASSERT(reporter, b1.height() == b2.height()); 312 313 if ((b1.width() != b2.width()) || 314 (b1.height() != b2.height())) { 315 return; 316 } 317 318 int pixelErrors = 0; 319 for (int y = 0; y < b2.height(); ++y) { 320 for (int x = 0; x < b2.width(); ++x) { 321 if (b1.getColor(x, y) != b2.getColor(x, y)) 322 ++pixelErrors; 323 } 324 } 325 REPORTER_ASSERT(reporter, 0 == pixelErrors); 326} 327static void serialize_and_compare_typeface(sk_sp<SkTypeface> typeface, const char* text, 328 skiatest::Reporter* reporter) 329{ 330 // Create a paint with the typeface. 331 SkPaint paint; 332 paint.setColor(SK_ColorGRAY); 333 paint.setTextSize(SkIntToScalar(30)); 334 paint.setTypeface(std::move(typeface)); 335 336 // Paint some text. 337 SkPictureRecorder recorder; 338 SkIRect canvasRect = SkIRect::MakeWH(kBitmapSize, kBitmapSize); 339 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(canvasRect.width()), 340 SkIntToScalar(canvasRect.height()), 341 nullptr, 0); 342 canvas->drawColor(SK_ColorWHITE); 343 canvas->drawText(text, 2, 24, 32, paint); 344 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 345 346 // Serlialize picture and create its clone from stream. 347 SkDynamicMemoryWStream stream; 348 picture->serialize(&stream); 349 std::unique_ptr<SkStream> inputStream(stream.detachAsStream()); 350 sk_sp<SkPicture> loadedPicture(SkPicture::MakeFromStream(inputStream.get())); 351 352 // Draw both original and clone picture and compare bitmaps -- they should be identical. 353 SkBitmap origBitmap = draw_picture(*picture); 354 SkBitmap destBitmap = draw_picture(*loadedPicture); 355 compare_bitmaps(reporter, origBitmap, destBitmap); 356} 357 358static void TestPictureTypefaceSerialization(skiatest::Reporter* reporter) { 359 { 360 // Load typeface from file to test CreateFromFile with index. 361 auto data = GetResourceAsData("fonts/test.ttc"); 362 auto typeface = SkTypeface::MakeFromStream(new SkMemoryStream(std::move(data)), 1); 363 if (!typeface) { 364 INFOF(reporter, "Could not run fontstream test because test.ttc not found."); 365 } else { 366 serialize_and_compare_typeface(std::move(typeface), "A!", reporter); 367 } 368 } 369 370 { 371 // Load typeface as stream to create with axis settings. 372 std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("fonts/Distortable.ttf")); 373 if (!distortable) { 374 INFOF(reporter, "Could not run fontstream test because Distortable.ttf not found."); 375 } else { 376 SkFixed axis = SK_FixedSqrt2; 377 sk_sp<SkTypeface> typeface(SkTypeface::MakeFromFontData( 378 skstd::make_unique<SkFontData>(std::move(distortable), 0, &axis, 1))); 379 if (!typeface) { 380 INFOF(reporter, "Could not run fontstream test because Distortable.ttf not created."); 381 } else { 382 serialize_and_compare_typeface(std::move(typeface), "abc", reporter); 383 } 384 } 385 } 386} 387 388static void setup_bitmap_for_canvas(SkBitmap* bitmap) { 389 bitmap->allocN32Pixels(kBitmapSize, kBitmapSize); 390} 391 392static void make_checkerboard_bitmap(SkBitmap& bitmap) { 393 setup_bitmap_for_canvas(&bitmap); 394 395 SkCanvas canvas(bitmap); 396 canvas.clear(0x00000000); 397 SkPaint darkPaint; 398 darkPaint.setColor(0xFF804020); 399 SkPaint lightPaint; 400 lightPaint.setColor(0xFF244484); 401 const int i = kBitmapSize / 8; 402 const SkScalar f = SkIntToScalar(i); 403 for (int y = 0; y < kBitmapSize; y += i) { 404 for (int x = 0; x < kBitmapSize; x += i) { 405 canvas.save(); 406 canvas.translate(SkIntToScalar(x), SkIntToScalar(y)); 407 canvas.drawRect(SkRect::MakeXYWH(0, 0, f, f), darkPaint); 408 canvas.drawRect(SkRect::MakeXYWH(f, 0, f, f), lightPaint); 409 canvas.drawRect(SkRect::MakeXYWH(0, f, f, f), lightPaint); 410 canvas.drawRect(SkRect::MakeXYWH(f, f, f, f), darkPaint); 411 canvas.restore(); 412 } 413 } 414} 415 416static void draw_something(SkCanvas* canvas) { 417 SkPaint paint; 418 SkBitmap bitmap; 419 make_checkerboard_bitmap(bitmap); 420 421 canvas->save(); 422 canvas->scale(0.5f, 0.5f); 423 canvas->drawBitmap(bitmap, 0, 0, nullptr); 424 canvas->restore(); 425 426 paint.setAntiAlias(true); 427 428 paint.setColor(SK_ColorRED); 429 canvas->drawCircle(SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/3), paint); 430 paint.setColor(SK_ColorBLACK); 431 paint.setTextSize(SkIntToScalar(kBitmapSize/3)); 432 canvas->drawString("Picture", SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/4), paint); 433} 434 435DEF_TEST(Serialization, reporter) { 436 // Test matrix serialization 437 { 438 SkMatrix matrix = SkMatrix::I(); 439 TestObjectSerialization(&matrix, reporter); 440 } 441 442 // Test path serialization 443 { 444 SkPath path; 445 TestObjectSerialization(&path, reporter); 446 } 447 448 // Test region serialization 449 { 450 SkRegion region; 451 TestObjectSerialization(®ion, reporter); 452 } 453 454 // Test color filter serialization 455 { 456 TestColorFilterSerialization(reporter); 457 } 458 459 // Test string serialization 460 { 461 SkString string("string"); 462 TestObjectSerializationNoAlign<SkString, false>(&string, reporter); 463 TestObjectSerializationNoAlign<SkString, true>(&string, reporter); 464 } 465 466 // Test rrect serialization 467 { 468 // SkRRect does not initialize anything. 469 // An uninitialized SkRRect can be serialized, 470 // but will branch on uninitialized data when deserialized. 471 SkRRect rrect; 472 SkRect rect = SkRect::MakeXYWH(1, 2, 20, 30); 473 SkVector corners[4] = { {1, 2}, {2, 3}, {3,4}, {4,5} }; 474 rrect.setRectRadii(rect, corners); 475 SerializationTest::TestAlignment(&rrect, reporter); 476 } 477 478 // Test readByteArray 479 { 480 unsigned char data[kArraySize] = { 1, 2, 3 }; 481 TestArraySerialization(data, reporter); 482 } 483 484 // Test readColorArray 485 { 486 SkColor data[kArraySize] = { SK_ColorBLACK, SK_ColorWHITE, SK_ColorRED }; 487 TestArraySerialization(data, reporter); 488 } 489 490 // Test readColor4fArray 491 { 492 SkColor4f data[kArraySize] = { 493 SkColor4f::FromColor(SK_ColorBLACK), 494 SkColor4f::FromColor(SK_ColorWHITE), 495 SkColor4f::FromColor(SK_ColorRED), 496 { 1.f, 2.f, 4.f, 8.f } 497 }; 498 TestArraySerialization(data, reporter); 499 } 500 501 // Test readIntArray 502 { 503 int32_t data[kArraySize] = { 1, 2, 4, 8 }; 504 TestArraySerialization(data, reporter); 505 } 506 507 // Test readPointArray 508 { 509 SkPoint data[kArraySize] = { {6, 7}, {42, 128} }; 510 TestArraySerialization(data, reporter); 511 } 512 513 // Test readScalarArray 514 { 515 SkScalar data[kArraySize] = { SK_Scalar1, SK_ScalarHalf, SK_ScalarMax }; 516 TestArraySerialization(data, reporter); 517 } 518 519 // Test invalid deserializations 520 { 521 SkImageInfo info = SkImageInfo::MakeN32Premul(kBitmapSize, kBitmapSize); 522 523 SkBitmap validBitmap; 524 validBitmap.setInfo(info); 525 526 // Create a bitmap with a really large height 527 SkBitmap invalidBitmap; 528 invalidBitmap.setInfo(info.makeWH(info.width(), 1000000000)); 529 530 // The deserialization should succeed, and the rendering shouldn't crash, 531 // even when the device fails to initialize, due to its size 532 TestBitmapSerialization(validBitmap, invalidBitmap, true, reporter); 533 } 534 535 // Test simple SkPicture serialization 536 { 537 SkPictureRecorder recorder; 538 draw_something(recorder.beginRecording(SkIntToScalar(kBitmapSize), 539 SkIntToScalar(kBitmapSize), 540 nullptr, 0)); 541 sk_sp<SkPicture> pict(recorder.finishRecordingAsPicture()); 542 543 // Serialize picture 544 SkBinaryWriteBuffer writer; 545 pict->flatten(writer); 546 size_t size = writer.bytesWritten(); 547 SkAutoTMalloc<unsigned char> data(size); 548 writer.writeToMemory(static_cast<void*>(data.get())); 549 550 // Deserialize picture 551 SkReadBuffer reader(static_cast<void*>(data.get()), size); 552 sk_sp<SkPicture> readPict(SkPicture::MakeFromBuffer(reader)); 553 REPORTER_ASSERT(reporter, reader.isValid()); 554 REPORTER_ASSERT(reporter, readPict.get()); 555 } 556 557 TestPictureTypefaceSerialization(reporter); 558} 559 560/////////////////////////////////////////////////////////////////////////////////////////////////// 561#include "SkAnnotation.h" 562 563static sk_sp<SkPicture> copy_picture_via_serialization(SkPicture* src) { 564 SkDynamicMemoryWStream wstream; 565 src->serialize(&wstream); 566 std::unique_ptr<SkStreamAsset> rstream(wstream.detachAsStream()); 567 return SkPicture::MakeFromStream(rstream.get()); 568} 569 570struct AnnotationRec { 571 const SkRect fRect; 572 const char* fKey; 573 sk_sp<SkData> fValue; 574}; 575 576class TestAnnotationCanvas : public SkCanvas { 577 skiatest::Reporter* fReporter; 578 const AnnotationRec* fRec; 579 int fCount; 580 int fCurrIndex; 581 582public: 583 TestAnnotationCanvas(skiatest::Reporter* reporter, const AnnotationRec rec[], int count) 584 : SkCanvas(100, 100) 585 , fReporter(reporter) 586 , fRec(rec) 587 , fCount(count) 588 , fCurrIndex(0) 589 {} 590 591 ~TestAnnotationCanvas() { 592 REPORTER_ASSERT(fReporter, fCount == fCurrIndex); 593 } 594 595protected: 596 void onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) { 597 REPORTER_ASSERT(fReporter, fCurrIndex < fCount); 598 REPORTER_ASSERT(fReporter, rect == fRec[fCurrIndex].fRect); 599 REPORTER_ASSERT(fReporter, !strcmp(key, fRec[fCurrIndex].fKey)); 600 REPORTER_ASSERT(fReporter, value->equals(fRec[fCurrIndex].fValue.get())); 601 fCurrIndex += 1; 602 } 603}; 604 605/* 606 * Test the 3 annotation types by recording them into a picture, serializing, and then playing 607 * them back into another canvas. 608 */ 609DEF_TEST(Annotations, reporter) { 610 SkPictureRecorder recorder; 611 SkCanvas* recordingCanvas = recorder.beginRecording(SkRect::MakeWH(100, 100)); 612 613 const char* str0 = "rect-with-url"; 614 const SkRect r0 = SkRect::MakeWH(10, 10); 615 sk_sp<SkData> d0(SkData::MakeWithCString(str0)); 616 SkAnnotateRectWithURL(recordingCanvas, r0, d0.get()); 617 618 const char* str1 = "named-destination"; 619 const SkRect r1 = SkRect::MakeXYWH(5, 5, 0, 0); // collapsed to a point 620 sk_sp<SkData> d1(SkData::MakeWithCString(str1)); 621 SkAnnotateNamedDestination(recordingCanvas, {r1.x(), r1.y()}, d1.get()); 622 623 const char* str2 = "link-to-destination"; 624 const SkRect r2 = SkRect::MakeXYWH(20, 20, 5, 6); 625 sk_sp<SkData> d2(SkData::MakeWithCString(str2)); 626 SkAnnotateLinkToDestination(recordingCanvas, r2, d2.get()); 627 628 const AnnotationRec recs[] = { 629 { r0, SkAnnotationKeys::URL_Key(), std::move(d0) }, 630 { r1, SkAnnotationKeys::Define_Named_Dest_Key(), std::move(d1) }, 631 { r2, SkAnnotationKeys::Link_Named_Dest_Key(), std::move(d2) }, 632 }; 633 634 sk_sp<SkPicture> pict0(recorder.finishRecordingAsPicture()); 635 sk_sp<SkPicture> pict1(copy_picture_via_serialization(pict0.get())); 636 637 TestAnnotationCanvas canvas(reporter, recs, SK_ARRAY_COUNT(recs)); 638 canvas.drawPicture(pict1); 639} 640