Import Cobalt 16.136005 Change-Id: I2b9198982828ad841d50902b145910c0aeb689d3
diff --git a/src/third_party/skia/tests/AAClipTest.cpp b/src/third_party/skia/tests/AAClipTest.cpp index 64e3784..5b156a4 100644 --- a/src/third_party/skia/tests/AAClipTest.cpp +++ b/src/third_party/skia/tests/AAClipTest.cpp
@@ -10,6 +10,8 @@ #include "SkMask.h" #include "SkPath.h" #include "SkRandom.h" +#include "SkRasterClip.h" +#include "SkRRect.h" #include "Test.h" static bool operator==(const SkMask& a, const SkMask& b) { @@ -34,7 +36,6 @@ case SkMask::kLCD16_Format: wbytes <<= 1; break; - case SkMask::kLCD32_Format: case SkMask::kARGB32_Format: wbytes <<= 2; break; @@ -62,7 +63,7 @@ if (rgn.isEmpty()) { mask->fBounds.setEmpty(); mask->fRowBytes = 0; - mask->fImage = NULL; + mask->fImage = nullptr; return; } @@ -185,7 +186,7 @@ path.addOval(SkRect::MakeWH(sheight, sheight)); REPORTER_ASSERT(reporter, sheight == path.getBounds().height()); - clip.setPath(path, NULL, true); + clip.setPath(path, nullptr, true); REPORTER_ASSERT(reporter, height == clip.getBounds().height()); // this is the trimmed height of this cubic (with aa). The critical thing @@ -199,7 +200,7 @@ imoveTo(path, 0, 20); icubicTo(path, 40, 40, 40, 0, 0, 20); REPORTER_ASSERT(reporter, sheight == path.getBounds().height()); - clip.setPath(path, NULL, true); + clip.setPath(path, nullptr, true); REPORTER_ASSERT(reporter, teardrop_height == clip.getBounds().height()); } @@ -228,9 +229,8 @@ REPORTER_ASSERT(reporter, clip1 == clip0); SkMask mask; - mask.fImage = NULL; clip0.copyToMask(&mask); - REPORTER_ASSERT(reporter, NULL == mask.fImage); + REPORTER_ASSERT(reporter, nullptr == mask.fImage); REPORTER_ASSERT(reporter, mask.fBounds.isEmpty()); } @@ -263,17 +263,18 @@ bool nonEmptyAA = clip2.op(clip0, clip1, op); bool nonEmptyBW = rgn2.op(rgn0, rgn1, op); if (nonEmptyAA != nonEmptyBW || clip2.getBounds() != rgn2.getBounds()) { - SkDebugf("[%d %d %d %d] %s [%d %d %d %d] = BW:[%d %d %d %d] AA:[%d %d %d %d]\n", - r0.fLeft, r0.fTop, r0.right(), r0.bottom(), - gRgnOpNames[j], - r1.fLeft, r1.fTop, r1.right(), r1.bottom(), - rgn2.getBounds().fLeft, rgn2.getBounds().fTop, - rgn2.getBounds().right(), rgn2.getBounds().bottom(), - clip2.getBounds().fLeft, clip2.getBounds().fTop, - clip2.getBounds().right(), clip2.getBounds().bottom()); + ERRORF(reporter, "%s %s " + "[%d %d %d %d] %s [%d %d %d %d] = BW:[%d %d %d %d] AA:[%d %d %d %d]\n", + nonEmptyAA == nonEmptyBW ? "true" : "false", + clip2.getBounds() == rgn2.getBounds() ? "true" : "false", + r0.fLeft, r0.fTop, r0.right(), r0.bottom(), + gRgnOpNames[j], + r1.fLeft, r1.fTop, r1.right(), r1.bottom(), + rgn2.getBounds().fLeft, rgn2.getBounds().fTop, + rgn2.getBounds().right(), rgn2.getBounds().bottom(), + clip2.getBounds().fLeft, clip2.getBounds().fTop, + clip2.getBounds().right(), clip2.getBounds().bottom()); } - REPORTER_ASSERT(reporter, nonEmptyAA == nonEmptyBW); - REPORTER_ASSERT(reporter, clip2.getBounds() == rgn2.getBounds()); SkMask maskBW, maskAA; copyToMask(rgn2, &maskBW); @@ -308,7 +309,7 @@ for (int i = 0; i < 2; ++i) { SkAAClip clip; - clip.setPath(path, NULL, 1 == i); + clip.setPath(path, nullptr, 1 == i); SkMask mask; clip.copyToMask(&mask); @@ -334,7 +335,7 @@ // This rect should intersect the clip, but slice-out all of the "soft" parts, // leaving just a rect. const SkIRect ir = SkIRect::MakeLTRB(10, -10, 50, 90); - + clip.op(ir, SkRegion::kIntersect_Op); REPORTER_ASSERT(reporter, clip.getBounds() == SkIRect::MakeLTRB(10, 0, 50, 90)); @@ -342,36 +343,9 @@ REPORTER_ASSERT(reporter, clip.isRect()); } -#include "SkRasterClip.h" - -static void copyToMask(const SkRasterClip& rc, SkMask* mask) { - if (rc.isAA()) { - rc.aaRgn().copyToMask(mask); - } else { - copyToMask(rc.bwRgn(), mask); - } -} - -static bool operator==(const SkRasterClip& a, const SkRasterClip& b) { - if (a.isEmpty()) { - return b.isEmpty(); - } - if (b.isEmpty()) { - return false; - } - - SkMask ma, mb; - copyToMask(a, &ma); - copyToMask(b, &mb); - SkAutoMaskFreeImage aCleanUp(ma.fImage); - SkAutoMaskFreeImage bCleanUp(mb.fImage); - - return ma == mb; -} - static void did_dx_affect(skiatest::Reporter* reporter, const SkScalar dx[], size_t count, bool changed) { - const SkISize baseSize = SkISize::Make(10, 10); + const SkIRect baseBounds = SkIRect::MakeXYWH(0, 0, 10, 10); SkIRect ir = { 0, 0, 10, 10 }; for (size_t i = 0; i < count; ++i) { @@ -382,11 +356,11 @@ SkRasterClip rc1(ir); SkRasterClip rc2(ir); - rc0.op(r, baseSize, SkRegion::kIntersect_Op, false); + rc0.op(r, SkMatrix::I(), baseBounds, SkRegion::kIntersect_Op, false); r.offset(dx[i], 0); - rc1.op(r, baseSize, SkRegion::kIntersect_Op, true); + rc1.op(r, SkMatrix::I(), baseBounds, SkRegion::kIntersect_Op, true); r.offset(-2*dx[i], 0); - rc2.op(r, baseSize, SkRegion::kIntersect_Op, true); + rc2.op(r, SkMatrix::I(), baseBounds, SkRegion::kIntersect_Op, true); REPORTER_ASSERT(reporter, changed != (rc0 == rc1)); REPORTER_ASSERT(reporter, changed != (rc0 == rc2)); @@ -421,6 +395,20 @@ } } +// Building aaclip meant aa-scan-convert a path into a huge clip. +// the old algorithm sized the supersampler to the size of the clip, which overflowed +// its internal 16bit coordinates. The fix was to intersect the clip+path_bounds before +// sizing the supersampler. +// +// Before the fix, the following code would assert in debug builds. +// +static void test_crbug_422693(skiatest::Reporter* reporter) { + SkRasterClip rc(SkIRect::MakeLTRB(-25000, -25000, 25000, 25000)); + SkPath path; + path.addCircle(50, 50, 50); + rc.op(path, SkMatrix::I(), rc.getBounds(), SkRegion::kIntersect_Op, true); +} + DEF_TEST(AAClip, reporter) { test_empty(reporter); test_path_bounds(reporter); @@ -430,4 +418,5 @@ test_regressions(); test_nearly_integral(reporter); test_really_a_rect(reporter); + test_crbug_422693(reporter); }
diff --git a/src/third_party/skia/tests/ARGBImageEncoderTest.cpp b/src/third_party/skia/tests/ARGBImageEncoderTest.cpp deleted file mode 100644 index 4d16f4c..0000000 --- a/src/third_party/skia/tests/ARGBImageEncoderTest.cpp +++ /dev/null
@@ -1,61 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkImageEncoder.h" - -#include "SkBitmap.h" -#include "SkCanvas.h" -#include "SkStream.h" -#include "Test.h" - -static SkColorType gColorTypes[] = { - kRGB_565_SkColorType, - kN32_SkColorType, -}; - -DEF_TEST(ARGBImageEncoder, reporter) { - // Bytes we expect to get: - const int kWidth = 3; - const int kHeight = 5; - const unsigned char comparisonBuffer[] = { - // kHeight rows, each with kWidth pixels, premultiplied ARGB for each pixel - 0xff,0xff,0x00,0x00, 0xff,0xff,0x00,0x00, 0xff,0xff,0x00,0x00, // red - 0xff,0x00,0xff,0x00, 0xff,0x00,0xff,0x00, 0xff,0x00,0xff,0x00, // green - 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, // blue - 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, // blue - 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, // blue - }; - - SkAutoTDelete<SkImageEncoder> enc(CreateARGBImageEncoder()); - for (size_t ctIndex = 0; ctIndex < SK_ARRAY_COUNT(gColorTypes); ++ctIndex) { - // A bitmap that should generate the above bytes: - SkBitmap bitmap; - { - bitmap.allocPixels(SkImageInfo::Make(kWidth, kHeight, gColorTypes[ctIndex], - kOpaque_SkAlphaType)); - bitmap.eraseColor(SK_ColorBLUE); - // Change rows [0,1] from blue to [red,green]. - SkCanvas canvas(bitmap); - SkPaint paint; - paint.setColor(SK_ColorRED); - canvas.drawIRect(SkIRect::MakeLTRB(0, 0, kWidth, 1), paint); - paint.setColor(SK_ColorGREEN); - canvas.drawIRect(SkIRect::MakeLTRB(0, 1, kWidth, 2), paint); - } - - // Transform the bitmap. - int bufferSize = bitmap.width() * bitmap.height() * 4; - SkAutoMalloc pixelBufferManager(bufferSize); - char *pixelBuffer = static_cast<char *>(pixelBufferManager.get()); - SkMemoryWStream out(pixelBuffer, bufferSize); - REPORTER_ASSERT(reporter, enc->encodeStream(&out, bitmap, SkImageEncoder::kDefaultQuality)); - - // Confirm we got the expected results. - REPORTER_ASSERT(reporter, bufferSize == sizeof(comparisonBuffer)); - REPORTER_ASSERT(reporter, memcmp(pixelBuffer, comparisonBuffer, bufferSize) == 0); - } -}
diff --git a/src/third_party/skia/tests/AnnotationTest.cpp b/src/third_party/skia/tests/AnnotationTest.cpp index 274d53e..cc2fd1f 100644 --- a/src/third_party/skia/tests/AnnotationTest.cpp +++ b/src/third_party/skia/tests/AnnotationTest.cpp
@@ -1,4 +1,3 @@ - /* * Copyright 2011 Google Inc. * @@ -8,8 +7,8 @@ #include "SkAnnotation.h" #include "SkCanvas.h" #include "SkData.h" -#include "SkPDFDevice.h" -#include "SkPDFDocument.h" +#include "SkDocument.h" +#include "SkStream.h" #include "Test.h" /** Returns true if data (may contain null characters) contains needle (null @@ -32,62 +31,45 @@ SkCanvas canvas(bm); SkRect r = SkRect::MakeWH(SkIntToScalar(10), SkIntToScalar(10)); - SkAutoDataUnref data(SkData::NewWithCString("http://www.gooogle.com")); + sk_sp<SkData> data(SkData::MakeWithCString("http://www.gooogle.com")); REPORTER_ASSERT(reporter, 0 == *bm.getAddr32(0, 0)); SkAnnotateRectWithURL(&canvas, r, data.get()); REPORTER_ASSERT(reporter, 0 == *bm.getAddr32(0, 0)); } -struct testCase { - SkPDFDocument::Flags flags; - bool expectAnnotations; -}; - DEF_TEST(Annotation_PdfLink, reporter) { - SkISize size = SkISize::Make(612, 792); - SkMatrix initialTransform; - initialTransform.reset(); - SkPDFDevice device(size, size, initialTransform); - SkCanvas canvas(&device); + REQUIRE_PDF_DOCUMENT(Annotation_PdfLink, reporter); + SkDynamicMemoryWStream outStream; + sk_sp<SkDocument> doc(SkDocument::MakePDF(&outStream)); + SkCanvas* canvas = doc->beginPage(612.0f, 792.0f); + REPORTER_ASSERT(reporter, canvas); SkRect r = SkRect::MakeXYWH(SkIntToScalar(72), SkIntToScalar(72), SkIntToScalar(288), SkIntToScalar(72)); - SkAutoDataUnref data(SkData::NewWithCString("http://www.gooogle.com")); - SkAnnotateRectWithURL(&canvas, r, data.get()); + sk_sp<SkData> data(SkData::MakeWithCString("http://www.gooogle.com")); + SkAnnotateRectWithURL(canvas, r, data.get()); - testCase tests[] = {{(SkPDFDocument::Flags)0, true}, - {SkPDFDocument::kNoLinks_Flags, false}}; - for (size_t testNum = 0; testNum < SK_ARRAY_COUNT(tests); testNum++) { - SkPDFDocument doc(tests[testNum].flags); - doc.appendPage(&device); - SkDynamicMemoryWStream outStream; - doc.emitPDF(&outStream); - SkAutoDataUnref out(outStream.copyToData()); - const char* rawOutput = (const char*)out->data(); + doc->close(); + sk_sp<SkData> out = outStream.detachAsData(); + const char* rawOutput = (const char*)out->data(); - REPORTER_ASSERT(reporter, - ContainsString(rawOutput, out->size(), "/Annots ") - == tests[testNum].expectAnnotations); - } + REPORTER_ASSERT(reporter, ContainsString(rawOutput, out->size(), "/Annots ")); } DEF_TEST(Annotation_NamedDestination, reporter) { - SkISize size = SkISize::Make(612, 792); - SkMatrix initialTransform; - initialTransform.reset(); - SkPDFDevice device(size, size, initialTransform); - SkCanvas canvas(&device); + REQUIRE_PDF_DOCUMENT(Annotation_NamedDestination, reporter); + SkDynamicMemoryWStream outStream; + sk_sp<SkDocument> doc(SkDocument::MakePDF(&outStream)); + SkCanvas* canvas = doc->beginPage(612.0f, 792.0f); + REPORTER_ASSERT(reporter, canvas); SkPoint p = SkPoint::Make(SkIntToScalar(72), SkIntToScalar(72)); - SkAutoDataUnref data(SkData::NewWithCString("example")); - SkAnnotateNamedDestination(&canvas, p, data.get()); + sk_sp<SkData> data(SkData::MakeWithCString("example")); + SkAnnotateNamedDestination(canvas, p, data.get()); - SkPDFDocument doc; - doc.appendPage(&device); - SkDynamicMemoryWStream outStream; - doc.emitPDF(&outStream); - SkAutoDataUnref out(outStream.copyToData()); + doc->close(); + sk_sp<SkData> out = outStream.detachAsData(); const char* rawOutput = (const char*)out->data(); REPORTER_ASSERT(reporter,
diff --git a/src/third_party/skia/tests/ApplyGammaTest.cpp b/src/third_party/skia/tests/ApplyGammaTest.cpp new file mode 100644 index 0000000..83b5968 --- /dev/null +++ b/src/third_party/skia/tests/ApplyGammaTest.cpp
@@ -0,0 +1,155 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <initializer_list> +#include "Test.h" + +#if SK_SUPPORT_GPU +#include "GrContext.h" + +#include "SkCanvas.h" +#include "SkColorFilter.h" +#include "SkSurface.h" +#include "SkUtils.h" + +/** convert 0..1 linear value to 0..1 srgb */ +static float linear_to_srgb(float linear) { + if (linear <= 0.0031308) { + return linear * 12.92f; + } else { + return 1.055f * powf(linear, 1.f / 2.4f) - 0.055f; + } +} + +/** convert 0..1 srgb value to 0..1 linear */ +static float srgb_to_linear(float srgb) { + if (srgb <= 0.04045f) { + return srgb / 12.92f; + } else { + return powf((srgb + 0.055f) / 1.055f, 2.4f); + } +} + +bool check_gamma(uint32_t src, uint32_t dst, bool toSRGB, float error, + uint32_t* expected) { + bool result = true; + uint32_t expectedColor = src & 0xff000000; + + // Alpha should always be exactly preserved. + if ((src & 0xff000000) != (dst & 0xff000000)) { + result = false; + } + + // need to unpremul before we can perform srgb magic + float invScale = 0; + float alpha = SkGetPackedA32(src); + if (alpha) { + invScale = 255.0f / alpha; + } + + for (int c = 0; c < 3; ++c) { + float srcComponent = ((src & (0xff << (c * 8))) >> (c * 8)) * invScale; + float lower = SkTMax(0.f, srcComponent - error); + float upper = SkTMin(255.f, srcComponent + error); + if (toSRGB) { + lower = linear_to_srgb(lower / 255.f); + upper = linear_to_srgb(upper / 255.f); + } else { + lower = srgb_to_linear(lower / 255.f); + upper = srgb_to_linear(upper / 255.f); + } + lower *= alpha; + upper *= alpha; + SkASSERT(lower >= 0.f && lower <= 255.f); + SkASSERT(upper >= 0.f && upper <= 255.f); + uint8_t dstComponent = (dst & (0xff << (c * 8))) >> (c * 8); + if (dstComponent < SkScalarFloorToInt(lower) || + dstComponent > SkScalarCeilToInt(upper)) { + result = false; + } + uint8_t expectedComponent = SkScalarRoundToInt((lower + upper) * 0.5f); + expectedColor |= expectedComponent << (c * 8); + } + + *expected = expectedColor; + return result; +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ApplyGamma, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + static const int kW = 256; + static const int kH = 256; + static const size_t kRowBytes = sizeof(uint32_t) * kW; + + GrSurfaceDesc baseDesc; + baseDesc.fConfig = kRGBA_8888_GrPixelConfig; + baseDesc.fWidth = kW; + baseDesc.fHeight = kH; + + const SkImageInfo ii = SkImageInfo::MakeN32Premul(kW, kH); + + SkAutoTMalloc<uint32_t> srcPixels(kW * kH); + for (int y = 0; y < kH; ++y) { + for (int x = 0; x < kW; ++x) { + srcPixels.get()[y*kW+x] = SkPreMultiplyARGB(x, y, x, 0xFF); + } + } + + SkBitmap bm; + bm.installPixels(ii, srcPixels.get(), kRowBytes); + + SkAutoTMalloc<uint32_t> read(kW * kH); + + // We allow more error on GPUs with lower precision shader variables. + float error = context->caps()->shaderCaps()->floatPrecisionVaries() ? 1.2f : 0.5f; + + for (auto toSRGB : { false, true }) { + sk_sp<SkSurface> dst(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, ii)); + + if (!dst) { + ERRORF(reporter, "Could not create surfaces for copy surface test."); + continue; + } + + SkCanvas* dstCanvas = dst->getCanvas(); + + dstCanvas->clear(SK_ColorRED); + dstCanvas->flush(); + + SkPaint gammaPaint; + gammaPaint.setBlendMode(SkBlendMode::kSrc); + gammaPaint.setColorFilter(toSRGB ? SkColorFilter::MakeLinearToSRGBGamma() + : SkColorFilter::MakeSRGBToLinearGamma()); + + dstCanvas->drawBitmap(bm, 0, 0, &gammaPaint); + dstCanvas->flush(); + + sk_memset32(read.get(), 0, kW * kH); + if (!dstCanvas->readPixels(ii, read.get(), kRowBytes, 0, 0)) { + ERRORF(reporter, "Error calling readPixels"); + continue; + } + + bool abort = false; + // Validate that pixels were copied/transformed correctly. + for (int y = 0; y < kH && !abort; ++y) { + for (int x = 0; x < kW && !abort; ++x) { + uint32_t r = read.get()[y * kW + x]; + uint32_t s = srcPixels.get()[y * kW + x]; + uint32_t expected; + if (!check_gamma(s, r, toSRGB, error, &expected)) { + ERRORF(reporter, "Expected dst %d,%d to contain 0x%08x " + "from src 0x%08x and mode %s. Got %08x", x, y, expected, s, + toSRGB ? "ToSRGB" : "ToLinear", r); + abort = true; + break; + } + } + } + } +} +#endif
diff --git a/src/third_party/skia/tests/ArenaAllocTest.cpp b/src/third_party/skia/tests/ArenaAllocTest.cpp new file mode 100644 index 0000000..137e60e --- /dev/null +++ b/src/third_party/skia/tests/ArenaAllocTest.cpp
@@ -0,0 +1,183 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" +#include "SkArenaAlloc.h" +#include "SkRefCnt.h" + +namespace { + + static int created, destroyed; + + struct Foo { + Foo() : x(-2), y(-3.0f) { created++; } + Foo(int X, float Y) : x(X), y(Y) { created++; } + ~Foo() { destroyed++; } + + int x; + float y; + }; + + struct Big { + Big() {} + uint32_t array[128]; + }; + + struct Node { + Node(Node* n) : next(n) { created++; } + ~Node() { + destroyed++; + if (next) { + next->~Node(); + } + } + Node *next; + }; + + struct Start { + ~Start() { + if (start) { + start->~Node(); + } + } + Node* start; + }; + + struct FooRefCnt : public SkRefCnt { + FooRefCnt() : x(-2), y(-3.0f) { created++; } + FooRefCnt(int X, float Y) : x(X), y(Y) { created++; } + ~FooRefCnt() { destroyed++; } + + int x; + float y; + }; + +} + +struct WithDtor { + ~WithDtor() { } +}; + +DEF_TEST(ArenaAlloc, r) { + + { + created = 0; + destroyed = 0; + + SkArenaAlloc arena{0}; + REPORTER_ASSERT(r, *arena.make<int>(3) == 3); + Foo* foo = arena.make<Foo>(3, 4.0f); + REPORTER_ASSERT(r, foo->x == 3); + REPORTER_ASSERT(r, foo->y == 4.0f); + REPORTER_ASSERT(r, created == 1); + REPORTER_ASSERT(r, destroyed == 0); + arena.makeArrayDefault<int>(10); + int* zeroed = arena.makeArray<int>(10); + for (int i = 0; i < 10; i++) { + REPORTER_ASSERT(r, zeroed[i] == 0); + } + Foo* fooArray = arena.makeArrayDefault<Foo>(10); + REPORTER_ASSERT(r, fooArray[3].x == -2); + REPORTER_ASSERT(r, fooArray[4].y == -3.0f); + REPORTER_ASSERT(r, created == 11); + REPORTER_ASSERT(r, destroyed == 0); + arena.make<typename std::aligned_storage<10,8>::type>(); + } + REPORTER_ASSERT(r, created == 11); + REPORTER_ASSERT(r, destroyed == 11); + + { + created = 0; + destroyed = 0; + SkSTArenaAlloc<64> arena; + + REPORTER_ASSERT(r, *arena.make<int>(3) == 3); + Foo* foo = arena.make<Foo>(3, 4.0f); + REPORTER_ASSERT(r, foo->x == 3); + REPORTER_ASSERT(r, foo->y == 4.0f); + REPORTER_ASSERT(r, created == 1); + REPORTER_ASSERT(r, destroyed == 0); + arena.makeArrayDefault<int>(10); + int* zeroed = arena.makeArray<int>(10); + for (int i = 0; i < 10; i++) { + REPORTER_ASSERT(r, zeroed[i] == 0); + } + Foo* fooArray = arena.makeArrayDefault<Foo>(10); + REPORTER_ASSERT(r, fooArray[3].x == -2); + REPORTER_ASSERT(r, fooArray[4].y == -3.0f); + REPORTER_ASSERT(r, created == 11); + REPORTER_ASSERT(r, destroyed == 0); + arena.make<typename std::aligned_storage<10,8>::type>(); + } + REPORTER_ASSERT(r, created == 11); + REPORTER_ASSERT(r, destroyed == 11); + + { + created = 0; + destroyed = 0; + std::unique_ptr<char[]> block{new char[1024]}; + SkArenaAlloc arena{block.get(), 1024, 0}; + + REPORTER_ASSERT(r, *arena.make<int>(3) == 3); + Foo* foo = arena.make<Foo>(3, 4.0f); + REPORTER_ASSERT(r, foo->x == 3); + REPORTER_ASSERT(r, foo->y == 4.0f); + REPORTER_ASSERT(r, created == 1); + REPORTER_ASSERT(r, destroyed == 0); + arena.makeArrayDefault<int>(10); + int* zeroed = arena.makeArray<int>(10); + for (int i = 0; i < 10; i++) { + REPORTER_ASSERT(r, zeroed[i] == 0); + } + Foo* fooArray = arena.makeArrayDefault<Foo>(10); + REPORTER_ASSERT(r, fooArray[3].x == -2); + REPORTER_ASSERT(r, fooArray[4].y == -3.0f); + REPORTER_ASSERT(r, created == 11); + REPORTER_ASSERT(r, destroyed == 0); + arena.make<typename std::aligned_storage<10,8>::type>(); + } + REPORTER_ASSERT(r, created == 11); + REPORTER_ASSERT(r, destroyed == 11); + + { + SkSTArenaAlloc<64> arena; + arena.makeArrayDefault<char>(256); + arena.reset(); + arena.reset(); + } + + { + created = 0; + destroyed = 0; + SkSTArenaAlloc<64> arena; + + Start start; + Node* current = nullptr; + for (int i = 0; i < 128; i++) { + uint64_t* temp = arena.makeArrayDefault<uint64_t>(sizeof(Node) / sizeof(Node*)); + current = new (temp)Node(current); + } + start.start = current; + } + + REPORTER_ASSERT(r, created == 128); + REPORTER_ASSERT(r, destroyed == 128); + + { + created = 0; + destroyed = 0; + SkSTArenaAlloc<64> arena; + + sk_sp<FooRefCnt> f = arena.makeSkSp<FooRefCnt>(4, 5.0f); + REPORTER_ASSERT(r, f->x == 4); + REPORTER_ASSERT(r, f->y == 5.0f); + REPORTER_ASSERT(r, created == 1); + REPORTER_ASSERT(r, destroyed == 0); + } + REPORTER_ASSERT(r, created == 1); + REPORTER_ASSERT(r, destroyed == 1); +}
diff --git a/src/third_party/skia/tests/AsADashTest.cpp b/src/third_party/skia/tests/AsADashTest.cpp index 47f1971..2077b66 100644 --- a/src/third_party/skia/tests/AsADashTest.cpp +++ b/src/third_party/skia/tests/AsADashTest.cpp
@@ -12,7 +12,7 @@ #include "SkCornerPathEffect.h" DEF_TEST(AsADashTest_noneDash, reporter) { - SkAutoTUnref<SkCornerPathEffect> pe(SkCornerPathEffect::Create(1.0)); + sk_sp<SkPathEffect> pe(SkCornerPathEffect::Make(1.0)); SkPathEffect::DashInfo info; SkPathEffect::DashType dashType = pe->asADash(&info); @@ -22,9 +22,9 @@ DEF_TEST(AsADashTest_nullInfo, reporter) { SkScalar inIntervals[] = { 4.0, 2.0, 1.0, 3.0 }; const SkScalar phase = 2.0; - SkAutoTUnref<SkDashPathEffect> pe(SkDashPathEffect::Create(inIntervals, 4, phase)); + sk_sp<SkPathEffect> pe(SkDashPathEffect::Make(inIntervals, 4, phase)); - SkPathEffect::DashType dashType = pe->asADash(NULL); + SkPathEffect::DashType dashType = pe->asADash(nullptr); REPORTER_ASSERT(reporter, SkPathEffect::kDash_DashType == dashType); } @@ -33,7 +33,7 @@ SkScalar totalIntSum = 10.0; const SkScalar phase = 2.0; - SkAutoTUnref<SkDashPathEffect> pe(SkDashPathEffect::Create(inIntervals, 4, phase)); + sk_sp<SkPathEffect> pe(SkDashPathEffect::Make(inIntervals, 4, phase)); SkPathEffect::DashInfo info;
diff --git a/src/third_party/skia/tests/AtomicTest.cpp b/src/third_party/skia/tests/AtomicTest.cpp deleted file mode 100644 index 8b5ac9b..0000000 --- a/src/third_party/skia/tests/AtomicTest.cpp +++ /dev/null
@@ -1,57 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkThread.h" -#include "SkThreadUtils.h" -#include "SkTypes.h" -#include "Test.h" - -struct AddInfo { - int32_t valueToAdd; - int timesToAdd; - unsigned int processorAffinity; -}; - -static int32_t base = 0; - -static AddInfo gAdds[] = { - { 3, 100, 23 }, - { 2, 200, 2 }, - { 7, 150, 17 }, -}; - -static void addABunchOfTimes(void* data) { - AddInfo* addInfo = static_cast<AddInfo*>(data); - for (int i = 0; i < addInfo->timesToAdd; i++) { - sk_atomic_add(&base, addInfo->valueToAdd); - } -} - -DEF_TEST(Atomic, reporter) { - int32_t total = base; - SkThread* threads[SK_ARRAY_COUNT(gAdds)]; - for (size_t i = 0; i < SK_ARRAY_COUNT(gAdds); i++) { - total += gAdds[i].valueToAdd * gAdds[i].timesToAdd; - } - // Start the threads - for (size_t i = 0; i < SK_ARRAY_COUNT(gAdds); i++) { - threads[i] = new SkThread(addABunchOfTimes, &gAdds[i]); - threads[i]->setProcessorAffinity(gAdds[i].processorAffinity); - threads[i]->start(); - } - - // Now end the threads - for (size_t i = 0; i < SK_ARRAY_COUNT(gAdds); i++) { - threads[i]->join(); - delete threads[i]; - } - REPORTER_ASSERT(reporter, total == base); - // Ensure that the returned value from sk_atomic_add is correct. - int32_t valueToModify = 3; - const int32_t originalValue = valueToModify; - REPORTER_ASSERT(reporter, originalValue == sk_atomic_add(&valueToModify, 7)); -}
diff --git a/src/third_party/skia/tests/BBoxHierarchyTest.cpp b/src/third_party/skia/tests/BBoxHierarchyTest.cpp deleted file mode 100644 index 71b9699..0000000 --- a/src/third_party/skia/tests/BBoxHierarchyTest.cpp +++ /dev/null
@@ -1,170 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Test.h" -#include "SkRandom.h" -#include "SkRTree.h" -#include "SkTSort.h" - -static const size_t RTREE_MIN_CHILDREN = 6; -static const size_t RTREE_MAX_CHILDREN = 11; - -static const int NUM_RECTS = 200; -static const size_t NUM_ITERATIONS = 100; -static const size_t NUM_QUERIES = 50; - -static const SkScalar MAX_SIZE = 1000.0f; - -struct DataRect { - SkRect rect; - void* data; -}; - -static SkRect random_rect(SkRandom& rand) { - SkRect rect = {0,0,0,0}; - while (rect.isEmpty()) { - rect.fLeft = rand.nextRangeF(0, MAX_SIZE); - rect.fRight = rand.nextRangeF(0, MAX_SIZE); - rect.fTop = rand.nextRangeF(0, MAX_SIZE); - rect.fBottom = rand.nextRangeF(0, MAX_SIZE); - rect.sort(); - } - return rect; -} - -static void random_data_rects(SkRandom& rand, DataRect out[], int n) { - for (int i = 0; i < n; ++i) { - out[i].rect = random_rect(rand); - out[i].data = reinterpret_cast<void*>(i); - } -} - -static bool verify_query(SkRect query, DataRect rects[], - SkTDArray<void*>& found) { - // TODO(mtklein): no need to do this after everything's SkRects - query.roundOut(); - - SkTDArray<void*> expected; - // manually intersect with every rectangle - for (int i = 0; i < NUM_RECTS; ++i) { - if (SkRect::Intersects(query, rects[i].rect)) { - expected.push(rects[i].data); - } - } - - if (expected.count() != found.count()) { - return false; - } - - if (0 == expected.count()) { - return true; - } - - // Just cast to long since sorting by the value of the void*'s was being problematic... - SkTQSort(reinterpret_cast<long*>(expected.begin()), - reinterpret_cast<long*>(expected.end() - 1)); - SkTQSort(reinterpret_cast<long*>(found.begin()), - reinterpret_cast<long*>(found.end() - 1)); - return found == expected; -} - -static void run_queries(skiatest::Reporter* reporter, SkRandom& rand, DataRect rects[], - SkBBoxHierarchy& tree) { - for (size_t i = 0; i < NUM_QUERIES; ++i) { - SkTDArray<void*> hits; - SkRect query = random_rect(rand); - tree.search(query, &hits); - REPORTER_ASSERT(reporter, verify_query(query, rects, hits)); - } -} - -static void tree_test_main(SkBBoxHierarchy* tree, int minChildren, int maxChildren, - skiatest::Reporter* reporter) { - DataRect rects[NUM_RECTS]; - SkRandom rand; - REPORTER_ASSERT(reporter, tree); - - int expectedDepthMin = -1; - int expectedDepthMax = -1; - - int tmp = NUM_RECTS; - if (maxChildren > 0) { - while (tmp > 0) { - tmp -= static_cast<int>(pow(static_cast<double>(maxChildren), - static_cast<double>(expectedDepthMin + 1))); - ++expectedDepthMin; - } - } - - tmp = NUM_RECTS; - if (minChildren > 0) { - while (tmp > 0) { - tmp -= static_cast<int>(pow(static_cast<double>(minChildren), - static_cast<double>(expectedDepthMax + 1))); - ++expectedDepthMax; - } - } - - for (size_t i = 0; i < NUM_ITERATIONS; ++i) { - random_data_rects(rand, rects, NUM_RECTS); - - // First try bulk-loaded inserts - for (int i = 0; i < NUM_RECTS; ++i) { - tree->insert(rects[i].data, rects[i].rect, true); - } - tree->flushDeferredInserts(); - run_queries(reporter, rand, rects, *tree); - REPORTER_ASSERT(reporter, NUM_RECTS == tree->getCount()); - REPORTER_ASSERT(reporter, - ((expectedDepthMin <= 0) || (expectedDepthMin <= tree->getDepth())) && - ((expectedDepthMax <= 0) || (expectedDepthMax >= tree->getDepth()))); - tree->clear(); - REPORTER_ASSERT(reporter, 0 == tree->getCount()); - - // Then try immediate inserts - tree->insert(rects[0].data, rects[0].rect); - tree->flushDeferredInserts(); - for (int i = 1; i < NUM_RECTS; ++i) { - tree->insert(rects[i].data, rects[i].rect); - } - run_queries(reporter, rand, rects, *tree); - REPORTER_ASSERT(reporter, NUM_RECTS == tree->getCount()); - REPORTER_ASSERT(reporter, - ((expectedDepthMin <= 0) || (expectedDepthMin <= tree->getDepth())) && - ((expectedDepthMax <= 0) || (expectedDepthMax >= tree->getDepth()))); - tree->clear(); - REPORTER_ASSERT(reporter, 0 == tree->getCount()); - - // And for good measure try immediate inserts, but in reversed order - tree->insert(rects[NUM_RECTS - 1].data, rects[NUM_RECTS - 1].rect); - tree->flushDeferredInserts(); - for (int i = NUM_RECTS - 2; i >= 0; --i) { - tree->insert(rects[i].data, rects[i].rect); - } - run_queries(reporter, rand, rects, *tree); - REPORTER_ASSERT(reporter, NUM_RECTS == tree->getCount()); - REPORTER_ASSERT(reporter, - ((expectedDepthMin < 0) || (expectedDepthMin <= tree->getDepth())) && - ((expectedDepthMax < 0) || (expectedDepthMax >= tree->getDepth()))); - tree->clear(); - REPORTER_ASSERT(reporter, 0 == tree->getCount()); - } -} - -DEF_TEST(BBoxHierarchy, reporter) { - // RTree - { - SkRTree* rtree = SkRTree::Create(RTREE_MIN_CHILDREN, RTREE_MAX_CHILDREN); - SkAutoUnref au(rtree); - tree_test_main(rtree, RTREE_MIN_CHILDREN, RTREE_MAX_CHILDREN, reporter); - - // Rtree that orders input rectangles on deferred insert. - SkRTree* unsortedRtree = SkRTree::Create(RTREE_MIN_CHILDREN, RTREE_MAX_CHILDREN, 1, false); - SkAutoUnref auo(unsortedRtree); - tree_test_main(unsortedRtree, RTREE_MIN_CHILDREN, RTREE_MAX_CHILDREN, reporter); - } -}
diff --git a/src/third_party/skia/tests/BadIcoTest.cpp b/src/third_party/skia/tests/BadIcoTest.cpp new file mode 100644 index 0000000..670c2ac --- /dev/null +++ b/src/third_party/skia/tests/BadIcoTest.cpp
@@ -0,0 +1,46 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Resources.h" +#include "Test.h" + +#include "SkBitmap.h" +#include "SkCodec.h" +#include "SkOSFile.h" +#include "SkOSPath.h" +#include "SkStream.h" + +DEF_TEST(BadImage, reporter) { + const char* const badImages [] = { + "sigabort_favicon.ico", + "sigsegv_favicon.ico", + "sigsegv_favicon_2.ico", + "ico_leak01.ico", + "ico_fuzz0.ico", + "ico_fuzz1.ico", + "skbug3442.webp", + "skbug3429.webp", + "b38116746.ico", + }; + + const char* badImagesFolder = "invalid_images"; + + for (size_t i = 0; i < SK_ARRAY_COUNT(badImages); ++i) { + SkString resourcePath = SkOSPath::Join(badImagesFolder, badImages[i]); + std::unique_ptr<SkStream> stream(GetResourceAsStream(resourcePath.c_str())); + std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release())); + + // These images are corrupt. It's not important whether we succeed/fail in codec + // creation or decoding. We just want to make sure that we don't crash. + if (codec) { + SkBitmap bm; + bm.allocPixels(codec->getInfo()); + codec->getPixels(codec->getInfo(), bm.getPixels(), + bm.rowBytes()); + } + } +}
diff --git a/src/third_party/skia/tests/BitSetTest.cpp b/src/third_party/skia/tests/BitSetTest.cpp index da02376..6fc8351 100644 --- a/src/third_party/skia/tests/BitSetTest.cpp +++ b/src/third_party/skia/tests/BitSetTest.cpp
@@ -10,67 +10,30 @@ DEF_TEST(BitSet, reporter) { SkBitSet set0(65536); - REPORTER_ASSERT(reporter, set0.isBitSet(0) == false); - REPORTER_ASSERT(reporter, set0.isBitSet(32767) == false); - REPORTER_ASSERT(reporter, set0.isBitSet(65535) == false); + REPORTER_ASSERT(reporter, set0.has(0) == false); + REPORTER_ASSERT(reporter, set0.has(32767) == false); + REPORTER_ASSERT(reporter, set0.has(65535) == false); - SkBitSet set1(65536); - REPORTER_ASSERT(reporter, set0 == set1); - - set0.setBit(22, true); - REPORTER_ASSERT(reporter, set0.isBitSet(22) == true); - set0.setBit(24, true); - REPORTER_ASSERT(reporter, set0.isBitSet(24) == true); - set0.setBit(35, true); // on a different DWORD - REPORTER_ASSERT(reporter, set0.isBitSet(35) == true); - set0.setBit(22, false); - REPORTER_ASSERT(reporter, set0.isBitSet(22) == false); - REPORTER_ASSERT(reporter, set0.isBitSet(24) == true); - REPORTER_ASSERT(reporter, set0.isBitSet(35) == true); + set0.set(22); + REPORTER_ASSERT(reporter, set0.has(22) == true); + set0.set(24); + REPORTER_ASSERT(reporter, set0.has(24) == true); + set0.set(35); // on a different DWORD + REPORTER_ASSERT(reporter, set0.has(35) == true); + REPORTER_ASSERT(reporter, set0.has(24) == true); + REPORTER_ASSERT(reporter, set0.has(35) == true); SkTDArray<unsigned int> data; set0.exportTo(&data); - REPORTER_ASSERT(reporter, data.count() == 2); - REPORTER_ASSERT(reporter, data[0] == 24); - REPORTER_ASSERT(reporter, data[1] == 35); + REPORTER_ASSERT(reporter, data.count() == 3); + REPORTER_ASSERT(reporter, data[0] == 22); + REPORTER_ASSERT(reporter, data[1] == 24); + REPORTER_ASSERT(reporter, data[2] == 35); - set1.setBit(12345, true); - set1.orBits(set0); - REPORTER_ASSERT(reporter, set0.isBitSet(12345) == false); - REPORTER_ASSERT(reporter, set1.isBitSet(12345) == true); - REPORTER_ASSERT(reporter, set1.isBitSet(22) == false); - REPORTER_ASSERT(reporter, set1.isBitSet(24) == true); - REPORTER_ASSERT(reporter, set0.isBitSet(35) == true); - REPORTER_ASSERT(reporter, set1 != set0); - - set1.clearAll(); - REPORTER_ASSERT(reporter, set0.isBitSet(12345) == false); - REPORTER_ASSERT(reporter, set1.isBitSet(12345) == false); - REPORTER_ASSERT(reporter, set1.isBitSet(22) == false); - REPORTER_ASSERT(reporter, set1.isBitSet(24) == false); - REPORTER_ASSERT(reporter, set1.isBitSet(35) == false); - - set1.orBits(set0); - REPORTER_ASSERT(reporter, set1 == set0); - - SkBitSet set2(1); - SkBitSet set3(1); - SkBitSet set4(4); - SkBitSet set5(33); - - REPORTER_ASSERT(reporter, set2 == set3); - REPORTER_ASSERT(reporter, set2 != set4); - REPORTER_ASSERT(reporter, set2 != set5); - - set2.setBit(0, true); - REPORTER_ASSERT(reporter, set2 != set5); - set5.setBit(0, true); - REPORTER_ASSERT(reporter, set2 != set5); - REPORTER_ASSERT(reporter, set2 != set3); - set3.setBit(0, true); - REPORTER_ASSERT(reporter, set2 == set3); - set3.clearAll(); - set3 = set2; - set2 = set2; - REPORTER_ASSERT(reporter, set2 == set3); + SkBitSet set1(65536); + set1.set(12345); + REPORTER_ASSERT(reporter, set0.has(12345) == false); + REPORTER_ASSERT(reporter, set1.has(12345) == true); + REPORTER_ASSERT(reporter, set1.has(22) == false); + REPORTER_ASSERT(reporter, set0.has(35) == true); }
diff --git a/src/third_party/skia/tests/BitmapCopyTest.cpp b/src/third_party/skia/tests/BitmapCopyTest.cpp index 3923846..09e44ce 100644 --- a/src/third_party/skia/tests/BitmapCopyTest.cpp +++ b/src/third_party/skia/tests/BitmapCopyTest.cpp
@@ -7,67 +7,16 @@ #include "SkBitmap.h" #include "SkRect.h" +#include "SkTemplates.h" #include "Test.h" - -static const char* boolStr(bool value) { - return value ? "true" : "false"; -} - -// these are in the same order as the SkColorType enum -static const char* gColorTypeName[] = { - "None", "A8", "565", "4444", "RGBA", "BGRA", "Index8" -}; - -static void report_opaqueness(skiatest::Reporter* reporter, const SkBitmap& src, - const SkBitmap& dst) { - ERRORF(reporter, "src %s opaque:%d, dst %s opaque:%d", - gColorTypeName[src.colorType()], src.isOpaque(), - gColorTypeName[dst.colorType()], dst.isOpaque()); -} - -static bool canHaveAlpha(SkColorType ct) { - return kRGB_565_SkColorType != ct; -} - -// copyTo() should preserve isOpaque when it makes sense -static void test_isOpaque(skiatest::Reporter* reporter, - const SkBitmap& srcOpaque, const SkBitmap& srcPremul, - SkColorType dstColorType) { - SkBitmap dst; - - if (canHaveAlpha(srcPremul.colorType()) && canHaveAlpha(dstColorType)) { - REPORTER_ASSERT(reporter, srcPremul.copyTo(&dst, dstColorType)); - REPORTER_ASSERT(reporter, dst.colorType() == dstColorType); - if (srcPremul.isOpaque() != dst.isOpaque()) { - report_opaqueness(reporter, srcPremul, dst); - } - } - - REPORTER_ASSERT(reporter, srcOpaque.copyTo(&dst, dstColorType)); - REPORTER_ASSERT(reporter, dst.colorType() == dstColorType); - if (srcOpaque.isOpaque() != dst.isOpaque()) { - report_opaqueness(reporter, srcOpaque, dst); - } -} +#include "sk_tool_utils.h" static void init_src(const SkBitmap& bitmap) { - SkAutoLockPixels lock(bitmap); if (bitmap.getPixels()) { - if (bitmap.getColorTable()) { - sk_bzero(bitmap.getPixels(), bitmap.getSize()); - } else { - bitmap.eraseColor(SK_ColorWHITE); - } + bitmap.eraseColor(SK_ColorWHITE); } } -static SkColorTable* init_ctable(SkAlphaType alphaType) { - static const SkColor colors[] = { - SK_ColorBLACK, SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE - }; - return new SkColorTable(colors, SK_ARRAY_COUNT(colors), alphaType); -} - struct Pair { SkColorType fColorType; const char* fValid; @@ -81,60 +30,6 @@ // reportCopyVerification() // writeCoordPixels() -// Utility function to read the value of a given pixel in bm. All -// values converted to uint32_t for simplification of comparisons. -static uint32_t getPixel(int x, int y, const SkBitmap& bm) { - uint32_t val = 0; - uint16_t val16; - uint8_t val8; - SkAutoLockPixels lock(bm); - const void* rawAddr = bm.getAddr(x,y); - - switch (bm.bytesPerPixel()) { - case 4: - memcpy(&val, rawAddr, sizeof(uint32_t)); - break; - case 2: - memcpy(&val16, rawAddr, sizeof(uint16_t)); - val = val16; - break; - case 1: - memcpy(&val8, rawAddr, sizeof(uint8_t)); - val = val8; - break; - default: - break; - } - return val; -} - -// Utility function to set value of any pixel in bm. -// bm.getConfig() specifies what format 'val' must be -// converted to, but at present uint32_t can handle all formats. -static void setPixel(int x, int y, uint32_t val, SkBitmap& bm) { - uint16_t val16; - uint8_t val8; - SkAutoLockPixels lock(bm); - void* rawAddr = bm.getAddr(x,y); - - switch (bm.bytesPerPixel()) { - case 4: - memcpy(rawAddr, &val, sizeof(uint32_t)); - break; - case 2: - val16 = val & 0xFFFF; - memcpy(rawAddr, &val16, sizeof(uint16_t)); - break; - case 1: - val8 = val & 0xFF; - memcpy(rawAddr, &val8, sizeof(uint8_t)); - break; - default: - // Ignore. - break; - } -} - // Helper struct to contain pixel locations, while avoiding need for STL. struct Coordinates { @@ -154,41 +49,13 @@ } }; -// A function to verify that two bitmaps contain the same pixel values -// at all coordinates indicated by coords. Simplifies verification of -// copied bitmaps. -static void reportCopyVerification(const SkBitmap& bm1, const SkBitmap& bm2, - Coordinates& coords, - const char* msg, - skiatest::Reporter* reporter){ - bool success = true; - - // Confirm all pixels in the list match. - for (int i = 0; i < coords.length; ++i) { - success = success && - (getPixel(coords[i]->fX, coords[i]->fY, bm1) == - getPixel(coords[i]->fX, coords[i]->fY, bm2)); - } - - if (!success) { - ERRORF(reporter, "%s [colortype = %s]", msg, - gColorTypeName[bm1.colorType()]); - } -} - -// Writes unique pixel values at locations specified by coords. -static void writeCoordPixels(SkBitmap& bm, const Coordinates& coords) { - for (int i = 0; i < coords.length; ++i) - setPixel(coords[i]->fX, coords[i]->fY, i, bm); -} - static const Pair gPairs[] = { - { kUnknown_SkColorType, "000000" }, - { kAlpha_8_SkColorType, "010101" }, - { kIndex_8_SkColorType, "011111" }, - { kRGB_565_SkColorType, "010101" }, - { kARGB_4444_SkColorType, "010111" }, - { kN32_SkColorType, "010111" }, + { kUnknown_SkColorType, "0000000" }, + { kAlpha_8_SkColorType, "0100000" }, + { kRGB_565_SkColorType, "0101011" }, + { kARGB_4444_SkColorType, "0101111" }, + { kN32_SkColorType, "0101111" }, + { kRGBA_F16_SkColorType, "0101011" }, }; static const int W = 20; @@ -196,19 +63,13 @@ static void setup_src_bitmaps(SkBitmap* srcOpaque, SkBitmap* srcPremul, SkColorType ct) { - SkColorTable* ctOpaque = NULL; - SkColorTable* ctPremul = NULL; - if (kIndex_8_SkColorType == ct) { - ctOpaque = init_ctable(kOpaque_SkAlphaType); - ctPremul = init_ctable(kPremul_SkAlphaType); + sk_sp<SkColorSpace> colorSpace = nullptr; + if (kRGBA_F16_SkColorType == ct) { + colorSpace = SkColorSpace::MakeSRGBLinear(); } - srcOpaque->allocPixels(SkImageInfo::Make(W, H, ct, kOpaque_SkAlphaType), - NULL, ctOpaque); - srcPremul->allocPixels(SkImageInfo::Make(W, H, ct, kPremul_SkAlphaType), - NULL, ctPremul); - SkSafeUnref(ctOpaque); - SkSafeUnref(ctPremul); + srcOpaque->allocPixels(SkImageInfo::Make(W, H, ct, kOpaque_SkAlphaType, colorSpace)); + srcPremul->allocPixels(SkImageInfo::Make(W, H, ct, kPremul_SkAlphaType, colorSpace)); init_src(*srcOpaque); init_src(*srcPremul); } @@ -235,7 +96,7 @@ // Test copying an extracted subset. for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) { SkBitmap copy; - bool success = subset.copyTo(©, gPairs[j].fColorType); + bool success = sk_tool_utils::copy_to(©, gPairs[j].fColorType, subset); if (!success) { // Skip checking that success matches fValid, which is redundant // with the code below. @@ -249,14 +110,6 @@ REPORTER_ASSERT(reporter, copy.width() == W); REPORTER_ASSERT(reporter, copy.height() == 2); - - if (gPairs[i].fColorType == gPairs[j].fColorType) { - SkAutoLockPixels alp0(subset); - SkAutoLockPixels alp1(copy); - // they should both have, or both not-have, a colortable - bool hasCT = subset.getColorTable() != NULL; - REPORTER_ASSERT(reporter, (copy.getColorTable() != NULL) == hasCT); - } } } @@ -269,282 +122,6 @@ } } -DEF_TEST(BitmapCopy, reporter) { - static const bool isExtracted[] = { - false, true - }; - - for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) { - SkBitmap srcOpaque, srcPremul; - setup_src_bitmaps(&srcOpaque, &srcPremul, gPairs[i].fColorType); - - for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) { - SkBitmap dst; - - bool success = srcPremul.copyTo(&dst, gPairs[j].fColorType); - bool expected = gPairs[i].fValid[j] != '0'; - if (success != expected) { - ERRORF(reporter, "SkBitmap::copyTo from %s to %s. expected %s " - "returned %s", gColorTypeName[i], gColorTypeName[j], - boolStr(expected), boolStr(success)); - } - - bool canSucceed = srcPremul.canCopyTo(gPairs[j].fColorType); - if (success != canSucceed) { - ERRORF(reporter, "SkBitmap::copyTo from %s to %s. returned %s " - "canCopyTo %s", gColorTypeName[i], gColorTypeName[j], - boolStr(success), boolStr(canSucceed)); - } - - if (success) { - REPORTER_ASSERT(reporter, srcPremul.width() == dst.width()); - REPORTER_ASSERT(reporter, srcPremul.height() == dst.height()); - REPORTER_ASSERT(reporter, dst.colorType() == gPairs[j].fColorType); - test_isOpaque(reporter, srcOpaque, srcPremul, dst.colorType()); - if (srcPremul.colorType() == dst.colorType()) { - SkAutoLockPixels srcLock(srcPremul); - SkAutoLockPixels dstLock(dst); - REPORTER_ASSERT(reporter, srcPremul.readyToDraw()); - REPORTER_ASSERT(reporter, dst.readyToDraw()); - const char* srcP = (const char*)srcPremul.getAddr(0, 0); - const char* dstP = (const char*)dst.getAddr(0, 0); - REPORTER_ASSERT(reporter, srcP != dstP); - REPORTER_ASSERT(reporter, !memcmp(srcP, dstP, - srcPremul.getSize())); - REPORTER_ASSERT(reporter, srcPremul.getGenerationID() == dst.getGenerationID()); - } else { - REPORTER_ASSERT(reporter, srcPremul.getGenerationID() != dst.getGenerationID()); - } - } else { - // dst should be unchanged from its initial state - REPORTER_ASSERT(reporter, dst.colorType() == kUnknown_SkColorType); - REPORTER_ASSERT(reporter, dst.width() == 0); - REPORTER_ASSERT(reporter, dst.height() == 0); - } - } // for (size_t j = ... - - // Tests for getSafeSize(), getSafeSize64(), copyPixelsTo(), - // copyPixelsFrom(). - // - for (size_t copyCase = 0; copyCase < SK_ARRAY_COUNT(isExtracted); - ++copyCase) { - // Test copying to/from external buffer. - // Note: the tests below have hard-coded values --- - // Please take care if modifying. - - // Tests for getSafeSize64(). - // Test with a very large configuration without pixel buffer - // attached. - SkBitmap tstSafeSize; - tstSafeSize.setInfo(SkImageInfo::Make(100000000U, 100000000U, - gPairs[i].fColorType, kPremul_SkAlphaType)); - int64_t safeSize = tstSafeSize.computeSafeSize64(); - if (safeSize < 0) { - ERRORF(reporter, "getSafeSize64() negative: %s", - gColorTypeName[tstSafeSize.colorType()]); - } - bool sizeFail = false; - // Compare against hand-computed values. - switch (gPairs[i].fColorType) { - case kUnknown_SkColorType: - break; - - case kAlpha_8_SkColorType: - case kIndex_8_SkColorType: - if (safeSize != 0x2386F26FC10000LL) { - sizeFail = true; - } - break; - - case kRGB_565_SkColorType: - case kARGB_4444_SkColorType: - if (safeSize != 0x470DE4DF820000LL) { - sizeFail = true; - } - break; - - case kN32_SkColorType: - if (safeSize != 0x8E1BC9BF040000LL) { - sizeFail = true; - } - break; - - default: - break; - } - if (sizeFail) { - ERRORF(reporter, "computeSafeSize64() wrong size: %s", - gColorTypeName[tstSafeSize.colorType()]); - } - - int subW = 2; - int subH = 2; - - // Create bitmap to act as source for copies and subsets. - SkBitmap src, subset; - SkColorTable* ct = NULL; - if (kIndex_8_SkColorType == src.colorType()) { - ct = init_ctable(kPremul_SkAlphaType); - } - - int localSubW; - if (isExtracted[copyCase]) { // A larger image to extract from. - localSubW = 2 * subW + 1; - } else { // Tests expect a 2x2 bitmap, so make smaller. - localSubW = subW; - } - // could fail if we pass kIndex_8 for the colortype - if (src.tryAllocPixels(SkImageInfo::Make(localSubW, subH, gPairs[i].fColorType, - kPremul_SkAlphaType))) { - // failure is fine, as we will notice later on - } - SkSafeUnref(ct); - - // Either copy src or extract into 'subset', which is used - // for subsequent calls to copyPixelsTo/From. - bool srcReady = false; - // Test relies on older behavior that extractSubset will fail on - // kUnknown_SkColorType - if (kUnknown_SkColorType != src.colorType() && - isExtracted[copyCase]) { - // The extractedSubset() test case allows us to test copy- - // ing when src and dst mave possibly different strides. - SkIRect r; - r.set(1, 0, 1 + subW, subH); // 2x2 extracted bitmap - - srcReady = src.extractSubset(&subset, r); - } else { - srcReady = src.copyTo(&subset); - } - - // Not all configurations will generate a valid 'subset'. - if (srcReady) { - - // Allocate our target buffer 'buf' for all copies. - // To simplify verifying correctness of copies attach - // buf to a SkBitmap, but copies are done using the - // raw buffer pointer. - const size_t bufSize = subH * - SkColorTypeMinRowBytes(src.colorType(), subW) * 2; - SkAutoMalloc autoBuf (bufSize); - uint8_t* buf = static_cast<uint8_t*>(autoBuf.get()); - - SkBitmap bufBm; // Attach buf to this bitmap. - bool successExpected; - - // Set up values for each pixel being copied. - Coordinates coords(subW * subH); - for (int x = 0; x < subW; ++x) - for (int y = 0; y < subH; ++y) - { - int index = y * subW + x; - SkASSERT(index < coords.length); - coords[index]->fX = x; - coords[index]->fY = y; - } - - writeCoordPixels(subset, coords); - - // Test #1 //////////////////////////////////////////// - - const SkImageInfo info = SkImageInfo::Make(subW, subH, - gPairs[i].fColorType, - kPremul_SkAlphaType); - // Before/after comparisons easier if we attach buf - // to an appropriately configured SkBitmap. - memset(buf, 0xFF, bufSize); - // Config with stride greater than src but that fits in buf. - bufBm.installPixels(info, buf, info.minRowBytes() * 2); - successExpected = false; - // Then attempt to copy with a stride that is too large - // to fit in the buffer. - REPORTER_ASSERT(reporter, - subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes() * 3) - == successExpected); - - if (successExpected) - reportCopyVerification(subset, bufBm, coords, - "copyPixelsTo(buf, bufSize, 1.5*maxRowBytes)", - reporter); - - // Test #2 //////////////////////////////////////////// - // This test should always succeed, but in the case - // of extracted bitmaps only because we handle the - // issue of getSafeSize(). Without getSafeSize() - // buffer overrun/read would occur. - memset(buf, 0xFF, bufSize); - bufBm.installPixels(info, buf, subset.rowBytes()); - successExpected = subset.getSafeSize() <= bufSize; - REPORTER_ASSERT(reporter, - subset.copyPixelsTo(buf, bufSize) == - successExpected); - if (successExpected) - reportCopyVerification(subset, bufBm, coords, - "copyPixelsTo(buf, bufSize)", reporter); - - // Test #3 //////////////////////////////////////////// - // Copy with different stride between src and dst. - memset(buf, 0xFF, bufSize); - bufBm.installPixels(info, buf, subset.rowBytes()+1); - successExpected = true; // Should always work. - REPORTER_ASSERT(reporter, - subset.copyPixelsTo(buf, bufSize, - subset.rowBytes()+1) == successExpected); - if (successExpected) - reportCopyVerification(subset, bufBm, coords, - "copyPixelsTo(buf, bufSize, rowBytes+1)", reporter); - - // Test #4 //////////////////////////////////////////// - // Test copy with stride too small. - memset(buf, 0xFF, bufSize); - bufBm.installPixels(info, buf, info.minRowBytes()); - successExpected = false; - // Request copy with stride too small. - REPORTER_ASSERT(reporter, - subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes()-1) - == successExpected); - if (successExpected) - reportCopyVerification(subset, bufBm, coords, - "copyPixelsTo(buf, bufSize, rowBytes()-1)", reporter); - -#if 0 // copyPixelsFrom is gone - // Test #5 //////////////////////////////////////////// - // Tests the case where the source stride is too small - // for the source configuration. - memset(buf, 0xFF, bufSize); - bufBm.installPixels(info, buf, info.minRowBytes()); - writeCoordPixels(bufBm, coords); - REPORTER_ASSERT(reporter, - subset.copyPixelsFrom(buf, bufSize, 1) == false); - - // Test #6 /////////////////////////////////////////// - // Tests basic copy from an external buffer to the bitmap. - // If the bitmap is "extracted", this also tests the case - // where the source stride is different from the dest. - // stride. - // We've made the buffer large enough to always succeed. - bufBm.installPixels(info, buf, info.minRowBytes()); - writeCoordPixels(bufBm, coords); - REPORTER_ASSERT(reporter, - subset.copyPixelsFrom(buf, bufSize, bufBm.rowBytes()) == - true); - reportCopyVerification(bufBm, subset, coords, - "copyPixelsFrom(buf, bufSize)", - reporter); - - // Test #7 //////////////////////////////////////////// - // Tests the case where the source buffer is too small - // for the transfer. - REPORTER_ASSERT(reporter, - subset.copyPixelsFrom(buf, 1, subset.rowBytes()) == - false); - -#endif - } - } // for (size_t copyCase ... - } -} - #include "SkColorPriv.h" #include "SkUtils.h" @@ -613,7 +190,7 @@ gRec[i].fRequestedDstSize.height()); bool success = srcBM.readPixels(dstInfo, dstPixels, rowBytes, gRec[i].fRequestedSrcLoc.x(), gRec[i].fRequestedSrcLoc.y()); - + REPORTER_ASSERT(reporter, gRec[i].fExpectedSuccess == success); if (success) { const SkIRect srcR = gRec[i].fExpectedSrcR; @@ -636,4 +213,3 @@ } } } -
diff --git a/src/third_party/skia/tests/BitmapHasherTest.cpp b/src/third_party/skia/tests/BitmapHasherTest.cpp deleted file mode 100644 index 3b51706..0000000 --- a/src/third_party/skia/tests/BitmapHasherTest.cpp +++ /dev/null
@@ -1,40 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkBitmapHasher.h" - -#include "SkBitmap.h" -#include "SkColor.h" -#include "Test.h" - -// Word size that is large enough to hold results of any checksum type. -typedef uint64_t checksum_result; - -// Fill in bitmap with test data. -static void CreateTestBitmap(SkBitmap* bitmap, int width, int height, - SkColor color, skiatest::Reporter* reporter) { - bitmap->allocN32Pixels(width, height, kOpaque_SkAlphaType); - bitmap->eraseColor(color); -} - -DEF_TEST(BitmapHasher, reporter) { - // Test SkBitmapHasher - SkBitmap bitmap; - uint64_t digest; - // initial test case - CreateTestBitmap(&bitmap, 333, 555, SK_ColorBLUE, reporter); - REPORTER_ASSERT(reporter, SkBitmapHasher::ComputeDigest(bitmap, &digest)); - REPORTER_ASSERT(reporter, digest == 0xfb2903562766ef87ULL); - // same pixel data but different dimensions should yield a different checksum - CreateTestBitmap(&bitmap, 555, 333, SK_ColorBLUE, reporter); - REPORTER_ASSERT(reporter, SkBitmapHasher::ComputeDigest(bitmap, &digest)); - REPORTER_ASSERT(reporter, digest == 0xfe04023fb97d0f61ULL); - // same dimensions but different color should yield a different checksum - CreateTestBitmap(&bitmap, 555, 333, SK_ColorGREEN, reporter); - REPORTER_ASSERT(reporter, SkBitmapHasher::ComputeDigest(bitmap, &digest)); - REPORTER_ASSERT(reporter, digest == 0x2423c51cad6d1edcULL); -}
diff --git a/src/third_party/skia/tests/BitmapHeapTest.cpp b/src/third_party/skia/tests/BitmapHeapTest.cpp deleted file mode 100644 index dc9905e..0000000 --- a/src/third_party/skia/tests/BitmapHeapTest.cpp +++ /dev/null
@@ -1,85 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkBitmap.h" -#include "SkBitmapHeap.h" -#include "SkColor.h" -#include "SkFlattenable.h" -#include "SkWriteBuffer.h" -#include "SkPictureFlat.h" -#include "SkRefCnt.h" -#include "SkShader.h" -#include "Test.h" - -struct SkShaderTraits { - static void Flatten(SkWriteBuffer& buffer, const SkShader& shader) { - buffer.writeFlattenable(&shader); - } -}; -typedef SkFlatDictionary<SkShader, SkShaderTraits> FlatDictionary; - -class SkBitmapHeapTester { - -public: - static int32_t GetRefCount(const SkBitmapHeapEntry* entry) { - return entry->fRefCount; - } -}; - -DEF_TEST(BitmapHeap, reporter) { - // Create a bitmap shader. - SkBitmap bm; - bm.allocN32Pixels(2, 2); - bm.eraseColor(SK_ColorRED); - uint32_t* pixel = bm.getAddr32(1,0); - *pixel = SK_ColorBLUE; - - SkShader* bitmapShader = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, - SkShader::kRepeat_TileMode); - SkAutoTUnref<SkShader> aur(bitmapShader); - - // Flatten, storing it in the bitmap heap. - SkBitmapHeap heap(1, 1); - SkChunkFlatController controller(1024); - controller.setBitmapStorage(&heap); - FlatDictionary dictionary(&controller); - - // Dictionary and heap start off empty. - REPORTER_ASSERT(reporter, heap.count() == 0); - REPORTER_ASSERT(reporter, dictionary.count() == 0); - - heap.deferAddingOwners(); - int index = dictionary.find(*bitmapShader); - heap.endAddingOwnersDeferral(true); - - // The dictionary and heap should now each have one entry. - REPORTER_ASSERT(reporter, 1 == index); - REPORTER_ASSERT(reporter, heap.count() == 1); - REPORTER_ASSERT(reporter, dictionary.count() == 1); - - // The bitmap entry's refcount should be 1, then 0 after release. - SkBitmapHeapEntry* entry = heap.getEntry(0); - REPORTER_ASSERT(reporter, SkBitmapHeapTester::GetRefCount(entry) == 1); - - entry->releaseRef(); - REPORTER_ASSERT(reporter, SkBitmapHeapTester::GetRefCount(entry) == 0); - - // Now clear out the heap, after which it should be empty. - heap.freeMemoryIfPossible(~0U); - REPORTER_ASSERT(reporter, heap.count() == 0); - - // Now attempt to flatten the shader again. - heap.deferAddingOwners(); - index = dictionary.find(*bitmapShader); - heap.endAddingOwnersDeferral(false); - - // The dictionary should report the same index since the new entry is identical. - // The bitmap heap should contain the bitmap, but with no references. - REPORTER_ASSERT(reporter, 1 == index); - REPORTER_ASSERT(reporter, heap.count() == 1); - REPORTER_ASSERT(reporter, SkBitmapHeapTester::GetRefCount(heap.getEntry(0)) == 0); -}
diff --git a/src/third_party/skia/tests/BitmapTest.cpp b/src/third_party/skia/tests/BitmapTest.cpp index ef69531..ddc770f 100644 --- a/src/third_party/skia/tests/BitmapTest.cpp +++ b/src/third_party/skia/tests/BitmapTest.cpp
@@ -6,8 +6,47 @@ */ #include "SkBitmap.h" - +#include "SkMallocPixelRef.h" +#include "SkRandom.h" #include "Test.h" +#include "sk_tool_utils.h" + +static void test_peekpixels(skiatest::Reporter* reporter) { + const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10); + + SkPixmap pmap; + SkBitmap bm; + + // empty should return false + REPORTER_ASSERT(reporter, !bm.peekPixels(nullptr)); + REPORTER_ASSERT(reporter, !bm.peekPixels(&pmap)); + + // no pixels should return false + bm.setInfo(SkImageInfo::MakeN32Premul(10, 10)); + REPORTER_ASSERT(reporter, !bm.peekPixels(nullptr)); + REPORTER_ASSERT(reporter, !bm.peekPixels(&pmap)); + + // real pixels should return true + bm.allocPixels(info); + REPORTER_ASSERT(reporter, bm.peekPixels(nullptr)); + REPORTER_ASSERT(reporter, bm.peekPixels(&pmap)); + REPORTER_ASSERT(reporter, pmap.info() == bm.info()); + REPORTER_ASSERT(reporter, pmap.addr() == bm.getPixels()); + REPORTER_ASSERT(reporter, pmap.rowBytes() == bm.rowBytes()); +} + +// https://code.google.com/p/chromium/issues/detail?id=446164 +static void test_bigalloc(skiatest::Reporter* reporter) { + const int width = 0x40000001; + const int height = 0x00000096; + const SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); + + SkBitmap bm; + REPORTER_ASSERT(reporter, !bm.tryAllocPixels(info)); + + sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(info, info.minRowBytes()); + REPORTER_ASSERT(reporter, !pr); +} static void test_allocpixels(skiatest::Reporter* reporter) { const int width = 10; @@ -81,4 +120,101 @@ test_bigwidth(reporter); test_allocpixels(reporter); + test_bigalloc(reporter); + test_peekpixels(reporter); } + +/** + * This test checks that getColor works for both swizzles. + */ +DEF_TEST(Bitmap_getColor_Swizzle, r) { + SkBitmap source; + source.allocN32Pixels(1,1); + source.eraseColor(SK_ColorRED); + SkColorType colorTypes[] = { + kRGBA_8888_SkColorType, + kBGRA_8888_SkColorType, + }; + for (SkColorType ct : colorTypes) { + SkBitmap copy; + if (!sk_tool_utils::copy_to(©, ct, source)) { + ERRORF(r, "SkBitmap::copy failed %d", (int)ct); + continue; + } + REPORTER_ASSERT(r, source.getColor(0, 0) == copy.getColor(0, 0)); + } +} + +static void test_erasecolor_premul(skiatest::Reporter* reporter, SkColorType ct, SkColor input, + SkColor expected) { + SkBitmap bm; + bm.allocPixels(SkImageInfo::Make(1, 1, ct, kPremul_SkAlphaType)); + bm.eraseColor(input); + INFOF(reporter, "expected: %x actual: %x\n", expected, bm.getColor(0, 0)); + REPORTER_ASSERT(reporter, bm.getColor(0, 0) == expected); +} + +/** + * This test checks that eraseColor premultiplies the color correctly. + */ +DEF_TEST(Bitmap_eraseColor_Premul, r) { + SkColor color = 0x80FF0080; + test_erasecolor_premul(r, kAlpha_8_SkColorType, color, 0x80000000); + test_erasecolor_premul(r, kRGB_565_SkColorType, color, 0xFF840042); + test_erasecolor_premul(r, kARGB_4444_SkColorType, color, 0x88FF0080); + test_erasecolor_premul(r, kRGBA_8888_SkColorType, color, color); + test_erasecolor_premul(r, kBGRA_8888_SkColorType, color, color); +} + +// Test that SkBitmap::ComputeOpaque() is correct for various colortypes. +DEF_TEST(Bitmap_compute_is_opaque, r) { + struct { + SkColorType fCT; + SkAlphaType fAT; + } types[] = { + { kGray_8_SkColorType, kOpaque_SkAlphaType }, + { kAlpha_8_SkColorType, kPremul_SkAlphaType }, + { kARGB_4444_SkColorType, kPremul_SkAlphaType }, + { kRGB_565_SkColorType, kOpaque_SkAlphaType }, + { kBGRA_8888_SkColorType, kPremul_SkAlphaType }, + { kRGBA_8888_SkColorType, kPremul_SkAlphaType }, + { kRGBA_F16_SkColorType, kPremul_SkAlphaType }, + }; + for (auto type : types) { + SkBitmap bm; + REPORTER_ASSERT(r, !SkBitmap::ComputeIsOpaque(bm)); + + bm.allocPixels(SkImageInfo::Make(13, 17, type.fCT, type.fAT)); + bm.eraseColor(SkColorSetARGB(255, 10, 20, 30)); + REPORTER_ASSERT(r, SkBitmap::ComputeIsOpaque(bm)); + + bm.eraseColor(SkColorSetARGB(128, 255, 255, 255)); + bool isOpaque = SkBitmap::ComputeIsOpaque(bm); + bool shouldBeOpaque = (type.fAT == kOpaque_SkAlphaType); + REPORTER_ASSERT(r, isOpaque == shouldBeOpaque); + } +} + +// Test that erase+getColor round trips with RGBA_F16 pixels. +DEF_TEST(Bitmap_erase_f16_erase_getColor, r) { + SkRandom random; + SkPixmap pm; + SkBitmap bm; + bm.allocPixels(SkImageInfo::Make(1, 1, kRGBA_F16_SkColorType, kPremul_SkAlphaType)); + REPORTER_ASSERT(r, bm.peekPixels(&pm)); + for (unsigned i = 0; i < 0x100; ++i) { + // Test all possible values of blue component. + SkColor color1 = (SkColor)((random.nextU() & 0xFFFFFF00) | i); + // Test all possible values of alpha component. + SkColor color2 = (SkColor)((random.nextU() & 0x00FFFFFF) | (i << 24)); + for (SkColor color : {color1, color2}) { + pm.erase(color); + if (SkColorGetA(color) != 0) { + REPORTER_ASSERT(r, color == pm.getColor(0, 0)); + } else { + REPORTER_ASSERT(r, 0 == SkColorGetA(pm.getColor(0, 0))); + } + } + } +} +
diff --git a/src/third_party/skia/tests/BlendTest.cpp b/src/third_party/skia/tests/BlendTest.cpp index a0a84d7..20ea6e0 100644 --- a/src/third_party/skia/tests/BlendTest.cpp +++ b/src/third_party/skia/tests/BlendTest.cpp
@@ -1,258 +1,206 @@ -#include "Test.h" +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <functional> +#include "SkBitmap.h" +#include "SkCanvas.h" #include "SkColor.h" +#include "SkColorPriv.h" +#include "SkSurface.h" +#include "SkTaskGroup.h" +#include "SkUtils.h" +#include "Test.h" -#define ASSERT(x) REPORTER_ASSERT(r, x) +#if SK_SUPPORT_GPU +#include "GrContext.h" +#include "GrContextPriv.h" +#include "GrResourceProvider.h" +#include "GrSurfaceContext.h" +#include "GrSurfaceProxy.h" +#include "GrTest.h" +#include "GrTexture.h" +#endif -static uint8_t double_to_u8(double d) { - SkASSERT(d >= 0); - SkASSERT(d < 256); - return uint8_t(d); +struct Results { int diffs, diffs_0x00, diffs_0xff, diffs_by_1; }; + +static bool acceptable(const Results& r) { +#if 0 + SkDebugf("%d diffs, %d at 0x00, %d at 0xff, %d off by 1, all out of 65536\n", + r.diffs, r.diffs_0x00, r.diffs_0xff, r.diffs_by_1); +#endif + return r.diffs_by_1 == r.diffs // never off by more than 1 + && r.diffs_0x00 == 0 // transparent must stay transparent + && r.diffs_0xff == 0; // opaque must stay opaque } -// All algorithms we're testing have this interface. -// We want a single channel blend, src over dst, assuming src is premultiplied by srcAlpha. -typedef uint8_t(*Blend)(uint8_t dst, uint8_t src, uint8_t srcAlpha); - -// This is our golden algorithm. -static uint8_t blend_double_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) { - SkASSERT(src <= srcAlpha); - return double_to_u8(0.5 + src + dst * (255.0 - srcAlpha) / 255.0); +template <typename Fn> +static Results test(Fn&& multiply) { + Results r = { 0,0,0,0 }; + for (int x = 0; x < 256; x++) { + for (int y = 0; y < 256; y++) { + int p = multiply(x, y), + ideal = (x*y+127)/255; + if (p != ideal) { + r.diffs++; + if (x == 0x00 || y == 0x00) { r.diffs_0x00++; } + if (x == 0xff || y == 0xff) { r.diffs_0xff++; } + if (SkTAbs(ideal - p) == 1) { r.diffs_by_1++; } + } + }} + return r; } -static uint8_t abs_diff(uint8_t a, uint8_t b) { - const int diff = a - b; - return diff > 0 ? diff : -diff; +DEF_TEST(Blend_byte_multiply, r) { + // These are all temptingly close but fundamentally broken. + int (*broken[])(int, int) = { + [](int x, int y) { return (x*y)>>8; }, + [](int x, int y) { return (x*y+128)>>8; }, + [](int x, int y) { y += y>>7; return (x*y)>>8; }, + }; + for (auto multiply : broken) { REPORTER_ASSERT(r, !acceptable(test(multiply))); } + + // These are fine to use, but not perfect. + int (*fine[])(int, int) = { + [](int x, int y) { return (x*y+x)>>8; }, + [](int x, int y) { return (x*y+y)>>8; }, + [](int x, int y) { return (x*y+255)>>8; }, + [](int x, int y) { y += y>>7; return (x*y+128)>>8; }, + }; + for (auto multiply : fine) { REPORTER_ASSERT(r, acceptable(test(multiply))); } + + // These are pefect. + int (*perfect[])(int, int) = { + [](int x, int y) { return (x*y+127)/255; }, // Duh. + [](int x, int y) { int p = (x*y+128); return (p+(p>>8))>>8; }, + [](int x, int y) { return ((x*y+128)*257)>>16; }, + }; + for (auto multiply : perfect) { REPORTER_ASSERT(r, test(multiply).diffs == 0); } } -static void test(skiatest::Reporter* r, int maxDiff, Blend algorithm, - uint8_t dst, uint8_t src, uint8_t alpha) { - const uint8_t golden = blend_double_round(dst, src, alpha); - const uint8_t blend = algorithm(dst, src, alpha); - if (abs_diff(blend, golden) > maxDiff) { - SkDebugf("dst %02x, src %02x, alpha %02x, |%02x - %02x| > %d\n", - dst, src, alpha, blend, golden, maxDiff); - ASSERT(abs_diff(blend, golden) <= maxDiff); +#if SK_SUPPORT_GPU +namespace { +static sk_sp<SkSurface> create_gpu_surface_backend_texture_as_render_target( + GrContext* context, int sampleCnt, int width, int height, GrPixelConfig config, + GrSurfaceOrigin origin, + sk_sp<GrTexture>* backingSurface) { + GrSurfaceDesc backingDesc; + backingDesc.fFlags = kRenderTarget_GrSurfaceFlag; + backingDesc.fOrigin = origin; + backingDesc.fWidth = width; + backingDesc.fHeight = height; + backingDesc.fConfig = config; + backingDesc.fSampleCnt = sampleCnt; + + *backingSurface = context->resourceProvider()->createTexture(backingDesc, SkBudgeted::kNo); + if (!(*backingSurface)) { + return nullptr; } + + GrBackendTexture backendTex = + GrTest::CreateBackendTexture(context->contextPriv().getBackend(), + width, + height, + config, + (*backingSurface)->getTextureHandle()); + sk_sp<SkSurface> surface = + SkSurface::MakeFromBackendTextureAsRenderTarget(context, backendTex, origin, + sampleCnt, nullptr, nullptr); + + return surface; +} } -// Exhaustively compare an algorithm against our golden, for a given alpha. -static void test_alpha(skiatest::Reporter* r, uint8_t alpha, int maxDiff, Blend algorithm) { - SkASSERT(maxDiff >= 0); +// Tests blending to a surface with no texture available. +DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ES2BlendWithNoTexture, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + const int kWidth = 10; + const int kHeight = 10; + const GrPixelConfig kConfig = kRGBA_8888_GrPixelConfig; + const SkColorType kColorType = kRGBA_8888_SkColorType; - for (unsigned src = 0; src <= alpha; src++) { - for (unsigned dst = 0; dst < 256; dst++) { - test(r, maxDiff, algorithm, dst, src, alpha); + // Build our test cases: + struct RectAndSamplePoint { + SkRect rect; + SkIPoint outPoint; + SkIPoint inPoint; + } allRectsAndPoints[3] = { + {SkRect::MakeXYWH(0, 0, 5, 5), SkIPoint::Make(7, 7), SkIPoint::Make(2, 2)}, + {SkRect::MakeXYWH(2, 2, 5, 5), SkIPoint::Make(1, 1), SkIPoint::Make(4, 4)}, + {SkRect::MakeXYWH(5, 5, 5, 5), SkIPoint::Make(2, 2), SkIPoint::Make(7, 7)}, + }; + + struct TestCase { + RectAndSamplePoint fRectAndPoints; + SkRect fClip; + int fSampleCnt; + GrSurfaceOrigin fOrigin; + }; + std::vector<TestCase> testCases; + + for (auto origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) { + for (int sampleCnt : {0, 4}) { + for (auto rectAndPoints : allRectsAndPoints) { + for (auto clip : {SkRect::MakeXYWH(0, 0, 10, 10), SkRect::MakeXYWH(1, 1, 8, 8)}) { + testCases.push_back({rectAndPoints, clip, sampleCnt, origin}); + } + } } } -} -// Exhaustively compare an algorithm against our golden, for a given dst. -static void test_dst(skiatest::Reporter* r, uint8_t dst, int maxDiff, Blend algorithm) { - SkASSERT(maxDiff >= 0); + // Run each test case: + for (auto testCase : testCases) { + int sampleCnt = testCase.fSampleCnt; + SkRect paintRect = testCase.fRectAndPoints.rect; + SkIPoint outPoint = testCase.fRectAndPoints.outPoint; + SkIPoint inPoint = testCase.fRectAndPoints.inPoint; + GrSurfaceOrigin origin = testCase.fOrigin; - for (unsigned alpha = 0; alpha < 256; alpha++) { - for (unsigned src = 0; src <= alpha; src++) { - test(r, maxDiff, algorithm, dst, src, alpha); + sk_sp<GrTexture> backingSurface; + // BGRA forces a framebuffer blit on ES2. + sk_sp<SkSurface> surface = create_gpu_surface_backend_texture_as_render_target( + context, sampleCnt, kWidth, kHeight, kConfig, origin, &backingSurface); + + if (!surface && sampleCnt > 0) { + // Some platforms don't support MSAA. + continue; } + REPORTER_ASSERT(reporter, !!surface); + + // Fill our canvas with 0xFFFF80 + SkCanvas* canvas = surface->getCanvas(); + canvas->clipRect(testCase.fClip, false); + SkPaint black_paint; + black_paint.setColor(SkColorSetRGB(0xFF, 0xFF, 0x80)); + canvas->drawRect(SkRect::MakeXYWH(0, 0, kWidth, kHeight), black_paint); + + // Blend 2x2 pixels at 5,5 with 0x80FFFF. Use multiply blend mode as this will trigger + // a copy of the destination. + SkPaint white_paint; + white_paint.setColor(SkColorSetRGB(0x80, 0xFF, 0xFF)); + white_paint.setBlendMode(SkBlendMode::kMultiply); + canvas->drawRect(paintRect, white_paint); + + // Read the result into a bitmap. + SkBitmap bitmap; + REPORTER_ASSERT(reporter, bitmap.tryAllocPixels(SkImageInfo::Make( + kWidth, kHeight, kColorType, kPremul_SkAlphaType))); + REPORTER_ASSERT( + reporter, + surface->readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 0, 0)); + + // Check the in/out pixels. + REPORTER_ASSERT(reporter, bitmap.getColor(outPoint.x(), outPoint.y()) == + SkColorSetRGB(0xFF, 0xFF, 0x80)); + REPORTER_ASSERT(reporter, bitmap.getColor(inPoint.x(), inPoint.y()) == + SkColorSetRGB(0x80, 0xFF, 0x80)); + + // Clean up - surface depends on backingSurface and must be released first. + surface.reset(); + backingSurface.reset(); } } - -static uint8_t blend_double_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) { - return double_to_u8(src + dst * (255.0 - srcAlpha) / 255.0); -} - -static uint8_t blend_float_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) { - return double_to_u8(src + dst * (255.0f - srcAlpha) / 255.0f); -} - -static uint8_t blend_float_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) { - return double_to_u8(0.5f + src + dst * (255.0f - srcAlpha) / 255.0f); -} - -static uint8_t blend_255_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) { - const uint16_t invAlpha = 255 - srcAlpha; - const uint16_t product = dst * invAlpha; - return src + (product >> 8); -} - -static uint8_t blend_255_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) { - const uint16_t invAlpha = 255 - srcAlpha; - const uint16_t product = dst * invAlpha + 128; - return src + (product >> 8); -} - -static uint8_t blend_256_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) { - const uint16_t invAlpha = 256 - (srcAlpha + (srcAlpha >> 7)); - const uint16_t product = dst * invAlpha; - return src + (product >> 8); -} - -static uint8_t blend_256_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) { - const uint16_t invAlpha = 256 - (srcAlpha + (srcAlpha >> 7)); - const uint16_t product = dst * invAlpha + 128; - return src + (product >> 8); -} - -static uint8_t blend_256_round_alt(uint8_t dst, uint8_t src, uint8_t srcAlpha) { - const uint8_t invAlpha8 = 255 - srcAlpha; - const uint16_t invAlpha = invAlpha8 + (invAlpha8 >> 7); - const uint16_t product = dst * invAlpha + 128; - return src + (product >> 8); -} - -static uint8_t blend_256_plus1_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) { - const uint16_t invAlpha = 256 - (srcAlpha + 1); - const uint16_t product = dst * invAlpha; - return src + (product >> 8); -} - -static uint8_t blend_256_plus1_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) { - const uint16_t invAlpha = 256 - (srcAlpha + 1); - const uint16_t product = dst * invAlpha + 128; - return src + (product >> 8); -} - -static uint8_t blend_perfect(uint8_t dst, uint8_t src, uint8_t srcAlpha) { - const uint8_t invAlpha = 255 - srcAlpha; - const uint16_t product = dst * invAlpha + 128; - return src + ((product + (product >> 8)) >> 8); -} - - -// We want 0 diff whenever src is fully transparent. -DEF_TEST(Blend_alpha_0x00, r) { - const uint8_t alpha = 0x00; - - // GOOD - test_alpha(r, alpha, 0, blend_256_round); - test_alpha(r, alpha, 0, blend_256_round_alt); - test_alpha(r, alpha, 0, blend_256_trunc); - test_alpha(r, alpha, 0, blend_double_trunc); - test_alpha(r, alpha, 0, blend_float_round); - test_alpha(r, alpha, 0, blend_float_trunc); - test_alpha(r, alpha, 0, blend_perfect); - - // BAD - test_alpha(r, alpha, 1, blend_255_round); - test_alpha(r, alpha, 1, blend_255_trunc); - test_alpha(r, alpha, 1, blend_256_plus1_round); - test_alpha(r, alpha, 1, blend_256_plus1_trunc); -} - -// We want 0 diff whenever dst is 0. -DEF_TEST(Blend_dst_0x00, r) { - const uint8_t dst = 0x00; - - // GOOD - test_dst(r, dst, 0, blend_255_round); - test_dst(r, dst, 0, blend_255_trunc); - test_dst(r, dst, 0, blend_256_plus1_round); - test_dst(r, dst, 0, blend_256_plus1_trunc); - test_dst(r, dst, 0, blend_256_round); - test_dst(r, dst, 0, blend_256_round_alt); - test_dst(r, dst, 0, blend_256_trunc); - test_dst(r, dst, 0, blend_double_trunc); - test_dst(r, dst, 0, blend_float_round); - test_dst(r, dst, 0, blend_float_trunc); - test_dst(r, dst, 0, blend_perfect); - - // BAD -} - -// We want 0 diff whenever src is fully opaque. -DEF_TEST(Blend_alpha_0xFF, r) { - const uint8_t alpha = 0xFF; - - // GOOD - test_alpha(r, alpha, 0, blend_255_round); - test_alpha(r, alpha, 0, blend_255_trunc); - test_alpha(r, alpha, 0, blend_256_plus1_round); - test_alpha(r, alpha, 0, blend_256_plus1_trunc); - test_alpha(r, alpha, 0, blend_256_round); - test_alpha(r, alpha, 0, blend_256_round_alt); - test_alpha(r, alpha, 0, blend_256_trunc); - test_alpha(r, alpha, 0, blend_double_trunc); - test_alpha(r, alpha, 0, blend_float_round); - test_alpha(r, alpha, 0, blend_float_trunc); - test_alpha(r, alpha, 0, blend_perfect); - - // BAD -} - -// We want 0 diff whenever dst is 0xFF. -DEF_TEST(Blend_dst_0xFF, r) { - const uint8_t dst = 0xFF; - - // GOOD - test_dst(r, dst, 0, blend_256_round); - test_dst(r, dst, 0, blend_256_round_alt); - test_dst(r, dst, 0, blend_double_trunc); - test_dst(r, dst, 0, blend_float_round); - test_dst(r, dst, 0, blend_float_trunc); - test_dst(r, dst, 0, blend_perfect); - - // BAD - test_dst(r, dst, 1, blend_255_round); - test_dst(r, dst, 1, blend_255_trunc); - test_dst(r, dst, 1, blend_256_plus1_round); - test_dst(r, dst, 1, blend_256_plus1_trunc); - test_dst(r, dst, 1, blend_256_trunc); -} - -// We'd like diff <= 1 everywhere. -DEF_TEST(Blend_alpha_Exhaustive, r) { - for (unsigned alpha = 0; alpha < 256; alpha++) { - // PERFECT - test_alpha(r, alpha, 0, blend_float_round); - test_alpha(r, alpha, 0, blend_perfect); - - // GOOD - test_alpha(r, alpha, 1, blend_255_round); - test_alpha(r, alpha, 1, blend_256_plus1_round); - test_alpha(r, alpha, 1, blend_256_round); - test_alpha(r, alpha, 1, blend_256_round_alt); - test_alpha(r, alpha, 1, blend_256_trunc); - test_alpha(r, alpha, 1, blend_double_trunc); - test_alpha(r, alpha, 1, blend_float_trunc); - - // BAD - test_alpha(r, alpha, 2, blend_255_trunc); - test_alpha(r, alpha, 2, blend_256_plus1_trunc); - } -} - -// We'd like diff <= 1 everywhere. -DEF_TEST(Blend_dst_Exhaustive, r) { - for (unsigned dst = 0; dst < 256; dst++) { - // PERFECT - test_dst(r, dst, 0, blend_float_round); - test_dst(r, dst, 0, blend_perfect); - - // GOOD - test_dst(r, dst, 1, blend_255_round); - test_dst(r, dst, 1, blend_256_plus1_round); - test_dst(r, dst, 1, blend_256_round); - test_dst(r, dst, 1, blend_256_round_alt); - test_dst(r, dst, 1, blend_256_trunc); - test_dst(r, dst, 1, blend_double_trunc); - test_dst(r, dst, 1, blend_float_trunc); - - // BAD - test_dst(r, dst, 2, blend_255_trunc); - test_dst(r, dst, 2, blend_256_plus1_trunc); - } -} -// Overall summary: -// PERFECT -// blend_double_round -// blend_float_round -// blend_perfect -// GOOD ENOUGH -// blend_double_trunc -// blend_float_trunc -// blend_256_round -// blend_256_round_alt -// NOT GOOD ENOUGH -// all others -// -// Algorithms that make sense to use in Skia: blend_256_round, blend_256_round_alt, blend_perfect +#endif
diff --git a/src/third_party/skia/tests/BlitMaskClip.cpp b/src/third_party/skia/tests/BlitMaskClip.cpp new file mode 100644 index 0000000..21fe562 --- /dev/null +++ b/src/third_party/skia/tests/BlitMaskClip.cpp
@@ -0,0 +1,70 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkBlitter.h" +#include "SkMask.h" +#include "Test.h" +#include <string.h> + +class TestBlitter : public SkBlitter { +public: + TestBlitter(SkIRect bounds, skiatest::Reporter* reporter) + : fBounds(bounds) + , fReporter(reporter) { } + + void blitH(int x, int y, int width) override { + + REPORTER_ASSERT(fReporter, x >= fBounds.fLeft && x < fBounds.fRight); + REPORTER_ASSERT(fReporter, y >= fBounds.fTop && y < fBounds.fBottom); + int right = x + width; + REPORTER_ASSERT(fReporter, right > fBounds.fLeft && right <= fBounds.fRight); + } + + void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) override { + SkDEBUGFAIL("blitAntiH not implemented"); + } + +private: + SkIRect fBounds; + skiatest::Reporter* fReporter; +}; + +// Exercise all clips compared with different widths of bitMask. Make sure that no buffer +// overruns happen. +DEF_TEST(BlitAndClip, reporter) { + const int originX = 100; + const int originY = 100; + for (int width = 1; width <= 32; width++) { + const int height = 2; + int rowBytes = (width + 7) >> 3; + uint8_t* bits = new uint8_t[rowBytes * height]; + memset(bits, 0xAA, rowBytes * height); + + SkIRect b = {originX, originY, originX + width, originY + height}; + + SkMask mask; + mask.fFormat = SkMask::kBW_Format; + mask.fBounds = b; + mask.fImage = (uint8_t*)bits; + mask.fRowBytes = rowBytes; + + TestBlitter tb(mask.fBounds, reporter); + + for (int top = b.fTop; top < b.fBottom; top++) { + for (int bottom = top + 1; bottom <= b.fBottom; bottom++) { + for (int left = b.fLeft; left < b.fRight; left++) { + for (int right = left + 1; right <= b.fRight; right++) { + SkIRect clipRect = {left, top, right, bottom}; + tb.blitMask(mask, clipRect); + } + } + } + } + + delete [] bits; + } +}
diff --git a/src/third_party/skia/tests/BlitRowTest.cpp b/src/third_party/skia/tests/BlitRowTest.cpp deleted file mode 100644 index 4689a30..0000000 --- a/src/third_party/skia/tests/BlitRowTest.cpp +++ /dev/null
@@ -1,264 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkBitmap.h" -#include "SkCanvas.h" -#include "SkColorPriv.h" -#include "SkGradientShader.h" -#include "SkRect.h" -#include "Test.h" - -// these are in the same order as the SkColorType enum -static const char* gColorTypeName[] = { - "None", "A8", "565", "4444", "RGBA", "BGRA", "Index8" -}; - -/** Returns -1 on success, else the x coord of the first bad pixel, return its - value in bad - */ -typedef int (*Proc)(const void*, int width, uint32_t expected, uint32_t* bad); - -static int proc_32(const void* ptr, int w, uint32_t expected, uint32_t* bad) { - const SkPMColor* addr = static_cast<const SkPMColor*>(ptr); - for (int x = 0; x < w; x++) { - if (addr[x] != expected) { - *bad = addr[x]; - return x; - } - } - return -1; -} - -static int proc_16(const void* ptr, int w, uint32_t expected, uint32_t* bad) { - const uint16_t* addr = static_cast<const uint16_t*>(ptr); - for (int x = 0; x < w; x++) { - if (addr[x] != expected) { - *bad = addr[x]; - return x; - } - } - return -1; -} - -static int proc_8(const void* ptr, int w, uint32_t expected, uint32_t* bad) { - const SkPMColor* addr = static_cast<const SkPMColor*>(ptr); - for (int x = 0; x < w; x++) { - if (SkGetPackedA32(addr[x]) != expected) { - *bad = SkGetPackedA32(addr[x]); - return x; - } - } - return -1; -} - -static int proc_bad(const void*, int, uint32_t, uint32_t* bad) { - *bad = 0; - return 0; -} - -static Proc find_proc(const SkBitmap& bm, SkPMColor expect32, uint16_t expect16, - uint8_t expect8, uint32_t* expect) { - switch (bm.colorType()) { - case kN32_SkColorType: - *expect = expect32; - return proc_32; - case kARGB_4444_SkColorType: - case kRGB_565_SkColorType: - *expect = expect16; - return proc_16; - case kAlpha_8_SkColorType: - *expect = expect8; - return proc_8; - default: - *expect = 0; - return proc_bad; - } -} - -static bool check_color(const SkBitmap& bm, SkPMColor expect32, - uint16_t expect16, uint8_t expect8, - skiatest::Reporter* reporter) { - uint32_t expect; - Proc proc = find_proc(bm, expect32, expect16, expect8, &expect); - for (int y = 0; y < bm.height(); y++) { - uint32_t bad; - int x = proc(bm.getAddr(0, y), bm.width(), expect, &bad); - if (x >= 0) { - ERRORF(reporter, "BlitRow colortype=%s [%d %d] expected %x got %x", - gColorTypeName[bm.colorType()], x, y, expect, bad); - return false; - } - } - return true; -} - -// Make sure our blits always map src==0 to a noop, and src==FF to full opaque -static void test_00_FF(skiatest::Reporter* reporter) { - static const int W = 256; - - static const SkColorType gDstColorType[] = { - kN32_SkColorType, - kRGB_565_SkColorType, - }; - - static const struct { - SkColor fSrc; - SkColor fDst; - SkPMColor fResult32; - uint16_t fResult16; - uint8_t fResult8; - } gSrcRec[] = { - { 0, 0, 0, 0, 0 }, - { 0, 0xFFFFFFFF, SkPackARGB32(0xFF, 0xFF, 0xFF, 0xFF), 0xFFFF, 0xFF }, - { 0xFFFFFFFF, 0, SkPackARGB32(0xFF, 0xFF, 0xFF, 0xFF), 0xFFFF, 0xFF }, - { 0xFFFFFFFF, 0xFFFFFFFF, SkPackARGB32(0xFF, 0xFF, 0xFF, 0xFF), 0xFFFF, 0xFF }, - }; - - SkPaint paint; - - SkBitmap srcBM; - srcBM.allocN32Pixels(W, 1); - - for (size_t i = 0; i < SK_ARRAY_COUNT(gDstColorType); i++) { - SkImageInfo info = SkImageInfo::Make(W, 1, gDstColorType[i], - kPremul_SkAlphaType); - SkBitmap dstBM; - dstBM.allocPixels(info); - - SkCanvas canvas(dstBM); - for (size_t j = 0; j < SK_ARRAY_COUNT(gSrcRec); j++) { - srcBM.eraseColor(gSrcRec[j].fSrc); - dstBM.eraseColor(gSrcRec[j].fDst); - - for (int k = 0; k < 4; k++) { - bool dither = (k & 1) != 0; - bool blend = (k & 2) != 0; - if (gSrcRec[j].fSrc != 0 && blend) { - // can't make a numerical promise about blending anything - // but 0 - // continue; - } - paint.setDither(dither); - paint.setAlpha(blend ? 0x80 : 0xFF); - canvas.drawBitmap(srcBM, 0, 0, &paint); - if (!check_color(dstBM, gSrcRec[j].fResult32, gSrcRec[j].fResult16, - gSrcRec[j].fResult8, reporter)) { - SkDebugf("--- src index %d dither %d blend %d\n", j, dither, blend); - } - } - } - } -} - -/////////////////////////////////////////////////////////////////////////////// - -struct Mesh { - SkPoint fPts[4]; - - Mesh(const SkBitmap& bm, SkPaint* paint) { - const SkScalar w = SkIntToScalar(bm.width()); - const SkScalar h = SkIntToScalar(bm.height()); - fPts[0].set(0, 0); - fPts[1].set(w, 0); - fPts[2].set(w, h); - fPts[3].set(0, h); - SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kClamp_TileMode, - SkShader::kClamp_TileMode); - paint->setShader(s)->unref(); - - } - - void draw(SkCanvas* canvas, SkPaint* paint) { - canvas->drawVertices(SkCanvas::kTriangleFan_VertexMode, 4, fPts, fPts, - NULL, NULL, NULL, 0, *paint); - } -}; - -#include "SkImageEncoder.h" -static void save_bm(const SkBitmap& bm, const char name[]) { - SkImageEncoder::EncodeFile(name, bm, SkImageEncoder::kPNG_Type, 100); -} - -static bool gOnce; - -// Make sure our blits are invariant with the width of the blit (i.e. that -// special case for 8 at a time have the same results as narrower blits) -static void test_diagonal(skiatest::Reporter* reporter) { - static const int W = 64; - static const int H = W; - - static const SkColorType gDstColorType[] = { - kN32_SkColorType, - kRGB_565_SkColorType, - }; - - static const SkColor gDstBG[] = { 0, 0xFFFFFFFF }; - - SkPaint paint; - - SkBitmap srcBM; - srcBM.allocN32Pixels(W, H); - SkRect srcR = { - 0, 0, SkIntToScalar(srcBM.width()), SkIntToScalar(srcBM.height()) }; - - // cons up a mesh to draw the bitmap with - Mesh mesh(srcBM, &paint); - - SkImageInfo info = SkImageInfo::Make(W, H, kUnknown_SkColorType, - kPremul_SkAlphaType); - - for (size_t i = 0; i < SK_ARRAY_COUNT(gDstColorType); i++) { - info = info.makeColorType(gDstColorType[i]); - - SkBitmap dstBM0, dstBM1; - dstBM0.allocPixels(info); - dstBM1.allocPixels(info); - - SkCanvas canvas0(dstBM0); - SkCanvas canvas1(dstBM1); - SkColor bgColor; - - for (size_t j = 0; j < SK_ARRAY_COUNT(gDstBG); j++) { - bgColor = gDstBG[j]; - - for (int c = 0; c <= 0xFF; c++) { - srcBM.eraseARGB(0xFF, c, c, c); - - for (int k = 0; k < 4; k++) { - bool dither = (k & 1) != 0; - uint8_t alpha = (k & 2) ? 0x80 : 0xFF; - paint.setDither(dither); - paint.setAlpha(alpha); - - dstBM0.eraseColor(bgColor); - dstBM1.eraseColor(bgColor); - - canvas0.drawRect(srcR, paint); - mesh.draw(&canvas1, &paint); - - if (!gOnce && false) { - save_bm(dstBM0, "drawBitmap.png"); - save_bm(dstBM1, "drawMesh.png"); - gOnce = true; - } - - if (memcmp(dstBM0.getPixels(), dstBM1.getPixels(), dstBM0.getSize())) { - ERRORF(reporter, "Diagonal colortype=%s bg=0x%x dither=%d" - " alpha=0x%x src=0x%x", - gColorTypeName[gDstColorType[i]], bgColor, dither, - alpha, c); - } - } - } - } - } -} - -DEF_TEST(BlitRow, reporter) { - test_00_FF(reporter); - test_diagonal(reporter); -}
diff --git a/src/third_party/skia/tests/BlurTest.cpp b/src/third_party/skia/tests/BlurTest.cpp index 143d777..5d95718 100644 --- a/src/third_party/skia/tests/BlurTest.cpp +++ b/src/third_party/skia/tests/BlurTest.cpp
@@ -8,11 +8,13 @@ #include "SkBlurMask.h" #include "SkBlurMaskFilter.h" #include "SkBlurDrawLooper.h" -#include "SkLayerDrawLooper.h" -#include "SkEmbossMaskFilter.h" #include "SkCanvas.h" +#include "SkColorFilter.h" +#include "SkEmbossMaskFilter.h" +#include "SkLayerDrawLooper.h" #include "SkMath.h" #include "SkPaint.h" +#include "SkPath.h" #include "Test.h" #if SK_SUPPORT_GPU @@ -74,9 +76,6 @@ const int xOff = itest.fLeft - iref.fLeft; const int yOff = itest.fTop - iref.fTop; - SkAutoLockPixels alpRef(ref); - SkAutoLockPixels alpTest(test); - for (int y = 0; y < test.height(); ++y) { for (int x = 0; x < test.width(); ++x) { SkColor testColor = test.getColor(x, y); @@ -98,8 +97,7 @@ return true; } -static void test_blur_drawing(skiatest::Reporter* reporter) { - +DEF_TEST(BlurDrawing, reporter) { SkPaint paint; paint.setColor(SK_ColorGRAY); paint.setStyle(SkPaint::kStroke_Style); @@ -111,11 +109,7 @@ const uint32_t flagPermutations = SkBlurMaskFilter::kAll_BlurFlag; for (uint32_t flags = 0; flags < flagPermutations; ++flags) { - SkMaskFilter* filter; - filter = SkBlurMaskFilter::Create(blurStyle, sigma, flags); - - paint.setMaskFilter(filter); - filter->unref(); + paint.setMaskFilter(SkBlurMaskFilter::Make(blurStyle, sigma, flags)); for (size_t test = 0; test < SK_ARRAY_COUNT(tests); ++test) { SkPath path; @@ -170,8 +164,9 @@ memset(src.fImage, 0xff, src.computeTotalImageSize()); - dst.fImage = NULL; - SkBlurMask::BlurGroundTruth(sigma, &dst, src, kNormal_SkBlurStyle); + if (!SkBlurMask::BlurGroundTruth(sigma, &dst, src, kNormal_SkBlurStyle)) { + return; + } int midX = dst.fBounds.centerX(); int midY = dst.fBounds.centerY(); @@ -233,10 +228,8 @@ SkPaint blurPaint; blurPaint.setColor(SK_ColorWHITE); - SkMaskFilter* filter = SkBlurMaskFilter::Create(kNormal_SkBlurStyle, - gaussianSigma, - SkBlurMaskFilter::kHighQuality_BlurFlag); - blurPaint.setMaskFilter(filter)->unref(); + blurPaint.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, gaussianSigma, + SkBlurMaskFilter::kHighQuality_BlurFlag)); canvas->drawColor(SK_ColorBLACK); canvas->drawPath(path, blurPaint); @@ -246,12 +239,8 @@ static void readback(SkCanvas* canvas, int* result, int resultCount) { SkBitmap readback; readback.allocN32Pixels(resultCount, 30); + canvas->readPixels(readback, 0, 0); - SkIRect readBackRect = { 0, 0, resultCount, 30 }; - - canvas->readPixels(readBackRect, &readback); - - readback.lockPixels(); SkPMColor* pixels = (SkPMColor*) readback.getAddr32(0, 15); for (int i = 0; i < resultCount; ++i) { @@ -275,24 +264,18 @@ #if SK_SUPPORT_GPU #if 0 // temporary disable; see below for explanation -static bool gpu_blur_path(GrContextFactory* factory, const SkPath& path, +static bool gpu_blur_path(GrContext* context, const SkPath& path, SkScalar gaussianSigma, int* result, int resultCount) { - - GrContext* grContext = factory->get(GrContextFactory::kNative_GLContextType); - if (NULL == grContext) { - return false; - } - - GrTextureDesc desc; + GrSurfaceDesc desc; desc.fConfig = kSkia8888_GrPixelConfig; - desc.fFlags = kRenderTarget_GrTextureFlagBit; + desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = resultCount; desc.fHeight = 30; desc.fSampleCnt = 0; - SkAutoTUnref<GrTexture> texture(grContext->createUncachedTexture(desc, NULL, 0)); - SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice, (grContext, texture.get()))); + sk_sp<GrTexture> texture(grContext->createTexture(desc, false, nullptr, 0)); + sk_sp<SkGpuDevice> device(new SkGpuDevice(grContext, texture.get())); SkCanvas canvas(device.get()); blur_path(&canvas, path, gaussianSigma); @@ -325,8 +308,7 @@ } // Test out the normal blur style with a wide range of sigmas -static void test_sigma_range(skiatest::Reporter* reporter, GrContextFactory* factory) { - +DEF_TEST(BlurSigmaRange, reporter) { static const int kSize = 100; // The geometry is offset a smidge to trigger: @@ -364,7 +346,7 @@ #if SK_SUPPORT_GPU #if 0 int gpuResult[kSize]; - bool haveGPUResult = gpu_blur_path(factory, rectPath, sigma, gpuResult, kSize); + bool haveGPUResult = gpu_blur_path(context, rectPath, sigma, gpuResult, kSize); // Disabling this test for now -- I don't think it's a legit comparison. // Will continue to investigate this. if (haveGPUResult) { @@ -394,24 +376,6 @@ kHigh_SkBlurQuality : kLow_SkBlurQuality; } -static uint32_t blurMaskFilterFlags_to_blurDrawLooperFlags(uint32_t bmf) { - const struct { - uint32_t fBlurMaskFilterFlag; - uint32_t fBlurDrawLooperFlag; - } pairs[] = { - { SkBlurMaskFilter::kIgnoreTransform_BlurFlag, SkBlurDrawLooper::kIgnoreTransform_BlurFlag }, - { SkBlurMaskFilter::kHighQuality_BlurFlag, SkBlurDrawLooper::kHighQuality_BlurFlag }, - }; - - uint32_t bdl = 0; - for (size_t i = 0; i < SK_ARRAY_COUNT(pairs); ++i) { - if (bmf & pairs[i].fBlurMaskFilterFlag) { - bdl |= pairs[i].fBlurDrawLooperFlag; - } - } - return bdl; -} - static void test_blurDrawLooper(skiatest::Reporter* reporter, SkScalar sigma, SkBlurStyle style, uint32_t blurMaskFilterFlags) { if (kNormal_SkBlurStyle != style) { @@ -421,15 +385,10 @@ const SkColor color = 0xFF335577; const SkScalar dx = 10; const SkScalar dy = -5; - const SkBlurQuality quality = blurMaskFilterFlags_as_quality(blurMaskFilterFlags); - uint32_t flags = blurMaskFilterFlags_to_blurDrawLooperFlags(blurMaskFilterFlags); + sk_sp<SkDrawLooper> lp(SkBlurDrawLooper::Make(color, sigma, dx, dy)); + const bool expectSuccess = sigma > 0; - SkAutoTUnref<SkDrawLooper> lp(SkBlurDrawLooper::Create(color, sigma, dx, dy, flags)); - - const bool expectSuccess = sigma > 0 && - 0 == (flags & SkBlurDrawLooper::kIgnoreTransform_BlurFlag); - - if (NULL == lp.get()) { + if (nullptr == lp) { REPORTER_ASSERT(reporter, sigma <= 0); } else { SkDrawLooper::BlurShadowRec rec; @@ -441,13 +400,13 @@ REPORTER_ASSERT(reporter, rec.fOffset.y() == dy); REPORTER_ASSERT(reporter, rec.fColor == color); REPORTER_ASSERT(reporter, rec.fStyle == style); - REPORTER_ASSERT(reporter, rec.fQuality == quality); + REPORTER_ASSERT(reporter, rec.fQuality == kLow_SkBlurQuality); } } } -static void test_delete_looper(skiatest::Reporter* reporter, SkDrawLooper* lp, SkScalar sigma, - SkBlurStyle style, SkBlurQuality quality, bool expectSuccess) { +static void test_looper(skiatest::Reporter* reporter, sk_sp<SkDrawLooper> lp, SkScalar sigma, + SkBlurStyle style, SkBlurQuality quality, bool expectSuccess) { SkDrawLooper::BlurShadowRec rec; bool success = lp->asABlurShadow(&rec); REPORTER_ASSERT(reporter, success == expectSuccess); @@ -459,54 +418,54 @@ REPORTER_ASSERT(reporter, rec.fStyle == style); REPORTER_ASSERT(reporter, rec.fQuality == quality); } - lp->unref(); } static void make_noop_layer(SkLayerDrawLooper::Builder* builder) { SkLayerDrawLooper::LayerInfo info; info.fPaintBits = 0; - info.fColorMode = SkXfermode::kDst_Mode; + info.fColorMode = SkBlendMode::kDst; builder->addLayer(info); } -static void make_blur_layer(SkLayerDrawLooper::Builder* builder, SkMaskFilter* mf) { +static void make_blur_layer(SkLayerDrawLooper::Builder* builder, sk_sp<SkMaskFilter> mf) { SkLayerDrawLooper::LayerInfo info; info.fPaintBits = SkLayerDrawLooper::kMaskFilter_Bit; - info.fColorMode = SkXfermode::kSrc_Mode; + info.fColorMode = SkBlendMode::kSrc; SkPaint* paint = builder->addLayer(info); - paint->setMaskFilter(mf); + paint->setMaskFilter(std::move(mf)); } -static void test_layerDrawLooper(skiatest::Reporter* reporter, SkMaskFilter* mf, SkScalar sigma, - SkBlurStyle style, SkBlurQuality quality, bool expectSuccess) { +static void test_layerDrawLooper(skiatest::Reporter* reporter, sk_sp<SkMaskFilter> mf, + SkScalar sigma, SkBlurStyle style, SkBlurQuality quality, + bool expectSuccess) { SkLayerDrawLooper::LayerInfo info; SkLayerDrawLooper::Builder builder; // 1 layer is too few make_noop_layer(&builder); - test_delete_looper(reporter, builder.detachLooper(), sigma, style, quality, false); + test_looper(reporter, builder.detach(), sigma, style, quality, false); // 2 layers is good, but need blur make_noop_layer(&builder); make_noop_layer(&builder); - test_delete_looper(reporter, builder.detachLooper(), sigma, style, quality, false); + test_looper(reporter, builder.detach(), sigma, style, quality, false); // 2 layers is just right make_noop_layer(&builder); make_blur_layer(&builder, mf); - test_delete_looper(reporter, builder.detachLooper(), sigma, style, quality, expectSuccess); + test_looper(reporter, builder.detach(), sigma, style, quality, expectSuccess); // 3 layers is too many make_noop_layer(&builder); make_blur_layer(&builder, mf); make_noop_layer(&builder); - test_delete_looper(reporter, builder.detachLooper(), sigma, style, quality, false); + test_looper(reporter, builder.detach(), sigma, style, quality, false); } -static void test_asABlur(skiatest::Reporter* reporter) { +DEF_TEST(BlurAsABlur, reporter) { const SkBlurStyle styles[] = { kNormal_SkBlurStyle, kSolid_SkBlurStyle, kOuter_SkBlurStyle, kInner_SkBlurStyle }; @@ -518,14 +477,14 @@ // Test asABlur for SkBlurMaskFilter // for (size_t i = 0; i < SK_ARRAY_COUNT(styles); ++i) { - const SkBlurStyle style = (SkBlurStyle)styles[i]; + const SkBlurStyle style = styles[i]; for (size_t j = 0; j < SK_ARRAY_COUNT(sigmas); ++j) { const SkScalar sigma = sigmas[j]; for (int flags = 0; flags <= SkBlurMaskFilter::kAll_BlurFlag; ++flags) { const SkBlurQuality quality = blurMaskFilterFlags_as_quality(flags); - SkAutoTUnref<SkMaskFilter> mf(SkBlurMaskFilter::Create(style, sigma, flags)); - if (NULL == mf.get()) { + sk_sp<SkMaskFilter> mf(SkBlurMaskFilter::Make(style, sigma, flags)); + if (nullptr == mf.get()) { REPORTER_ASSERT(reporter, sigma <= 0); } else { REPORTER_ASSERT(reporter, sigma > 0); @@ -539,7 +498,7 @@ REPORTER_ASSERT(reporter, rec.fStyle == style); REPORTER_ASSERT(reporter, rec.fQuality == quality); } - test_layerDrawLooper(reporter, mf, sigma, style, quality, success); + test_layerDrawLooper(reporter, std::move(mf), sigma, style, quality, success); } test_blurDrawLooper(reporter, sigma, style, flags); } @@ -554,8 +513,8 @@ }; for (size_t j = 0; j < SK_ARRAY_COUNT(sigmas); ++j) { const SkScalar sigma = sigmas[j]; - SkAutoTUnref<SkMaskFilter> mf(SkEmbossMaskFilter::Create(sigma, light)); - if (mf.get()) { + auto mf(SkEmbossMaskFilter::Make(sigma, light)); + if (mf) { SkMaskFilter::BlurRec rec; bool success = mf->asABlur(&rec); REPORTER_ASSERT(reporter, !success); @@ -564,10 +523,166 @@ } } -/////////////////////////////////////////////////////////////////////////////////////////// +#if SK_SUPPORT_GPU -DEF_GPUTEST(Blur, reporter, factory) { - test_blur_drawing(reporter); - test_sigma_range(reporter, factory); - test_asABlur(reporter); +// This exercises the problem discovered in crbug.com/570232. The return value from +// SkBlurMask::BoxBlur wasn't being checked in SkBlurMaskFilter.cpp::GrRRectBlurEffect::Create +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SmallBoxBlurBug, reporter, ctxInfo) { + + SkImageInfo info = SkImageInfo::MakeN32Premul(128, 128); + auto surface(SkSurface::MakeRenderTarget(ctxInfo.grContext(), SkBudgeted::kNo, info)); + SkCanvas* canvas = surface->getCanvas(); + + SkRect r = SkRect::MakeXYWH(10, 10, 100, 100); + SkRRect rr = SkRRect::MakeRectXY(r, 10, 10); + + SkPaint p; + p.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, 0.01f)); + + canvas->drawRRect(rr, p); } + +#endif + + +DEF_TEST(BlurredRRectNinePatchComputation, reporter) { + const SkRect r = SkRect::MakeXYWH(10, 10, 100, 100); + static const SkScalar kBlurRad = 3.0f; + + bool ninePatchable; + SkRRect rrectToDraw; + SkISize size; + SkScalar rectXs[SkBlurMaskFilter::kMaxDivisions], rectYs[SkBlurMaskFilter::kMaxDivisions]; + SkScalar texXs[SkBlurMaskFilter::kMaxDivisions], texYs[SkBlurMaskFilter::kMaxDivisions]; + int numX, numY; + uint32_t skipMask; + + // not nine-patchable + { + SkVector radii[4] = { { 100, 100 }, { 0, 0 }, { 100, 100 }, { 0, 0 } }; + + SkRRect rr; + rr.setRectRadii(r, radii); + + ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, rr, SkRect::MakeEmpty(), + kBlurRad, kBlurRad, + &rrectToDraw, &size, + rectXs, rectYs, texXs, texYs, + &numX, &numY, &skipMask); + REPORTER_ASSERT(reporter, !ninePatchable); + } + + // simple circular + { + static const SkScalar kCornerRad = 10.0f; + SkRRect rr; + rr.setRectXY(r, kCornerRad, kCornerRad); + + ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, rr, SkRect::MakeEmpty(), + kBlurRad, kBlurRad, + &rrectToDraw, &size, + rectXs, rectYs, texXs, texYs, + &numX, &numY, &skipMask); + + static const SkScalar kAns = 12.0f * kBlurRad + 2.0f * kCornerRad + 1.0f; + REPORTER_ASSERT(reporter, ninePatchable); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fWidth), kAns)); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fHeight), kAns)); + REPORTER_ASSERT(reporter, 4 == numX && 4 == numY); + REPORTER_ASSERT(reporter, !skipMask); + } + + // simple elliptical + { + static const SkScalar kXCornerRad = 2.0f; + static const SkScalar kYCornerRad = 10.0f; + SkRRect rr; + rr.setRectXY(r, kXCornerRad, kYCornerRad); + + ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, rr, SkRect::MakeEmpty(), + kBlurRad, kBlurRad, + &rrectToDraw, &size, + rectXs, rectYs, texXs, texYs, + &numX, &numY, &skipMask); + + static const SkScalar kXAns = 12.0f * kBlurRad + 2.0f * kXCornerRad + 1.0f; + static const SkScalar kYAns = 12.0f * kBlurRad + 2.0f * kYCornerRad + 1.0f; + + REPORTER_ASSERT(reporter, ninePatchable); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fWidth), kXAns)); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fHeight), kYAns)); + REPORTER_ASSERT(reporter, 4 == numX && 4 == numY); + REPORTER_ASSERT(reporter, !skipMask); + } + + // test-out occlusion + { + static const SkScalar kCornerRad = 10.0f; + SkRRect rr; + rr.setRectXY(r, kCornerRad, kCornerRad); + + // The rectXs & rectYs should be { 1, 29, 91, 119 }. Add two more points around each. + SkScalar testLocs[] = { + -18.0f, -9.0f, + 1.0f, + 9.0f, 18.0f, + 29.0f, + 39.0f, 49.0f, + 91.0f, + 109.0f, 118.0f, + 119.0f, + 139.0f, 149.0f + }; + + for (int minY = 0; minY < (int)SK_ARRAY_COUNT(testLocs); ++minY) { + for (int maxY = minY+1; maxY < (int)SK_ARRAY_COUNT(testLocs); ++maxY) { + for (int minX = 0; minX < (int)SK_ARRAY_COUNT(testLocs); ++minX) { + for (int maxX = minX+1; maxX < (int)SK_ARRAY_COUNT(testLocs); ++maxX) { + SkRect occluder = SkRect::MakeLTRB(testLocs[minX], testLocs[minY], + testLocs[maxX], testLocs[maxY]); + if (occluder.isEmpty()) { + continue; + } + + ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams( + rr, rr, occluder, + kBlurRad, kBlurRad, + &rrectToDraw, &size, + rectXs, rectYs, texXs, texYs, + &numX, &numY, &skipMask); + + static const SkScalar kAns = 12.0f * kBlurRad + 2.0f * kCornerRad + 1.0f; + REPORTER_ASSERT(reporter, ninePatchable); + REPORTER_ASSERT(reporter, + SkScalarNearlyEqual(SkIntToScalar(size.fWidth), kAns)); + REPORTER_ASSERT(reporter, + SkScalarNearlyEqual(SkIntToScalar(size.fHeight), kAns)); + + int checkBit = 0x1; + for (int y = 0; y < numY-1; ++y) { + for (int x = 0; x < numX-1; ++x) { + SkRect cell = SkRect::MakeLTRB(rectXs[x], rectYs[y], + rectXs[x+1], rectYs[y+1]); + REPORTER_ASSERT(reporter, + SkToBool(skipMask & checkBit) == + (cell.isEmpty() || occluder.contains(cell))); + + REPORTER_ASSERT(reporter, texXs[x] >= 0 && + texXs[x] <= size.fWidth); + REPORTER_ASSERT(reporter, texYs[y] >= 0 && + texXs[y] <= size.fHeight); + + checkBit <<= 1; + } + } + } + } + } + } + + + } + +} + +///////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/third_party/skia/tests/CPlusPlusEleven.cpp b/src/third_party/skia/tests/CPlusPlusEleven.cpp new file mode 100644 index 0000000..bbd8a12 --- /dev/null +++ b/src/third_party/skia/tests/CPlusPlusEleven.cpp
@@ -0,0 +1,67 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "Test.h" +#include "SkTemplates.h" +#include "SkScopeExit.h" +#include <utility> + +namespace { +class Moveable { +public: + Moveable() {} + Moveable(Moveable&&) {} + Moveable& operator=(Moveable&&) { return *this; } +private: + Moveable(const Moveable&); + Moveable& operator=(const Moveable&); +}; +template <typename T> void deleter(T*) { } +template <typename T> struct Deleter { + void operator()(T* t) { delete static_cast<const Moveable*>(t); } +}; +} // namespace + +DEF_TEST(CPlusPlusEleven_RvalueAndMove, r) { + Moveable src1; Moveable dst1(std::move(src1)); + Moveable src2, dst2; dst2 = std::move(src2); +} + +DEF_TEST(CPlusPlusEleven_constexpr, r) { + static constexpr int x = Sk32ToBool(50); + REPORTER_ASSERT(r, x == 1); + static constexpr int y = SkTPin<int>(100, 0, 10); + REPORTER_ASSERT(r, y == 10); +} + +namespace { +struct MoveableCopyable { + bool fCopied; + MoveableCopyable() : fCopied(false) {} + MoveableCopyable(const MoveableCopyable &o) : fCopied(true) {} + MoveableCopyable(MoveableCopyable &&o) : fCopied(o.fCopied) {} +}; +struct TestClass { + MoveableCopyable fFoo; +}; +} // namespace + +DEF_TEST(CPlusPlusEleven_default_move, r) { + TestClass a; + TestClass b(a); + TestClass c(std::move(a)); + REPORTER_ASSERT(r, b.fFoo.fCopied); + REPORTER_ASSERT(r, !c.fFoo.fCopied); +} + +DEF_TEST(SkAtScopeExit, r) { + int x = 5; + { + SK_AT_SCOPE_EXIT(x--); + REPORTER_ASSERT(r, x == 5); + } + REPORTER_ASSERT(r, x == 4); +}
diff --git a/src/third_party/skia/tests/CTest.cpp b/src/third_party/skia/tests/CTest.cpp new file mode 100644 index 0000000..1f301ef --- /dev/null +++ b/src/third_party/skia/tests/CTest.cpp
@@ -0,0 +1,88 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#include "sk_canvas.h" +#include "sk_paint.h" +#include "sk_surface.h" +#include "sk_shader.h" + +static void shader_test(skiatest::Reporter* reporter) { + sk_imageinfo_t info = + {64, 64, sk_colortype_get_default_8888(), PREMUL_SK_ALPHATYPE}; + sk_surface_t* surface = sk_surface_new_raster(&info, nullptr); + sk_canvas_t* canvas = sk_surface_get_canvas(surface); + sk_paint_t* paint = sk_paint_new(); + + sk_shader_tilemode_t tilemode = CLAMP_SK_SHADER_TILEMODE; + sk_point_t point = {0.0f, 0.0f}; + sk_point_t point2 = {30.0f, 40.0f}; + sk_color_t colors[] = { + (sk_color_t)sk_color_set_argb(0xFF, 0x00, 0x00, 0xFF), + (sk_color_t)sk_color_set_argb(0xFF, 0x00, 0xFF, 0x00) + }; + sk_shader_t* shader; + + shader = sk_shader_new_radial_gradient( + &point, 1.0f, colors, nullptr, 2, tilemode, nullptr); + REPORTER_ASSERT(reporter, shader != nullptr); + sk_paint_set_shader(paint, shader); + sk_shader_unref(shader); + sk_canvas_draw_paint(canvas, paint); + + shader = sk_shader_new_sweep_gradient(&point, colors, nullptr, 2, nullptr); + REPORTER_ASSERT(reporter, shader != nullptr); + sk_paint_set_shader(paint, shader); + sk_shader_unref(shader); + sk_canvas_draw_paint(canvas, paint); + + shader = sk_shader_new_two_point_conical_gradient( + &point, 10.0f, &point2, 50.0f, colors, nullptr, 2, tilemode, nullptr); + REPORTER_ASSERT(reporter, shader != nullptr); + sk_paint_set_shader(paint, shader); + sk_shader_unref(shader); + sk_canvas_draw_paint(canvas, paint); + + sk_paint_delete(paint); + sk_surface_unref(surface); +} + +static void test_c(skiatest::Reporter* reporter) { + sk_colortype_t ct = sk_colortype_get_default_8888(); + + sk_imageinfo_t info = { + 1, 1, ct, PREMUL_SK_ALPHATYPE + }; + uint32_t pixel[1] = { 0 }; + sk_surfaceprops_t surfaceProps = { UNKNOWN_SK_PIXELGEOMETRY }; + + sk_surface_t* surface = sk_surface_new_raster_direct(&info, pixel, sizeof(uint32_t), + &surfaceProps); + sk_paint_t* paint = sk_paint_new(); + + sk_canvas_t* canvas = sk_surface_get_canvas(surface); + sk_canvas_draw_paint(canvas, paint); + REPORTER_ASSERT(reporter, 0xFF000000 == pixel[0]); + + sk_paint_set_color(paint, sk_color_set_argb(0xFF, 0xFF, 0xFF, 0xFF)); + sk_canvas_draw_paint(canvas, paint); + REPORTER_ASSERT(reporter, 0xFFFFFFFF == pixel[0]); + + sk_paint_set_xfermode_mode(paint, SRC_SK_XFERMODE_MODE); + sk_paint_set_color(paint, sk_color_set_argb(0x80, 0x80, 0x80, 0x80)); + sk_canvas_draw_paint(canvas, paint); + REPORTER_ASSERT(reporter, 0x80404040 == pixel[0]); + + sk_paint_delete(paint); + sk_surface_unref(surface); +} + +DEF_TEST(C_API, reporter) { + test_c(reporter); + shader_test(reporter); +}
diff --git a/src/third_party/skia/tests/CachedDataTest.cpp b/src/third_party/skia/tests/CachedDataTest.cpp new file mode 100644 index 0000000..fe7bb5f --- /dev/null +++ b/src/third_party/skia/tests/CachedDataTest.cpp
@@ -0,0 +1,94 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCachedData.h" +#include "SkDiscardableMemoryPool.h" +#include "Test.h" + +enum LockedState { + kUnlocked, + kLocked, +}; + +enum CachedState { + kNotInCache, + kInCache, +}; + +static void check_data(skiatest::Reporter* reporter, SkCachedData* data, + int refcnt, CachedState cacheState, LockedState lockedState) { + REPORTER_ASSERT(reporter, data->testing_only_getRefCnt() == refcnt); + REPORTER_ASSERT(reporter, data->testing_only_isInCache() == (kInCache == cacheState)); + REPORTER_ASSERT(reporter, data->testing_only_isLocked() == (lockedState == kLocked)); +} + +static SkCachedData* make_data(size_t size, SkDiscardableMemoryPool* pool) { + if (pool) { + SkDiscardableMemory* dm = pool->create(size); + // the pool "can" return null, but it shouldn't in these controlled conditions + SkASSERT_RELEASE(dm); + return new SkCachedData(size, dm); + } else { + return new SkCachedData(sk_malloc_throw(size), size); + } +} + +// returns with the data locked by client and cache +static SkCachedData* test_locking(skiatest::Reporter* reporter, + size_t size, SkDiscardableMemoryPool* pool) { + SkCachedData* data = make_data(size, pool); + + memset(data->writable_data(), 0x80, size); // just to use writable_data() + + check_data(reporter, data, 1, kNotInCache, kLocked); + + data->ref(); + check_data(reporter, data, 2, kNotInCache, kLocked); + data->unref(); + check_data(reporter, data, 1, kNotInCache, kLocked); + + data->attachToCacheAndRef(); + check_data(reporter, data, 2, kInCache, kLocked); + + data->unref(); + check_data(reporter, data, 1, kInCache, kUnlocked); + + data->ref(); + check_data(reporter, data, 2, kInCache, kLocked); + + return data; +} + +/* + * SkCachedData behaves differently (regarding its locked/unlocked state) depending on + * when it is in the cache or not. Being in the cache is signaled by calling attachToCacheAndRef() + * instead of ref(). (and balanced by detachFromCacheAndUnref). + * + * Thus, among other things, we test the end-of-life behavior when the client is the last owner + * and when the cache is. + */ +DEF_TEST(CachedData, reporter) { + sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Make(1000)); + + for (int useDiscardable = 0; useDiscardable <= 1; ++useDiscardable) { + const size_t size = 100; + + // test with client as last owner + SkCachedData* data = test_locking(reporter, size, useDiscardable ? pool.get() : nullptr); + check_data(reporter, data, 2, kInCache, kLocked); + data->detachFromCacheAndUnref(); + check_data(reporter, data, 1, kNotInCache, kLocked); + data->unref(); + + // test with cache as last owner + data = test_locking(reporter, size, useDiscardable ? pool.get() : nullptr); + check_data(reporter, data, 2, kInCache, kLocked); + data->unref(); + check_data(reporter, data, 1, kInCache, kUnlocked); + data->detachFromCacheAndUnref(); + } +}
diff --git a/src/third_party/skia/tests/CachedDecodingPixelRefTest.cpp b/src/third_party/skia/tests/CachedDecodingPixelRefTest.cpp index b3eb532..0eecdbb 100644 --- a/src/third_party/skia/tests/CachedDecodingPixelRefTest.cpp +++ b/src/third_party/skia/tests/CachedDecodingPixelRefTest.cpp
@@ -6,209 +6,73 @@ */ #include "SkBitmap.h" -#include "SkCachingPixelRef.h" #include "SkCanvas.h" #include "SkData.h" -#include "SkDecodingImageGenerator.h" #include "SkDiscardableMemoryPool.h" -#include "SkImageDecoder.h" -#include "SkImageGeneratorPriv.h" +#include "SkImage.h" +#include "SkImageEncoder.h" +#include "SkImageGenerator.h" +#include "SkMakeUnique.h" #include "SkResourceCache.h" #include "SkStream.h" #include "SkUtils.h" #include "Test.h" -/** - * Fill this bitmap with some color. - */ -static void make_test_image(SkBitmap* bm) { - const int W = 50, H = 50; - bm->allocN32Pixels(W, H); - bm->eraseColor(SK_ColorBLACK); - SkCanvas canvas(*bm); - SkPaint paint; - paint.setColor(SK_ColorBLUE); - canvas.drawRectCoords(0, 0, SkIntToScalar(W/2), - SkIntToScalar(H/2), paint); - paint.setColor(SK_ColorWHITE); - canvas.drawRectCoords(SkIntToScalar(W/2), SkIntToScalar(H/2), - SkIntToScalar(W), SkIntToScalar(H), paint); -} - -/** - * encode this bitmap into some data via SkImageEncoder - */ -static SkData* create_data_from_bitmap(const SkBitmap& bm, - SkImageEncoder::Type type) { - SkDynamicMemoryWStream stream; - if (SkImageEncoder::EncodeStream(&stream, bm, type, 100)) { - return stream.copyToData(); - } - return NULL; -} - -//////////////////////////////////////////////////////////////////////////////// - -static void compare_bitmaps(skiatest::Reporter* reporter, - const SkBitmap& b1, const SkBitmap& b2, - bool pixelPerfect = true) { - REPORTER_ASSERT(reporter, b1.empty() == b2.empty()); - REPORTER_ASSERT(reporter, b1.width() == b2.width()); - REPORTER_ASSERT(reporter, b1.height() == b2.height()); - REPORTER_ASSERT(reporter, b1.isNull() == b2.isNull()); - SkAutoLockPixels autoLockPixels1(b1); - SkAutoLockPixels autoLockPixels2(b2); - REPORTER_ASSERT(reporter, b1.isNull() == b2.isNull()); - if (b1.isNull() || b1.empty()) { - return; - } - REPORTER_ASSERT(reporter, b1.getPixels()); - REPORTER_ASSERT(reporter, b2.getPixels()); - if ((!(b1.getPixels())) || (!(b2.getPixels()))) { - return; - } - if ((b1.width() != b2.width()) || - (b1.height() != b2.height())) { - return; - } - if (!pixelPerfect) { - return; - } - - int pixelErrors = 0; - for (int y = 0; y < b2.height(); ++y) { - for (int x = 0; x < b2.width(); ++x) { - if (b1.getColor(x, y) != b2.getColor(x, y)) { - ++pixelErrors; - } - } - } - REPORTER_ASSERT(reporter, 0 == pixelErrors); -} - -typedef bool (*InstallEncoded)(SkData* encoded, SkBitmap* dst); - -/** - This function tests three differently encoded images against the - original bitmap */ -static void test_three_encodings(skiatest::Reporter* reporter, - InstallEncoded install) { - SkBitmap original; - make_test_image(&original); - REPORTER_ASSERT(reporter, !original.empty()); - REPORTER_ASSERT(reporter, !original.isNull()); - if (original.empty() || original.isNull()) { - return; - } - static const SkImageEncoder::Type types[] = { - SkImageEncoder::kPNG_Type, - SkImageEncoder::kJPEG_Type, - SkImageEncoder::kWEBP_Type - }; - for (size_t i = 0; i < SK_ARRAY_COUNT(types); i++) { - SkImageEncoder::Type type = types[i]; - SkAutoDataUnref encoded(create_data_from_bitmap(original, type)); - REPORTER_ASSERT(reporter, encoded.get() != NULL); - if (NULL == encoded.get()) { - continue; - } - SkBitmap lazy; - bool installSuccess = install(encoded.get(), &lazy); - REPORTER_ASSERT(reporter, installSuccess); - if (!installSuccess) { - continue; - } - REPORTER_ASSERT(reporter, NULL == lazy.getPixels()); - { - SkAutoLockPixels autoLockPixels(lazy); // now pixels are good. - REPORTER_ASSERT(reporter, lazy.getPixels()); - if (NULL == lazy.getPixels()) { - continue; - } - } - // pixels should be gone! - REPORTER_ASSERT(reporter, NULL == lazy.getPixels()); - { - SkAutoLockPixels autoLockPixels(lazy); // now pixels are good. - REPORTER_ASSERT(reporter, lazy.getPixels()); - if (NULL == lazy.getPixels()) { - continue; - } - } - bool comparePixels = (SkImageEncoder::kPNG_Type == type); - compare_bitmaps(reporter, original, lazy, comparePixels); - } -} - -//////////////////////////////////////////////////////////////////////////////// -static bool install_skCachingPixelRef(SkData* encoded, SkBitmap* dst) { - return SkCachingPixelRef::Install( - SkDecodingImageGenerator::Create( - encoded, SkDecodingImageGenerator::Options()), dst); -} -static bool install_skDiscardablePixelRef(SkData* encoded, SkBitmap* dst) { - // Use system-default discardable memory. - return SkInstallDiscardablePixelRef( - SkDecodingImageGenerator::Create( - encoded, SkDecodingImageGenerator::Options()), dst); -} - -//////////////////////////////////////////////////////////////////////////////// -/** - * This checks to see that a SkCachingPixelRef and a - * SkDiscardablePixelRef works as advertised with a - * SkDecodingImageGenerator. - */ -DEF_TEST(DecodingImageGenerator, reporter) { - test_three_encodings(reporter, install_skCachingPixelRef); - test_three_encodings(reporter, install_skDiscardablePixelRef); -} - class TestImageGenerator : public SkImageGenerator { public: enum TestType { - kFailGetInfo_TestType, kFailGetPixels_TestType, kSucceedGetPixels_TestType, kLast_TestType = kSucceedGetPixels_TestType }; static int Width() { return 10; } static int Height() { return 10; } - static uint32_t Color() { return 0xff123456; } - TestImageGenerator(TestType type, skiatest::Reporter* reporter) - : fType(type), fReporter(reporter) { + // value choosen so that there is no loss when converting to to RGB565 and back + static SkColor Color() { return 0xff10355a; } + static SkPMColor PMColor() { return SkPreMultiplyColor(Color()); } + + TestImageGenerator(TestType type, skiatest::Reporter* reporter, + SkColorType colorType = kN32_SkColorType) + : INHERITED(GetMyInfo(colorType)), fType(type), fReporter(reporter) { SkASSERT((fType <= kLast_TestType) && (fType >= 0)); } - virtual ~TestImageGenerator() { } + ~TestImageGenerator() override {} protected: - virtual bool onGetInfo(SkImageInfo* info) SK_OVERRIDE { - REPORTER_ASSERT(fReporter, info); - if ((NULL == info) || (kFailGetInfo_TestType == fType)) { - return false; - } - *info = SkImageInfo::MakeN32(TestImageGenerator::Width(), - TestImageGenerator::Height(), - kOpaque_SkAlphaType); - return true; + static SkImageInfo GetMyInfo(SkColorType colorType) { + return SkImageInfo::Make(TestImageGenerator::Width(), TestImageGenerator::Height(), + colorType, kOpaque_SkAlphaType); } - virtual bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, - SkPMColor ctable[], int* ctableCount) SK_OVERRIDE { - REPORTER_ASSERT(fReporter, pixels != NULL); - size_t minRowBytes = static_cast<size_t>(info.width() * info.bytesPerPixel()); - REPORTER_ASSERT(fReporter, rowBytes >= minRowBytes); - if ((NULL == pixels) - || (fType != kSucceedGetPixels_TestType) - || (info.colorType() != kN32_SkColorType)) { + bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, + const Options& options) override { + REPORTER_ASSERT(fReporter, pixels != nullptr); + REPORTER_ASSERT(fReporter, rowBytes >= info.minRowBytes()); + if (fType != kSucceedGetPixels_TestType) { + return false; + } + if (info.colorType() != kN32_SkColorType && info.colorType() != getInfo().colorType()) { return false; } char* bytePtr = static_cast<char*>(pixels); - for (int y = 0; y < info.height(); ++y) { - sk_memset32(reinterpret_cast<SkColor*>(bytePtr), - TestImageGenerator::Color(), info.width()); - bytePtr += rowBytes; + switch (info.colorType()) { + case kN32_SkColorType: + for (int y = 0; y < info.height(); ++y) { + sk_memset32((uint32_t*)bytePtr, + TestImageGenerator::PMColor(), info.width()); + bytePtr += rowBytes; + } + break; + case kRGB_565_SkColorType: + for (int y = 0; y < info.height(); ++y) { + sk_memset16((uint16_t*)bytePtr, + SkPixel32ToPixel16(TestImageGenerator::PMColor()), info.width()); + bytePtr += rowBytes; + } + break; + default: + return false; } return true; } @@ -216,146 +80,48 @@ private: const TestType fType; skiatest::Reporter* const fReporter; + + typedef SkImageGenerator INHERITED; }; -static void check_test_image_generator_bitmap(skiatest::Reporter* reporter, - const SkBitmap& bm) { - REPORTER_ASSERT(reporter, TestImageGenerator::Width() == bm.width()); - REPORTER_ASSERT(reporter, TestImageGenerator::Height() == bm.height()); - SkAutoLockPixels autoLockPixels(bm); - REPORTER_ASSERT(reporter, bm.getPixels()); - if (NULL == bm.getPixels()) { - return; - } - int errors = 0; - for (int y = 0; y < bm.height(); ++y) { - for (int x = 0; x < bm.width(); ++x) { - if (TestImageGenerator::Color() != *bm.getAddr32(x, y)) { - ++errors; - } - } - } - REPORTER_ASSERT(reporter, 0 == errors); -} - -enum PixelRefType { - kSkCaching_PixelRefType, - kSkDiscardable_PixelRefType, - kLast_PixelRefType = kSkDiscardable_PixelRefType -}; - -static void check_pixelref(TestImageGenerator::TestType type, - skiatest::Reporter* reporter, - PixelRefType pixelRefType, - SkDiscardableMemory::Factory* factory) { - SkASSERT((pixelRefType >= 0) && (pixelRefType <= kLast_PixelRefType)); - SkAutoTDelete<SkImageGenerator> gen(SkNEW_ARGS(TestImageGenerator, - (type, reporter))); - REPORTER_ASSERT(reporter, gen.get() != NULL); - SkBitmap lazy; - bool success; - if (kSkCaching_PixelRefType == pixelRefType) { - // Ignore factory; use global cache. - success = SkCachingPixelRef::Install(gen.detach(), &lazy); - } else { - success = SkInstallDiscardablePixelRef(gen.detach(), &lazy, factory); - } - REPORTER_ASSERT(reporter, success - == (TestImageGenerator::kFailGetInfo_TestType != type)); - if (TestImageGenerator::kSucceedGetPixels_TestType == type) { - check_test_image_generator_bitmap(reporter, lazy); - } else if (TestImageGenerator::kFailGetPixels_TestType == type) { - SkAutoLockPixels autoLockPixels(lazy); - REPORTER_ASSERT(reporter, NULL == lazy.getPixels()); - } -} - -// new/lock/delete is an odd pattern for a pixelref, but it needs to not assert -static void test_newlockdelete(skiatest::Reporter* reporter) { - SkBitmap bm; - SkImageGenerator* ig = new TestImageGenerator( - TestImageGenerator::kSucceedGetPixels_TestType, reporter); - SkInstallDiscardablePixelRef(ig, &bm); - bm.pixelRef()->lockPixels(); -} - -/** - * This tests the basic functionality of SkDiscardablePixelRef with a - * basic SkImageGenerator implementation and several - * SkDiscardableMemory::Factory choices. - */ -DEF_TEST(DiscardableAndCachingPixelRef, reporter) { - test_newlockdelete(reporter); - - check_pixelref(TestImageGenerator::kFailGetInfo_TestType, - reporter, kSkCaching_PixelRefType, NULL); - check_pixelref(TestImageGenerator::kFailGetPixels_TestType, - reporter, kSkCaching_PixelRefType, NULL); - check_pixelref(TestImageGenerator::kSucceedGetPixels_TestType, - reporter, kSkCaching_PixelRefType, NULL); - - check_pixelref(TestImageGenerator::kFailGetInfo_TestType, - reporter, kSkDiscardable_PixelRefType, NULL); - check_pixelref(TestImageGenerator::kFailGetPixels_TestType, - reporter, kSkDiscardable_PixelRefType, NULL); - check_pixelref(TestImageGenerator::kSucceedGetPixels_TestType, - reporter, kSkDiscardable_PixelRefType, NULL); - - SkAutoTUnref<SkDiscardableMemoryPool> pool( - SkDiscardableMemoryPool::Create(1, NULL)); - REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed()); - check_pixelref(TestImageGenerator::kFailGetPixels_TestType, - reporter, kSkDiscardable_PixelRefType, pool); - REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed()); - check_pixelref(TestImageGenerator::kSucceedGetPixels_TestType, - reporter, kSkDiscardable_PixelRefType, pool); - REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed()); - - SkDiscardableMemoryPool* globalPool = SkGetGlobalDiscardableMemoryPool(); - // Only acts differently from NULL on a platform that has a - // default discardable memory implementation that differs from the - // global DM pool. - check_pixelref(TestImageGenerator::kFailGetPixels_TestType, - reporter, kSkDiscardable_PixelRefType, globalPool); - check_pixelref(TestImageGenerator::kSucceedGetPixels_TestType, - reporter, kSkDiscardable_PixelRefType, globalPool); -} - //////////////////////////////////////////////////////////////////////////////// DEF_TEST(Image_NewFromGenerator, r) { - TestImageGenerator::TestType testTypes[] = { - TestImageGenerator::kFailGetInfo_TestType, + const TestImageGenerator::TestType testTypes[] = { TestImageGenerator::kFailGetPixels_TestType, TestImageGenerator::kSucceedGetPixels_TestType, }; + const SkColorType testColorTypes[] = { + kN32_SkColorType, + kRGB_565_SkColorType + }; for (size_t i = 0; i < SK_ARRAY_COUNT(testTypes); ++i) { TestImageGenerator::TestType test = testTypes[i]; - SkImageGenerator* gen = SkNEW_ARGS(TestImageGenerator, (test, r)); - SkAutoTUnref<SkImage> image(SkImage::NewFromGenerator(gen)); - if (TestImageGenerator::kFailGetInfo_TestType == test) { - REPORTER_ASSERT(r, NULL == image.get()); - continue; - } - if (NULL == image.get()) { - ERRORF(r, "SkImage::NewFromGenerator unexpecedly failed [" - SK_SIZE_T_SPECIFIER "]", i); - continue; - } - REPORTER_ASSERT(r, TestImageGenerator::Width() == image->width()); - REPORTER_ASSERT(r, TestImageGenerator::Height() == image->height()); + for (const SkColorType testColorType : testColorTypes) { + auto gen = skstd::make_unique<TestImageGenerator>(test, r, testColorType); + sk_sp<SkImage> image(SkImage::MakeFromGenerator(std::move(gen))); + if (nullptr == image) { + ERRORF(r, "SkImage::NewFromGenerator unexpecedly failed [" + SK_SIZE_T_SPECIFIER "]", i); + continue; + } + REPORTER_ASSERT(r, TestImageGenerator::Width() == image->width()); + REPORTER_ASSERT(r, TestImageGenerator::Height() == image->height()); + REPORTER_ASSERT(r, image->isLazyGenerated()); - SkBitmap bitmap; - bitmap.allocN32Pixels(TestImageGenerator::Width(), TestImageGenerator::Height()); - SkCanvas canvas(bitmap); - const SkColor kDefaultColor = 0xffabcdef; - canvas.clear(kDefaultColor); - canvas.drawImage(image, 0, 0, NULL); - if (TestImageGenerator::kSucceedGetPixels_TestType == test) { - REPORTER_ASSERT( - r, TestImageGenerator::Color() == *bitmap.getAddr32(0, 0)); - } else { - REPORTER_ASSERT(r, kDefaultColor == bitmap.getColor(0,0)); + SkBitmap bitmap; + bitmap.allocN32Pixels(TestImageGenerator::Width(), TestImageGenerator::Height()); + SkCanvas canvas(bitmap); + const SkColor kDefaultColor = 0xffabcdef; + canvas.clear(kDefaultColor); + canvas.drawImage(image, 0, 0, nullptr); + if (TestImageGenerator::kSucceedGetPixels_TestType == test) { + REPORTER_ASSERT( + r, TestImageGenerator::Color() == bitmap.getColor(0, 0)); + } + else { + REPORTER_ASSERT(r, kDefaultColor == bitmap.getColor(0, 0)); + } } } }
diff --git a/src/third_party/skia/tests/CanvasStateHelpers.cpp b/src/third_party/skia/tests/CanvasStateHelpers.cpp index 96972b8..1547d2f 100644 --- a/src/third_party/skia/tests/CanvasStateHelpers.cpp +++ b/src/third_party/skia/tests/CanvasStateHelpers.cpp
@@ -27,12 +27,11 @@ extern "C" bool complex_layers_draw_from_canvas_state(SkCanvasState* state, float left, float top, float right, float bottom, int32_t spacer) { - SkCanvas* canvas = SkCanvasStateUtils::CreateFromCanvasState(state); + std::unique_ptr<SkCanvas> canvas = SkCanvasStateUtils::MakeFromCanvasState(state); if (!canvas) { return false; } - complex_layers_draw(canvas, left, top, right, bottom, spacer); - canvas->unref(); + complex_layers_draw(canvas.get(), left, top, right, bottom, spacer); return true; } @@ -45,14 +44,14 @@ canvas->drawColor(SK_ColorBLUE); canvas->restore(); - canvas->clipRegion(localRegion, (SkRegion::Op) clipOp); + canvas->clipRegion(localRegion, (SkClipOp) clipOp); canvas->drawColor(SK_ColorBLUE); } extern "C" bool complex_clips_draw_from_canvas_state(SkCanvasState* state, int32_t left, int32_t top, int32_t right, int32_t bottom, int32_t clipOp, int32_t regionRects, int32_t* rectCoords) { - SkCanvas* canvas = SkCanvasStateUtils::CreateFromCanvasState(state); + std::unique_ptr<SkCanvas> canvas = SkCanvasStateUtils::MakeFromCanvasState(state); if (!canvas) { return false; } @@ -64,8 +63,7 @@ rectCoords += 4; } - complex_clips_draw(canvas, left, top, right, bottom, clipOp, localRegion); - canvas->unref(); + complex_clips_draw(canvas.get(), left, top, right, bottom, clipOp, localRegion); return true; } #endif // SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
diff --git a/src/third_party/skia/tests/CanvasStateTest.cpp b/src/third_party/skia/tests/CanvasStateTest.cpp index 8364a4b..ae417d4 100644 --- a/src/third_party/skia/tests/CanvasStateTest.cpp +++ b/src/third_party/skia/tests/CanvasStateTest.cpp
@@ -6,14 +6,17 @@ */ #include "CanvasStateHelpers.h" +#include "SkBitmap.h" #include "SkCanvas.h" +#include "SkClipOpPriv.h" #include "SkCanvasStateUtils.h" #include "SkCommandLineFlags.h" #include "SkDrawFilter.h" -#include "SkError.h" #include "SkPaint.h" +#include "SkRegion.h" #include "SkRRect.h" #include "SkRect.h" +#include "SkTLazy.h" #include "Test.h" // dlopen and the library flag are only used for tests which require this flag. @@ -37,9 +40,9 @@ OpenLibResult(skiatest::Reporter* reporter) { if (FLAGS_library.count() == 1) { fHandle = dlopen(FLAGS_library[0], RTLD_LAZY | RTLD_LOCAL); - REPORTER_ASSERT_MESSAGE(reporter, fHandle != NULL, "Failed to open library!"); + REPORTER_ASSERT_MESSAGE(reporter, fHandle != nullptr, "Failed to open library!"); } else { - fHandle = NULL; + fHandle = nullptr; } } @@ -71,9 +74,10 @@ }; const int layerAlpha[] = { 255, 255, 0 }; - const SkCanvas::SaveFlags flags[] = { SkCanvas::kARGB_NoClipLayer_SaveFlag, - SkCanvas::kARGB_ClipLayer_SaveFlag, - SkCanvas::kARGB_NoClipLayer_SaveFlag + const SkCanvas::SaveLayerFlags flags[] = { + static_cast<SkCanvas::SaveLayerFlags>(SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag), + 0, + static_cast<SkCanvas::SaveLayerFlags>(SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag), }; REPORTER_ASSERT(reporter, sizeof(layerAlpha) == sizeof(flags)); @@ -81,7 +85,7 @@ float r, float b, int32_t s); OpenLibResult openLibResult(reporter); - if (openLibResult.handle() != NULL) { + if (openLibResult.handle() != nullptr) { *(void**) (&drawFn) = dlsym(openLibResult.handle(), "complex_layers_draw_from_canvas_state"); } else { @@ -105,8 +109,13 @@ canvas.drawColor(SK_ColorRED); for (size_t k = 0; k < SK_ARRAY_COUNT(layerAlpha); ++k) { + SkTLazy<SkPaint> paint; + if (layerAlpha[k] != 0xFF) { + paint.init()->setAlpha(layerAlpha[k]); + } + // draw a rect within the layer's bounds and again outside the layer's bounds - canvas.saveLayerAlpha(&rect, layerAlpha[k], flags[k]); + canvas.saveLayer(SkCanvas::SaveLayerRec(&rect, paint.getMaybeNull(), flags[k])); if (j) { // Capture from the first Skia. @@ -172,9 +181,10 @@ SkRegion::kIntersect_Op, SkRegion::kReplace_Op, }; - const SkCanvas::SaveFlags flags[] = { SkCanvas::kARGB_NoClipLayer_SaveFlag, - SkCanvas::kARGB_ClipLayer_SaveFlag, - SkCanvas::kARGB_NoClipLayer_SaveFlag, + const SkCanvas::SaveLayerFlags flags[] = { + static_cast<SkCanvas::SaveLayerFlags>(SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag), + 0, + static_cast<SkCanvas::SaveLayerFlags>(SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag), }; REPORTER_ASSERT(reporter, sizeof(clipOps) == sizeof(flags)); @@ -183,7 +193,7 @@ int32_t regionRects, int32_t* rectCoords); OpenLibResult openLibResult(reporter); - if (openLibResult.handle() != NULL) { + if (openLibResult.handle() != nullptr) { *(void**) (&drawFn) = dlsym(openLibResult.handle(), "complex_clips_draw_from_canvas_state"); } else { @@ -205,9 +215,11 @@ SkRegion localRegion = clipRegion; + SkPaint paint; + paint.setAlpha(128); for (size_t j = 0; j < SK_ARRAY_COUNT(flags); ++j) { SkRect layerBounds = SkRect::Make(layerRect); - canvas.saveLayerAlpha(&layerBounds, 128, flags[j]); + canvas.saveLayer(SkCanvas::SaveLayerRec(&layerBounds, &paint, flags[j])); if (i) { SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas); @@ -252,9 +264,11 @@ //////////////////////////////////////////////////////////////////////////////// +#ifdef SK_SUPPORT_LEGACY_DRAWFILTER + class TestDrawFilter : public SkDrawFilter { public: - virtual bool filter(SkPaint*, Type) SK_OVERRIDE { return true; } + bool filter(SkPaint*, Type) override { return true; } }; DEF_TEST(CanvasState_test_draw_filters, reporter) { @@ -267,20 +281,18 @@ SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas); REPORTER_ASSERT(reporter, state); - SkCanvas* tmpCanvas = SkCanvasStateUtils::CreateFromCanvasState(state); + std::unique_ptr<SkCanvas> tmpCanvas = SkCanvasStateUtils::MakeFromCanvasState(state); REPORTER_ASSERT(reporter, tmpCanvas); REPORTER_ASSERT(reporter, canvas.getDrawFilter()); - REPORTER_ASSERT(reporter, NULL == tmpCanvas->getDrawFilter()); + REPORTER_ASSERT(reporter, nullptr == tmpCanvas->getDrawFilter()); - tmpCanvas->unref(); SkCanvasStateUtils::ReleaseCanvasState(state); } -//////////////////////////////////////////////////////////////////////////////// +#endif -// we need this function to prevent SkError from printing to stdout -static void error_callback(SkError code, void* ctx) {} +//////////////////////////////////////////////////////////////////////////////// DEF_TEST(CanvasState_test_soft_clips, reporter) { SkBitmap bitmap; @@ -290,19 +302,17 @@ SkRRect roundRect; roundRect.setOval(SkRect::MakeWH(5, 5)); - canvas.clipRRect(roundRect, SkRegion::kIntersect_Op, true); - - SkSetErrorCallback(error_callback, NULL); + canvas.clipRRect(roundRect, kIntersect_SkClipOp, true); SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas); REPORTER_ASSERT(reporter, !state); - - REPORTER_ASSERT(reporter, kInvalidOperation_SkError == SkGetLastError()); - SkClearLastError(); } -#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG DEF_TEST(CanvasState_test_saveLayer_clip, reporter) { + const uint32_t dontSaveFlag = 1 << 31; // secret flag for don't save +#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG + static_assert(SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag == dontSaveFlag, ""); +#endif const int WIDTH = 100; const int HEIGHT = 100; const int LAYER_WIDTH = 50; @@ -315,23 +325,21 @@ SkRect bounds = SkRect::MakeWH(SkIntToScalar(LAYER_WIDTH), SkIntToScalar(LAYER_HEIGHT)); canvas.clipRect(SkRect::MakeWH(SkIntToScalar(WIDTH), SkIntToScalar(HEIGHT))); - // Check that saveLayer without the kClipToLayer_SaveFlag leaves the - // clip stack unchanged. - canvas.saveLayer(&bounds, NULL, SkCanvas::kARGB_NoClipLayer_SaveFlag); - SkRect clipStackBounds; - SkClipStack::BoundsType boundsType; - canvas.getClipStack()->getBounds(&clipStackBounds, &boundsType); - REPORTER_ASSERT(reporter, clipStackBounds.width() == WIDTH); - REPORTER_ASSERT(reporter, clipStackBounds.height() == HEIGHT); + SkIRect devClip; + // Check that saveLayer without the kClipToLayer_SaveFlag leaves the clip unchanged. + canvas.saveLayer(SkCanvas::SaveLayerRec(&bounds, nullptr, dontSaveFlag)); + devClip = canvas.getDeviceClipBounds(); + REPORTER_ASSERT(reporter, canvas.isClipRect()); + REPORTER_ASSERT(reporter, devClip.width() == WIDTH); + REPORTER_ASSERT(reporter, devClip.height() == HEIGHT); canvas.restore(); // Check that saveLayer with the kClipToLayer_SaveFlag sets the clip // stack to the layer bounds. - canvas.saveLayer(&bounds, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag); - canvas.getClipStack()->getBounds(&clipStackBounds, &boundsType); - REPORTER_ASSERT(reporter, clipStackBounds.width() == LAYER_WIDTH); - REPORTER_ASSERT(reporter, clipStackBounds.height() == LAYER_HEIGHT); - + canvas.saveLayer(&bounds, nullptr); + devClip = canvas.getDeviceClipBounds(); + REPORTER_ASSERT(reporter, canvas.isClipRect()); + REPORTER_ASSERT(reporter, devClip.width() == LAYER_WIDTH); + REPORTER_ASSERT(reporter, devClip.height() == LAYER_HEIGHT); canvas.restore(); } -#endif
diff --git a/src/third_party/skia/tests/CanvasTest.cpp b/src/third_party/skia/tests/CanvasTest.cpp index 216a408..e64f5e9 100644 --- a/src/third_party/skia/tests/CanvasTest.cpp +++ b/src/third_party/skia/tests/CanvasTest.cpp
@@ -18,6 +18,7 @@ * function of the form: * * static void MyTestStepFunction(SkCanvas* canvas, + * const TestData& d, * skiatest::Reporter* reporter, * CanvasTestStep* testStep) * { @@ -43,75 +44,238 @@ * works the same way as SIMPLE_TEST_STEP, and additionally verifies * that the invoked method returns a non-zero value. */ + #include "SkBitmap.h" #include "SkCanvas.h" -#include "SkDeferredCanvas.h" -#include "SkDevice.h" +#include "SkClipStack.h" +#include "SkDocument.h" #include "SkMatrix.h" #include "SkNWayCanvas.h" -#include "SkPDFDevice.h" -#include "SkPDFDocument.h" #include "SkPaint.h" +#include "SkPaintFilterCanvas.h" #include "SkPath.h" #include "SkPicture.h" #include "SkPictureRecord.h" #include "SkPictureRecorder.h" -#include "SkProxyCanvas.h" +#include "SkRasterClip.h" #include "SkRect.h" #include "SkRegion.h" #include "SkShader.h" #include "SkStream.h" #include "SkSurface.h" +#include "SkTemplates.h" #include "SkTDArray.h" +#include "SkVertices.h" #include "Test.h" -static bool equal_clips(const SkCanvas& a, const SkCanvas& b) { - if (a.isClipEmpty()) { - return b.isClipEmpty(); +DEF_TEST(canvas_clipbounds, reporter) { + SkCanvas canvas(10, 10); + SkIRect irect, irect2; + SkRect rect, rect2; + + irect = canvas.getDeviceClipBounds(); + REPORTER_ASSERT(reporter, irect == SkIRect::MakeWH(10, 10)); + REPORTER_ASSERT(reporter, canvas.getDeviceClipBounds(&irect2)); + REPORTER_ASSERT(reporter, irect == irect2); + + // local bounds are always too big today -- can we trim them? + rect = canvas.getLocalClipBounds(); + REPORTER_ASSERT(reporter, rect.contains(SkRect::MakeWH(10, 10))); + REPORTER_ASSERT(reporter, canvas.getLocalClipBounds(&rect2)); + REPORTER_ASSERT(reporter, rect == rect2); + + canvas.clipRect(SkRect::MakeEmpty()); + + irect = canvas.getDeviceClipBounds(); + REPORTER_ASSERT(reporter, irect == SkIRect::MakeEmpty()); + REPORTER_ASSERT(reporter, !canvas.getDeviceClipBounds(&irect2)); + REPORTER_ASSERT(reporter, irect == irect2); + + rect = canvas.getLocalClipBounds(); + REPORTER_ASSERT(reporter, rect == SkRect::MakeEmpty()); + REPORTER_ASSERT(reporter, !canvas.getLocalClipBounds(&rect2)); + REPORTER_ASSERT(reporter, rect == rect2); + + // Test for wacky sizes that we (historically) have guarded against + { + SkCanvas c(-10, -20); + REPORTER_ASSERT(reporter, c.getBaseLayerSize() == SkISize::MakeEmpty()); + + SkPictureRecorder().beginRecording({ 5, 5, 4, 4 }); } - if (!a.isClipRect()) { - // this is liberally true, since we don't expose a way to know this exactly (for non-rects) - return !b.isClipRect(); - } - SkIRect ar, br; - a.getClipDeviceBounds(&ar); - b.getClipDeviceBounds(&br); - return ar == br; } -class Canvas2CanvasClipVisitor : public SkCanvas::ClipVisitor { -public: - Canvas2CanvasClipVisitor(SkCanvas* target) : fTarget(target) {} +// Will call proc with multiple styles of canse (recording, raster, pdf) +// +template <typename F> static void multi_canvas_driver(int w, int h, F proc) { + proc(SkPictureRecorder().beginRecording(SkRect::MakeIWH(w, h))); - virtual void clipRect(const SkRect& r, SkRegion::Op op, bool aa) SK_OVERRIDE { - fTarget->clipRect(r, op, aa); - } - virtual void clipRRect(const SkRRect& r, SkRegion::Op op, bool aa) SK_OVERRIDE { - fTarget->clipRRect(r, op, aa); - } - virtual void clipPath(const SkPath& p, SkRegion::Op op, bool aa) SK_OVERRIDE { - fTarget->clipPath(p, op, aa); - } + SkNullWStream stream; + proc(SkDocument::MakePDF(&stream)->beginPage(SkIntToScalar(w), SkIntToScalar(h))); -private: - SkCanvas* fTarget; + proc(SkSurface::MakeRasterN32Premul(w, h, nullptr)->getCanvas()); +} + + +const SkIRect gBaseRestrictedR = { 0, 0, 10, 10 }; + +static void test_restriction(skiatest::Reporter* reporter, SkCanvas* canvas) { + REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == gBaseRestrictedR); + + const SkIRect restrictionR = { 2, 2, 8, 8 }; + canvas->androidFramework_setDeviceClipRestriction(restrictionR); + REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == restrictionR); + + const SkIRect clipR = { 4, 4, 6, 6 }; + canvas->clipRect(SkRect::Make(clipR), SkClipOp::kIntersect); + REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == clipR); + +#ifdef SK_SUPPORT_DEPRECATED_CLIPOPS + // now test that expanding clipops can't exceed the restriction + const SkClipOp expanders[] = { + SkClipOp::kUnion_deprecated, + SkClipOp::kXOR_deprecated, + SkClipOp::kReverseDifference_deprecated, + SkClipOp::kReplace_deprecated, + }; + + const SkRect expandR = { 0, 0, 5, 9 }; + SkASSERT(!SkRect::Make(restrictionR).contains(expandR)); + + for (SkClipOp op : expanders) { + canvas->save(); + canvas->clipRect(expandR, op); + REPORTER_ASSERT(reporter, gBaseRestrictedR.contains(canvas->getDeviceClipBounds())); + canvas->restore(); + } +#endif +} + +/** + * Clip restriction logic exists in the canvas itself, and in various kinds of devices. + * + * This test explicitly tries to exercise that variety: + * - picture : empty device but exercises canvas itself + * - pdf : uses SkClipStack in its device (as does SVG and GPU) + * - raster : uses SkRasterClip in its device + */ +DEF_TEST(canvas_clip_restriction, reporter) { + multi_canvas_driver(gBaseRestrictedR.width(), gBaseRestrictedR.height(), + [reporter](SkCanvas* canvas) { test_restriction(reporter, canvas); }); +} + +DEF_TEST(canvas_empty_clip, reporter) { + multi_canvas_driver(50, 50, [reporter](SkCanvas* canvas) { + canvas->save(); + canvas->clipRect({0, 0, 20, 40 }); + REPORTER_ASSERT(reporter, !canvas->isClipEmpty()); + canvas->clipRect({30, 0, 50, 40 }); + REPORTER_ASSERT(reporter, canvas->isClipEmpty()); + }); +} + +static const int kWidth = 2, kHeight = 2; + +static void createBitmap(SkBitmap* bm, SkColor color) { + bm->allocN32Pixels(kWidth, kHeight); + bm->eraseColor(color); +} + +/////////////////////////////////////////////////////////////////////////////// +// Constants used by test steps +const SkPoint kTestPoints[] = { + {SkIntToScalar(0), SkIntToScalar(0)}, + {SkIntToScalar(2), SkIntToScalar(1)}, + {SkIntToScalar(0), SkIntToScalar(2)} +}; +const SkPoint kTestPoints2[] = { + { SkIntToScalar(0), SkIntToScalar(1) }, + { SkIntToScalar(1), SkIntToScalar(1) }, + { SkIntToScalar(2), SkIntToScalar(1) }, + { SkIntToScalar(3), SkIntToScalar(1) }, + { SkIntToScalar(4), SkIntToScalar(1) }, + { SkIntToScalar(5), SkIntToScalar(1) }, + { SkIntToScalar(6), SkIntToScalar(1) }, + { SkIntToScalar(7), SkIntToScalar(1) }, + { SkIntToScalar(8), SkIntToScalar(1) }, + { SkIntToScalar(9), SkIntToScalar(1) }, + { SkIntToScalar(10), SkIntToScalar(1) } }; -static void test_clipVisitor(skiatest::Reporter* reporter, SkCanvas* canvas) { - SkISize size = canvas->getDeviceSize(); +struct TestData { +public: + TestData() + : fRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), + SkIntToScalar(2), SkIntToScalar(1))) + , fMatrix(TestMatrix()) + , fPath(TestPath()) + , fNearlyZeroLengthPath(TestNearlyZeroLengthPath()) + , fIRect(SkIRect::MakeXYWH(0, 0, 2, 1)) + , fRegion(TestRegion()) + , fColor(0x01020304) + , fPoints(kTestPoints) + , fPointCount(3) + , fWidth(2) + , fHeight(2) + , fText("Hello World") + , fPoints2(kTestPoints2) + , fBitmap(TestBitmap()) + { } - SkBitmap bm; - bm.setInfo(SkImageInfo::MakeN32Premul(size.width(), size.height())); - SkCanvas c(bm); + SkRect fRect; + SkMatrix fMatrix; + SkPath fPath; + SkPath fNearlyZeroLengthPath; + SkIRect fIRect; + SkRegion fRegion; + SkColor fColor; + SkPaint fPaint; + const SkPoint* fPoints; + size_t fPointCount; + int fWidth; + int fHeight; + SkString fText; + const SkPoint* fPoints2; + SkBitmap fBitmap; - Canvas2CanvasClipVisitor visitor(&c); - canvas->replayClips(&visitor); +private: + static SkMatrix TestMatrix() { + SkMatrix matrix; + matrix.reset(); + matrix.setScale(SkIntToScalar(2), SkIntToScalar(3)); - REPORTER_ASSERT(reporter, equal_clips(c, *canvas)); -} - -static const int kWidth = 2; -static const int kHeight = 2; + return matrix; + } + static SkPath TestPath() { + SkPath path; + path.addRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), + SkIntToScalar(2), SkIntToScalar(1))); + return path; + } + static SkPath TestNearlyZeroLengthPath() { + SkPath path; + SkPoint pt1 = { 0, 0 }; + SkPoint pt2 = { 0, SK_ScalarNearlyZero }; + SkPoint pt3 = { SkIntToScalar(1), 0 }; + SkPoint pt4 = { SkIntToScalar(1), SK_ScalarNearlyZero/2 }; + path.moveTo(pt1); + path.lineTo(pt2); + path.lineTo(pt3); + path.lineTo(pt4); + return path; + } + static SkRegion TestRegion() { + SkRegion region; + SkIRect rect = SkIRect::MakeXYWH(0, 0, 2, 1); + region.setRect(rect); + return region; + } + static SkBitmap TestBitmap() { + SkBitmap bitmap; + createBitmap(&bitmap, 0x05060708); + return bitmap; + } +}; // Format strings that describe the test context. The %s token is where // the name of the test step is inserted. The context is required for @@ -121,48 +285,9 @@ static const char* const kDefaultAssertMessageFormat = "%s"; static const char* const kCanvasDrawAssertMessageFormat = "Drawing test step %s with SkCanvas"; -static const char* const kPictureDrawAssertMessageFormat = - "Drawing test step %s with SkPicture"; -static const char* const kPictureSecondDrawAssertMessageFormat = - "Duplicate draw of test step %s with SkPicture"; -static const char* const kDeferredDrawAssertMessageFormat = - "Drawing test step %s with SkDeferredCanvas"; -static const char* const kProxyDrawAssertMessageFormat = - "Drawing test step %s with SkProxyCanvas"; -static const char* const kNWayDrawAssertMessageFormat = - "Drawing test step %s with SkNWayCanvas"; -static const char* const kDeferredPreFlushAssertMessageFormat = - "test step %s, SkDeferredCanvas state consistency before flush"; -static const char* const kDeferredPostFlushPlaybackAssertMessageFormat = - "test step %s, SkDeferredCanvas playback canvas state consistency after flush"; -static const char* const kDeferredPostSilentFlushPlaybackAssertMessageFormat = - "test step %s, SkDeferredCanvas playback canvas state consistency after silent flush"; -static const char* const kPictureResourceReuseMessageFormat = - "test step %s, SkPicture duplicate flattened object test"; -static const char* const kProxyStateAssertMessageFormat = - "test step %s, SkProxyCanvas state consistency"; -static const char* const kProxyIndirectStateAssertMessageFormat = - "test step %s, SkProxyCanvas indirect canvas state consistency"; -static const char* const kNWayStateAssertMessageFormat = - "test step %s, SkNWayCanvas state consistency"; -static const char* const kNWayIndirect1StateAssertMessageFormat = - "test step %s, SkNWayCanvas indirect canvas 1 state consistency"; -static const char* const kNWayIndirect2StateAssertMessageFormat = - "test step %s, SkNWayCanvas indirect canvas 2 state consistency"; static const char* const kPdfAssertMessageFormat = "PDF sanity check failed %s"; -static void createBitmap(SkBitmap* bm, SkColor color) { - bm->allocN32Pixels(kWidth, kHeight); - bm->eraseColor(color); -} - -static SkSurface* createSurface(SkColor color) { - SkSurface* surface = SkSurface::NewRasterPMColor(kWidth, kHeight); - surface->getCanvas()->clear(color); - return surface; -} - class CanvasTestStep; static SkTDArray<CanvasTestStep*>& testStepArray() { static SkTDArray<CanvasTestStep*> theTests; @@ -178,7 +303,7 @@ } virtual ~CanvasTestStep() { } - virtual void draw(SkCanvas*, skiatest::Reporter*) = 0; + virtual void draw(SkCanvas*, const TestData&, skiatest::Reporter*) = 0; virtual const char* name() const = 0; const char* assertMessage() { @@ -199,110 +324,41 @@ }; /////////////////////////////////////////////////////////////////////////////// -// Constants used by test steps - -const SkRect kTestRect = - SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), - SkIntToScalar(2), SkIntToScalar(1)); -static SkMatrix testMatrix() { - SkMatrix matrix; - matrix.reset(); - matrix.setScale(SkIntToScalar(2), SkIntToScalar(3)); - return matrix; -} -const SkMatrix kTestMatrix = testMatrix(); -static SkPath test_path() { - SkPath path; - path.addRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), - SkIntToScalar(2), SkIntToScalar(1))); - return path; -} -const SkPath kTestPath = test_path(); -static SkPath test_nearly_zero_length_path() { - SkPath path; - SkPoint pt1 = { 0, 0 }; - SkPoint pt2 = { 0, SK_ScalarNearlyZero }; - SkPoint pt3 = { SkIntToScalar(1), 0 }; - SkPoint pt4 = { SkIntToScalar(1), SK_ScalarNearlyZero/2 }; - path.moveTo(pt1); - path.lineTo(pt2); - path.lineTo(pt3); - path.lineTo(pt4); - return path; -} -const SkPath kNearlyZeroLengthPath = test_nearly_zero_length_path(); -static SkRegion testRegion() { - SkRegion region; - SkIRect rect = SkIRect::MakeXYWH(0, 0, 2, 1); - region.setRect(rect); - return region; -} -const SkIRect kTestIRect = SkIRect::MakeXYWH(0, 0, 2, 1); -const SkRegion kTestRegion = testRegion(); -const SkColor kTestColor = 0x01020304; -const SkPaint kTestPaint; -const SkPoint kTestPoints[3] = { - {SkIntToScalar(0), SkIntToScalar(0)}, - {SkIntToScalar(2), SkIntToScalar(1)}, - {SkIntToScalar(0), SkIntToScalar(2)} -}; -const size_t kTestPointCount = 3; -static SkBitmap testBitmap() { - SkBitmap bitmap; - createBitmap(&bitmap, 0x05060708); - return bitmap; -} -SkBitmap kTestBitmap; // cannot be created during static init -SkString kTestText("Hello World"); -SkPoint kTestPoints2[] = { - { SkIntToScalar(0), SkIntToScalar(1) }, - { SkIntToScalar(1), SkIntToScalar(1) }, - { SkIntToScalar(2), SkIntToScalar(1) }, - { SkIntToScalar(3), SkIntToScalar(1) }, - { SkIntToScalar(4), SkIntToScalar(1) }, - { SkIntToScalar(5), SkIntToScalar(1) }, - { SkIntToScalar(6), SkIntToScalar(1) }, - { SkIntToScalar(7), SkIntToScalar(1) }, - { SkIntToScalar(8), SkIntToScalar(1) }, - { SkIntToScalar(9), SkIntToScalar(1) }, - { SkIntToScalar(10), SkIntToScalar(1) }, -}; - - -/////////////////////////////////////////////////////////////////////////////// // Macros for defining test steps #define TEST_STEP(NAME, FUNCTION) \ class NAME##_TestStep : public CanvasTestStep{ \ public: \ - virtual void draw(SkCanvas* canvas, skiatest::Reporter* reporter) { \ - FUNCTION (canvas, reporter, this); \ + virtual void draw(SkCanvas* canvas, const TestData& d, \ + skiatest::Reporter* reporter) { \ + FUNCTION (canvas, d, reporter, this); \ } \ virtual const char* name() const {return #NAME ;} \ }; \ static NAME##_TestStep NAME##_TestStepInstance; -#define TEST_STEP_NO_PDF(NAME, FUNCTION) \ +#define TEST_STEP_NO_PDF(NAME, FUNCTION) \ class NAME##_TestStep : public CanvasTestStep{ \ public: \ NAME##_TestStep() : CanvasTestStep(false) {} \ - virtual void draw(SkCanvas* canvas, skiatest::Reporter* reporter) { \ - FUNCTION (canvas, reporter, this); \ + virtual void draw(SkCanvas* canvas, const TestData& d, \ + skiatest::Reporter* reporter) { \ + FUNCTION (canvas, d, reporter, this); \ } \ virtual const char* name() const {return #NAME ;} \ }; \ static NAME##_TestStep NAME##_TestStepInstance; -#define SIMPLE_TEST_STEP(NAME, CALL) \ -static void NAME##TestStep(SkCanvas* canvas, skiatest::Reporter*, \ - CanvasTestStep*) { \ - canvas-> CALL ; \ -} \ +#define SIMPLE_TEST_STEP(NAME, CALL) \ +static void NAME##TestStep(SkCanvas* canvas, const TestData& d, \ + skiatest::Reporter*, CanvasTestStep*) { \ + canvas-> CALL ; \ +} \ TEST_STEP(NAME, NAME##TestStep ) #define SIMPLE_TEST_STEP_WITH_ASSERT(NAME, CALL) \ -static void NAME##TestStep(SkCanvas* canvas, skiatest::Reporter* reporter, \ - CanvasTestStep* testStep) { \ +static void NAME##TestStep(SkCanvas* canvas, const TestData& d, \ + skiatest::Reporter*, CanvasTestStep* testStep) { \ REPORTER_ASSERT_MESSAGE(reporter, canvas-> CALL , \ testStep->assertMessage()); \ } \ @@ -317,63 +373,22 @@ SIMPLE_TEST_STEP(Scale, scale(SkIntToScalar(1), SkIntToScalar(2))); SIMPLE_TEST_STEP(Rotate, rotate(SkIntToScalar(1))); SIMPLE_TEST_STEP(Skew, skew(SkIntToScalar(1), SkIntToScalar(2))); -SIMPLE_TEST_STEP(Concat, concat(kTestMatrix)); -SIMPLE_TEST_STEP(SetMatrix, setMatrix(kTestMatrix)); -SIMPLE_TEST_STEP(ClipRect, clipRect(kTestRect)); -SIMPLE_TEST_STEP(ClipPath, clipPath(kTestPath)); -SIMPLE_TEST_STEP(ClipRegion, - clipRegion(kTestRegion, SkRegion::kReplace_Op)); -SIMPLE_TEST_STEP(Clear, clear(kTestColor)); -SIMPLE_TEST_STEP(DrawPaint, drawPaint(kTestPaint)); -SIMPLE_TEST_STEP(DrawPointsPoints, drawPoints(SkCanvas::kPoints_PointMode, - kTestPointCount, kTestPoints, kTestPaint)); -SIMPLE_TEST_STEP(DrawPointsLiness, drawPoints(SkCanvas::kLines_PointMode, - kTestPointCount, kTestPoints, kTestPaint)); -SIMPLE_TEST_STEP(DrawPointsPolygon, drawPoints(SkCanvas::kPolygon_PointMode, - kTestPointCount, kTestPoints, kTestPaint)); -SIMPLE_TEST_STEP(DrawRect, drawRect(kTestRect, kTestPaint)); -SIMPLE_TEST_STEP(DrawPath, drawPath(kTestPath, kTestPaint)); -SIMPLE_TEST_STEP(DrawBitmap, drawBitmap(kTestBitmap, 0, 0)); -SIMPLE_TEST_STEP(DrawBitmapPaint, drawBitmap(kTestBitmap, 0, 0, &kTestPaint)); -SIMPLE_TEST_STEP(DrawBitmapRect, drawBitmapRect(kTestBitmap, NULL, kTestRect, - NULL)); -SIMPLE_TEST_STEP(DrawBitmapRectSrcRect, drawBitmapRect(kTestBitmap, - &kTestIRect, kTestRect, NULL)); -SIMPLE_TEST_STEP(DrawBitmapRectPaint, drawBitmapRect(kTestBitmap, NULL, - kTestRect, &kTestPaint)); -SIMPLE_TEST_STEP(DrawBitmapMatrix, drawBitmapMatrix(kTestBitmap, kTestMatrix, - NULL)); -SIMPLE_TEST_STEP(DrawBitmapMatrixPaint, drawBitmapMatrix(kTestBitmap, - kTestMatrix, &kTestPaint)); -SIMPLE_TEST_STEP(DrawBitmapNine, drawBitmapNine(kTestBitmap, kTestIRect, - kTestRect, NULL)); -SIMPLE_TEST_STEP(DrawBitmapNinePaint, drawBitmapNine(kTestBitmap, kTestIRect, - kTestRect, &kTestPaint)); -SIMPLE_TEST_STEP(DrawSprite, drawSprite(kTestBitmap, 0, 0, NULL)); -SIMPLE_TEST_STEP(DrawSpritePaint, drawSprite(kTestBitmap, 0, 0, &kTestPaint)); -SIMPLE_TEST_STEP(DrawText, drawText(kTestText.c_str(), kTestText.size(), - 0, 1, kTestPaint)); -SIMPLE_TEST_STEP(DrawPosText, drawPosText(kTestText.c_str(), - kTestText.size(), kTestPoints2, kTestPaint)); -SIMPLE_TEST_STEP(DrawTextOnPath, drawTextOnPath(kTestText.c_str(), - kTestText.size(), kTestPath, NULL, kTestPaint)); -SIMPLE_TEST_STEP(DrawTextOnPathMatrix, drawTextOnPath(kTestText.c_str(), - kTestText.size(), kTestPath, &kTestMatrix, kTestPaint)); -SIMPLE_TEST_STEP(DrawData, drawData(kTestText.c_str(), kTestText.size())); -SIMPLE_TEST_STEP(BeginGroup, beginCommentGroup(kTestText.c_str())); -SIMPLE_TEST_STEP(AddComment, addComment(kTestText.c_str(), kTestText.c_str())); -SIMPLE_TEST_STEP(EndGroup, endCommentGroup()); +SIMPLE_TEST_STEP(Concat, concat(d.fMatrix)); +SIMPLE_TEST_STEP(SetMatrix, setMatrix(d.fMatrix)); +SIMPLE_TEST_STEP(ClipRect, clipRect(d.fRect)); +SIMPLE_TEST_STEP(ClipPath, clipPath(d.fPath)); +SIMPLE_TEST_STEP(ClipRegion, clipRegion(d.fRegion, kReplace_SkClipOp)); +SIMPLE_TEST_STEP(Clear, clear(d.fColor)); /////////////////////////////////////////////////////////////////////////////// // Complex test steps -static void SaveMatrixClipStep(SkCanvas* canvas, - skiatest::Reporter* reporter, - CanvasTestStep* testStep) { +static void SaveMatrixClipStep(SkCanvas* canvas, const TestData& d, + skiatest::Reporter* reporter, CanvasTestStep* testStep) { int saveCount = canvas->getSaveCount(); canvas->save(); canvas->translate(SkIntToScalar(1), SkIntToScalar(2)); - canvas->clipRegion(kTestRegion); + canvas->clipRegion(d.fRegion); canvas->restore(); REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount, testStep->assertMessage()); @@ -383,100 +398,91 @@ } TEST_STEP(SaveMatrixClip, SaveMatrixClipStep); -static void SaveLayerStep(SkCanvas* canvas, - skiatest::Reporter* reporter, - CanvasTestStep* testStep) { +static void SaveLayerStep(SkCanvas* canvas, const TestData& d, + skiatest::Reporter* reporter, CanvasTestStep* testStep) { int saveCount = canvas->getSaveCount(); - canvas->saveLayer(NULL, NULL); + canvas->saveLayer(nullptr, nullptr); canvas->restore(); REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount, testStep->assertMessage()); } TEST_STEP(SaveLayer, SaveLayerStep); -static void BoundedSaveLayerStep(SkCanvas* canvas, - skiatest::Reporter* reporter, - CanvasTestStep* testStep) { +static void BoundedSaveLayerStep(SkCanvas* canvas, const TestData& d, + skiatest::Reporter* reporter, CanvasTestStep* testStep) { int saveCount = canvas->getSaveCount(); - canvas->saveLayer(&kTestRect, NULL); + canvas->saveLayer(&d.fRect, nullptr); canvas->restore(); REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount, testStep->assertMessage()); } TEST_STEP(BoundedSaveLayer, BoundedSaveLayerStep); -static void PaintSaveLayerStep(SkCanvas* canvas, - skiatest::Reporter* reporter, - CanvasTestStep* testStep) { +static void PaintSaveLayerStep(SkCanvas* canvas, const TestData& d, + skiatest::Reporter* reporter, CanvasTestStep* testStep) { int saveCount = canvas->getSaveCount(); - canvas->saveLayer(NULL, &kTestPaint); + canvas->saveLayer(nullptr, &d.fPaint); canvas->restore(); REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount, testStep->assertMessage()); } TEST_STEP(PaintSaveLayer, PaintSaveLayerStep); -static void TwoClipOpsStep(SkCanvas* canvas, - skiatest::Reporter*, - CanvasTestStep*) { +static void TwoClipOpsStep(SkCanvas* canvas, const TestData& d, + skiatest::Reporter*, CanvasTestStep*) { // This test exercises a functionality in SkPicture that leads to the // recording of restore offset placeholders. This test will trigger an // assertion at playback time if the placeholders are not properly // filled when the recording ends. - canvas->clipRect(kTestRect); - canvas->clipRegion(kTestRegion); + canvas->clipRect(d.fRect); + canvas->clipRegion(d.fRegion); } TEST_STEP(TwoClipOps, TwoClipOpsStep); // exercise fix for http://code.google.com/p/skia/issues/detail?id=560 // ('SkPathStroker::lineTo() fails for line with length SK_ScalarNearlyZero') -static void DrawNearlyZeroLengthPathTestStep(SkCanvas* canvas, - skiatest::Reporter*, - CanvasTestStep*) { +static void DrawNearlyZeroLengthPathTestStep(SkCanvas* canvas, const TestData& d, + skiatest::Reporter*, CanvasTestStep*) { SkPaint paint; paint.setStrokeWidth(SkIntToScalar(1)); paint.setStyle(SkPaint::kStroke_Style); - canvas->drawPath(kNearlyZeroLengthPath, paint); + canvas->drawPath(d.fNearlyZeroLengthPath, paint); } TEST_STEP(DrawNearlyZeroLengthPath, DrawNearlyZeroLengthPathTestStep); -static void DrawVerticesShaderTestStep(SkCanvas* canvas, - skiatest::Reporter*, - CanvasTestStep*) { +static void DrawVerticesShaderTestStep(SkCanvas* canvas, const TestData& d, + skiatest::Reporter*, CanvasTestStep*) { SkPoint pts[4]; pts[0].set(0, 0); - pts[1].set(SkIntToScalar(kWidth), 0); - pts[2].set(SkIntToScalar(kWidth), SkIntToScalar(kHeight)); - pts[3].set(0, SkIntToScalar(kHeight)); + pts[1].set(SkIntToScalar(d.fWidth), 0); + pts[2].set(SkIntToScalar(d.fWidth), SkIntToScalar(d.fHeight)); + pts[3].set(0, SkIntToScalar(d.fHeight)); SkPaint paint; - SkShader* shader = SkShader::CreateBitmapShader(kTestBitmap, - SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); - paint.setShader(shader)->unref(); - canvas->drawVertices(SkCanvas::kTriangleFan_VertexMode, 4, pts, pts, - NULL, NULL, NULL, 0, paint); + paint.setShader(SkShader::MakeBitmapShader(d.fBitmap, SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode)); + canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4, pts, pts, + nullptr), + SkBlendMode::kModulate, paint); } // NYI: issue 240. TEST_STEP_NO_PDF(DrawVerticesShader, DrawVerticesShaderTestStep); -static void DrawPictureTestStep(SkCanvas* canvas, - skiatest::Reporter*, - CanvasTestStep*) { +static void DrawPictureTestStep(SkCanvas* canvas, const TestData& d, + skiatest::Reporter*, CanvasTestStep*) { SkPictureRecorder recorder; - SkCanvas* testCanvas = recorder.beginRecording(SkIntToScalar(kWidth), SkIntToScalar(kHeight), - NULL, 0); + SkCanvas* testCanvas = recorder.beginRecording(SkIntToScalar(d.fWidth), SkIntToScalar(d.fHeight), + nullptr, 0); testCanvas->scale(SkIntToScalar(2), SkIntToScalar(1)); - testCanvas->clipRect(kTestRect); - testCanvas->drawRect(kTestRect, kTestPaint); - SkAutoTUnref<SkPicture> testPicture(recorder.endRecording()); + testCanvas->clipRect(d.fRect); + testCanvas->drawRect(d.fRect, d.fPaint); - canvas->drawPicture(testPicture); + canvas->drawPicture(recorder.finishRecordingAsPicture()); } TEST_STEP(DrawPicture, DrawPictureTestStep); -static void SaveRestoreTestStep(SkCanvas* canvas, - skiatest::Reporter* reporter, - CanvasTestStep* testStep) { +static void SaveRestoreTestStep(SkCanvas* canvas, const TestData& d, + skiatest::Reporter* reporter, CanvasTestStep* testStep) { int baseSaveCount = canvas->getSaveCount(); int n = canvas->save(); REPORTER_ASSERT_MESSAGE(reporter, baseSaveCount == n, testStep->assertMessage()); @@ -497,43 +503,8 @@ } TEST_STEP(SaveRestore, SaveRestoreTestStep); -static void DrawLayerTestStep(SkCanvas* canvas, - skiatest::Reporter* reporter, - CanvasTestStep* testStep) { - REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(), - testStep->assertMessage()); - canvas->save(); - REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(), - testStep->assertMessage()); - canvas->restore(); - - const SkRect* bounds = NULL; // null means include entire bounds - const SkPaint* paint = NULL; - - canvas->saveLayer(bounds, paint); - REPORTER_ASSERT_MESSAGE(reporter, canvas->isDrawingToLayer(), - testStep->assertMessage()); - canvas->restore(); - REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(), - testStep->assertMessage()); - - canvas->saveLayer(bounds, paint); - canvas->saveLayer(bounds, paint); - REPORTER_ASSERT_MESSAGE(reporter, canvas->isDrawingToLayer(), - testStep->assertMessage()); - canvas->restore(); - REPORTER_ASSERT_MESSAGE(reporter, canvas->isDrawingToLayer(), - testStep->assertMessage()); - canvas->restore(); - // now layer count should be 0 - REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(), - testStep->assertMessage()); -} -TEST_STEP(DrawLayer, DrawLayerTestStep); - -static void NestedSaveRestoreWithSolidPaintTestStep(SkCanvas* canvas, - skiatest::Reporter*, - CanvasTestStep*) { +static void NestedSaveRestoreWithSolidPaintTestStep(SkCanvas* canvas, const TestData& d, + skiatest::Reporter*, CanvasTestStep*) { // This test step challenges the TestDeferredCanvasStateConsistency // test cases because the opaque paint can trigger an optimization // that discards previously recorded commands. The challenge is to maintain @@ -553,9 +524,8 @@ TEST_STEP(NestedSaveRestoreWithSolidPaint, \ NestedSaveRestoreWithSolidPaintTestStep); -static void NestedSaveRestoreWithFlushTestStep(SkCanvas* canvas, - skiatest::Reporter*, - CanvasTestStep*) { +static void NestedSaveRestoreWithFlushTestStep(SkCanvas* canvas, const TestData& d, + skiatest::Reporter*, CanvasTestStep*) { // This test step challenges the TestDeferredCanvasStateConsistency // test case because the canvas flush on a deferred canvas will // reset the recording session. The challenge is to maintain correct @@ -566,262 +536,25 @@ canvas->translate(SkIntToScalar(2), SkIntToScalar(1)); canvas->save(); canvas->scale(SkIntToScalar(3), SkIntToScalar(3)); - canvas->drawRect(kTestRect,kTestPaint); + canvas->drawRect(d.fRect,d.fPaint); canvas->flush(); canvas->restore(); canvas->restore(); } -TEST_STEP(NestedSaveRestoreWithFlush, \ - NestedSaveRestoreWithFlushTestStep); +TEST_STEP(NestedSaveRestoreWithFlush, NestedSaveRestoreWithFlushTestStep); -static void AssertCanvasStatesEqual(skiatest::Reporter* reporter, - const SkCanvas* canvas1, - const SkCanvas* canvas2, - CanvasTestStep* testStep) { - REPORTER_ASSERT_MESSAGE(reporter, canvas1->getDeviceSize() == - canvas2->getDeviceSize(), testStep->assertMessage()); - REPORTER_ASSERT_MESSAGE(reporter, canvas1->getSaveCount() == - canvas2->getSaveCount(), testStep->assertMessage()); - REPORTER_ASSERT_MESSAGE(reporter, canvas1->isDrawingToLayer() == - canvas2->isDrawingToLayer(), testStep->assertMessage()); - - SkRect bounds1, bounds2; - REPORTER_ASSERT_MESSAGE(reporter, - canvas1->getClipBounds(&bounds1) == canvas2->getClipBounds(&bounds2), - testStep->assertMessage()); - REPORTER_ASSERT_MESSAGE(reporter, bounds1 == bounds2, - testStep->assertMessage()); - - REPORTER_ASSERT_MESSAGE(reporter, canvas1->getDrawFilter() == - canvas2->getDrawFilter(), testStep->assertMessage()); - SkIRect deviceBounds1, deviceBounds2; - REPORTER_ASSERT_MESSAGE(reporter, - canvas1->getClipDeviceBounds(&deviceBounds1) == - canvas2->getClipDeviceBounds(&deviceBounds2), - testStep->assertMessage()); - REPORTER_ASSERT_MESSAGE(reporter, deviceBounds1 == deviceBounds2, testStep->assertMessage()); - REPORTER_ASSERT_MESSAGE(reporter, canvas1->getTotalMatrix() == - canvas2->getTotalMatrix(), testStep->assertMessage()); - REPORTER_ASSERT_MESSAGE(reporter, equal_clips(*canvas1, *canvas2), testStep->assertMessage()); - - // The following test code is commented out because the test fails when - // the canvas is an SkPictureRecord or SkDeferredCanvas - // Issue: http://code.google.com/p/skia/issues/detail?id=498 - // Also, creating a LayerIter on an SkProxyCanvas crashes - // Issue: http://code.google.com/p/skia/issues/detail?id=499 - /* - SkCanvas::LayerIter layerIter1(const_cast<SkCanvas*>(canvas1), false); - SkCanvas::LayerIter layerIter2(const_cast<SkCanvas*>(canvas2), false); - while (!layerIter1.done() && !layerIter2.done()) { - REPORTER_ASSERT_MESSAGE(reporter, layerIter1.matrix() == - layerIter2.matrix(), testStep->assertMessage()); - REPORTER_ASSERT_MESSAGE(reporter, layerIter1.clip() == - layerIter2.clip(), testStep->assertMessage()); - REPORTER_ASSERT_MESSAGE(reporter, layerIter1.paint() == - layerIter2.paint(), testStep->assertMessage()); - REPORTER_ASSERT_MESSAGE(reporter, layerIter1.x() == - layerIter2.x(), testStep->assertMessage()); - REPORTER_ASSERT_MESSAGE(reporter, layerIter1.y() == - layerIter2.y(), testStep->assertMessage()); - layerIter1.next(); - layerIter2.next(); +static void TestPdfDevice(skiatest::Reporter* reporter, const TestData& d, CanvasTestStep* step) { + SkDynamicMemoryWStream outStream; + sk_sp<SkDocument> doc(SkDocument::MakePDF(&outStream)); + REPORTER_ASSERT(reporter, doc); + if (!doc) { + return; } - REPORTER_ASSERT_MESSAGE(reporter, layerIter1.done(), - testStep->assertMessage()); - REPORTER_ASSERT_MESSAGE(reporter, layerIter2.done(), - testStep->assertMessage()); - */ -} - -// The following class groups static functions that need to access -// the privates members of SkPictureRecord -class SkPictureTester { -private: - static int EQ(const SkFlatData* a, const SkFlatData* b) { - return *a == *b; - } - - static void AssertFlattenedObjectsEqual( - SkPictureRecord* referenceRecord, - SkPictureRecord* testRecord, - skiatest::Reporter* reporter, - CanvasTestStep* testStep) { - - REPORTER_ASSERT_MESSAGE(reporter, - referenceRecord->fBitmapHeap->count() == - testRecord->fBitmapHeap->count(), testStep->assertMessage()); - REPORTER_ASSERT_MESSAGE(reporter, - referenceRecord->fPaints.count() == - testRecord->fPaints.count(), testStep->assertMessage()); - for (int i = 0; i < referenceRecord->fPaints.count(); ++i) { - REPORTER_ASSERT_MESSAGE(reporter, - EQ(referenceRecord->fPaints[i], testRecord->fPaints[i]), - testStep->assertMessage()); - } - REPORTER_ASSERT_MESSAGE(reporter, - !referenceRecord->fPathHeap == !testRecord->fPathHeap, - testStep->assertMessage()); - // The following tests are commented out because they currently - // fail. Issue: http://code.google.com/p/skia/issues/detail?id=507 - /* - if (referenceRecord->fPathHeap) { - REPORTER_ASSERT_MESSAGE(reporter, - referenceRecord->fPathHeap->count() == - testRecord->fPathHeap->count(), - testStep->assertMessage()); - for (int i = 0; i < referenceRecord->fPathHeap->count(); ++i) { - REPORTER_ASSERT_MESSAGE(reporter, - (*referenceRecord->fPathHeap)[i] == - (*testRecord->fPathHeap)[i], testStep->assertMessage()); - } - } - */ - - } - -public: - - static void TestPictureFlattenedObjectReuse(skiatest::Reporter* reporter, - CanvasTestStep* testStep, - uint32_t recordFlags) { - // Verify that when a test step is executed twice, no extra resources - // are flattened during the second execution - testStep->setAssertMessageFormat(kPictureDrawAssertMessageFormat); - SkPictureRecorder referenceRecorder; - SkCanvas* referenceCanvas = - referenceRecorder.DEPRECATED_beginRecording(SkIntToScalar(kWidth), - SkIntToScalar(kHeight), - NULL, recordFlags); - testStep->draw(referenceCanvas, reporter); - - SkPictureRecorder testRecorder; - SkCanvas* testCanvas = - testRecorder.DEPRECATED_beginRecording(SkIntToScalar(kWidth), - SkIntToScalar(kHeight), - NULL, recordFlags); - testStep->draw(testCanvas, reporter); - testStep->setAssertMessageFormat(kPictureSecondDrawAssertMessageFormat); - testStep->draw(testCanvas, reporter); - - SkPictureRecord* referenceRecord = static_cast<SkPictureRecord*>(referenceCanvas); - SkPictureRecord* testRecord = static_cast<SkPictureRecord*>(testCanvas); - testStep->setAssertMessageFormat(kPictureResourceReuseMessageFormat); - AssertFlattenedObjectsEqual(referenceRecord, testRecord, - reporter, testStep); - } -}; - -static void TestPdfDevice(skiatest::Reporter* reporter, - CanvasTestStep* testStep) { - SkISize pageSize = SkISize::Make(kWidth, kHeight); - SkPDFDevice device(pageSize, pageSize, SkMatrix::I()); - SkCanvas canvas(&device); - testStep->setAssertMessageFormat(kPdfAssertMessageFormat); - testStep->draw(&canvas, reporter); - SkPDFDocument doc; - doc.appendPage(&device); - SkDynamicMemoryWStream stream; - doc.emitPDF(&stream); -} - -// The following class groups static functions that need to access -// the privates members of SkDeferredCanvas -class SkDeferredCanvasTester { -public: - static void TestDeferredCanvasStateConsistency( - skiatest::Reporter* reporter, - CanvasTestStep* testStep, - const SkCanvas& referenceCanvas, bool silent) { - - SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF)); - SkAutoTUnref<SkDeferredCanvas> deferredCanvas(SkDeferredCanvas::Create(surface.get())); - - testStep->setAssertMessageFormat(kDeferredDrawAssertMessageFormat); - testStep->draw(deferredCanvas, reporter); - testStep->setAssertMessageFormat(kDeferredPreFlushAssertMessageFormat); - AssertCanvasStatesEqual(reporter, deferredCanvas, &referenceCanvas, - testStep); - - if (silent) { - deferredCanvas->silentFlush(); - } else { - deferredCanvas->flush(); - } - - testStep->setAssertMessageFormat( - silent ? kDeferredPostSilentFlushPlaybackAssertMessageFormat : - kDeferredPostFlushPlaybackAssertMessageFormat); - AssertCanvasStatesEqual(reporter, - deferredCanvas->immediateCanvas(), - &referenceCanvas, testStep); - - // Verified that deferred canvas state is not affected by flushing - // pending draw operations - - // The following test code is commented out because it currently fails. - // Issue: http://code.google.com/p/skia/issues/detail?id=496 - /* - testStep->setAssertMessageFormat(kDeferredPostFlushAssertMessageFormat); - AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas, - testStep); - */ - } -}; - -// unused -static void TestProxyCanvasStateConsistency( - skiatest::Reporter* reporter, - CanvasTestStep* testStep, - const SkCanvas& referenceCanvas) { - - SkBitmap indirectStore; - createBitmap(&indirectStore, 0xFFFFFFFF); - SkCanvas indirectCanvas(indirectStore); - SkProxyCanvas proxyCanvas(&indirectCanvas); - testStep->setAssertMessageFormat(kProxyDrawAssertMessageFormat); - testStep->draw(&proxyCanvas, reporter); - // Verify that the SkProxyCanvas reports consitent state - testStep->setAssertMessageFormat(kProxyStateAssertMessageFormat); - AssertCanvasStatesEqual(reporter, &proxyCanvas, &referenceCanvas, - testStep); - // Verify that the indirect canvas reports consitent state - testStep->setAssertMessageFormat(kProxyIndirectStateAssertMessageFormat); - AssertCanvasStatesEqual(reporter, &indirectCanvas, &referenceCanvas, - testStep); -} - -// unused -static void TestNWayCanvasStateConsistency( - skiatest::Reporter* reporter, - CanvasTestStep* testStep, - const SkCanvas& referenceCanvas) { - - SkBitmap indirectStore1; - createBitmap(&indirectStore1, 0xFFFFFFFF); - SkCanvas indirectCanvas1(indirectStore1); - - SkBitmap indirectStore2; - createBitmap(&indirectStore2, 0xFFFFFFFF); - SkCanvas indirectCanvas2(indirectStore2); - - SkISize canvasSize = referenceCanvas.getDeviceSize(); - SkNWayCanvas nWayCanvas(canvasSize.width(), canvasSize.height()); - nWayCanvas.addCanvas(&indirectCanvas1); - nWayCanvas.addCanvas(&indirectCanvas2); - - testStep->setAssertMessageFormat(kNWayDrawAssertMessageFormat); - testStep->draw(&nWayCanvas, reporter); - // Verify that the SkProxyCanvas reports consitent state - testStep->setAssertMessageFormat(kNWayStateAssertMessageFormat); - AssertCanvasStatesEqual(reporter, &nWayCanvas, &referenceCanvas, - testStep); - // Verify that the indirect canvases report consitent state - testStep->setAssertMessageFormat(kNWayIndirect1StateAssertMessageFormat); - AssertCanvasStatesEqual(reporter, &indirectCanvas1, &referenceCanvas, - testStep); - testStep->setAssertMessageFormat(kNWayIndirect2StateAssertMessageFormat); - AssertCanvasStatesEqual(reporter, &indirectCanvas2, &referenceCanvas, - testStep); + SkCanvas* canvas = doc->beginPage(SkIntToScalar(d.fWidth), + SkIntToScalar(d.fHeight)); + REPORTER_ASSERT(reporter, canvas); + step->setAssertMessageFormat(kPdfAssertMessageFormat); + step->draw(canvas, d, reporter); } /* @@ -830,93 +563,254 @@ * that the all canvas derivatives report the same state as an SkCanvas * after having executed the test step. */ -static void TestOverrideStateConsistency(skiatest::Reporter* reporter, +static void TestOverrideStateConsistency(skiatest::Reporter* reporter, const TestData& d, CanvasTestStep* testStep) { SkBitmap referenceStore; createBitmap(&referenceStore, 0xFFFFFFFF); SkCanvas referenceCanvas(referenceStore); testStep->setAssertMessageFormat(kCanvasDrawAssertMessageFormat); - testStep->draw(&referenceCanvas, reporter); - - SkDeferredCanvasTester::TestDeferredCanvasStateConsistency(reporter, testStep, referenceCanvas, false); - - SkDeferredCanvasTester::TestDeferredCanvasStateConsistency(reporter, testStep, referenceCanvas, true); - - // The following test code is disabled because SkProxyCanvas is - // missing a lot of virtual overrides on get* methods, which are used - // to verify canvas state. - // Issue: http://code.google.com/p/skia/issues/detail?id=500 - - if (false) { // avoid bit rot, suppress warning - TestProxyCanvasStateConsistency(reporter, testStep, referenceCanvas); - } - - // The following test code is disabled because SkNWayCanvas does not - // report correct clipping and device bounds information - // Issue: http://code.google.com/p/skia/issues/detail?id=501 - - if (false) { // avoid bit rot, suppress warning - TestNWayCanvasStateConsistency(reporter, testStep, referenceCanvas); - } - - if (false) { // avoid bit rot, suppress warning - test_clipVisitor(reporter, &referenceCanvas); - } + testStep->draw(&referenceCanvas, d, reporter); } static void test_newraster(skiatest::Reporter* reporter) { SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10); - SkCanvas* canvas = SkCanvas::NewRaster(info); + const size_t minRowBytes = info.minRowBytes(); + const size_t size = info.getSafeSize(minRowBytes); + SkAutoTMalloc<SkPMColor> storage(size); + SkPMColor* baseAddr = storage.get(); + sk_bzero(baseAddr, size); + + std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes); REPORTER_ASSERT(reporter, canvas); - SkImageInfo info2; - size_t rowBytes; - const SkPMColor* addr = (const SkPMColor*)canvas->peekPixels(&info2, &rowBytes); + SkPixmap pmap; + const SkPMColor* addr = canvas->peekPixels(&pmap) ? pmap.addr32() : nullptr; REPORTER_ASSERT(reporter, addr); - REPORTER_ASSERT(reporter, info == info2); + REPORTER_ASSERT(reporter, info == pmap.info()); + REPORTER_ASSERT(reporter, minRowBytes == pmap.rowBytes()); for (int y = 0; y < info.height(); ++y) { for (int x = 0; x < info.width(); ++x) { REPORTER_ASSERT(reporter, 0 == addr[x]); } - addr = (const SkPMColor*)((const char*)addr + rowBytes); + addr = (const SkPMColor*)((const char*)addr + pmap.rowBytes()); } - SkDELETE(canvas); // now try a deliberately bad info info = info.makeWH(-1, info.height()); - REPORTER_ASSERT(reporter, NULL == SkCanvas::NewRaster(info)); + REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes)); // too big info = info.makeWH(1 << 30, 1 << 30); - REPORTER_ASSERT(reporter, NULL == SkCanvas::NewRaster(info)); + REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes)); // not a valid pixel type info = SkImageInfo::Make(10, 10, kUnknown_SkColorType, info.alphaType()); - REPORTER_ASSERT(reporter, NULL == SkCanvas::NewRaster(info)); + REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes)); // We should succeed with a zero-sized valid info info = SkImageInfo::MakeN32Premul(0, 0); - canvas = SkCanvas::NewRaster(info); + canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes); REPORTER_ASSERT(reporter, canvas); - SkDELETE(canvas); } DEF_TEST(Canvas, reporter) { - // Init global here because bitmap pixels cannot be alocated during - // static initialization - kTestBitmap = testBitmap(); + TestData d; for (int testStep = 0; testStep < testStepArray().count(); testStep++) { - TestOverrideStateConsistency(reporter, testStepArray()[testStep]); - SkPictureTester::TestPictureFlattenedObjectReuse(reporter, - testStepArray()[testStep], 0); + TestOverrideStateConsistency(reporter, d, testStepArray()[testStep]); if (testStepArray()[testStep]->enablePdfTesting()) { - TestPdfDevice(reporter, testStepArray()[testStep]); + TestPdfDevice(reporter, d, testStepArray()[testStep]); } } - // Explicitly call reset(), so we don't leak the pixels (since kTestBitmap is a global) - kTestBitmap.reset(); - test_newraster(reporter); } + +DEF_TEST(Canvas_SaveState, reporter) { + SkCanvas canvas(10, 10); + REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount()); + + int n = canvas.save(); + REPORTER_ASSERT(reporter, 1 == n); + REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount()); + + n = canvas.saveLayer(nullptr, nullptr); + REPORTER_ASSERT(reporter, 2 == n); + REPORTER_ASSERT(reporter, 3 == canvas.getSaveCount()); + + canvas.restore(); + REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount()); + canvas.restore(); + REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount()); +} + +DEF_TEST(Canvas_ClipEmptyPath, reporter) { + SkCanvas canvas(10, 10); + canvas.save(); + SkPath path; + canvas.clipPath(path); + canvas.restore(); + canvas.save(); + path.moveTo(5, 5); + canvas.clipPath(path); + canvas.restore(); + canvas.save(); + path.moveTo(7, 7); + canvas.clipPath(path); // should not assert here + canvas.restore(); +} + +namespace { + +class MockFilterCanvas : public SkPaintFilterCanvas { +public: + MockFilterCanvas(SkCanvas* canvas) : INHERITED(canvas) { } + +protected: + bool onFilter(SkTCopyOnFirstWrite<SkPaint>*, Type) const override { return true; } + +private: + typedef SkPaintFilterCanvas INHERITED; +}; + +} // anonymous namespace + +// SkPaintFilterCanvas should inherit the initial target canvas state. +DEF_TEST(PaintFilterCanvas_ConsistentState, reporter) { + SkCanvas canvas(100, 100); + canvas.clipRect(SkRect::MakeXYWH(12.7f, 12.7f, 75, 75)); + canvas.scale(0.5f, 0.75f); + + MockFilterCanvas filterCanvas(&canvas); + REPORTER_ASSERT(reporter, canvas.getTotalMatrix() == filterCanvas.getTotalMatrix()); + REPORTER_ASSERT(reporter, canvas.getLocalClipBounds() == filterCanvas.getLocalClipBounds()); + + filterCanvas.clipRect(SkRect::MakeXYWH(30.5f, 30.7f, 100, 100)); + filterCanvas.scale(0.75f, 0.5f); + REPORTER_ASSERT(reporter, canvas.getTotalMatrix() == filterCanvas.getTotalMatrix()); + REPORTER_ASSERT(reporter, filterCanvas.getLocalClipBounds().contains(canvas.getLocalClipBounds())); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkCanvasStack.h" +#include "SkNWayCanvas.h" + +// Subclass that takes a bool*, which it updates in its construct (true) and destructor (false) +// to allow the caller to know how long the object is alive. +class LifeLineCanvas : public SkCanvas { + bool* fLifeLine; +public: + LifeLineCanvas(int w, int h, bool* lifeline) : SkCanvas(w, h), fLifeLine(lifeline) { + *fLifeLine = true; + } + ~LifeLineCanvas() { + *fLifeLine = false; + } +}; + +// Check that NWayCanvas does NOT try to manage the lifetime of its sub-canvases +DEF_TEST(NWayCanvas, r) { + const int w = 10; + const int h = 10; + bool life[2]; + { + LifeLineCanvas c0(w, h, &life[0]); + REPORTER_ASSERT(r, life[0]); + } + REPORTER_ASSERT(r, !life[0]); + + + std::unique_ptr<SkCanvas> c0 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[0])); + std::unique_ptr<SkCanvas> c1 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[1])); + REPORTER_ASSERT(r, life[0]); + REPORTER_ASSERT(r, life[1]); + + { + SkNWayCanvas nway(w, h); + nway.addCanvas(c0.get()); + nway.addCanvas(c1.get()); + REPORTER_ASSERT(r, life[0]); + REPORTER_ASSERT(r, life[1]); + } + // Now assert that the death of the nway has NOT also killed the sub-canvases + REPORTER_ASSERT(r, life[0]); + REPORTER_ASSERT(r, life[1]); +} + +// Check that CanvasStack DOES manage the lifetime of its sub-canvases +DEF_TEST(CanvasStack, r) { + const int w = 10; + const int h = 10; + bool life[2]; + std::unique_ptr<SkCanvas> c0 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[0])); + std::unique_ptr<SkCanvas> c1 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[1])); + REPORTER_ASSERT(r, life[0]); + REPORTER_ASSERT(r, life[1]); + + { + SkCanvasStack stack(w, h); + stack.pushCanvas(std::move(c0), {0,0}); + stack.pushCanvas(std::move(c1), {0,0}); + REPORTER_ASSERT(r, life[0]); + REPORTER_ASSERT(r, life[1]); + } + // Now assert that the death of the canvasstack has also killed the sub-canvases + REPORTER_ASSERT(r, !life[0]); + REPORTER_ASSERT(r, !life[1]); +} + +static void test_cliptype(SkCanvas* canvas, skiatest::Reporter* r) { + REPORTER_ASSERT(r, !canvas->isClipEmpty()); + REPORTER_ASSERT(r, canvas->isClipRect()); + + canvas->save(); + canvas->clipRect({0, 0, 0, 0}); + REPORTER_ASSERT(r, canvas->isClipEmpty()); + REPORTER_ASSERT(r, !canvas->isClipRect()); + canvas->restore(); + + canvas->save(); + canvas->clipRect({2, 2, 6, 6}); + REPORTER_ASSERT(r, !canvas->isClipEmpty()); + REPORTER_ASSERT(r, canvas->isClipRect()); + canvas->restore(); + + canvas->save(); + canvas->clipRect({2, 2, 6, 6}, SkClipOp::kDifference); // punch a hole in the clip + REPORTER_ASSERT(r, !canvas->isClipEmpty()); + REPORTER_ASSERT(r, !canvas->isClipRect()); + canvas->restore(); + + REPORTER_ASSERT(r, !canvas->isClipEmpty()); + REPORTER_ASSERT(r, canvas->isClipRect()); +} + +DEF_TEST(CanvasClipType, r) { + // test rasterclip backend + test_cliptype(SkSurface::MakeRasterN32Premul(10, 10)->getCanvas(), r); + + // test clipstack backend + SkDynamicMemoryWStream stream; + test_cliptype(SkDocument::MakePDF(&stream)->beginPage(100, 100), r); +} + +#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK +DEF_TEST(Canvas_LegacyColorBehavior, r) { + sk_sp<SkColorSpace> cs = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, + SkColorSpace::kAdobeRGB_Gamut); + + // Make a Adobe RGB bitmap. + SkBitmap bitmap; + bitmap.allocPixels(SkImageInfo::MakeN32(1, 1, kOpaque_SkAlphaType, cs)); + bitmap.eraseColor(0xFF000000); + + // Wrap it in a legacy canvas. Test that the canvas behaves like a legacy canvas. + SkCanvas canvas(bitmap, SkCanvas::ColorBehavior::kLegacy); + REPORTER_ASSERT(r, !canvas.imageInfo().colorSpace()); + SkPaint p; + p.setColor(SK_ColorRED); + canvas.drawIRect(SkIRect::MakeWH(1, 1), p); + REPORTER_ASSERT(r, SK_ColorRED == SkSwizzle_BGRA_to_PMColor(*bitmap.getAddr32(0, 0))); +} +#endif
diff --git a/src/third_party/skia/tests/ChecksumTest.cpp b/src/third_party/skia/tests/ChecksumTest.cpp index 6248678..1882235 100644 --- a/src/third_party/skia/tests/ChecksumTest.cpp +++ b/src/third_party/skia/tests/ChecksumTest.cpp
@@ -6,20 +6,11 @@ */ #include "SkChecksum.h" +#include "SkOpts.h" #include "SkRandom.h" #include "Test.h" - -// Murmur3 has an optional third seed argument, so we wrap it to fit a uniform type. -static uint32_t murmur_noseed(const uint32_t* d, size_t l) { return SkChecksum::Murmur3(d, l); } - -#define ASSERT(x) REPORTER_ASSERT(r, x) - DEF_TEST(Checksum, r) { - // Algorithms to test. They're currently all uint32_t(const uint32_t*, size_t). - typedef uint32_t(*algorithmProc)(const uint32_t*, size_t); - const algorithmProc kAlgorithms[] = { &SkChecksum::Compute, &murmur_noseed }; - // Put 128 random bytes into two identical buffers. Any multiple of 4 will do. const size_t kBytes = SkAlign4(128); SkRandom rand; @@ -28,25 +19,26 @@ data[i] = tweaked[i] = rand.nextU(); } - // Test each algorithm. - for (size_t i = 0; i < SK_ARRAY_COUNT(kAlgorithms); ++i) { - const algorithmProc algorithm = kAlgorithms[i]; + // Hash of nullptr is always 0. + REPORTER_ASSERT(r, SkOpts::hash(nullptr, 0) == 0); - // Hash of NULL is always 0. - ASSERT(algorithm(NULL, 0) == 0); + const uint32_t hash = SkOpts::hash(data, kBytes); + // Should be deterministic. + REPORTER_ASSERT(r, hash == SkOpts::hash(data, kBytes)); - const uint32_t hash = algorithm(data, kBytes); - // Should be deterministic. - ASSERT(hash == algorithm(data, kBytes)); - - // Changing any single element should change the hash. - for (size_t j = 0; j < SK_ARRAY_COUNT(tweaked); ++j) { - const uint32_t saved = tweaked[j]; - tweaked[j] = rand.nextU(); - const uint32_t tweakedHash = algorithm(tweaked, kBytes); - ASSERT(tweakedHash != hash); - ASSERT(tweakedHash == algorithm(tweaked, kBytes)); - tweaked[j] = saved; - } + // Changing any single element should change the hash. + for (size_t j = 0; j < SK_ARRAY_COUNT(tweaked); ++j) { + const uint32_t saved = tweaked[j]; + tweaked[j] = rand.nextU(); + const uint32_t tweakedHash = SkOpts::hash(tweaked, kBytes); + REPORTER_ASSERT(r, tweakedHash != hash); + REPORTER_ASSERT(r, tweakedHash == SkOpts::hash(tweaked, kBytes)); + tweaked[j] = saved; } } + +DEF_TEST(GoodHash, r) { + // 4 bytes --> hits SkChecksum::Mix fast path. + REPORTER_ASSERT(r, SkGoodHash()(( int32_t)4) == 614249093); + REPORTER_ASSERT(r, SkGoodHash()((uint32_t)4) == 614249093); +}
diff --git a/src/third_party/skia/tests/ClampRangeTest.cpp b/src/third_party/skia/tests/ClampRangeTest.cpp index bf7c95f..9027e9b 100644 --- a/src/third_party/skia/tests/ClampRangeTest.cpp +++ b/src/third_party/skia/tests/ClampRangeTest.cpp
@@ -21,9 +21,9 @@ static void check_value(int64_t bigfx, int expected) { if (bigfx < 0) { R_ASSERT(expected == kV0); - } else if (bigfx > 0xFFFF) { + } else if (bigfx > kFracMax_SkGradFixed) { R_ASSERT(expected == kV1); - } else if (bigfx == 0xFFFF) { + } else if (bigfx == kFracMax_SkGradFixed) { // Either one is fine (and we do see both). R_ASSERT(expected == kV1 || expected == kRamp); } else { @@ -32,7 +32,7 @@ } static void slow_check(const SkClampRange& range, - const SkFixed fx, SkFixed dx, int count) { + const SkGradFixed fx, SkGradFixed dx, int count) { SkASSERT(range.fCount0 + range.fCount1 + range.fCount2 == count); // If dx is large, fx will overflow if updated naively. So we use more bits. @@ -56,9 +56,12 @@ static void test_range(SkFixed fx, SkFixed dx, int count) { + const SkGradFixed gfx = SkFixedToGradFixed(fx); + const SkGradFixed gdx = SkFixedToGradFixed(dx); + SkClampRange range; - range.init(fx, dx, count, kV0, kV1); - slow_check(range, fx, dx, count); + range.init(gfx, gdx, count, kV0, kV1); + slow_check(range, gfx, gdx, count); } #define ff(x) SkIntToFixed(x) @@ -79,8 +82,7 @@ test_range(ff(1), ff(16384), 100); test_range(ff(-1), ff(-16384), 100); test_range(ff(1)/2, ff(16384), 100); - // TODO(reed): skia:2481, fix whatever bug this is, then uncomment - //test_range(ff(1)/2, ff(-16384), 100); + test_range(ff(1)/2, ff(-16384), 100); SkRandom rand;
diff --git a/src/third_party/skia/tests/ClearTest.cpp b/src/third_party/skia/tests/ClearTest.cpp new file mode 100644 index 0000000..bcd0899 --- /dev/null +++ b/src/third_party/skia/tests/ClearTest.cpp
@@ -0,0 +1,205 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#if SK_SUPPORT_GPU +#include "GrContext.h" +#include "GrRenderTargetContext.h" + +static bool check_rect(GrRenderTargetContext* rtc, const SkIRect& rect, uint32_t expectedValue, + uint32_t* actualValue, int* failX, int* failY) { + int w = rect.width(); + int h = rect.height(); + std::unique_ptr<uint32_t[]> pixels(new uint32_t[w * h]); + memset(pixels.get(), ~expectedValue, sizeof(uint32_t) * w * h); + + SkImageInfo dstInfo = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kPremul_SkAlphaType); + + if (!rtc->readPixels(dstInfo, pixels.get(), 0, rect.fLeft, rect.fTop)) { + return false; + } + + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + uint32_t pixel = pixels.get()[y * w + x]; + if (pixel != expectedValue) { + *actualValue = pixel; + *failX = x + rect.fLeft; + *failY = y + rect.fTop; + return false; + } + } + } + return true; +} + +sk_sp<GrRenderTargetContext> newRTC(GrContext* context, int w, int h) { + return context->makeDeferredRenderTargetContext(SkBackingFit::kExact, w, h, + kRGBA_8888_GrPixelConfig, nullptr); +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ClearOp, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + static const int kW = 10; + static const int kH = 10; + + SkIRect fullRect = SkIRect::MakeWH(kW, kH); + sk_sp<GrRenderTargetContext> rtContext; + + // A rectangle that is inset by one on all sides and the 1-pixel wide rectangles that surround + // it. + SkIRect mid1Rect = SkIRect::MakeXYWH(1, 1, kW-2, kH-2); + SkIRect outerLeftEdge = SkIRect::MakeXYWH(0, 0, 1, kH); + SkIRect outerTopEdge = SkIRect::MakeXYWH(0, 0, kW, 1); + SkIRect outerRightEdge = SkIRect::MakeXYWH(kW-1, 0, 1, kH); + SkIRect outerBottomEdge = SkIRect::MakeXYWH(0, kH-1, kW, 1); + + // A rectangle that is inset by two on all sides and the 1-pixel wide rectangles that surround + // it. + SkIRect mid2Rect = SkIRect::MakeXYWH(2, 2, kW-4, kH-4); + SkIRect innerLeftEdge = SkIRect::MakeXYWH(1, 1, 1, kH-2); + SkIRect innerTopEdge = SkIRect::MakeXYWH(1, 1, kW-2, 1); + SkIRect innerRightEdge = SkIRect::MakeXYWH(kW-2, 1, 1, kH-2); + SkIRect innerBottomEdge = SkIRect::MakeXYWH(1, kH-2, kW-2, 1); + + uint32_t actualValue; + int failX, failY; + + static const GrColor kColor1 = 0xABCDEF01; + static const GrColor kColor2 = ~kColor1; + + rtContext = newRTC(context, kW, kH); + SkASSERT(rtContext); + + // Check a full clear + rtContext->clear(&fullRect, kColor1, false); + if (!check_rect(rtContext.get(), fullRect, kColor1, &actualValue, &failX, &failY)) { + ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue, + failX, failY); + } + + rtContext = newRTC(context, kW, kH); + SkASSERT(rtContext); + + // Check two full clears, same color + rtContext->clear(&fullRect, kColor1, false); + rtContext->clear(&fullRect, kColor1, false); + if (!check_rect(rtContext.get(), fullRect, kColor1, &actualValue, &failX, &failY)) { + ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue, + failX, failY); + } + + rtContext = newRTC(context, kW, kH); + SkASSERT(rtContext); + + // Check two full clears, different colors + rtContext->clear(&fullRect, kColor1, false); + rtContext->clear(&fullRect, kColor2, false); + if (!check_rect(rtContext.get(), fullRect, kColor2, &actualValue, &failX, &failY)) { + ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor2, actualValue, + failX, failY); + } + + rtContext = newRTC(context, kW, kH); + SkASSERT(rtContext); + + // Test a full clear followed by a same color inset clear + rtContext->clear(&fullRect, kColor1, false); + rtContext->clear(&mid1Rect, kColor1, false); + if (!check_rect(rtContext.get(), fullRect, kColor1, &actualValue, &failX, &failY)) { + ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue, + failX, failY); + } + + rtContext = newRTC(context, kW, kH); + SkASSERT(rtContext); + + // Test a inset clear followed by same color full clear + rtContext->clear(&mid1Rect, kColor1, false); + rtContext->clear(&fullRect, kColor1, false); + if (!check_rect(rtContext.get(), fullRect, kColor1, &actualValue, &failX, &failY)) { + ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue, + failX, failY); + } + + rtContext = newRTC(context, kW, kH); + SkASSERT(rtContext); + + // Test a full clear followed by a different color inset clear + rtContext->clear(&fullRect, kColor1, false); + rtContext->clear(&mid1Rect, kColor2, false); + if (!check_rect(rtContext.get(), mid1Rect, kColor2, &actualValue, &failX, &failY)) { + ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor2, actualValue, + failX, failY); + } + if (!check_rect(rtContext.get(), outerLeftEdge, kColor1, &actualValue, &failX, &failY) || + !check_rect(rtContext.get(), outerTopEdge, kColor1, &actualValue, &failX, &failY) || + !check_rect(rtContext.get(), outerRightEdge, kColor1, &actualValue, &failX, &failY) || + !check_rect(rtContext.get(), outerBottomEdge, kColor1, &actualValue, &failX, &failY)) { + ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue, + failX, failY); + } + + rtContext = newRTC(context, kW, kH); + SkASSERT(rtContext); + + // Test a inset clear followed by a different full clear + rtContext->clear(&mid1Rect, kColor2, false); + rtContext->clear(&fullRect, kColor1, false); + if (!check_rect(rtContext.get(), fullRect, kColor1, &actualValue, &failX, &failY)) { + ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue, + failX, failY); + } + + rtContext = newRTC(context, kW, kH); + SkASSERT(rtContext); + + // Check three nested clears from largest to smallest where outermost and innermost are same + // color. + rtContext->clear(&fullRect, kColor1, false); + rtContext->clear(&mid1Rect, kColor2, false); + rtContext->clear(&mid2Rect, kColor1, false); + if (!check_rect(rtContext.get(), mid2Rect, kColor1, &actualValue, &failX, &failY)) { + ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue, + failX, failY); + } + if (!check_rect(rtContext.get(), innerLeftEdge, kColor2, &actualValue, &failX, &failY) || + !check_rect(rtContext.get(), innerTopEdge, kColor2, &actualValue, &failX, &failY) || + !check_rect(rtContext.get(), innerRightEdge, kColor2, &actualValue, &failX, &failY) || + !check_rect(rtContext.get(), innerBottomEdge, kColor2, &actualValue, &failX, &failY)) { + ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor2, actualValue, + failX, failY); + } + if (!check_rect(rtContext.get(), outerLeftEdge, kColor1, &actualValue, &failX, &failY) || + !check_rect(rtContext.get(), outerTopEdge, kColor1, &actualValue, &failX, &failY) || + !check_rect(rtContext.get(), outerRightEdge, kColor1, &actualValue, &failX, &failY) || + !check_rect(rtContext.get(), outerBottomEdge, kColor1, &actualValue, &failX, &failY)) { + ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue, + failX, failY); + } + + rtContext = newRTC(context, kW, kH); + SkASSERT(rtContext); + + // Swap the order of the second two clears in the above test. + rtContext->clear(&fullRect, kColor1, false); + rtContext->clear(&mid2Rect, kColor1, false); + rtContext->clear(&mid1Rect, kColor2, false); + if (!check_rect(rtContext.get(), mid1Rect, kColor2, &actualValue, &failX, &failY)) { + ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor2, actualValue, + failX, failY); + } + if (!check_rect(rtContext.get(), outerLeftEdge, kColor1, &actualValue, &failX, &failY) || + !check_rect(rtContext.get(), outerTopEdge, kColor1, &actualValue, &failX, &failY) || + !check_rect(rtContext.get(), outerRightEdge, kColor1, &actualValue, &failX, &failY) || + !check_rect(rtContext.get(), outerBottomEdge, kColor1, &actualValue, &failX, &failY)) { + ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue, + failX, failY); + } +} +#endif
diff --git a/src/third_party/skia/tests/ClipBoundsTest.cpp b/src/third_party/skia/tests/ClipBoundsTest.cpp new file mode 100644 index 0000000..e8aae38 --- /dev/null +++ b/src/third_party/skia/tests/ClipBoundsTest.cpp
@@ -0,0 +1,55 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" +// This is a GR test +#if SK_SUPPORT_GPU +#include "GrClipStackClip.h" +#include "GrContext.h" + +// Ensure that the 'getConservativeBounds' calls are returning bounds clamped +// to the render target +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrClipBounds, reporter, ctxInfo) { + static const int kXSize = 100; + static const int kYSize = 100; + + const SkIRect intScreen = SkIRect::MakeWH(kXSize, kYSize); + const SkRect screen = SkRect::Make(intScreen); + + SkRect clipRect(screen); + clipRect.outset(10, 10); + + // create a clip stack that will (trivially) reduce to a single rect that + // is larger than the screen + SkClipStack stack; + stack.clipRect(clipRect, SkMatrix::I(), kReplace_SkClipOp, false); + + bool isIntersectionOfRects = true; + SkRect devStackBounds; + + stack.getConservativeBounds(0, 0, kXSize, kYSize, + &devStackBounds, + &isIntersectionOfRects); + + // make sure that the SkClipStack is behaving itself + REPORTER_ASSERT(reporter, screen == devStackBounds); + REPORTER_ASSERT(reporter, isIntersectionOfRects); + + // wrap the SkClipStack in a GrClip + GrClipStackClip clipData(&stack); + + SkIRect devGrClipBound; + clipData.getConservativeBounds(kXSize, kYSize, + &devGrClipBound, + &isIntersectionOfRects); + + // make sure that GrClip is behaving itself + REPORTER_ASSERT(reporter, intScreen == devGrClipBound); + REPORTER_ASSERT(reporter, isIntersectionOfRects); +} + +#endif
diff --git a/src/third_party/skia/tests/ClipCacheTest.cpp b/src/third_party/skia/tests/ClipCacheTest.cpp deleted file mode 100644 index d9f3b57..0000000 --- a/src/third_party/skia/tests/ClipCacheTest.cpp +++ /dev/null
@@ -1,233 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Test.h" -// This is a GR test -#if SK_SUPPORT_GPU -#include "../../src/gpu/GrClipMaskManager.h" -#include "GrContextFactory.h" -#include "SkGpuDevice.h" - -static const int X_SIZE = 12; -static const int Y_SIZE = 12; - -//////////////////////////////////////////////////////////////////////////////// -// note: this is unused -static GrTexture* createTexture(GrContext* context) { - unsigned char textureData[X_SIZE][Y_SIZE][4]; - - memset(textureData, 0, 4* X_SIZE * Y_SIZE); - - GrTextureDesc desc; - - // let Skia know we will be using this texture as a render target - desc.fFlags = kRenderTarget_GrTextureFlagBit; - desc.fConfig = kSkia8888_GrPixelConfig; - desc.fWidth = X_SIZE; - desc.fHeight = Y_SIZE; - - // We are initializing the texture with zeros here - GrTexture* texture = context->createUncachedTexture(desc, textureData, 0); - if (!texture) { - return NULL; - } - - return texture; -} - -// Ensure that the 'getConservativeBounds' calls are returning bounds clamped -// to the render target -static void test_clip_bounds(skiatest::Reporter* reporter, GrContext* context) { - - static const int kXSize = 100; - static const int kYSize = 100; - - GrTextureDesc desc; - desc.fFlags = kRenderTarget_GrTextureFlagBit; - desc.fConfig = kAlpha_8_GrPixelConfig; - desc.fWidth = kXSize; - desc.fHeight = kYSize; - - GrTexture* texture = context->createUncachedTexture(desc, NULL, 0); - if (!texture) { - return; - } - - SkAutoTUnref<GrTexture> au(texture); - - SkIRect intScreen = SkIRect::MakeWH(kXSize, kYSize); - SkRect screen; - - screen = SkRect::MakeWH(SkIntToScalar(kXSize), - SkIntToScalar(kYSize)); - - SkRect clipRect(screen); - clipRect.outset(10, 10); - - // create a clip stack that will (trivially) reduce to a single rect that - // is larger than the screen - SkClipStack stack; - stack.clipDevRect(clipRect, SkRegion::kReplace_Op, false); - - bool isIntersectionOfRects = true; - SkRect devStackBounds; - - stack.getConservativeBounds(0, 0, kXSize, kYSize, - &devStackBounds, - &isIntersectionOfRects); - - // make sure that the SkClipStack is behaving itself - REPORTER_ASSERT(reporter, screen == devStackBounds); - REPORTER_ASSERT(reporter, isIntersectionOfRects); - - // wrap the SkClipStack in a GrClipData - GrClipData clipData; - clipData.fClipStack = &stack; - - SkIRect devGrClipDataBound; - clipData.getConservativeBounds(texture, - &devGrClipDataBound, - &isIntersectionOfRects); - - // make sure that GrClipData is behaving itself - REPORTER_ASSERT(reporter, intScreen == devGrClipDataBound); - REPORTER_ASSERT(reporter, isIntersectionOfRects); -} - -//////////////////////////////////////////////////////////////////////////////// -// verify that the top state of the stack matches the passed in state -static void check_state(skiatest::Reporter* reporter, - const GrClipMaskCache& cache, - const SkClipStack& clip, - GrTexture* mask, - const SkIRect& bound) { - REPORTER_ASSERT(reporter, clip.getTopmostGenID() == cache.getLastClipGenID()); - - REPORTER_ASSERT(reporter, mask == cache.getLastMask()); - - SkIRect cacheBound; - cache.getLastBound(&cacheBound); - REPORTER_ASSERT(reporter, bound == cacheBound); -} - -static void check_empty_state(skiatest::Reporter* reporter, - const GrClipMaskCache& cache) { - REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID == cache.getLastClipGenID()); - REPORTER_ASSERT(reporter, NULL == cache.getLastMask()); - - SkIRect emptyBound; - emptyBound.setEmpty(); - - SkIRect cacheBound; - cache.getLastBound(&cacheBound); - REPORTER_ASSERT(reporter, emptyBound == cacheBound); -} - -//////////////////////////////////////////////////////////////////////////////// -// basic test of the cache's base functionality: -// push, pop, set, canReuse & getters -static void test_cache(skiatest::Reporter* reporter, GrContext* context) { - - if (false) { // avoid bit rot, suppress warning - createTexture(context); - } - GrClipMaskCache cache; - - cache.setContext(context); - - // check initial state - check_empty_state(reporter, cache); - - // set the current state - SkIRect bound1; - bound1.set(0, 0, 100, 100); - - SkClipStack clip1(bound1); - - GrTextureDesc desc; - desc.fFlags = kRenderTarget_GrTextureFlagBit; - desc.fWidth = X_SIZE; - desc.fHeight = Y_SIZE; - desc.fConfig = kSkia8888_GrPixelConfig; - - cache.acquireMask(clip1.getTopmostGenID(), desc, bound1); - - GrTexture* texture1 = cache.getLastMask(); - REPORTER_ASSERT(reporter, texture1); - if (NULL == texture1) { - return; - } - - // check that the set took - check_state(reporter, cache, clip1, texture1, bound1); - - // push the state - cache.push(); - - // verify that the pushed state is initially empty - check_empty_state(reporter, cache); - - // modify the new state - SkIRect bound2; - bound2.set(-10, -10, 10, 10); - - SkClipStack clip2(bound2); - - cache.acquireMask(clip2.getTopmostGenID(), desc, bound2); - - GrTexture* texture2 = cache.getLastMask(); - REPORTER_ASSERT(reporter, texture2); - if (NULL == texture2) { - return; - } - - // check that the changes took - check_state(reporter, cache, clip2, texture2, bound2); - - // check to make sure canReuse works - REPORTER_ASSERT(reporter, cache.canReuse(clip2.getTopmostGenID(), bound2)); - REPORTER_ASSERT(reporter, !cache.canReuse(clip1.getTopmostGenID(), bound1)); - - // pop the state - cache.pop(); - - // verify that the old state is restored - check_state(reporter, cache, clip1, texture1, bound1); - - // manually clear the state - cache.reset(); - - // verify it is now empty - check_empty_state(reporter, cache); - - // pop again - so there is no state - cache.pop(); - -#if !defined(SK_DEBUG) - // verify that the getters don't crash - // only do in release since it generates asserts in debug - check_empty_state(reporter, cache); -#endif -} - -DEF_GPUTEST(ClipCache, reporter, factory) { - for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) { - GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type); - if (!GrContextFactory::IsRenderingGLContext(glType)) { - continue; - } - GrContext* context = factory->get(glType); - if (NULL == context) { - continue; - } - - test_cache(reporter, context); - test_clip_bounds(reporter, context); - } -} - -#endif
diff --git a/src/third_party/skia/tests/ClipCubicTest.cpp b/src/third_party/skia/tests/ClipCubicTest.cpp index 31b38f2..854ac32 100644 --- a/src/third_party/skia/tests/ClipCubicTest.cpp +++ b/src/third_party/skia/tests/ClipCubicTest.cpp
@@ -5,10 +5,12 @@ * found in the LICENSE file. */ +#include "SkBitmap.h" #include "SkCanvas.h" #include "SkCubicClipper.h" #include "SkGeometry.h" #include "SkPaint.h" +#include "SkPath.h" #include "Test.h" // Currently the supersampler blitter uses int16_t for its index into an array @@ -161,3 +163,44 @@ test_giantClip(); } + +#include "SkSurface.h" + +DEF_TEST(test_fuzz_crbug_698714, reporter) { + auto surface(SkSurface::MakeRasterN32Premul(500, 500)); + SkCanvas* canvas = surface->getCanvas(); + SkPaint paint; + paint.setAntiAlias(true); + SkPath path; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0,0 + path.lineTo(SkBits2Float(0x43434343), SkBits2Float(0x43430143)); //195.263f, 195.005f + path.lineTo(SkBits2Float(0x43434343), SkBits2Float(0x43434343)); //195.263f, 195.263f + path.lineTo(SkBits2Float(0xb5434343), SkBits2Float(0x434300be)); //-7.2741e-07f, 195.003f + // 195.263f, 195.263f, -1.16387e-05f, 3.58641e-38f, 3.85088e-29f,1.86082e-39f + path.cubicTo(SkBits2Float(0x43434343), SkBits2Float(0x43434341), + SkBits2Float(0xb74343bd), SkBits2Float(0x01434343), + SkBits2Float(0x10434343), SkBits2Float(0x00144332)); + // 4.11823e-38f, 195.263f, 195.263f, 195.263f, -7.2741e-07f, 195.263f + path.cubicTo(SkBits2Float(0x016037c0), SkBits2Float(0x43434343), + SkBits2Float(0x43434343), SkBits2Float(0x43434343), + SkBits2Float(0xb5434343), SkBits2Float(0x43434343)); + // 195.263f, 195.263f, -1.16387e-05f, 3.58641e-38f, 195.263f, -2 + path.cubicTo(SkBits2Float(0x43434344), SkBits2Float(0x43434341), + SkBits2Float(0xb74343bd), SkBits2Float(0x01434343), + SkBits2Float(0x43434343), SkBits2Float(0xc0000014)); + // -5.87228e+06f, 3.7773e-07f, 3.60231e-13f, -6.64511e+06f,2.77692e-15f, 2.48803e-15f + path.cubicTo(SkBits2Float(0xcab33535), SkBits2Float(0x34cacaca), + SkBits2Float(0x2acacaca), SkBits2Float(0xcacacae3), + SkBits2Float(0x27481927), SkBits2Float(0x27334805)); + path.lineTo(SkBits2Float(0xb5434343), SkBits2Float(0x43434343)); //-7.2741e-07f, 195.263f + // 195.263f, 195.263f, -1.16387e-05f, 195.212f, 195.263f, -2 + path.cubicTo(SkBits2Float(0x43434343), SkBits2Float(0x43434341), + SkBits2Float(0xb74343b9), SkBits2Float(0x43433643), + SkBits2Float(0x43434343), SkBits2Float(0xc0000014)); + path.lineTo(SkBits2Float(0xc7004343), SkBits2Float(0x27480527)); //-32835.3f, 2.77584e-15f + path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0,0 + path.close(); + canvas->clipRect({0, 0, 65, 202}); + canvas->drawPath(path, paint); +}
diff --git a/src/third_party/skia/tests/ClipStackTest.cpp b/src/third_party/skia/tests/ClipStackTest.cpp index 662e680..6f24908 100644 --- a/src/third_party/skia/tests/ClipStackTest.cpp +++ b/src/third_party/skia/tests/ClipStackTest.cpp
@@ -6,15 +6,23 @@ */ #include "Test.h" -#if SK_SUPPORT_GPU - #include "GrReducedClip.h" -#endif #include "SkClipStack.h" #include "SkPath.h" #include "SkRandom.h" #include "SkRect.h" #include "SkRegion.h" +#if SK_SUPPORT_GPU +#include "GrClipStackClip.h" +#include "GrReducedClip.h" +#include "GrResourceCache.h" +#include "GrSurfaceProxyPriv.h" +#include "GrTexture.h" +#include "GrTextureProxy.h" +typedef GrReducedClip::ElementList ElementList; +typedef GrReducedClip::InitialState InitialState; +#endif + static void test_assign_and_comparison(skiatest::Reporter* reporter) { SkClipStack s; bool doAA = false; @@ -30,21 +38,21 @@ p.lineTo(7, 8); p.lineTo(5, 9); p.close(); - s.clipDevPath(p, SkRegion::kIntersect_Op, doAA); + s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA); s.save(); REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); SkRect r = SkRect::MakeLTRB(1, 2, 3, 4); - s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); + s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA); r = SkRect::MakeLTRB(10, 11, 12, 13); - s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); + s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA); s.save(); REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); r = SkRect::MakeLTRB(14, 15, 16, 17); - s.clipDevRect(r, SkRegion::kUnion_Op, doAA); + s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA); // Test that assignment works. SkClipStack copy = s; @@ -59,7 +67,7 @@ s.save(); REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); r = SkRect::MakeLTRB(14, 15, 16, 17); - s.clipDevRect(r, SkRegion::kUnion_Op, doAA); + s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA); REPORTER_ASSERT(reporter, s == copy); // Test that a different op on one level triggers not equal. @@ -68,7 +76,7 @@ s.save(); REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); r = SkRect::MakeLTRB(14, 15, 16, 17); - s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); + s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA); REPORTER_ASSERT(reporter, s != copy); // Test that version constructed with rect-path rather than a rect is still considered equal. @@ -76,7 +84,7 @@ s.save(); SkPath rp; rp.addRect(r); - s.clipDevPath(rp, SkRegion::kUnion_Op, doAA); + s.clipPath(rp, SkMatrix::I(), kUnion_SkClipOp, doAA); REPORTER_ASSERT(reporter, s == copy); // Test that different rects triggers not equal. @@ -86,7 +94,7 @@ REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); r = SkRect::MakeLTRB(24, 25, 26, 27); - s.clipDevRect(r, SkRegion::kUnion_Op, doAA); + s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA); REPORTER_ASSERT(reporter, s != copy); // Sanity check @@ -109,7 +117,7 @@ REPORTER_ASSERT(reporter, 1 == s.getSaveCount()); p.addRect(r); - s.clipDevPath(p, SkRegion::kIntersect_Op, doAA); + s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA); REPORTER_ASSERT(reporter, s != copy); } @@ -137,14 +145,14 @@ for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) { // the union op will prevent these from being fused together - stack.clipDevRect(gRects[i], SkRegion::kUnion_Op, false); + stack.clipRect(gRects[i], SkMatrix::I(), kUnion_SkClipOp, false); } assert_count(reporter, stack, 4); // bottom to top iteration { - const SkClipStack::Element* element = NULL; + const SkClipStack::Element* element = nullptr; SkClipStack::B2TIter iter(stack); int i; @@ -159,7 +167,7 @@ // top to bottom iteration { - const SkClipStack::Element* element = NULL; + const SkClipStack::Element* element = nullptr; SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); int i; @@ -174,11 +182,11 @@ // skipToTopmost { - const SkClipStack::Element* element = NULL; + const SkClipStack::Element* element = nullptr; SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart); - element = iter.skipToTopmost(SkRegion::kUnion_Op); + element = iter.skipToTopmost(kUnion_SkClipOp); REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType()); REPORTER_ASSERT(reporter, element->getRect() == gRects[3]); } @@ -217,12 +225,12 @@ { 10, 10, 50, 50 }, }; - static const SkRegion::Op gOps[] = { - SkRegion::kIntersect_Op, - SkRegion::kDifference_Op, - SkRegion::kUnion_Op, - SkRegion::kXOR_Op, - SkRegion::kReverseDifference_Op + static const SkClipOp gOps[] = { + kIntersect_SkClipOp, + kDifference_SkClipOp, + kUnion_SkClipOp, + kXOR_SkClipOp, + kReverseDifference_SkClipOp }; SkRect rectA, rectB; @@ -262,16 +270,16 @@ SkDEBUGFAIL("Don't call this with kEmpty."); break; case SkClipStack::Element::kRect_Type: - stack.clipDevRect(rectA, SkRegion::kIntersect_Op, false); - stack.clipDevRect(rectB, gOps[op], false); + stack.clipRect(rectA, SkMatrix::I(), kIntersect_SkClipOp, false); + stack.clipRect(rectB, SkMatrix::I(), gOps[op], false); break; case SkClipStack::Element::kRRect_Type: - stack.clipDevRRect(rrectA, SkRegion::kIntersect_Op, false); - stack.clipDevRRect(rrectB, gOps[op], false); + stack.clipRRect(rrectA, SkMatrix::I(), kIntersect_SkClipOp, false); + stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false); break; case SkClipStack::Element::kPath_Type: - stack.clipDevPath(pathA, SkRegion::kIntersect_Op, false); - stack.clipDevPath(pathB, gOps[op], false); + stack.clipPath(pathA, SkMatrix::I(), kIntersect_SkClipOp, false); + stack.clipPath(pathB, SkMatrix::I(), gOps[op], false); break; } @@ -283,7 +291,7 @@ if (SkClipStack::Element::kRect_Type == primType) { REPORTER_ASSERT(reporter, isIntersectionOfRects == - (gOps[op] == SkRegion::kIntersect_Op)); + (gOps[op] == kIntersect_SkClipOp)); } else { REPORTER_ASSERT(reporter, !isIntersectionOfRects); } @@ -331,8 +339,8 @@ clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5)); clipB.setFillType(SkPath::kInverseEvenOdd_FillType); - stack.clipDevPath(clipA, SkRegion::kReplace_Op, false); - stack.clipDevPath(clipB, SkRegion::kUnion_Op, false); + stack.clipPath(clipA, SkMatrix::I(), kReplace_SkClipOp, false); + stack.clipPath(clipB, SkMatrix::I(), kUnion_SkClipOp, false); REPORTER_ASSERT(reporter, stack.isWideOpen()); REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID()); @@ -342,7 +350,7 @@ { SkClipStack stack; - stack.clipDevRect(rectA, SkRegion::kUnion_Op, false); + stack.clipRect(rectA, SkMatrix::I(), kUnion_SkClipOp, false); REPORTER_ASSERT(reporter, stack.isWideOpen()); REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID()); @@ -355,7 +363,7 @@ SkRect emptyRect; emptyRect.setEmpty(); - stack.clipDevRect(emptyRect, SkRegion::kDifference_Op, false); + stack.clipRect(emptyRect, SkMatrix::I(), kDifference_SkClipOp, false); REPORTER_ASSERT(reporter, stack.isWideOpen()); REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID()); @@ -367,7 +375,7 @@ stack.save(); - stack.clipDevRect(rectA, SkRegion::kReplace_Op, false); + stack.clipRect(rectA, SkMatrix::I(), kReplace_SkClipOp, false); REPORTER_ASSERT(reporter, !stack.isWideOpen()); REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID()); @@ -383,7 +391,7 @@ SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); - const SkClipStack::Element* element = NULL; + const SkClipStack::Element* element = nullptr; int count = 0; for (element = iter.prev(); element; element = iter.prev(), ++count) { @@ -401,7 +409,7 @@ path.addRect(rect); path.toggleInverseFillType(); SkClipStack stack; - stack.clipDevPath(path, SkRegion::kIntersect_Op, false); + stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false); SkRect bounds; SkClipStack::BoundsType boundsType; @@ -423,9 +431,9 @@ { SkClipStack stack; REPORTER_ASSERT(reporter, 0 == count(stack)); - stack.clipDevRect(rect, SkRegion::kReplace_Op, false); + stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false); REPORTER_ASSERT(reporter, 1 == count(stack)); - stack.clipDevRect(rect, SkRegion::kReplace_Op, false); + stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false); REPORTER_ASSERT(reporter, 1 == count(stack)); } @@ -434,9 +442,9 @@ { SkClipStack stack; REPORTER_ASSERT(reporter, 0 == count(stack)); - stack.clipDevRect(rect, SkRegion::kReplace_Op, true); + stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true); REPORTER_ASSERT(reporter, 1 == count(stack)); - stack.clipDevRect(rect, SkRegion::kReplace_Op, true); + stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true); REPORTER_ASSERT(reporter, 1 == count(stack)); } @@ -445,23 +453,23 @@ { SkClipStack stack; REPORTER_ASSERT(reporter, 0 == count(stack)); - stack.clipDevRect(rect, SkRegion::kReplace_Op, false); + stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false); REPORTER_ASSERT(reporter, 1 == count(stack)); - stack.clipDevRect(rect, SkRegion::kReplace_Op, true); + stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true); REPORTER_ASSERT(reporter, 1 == count(stack)); - stack.clipDevRect(rect, SkRegion::kReplace_Op, false); + stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false); REPORTER_ASSERT(reporter, 1 == count(stack)); } // Make sure replace clip rects don't collapse too much. { SkClipStack stack; - stack.clipDevRect(rect, SkRegion::kReplace_Op, false); - stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false); + stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false); + stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, 1 == count(stack)); stack.save(); - stack.clipDevRect(rect, SkRegion::kReplace_Op, false); + stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false); REPORTER_ASSERT(reporter, 2 == count(stack)); stack.getBounds(&bound, &type, &isIntersectionOfRects); REPORTER_ASSERT(reporter, bound == rect); @@ -469,16 +477,16 @@ REPORTER_ASSERT(reporter, 1 == count(stack)); stack.save(); - stack.clipDevRect(rect, SkRegion::kReplace_Op, false); - stack.clipDevRect(rect, SkRegion::kReplace_Op, false); + stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false); + stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false); REPORTER_ASSERT(reporter, 2 == count(stack)); stack.restore(); REPORTER_ASSERT(reporter, 1 == count(stack)); stack.save(); - stack.clipDevRect(rect, SkRegion::kReplace_Op, false); - stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false); - stack.clipDevRect(rect, SkRegion::kReplace_Op, false); + stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false); + stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false); + stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false); REPORTER_ASSERT(reporter, 2 == count(stack)); stack.restore(); REPORTER_ASSERT(reporter, 1 == count(stack)); @@ -495,18 +503,18 @@ { SkClipStack stack; REPORTER_ASSERT(reporter, 0 == count(stack)); - stack.clipDevPath(path, SkRegion::kReplace_Op, false); + stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false); REPORTER_ASSERT(reporter, 1 == count(stack)); - stack.clipDevPath(path, SkRegion::kReplace_Op, false); + stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false); REPORTER_ASSERT(reporter, 1 == count(stack)); } // Replacing rect with path. { SkClipStack stack; - stack.clipDevRect(rect, SkRegion::kReplace_Op, true); + stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true); REPORTER_ASSERT(reporter, 1 == count(stack)); - stack.clipDevPath(path, SkRegion::kReplace_Op, true); + stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, true); REPORTER_ASSERT(reporter, 1 == count(stack)); } } @@ -529,9 +537,9 @@ { SkClipStack stack; - stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, false); + stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, false); - stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false); + stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, 1 == count(stack)); @@ -544,9 +552,9 @@ { SkClipStack stack; - stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true); + stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true); - stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true); + stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, true); REPORTER_ASSERT(reporter, 1 == count(stack)); @@ -559,9 +567,9 @@ { SkClipStack stack; - stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true); + stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true); - stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false); + stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, 2 == count(stack)); @@ -574,9 +582,9 @@ { SkClipStack stack; - stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, true); + stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, true); - stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, false); + stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, 1 == count(stack)); @@ -589,9 +597,9 @@ { SkClipStack stack; - stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, false); + stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, false); - stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, true); + stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, true); REPORTER_ASSERT(reporter, 1 == count(stack)); @@ -604,9 +612,9 @@ { SkClipStack stack; - stack.clipDevRect(nestedChild, SkRegion::kReplace_Op, false); + stack.clipRect(nestedChild, SkMatrix::I(), kReplace_SkClipOp, false); - stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, true); + stack.clipRect(nestedParent, SkMatrix::I(), kIntersect_SkClipOp, true); REPORTER_ASSERT(reporter, 2 == count(stack)); @@ -634,32 +642,32 @@ { SkClipStack stack; - stack.clipDevRect(outsideRect, SkRegion::kDifference_Op, false); - // return false because quickContains currently does not care for kDifference_Op + stack.clipRect(outsideRect, SkMatrix::I(), kDifference_SkClipOp, false); + // return false because quickContains currently does not care for kDifference_SkClipOp REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); } // Replace Op tests { SkClipStack stack; - stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false); + stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false); REPORTER_ASSERT(reporter, true == stack.quickContains(testRect)); } { SkClipStack stack; - stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false); + stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false); stack.save(); // To prevent in-place substitution by replace OP - stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false); + stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false); REPORTER_ASSERT(reporter, true == stack.quickContains(testRect)); stack.restore(); } { SkClipStack stack; - stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false); + stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false); stack.save(); // To prevent in-place substitution by replace OP - stack.clipDevRect(insideRect, SkRegion::kReplace_Op, false); + stack.clipRect(insideRect, SkMatrix::I(), kReplace_SkClipOp, false); REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); stack.restore(); } @@ -667,59 +675,59 @@ // Verify proper traversal of multi-element clip { SkClipStack stack; - stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false); + stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false); // Use a path for second clip to prevent in-place intersection - stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false); + stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); } // Intersect Op tests with rectangles { SkClipStack stack; - stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false); + stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, true == stack.quickContains(testRect)); } { SkClipStack stack; - stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false); + stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); } { SkClipStack stack; - stack.clipDevRect(intersectingRect, SkRegion::kIntersect_Op, false); + stack.clipRect(intersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); } { SkClipStack stack; - stack.clipDevRect(nonIntersectingRect, SkRegion::kIntersect_Op, false); + stack.clipRect(nonIntersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); } // Intersect Op tests with circle paths { SkClipStack stack; - stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false); + stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, true == stack.quickContains(testRect)); } { SkClipStack stack; - stack.clipDevPath(insideCircle, SkRegion::kIntersect_Op, false); + stack.clipPath(insideCircle, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); } { SkClipStack stack; - stack.clipDevPath(intersectingCircle, SkRegion::kIntersect_Op, false); + stack.clipPath(intersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); } { SkClipStack stack; - stack.clipDevPath(nonIntersectingCircle, SkRegion::kIntersect_Op, false); + stack.clipPath(nonIntersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); } @@ -729,7 +737,7 @@ SkPath path; path.addRect(outsideRect); path.toggleInverseFillType(); - stack.clipDevPath(path, SkRegion::kIntersect_Op, false); + stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); } @@ -738,7 +746,7 @@ SkPath path; path.addRect(insideRect); path.toggleInverseFillType(); - stack.clipDevPath(path, SkRegion::kIntersect_Op, false); + stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); } @@ -747,7 +755,7 @@ SkPath path; path.addRect(intersectingRect); path.toggleInverseFillType(); - stack.clipDevPath(path, SkRegion::kIntersect_Op, false); + stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); } @@ -756,7 +764,7 @@ SkPath path; path.addRect(nonIntersectingRect); path.toggleInverseFillType(); - stack.clipDevPath(path, SkRegion::kIntersect_Op, false); + stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, true == stack.quickContains(testRect)); } @@ -765,7 +773,7 @@ SkClipStack stack; SkPath path = outsideCircle; path.toggleInverseFillType(); - stack.clipDevPath(path, SkRegion::kIntersect_Op, false); + stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); } @@ -773,7 +781,7 @@ SkClipStack stack; SkPath path = insideCircle; path.toggleInverseFillType(); - stack.clipDevPath(path, SkRegion::kIntersect_Op, false); + stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); } @@ -781,7 +789,7 @@ SkClipStack stack; SkPath path = intersectingCircle; path.toggleInverseFillType(); - stack.clipDevPath(path, SkRegion::kIntersect_Op, false); + stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, false == stack.quickContains(testRect)); } @@ -789,11 +797,56 @@ SkClipStack stack; SkPath path = nonIntersectingCircle; path.toggleInverseFillType(); - stack.clipDevPath(path, SkRegion::kIntersect_Op, false); + stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false); REPORTER_ASSERT(reporter, true == stack.quickContains(testRect)); } } +static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) { + region->setRect(bounds); + SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart); + while (const SkClipStack::Element *element = iter.next()) { + SkRegion elemRegion; + SkRegion boundsRgn(bounds); + SkPath path; + + switch (element->getType()) { + case SkClipStack::Element::kEmpty_Type: + elemRegion.setEmpty(); + break; + default: + element->asPath(&path); + elemRegion.setPath(path, boundsRgn); + break; + } + region->op(elemRegion, (SkRegion::Op)element->getOp()); + } +} + +static void test_invfill_diff_bug(skiatest::Reporter* reporter) { + SkClipStack stack; + stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), kIntersect_SkClipOp, false); + + SkPath path; + path.addRect({30, 10, 40, 20}); + path.setFillType(SkPath::kInverseWinding_FillType); + stack.clipPath(path, SkMatrix::I(), kDifference_SkClipOp, false); + + REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID()); + + SkRect stackBounds; + SkClipStack::BoundsType stackBoundsType; + stack.getBounds(&stackBounds, &stackBoundsType); + + REPORTER_ASSERT(reporter, stackBounds.isEmpty()); + REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType); + + SkRegion region; + set_region_to_stack(stack, {0, 0, 50, 30}, ®ion); + + REPORTER_ASSERT(reporter, region.isEmpty()); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// #if SK_SUPPORT_GPU @@ -803,54 +856,58 @@ // reduced stack. typedef void (*AddElementFunc) (const SkRect& rect, bool invert, - SkRegion::Op op, - SkClipStack* stack); + SkClipOp op, + SkClipStack* stack, + bool doAA); -static void add_round_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) { +static void add_round_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack, + bool doAA) { SkScalar rx = rect.width() / 10; SkScalar ry = rect.height() / 20; if (invert) { SkPath path; path.addRoundRect(rect, rx, ry); path.setFillType(SkPath::kInverseWinding_FillType); - stack->clipDevPath(path, op, false); + stack->clipPath(path, SkMatrix::I(), op, doAA); } else { SkRRect rrect; rrect.setRectXY(rect, rx, ry); - stack->clipDevRRect(rrect, op, false); + stack->clipRRect(rrect, SkMatrix::I(), op, doAA); } }; -static void add_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) { +static void add_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack, + bool doAA) { if (invert) { SkPath path; path.addRect(rect); path.setFillType(SkPath::kInverseWinding_FillType); - stack->clipDevPath(path, op, false); + stack->clipPath(path, SkMatrix::I(), op, doAA); } else { - stack->clipDevRect(rect, op, false); + stack->clipRect(rect, SkMatrix::I(), op, doAA); } }; -static void add_oval(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) { +static void add_oval(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack, + bool doAA) { SkPath path; path.addOval(rect); if (invert) { path.setFillType(SkPath::kInverseWinding_FillType); } - stack->clipDevPath(path, op, false); + stack->clipPath(path, SkMatrix::I(), op, doAA); }; static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) { switch (element.getType()) { case SkClipStack::Element::kRect_Type: - stack->clipDevRect(element.getRect(), element.getOp(), element.isAA()); + stack->clipRect(element.getRect(), SkMatrix::I(), element.getOp(), element.isAA()); break; case SkClipStack::Element::kRRect_Type: - stack->clipDevRRect(element.getRRect(), element.getOp(), element.isAA()); + stack->clipRRect(element.getRRect(), SkMatrix::I(), element.getOp(), element.isAA()); break; case SkClipStack::Element::kPath_Type: - stack->clipDevPath(element.getPath(), element.getOp(), element.isAA()); + stack->clipPath(element.getPath(), SkMatrix::I(), element.getOp(), element.isAA()); break; case SkClipStack::Element::kEmpty_Type: SkDEBUGFAIL("Why did the reducer produce an explicit empty."); @@ -859,34 +916,16 @@ } } -static void add_elem_to_region(const SkClipStack::Element& element, - const SkIRect& bounds, - SkRegion* region) { - SkRegion elemRegion; - SkRegion boundsRgn(bounds); - SkPath path; - - switch (element.getType()) { - case SkClipStack::Element::kEmpty_Type: - elemRegion.setEmpty(); - break; - default: - element.asPath(&path); - elemRegion.setPath(path, boundsRgn); - break; - } - region->op(elemRegion, element.getOp()); -} - static void test_reduced_clip_stack(skiatest::Reporter* reporter) { // We construct random clip stacks, reduce them, and then rasterize both versions to verify that // they are equal. // All the clip elements will be contained within these bounds. - static const SkRect kBounds = SkRect::MakeWH(100, 100); + static const SkIRect kIBounds = SkIRect::MakeWH(100, 100); + static const SkRect kBounds = SkRect::Make(kIBounds); enum { - kNumTests = 200, + kNumTests = 250, kMinElemsPerTest = 1, kMaxElemsPerTest = 50, }; @@ -895,13 +934,13 @@ static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5; static const SkScalar kMaxElemSizeFrac = SK_Scalar1; - static const SkRegion::Op kOps[] = { - SkRegion::kDifference_Op, - SkRegion::kIntersect_Op, - SkRegion::kUnion_Op, - SkRegion::kXOR_Op, - SkRegion::kReverseDifference_Op, - SkRegion::kReplace_Op, + static const SkClipOp kOps[] = { + kDifference_SkClipOp, + kIntersect_SkClipOp, + kUnion_SkClipOp, + kXOR_SkClipOp, + kReverseDifference_SkClipOp, + kReplace_SkClipOp, }; // Replace operations short-circuit the optimizer. We want to make sure that we test this code @@ -912,6 +951,8 @@ // We want to test inverse fills. However, they are quite rare in practice so don't over do it. static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest; + static const SkScalar kFractionAntialiased = 0.25; + static const AddElementFunc kElementFuncs[] = { add_rect, add_round_rect, @@ -921,12 +962,16 @@ SkRandom r; for (int i = 0; i < kNumTests; ++i) { + SkString testCase; + testCase.printf("Iteration %d", i); + // Randomly generate a clip stack. SkClipStack stack; int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest); + bool doAA = r.nextBiasedBool(kFractionAntialiased); for (int e = 0; e < numElems; ++e) { - SkRegion::Op op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))]; - if (op == SkRegion::kReplace_Op) { + SkClipOp op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))]; + if (op == kReplace_SkClipOp) { if (r.nextU() % kReplaceDiv) { --e; continue; @@ -937,81 +982,97 @@ bool doSave = r.nextBool(); SkSize size = SkSize::Make( - SkScalarFloorToScalar(SkScalarMul(kBounds.width(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))), - SkScalarFloorToScalar(SkScalarMul(kBounds.height(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac)))); + kBounds.width() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac), + kBounds.height() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac)); - SkPoint xy = {SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth)), - SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight))}; + SkPoint xy = {r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth), + r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight)}; - SkRect rect = SkRect::MakeXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight); + SkRect rect; + if (doAA) { + rect.setXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight); + if (GrClip::IsPixelAligned(rect)) { + // Don't create an element that may accidentally become not antialiased. + rect.outset(0.5f, 0.5f); + } + SkASSERT(!GrClip::IsPixelAligned(rect)); + } else { + rect.setXYWH(SkScalarFloorToScalar(xy.fX), + SkScalarFloorToScalar(xy.fY), + SkScalarCeilToScalar(size.fWidth), + SkScalarCeilToScalar(size.fHeight)); + } bool invert = r.nextBiasedBool(kFractionInverted); - kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack); + kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack, + doAA); if (doSave) { stack.save(); } } - SkRect inflatedBounds = kBounds; - inflatedBounds.outset(kBounds.width() / 2, kBounds.height() / 2); - SkIRect inflatedIBounds; - inflatedBounds.roundOut(&inflatedIBounds); + // Zero the memory we will new the GrReducedClip into. This ensures the elements gen ID + // will be kInvalidGenID if left uninitialized. + SkAlignedSTStorage<1, GrReducedClip> storage; + memset(storage.get(), 0, sizeof(GrReducedClip)); + GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID); - typedef GrReducedClip::ElementList ElementList; // Get the reduced version of the stack. - ElementList reducedClips; - int32_t reducedGenID; - GrReducedClip::InitialState initial; - SkIRect tBounds(inflatedIBounds); - SkIRect* tightBounds = r.nextBool() ? &tBounds : NULL; - GrReducedClip::ReduceClipStack(stack, - inflatedIBounds, - &reducedClips, - &reducedGenID, - &initial, - tightBounds); + SkRect queryBounds = kBounds; + queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2); + const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds); - REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reducedGenID); + REPORTER_ASSERT_MESSAGE(reporter, + reduced->elements().isEmpty() || + SkClipStack::kInvalidGenID != reduced->elementsGenID(), + testCase.c_str()); + + if (!reduced->elements().isEmpty()) { + REPORTER_ASSERT_MESSAGE(reporter, reduced->hasIBounds(), testCase.c_str()); + SkRect stackBounds; + SkClipStack::BoundsType stackBoundsType; + stack.getBounds(&stackBounds, &stackBoundsType); + if (SkClipStack::kNormal_BoundsType == stackBoundsType) { + // Unless GrReducedClip starts doing some heroic tightening of the clip bounds, this + // will be true since the stack bounds are completely contained inside the query. + REPORTER_ASSERT_MESSAGE(reporter, + GrClip::IsInsideClip(reduced->ibounds(), stackBounds), + testCase.c_str()); + } + REPORTER_ASSERT_MESSAGE(reporter, reduced->requiresAA() == doAA, testCase.c_str()); + } // Build a new clip stack based on the reduced clip elements SkClipStack reducedStack; - if (GrReducedClip::kAllOut_InitialState == initial) { + if (GrReducedClip::InitialState::kAllOut == reduced->initialState()) { // whether the result is bounded or not, the whole plane should start outside the clip. reducedStack.clipEmpty(); } - for (ElementList::Iter iter = reducedClips.headIter(); iter.get(); iter.next()) { + for (ElementList::Iter iter(reduced->elements()); iter.get(); iter.next()) { add_elem_to_stack(*iter.get(), &reducedStack); } + SkIRect ibounds = reduced->hasIBounds() ? reduced->ibounds() : kIBounds; + // GrReducedClipStack assumes that the final result is clipped to the returned bounds - if (tightBounds) { - reducedStack.clipDevRect(*tightBounds, SkRegion::kIntersect_Op); - } + reducedStack.clipDevRect(ibounds, kIntersect_SkClipOp); + stack.clipDevRect(ibounds, kIntersect_SkClipOp); // convert both the original stack and reduced stack to SkRegions and see if they're equal SkRegion region; + set_region_to_stack(stack, ibounds, ®ion); + SkRegion reducedRegion; + set_region_to_stack(reducedStack, ibounds, &reducedRegion); - region.setRect(inflatedIBounds); - const SkClipStack::Element* element; - SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart); - while ((element = iter.next())) { - add_elem_to_region(*element, inflatedIBounds, ®ion); - } - - reducedRegion.setRect(inflatedIBounds); - iter.reset(reducedStack, SkClipStack::Iter::kBottom_IterStart); - while ((element = iter.next())) { - add_elem_to_region(*element, inflatedIBounds, &reducedRegion); - } - SkString testCase; - testCase.printf("Iteration %d", i); REPORTER_ASSERT_MESSAGE(reporter, region == reducedRegion, testCase.c_str()); + + reduced->~GrReducedClip(); } } -#if defined(WIN32) +#ifdef SK_BUILD_FOR_WIN #define SUPPRESS_VISIBILITY_WARNING #else #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden"))) @@ -1020,25 +1081,22 @@ static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) { { SkClipStack stack; - stack.clipDevRect(SkRect::MakeXYWH(0, 0, 100, 100), SkRegion::kReplace_Op, true); - stack.clipDevRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkRegion::kReplace_Op, true); - SkIRect inflatedIBounds = SkIRect::MakeXYWH(0, 0, 100, 100); + stack.clipRect(SkRect::MakeXYWH(0, 0, 100, 100), SkMatrix::I(), kReplace_SkClipOp, + true); + stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkMatrix::I(), + kReplace_SkClipOp, true); + SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100); - GrReducedClip::ElementList reducedClips; - int32_t reducedGenID; - GrReducedClip::InitialState initial; - SkIRect tightBounds; + SkAlignedSTStorage<1, GrReducedClip> storage; + memset(storage.get(), 0, sizeof(GrReducedClip)); + GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID); + const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds); - GrReducedClip::ReduceClipStack(stack, - inflatedIBounds, - &reducedClips, - &reducedGenID, - &initial, - &tightBounds); - - REPORTER_ASSERT(reporter, reducedClips.count() == 1); + REPORTER_ASSERT(reporter, reduced->elements().count() == 1); // Clips will be cached based on the generation id. Make sure the gen id is valid. - REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reducedGenID); + REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced->elementsGenID()); + + reduced->~GrReducedClip(); } { SkClipStack stack; @@ -1047,21 +1105,24 @@ // A B // C D - stack.clipDevRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkRegion::kReplace_Op, true); - int32_t genIDA = stack.getTopmostGenID(); - stack.clipDevRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true); - int32_t genIDB = stack.getTopmostGenID(); - stack.clipDevRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true); - int32_t genIDC = stack.getTopmostGenID(); - stack.clipDevRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true); - int32_t genIDD = stack.getTopmostGenID(); + stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(), + kReplace_SkClipOp, true); + uint32_t genIDA = stack.getTopmostGenID(); + stack.clipRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(), + kUnion_SkClipOp, true); + uint32_t genIDB = stack.getTopmostGenID(); + stack.clipRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(), + kUnion_SkClipOp, true); + uint32_t genIDC = stack.getTopmostGenID(); + stack.clipRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(), + kUnion_SkClipOp, true); + uint32_t genIDD = stack.getTopmostGenID(); -#define XYWH SkIRect::MakeXYWH +#define IXYWH SkIRect::MakeXYWH +#define XYWH SkRect::MakeXYWH - SkIRect unused; - unused.setEmpty(); - SkIRect stackBounds = XYWH(0, 0, 76, 76); + SkIRect stackBounds = IXYWH(0, 0, 76, 76); // The base test is to test each rect in two ways: // 1) The box dimensions. (Should reduce to "all in", no elements). @@ -1072,99 +1133,259 @@ // Not passing in tighter bounds is tested for consistency. static const struct SUPPRESS_VISIBILITY_WARNING { - SkIRect testBounds; + SkRect testBounds; int reducedClipCount; - int32_t reducedGenID; - GrReducedClip::InitialState initialState; - SkIRect tighterBounds; // If this is empty, the query will not pass tighter bounds + uint32_t reducedGenID; + InitialState initialState; + SkIRect clipIRect; // parameter. } testCases[] = { + // Rect A. - { XYWH(0, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(0, 0, 25, 25) }, - { XYWH(0, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused }, - { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::kAllOut_InitialState, XYWH(0, 0, 27, 27)}, - { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::kAllOut_InitialState, unused }, + { XYWH(0, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 25, 25) }, + { XYWH(0.1f, 0.1f, 25.1f, 25.1f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 26, 26) }, + { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::InitialState::kAllOut, IXYWH(0, 0, 27, 27)}, // Rect B. - { XYWH(50, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(50, 0, 25, 25) }, - { XYWH(50, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused }, - { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::kAllOut_InitialState, XYWH(50, 0, 26, 27) }, - { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::kAllOut_InitialState, unused }, + { XYWH(50, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 25, 25) }, + { XYWH(50, 0, 25.3f, 25.3f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 26, 26) }, + { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::InitialState::kAllOut, IXYWH(50, 0, 26, 27) }, // Rect C. - { XYWH(0, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(0, 50, 25, 25) }, - { XYWH(0, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused }, - { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::kAllOut_InitialState, XYWH(0, 50, 27, 26) }, - { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::kAllOut_InitialState, unused }, + { XYWH(0, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 25, 25) }, + { XYWH(0.2f, 50.1f, 25.1f, 25.2f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 26, 26) }, + { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::InitialState::kAllOut, IXYWH(0, 50, 27, 26) }, // Rect D. - { XYWH(50, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused }, - { XYWH(50, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(50, 50, 25, 25)}, - { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::kAllOut_InitialState, unused }, - { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::kAllOut_InitialState, XYWH(50, 50, 26, 26)}, + { XYWH(50, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 25, 25)}, + { XYWH(50.3f, 50.3f, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 26, 26)}, + { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(50, 50, 26, 26)}, // Other tests: - { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::kAllOut_InitialState, unused }, - { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::kAllOut_InitialState, stackBounds }, + { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::InitialState::kAllOut, stackBounds }, // Rect in the middle, touches none. - { XYWH(26, 26, 24, 24), 0, SkClipStack::kEmptyGenID, GrReducedClip::kAllOut_InitialState, unused }, - { XYWH(26, 26, 24, 24), 0, SkClipStack::kEmptyGenID, GrReducedClip::kAllOut_InitialState, XYWH(26, 26, 24, 24) }, + { XYWH(26, 26, 24, 24), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllOut, IXYWH(26, 26, 24, 24) }, // Rect in the middle, touches all the rects. GenID is the last rect. - { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::kAllOut_InitialState, unused }, - { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::kAllOut_InitialState, XYWH(24, 24, 27, 27) }, + { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(24, 24, 27, 27) }, }; #undef XYWH +#undef IXYWH for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) { - GrReducedClip::ElementList reducedClips; - int32_t reducedGenID; - GrReducedClip::InitialState initial; - SkIRect tightBounds; - - GrReducedClip::ReduceClipStack(stack, - testCases[i].testBounds, - &reducedClips, - &reducedGenID, - &initial, - testCases[i].tighterBounds.isEmpty() ? NULL : &tightBounds); - - REPORTER_ASSERT(reporter, reducedClips.count() == testCases[i].reducedClipCount); - SkASSERT(reducedClips.count() == testCases[i].reducedClipCount); - REPORTER_ASSERT(reporter, reducedGenID == testCases[i].reducedGenID); - SkASSERT(reducedGenID == testCases[i].reducedGenID); - REPORTER_ASSERT(reporter, initial == testCases[i].initialState); - SkASSERT(initial == testCases[i].initialState); - if (!testCases[i].tighterBounds.isEmpty()) { - REPORTER_ASSERT(reporter, tightBounds == testCases[i].tighterBounds); - SkASSERT(tightBounds == testCases[i].tighterBounds); + const GrReducedClip reduced(stack, testCases[i].testBounds); + REPORTER_ASSERT(reporter, reduced.elements().count() == testCases[i].reducedClipCount); + SkASSERT(reduced.elements().count() == testCases[i].reducedClipCount); + if (reduced.elements().count()) { + REPORTER_ASSERT(reporter, reduced.elementsGenID() == testCases[i].reducedGenID); + SkASSERT(reduced.elementsGenID() == testCases[i].reducedGenID); } + REPORTER_ASSERT(reporter, reduced.initialState() == testCases[i].initialState); + SkASSERT(reduced.initialState() == testCases[i].initialState); + REPORTER_ASSERT(reporter, reduced.hasIBounds()); + SkASSERT(reduced.hasIBounds()); + REPORTER_ASSERT(reporter, reduced.ibounds() == testCases[i].clipIRect); + SkASSERT(reduced.ibounds() == testCases[i].clipIRect); } } } static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) { SkClipStack stack; - stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), SkRegion::kReplace_Op); - stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), SkRegion::kReplace_Op); - SkIRect inflatedIBounds = SkIRect::MakeXYWH(0, 0, 100, 100); - - GrReducedClip::ElementList reducedClips; - int32_t reducedGenID; - GrReducedClip::InitialState initial; - SkIRect tightBounds; + stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), kReplace_SkClipOp); + stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), kReplace_SkClipOp); + SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100); // At the time, this would crash. - GrReducedClip::ReduceClipStack(stack, - inflatedIBounds, - &reducedClips, - &reducedGenID, - &initial, - &tightBounds); + const GrReducedClip reduced(stack, bounds); + REPORTER_ASSERT(reporter, reduced.elements().isEmpty()); +} - REPORTER_ASSERT(reporter, 0 == reducedClips.count()); +enum class ClipMethod { + kSkipDraw, + kIgnoreClip, + kScissor, + kAAElements +}; + +static void test_aa_query(skiatest::Reporter* reporter, const SkString& testName, + const SkClipStack& stack, const SkMatrix& queryXform, + const SkRect& preXformQuery, ClipMethod expectedMethod, + int numExpectedElems = 0) { + SkRect queryBounds; + queryXform.mapRect(&queryBounds, preXformQuery); + const GrReducedClip reduced(stack, queryBounds); + + SkClipStack::BoundsType stackBoundsType; + SkRect stackBounds; + stack.getBounds(&stackBounds, &stackBoundsType); + + switch (expectedMethod) { + case ClipMethod::kSkipDraw: + SkASSERT(0 == numExpectedElems); + REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str()); + REPORTER_ASSERT_MESSAGE(reporter, + GrReducedClip::InitialState::kAllOut == reduced.initialState(), + testName.c_str()); + return; + case ClipMethod::kIgnoreClip: + SkASSERT(0 == numExpectedElems); + REPORTER_ASSERT_MESSAGE(reporter, + !reduced.hasIBounds() || + GrClip::IsInsideClip(reduced.ibounds(), queryBounds), + testName.c_str()); + REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str()); + REPORTER_ASSERT_MESSAGE(reporter, + GrReducedClip::InitialState::kAllIn == reduced.initialState(), + testName.c_str()); + return; + case ClipMethod::kScissor: { + SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType); + SkASSERT(0 == numExpectedElems); + SkIRect expectedScissor; + stackBounds.round(&expectedScissor); + REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str()); + REPORTER_ASSERT_MESSAGE(reporter, reduced.hasIBounds(), testName.c_str()); + REPORTER_ASSERT_MESSAGE(reporter, expectedScissor == reduced.ibounds(), + testName.c_str()); + REPORTER_ASSERT_MESSAGE(reporter, + GrReducedClip::InitialState::kAllIn == reduced.initialState(), + testName.c_str()); + return; + } + case ClipMethod::kAAElements: { + SkIRect expectedClipIBounds = GrClip::GetPixelIBounds(queryBounds); + if (SkClipStack::kNormal_BoundsType == stackBoundsType) { + SkAssertResult(expectedClipIBounds.intersect(GrClip::GetPixelIBounds(stackBounds))); + } + REPORTER_ASSERT_MESSAGE(reporter, numExpectedElems == reduced.elements().count(), + testName.c_str()); + REPORTER_ASSERT_MESSAGE(reporter, reduced.hasIBounds(), testName.c_str()); + REPORTER_ASSERT_MESSAGE(reporter, expectedClipIBounds == reduced.ibounds(), + testName.c_str()); + REPORTER_ASSERT_MESSAGE(reporter, reduced.requiresAA() == !reduced.elements().isEmpty(), + testName.c_str()); + break; + } + } +} + +static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) { + constexpr SkScalar IL = 2, IT = 1, IR = 6, IB = 7; // Pixel aligned rect. + constexpr SkScalar L = 2.2f, T = 1.7f, R = 5.8f, B = 7.3f; // Generic rect. + constexpr SkScalar l = 3.3f, t = 2.8f, r = 4.7f, b = 6.2f; // Small rect contained in R. + + SkRect alignedRect = {IL, IT, IR, IB}; + SkRect rect = {L, T, R, B}; + SkRect innerRect = {l, t, r, b}; + + SkMatrix m; + m.setIdentity(); + + constexpr SkScalar kMinScale = 2.0001f; + constexpr SkScalar kMaxScale = 3; + constexpr int kNumIters = 8; + + SkString name; + SkRandom rand; + + for (int i = 0; i < kNumIters; ++i) { + // Pixel-aligned rect (iior=true). + name.printf("Pixel-aligned rect test, iter %i", i); + SkClipStack stack; + stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true); + test_aa_query(reporter, name, stack, m, {IL, IT, IR, IB}, ClipMethod::kIgnoreClip); + test_aa_query(reporter, name, stack, m, {IL, IT-1, IR, IT}, ClipMethod::kSkipDraw); + test_aa_query(reporter, name, stack, m, {IL, IT-2, IR, IB}, ClipMethod::kScissor); + + // Rect (iior=true). + name.printf("Rect test, iter %i", i); + stack.reset(); + stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true); + test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kIgnoreClip); + test_aa_query(reporter, name, stack, m, {L-.1f, T, L, B}, ClipMethod::kSkipDraw); + test_aa_query(reporter, name, stack, m, {L-.1f, T, L+.1f, B}, ClipMethod::kAAElements, 1); + + // Difference rect (iior=false, inside-out bounds). + name.printf("Difference rect test, iter %i", i); + stack.reset(); + stack.clipRect(rect, SkMatrix::I(), kDifference_SkClipOp, true); + test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kSkipDraw); + test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T}, ClipMethod::kIgnoreClip); + test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T+.1f}, ClipMethod::kAAElements, 1); + + // Complex clip (iior=false, normal bounds). + name.printf("Complex clip test, iter %i", i); + stack.reset(); + stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true); + stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true); + test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw); + test_aa_query(reporter, name, stack, m, {r-.1f, t, R, b}, ClipMethod::kAAElements, 1); + test_aa_query(reporter, name, stack, m, {r-.1f, t, R+.1f, b}, ClipMethod::kAAElements, 2); + test_aa_query(reporter, name, stack, m, {r, t, R+.1f, b}, ClipMethod::kAAElements, 1); + test_aa_query(reporter, name, stack, m, {r, t, R, b}, ClipMethod::kIgnoreClip); + test_aa_query(reporter, name, stack, m, {R, T, R+.1f, B}, ClipMethod::kSkipDraw); + + // Complex clip where outer rect is pixel aligned (iior=false, normal bounds). + name.printf("Aligned Complex clip test, iter %i", i); + stack.reset(); + stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true); + stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true); + test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw); + test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB}, ClipMethod::kAAElements, 1); + test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB+.1f}, ClipMethod::kAAElements, 1); + test_aa_query(reporter, name, stack, m, {l, b, r, IB+.1f}, ClipMethod::kAAElements, 0); + test_aa_query(reporter, name, stack, m, {l, b, r, IB}, ClipMethod::kIgnoreClip); + test_aa_query(reporter, name, stack, m, {IL, IB, IR, IB+.1f}, ClipMethod::kSkipDraw); + + // Apply random transforms and try again. This ensures the clip stack reduction is hardened + // against FP rounding error. + SkScalar sx = rand.nextRangeScalar(kMinScale, kMaxScale); + sx = SkScalarFloorToScalar(sx * alignedRect.width()) / alignedRect.width(); + SkScalar sy = rand.nextRangeScalar(kMinScale, kMaxScale); + sy = SkScalarFloorToScalar(sy * alignedRect.height()) / alignedRect.height(); + SkScalar tx = SkScalarRoundToScalar(sx * alignedRect.x()) - sx * alignedRect.x(); + SkScalar ty = SkScalarRoundToScalar(sy * alignedRect.y()) - sy * alignedRect.y(); + + SkMatrix xform = SkMatrix::MakeScale(sx, sy); + xform.postTranslate(tx, ty); + xform.mapRect(&alignedRect); + xform.mapRect(&rect); + xform.mapRect(&innerRect); + m.postConcat(xform); + } +} + +static void test_tiny_query_bounds_assertion_bug(skiatest::Reporter* reporter) { + // https://bugs.chromium.org/p/skia/issues/detail?id=5990 + const SkRect clipBounds = SkRect::MakeXYWH(1.5f, 100, 1000, 1000); + + SkClipStack rectStack; + rectStack.clipRect(clipBounds, SkMatrix::I(), kIntersect_SkClipOp, true); + + SkPath clipPath; + clipPath.moveTo(clipBounds.left(), clipBounds.top()); + clipPath.quadTo(clipBounds.right(), clipBounds.top(), + clipBounds.right(), clipBounds.bottom()); + clipPath.quadTo(clipBounds.left(), clipBounds.bottom(), + clipBounds.left(), clipBounds.top()); + SkClipStack pathStack; + pathStack.clipPath(clipPath, SkMatrix::I(), kIntersect_SkClipOp, true); + + for (const SkClipStack& stack : {rectStack, pathStack}) { + for (SkRect queryBounds : {SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance, 1000), + SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance/2, 1000), + SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance), + SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance/2)}) { + const GrReducedClip reduced(stack, queryBounds); + REPORTER_ASSERT(reporter, !reduced.hasIBounds()); + REPORTER_ASSERT(reporter, reduced.elements().isEmpty()); + REPORTER_ASSERT(reporter, + GrReducedClip::InitialState::kAllOut == reduced.initialState()); + } + } } #endif @@ -1182,7 +1403,7 @@ { 0, 0, 75, 75 } }; for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) { - stack.clipDevRect(gRects[i], SkRegion::kIntersect_Op); + stack.clipDevRect(gRects[i], kIntersect_SkClipOp); } // all of the above rects should have been intersected, leaving only 1 rect @@ -1193,7 +1414,7 @@ REPORTER_ASSERT(reporter, element); REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType()); - REPORTER_ASSERT(reporter, SkRegion::kIntersect_Op == element->getOp()); + REPORTER_ASSERT(reporter, kIntersect_SkClipOp == element->getOp()); REPORTER_ASSERT(reporter, element->getRect() == answer); // now check that we only had one in our iterator REPORTER_ASSERT(reporter, !iter.next()); @@ -1213,9 +1434,91 @@ test_rect_inverse_fill(reporter); test_path_replace(reporter); test_quickContains(reporter); + test_invfill_diff_bug(reporter); #if SK_SUPPORT_GPU test_reduced_clip_stack(reporter); test_reduced_clip_stack_genid(reporter); test_reduced_clip_stack_no_aa_crash(reporter); + test_reduced_clip_stack_aa(reporter); + test_tiny_query_bounds_assertion_bug(reporter); #endif } + +////////////////////////////////////////////////////////////////////////////// + +#if SK_SUPPORT_GPU +sk_sp<GrTextureProxy> GrClipStackClip::testingOnly_createClipMask(GrContext* context) const { + const GrReducedClip reducedClip(*fStack, SkRect::MakeWH(512, 512), 0); + return this->createSoftwareClipMask(context, reducedClip); +} + +// Verify that clip masks are freed up when the clip state that generated them goes away. +DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache, reporter, ctxInfo) { + // This test uses resource key tags which only function in debug builds. +#ifdef SK_DEBUG + GrContext* context = ctxInfo.grContext(); + SkClipStack stack; + + SkPath path; + path.addCircle(10, 10, 8); + path.addCircle(15, 15, 8); + path.setFillType(SkPath::kEvenOdd_FillType); + + static const char* kTag = GrClipStackClip::kMaskTestTag; + GrResourceCache* cache = context->getResourceCache(); + + static constexpr int kN = 5; + + for (int i = 0; i < kN; ++i) { + SkMatrix m; + m.setTranslate(0.5, 0.5); + stack.save(); + stack.clipPath(path, m, SkClipOp::kIntersect, true); + sk_sp<GrTextureProxy> mask = GrClipStackClip(&stack).testingOnly_createClipMask(context); + mask->instantiate(context->resourceProvider()); + GrTexture* tex = mask->priv().peekTexture(); + REPORTER_ASSERT(reporter, 0 == strcmp(tex->getUniqueKey().tag(), kTag)); + // Make sure mask isn't pinned in cache. + mask.reset(nullptr); + context->flush(); + REPORTER_ASSERT(reporter, i + 1 == cache->countUniqueKeysWithTag(kTag)); + } + + for (int i = 0; i < kN; ++i) { + stack.restore(); + cache->purgeAsNeeded(); + REPORTER_ASSERT(reporter, kN - (i + 1) == cache->countUniqueKeysWithTag(kTag)); + } +#endif +} + +#include "SkSurface.h" +DEF_GPUTEST_FOR_ALL_CONTEXTS(canvas_private_clipRgn, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + + const int w = 10; + const int h = 10; + SkImageInfo info = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kPremul_SkAlphaType); + sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info); + SkCanvas* canvas = surf->getCanvas(); + SkRegion rgn; + + canvas->temporary_internal_getRgnClip(&rgn); + REPORTER_ASSERT(reporter, rgn.isRect()); + REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h)); + + canvas->save(); + canvas->clipRect(SkRect::MakeWH(5, 5), kDifference_SkClipOp); + canvas->temporary_internal_getRgnClip(&rgn); + REPORTER_ASSERT(reporter, rgn.isComplex()); + REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h)); + canvas->restore(); + + canvas->save(); + canvas->clipRRect(SkRRect::MakeOval(SkRect::MakeLTRB(3, 3, 7, 7))); + canvas->temporary_internal_getRgnClip(&rgn); + REPORTER_ASSERT(reporter, rgn.isComplex()); + REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeLTRB(3, 3, 7, 7)); + canvas->restore(); +} +#endif
diff --git a/src/third_party/skia/tests/ClipperTest.cpp b/src/third_party/skia/tests/ClipperTest.cpp index 00b6229..641ed01 100644 --- a/src/third_party/skia/tests/ClipperTest.cpp +++ b/src/third_party/skia/tests/ClipperTest.cpp
@@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include "SkBitmap.h" #include "SkCanvas.h" #include "SkEdgeClipper.h" #include "SkLineClipper.h" @@ -48,7 +49,7 @@ } static void test_edgeclipper() { - SkEdgeClipper clipper; + SkEdgeClipper clipper(false); const SkPoint pts[] = { { 3.0995476e+010f, 42.929779f },
diff --git a/src/third_party/skia/tests/CodecAnimTest.cpp b/src/third_party/skia/tests/CodecAnimTest.cpp new file mode 100644 index 0000000..84dc9fa --- /dev/null +++ b/src/third_party/skia/tests/CodecAnimTest.cpp
@@ -0,0 +1,389 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkBitmap.h" +#include "SkCodec.h" +#include "SkCommonFlags.h" +#include "SkImageEncoder.h" +#include "SkOSPath.h" +#include "SkStream.h" + +#include "Resources.h" +#include "Test.h" +#include "sk_tool_utils.h" + +#include <initializer_list> +#include <vector> + +static void write_bm(const char* name, const SkBitmap& bm) { + if (FLAGS_writePath.isEmpty()) { + return; + } + + SkString filename = SkOSPath::Join(FLAGS_writePath[0], name); + filename.appendf(".png"); + SkFILEWStream file(filename.c_str()); + if (!SkEncodeImage(&file, bm, SkEncodedImageFormat::kPNG, 100)) { + SkDebugf("failed to write '%s'\n", filename.c_str()); + } +} + +DEF_TEST(Codec_trunc, r) { + sk_sp<SkData> data(GetResourceAsData("box.gif")); + if (!data) { + return; + } + data = SkData::MakeSubset(data.get(), 0, 23); + std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data)); + codec->getFrameInfo(); +} + +// 565 does not support alpha, but there is no reason for it not to support an +// animated image with a frame that has alpha but then blends onto an opaque +// frame making the result opaque. Test that we can decode such a frame. +DEF_TEST(Codec_565, r) { + sk_sp<SkData> data(GetResourceAsData("blendBG.webp")); + if (!data) { + return; + } + std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(std::move(data))); + auto info = codec->getInfo().makeColorType(kRGB_565_SkColorType); + SkBitmap bm; + bm.allocPixels(info); + + SkCodec::Options options; + options.fFrameIndex = 1; + options.fPriorFrame = SkCodec::kNone; + + const auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(), + &options); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); +} + +static bool restore_previous(const SkCodec::FrameInfo& info) { + return info.fDisposalMethod == SkCodecAnimation::DisposalMethod::kRestorePrevious; +} + +DEF_TEST(Codec_frames, r) { + #define kOpaque kOpaque_SkAlphaType + #define kUnpremul kUnpremul_SkAlphaType + #define kKeep SkCodecAnimation::DisposalMethod::kKeep + #define kRestoreBG SkCodecAnimation::DisposalMethod::kRestoreBGColor + #define kRestorePrev SkCodecAnimation::DisposalMethod::kRestorePrevious + static const struct { + const char* fName; + int fFrameCount; + // One less than fFramecount, since the first frame is always + // independent. + std::vector<int> fRequiredFrames; + // Same, since the first frame should match getInfo. + std::vector<SkAlphaType> fAlphaTypes; + // The size of this one should match fFrameCount for animated, empty + // otherwise. + std::vector<int> fDurations; + int fRepetitionCount; + std::vector<SkCodecAnimation::DisposalMethod> fDisposalMethods; + } gRecs[] = { + { "required.gif", 7, + { 0, 1, 1, SkCodec::kNone, 4, 4 }, + { kOpaque, kUnpremul, kUnpremul, kOpaque, kOpaque, kOpaque }, + { 100, 100, 100, 100, 100, 100, 100 }, + 0, + { kKeep, kRestoreBG, kKeep, kKeep, kKeep, kRestoreBG, kKeep } }, + { "alphabetAnim.gif", 13, + { SkCodec::kNone, 0, 0, 0, 0, 5, 6, SkCodec::kNone, + SkCodec::kNone, SkCodec::kNone, 10, 11 }, + { kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, + kUnpremul, kUnpremul, kUnpremul, kOpaque, kOpaque, kUnpremul }, + { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 }, + 0, + { kKeep, kRestorePrev, kRestorePrev, kRestorePrev, kRestorePrev, + kRestoreBG, kKeep, kRestoreBG, kRestoreBG, kKeep, kKeep, + kRestoreBG, kKeep } }, + { "randPixelsAnim2.gif", 4, + // required frames + { 0, 0, 1 }, + // alphas + { kOpaque, kOpaque, kOpaque }, + // durations + { 0, 1000, 170, 40 }, + // repetition count + 0, + { kKeep, kKeep, kRestorePrev, kKeep } }, + { "randPixelsAnim.gif", 13, + // required frames + { SkCodec::kNone, 1, 2, 3, 4, 3, 6, 7, 7, 7, 9, 9 }, + { kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, + kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul }, + // durations + { 0, 1000, 170, 40, 220, 7770, 90, 90, 90, 90, 90, 90, 90 }, + // repetition count + 0, + { kKeep, kKeep, kKeep, kKeep, kRestoreBG, kRestoreBG, kRestoreBG, + kRestoreBG, kRestorePrev, kRestoreBG, kRestorePrev, kRestorePrev, + kRestorePrev, } }, + { "box.gif", 1, {}, {}, {}, 0, { kKeep } }, + { "color_wheel.gif", 1, {}, {}, {}, 0, { kKeep } }, + { "test640x479.gif", 4, { 0, 1, 2 }, + { kOpaque, kOpaque, kOpaque }, + { 200, 200, 200, 200 }, + SkCodec::kRepetitionCountInfinite, + { kKeep, kKeep, kKeep, kKeep } }, + { "colorTables.gif", 2, { 0 }, { kOpaque }, { 1000, 1000 }, 5, + { kKeep, kKeep } }, + + { "arrow.png", 1, {}, {}, {}, 0, {} }, + { "google_chrome.ico", 1, {}, {}, {}, 0, {} }, + { "brickwork-texture.jpg", 1, {}, {}, {}, 0, {} }, +#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32)) + { "dng_with_preview.dng", 1, {}, {}, {}, 0, {} }, +#endif + { "mandrill.wbmp", 1, {}, {}, {}, 0, {} }, + { "randPixels.bmp", 1, {}, {}, {}, 0, {} }, + { "yellow_rose.webp", 1, {}, {}, {}, 0, {} }, + { "webp-animated.webp", 3, { 0, 1 }, { kOpaque, kOpaque }, + { 1000, 500, 1000 }, SkCodec::kRepetitionCountInfinite, + { kKeep, kKeep, kKeep } }, + { "blendBG.webp", 7, { 0, SkCodec::kNone, SkCodec::kNone, SkCodec::kNone, + 4, 4 }, + { kOpaque, kOpaque, kUnpremul, kOpaque, kUnpremul, kUnpremul }, + { 525, 500, 525, 437, 609, 729, 444 }, 7, + { kKeep, kKeep, kKeep, kKeep, kKeep, kKeep, kKeep } }, + { "required.webp", 7, + { 0, 1, 1, SkCodec::kNone, 4, 4 }, + { kOpaque, kUnpremul, kUnpremul, kOpaque, kOpaque, kOpaque }, + { 100, 100, 100, 100, 100, 100, 100 }, + 1, + { kKeep, kRestoreBG, kKeep, kKeep, kKeep, kRestoreBG, kKeep } }, + }; + #undef kOpaque + #undef kUnpremul + #undef kKeep + #undef kRestorePrev + #undef kRestoreBG + + for (const auto& rec : gRecs) { + sk_sp<SkData> data(GetResourceAsData(rec.fName)); + if (!data) { + // Useful error statement, but sometimes people run tests without + // resources, and they do not want to see these messages. + //ERRORF(r, "Missing resources? Could not find '%s'", rec.fName); + continue; + } + + std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data)); + if (!codec) { + ERRORF(r, "Failed to create an SkCodec from '%s'", rec.fName); + continue; + } + + { + SkCodec::FrameInfo frameInfo; + REPORTER_ASSERT(r, !codec->getFrameInfo(0, &frameInfo)); + } + + const int repetitionCount = codec->getRepetitionCount(); + if (repetitionCount != rec.fRepetitionCount) { + ERRORF(r, "%s repetition count does not match! expected: %i\tactual: %i", + rec.fName, rec.fRepetitionCount, repetitionCount); + } + + const int expected = rec.fFrameCount; + if (rec.fRequiredFrames.size() + 1 != static_cast<size_t>(expected)) { + ERRORF(r, "'%s' has wrong number entries in fRequiredFrames; expected: %i\tactual: %i", + rec.fName, expected - 1, rec.fRequiredFrames.size()); + continue; + } + + if (expected > 1) { + if (rec.fDurations.size() != static_cast<size_t>(expected)) { + ERRORF(r, "'%s' has wrong number entries in fDurations; expected: %i\tactual: %i", + rec.fName, expected, rec.fDurations.size()); + continue; + } + + if (rec.fAlphaTypes.size() + 1 != static_cast<size_t>(expected)) { + ERRORF(r, "'%s' has wrong number entries in fAlphaTypes; expected: %i\tactual: %i", + rec.fName, expected - 1, rec.fAlphaTypes.size()); + continue; + } + + if (rec.fDisposalMethods.size() != static_cast<size_t>(expected)) { + ERRORF(r, "'%s' has wrong number entries in fDisposalMethods; " + "expected %i\tactual: %i", + rec.fName, expected, rec.fDisposalMethods.size()); + continue; + } + } + + enum class TestMode { + kVector, + kIndividual, + }; + + for (auto mode : { TestMode::kVector, TestMode::kIndividual }) { + // Re-create the codec to reset state and test parsing. + codec.reset(SkCodec::NewFromData(data)); + + int frameCount; + std::vector<SkCodec::FrameInfo> frameInfos; + switch (mode) { + case TestMode::kVector: + frameInfos = codec->getFrameInfo(); + // getFrameInfo returns empty set for non-animated. + frameCount = frameInfos.empty() ? 1 : frameInfos.size(); + break; + case TestMode::kIndividual: + frameCount = codec->getFrameCount(); + break; + } + + if (frameCount != expected) { + ERRORF(r, "'%s' expected frame count: %i\tactual: %i", + rec.fName, expected, frameCount); + continue; + } + + // From here on, we are only concerned with animated images. + if (1 == frameCount) { + continue; + } + + for (int i = 0; i < frameCount; i++) { + SkCodec::FrameInfo frameInfo; + switch (mode) { + case TestMode::kVector: + frameInfo = frameInfos[i]; + break; + case TestMode::kIndividual: + REPORTER_ASSERT(r, codec->getFrameInfo(i, nullptr)); + REPORTER_ASSERT(r, codec->getFrameInfo(i, &frameInfo)); + break; + } + + if (rec.fDurations[i] != frameInfo.fDuration) { + ERRORF(r, "%s frame %i's durations do not match! expected: %i\tactual: %i", + rec.fName, i, rec.fDurations[i], frameInfo.fDuration); + } + + auto to_string = [](SkAlphaType type) { + switch (type) { + case kUnpremul_SkAlphaType: + return "unpremul"; + case kOpaque_SkAlphaType: + return "opaque"; + default: + return "other"; + } + }; + + auto expectedAlpha = 0 == i ? codec->getInfo().alphaType() : rec.fAlphaTypes[i-1]; + auto alpha = frameInfo.fAlphaType; + if (expectedAlpha != alpha) { + ERRORF(r, "%s's frame %i has wrong alpha type! expected: %s\tactual: %s", + rec.fName, i, to_string(expectedAlpha), to_string(alpha)); + } + + if (0 == i) { + REPORTER_ASSERT(r, frameInfo.fRequiredFrame == SkCodec::kNone); + } else if (rec.fRequiredFrames[i-1] != frameInfo.fRequiredFrame) { + ERRORF(r, "%s's frame %i has wrong dependency! expected: %i\tactual: %i", + rec.fName, i, rec.fRequiredFrames[i-1], frameInfo.fRequiredFrame); + } + + REPORTER_ASSERT(r, frameInfo.fDisposalMethod == rec.fDisposalMethods[i]); + } + + if (TestMode::kIndividual == mode) { + // No need to test decoding twice. + continue; + } + + // Compare decoding in multiple ways: + // - Start from scratch for each frame. |codec| will have to decode the required frame + // (and any it depends on) to decode. This is stored in |cachedFrames|. + // - Provide the frame that a frame depends on, so |codec| just has to blend. + // - Provide a frame after the required frame, which will be covered up by the newest + // frame. + // All should look the same. + std::vector<SkBitmap> cachedFrames(frameCount); + const auto info = codec->getInfo().makeColorType(kN32_SkColorType); + + auto decode = [&](SkBitmap* bm, int index, int cachedIndex) { + auto decodeInfo = info; + if (index > 0) { + decodeInfo = info.makeAlphaType(frameInfos[index].fAlphaType); + } + bm->allocPixels(decodeInfo); + if (cachedIndex != SkCodec::kNone) { + // First copy the pixels from the cached frame + const bool success = sk_tool_utils::copy_to(bm, kN32_SkColorType, + cachedFrames[cachedIndex]); + REPORTER_ASSERT(r, success); + } + SkCodec::Options opts; + opts.fFrameIndex = index; + opts.fPriorFrame = cachedIndex; + const auto result = codec->getPixels(decodeInfo, bm->getPixels(), bm->rowBytes(), + &opts); + if (cachedIndex != SkCodec::kNone && restore_previous(frameInfos[cachedIndex])) { + if (result == SkCodec::kInvalidParameters) { + return true; + } + ERRORF(r, "Using a kRestorePrevious frame as fPriorFrame should fail"); + return false; + } + if (result != SkCodec::kSuccess) { + ERRORF(r, "Failed to decode frame %i from %s when providing prior frame %i, " + "error %i", index, rec.fName, cachedIndex, result); + } + return result == SkCodec::kSuccess; + }; + + for (int i = 0; i < frameCount; i++) { + SkBitmap& cachedFrame = cachedFrames[i]; + if (!decode(&cachedFrame, i, SkCodec::kNone)) { + continue; + } + const auto reqFrame = frameInfos[i].fRequiredFrame; + if (reqFrame == SkCodec::kNone) { + // Nothing to compare against. + continue; + } + for (int j = reqFrame; j < i; j++) { + SkBitmap frame; + if (restore_previous(frameInfos[j])) { + (void) decode(&frame, i, j); + continue; + } + if (!decode(&frame, i, j)) { + continue; + } + + // Now verify they're equal. + const size_t rowLen = info.bytesPerPixel() * info.width(); + for (int y = 0; y < info.height(); y++) { + const void* cachedAddr = cachedFrame.getAddr(0, y); + SkASSERT(cachedAddr != nullptr); + const void* addr = frame.getAddr(0, y); + SkASSERT(addr != nullptr); + const bool lineMatches = memcmp(cachedAddr, addr, rowLen) == 0; + if (!lineMatches) { + SkString name = SkStringPrintf("cached_%i", i); + write_bm(name.c_str(), cachedFrame); + name = SkStringPrintf("frame_%i", i); + write_bm(name.c_str(), frame); + ERRORF(r, "%s's frame %i is different (starting from line %i) when " + "providing prior frame %i!", rec.fName, i, y, j); + break; + } + } + } + } + } + } +}
diff --git a/src/third_party/skia/tests/CodecExactReadTest.cpp b/src/third_party/skia/tests/CodecExactReadTest.cpp new file mode 100644 index 0000000..9b6acc8 --- /dev/null +++ b/src/third_party/skia/tests/CodecExactReadTest.cpp
@@ -0,0 +1,87 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Resources.h" +#include "Test.h" + +#include "SkBitmap.h" +#include "SkCodec.h" +#include "SkData.h" +#include "SkStream.h" + +namespace { +// This class wraps another SkStream. It does not own the underlying stream, so +// that the underlying stream can be reused starting from where the first +// client left off. This mimics Android's JavaInputStreamAdaptor. +class UnowningStream : public SkStream { +public: + explicit UnowningStream(SkStream* stream) + : fStream(stream) + {} + + size_t read(void* buf, size_t bytes) override { + return fStream->read(buf, bytes); + } + + bool rewind() override { + return fStream->rewind(); + } + + bool isAtEnd() const override { + return fStream->isAtEnd(); + } +private: + SkStream* fStream; // Unowned. +}; +} // namespace + +// Test that some SkCodecs do not attempt to read input beyond the logical +// end of the data. Some other SkCodecs do, but some Android apps rely on not +// doing so for PNGs. Test on other formats that work. +DEF_TEST(Codec_end, r) { + for (const char* path : { "plane.png", + "yellow_rose.png", + "plane_interlaced.png", + "google_chrome.ico", + "color_wheel.ico", + "mandrill.wbmp", + "randPixels.bmp", + }) { + sk_sp<SkData> data = GetResourceAsData(path); + if (!data) { + continue; + } + + const int kNumImages = 2; + const size_t size = data->size(); + sk_sp<SkData> multiData = SkData::MakeUninitialized(size * kNumImages); + void* dst = multiData->writable_data(); + for (int i = 0; i < kNumImages; i++) { + memcpy(SkTAddOffset<void>(dst, size * i), data->data(), size); + } + data.reset(); + + SkMemoryStream stream(std::move(multiData)); + for (int i = 0; i < kNumImages; ++i) { + std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(new UnowningStream(&stream))); + if (!codec) { + ERRORF(r, "Failed to create a codec from %s, iteration %i", path, i); + continue; + } + + auto info = codec->getInfo().makeColorType(kN32_SkColorType); + SkBitmap bm; + bm.allocPixels(info); + + auto result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes()); + if (result != SkCodec::kSuccess) { + ERRORF(r, "Failed to getPixels from %s, iteration %i error %i", path, i, result); + continue; + } + } + } +}
diff --git a/src/third_party/skia/tests/CodecPartialTest.cpp b/src/third_party/skia/tests/CodecPartialTest.cpp new file mode 100644 index 0000000..4a56f46 --- /dev/null +++ b/src/third_party/skia/tests/CodecPartialTest.cpp
@@ -0,0 +1,456 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkBitmap.h" +#include "SkCodec.h" +#include "SkData.h" +#include "SkImageInfo.h" +#include "SkRWBuffer.h" +#include "SkString.h" + +#include "FakeStreams.h" +#include "Resources.h" +#include "Test.h" + +static SkImageInfo standardize_info(SkCodec* codec) { + SkImageInfo defaultInfo = codec->getInfo(); + // Note: This drops the SkColorSpace, allowing the equality check between two + // different codecs created from the same file to have the same SkImageInfo. + return SkImageInfo::MakeN32Premul(defaultInfo.width(), defaultInfo.height()); +} + +static bool create_truth(sk_sp<SkData> data, SkBitmap* dst) { + std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(std::move(data))); + if (!codec) { + return false; + } + + const SkImageInfo info = standardize_info(codec.get()); + dst->allocPixels(info); + return SkCodec::kSuccess == codec->getPixels(info, dst->getPixels(), dst->rowBytes()); +} + +static void compare_bitmaps(skiatest::Reporter* r, const SkBitmap& bm1, const SkBitmap& bm2) { + const SkImageInfo& info = bm1.info(); + if (info != bm2.info()) { + ERRORF(r, "Bitmaps have different image infos!"); + return; + } + const size_t rowBytes = info.minRowBytes(); + for (int i = 0; i < info.height(); i++) { + REPORTER_ASSERT(r, !memcmp(bm1.getAddr(0, 0), bm2.getAddr(0, 0), rowBytes)); + } +} + +static void test_partial(skiatest::Reporter* r, const char* name, size_t minBytes = 0) { + sk_sp<SkData> file = GetResourceAsData(name); + if (!file) { + SkDebugf("missing resource %s\n", name); + return; + } + + SkBitmap truth; + if (!create_truth(file, &truth)) { + ERRORF(r, "Failed to decode %s\n", name); + return; + } + + // Now decode part of the file + HaltingStream* stream = new HaltingStream(file, SkTMax(file->size() / 2, minBytes)); + + // Note that we cheat and hold on to a pointer to stream, though it is owned by + // partialCodec. + std::unique_ptr<SkCodec> partialCodec(SkCodec::NewFromStream(stream)); + if (!partialCodec) { + // Technically, this could be a small file where half the file is not + // enough. + ERRORF(r, "Failed to create codec for %s", name); + return; + } + + const SkImageInfo info = standardize_info(partialCodec.get()); + SkASSERT(info == truth.info()); + SkBitmap incremental; + incremental.allocPixels(info); + + while (true) { + const SkCodec::Result startResult = partialCodec->startIncrementalDecode(info, + incremental.getPixels(), incremental.rowBytes()); + if (startResult == SkCodec::kSuccess) { + break; + } + + if (stream->isAllDataReceived()) { + ERRORF(r, "Failed to start incremental decode\n"); + return; + } + + // Append some data. The size is arbitrary, but deliberately different from + // the buffer size used by SkPngCodec. + stream->addNewData(1000); + } + + while (true) { + const SkCodec::Result result = partialCodec->incrementalDecode(); + + if (result == SkCodec::kSuccess) { + break; + } + + REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput); + + if (stream->isAllDataReceived()) { + ERRORF(r, "Failed to completely decode %s", name); + return; + } + + // Append some data. The size is arbitrary, but deliberately different from + // the buffer size used by SkPngCodec. + stream->addNewData(1000); + } + + // compare to original + compare_bitmaps(r, truth, incremental); +} + +DEF_TEST(Codec_partial, r) { +#if 0 + // FIXME (scroggo): SkPngCodec needs to use SkStreamBuffer in order to + // support incremental decoding. + test_partial(r, "plane.png"); + test_partial(r, "plane_interlaced.png"); + test_partial(r, "yellow_rose.png"); + test_partial(r, "index8.png"); + test_partial(r, "color_wheel.png"); + test_partial(r, "mandrill_256.png"); + test_partial(r, "mandrill_32.png"); + test_partial(r, "arrow.png"); + test_partial(r, "randPixels.png"); + test_partial(r, "baby_tux.png"); +#endif + test_partial(r, "box.gif"); + test_partial(r, "randPixels.gif", 215); + test_partial(r, "color_wheel.gif"); +} + +// Verify that when decoding an animated gif byte by byte we report the correct +// fRequiredFrame as soon as getFrameInfo reports the frame. +DEF_TEST(Codec_requiredFrame, r) { + auto path = "colorTables.gif"; + sk_sp<SkData> file = GetResourceAsData(path); + if (!file) { + return; + } + + std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(file)); + if (!codec) { + ERRORF(r, "Failed to create codec from %s", path); + return; + } + + auto frameInfo = codec->getFrameInfo(); + if (frameInfo.size() <= 1) { + ERRORF(r, "Test is uninteresting with 0 or 1 frames"); + return; + } + + HaltingStream* stream(nullptr); + std::unique_ptr<SkCodec> partialCodec(nullptr); + for (size_t i = 0; !partialCodec; i++) { + if (file->size() == i) { + ERRORF(r, "Should have created a partial codec for %s", path); + return; + } + stream = new HaltingStream(file, i); + partialCodec.reset(SkCodec::NewFromStream(stream)); + } + + std::vector<SkCodec::FrameInfo> partialInfo; + size_t frameToCompare = 0; + for (; stream->getLength() <= file->size(); stream->addNewData(1)) { + partialInfo = partialCodec->getFrameInfo(); + for (; frameToCompare < partialInfo.size(); frameToCompare++) { + REPORTER_ASSERT(r, partialInfo[frameToCompare].fRequiredFrame + == frameInfo[frameToCompare].fRequiredFrame); + } + + if (frameToCompare == frameInfo.size()) { + break; + } + } +} + +DEF_TEST(Codec_partialAnim, r) { + auto path = "test640x479.gif"; + sk_sp<SkData> file = GetResourceAsData(path); + if (!file) { + return; + } + + // This stream will be owned by fullCodec, but we hang on to the pointer + // to determine frame offsets. + SkStream* stream = new SkMemoryStream(file); + std::unique_ptr<SkCodec> fullCodec(SkCodec::NewFromStream(stream)); + const auto info = standardize_info(fullCodec.get()); + + // frameByteCounts stores the number of bytes to decode a particular frame. + // - [0] is the number of bytes for the header + // - frames[i] requires frameByteCounts[i+1] bytes to decode + const std::vector<size_t> frameByteCounts = { 455, 69350, 1344, 1346, 1327 }; + std::vector<SkBitmap> frames; + for (size_t i = 0; true; i++) { + SkBitmap frame; + frame.allocPixels(info); + + SkCodec::Options opts; + opts.fFrameIndex = i; + const SkCodec::Result result = fullCodec->getPixels(info, frame.getPixels(), + frame.rowBytes(), &opts); + + if (result == SkCodec::kIncompleteInput || result == SkCodec::kInvalidInput) { + // We need to distinguish between a partial frame and no more frames. + // getFrameInfo lets us do this, since it tells the number of frames + // not considering whether they are complete. + // FIXME: Should we use a different Result? + if (fullCodec->getFrameInfo().size() > i) { + // This is a partial frame. + frames.push_back(frame); + } + break; + } + + if (result != SkCodec::kSuccess) { + ERRORF(r, "Failed to decode frame %i from %s", i, path); + return; + } + + frames.push_back(frame); + } + + // Now decode frames partially, then completely, and compare to the original. + HaltingStream* haltingStream = new HaltingStream(file, frameByteCounts[0]); + std::unique_ptr<SkCodec> partialCodec(SkCodec::NewFromStream(haltingStream)); + if (!partialCodec) { + ERRORF(r, "Failed to create a partial codec from %s with %i bytes out of %i", + path, frameByteCounts[0], file->size()); + return; + } + + SkASSERT(frameByteCounts.size() > frames.size()); + for (size_t i = 0; i < frames.size(); i++) { + const size_t fullFrameBytes = frameByteCounts[i + 1]; + const size_t firstHalf = fullFrameBytes / 2; + const size_t secondHalf = fullFrameBytes - firstHalf; + + haltingStream->addNewData(firstHalf); + auto frameInfo = partialCodec->getFrameInfo(); + REPORTER_ASSERT(r, frameInfo.size() == i + 1); + REPORTER_ASSERT(r, !frameInfo[i].fFullyReceived); + + SkBitmap frame; + frame.allocPixels(info); + + SkCodec::Options opts; + opts.fFrameIndex = i; + SkCodec::Result result = partialCodec->startIncrementalDecode(info, + frame.getPixels(), frame.rowBytes(), &opts); + if (result != SkCodec::kSuccess) { + ERRORF(r, "Failed to start incremental decode for %s on frame %i", + path, i); + return; + } + + result = partialCodec->incrementalDecode(); + REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result); + + haltingStream->addNewData(secondHalf); + result = partialCodec->incrementalDecode(); + REPORTER_ASSERT(r, SkCodec::kSuccess == result); + + frameInfo = partialCodec->getFrameInfo(); + REPORTER_ASSERT(r, frameInfo.size() == i + 1); + REPORTER_ASSERT(r, frameInfo[i].fFullyReceived); + compare_bitmaps(r, frames[i], frame); + } +} + +// Test that calling getPixels when an incremental decode has been +// started (but not finished) makes the next call to incrementalDecode +// require a call to startIncrementalDecode. +static void test_interleaved(skiatest::Reporter* r, const char* name) { + sk_sp<SkData> file = GetResourceAsData(name); + if (!file) { + return; + } + const size_t halfSize = file->size() / 2; + std::unique_ptr<SkCodec> partialCodec(SkCodec::NewFromStream( + new HaltingStream(std::move(file), halfSize))); + if (!partialCodec) { + ERRORF(r, "Failed to create codec for %s", name); + return; + } + + const SkImageInfo info = standardize_info(partialCodec.get()); + SkBitmap incremental; + incremental.allocPixels(info); + + const SkCodec::Result startResult = partialCodec->startIncrementalDecode(info, + incremental.getPixels(), incremental.rowBytes()); + if (startResult != SkCodec::kSuccess) { + ERRORF(r, "Failed to start incremental decode\n"); + return; + } + + SkCodec::Result result = partialCodec->incrementalDecode(); + REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput); + + SkBitmap full; + full.allocPixels(info); + result = partialCodec->getPixels(info, full.getPixels(), full.rowBytes()); + REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput); + + // Now incremental decode will fail + result = partialCodec->incrementalDecode(); + REPORTER_ASSERT(r, result == SkCodec::kInvalidParameters); +} + +DEF_TEST(Codec_rewind, r) { + test_interleaved(r, "plane.png"); + test_interleaved(r, "plane_interlaced.png"); + test_interleaved(r, "box.gif"); +} + +// Modified version of the giflib logo, from +// http://giflib.sourceforge.net/whatsinagif/bits_and_bytes.html +// The global color map has been replaced with a local color map. +static unsigned char gNoGlobalColorMap[] = { + // Header + 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, + + // Logical screen descriptor + 0x0A, 0x00, 0x0A, 0x00, 0x11, 0x00, 0x00, + + // Image descriptor + 0x2C, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x81, + + // Local color table + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + + // Image data + 0x02, 0x16, 0x8C, 0x2D, 0x99, 0x87, 0x2A, 0x1C, 0xDC, 0x33, 0xA0, 0x02, 0x75, + 0xEC, 0x95, 0xFA, 0xA8, 0xDE, 0x60, 0x8C, 0x04, 0x91, 0x4C, 0x01, 0x00, + + // Trailer + 0x3B, +}; + +// Test that a gif file truncated before its local color map behaves as expected. +DEF_TEST(Codec_GifPreMap, r) { + sk_sp<SkData> data = SkData::MakeWithoutCopy(gNoGlobalColorMap, sizeof(gNoGlobalColorMap)); + std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data)); + if (!codec) { + ERRORF(r, "failed to create codec"); + return; + } + + SkBitmap truth; + auto info = standardize_info(codec.get()); + truth.allocPixels(info); + + auto result = codec->getPixels(info, truth.getPixels(), truth.rowBytes()); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); + + // Truncate to 23 bytes, just before the color map. This should fail to decode. + codec.reset(SkCodec::NewFromData(SkData::MakeWithoutCopy(gNoGlobalColorMap, 23))); + REPORTER_ASSERT(r, codec); + if (codec) { + SkBitmap bm; + bm.allocPixels(info); + result = codec->getPixels(info, bm.getPixels(), bm.rowBytes()); + REPORTER_ASSERT(r, result == SkCodec::kInvalidInput); + } + + // Again, truncate to 23 bytes, this time for an incremental decode. We + // cannot start an incremental decode until we have more data. If we did, + // we would be using the wrong color table. + HaltingStream* stream = new HaltingStream(data, 23); + codec.reset(SkCodec::NewFromStream(stream)); + REPORTER_ASSERT(r, codec); + if (codec) { + SkBitmap bm; + bm.allocPixels(info); + result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes()); + REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput); + + stream->addNewData(data->size()); + result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes()); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); + + result = codec->incrementalDecode(); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); + compare_bitmaps(r, truth, bm); + } +} + +DEF_TEST(Codec_emptyIDAT, r) { + const char* name = "baby_tux.png"; + sk_sp<SkData> file = GetResourceAsData(name); + if (!file) { + return; + } + + // Truncate to the beginning of the IDAT, immediately after the IDAT tag. + file = SkData::MakeSubset(file.get(), 0, 80); + + std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(std::move(file))); + if (!codec) { + ERRORF(r, "Failed to create a codec for %s", name); + return; + } + + SkBitmap bm; + const auto info = standardize_info(codec.get()); + bm.allocPixels(info); + + const auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes()); + REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result); +} + +DEF_TEST(Codec_incomplete, r) { + for (const char* name : { "baby_tux.png", + "baby_tux.webp", + "CMYK.jpg", + "color_wheel.gif", + "google_chrome.ico", + "rle.bmp", + "mandrill.wbmp", + }) { + sk_sp<SkData> file = GetResourceAsData(name); + if (!name) { + continue; + } + + for (size_t len = 14; len <= file->size(); len += 5) { + SkCodec::Result result; + auto* stream = new SkMemoryStream(file->data(), len); + std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream, &result)); + if (codec) { + if (result != SkCodec::kSuccess) { + ERRORF(r, "Created an SkCodec for %s with %lu bytes, but " + "reported an error %i", name, len, result); + } + break; + } + + if (SkCodec::kIncompleteInput != result) { + ERRORF(r, "Reported error %i for %s with %lu bytes", + result, name, len); + break; + } + } + } +}
diff --git a/src/third_party/skia/tests/CodecPriv.h b/src/third_party/skia/tests/CodecPriv.h new file mode 100644 index 0000000..d80bb69 --- /dev/null +++ b/src/third_party/skia/tests/CodecPriv.h
@@ -0,0 +1,25 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef CodecPriv_DEFINED +#define CodecPriv_DEFINED + +#include "SkBitmap.h" +#include "SkCodec.h" +#include "SkData.h" + +inline bool decode_memory(const void* mem, size_t size, SkBitmap* bm) { + std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(SkData::MakeWithoutCopy(mem, size))); + if (!codec) { + return false; + } + + bm->allocPixels(codec->getInfo()); + const SkCodec::Result result = codec->getPixels(codec->getInfo(), bm->getPixels(), + bm->rowBytes()); + return result == SkCodec::kSuccess || result == SkCodec::kIncompleteInput; +} +#endif // CodecPriv_DEFINED
diff --git a/src/third_party/skia/tests/CodecTest.cpp b/src/third_party/skia/tests/CodecTest.cpp new file mode 100644 index 0000000..ac4afd1 --- /dev/null +++ b/src/third_party/skia/tests/CodecTest.cpp
@@ -0,0 +1,1526 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "FakeStreams.h" +#include "Resources.h" +#include "SkAndroidCodec.h" +#include "SkAutoMalloc.h" +#include "SkBitmap.h" +#include "SkCodec.h" +#include "SkCodecImageGenerator.h" +#include "SkColorSpace_XYZ.h" +#include "SkColorSpacePriv.h" +#include "SkData.h" +#include "SkFrontBufferedStream.h" +#include "SkImageEncoder.h" +#include "SkImageEncoderPriv.h" +#include "SkMD5.h" +#include "SkOSPath.h" +#include "SkJpegEncoder.h" +#include "SkPngChunkReader.h" +#include "SkPngEncoder.h" +#include "SkRandom.h" +#include "SkStream.h" +#include "SkStreamPriv.h" +#include "SkWebpEncoder.h" +#include "Test.h" + +#include "png.h" + +#include "sk_tool_utils.h" + +#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR < 5 + // FIXME (scroggo): Google3 needs to be updated to use a newer version of libpng. In + // the meantime, we had to break some pieces of SkPngCodec in order to support Google3. + // The parts that are broken are likely not used by Google3. + #define SK_PNG_DISABLE_TESTS +#endif + +static void md5(const SkBitmap& bm, SkMD5::Digest* digest) { + SkASSERT(bm.getPixels()); + SkMD5 md5; + size_t rowLen = bm.info().bytesPerPixel() * bm.width(); + for (int y = 0; y < bm.height(); ++y) { + md5.write(bm.getAddr(0, y), rowLen); + } + md5.finish(*digest); +} + +/** + * Compute the digest for bm and compare it to a known good digest. + * @param r Reporter to assert that bm's digest matches goodDigest. + * @param goodDigest The known good digest to compare to. + * @param bm The bitmap to test. + */ +static void compare_to_good_digest(skiatest::Reporter* r, const SkMD5::Digest& goodDigest, + const SkBitmap& bm) { + SkMD5::Digest digest; + md5(bm, &digest); + REPORTER_ASSERT(r, digest == goodDigest); +} + +/** + * Test decoding an SkCodec to a particular SkImageInfo. + * + * Calling getPixels(info) should return expectedResult, and if goodDigest is non nullptr, + * the resulting decode should match. + */ +template<typename Codec> +static void test_info(skiatest::Reporter* r, Codec* codec, const SkImageInfo& info, + SkCodec::Result expectedResult, const SkMD5::Digest* goodDigest) { + SkBitmap bm; + bm.allocPixels(info); + + SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes()); + REPORTER_ASSERT(r, result == expectedResult); + + if (goodDigest) { + compare_to_good_digest(r, *goodDigest, bm); + } +} + +SkIRect generate_random_subset(SkRandom* rand, int w, int h) { + SkIRect rect; + do { + rect.fLeft = rand->nextRangeU(0, w); + rect.fTop = rand->nextRangeU(0, h); + rect.fRight = rand->nextRangeU(0, w); + rect.fBottom = rand->nextRangeU(0, h); + rect.sort(); + } while (rect.isEmpty()); + return rect; +} + +static void test_incremental_decode(skiatest::Reporter* r, SkCodec* codec, const SkImageInfo& info, + const SkMD5::Digest& goodDigest) { + SkBitmap bm; + bm.allocPixels(info); + + REPORTER_ASSERT(r, SkCodec::kSuccess == codec->startIncrementalDecode(info, bm.getPixels(), + bm.rowBytes())); + + REPORTER_ASSERT(r, SkCodec::kSuccess == codec->incrementalDecode()); + + compare_to_good_digest(r, goodDigest, bm); +} + +// Test in stripes, similar to DM's kStripe_Mode +static void test_in_stripes(skiatest::Reporter* r, SkCodec* codec, const SkImageInfo& info, + const SkMD5::Digest& goodDigest) { + SkBitmap bm; + bm.allocPixels(info); + bm.eraseColor(SK_ColorYELLOW); + + const int height = info.height(); + // Note that if numStripes does not evenly divide height there will be an extra + // stripe. + const int numStripes = 4; + + if (numStripes > height) { + // Image is too small. + return; + } + + const int stripeHeight = height / numStripes; + + // Iterate through the image twice. Once to decode odd stripes, and once for even. + for (int oddEven = 1; oddEven >= 0; oddEven--) { + for (int y = oddEven * stripeHeight; y < height; y += 2 * stripeHeight) { + SkIRect subset = SkIRect::MakeLTRB(0, y, info.width(), + SkTMin(y + stripeHeight, height)); + SkCodec::Options options; + options.fSubset = ⊂ + if (SkCodec::kSuccess != codec->startIncrementalDecode(info, bm.getAddr(0, y), + bm.rowBytes(), &options)) { + ERRORF(r, "failed to start incremental decode!\ttop: %i\tbottom%i\n", + subset.top(), subset.bottom()); + return; + } + if (SkCodec::kSuccess != codec->incrementalDecode()) { + ERRORF(r, "failed incremental decode starting from line %i\n", y); + return; + } + } + } + + compare_to_good_digest(r, goodDigest, bm); +} + +template<typename Codec> +static void test_codec(skiatest::Reporter* r, Codec* codec, SkBitmap& bm, const SkImageInfo& info, + const SkISize& size, SkCodec::Result expectedResult, SkMD5::Digest* digest, + const SkMD5::Digest* goodDigest) { + + REPORTER_ASSERT(r, info.dimensions() == size); + bm.allocPixels(info); + + SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes()); + REPORTER_ASSERT(r, result == expectedResult); + + md5(bm, digest); + if (goodDigest) { + REPORTER_ASSERT(r, *digest == *goodDigest); + } + + { + // Test decoding to 565 + SkImageInfo info565 = info.makeColorType(kRGB_565_SkColorType); + if (info.alphaType() == kOpaque_SkAlphaType) { + // Decoding to 565 should succeed. + SkBitmap bm565; + bm565.allocPixels(info565); + + // This will allow comparison even if the image is incomplete. + bm565.eraseColor(SK_ColorBLACK); + + REPORTER_ASSERT(r, expectedResult == codec->getPixels(info565, + bm565.getPixels(), bm565.rowBytes())); + + SkMD5::Digest digest565; + md5(bm565, &digest565); + + // A dumb client's request for non-opaque should also succeed. + for (auto alpha : { kPremul_SkAlphaType, kUnpremul_SkAlphaType }) { + info565 = info565.makeAlphaType(alpha); + test_info(r, codec, info565, expectedResult, &digest565); + } + } else { + test_info(r, codec, info565, SkCodec::kInvalidConversion, nullptr); + } + } + + if (codec->getInfo().colorType() == kGray_8_SkColorType) { + SkImageInfo grayInfo = codec->getInfo(); + SkBitmap grayBm; + grayBm.allocPixels(grayInfo); + + grayBm.eraseColor(SK_ColorBLACK); + + REPORTER_ASSERT(r, expectedResult == codec->getPixels(grayInfo, + grayBm.getPixels(), grayBm.rowBytes())); + + SkMD5::Digest grayDigest; + md5(grayBm, &grayDigest); + + for (auto alpha : { kPremul_SkAlphaType, kUnpremul_SkAlphaType }) { + grayInfo = grayInfo.makeAlphaType(alpha); + test_info(r, codec, grayInfo, expectedResult, &grayDigest); + } + } + + // Verify that re-decoding gives the same result. It is interesting to check this after + // a decode to 565, since choosing to decode to 565 may result in some of the decode + // options being modified. These options should return to their defaults on another + // decode to kN32, so the new digest should match the old digest. + test_info(r, codec, info, expectedResult, digest); + + { + // Check alpha type conversions + if (info.alphaType() == kOpaque_SkAlphaType) { + test_info(r, codec, info.makeAlphaType(kUnpremul_SkAlphaType), + expectedResult, digest); + test_info(r, codec, info.makeAlphaType(kPremul_SkAlphaType), + expectedResult, digest); + } else { + // Decoding to opaque should fail + test_info(r, codec, info.makeAlphaType(kOpaque_SkAlphaType), + SkCodec::kInvalidConversion, nullptr); + SkAlphaType otherAt = info.alphaType(); + if (kPremul_SkAlphaType == otherAt) { + otherAt = kUnpremul_SkAlphaType; + } else { + otherAt = kPremul_SkAlphaType; + } + // The other non-opaque alpha type should always succeed, but not match. + test_info(r, codec, info.makeAlphaType(otherAt), expectedResult, nullptr); + } + } +} + +static bool supports_partial_scanlines(const char path[]) { + static const char* const exts[] = { + "jpg", "jpeg", "png", "webp" + "JPG", "JPEG", "PNG", "WEBP" + }; + + for (uint32_t i = 0; i < SK_ARRAY_COUNT(exts); i++) { + if (SkStrEndsWith(path, exts[i])) { + return true; + } + } + return false; +} + +// FIXME: Break up this giant function +static void check(skiatest::Reporter* r, + const char path[], + SkISize size, + bool supportsScanlineDecoding, + bool supportsSubsetDecoding, + bool supportsIncomplete, + bool supportsNewScanlineDecoding = false) { + + std::unique_ptr<SkStream> stream(GetResourceAsStream(path)); + if (!stream) { + return; + } + + std::unique_ptr<SkCodec> codec(nullptr); + bool isIncomplete = supportsIncomplete; + if (isIncomplete) { + size_t size = stream->getLength(); + sk_sp<SkData> data((SkData::MakeFromStream(stream.get(), 2 * size / 3))); + codec.reset(SkCodec::NewFromData(data)); + } else { + codec.reset(SkCodec::NewFromStream(stream.release())); + } + if (!codec) { + ERRORF(r, "Unable to decode '%s'", path); + return; + } + + // Test full image decodes with SkCodec + SkMD5::Digest codecDigest; + const SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType); + SkBitmap bm; + SkCodec::Result expectedResult = isIncomplete ? SkCodec::kIncompleteInput : SkCodec::kSuccess; + test_codec(r, codec.get(), bm, info, size, expectedResult, &codecDigest, nullptr); + + // Scanline decoding follows. + + if (supportsNewScanlineDecoding && !isIncomplete) { + test_incremental_decode(r, codec.get(), info, codecDigest); + // This is only supported by codecs that use incremental decoding to + // support subset decodes - png and jpeg (once SkJpegCodec is + // converted). + if (SkStrEndsWith(path, "png") || SkStrEndsWith(path, "PNG")) { + test_in_stripes(r, codec.get(), info, codecDigest); + } + } + + // Need to call startScanlineDecode() first. + REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) == 0); + REPORTER_ASSERT(r, !codec->skipScanlines(1)); + const SkCodec::Result startResult = codec->startScanlineDecode(info); + if (supportsScanlineDecoding) { + bm.eraseColor(SK_ColorYELLOW); + + REPORTER_ASSERT(r, startResult == SkCodec::kSuccess); + + for (int y = 0; y < info.height(); y++) { + const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0); + if (!isIncomplete) { + REPORTER_ASSERT(r, 1 == lines); + } + } + // verify that scanline decoding gives the same result. + if (SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder()) { + compare_to_good_digest(r, codecDigest, bm); + } + + // Cannot continue to decode scanlines beyond the end + REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) + == 0); + + // Interrupting a scanline decode with a full decode starts from + // scratch + REPORTER_ASSERT(r, codec->startScanlineDecode(info) == SkCodec::kSuccess); + const int lines = codec->getScanlines(bm.getAddr(0, 0), 1, 0); + if (!isIncomplete) { + REPORTER_ASSERT(r, lines == 1); + } + REPORTER_ASSERT(r, codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes()) + == expectedResult); + REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) + == 0); + REPORTER_ASSERT(r, codec->skipScanlines(1) + == 0); + + // Test partial scanline decodes + if (supports_partial_scanlines(path) && info.width() >= 3) { + SkCodec::Options options; + int width = info.width(); + int height = info.height(); + SkIRect subset = SkIRect::MakeXYWH(2 * (width / 3), 0, width / 3, height); + options.fSubset = ⊂ + + const auto partialStartResult = codec->startScanlineDecode(info, &options); + REPORTER_ASSERT(r, partialStartResult == SkCodec::kSuccess); + + for (int y = 0; y < height; y++) { + const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0); + if (!isIncomplete) { + REPORTER_ASSERT(r, 1 == lines); + } + } + } + } else { + REPORTER_ASSERT(r, startResult == SkCodec::kUnimplemented); + } + + // The rest of this function tests decoding subsets, and will decode an arbitrary number of + // random subsets. + // Do not attempt to decode subsets of an image of only once pixel, since there is no + // meaningful subset. + if (size.width() * size.height() == 1) { + return; + } + + SkRandom rand; + SkIRect subset; + SkCodec::Options opts; + opts.fSubset = ⊂ + for (int i = 0; i < 5; i++) { + subset = generate_random_subset(&rand, size.width(), size.height()); + SkASSERT(!subset.isEmpty()); + const bool supported = codec->getValidSubset(&subset); + REPORTER_ASSERT(r, supported == supportsSubsetDecoding); + + SkImageInfo subsetInfo = info.makeWH(subset.width(), subset.height()); + SkBitmap bm; + bm.allocPixels(subsetInfo); + const auto result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes(), &opts); + + if (supportsSubsetDecoding) { + if (expectedResult == SkCodec::kSuccess) { + REPORTER_ASSERT(r, result == expectedResult); + } else { + SkASSERT(expectedResult == SkCodec::kIncompleteInput); + REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput + || result == SkCodec::kSuccess); + } + // Webp is the only codec that supports subsets, and it will have modified the subset + // to have even left/top. + REPORTER_ASSERT(r, SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop)); + } else { + // No subsets will work. + REPORTER_ASSERT(r, result == SkCodec::kUnimplemented); + } + } + + // SkAndroidCodec tests + if (supportsScanlineDecoding || supportsSubsetDecoding || supportsNewScanlineDecoding) { + + std::unique_ptr<SkStream> stream(GetResourceAsStream(path)); + if (!stream) { + return; + } + + std::unique_ptr<SkAndroidCodec> androidCodec(nullptr); + if (isIncomplete) { + size_t size = stream->getLength(); + sk_sp<SkData> data((SkData::MakeFromStream(stream.get(), 2 * size / 3))); + androidCodec.reset(SkAndroidCodec::NewFromData(data)); + } else { + androidCodec.reset(SkAndroidCodec::NewFromStream(stream.release())); + } + if (!androidCodec) { + ERRORF(r, "Unable to decode '%s'", path); + return; + } + + SkBitmap bm; + SkMD5::Digest androidCodecDigest; + test_codec(r, androidCodec.get(), bm, info, size, expectedResult, &androidCodecDigest, + &codecDigest); + } + + if (!isIncomplete) { + // Test SkCodecImageGenerator + std::unique_ptr<SkStream> stream(GetResourceAsStream(path)); + sk_sp<SkData> fullData(SkData::MakeFromStream(stream.get(), stream->getLength())); + std::unique_ptr<SkImageGenerator> gen( + SkCodecImageGenerator::MakeFromEncodedCodec(fullData)); + SkBitmap bm; + bm.allocPixels(info); + REPORTER_ASSERT(r, gen->getPixels(info, bm.getPixels(), bm.rowBytes())); + compare_to_good_digest(r, codecDigest, bm); + +#ifndef SK_PNG_DISABLE_TESTS + // Test using SkFrontBufferedStream, as Android does + SkStream* bufferedStream = SkFrontBufferedStream::Create( + new SkMemoryStream(std::move(fullData)), SkCodec::MinBufferedBytesNeeded()); + REPORTER_ASSERT(r, bufferedStream); + codec.reset(SkCodec::NewFromStream(bufferedStream)); + REPORTER_ASSERT(r, codec); + if (codec) { + test_info(r, codec.get(), info, SkCodec::kSuccess, &codecDigest); + } +#endif + } + + // If we've just tested incomplete decodes, let's run the same test again on full decodes. + if (isIncomplete) { + check(r, path, size, supportsScanlineDecoding, supportsSubsetDecoding, false, + supportsNewScanlineDecoding); + } +} + +DEF_TEST(Codec_wbmp, r) { + check(r, "mandrill.wbmp", SkISize::Make(512, 512), true, false, true); +} + +DEF_TEST(Codec_webp, r) { + check(r, "baby_tux.webp", SkISize::Make(386, 395), false, true, true); + check(r, "color_wheel.webp", SkISize::Make(128, 128), false, true, true); + check(r, "yellow_rose.webp", SkISize::Make(400, 301), false, true, true); +} + +DEF_TEST(Codec_bmp, r) { + check(r, "randPixels.bmp", SkISize::Make(8, 8), true, false, true); + check(r, "rle.bmp", SkISize::Make(320, 240), true, false, true); +} + +DEF_TEST(Codec_ico, r) { + // FIXME: We are not ready to test incomplete ICOs + // These two tests examine interestingly different behavior: + // Decodes an embedded BMP image + check(r, "color_wheel.ico", SkISize::Make(128, 128), true, false, false); + // Decodes an embedded PNG image + check(r, "google_chrome.ico", SkISize::Make(256, 256), false, false, false, true); +} + +DEF_TEST(Codec_gif, r) { + check(r, "box.gif", SkISize::Make(200, 55), false, false, true, true); + check(r, "color_wheel.gif", SkISize::Make(128, 128), false, false, true, true); + // randPixels.gif is too small to test incomplete + check(r, "randPixels.gif", SkISize::Make(8, 8), false, false, false, true); +} + +DEF_TEST(Codec_jpg, r) { + check(r, "CMYK.jpg", SkISize::Make(642, 516), true, false, true); + check(r, "color_wheel.jpg", SkISize::Make(128, 128), true, false, true); + // grayscale.jpg is too small to test incomplete + check(r, "grayscale.jpg", SkISize::Make(128, 128), true, false, false); + check(r, "mandrill_512_q075.jpg", SkISize::Make(512, 512), true, false, true); + // randPixels.jpg is too small to test incomplete + check(r, "randPixels.jpg", SkISize::Make(8, 8), true, false, false); +} + +DEF_TEST(Codec_png, r) { + check(r, "arrow.png", SkISize::Make(187, 312), false, false, true, true); + check(r, "baby_tux.png", SkISize::Make(240, 246), false, false, true, true); + check(r, "color_wheel.png", SkISize::Make(128, 128), false, false, true, true); + // half-transparent-white-pixel.png is too small to test incomplete + check(r, "half-transparent-white-pixel.png", SkISize::Make(1, 1), false, false, false, true); + check(r, "mandrill_128.png", SkISize::Make(128, 128), false, false, true, true); + check(r, "mandrill_16.png", SkISize::Make(16, 16), false, false, true, true); + check(r, "mandrill_256.png", SkISize::Make(256, 256), false, false, true, true); + check(r, "mandrill_32.png", SkISize::Make(32, 32), false, false, true, true); + check(r, "mandrill_512.png", SkISize::Make(512, 512), false, false, true, true); + check(r, "mandrill_64.png", SkISize::Make(64, 64), false, false, true, true); + check(r, "plane.png", SkISize::Make(250, 126), false, false, true, true); + check(r, "plane_interlaced.png", SkISize::Make(250, 126), false, false, true, true); + check(r, "randPixels.png", SkISize::Make(8, 8), false, false, true, true); + check(r, "yellow_rose.png", SkISize::Make(400, 301), false, false, true, true); +} + +// Disable RAW tests for Win32. +#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32)) +DEF_TEST(Codec_raw, r) { + check(r, "sample_1mp.dng", SkISize::Make(600, 338), false, false, false); + check(r, "sample_1mp_rotated.dng", SkISize::Make(600, 338), false, false, false); + check(r, "dng_with_preview.dng", SkISize::Make(600, 338), true, false, false); +} +#endif + +static void test_invalid_stream(skiatest::Reporter* r, const void* stream, size_t len) { + // Neither of these calls should return a codec. Bots should catch us if we leaked anything. + SkCodec* codec = SkCodec::NewFromStream(new SkMemoryStream(stream, len, false)); + REPORTER_ASSERT(r, !codec); + + SkAndroidCodec* androidCodec = + SkAndroidCodec::NewFromStream(new SkMemoryStream(stream, len, false)); + REPORTER_ASSERT(r, !androidCodec); +} + +// Ensure that SkCodec::NewFromStream handles freeing the passed in SkStream, +// even on failure. Test some bad streams. +DEF_TEST(Codec_leaks, r) { + // No codec should claim this as their format, so this tests SkCodec::NewFromStream. + const char nonSupportedStream[] = "hello world"; + // The other strings should look like the beginning of a file type, so we'll call some + // internal version of NewFromStream, which must also delete the stream on failure. + const unsigned char emptyPng[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a }; + const unsigned char emptyJpeg[] = { 0xFF, 0xD8, 0xFF }; + const char emptyWebp[] = "RIFF1234WEBPVP"; + const char emptyBmp[] = { 'B', 'M' }; + const char emptyIco[] = { '\x00', '\x00', '\x01', '\x00' }; + const char emptyGif[] = "GIFVER"; + + test_invalid_stream(r, nonSupportedStream, sizeof(nonSupportedStream)); + test_invalid_stream(r, emptyPng, sizeof(emptyPng)); + test_invalid_stream(r, emptyJpeg, sizeof(emptyJpeg)); + test_invalid_stream(r, emptyWebp, sizeof(emptyWebp)); + test_invalid_stream(r, emptyBmp, sizeof(emptyBmp)); + test_invalid_stream(r, emptyIco, sizeof(emptyIco)); + test_invalid_stream(r, emptyGif, sizeof(emptyGif)); +} + +DEF_TEST(Codec_null, r) { + // Attempting to create an SkCodec or an SkAndroidCodec with null should not + // crash. + SkCodec* codec = SkCodec::NewFromStream(nullptr); + REPORTER_ASSERT(r, !codec); + + SkAndroidCodec* androidCodec = SkAndroidCodec::NewFromStream(nullptr); + REPORTER_ASSERT(r, !androidCodec); +} + +static void test_dimensions(skiatest::Reporter* r, const char path[]) { + // Create the codec from the resource file + std::unique_ptr<SkStream> stream(GetResourceAsStream(path)); + if (!stream) { + return; + } + std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(stream.release())); + if (!codec) { + ERRORF(r, "Unable to create codec '%s'", path); + return; + } + + // Check that the decode is successful for a variety of scales + for (int sampleSize = 1; sampleSize < 32; sampleSize++) { + // Scale the output dimensions + SkISize scaledDims = codec->getSampledDimensions(sampleSize); + SkImageInfo scaledInfo = codec->getInfo() + .makeWH(scaledDims.width(), scaledDims.height()) + .makeColorType(kN32_SkColorType); + + // Set up for the decode + size_t rowBytes = scaledDims.width() * sizeof(SkPMColor); + size_t totalBytes = scaledInfo.getSafeSize(rowBytes); + SkAutoTMalloc<SkPMColor> pixels(totalBytes); + + SkAndroidCodec::AndroidOptions options; + options.fSampleSize = sampleSize; + SkCodec::Result result = + codec->getAndroidPixels(scaledInfo, pixels.get(), rowBytes, &options); + REPORTER_ASSERT(r, SkCodec::kSuccess == result); + } +} + +// Ensure that onGetScaledDimensions returns valid image dimensions to use for decodes +DEF_TEST(Codec_Dimensions, r) { + // JPG + test_dimensions(r, "CMYK.jpg"); + test_dimensions(r, "color_wheel.jpg"); + test_dimensions(r, "grayscale.jpg"); + test_dimensions(r, "mandrill_512_q075.jpg"); + test_dimensions(r, "randPixels.jpg"); + + // Decoding small images with very large scaling factors is a potential + // source of bugs and crashes. We disable these tests in Gold because + // tiny images are not very useful to look at. + // Here we make sure that we do not crash or access illegal memory when + // performing scaled decodes on small images. + test_dimensions(r, "1x1.png"); + test_dimensions(r, "2x2.png"); + test_dimensions(r, "3x3.png"); + test_dimensions(r, "3x1.png"); + test_dimensions(r, "1x1.png"); + test_dimensions(r, "16x1.png"); + test_dimensions(r, "1x16.png"); + test_dimensions(r, "mandrill_16.png"); + + // RAW +// Disable RAW tests for Win32. +#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32)) + test_dimensions(r, "sample_1mp.dng"); + test_dimensions(r, "sample_1mp_rotated.dng"); + test_dimensions(r, "dng_with_preview.dng"); +#endif +} + +static void test_invalid(skiatest::Reporter* r, const char path[]) { + std::unique_ptr<SkStream> stream(GetResourceAsStream(path)); + if (!stream) { + return; + } + std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release())); + REPORTER_ASSERT(r, nullptr == codec); +} + +DEF_TEST(Codec_Empty, r) { + // Test images that should not be able to create a codec + test_invalid(r, "empty_images/zero-dims.gif"); + test_invalid(r, "empty_images/zero-embedded.ico"); + test_invalid(r, "empty_images/zero-width.bmp"); + test_invalid(r, "empty_images/zero-height.bmp"); + test_invalid(r, "empty_images/zero-width.jpg"); + test_invalid(r, "empty_images/zero-height.jpg"); + test_invalid(r, "empty_images/zero-width.png"); + test_invalid(r, "empty_images/zero-height.png"); + test_invalid(r, "empty_images/zero-width.wbmp"); + test_invalid(r, "empty_images/zero-height.wbmp"); + // This image is an ico with an embedded mask-bmp. This is illegal. + test_invalid(r, "invalid_images/mask-bmp-ico.ico"); + // It is illegal for a webp frame to not be fully contained by the canvas. + test_invalid(r, "invalid_images/invalid-offset.webp"); +#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32)) + test_invalid(r, "empty_images/zero_height.tiff"); +#endif + test_invalid(r, "invalid_images/b37623797.ico"); +} + +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + +#ifndef SK_PNG_DISABLE_TESTS // reading chunks does not work properly with older versions. + // It does not appear that anyone in Google3 is reading chunks. + +static void codex_test_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) { + SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr); + if (!sk_stream->write(data, len)) { + png_error(png_ptr, "sk_write_fn Error!"); + } +} + +DEF_TEST(Codec_pngChunkReader, r) { + // Create a dummy bitmap. Use unpremul RGBA for libpng. + SkBitmap bm; + const int w = 1; + const int h = 1; + const SkImageInfo bmInfo = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, + kUnpremul_SkAlphaType); + bm.setInfo(bmInfo); + bm.allocPixels(); + bm.eraseColor(SK_ColorBLUE); + SkMD5::Digest goodDigest; + md5(bm, &goodDigest); + + // Write to a png file. + png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + REPORTER_ASSERT(r, png); + if (!png) { + return; + } + + png_infop info = png_create_info_struct(png); + REPORTER_ASSERT(r, info); + if (!info) { + png_destroy_write_struct(&png, nullptr); + return; + } + + if (setjmp(png_jmpbuf(png))) { + ERRORF(r, "failed writing png"); + png_destroy_write_struct(&png, &info); + return; + } + + SkDynamicMemoryWStream wStream; + png_set_write_fn(png, (void*) (&wStream), codex_test_write_fn, nullptr); + + png_set_IHDR(png, info, (png_uint_32)w, (png_uint_32)h, 8, + PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + // Create some chunks that match the Android framework's use. + static png_unknown_chunk gUnknowns[] = { + { "npOl", (png_byte*)"outline", sizeof("outline"), PNG_HAVE_IHDR }, + { "npLb", (png_byte*)"layoutBounds", sizeof("layoutBounds"), PNG_HAVE_IHDR }, + { "npTc", (png_byte*)"ninePatchData", sizeof("ninePatchData"), PNG_HAVE_IHDR }, + }; + + png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"npOl\0npLb\0npTc\0", 3); + png_set_unknown_chunks(png, info, gUnknowns, SK_ARRAY_COUNT(gUnknowns)); +#if PNG_LIBPNG_VER < 10600 + /* Deal with unknown chunk location bug in 1.5.x and earlier */ + png_set_unknown_chunk_location(png, info, 0, PNG_HAVE_IHDR); + png_set_unknown_chunk_location(png, info, 1, PNG_HAVE_IHDR); +#endif + + png_write_info(png, info); + + for (int j = 0; j < h; j++) { + png_bytep row = (png_bytep)(bm.getAddr(0, j)); + png_write_rows(png, &row, 1); + } + png_write_end(png, info); + png_destroy_write_struct(&png, &info); + + class ChunkReader : public SkPngChunkReader { + public: + ChunkReader(skiatest::Reporter* r) + : fReporter(r) + { + this->reset(); + } + + bool readChunk(const char tag[], const void* data, size_t length) override { + for (size_t i = 0; i < SK_ARRAY_COUNT(gUnknowns); ++i) { + if (!strcmp(tag, (const char*) gUnknowns[i].name)) { + // Tag matches. This should have been the first time we see it. + REPORTER_ASSERT(fReporter, !fSeen[i]); + fSeen[i] = true; + + // Data and length should match + REPORTER_ASSERT(fReporter, length == gUnknowns[i].size); + REPORTER_ASSERT(fReporter, !strcmp((const char*) data, + (const char*) gUnknowns[i].data)); + return true; + } + } + ERRORF(fReporter, "Saw an unexpected unknown chunk."); + return true; + } + + bool allHaveBeenSeen() { + bool ret = true; + for (auto seen : fSeen) { + ret &= seen; + } + return ret; + } + + void reset() { + sk_bzero(fSeen, sizeof(fSeen)); + } + + private: + skiatest::Reporter* fReporter; // Unowned + bool fSeen[3]; + }; + + ChunkReader chunkReader(r); + + // Now read the file with SkCodec. + std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(wStream.detachAsData(), &chunkReader)); + REPORTER_ASSERT(r, codec); + if (!codec) { + return; + } + + // Now compare to the original. + SkBitmap decodedBm; + decodedBm.setInfo(codec->getInfo()); + decodedBm.allocPixels(); + SkCodec::Result result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(), + decodedBm.rowBytes()); + REPORTER_ASSERT(r, SkCodec::kSuccess == result); + + if (decodedBm.colorType() != bm.colorType()) { + SkBitmap tmp; + bool success = sk_tool_utils::copy_to(&tmp, bm.colorType(), decodedBm); + REPORTER_ASSERT(r, success); + if (!success) { + return; + } + + tmp.swap(decodedBm); + } + + compare_to_good_digest(r, goodDigest, decodedBm); + REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen()); + + // Decoding again will read the chunks again. + chunkReader.reset(); + REPORTER_ASSERT(r, !chunkReader.allHaveBeenSeen()); + result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(), decodedBm.rowBytes()); + REPORTER_ASSERT(r, SkCodec::kSuccess == result); + REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen()); +} +#endif // SK_PNG_DISABLE_TESTS +#endif // PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + +// Stream that can only peek up to a limit +class LimitedPeekingMemStream : public SkStream { +public: + LimitedPeekingMemStream(sk_sp<SkData> data, size_t limit) + : fStream(std::move(data)) + , fLimit(limit) {} + + size_t peek(void* buf, size_t bytes) const override { + return fStream.peek(buf, SkTMin(bytes, fLimit)); + } + size_t read(void* buf, size_t bytes) override { + return fStream.read(buf, bytes); + } + bool rewind() override { + return fStream.rewind(); + } + bool isAtEnd() const override { + return fStream.isAtEnd(); + } +private: + SkMemoryStream fStream; + const size_t fLimit; +}; + +// Disable RAW tests for Win32. +#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32)) +// Test that the RawCodec works also for not asset stream. This will test the code path using +// SkRawBufferedStream instead of SkRawAssetStream. +DEF_TEST(Codec_raw_notseekable, r) { + const char* path = "dng_with_preview.dng"; + SkString fullPath(GetResourcePath(path)); + sk_sp<SkData> data(SkData::MakeFromFileName(fullPath.c_str())); + if (!data) { + SkDebugf("Missing resource '%s'\n", path); + return; + } + + std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(new NotAssetMemStream(std::move(data)))); + REPORTER_ASSERT(r, codec); + + test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr); +} +#endif + +// Test that even if webp_parse_header fails to peek enough, it will fall back to read() +// + rewind() and succeed. +DEF_TEST(Codec_webp_peek, r) { + const char* path = "baby_tux.webp"; + SkString fullPath(GetResourcePath(path)); + auto data = SkData::MakeFromFileName(fullPath.c_str()); + if (!data) { + SkDebugf("Missing resource '%s'\n", path); + return; + } + + // The limit is less than webp needs to peek or read. + std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream( + new LimitedPeekingMemStream(data, 25))); + REPORTER_ASSERT(r, codec); + + test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr); + + // Similarly, a stream which does not peek should still succeed. + codec.reset(SkCodec::NewFromStream(new LimitedPeekingMemStream(data, 0))); + REPORTER_ASSERT(r, codec); + + test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr); +} + +// SkCodec's wbmp decoder was initially unnecessarily restrictive. +// It required the second byte to be zero. The wbmp specification allows +// a couple of bits to be 1 (so long as they do not overlap with 0x9F). +// Test that SkCodec now supports an image with these bits set. +DEF_TEST(Codec_wbmp_restrictive, r) { + const char* path = "mandrill.wbmp"; + std::unique_ptr<SkStream> stream(GetResourceAsStream(path)); + if (!stream) { + return; + } + + // Modify the stream to contain a second byte with some bits set. + auto data = SkCopyStreamToData(stream.get()); + uint8_t* writeableData = static_cast<uint8_t*>(data->writable_data()); + writeableData[1] = static_cast<uint8_t>(~0x9F); + + // SkCodec should support this. + std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data)); + REPORTER_ASSERT(r, codec); + if (!codec) { + return; + } + test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr); +} + +// wbmp images have a header that can be arbitrarily large, depending on the +// size of the image. We cap the size at 65535, meaning we only need to look at +// 8 bytes to determine whether we can read the image. This is important +// because SkCodec only passes 14 bytes to SkWbmpCodec to determine whether the +// image is a wbmp. +DEF_TEST(Codec_wbmp_max_size, r) { + const unsigned char maxSizeWbmp[] = { 0x00, 0x00, // Header + 0x83, 0xFF, 0x7F, // W: 65535 + 0x83, 0xFF, 0x7F }; // H: 65535 + std::unique_ptr<SkStream> stream(new SkMemoryStream(maxSizeWbmp, sizeof(maxSizeWbmp), false)); + std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release())); + + REPORTER_ASSERT(r, codec); + if (!codec) return; + + REPORTER_ASSERT(r, codec->getInfo().width() == 65535); + REPORTER_ASSERT(r, codec->getInfo().height() == 65535); + + // Now test an image which is too big. Any image with a larger header (i.e. + // has bigger width/height) is also too big. + const unsigned char tooBigWbmp[] = { 0x00, 0x00, // Header + 0x84, 0x80, 0x00, // W: 65536 + 0x84, 0x80, 0x00 }; // H: 65536 + stream.reset(new SkMemoryStream(tooBigWbmp, sizeof(tooBigWbmp), false)); + codec.reset(SkCodec::NewFromStream(stream.release())); + + REPORTER_ASSERT(r, !codec); +} + +DEF_TEST(Codec_jpeg_rewind, r) { + const char* path = "mandrill_512_q075.jpg"; + sk_sp<SkData> data(GetResourceAsData(path)); + if (!data) { + return; + } + + data = SkData::MakeSubset(data.get(), 0, data->size() / 2); + std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromData(data)); + if (!codec) { + ERRORF(r, "Unable to create codec '%s'.", path); + return; + } + + const int width = codec->getInfo().width(); + const int height = codec->getInfo().height(); + size_t rowBytes = sizeof(SkPMColor) * width; + SkAutoMalloc pixelStorage(height * rowBytes); + + // Perform a sampled decode. + SkAndroidCodec::AndroidOptions opts; + opts.fSampleSize = 12; + auto sampledInfo = codec->getInfo().makeWH(width / 12, height / 12); + auto result = codec->getAndroidPixels(sampledInfo, pixelStorage.get(), rowBytes, &opts); + REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result); + + // Rewind the codec and perform a full image decode. + result = codec->getPixels(codec->getInfo(), pixelStorage.get(), rowBytes); + REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result); + + // Now perform a subset decode. + { + opts.fSampleSize = 1; + SkIRect subset = SkIRect::MakeWH(100, 100); + opts.fSubset = ⊂ + result = codec->getAndroidPixels(codec->getInfo().makeWH(100, 100), pixelStorage.get(), + rowBytes, &opts); + // Though we only have half the data, it is enough to decode this subset. + REPORTER_ASSERT(r, SkCodec::kSuccess == result); + } + + // Perform another full image decode. ASAN will detect if we look at the subset when it is + // out of scope. This would happen if we depend on the old state in the codec. + // This tests two layers of bugs: both SkJpegCodec::readRows and SkCodec::fillIncompleteImage + // used to look at the old subset. + opts.fSubset = nullptr; + result = codec->getAndroidPixels(codec->getInfo(), pixelStorage.get(), rowBytes, &opts); + REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result); +} + +static void check_color_xform(skiatest::Reporter* r, const char* path) { + std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(GetResourceAsStream(path))); + + SkAndroidCodec::AndroidOptions opts; + opts.fSampleSize = 3; + const int subsetWidth = codec->getInfo().width() / 2; + const int subsetHeight = codec->getInfo().height() / 2; + SkIRect subset = SkIRect::MakeWH(subsetWidth, subsetHeight); + opts.fSubset = ⊂ + + const int dstWidth = subsetWidth / opts.fSampleSize; + const int dstHeight = subsetHeight / opts.fSampleSize; + sk_sp<SkData> data = SkData::MakeFromFileName( + GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str()); + sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeICC(data->data(), data->size()); + SkImageInfo dstInfo = codec->getInfo().makeWH(dstWidth, dstHeight) + .makeColorType(kN32_SkColorType) + .makeColorSpace(colorSpace); + + size_t rowBytes = dstInfo.minRowBytes(); + SkAutoMalloc pixelStorage(dstInfo.getSafeSize(rowBytes)); + SkCodec::Result result = codec->getAndroidPixels(dstInfo, pixelStorage.get(), rowBytes, &opts); + REPORTER_ASSERT(r, SkCodec::kSuccess == result); +} + +DEF_TEST(Codec_ColorXform, r) { + check_color_xform(r, "mandrill_512_q075.jpg"); + check_color_xform(r, "mandrill_512.png"); +} + +static bool color_type_match(SkColorType origColorType, SkColorType codecColorType) { + switch (origColorType) { + case kRGBA_8888_SkColorType: + case kBGRA_8888_SkColorType: + return kRGBA_8888_SkColorType == codecColorType || + kBGRA_8888_SkColorType == codecColorType; + default: + return origColorType == codecColorType; + } +} + +static bool alpha_type_match(SkAlphaType origAlphaType, SkAlphaType codecAlphaType) { + switch (origAlphaType) { + case kUnpremul_SkAlphaType: + case kPremul_SkAlphaType: + return kUnpremul_SkAlphaType == codecAlphaType || + kPremul_SkAlphaType == codecAlphaType; + default: + return origAlphaType == codecAlphaType; + } +} + +static void check_round_trip(skiatest::Reporter* r, SkCodec* origCodec, const SkImageInfo& info) { + SkBitmap bm1; + bm1.allocPixels(info); + SkCodec::Result result = origCodec->getPixels(info, bm1.getPixels(), bm1.rowBytes()); + REPORTER_ASSERT(r, SkCodec::kSuccess == result); + + // Encode the image to png. + sk_sp<SkData> data = + sk_sp<SkData>(sk_tool_utils::EncodeImageToData(bm1, SkEncodedImageFormat::kPNG, 100)); + + std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data)); + REPORTER_ASSERT(r, color_type_match(info.colorType(), codec->getInfo().colorType())); + REPORTER_ASSERT(r, alpha_type_match(info.alphaType(), codec->getInfo().alphaType())); + + SkBitmap bm2; + bm2.allocPixels(info); + result = codec->getPixels(info, bm2.getPixels(), bm2.rowBytes()); + REPORTER_ASSERT(r, SkCodec::kSuccess == result); + + SkMD5::Digest d1, d2; + md5(bm1, &d1); + md5(bm2, &d2); + REPORTER_ASSERT(r, d1 == d2); +} + +DEF_TEST(Codec_PngRoundTrip, r) { + const char* path = "mandrill_512_q075.jpg"; + std::unique_ptr<SkStream> stream(GetResourceAsStream(path)); + std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release())); + + SkColorType colorTypesOpaque[] = { + kRGB_565_SkColorType, kRGBA_8888_SkColorType, kBGRA_8888_SkColorType + }; + for (SkColorType colorType : colorTypesOpaque) { + SkImageInfo newInfo = codec->getInfo().makeColorType(colorType); + check_round_trip(r, codec.get(), newInfo); + } + + path = "grayscale.jpg"; + stream.reset(GetResourceAsStream(path)); + codec.reset(SkCodec::NewFromStream(stream.release())); + check_round_trip(r, codec.get(), codec->getInfo()); + + path = "yellow_rose.png"; + stream.reset(GetResourceAsStream(path)); + codec.reset(SkCodec::NewFromStream(stream.release())); + + SkColorType colorTypesWithAlpha[] = { + kRGBA_8888_SkColorType, kBGRA_8888_SkColorType + }; + SkAlphaType alphaTypes[] = { + kUnpremul_SkAlphaType, kPremul_SkAlphaType + }; + for (SkColorType colorType : colorTypesWithAlpha) { + for (SkAlphaType alphaType : alphaTypes) { + // Set color space to nullptr because color correct premultiplies do not round trip. + SkImageInfo newInfo = codec->getInfo().makeColorType(colorType) + .makeAlphaType(alphaType) + .makeColorSpace(nullptr); + check_round_trip(r, codec.get(), newInfo); + } + } + + path = "index8.png"; + stream.reset(GetResourceAsStream(path)); + codec.reset(SkCodec::NewFromStream(stream.release())); + + for (SkAlphaType alphaType : alphaTypes) { + SkImageInfo newInfo = codec->getInfo().makeAlphaType(alphaType) + .makeColorSpace(nullptr); + check_round_trip(r, codec.get(), newInfo); + } +} + +static void test_conversion_possible(skiatest::Reporter* r, const char* path, + bool supportsScanlineDecoder, + bool supportsIncrementalDecoder) { + std::unique_ptr<SkStream> stream(GetResourceAsStream(path)); + if (!stream) { + return; + } + + std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release())); + if (!codec) { + ERRORF(r, "failed to create a codec for %s", path); + return; + } + + SkImageInfo infoF16 = codec->getInfo().makeColorType(kRGBA_F16_SkColorType); + + SkBitmap bm; + bm.allocPixels(infoF16); + SkCodec::Result result = codec->getPixels(infoF16, bm.getPixels(), bm.rowBytes()); + REPORTER_ASSERT(r, SkCodec::kInvalidConversion == result); + + result = codec->startScanlineDecode(infoF16); + if (supportsScanlineDecoder) { + REPORTER_ASSERT(r, SkCodec::kInvalidConversion == result); + } else { + REPORTER_ASSERT(r, SkCodec::kUnimplemented == result); + } + + result = codec->startIncrementalDecode(infoF16, bm.getPixels(), bm.rowBytes()); + if (supportsIncrementalDecoder) { + REPORTER_ASSERT(r, SkCodec::kInvalidConversion == result); + } else { + REPORTER_ASSERT(r, SkCodec::kUnimplemented == result); + } + + SkASSERT(SkColorSpace_Base::Type::kXYZ == as_CSB(infoF16.colorSpace())->type()); + SkColorSpace_XYZ* csXYZ = static_cast<SkColorSpace_XYZ*>(infoF16.colorSpace()); + infoF16 = infoF16.makeColorSpace(csXYZ->makeLinearGamma()); + result = codec->getPixels(infoF16, bm.getPixels(), bm.rowBytes()); + REPORTER_ASSERT(r, SkCodec::kSuccess == result); + result = codec->startScanlineDecode(infoF16); + if (supportsScanlineDecoder) { + REPORTER_ASSERT(r, SkCodec::kSuccess == result); + } else { + REPORTER_ASSERT(r, SkCodec::kUnimplemented == result); + } + + result = codec->startIncrementalDecode(infoF16, bm.getPixels(), bm.rowBytes()); + if (supportsIncrementalDecoder) { + REPORTER_ASSERT(r, SkCodec::kSuccess == result); + } else { + REPORTER_ASSERT(r, SkCodec::kUnimplemented == result); + } +} + +DEF_TEST(Codec_F16ConversionPossible, r) { + test_conversion_possible(r, "color_wheel.webp", false, false); + test_conversion_possible(r, "mandrill_512_q075.jpg", true, false); + test_conversion_possible(r, "yellow_rose.png", false, true); +} + +static void decode_frame(skiatest::Reporter* r, SkCodec* codec, size_t frame) { + SkBitmap bm; + auto info = codec->getInfo().makeColorType(kN32_SkColorType); + bm.allocPixels(info); + + SkCodec::Options opts; + opts.fFrameIndex = frame; + REPORTER_ASSERT(r, SkCodec::kSuccess == codec->getPixels(info, + bm.getPixels(), bm.rowBytes(), &opts)); +} + +// For an animated GIF, we should only read enough to decode frame 0 if the +// client never calls getFrameInfo and only decodes frame 0. +DEF_TEST(Codec_skipFullParse, r) { + auto path = "test640x479.gif"; + SkStream* stream(GetResourceAsStream(path)); + if (!stream) { + return; + } + + // Note that we cheat and hold on to the stream pointer, but SkCodec will + // take ownership. We will not refer to the stream after the SkCodec + // deletes it. + std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream)); + if (!codec) { + ERRORF(r, "Failed to create codec for %s", path); + return; + } + + REPORTER_ASSERT(r, stream->hasPosition()); + const size_t sizePosition = stream->getPosition(); + REPORTER_ASSERT(r, stream->hasLength() && sizePosition < stream->getLength()); + + // This should read more of the stream, but not the whole stream. + decode_frame(r, codec.get(), 0); + const size_t positionAfterFirstFrame = stream->getPosition(); + REPORTER_ASSERT(r, positionAfterFirstFrame > sizePosition + && positionAfterFirstFrame < stream->getLength()); + + // There is more data in the stream. + auto frameInfo = codec->getFrameInfo(); + REPORTER_ASSERT(r, frameInfo.size() == 4); + REPORTER_ASSERT(r, stream->getPosition() > positionAfterFirstFrame); +} + +// Only rewinds up to a limit. +class LimitedRewindingStream : public SkStream { +public: + static SkStream* Make(const char path[], size_t limit) { + SkStream* stream = GetResourceAsStream(path); + if (!stream) { + return nullptr; + } + return new LimitedRewindingStream(stream, limit); + } + + size_t read(void* buffer, size_t size) override { + const size_t bytes = fStream->read(buffer, size); + fPosition += bytes; + return bytes; + } + + bool isAtEnd() const override { + return fStream->isAtEnd(); + } + + bool rewind() override { + if (fPosition <= fLimit && fStream->rewind()) { + fPosition = 0; + return true; + } + + return false; + } + +private: + std::unique_ptr<SkStream> fStream; + const size_t fLimit; + size_t fPosition; + + LimitedRewindingStream(SkStream* stream, size_t limit) + : fStream(stream) + , fLimit(limit) + , fPosition(0) + { + SkASSERT(fStream); + } +}; + +DEF_TEST(Codec_fallBack, r) { + // SkAndroidCodec needs to be able to fall back to scanline decoding + // if incremental decoding does not work. Make sure this does not + // require a rewind. + + // Formats that currently do not support incremental decoding + auto files = { + "CMYK.jpg", + "color_wheel.ico", + "mandrill.wbmp", + "randPixels.bmp", + }; + for (auto file : files) { + SkStream* stream = LimitedRewindingStream::Make(file, 14); + if (!stream) { + SkDebugf("Missing resources (%s). Set --resourcePath.\n", file); + return; + } + + std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream)); + if (!codec) { + ERRORF(r, "Failed to create codec for %s,", file); + continue; + } + + SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType); + SkBitmap bm; + bm.allocPixels(info); + + if (SkCodec::kUnimplemented != codec->startIncrementalDecode(info, bm.getPixels(), + bm.rowBytes())) { + ERRORF(r, "Is scanline decoding now implemented for %s?", file); + continue; + } + + // Scanline decoding should not require a rewind. + SkCodec::Result result = codec->startScanlineDecode(info); + if (SkCodec::kSuccess != result) { + ERRORF(r, "Scanline decoding failed for %s with %i", file, result); + } + } +} + +// This test verifies that we fixed an assert statement that fired when reusing a png codec +// after scaling. +DEF_TEST(Codec_reusePng, r) { + std::unique_ptr<SkStream> stream(GetResourceAsStream("plane.png")); + if (!stream) { + return; + } + + std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(stream.release())); + if (!codec) { + ERRORF(r, "Failed to create codec\n"); + return; + } + + SkAndroidCodec::AndroidOptions opts; + opts.fSampleSize = 5; + auto size = codec->getSampledDimensions(opts.fSampleSize); + auto info = codec->getInfo().makeWH(size.fWidth, size.fHeight).makeColorType(kN32_SkColorType); + SkBitmap bm; + bm.allocPixels(info); + auto result = codec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &opts); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); + + info = codec->getInfo().makeColorType(kN32_SkColorType); + bm.allocPixels(info); + opts.fSampleSize = 1; + result = codec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &opts); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); +} + +DEF_TEST(Codec_rowsDecoded, r) { + auto file = "plane_interlaced.png"; + std::unique_ptr<SkStream> stream(GetResourceAsStream(file)); + if (!stream) { + return; + } + + // This is enough to read the header etc, but no rows. + auto data = SkData::MakeFromStream(stream.get(), 99); + std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data)); + if (!codec) { + ERRORF(r, "Failed to create codec\n"); + return; + } + + auto info = codec->getInfo().makeColorType(kN32_SkColorType); + SkBitmap bm; + bm.allocPixels(info); + auto result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes()); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); + + // This is an arbitrary value. The important fact is that it is not zero, and rowsDecoded + // should get set to zero by incrementalDecode. + int rowsDecoded = 77; + result = codec->incrementalDecode(&rowsDecoded); + REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput); + REPORTER_ASSERT(r, rowsDecoded == 0); +} + +static void test_invalid_images(skiatest::Reporter* r, const char* path, + SkCodec::Result expectedResult) { + auto* stream = GetResourceAsStream(path); + if (!stream) { + return; + } + + std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream)); + REPORTER_ASSERT(r, codec); + + test_info(r, codec.get(), codec->getInfo().makeColorType(kN32_SkColorType), expectedResult, + nullptr); +} + +DEF_TEST(Codec_InvalidImages, r) { + // ASAN will complain if there is an issue. + test_invalid_images(r, "invalid_images/skbug5887.gif", SkCodec::kErrorInInput); + test_invalid_images(r, "invalid_images/many-progressive-scans.jpg", SkCodec::kInvalidInput); + test_invalid_images(r, "invalid_images/b33251605.bmp", SkCodec::kIncompleteInput); + test_invalid_images(r, "invalid_images/bad_palette.png", SkCodec::kInvalidInput); +} + +static void test_invalid_header(skiatest::Reporter* r, const char* path) { + SkString resourcePath = GetResourcePath(path); + std::unique_ptr<SkFILEStream> stream(new SkFILEStream(resourcePath.c_str())); + if (!stream->isValid()) { + return; + } + + std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release())); + REPORTER_ASSERT(r, !codec); +} + +DEF_TEST(Codec_InvalidHeader, r) { + test_invalid_header(r, "invalid_images/int_overflow.ico"); + + // These files report values that have caused problems with SkFILEStreams. + // They are invalid, and should not create SkCodecs. + test_invalid_header(r, "invalid_images/b33651913.bmp"); + test_invalid_header(r, "invalid_images/b34778578.bmp"); +} + +DEF_TEST(Codec_InvalidAnimated, r) { + // ASAN will complain if there is an issue. + auto path = "invalid_images/skbug6046.gif"; + auto* stream = GetResourceAsStream(path); + if (!stream) { + return; + } + + std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream)); + REPORTER_ASSERT(r, codec); + if (!codec) { + return; + } + + const auto info = codec->getInfo().makeColorType(kN32_SkColorType); + SkBitmap bm; + bm.allocPixels(info); + + auto frameInfos = codec->getFrameInfo(); + SkCodec::Options opts; + for (int i = 0; static_cast<size_t>(i) < frameInfos.size(); i++) { + opts.fFrameIndex = i; + const auto reqFrame = frameInfos[i].fRequiredFrame; + opts.fPriorFrame = reqFrame == i - 1 ? reqFrame : SkCodec::kNone; + auto result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes(), &opts); + if (result != SkCodec::kSuccess) { + ERRORF(r, "Failed to start decoding frame %i (out of %i) with error %i\n", i, + frameInfos.size(), result); + continue; + } + + codec->incrementalDecode(); + } +} + +static void encode_format(SkDynamicMemoryWStream* stream, const SkPixmap& pixmap, + SkTransferFunctionBehavior unpremulBehavior, + SkEncodedImageFormat format) { + SkPngEncoder::Options pngOptions; + SkWebpEncoder::Options webpOptions; + pngOptions.fUnpremulBehavior = unpremulBehavior; + webpOptions.fUnpremulBehavior = unpremulBehavior; + switch (format) { + case SkEncodedImageFormat::kPNG: + SkPngEncoder::Encode(stream, pixmap, pngOptions); + break; + case SkEncodedImageFormat::kJPEG: + SkJpegEncoder::Encode(stream, pixmap, SkJpegEncoder::Options()); + break; + case SkEncodedImageFormat::kWEBP: + SkWebpEncoder::Encode(stream, pixmap, webpOptions); + break; + default: + SkASSERT(false); + break; + } +} + +static void test_encode_icc(skiatest::Reporter* r, SkEncodedImageFormat format, + SkTransferFunctionBehavior unpremulBehavior) { + // Test with sRGB color space. + SkBitmap srgbBitmap; + SkImageInfo srgbInfo = SkImageInfo::MakeS32(1, 1, kOpaque_SkAlphaType); + srgbBitmap.allocPixels(srgbInfo); + *srgbBitmap.getAddr32(0, 0) = 0; + SkPixmap pixmap; + srgbBitmap.peekPixels(&pixmap); + SkDynamicMemoryWStream srgbBuf; + encode_format(&srgbBuf, pixmap, unpremulBehavior, format); + sk_sp<SkData> srgbData = srgbBuf.detachAsData(); + std::unique_ptr<SkCodec> srgbCodec(SkCodec::NewFromData(srgbData)); + REPORTER_ASSERT(r, srgbCodec->getInfo().colorSpace() == SkColorSpace::MakeSRGB().get()); + + // Test with P3 color space. + SkDynamicMemoryWStream p3Buf; + sk_sp<SkColorSpace> p3 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, + SkColorSpace::kDCIP3_D65_Gamut); + pixmap.setColorSpace(p3); + encode_format(&p3Buf, pixmap, unpremulBehavior, format); + sk_sp<SkData> p3Data = p3Buf.detachAsData(); + std::unique_ptr<SkCodec> p3Codec(SkCodec::NewFromData(p3Data)); + REPORTER_ASSERT(r, p3Codec->getInfo().colorSpace()->gammaCloseToSRGB()); + SkMatrix44 mat0(SkMatrix44::kUninitialized_Constructor); + SkMatrix44 mat1(SkMatrix44::kUninitialized_Constructor); + bool success = p3->toXYZD50(&mat0); + REPORTER_ASSERT(r, success); + success = p3Codec->getInfo().colorSpace()->toXYZD50(&mat1); + REPORTER_ASSERT(r, success); + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + REPORTER_ASSERT(r, color_space_almost_equal(mat0.get(i, j), mat1.get(i, j))); + } + } +} + +DEF_TEST(Codec_EncodeICC, r) { + test_encode_icc(r, SkEncodedImageFormat::kPNG, SkTransferFunctionBehavior::kRespect); + test_encode_icc(r, SkEncodedImageFormat::kJPEG, SkTransferFunctionBehavior::kRespect); + test_encode_icc(r, SkEncodedImageFormat::kWEBP, SkTransferFunctionBehavior::kRespect); + test_encode_icc(r, SkEncodedImageFormat::kPNG, SkTransferFunctionBehavior::kIgnore); + test_encode_icc(r, SkEncodedImageFormat::kJPEG, SkTransferFunctionBehavior::kIgnore); + test_encode_icc(r, SkEncodedImageFormat::kWEBP, SkTransferFunctionBehavior::kIgnore); +}
diff --git a/src/third_party/skia/tests/ColorFilterTest.cpp b/src/third_party/skia/tests/ColorFilterTest.cpp index f3f6a0a..c502a70 100644 --- a/src/third_party/skia/tests/ColorFilterTest.cpp +++ b/src/third_party/skia/tests/ColorFilterTest.cpp
@@ -5,18 +5,19 @@ * found in the LICENSE file. */ +#include "SkAutoMalloc.h" #include "SkColor.h" #include "SkColorFilter.h" #include "SkColorPriv.h" #include "SkLumaColorFilter.h" +#include "SkRandom.h" #include "SkReadBuffer.h" #include "SkWriteBuffer.h" #include "SkRandom.h" -#include "SkXfermode.h" #include "Test.h" -static SkColorFilter* reincarnate_colorfilter(SkFlattenable* obj) { - SkWriteBuffer wb; +static sk_sp<SkColorFilter> reincarnate_colorfilter(SkFlattenable* obj) { + SkBinaryWriteBuffer wb; wb.writeFlattenable(obj); size_t size = wb.bytesWritten(); @@ -30,45 +31,63 @@ /////////////////////////////////////////////////////////////////////////////// -#define ILLEGAL_MODE ((SkXfermode::Mode)-1) +static sk_sp<SkColorFilter> make_filter() { + // pick a filter that cannot compose with itself via newComposed() + return SkColorFilter::MakeModeFilter(SK_ColorRED, SkBlendMode::kColorBurn); +} + +static void test_composecolorfilter_limit(skiatest::Reporter* reporter) { + // Test that CreateComposeFilter() has some finite limit (i.e. that the factory can return null) + const int way_too_many = 100; + auto parent(make_filter()); + for (int i = 2; i < way_too_many; ++i) { + auto filter(make_filter()); + parent = SkColorFilter::MakeComposeFilter(parent, filter); + if (nullptr == parent) { + REPORTER_ASSERT(reporter, i > 2); // we need to have succeeded at least once! + return; + } + } + REPORTER_ASSERT(reporter, false); // we never saw a nullptr :( +} + +#define ILLEGAL_MODE ((SkBlendMode)-1) DEF_TEST(ColorFilter, reporter) { SkRandom rand; - for (int mode = 0; mode <= SkXfermode::kLastMode; mode++) { + for (int mode = 0; mode <= (int)SkBlendMode::kLastMode; mode++) { SkColor color = rand.nextU(); // ensure we always get a filter, by avoiding the possibility of a - // special case that would return NULL (if color's alpha is 0 or 0xFF) + // special case that would return nullptr (if color's alpha is 0 or 0xFF) color = SkColorSetA(color, 0x7F); - SkColorFilter* cf = SkColorFilter::CreateModeFilter(color, - (SkXfermode::Mode)mode); + auto cf = SkColorFilter::MakeModeFilter(color, (SkBlendMode)mode); // allow for no filter if we're in Dst mode (its a no op) - if (SkXfermode::kDst_Mode == mode && NULL == cf) { + if (SkBlendMode::kDst == (SkBlendMode)mode && nullptr == cf) { continue; } - SkAutoUnref aur(cf); REPORTER_ASSERT(reporter, cf); SkColor c = ~color; - SkXfermode::Mode m = ILLEGAL_MODE; + SkBlendMode m = ILLEGAL_MODE; SkColor expectedColor = color; - SkXfermode::Mode expectedMode = (SkXfermode::Mode)mode; + SkBlendMode expectedMode = (SkBlendMode)mode; // SkDebugf("--- mc [%d %x] ", mode, color); - REPORTER_ASSERT(reporter, cf->asColorMode(&c, &m)); + REPORTER_ASSERT(reporter, cf->asColorMode(&c, (SkBlendMode*)&m)); // handle special-case folding by the factory - if (SkXfermode::kClear_Mode == mode) { + if (SkBlendMode::kClear == (SkBlendMode)mode) { if (c != expectedColor) { expectedColor = 0; } if (m != expectedMode) { - expectedMode = SkXfermode::kSrc_Mode; + expectedMode = SkBlendMode::kSrc; } } @@ -78,50 +97,16 @@ REPORTER_ASSERT(reporter, m == expectedMode); { - SkColorFilter* cf2 = reincarnate_colorfilter(cf); - SkAutoUnref aur2(cf2); + auto cf2 = reincarnate_colorfilter(cf.get()); REPORTER_ASSERT(reporter, cf2); SkColor c2 = ~color; - SkXfermode::Mode m2 = ILLEGAL_MODE; - REPORTER_ASSERT(reporter, cf2->asColorMode(&c2, &m2)); + SkBlendMode m2 = ILLEGAL_MODE; + REPORTER_ASSERT(reporter, cf2->asColorMode(&c2, (SkBlendMode*)&m2)); REPORTER_ASSERT(reporter, c2 == expectedColor); REPORTER_ASSERT(reporter, m2 == expectedMode); } } -} -/////////////////////////////////////////////////////////////////////////////// - -DEF_TEST(LumaColorFilter, reporter) { - SkPMColor in, out; - SkAutoTUnref<SkColorFilter> lf(SkLumaColorFilter::Create()); - - // Applying luma to white produces black with the same transparency. - for (unsigned i = 0; i < 256; ++i) { - in = SkPackARGB32(i, i, i, i); - lf->filterSpan(&in, 1, &out); - REPORTER_ASSERT(reporter, SkGetPackedA32(out) == i); - REPORTER_ASSERT(reporter, SkGetPackedR32(out) == 0); - REPORTER_ASSERT(reporter, SkGetPackedG32(out) == 0); - REPORTER_ASSERT(reporter, SkGetPackedB32(out) == 0); - } - - // Applying luma to black yields transparent black (luminance(black) == 0) - for (unsigned i = 0; i < 256; ++i) { - in = SkPackARGB32(i, 0, 0, 0); - lf->filterSpan(&in, 1, &out); - REPORTER_ASSERT(reporter, out == SK_ColorTRANSPARENT); - } - - // For general colors, a luma filter generates black with an attenuated alpha channel. - for (unsigned i = 1; i < 256; ++i) { - in = SkPackARGB32(i, i, i / 2, i / 3); - lf->filterSpan(&in, 1, &out); - REPORTER_ASSERT(reporter, out != in); - REPORTER_ASSERT(reporter, SkGetPackedA32(out) <= i); - REPORTER_ASSERT(reporter, SkGetPackedR32(out) == 0); - REPORTER_ASSERT(reporter, SkGetPackedG32(out) == 0); - REPORTER_ASSERT(reporter, SkGetPackedB32(out) == 0); - } + test_composecolorfilter_limit(reporter); }
diff --git a/src/third_party/skia/tests/ColorMatrixTest.cpp b/src/third_party/skia/tests/ColorMatrixTest.cpp new file mode 100644 index 0000000..7919b40 --- /dev/null +++ b/src/third_party/skia/tests/ColorMatrixTest.cpp
@@ -0,0 +1,101 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkColor.h" +#include "SkColorMatrixFilter.h" +#include "SkPaint.h" + +#include <stdlib.h> + +static inline void assert_color(skiatest::Reporter* reporter, + SkColor expected, SkColor actual, int tolerance) { + REPORTER_ASSERT(reporter, abs((int)(SkColorGetA(expected) - SkColorGetA(actual))) <= tolerance); + REPORTER_ASSERT(reporter, abs((int)(SkColorGetR(expected) - SkColorGetR(actual))) <= tolerance); + REPORTER_ASSERT(reporter, abs((int)(SkColorGetG(expected) - SkColorGetG(actual))) <= tolerance); + REPORTER_ASSERT(reporter, abs((int)(SkColorGetB(expected) - SkColorGetB(actual))) <= tolerance); +} + +static inline void assert_color(skiatest::Reporter* reporter, SkColor expected, SkColor actual) { + const int TOLERANCE = 1; + assert_color(reporter, expected, actual, TOLERANCE); +} + +/** + * This test case is a mirror of the Android CTS tests for MatrixColorFilter + * found in the android.graphics.ColorMatrixColorFilterTest class. + */ +static inline void test_colorMatrixCTS(skiatest::Reporter* reporter) { + + SkBitmap bitmap; + bitmap.allocN32Pixels(1,1); + + SkCanvas canvas(bitmap); + SkPaint paint; + + SkScalar blueToCyan[20] = { + 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, 0.0f }; + paint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(blueToCyan)); + + paint.setColor(SK_ColorBLUE); + canvas.drawPoint(0, 0, paint); + assert_color(reporter, SK_ColorCYAN, bitmap.getColor(0, 0)); + + paint.setColor(SK_ColorGREEN); + canvas.drawPoint(0, 0, paint); + assert_color(reporter, SK_ColorGREEN, bitmap.getColor(0, 0)); + + paint.setColor(SK_ColorRED); + canvas.drawPoint(0, 0, paint); + assert_color(reporter, SK_ColorRED, bitmap.getColor(0, 0)); + + // color components are clipped, not scaled + paint.setColor(SK_ColorMAGENTA); + canvas.drawPoint(0, 0, paint); + assert_color(reporter, SK_ColorWHITE, bitmap.getColor(0, 0)); + + SkScalar transparentRedAddBlue[20] = { + 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, 64.0f, + -0.5f, 0.0f, 0.0f, 1.0f, 0.0f + }; + paint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(transparentRedAddBlue)); + bitmap.eraseColor(SK_ColorTRANSPARENT); + + paint.setColor(SK_ColorRED); + canvas.drawPoint(0, 0, paint); + assert_color(reporter, SkColorSetARGB(128, 255, 0, 64), bitmap.getColor(0, 0), 2); + + paint.setColor(SK_ColorCYAN); + canvas.drawPoint(0, 0, paint); + // blue gets clipped + assert_color(reporter, SK_ColorCYAN, bitmap.getColor(0, 0)); + + // change array to filter out green + REPORTER_ASSERT(reporter, 1.0f == transparentRedAddBlue[6]); + transparentRedAddBlue[6] = 0.0f; + + // check that changing the array has no effect + canvas.drawPoint(0, 0, paint); + assert_color(reporter, SK_ColorCYAN, bitmap.getColor(0, 0)); + + // create a new filter with the changed matrix + paint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(transparentRedAddBlue)); + canvas.drawPoint(0, 0, paint); + assert_color(reporter, SK_ColorBLUE, bitmap.getColor(0, 0)); +} + +DEF_TEST(ColorMatrix, reporter) { + test_colorMatrixCTS(reporter); +}
diff --git a/src/third_party/skia/tests/ColorSpaceTest.cpp b/src/third_party/skia/tests/ColorSpaceTest.cpp new file mode 100644 index 0000000..9b2e1b3 --- /dev/null +++ b/src/third_party/skia/tests/ColorSpaceTest.cpp
@@ -0,0 +1,501 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Resources.h" +#include "SkCodec.h" +#include "SkColorSpace.h" +#include "SkColorSpace_Base.h" +#include "SkColorSpace_XYZ.h" +#include "SkColorSpacePriv.h" +#include "Test.h" + +#include "png.h" + +static bool almost_equal(float a, float b) { + return SkTAbs(a - b) < 0.001f; +} + +static void test_space(skiatest::Reporter* r, SkColorSpace* space, + const float red[], const float green[], const float blue[], + const SkGammaNamed expectedGamma) { + + REPORTER_ASSERT(r, nullptr != space); + SkASSERT(SkColorSpace_Base::Type::kXYZ == as_CSB(space)->type()); + SkColorSpace_XYZ* csXYZ = static_cast<SkColorSpace_XYZ*>(space); + REPORTER_ASSERT(r, expectedGamma == csXYZ->gammaNamed()); + + const SkMatrix44& mat = *csXYZ->toXYZD50(); + const float src[] = { + 1, 0, 0, 1, + 0, 1, 0, 1, + 0, 0, 1, 1, + }; + const float* ref[3] = { red, green, blue }; + float dst[4]; + for (int i = 0; i < 3; ++i) { + mat.mapScalars(&src[i*4], dst); + REPORTER_ASSERT(r, almost_equal(ref[i][0], dst[0])); + REPORTER_ASSERT(r, almost_equal(ref[i][1], dst[1])); + REPORTER_ASSERT(r, almost_equal(ref[i][2], dst[2])); + } +} + +static void test_path(skiatest::Reporter* r, const char* path, + const float red[], const float green[], const float blue[], + const SkGammaNamed expectedGamma) { + std::unique_ptr<SkStream> stream(GetResourceAsStream(path)); + REPORTER_ASSERT(r, nullptr != stream); + if (!stream) { + return; + } + + std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release())); + REPORTER_ASSERT(r, nullptr != codec); + if (!codec) { + return; + } + + SkColorSpace* colorSpace = codec->getInfo().colorSpace(); + test_space(r, colorSpace, red, green, blue, expectedGamma); +} + +static constexpr float g_sRGB_XYZ[]{ + 0.4358f, 0.3853f, 0.1430f, // Rx, Gx, Bx + 0.2224f, 0.7170f, 0.0606f, // Ry, Gy, Gz + 0.0139f, 0.0971f, 0.7139f, // Rz, Gz, Bz +}; + +static constexpr float g_sRGB_R[]{ 0.4358f, 0.2224f, 0.0139f }; +static constexpr float g_sRGB_G[]{ 0.3853f, 0.7170f, 0.0971f }; +static constexpr float g_sRGB_B[]{ 0.1430f, 0.0606f, 0.7139f }; + +DEF_TEST(ColorSpace_sRGB, r) { + test_space(r, SkColorSpace::MakeSRGB().get(), + g_sRGB_R, g_sRGB_G, g_sRGB_B, kSRGB_SkGammaNamed); + +} + +DEF_TEST(ColorSpaceParseICCProfiles, r) { + +#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6) + test_path(r, "color_wheel_with_profile.png", g_sRGB_R, g_sRGB_G, g_sRGB_B, + kSRGB_SkGammaNamed); +#endif + + const float red[] = { 0.385117f, 0.716904f, 0.0970612f }; + const float green[] = { 0.143051f, 0.0606079f, 0.713913f }; + const float blue[] = { 0.436035f, 0.222488f, 0.013916f }; + test_path(r, "icc-v2-gbr.jpg", red, green, blue, k2Dot2Curve_SkGammaNamed); + + test_path(r, "webp-color-profile-crash.webp", + red, green, blue, kNonStandard_SkGammaNamed); + test_path(r, "webp-color-profile-lossless.webp", + red, green, blue, kNonStandard_SkGammaNamed); + test_path(r, "webp-color-profile-lossy.webp", + red, green, blue, kNonStandard_SkGammaNamed); + test_path(r, "webp-color-profile-lossy-alpha.webp", + red, green, blue, kNonStandard_SkGammaNamed); +} + +DEF_TEST(ColorSpaceSRGBCompare, r) { + // Create an sRGB color space by name + sk_sp<SkColorSpace> namedColorSpace = SkColorSpace::MakeSRGB(); + + // Create an sRGB color space by value + SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor); + srgbToxyzD50.set3x3RowMajorf(g_sRGB_XYZ); + sk_sp<SkColorSpace> rgbColorSpace = + SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, srgbToxyzD50); + REPORTER_ASSERT(r, rgbColorSpace == namedColorSpace); + + SkColorSpaceTransferFn srgbFn; + srgbFn.fA = (1.0f / 1.055f); + srgbFn.fB = (0.055f / 1.055f); + srgbFn.fC = (1.0f / 12.92f); + srgbFn.fD = 0.04045f; + srgbFn.fE = 0.0f; + srgbFn.fF = 0.0f; + srgbFn.fG = 2.4f; + sk_sp<SkColorSpace> rgbColorSpace2 = SkColorSpace::MakeRGB(srgbFn, srgbToxyzD50); + REPORTER_ASSERT(r, rgbColorSpace2 == namedColorSpace); + + // Change a single value from the sRGB matrix + srgbToxyzD50.set(2, 2, 0.5f); + sk_sp<SkColorSpace> strangeColorSpace = + SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, srgbToxyzD50); + REPORTER_ASSERT(r, strangeColorSpace != namedColorSpace); +} + +DEF_TEST(ColorSpaceSRGBLinearCompare, r) { + // Create the linear sRGB color space by name + sk_sp<SkColorSpace> namedColorSpace = SkColorSpace::MakeSRGBLinear(); + + // Create the linear sRGB color space via the sRGB color space's makeLinearGamma() + auto srgb = SkColorSpace::MakeSRGB(); + auto srgbXYZ = static_cast<SkColorSpace_XYZ*>(srgb.get()); + sk_sp<SkColorSpace> viaSrgbColorSpace = srgbXYZ->makeLinearGamma(); + REPORTER_ASSERT(r, namedColorSpace == viaSrgbColorSpace); + + // Create a linear sRGB color space by value + SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor); + srgbToxyzD50.set3x3RowMajorf(g_sRGB_XYZ); + sk_sp<SkColorSpace> rgbColorSpace = + SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, srgbToxyzD50); + REPORTER_ASSERT(r, rgbColorSpace == namedColorSpace); + + SkColorSpaceTransferFn linearExpFn; + linearExpFn.fA = 1.0f; + linearExpFn.fB = 0.0f; + linearExpFn.fC = 0.0f; + linearExpFn.fD = 0.0f; + linearExpFn.fE = 0.0f; + linearExpFn.fF = 0.0f; + linearExpFn.fG = 1.0f; + sk_sp<SkColorSpace> rgbColorSpace2 = SkColorSpace::MakeRGB(linearExpFn, srgbToxyzD50); + REPORTER_ASSERT(r, rgbColorSpace2 == namedColorSpace); + + SkColorSpaceTransferFn linearFn; + linearFn.fA = 0.0f; + linearFn.fB = 0.0f; + linearFn.fC = 1.0f; + linearFn.fD = 1.0f; + linearFn.fE = 0.0f; + linearFn.fF = 0.0f; + linearFn.fG = 0.0f; + sk_sp<SkColorSpace> rgbColorSpace3 = SkColorSpace::MakeRGB(linearFn, srgbToxyzD50); + REPORTER_ASSERT(r, rgbColorSpace3 == namedColorSpace); + + // Change a single value from the sRGB matrix + srgbToxyzD50.set(2, 2, 0.5f); + sk_sp<SkColorSpace> strangeColorSpace = + SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, srgbToxyzD50); + REPORTER_ASSERT(r, strangeColorSpace != namedColorSpace); +} + +DEF_TEST(ColorSpaceAdobeCompare, r) { + // Create an sRGB color space by name + sk_sp<SkColorSpace> namedColorSpace = + SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named); + + // Create an sRGB color space by value + SkMatrix44 adobeToxyzD50(SkMatrix44::kUninitialized_Constructor); + adobeToxyzD50.set3x3RowMajorf(gAdobeRGB_toXYZD50); + + SkColorSpaceTransferFn fn; + fn.fA = 1.0f; + fn.fB = 0.0f; + fn.fC = 0.0f; + fn.fD = 0.0f; + fn.fE = 0.0f; + fn.fF = 0.0f; + fn.fG = 2.2f; + sk_sp<SkColorSpace> rgbColorSpace = SkColorSpace::MakeRGB(fn, adobeToxyzD50); + REPORTER_ASSERT(r, rgbColorSpace == namedColorSpace); +} + +DEF_TEST(ColorSpace_Named, r) { + const struct { + SkColorSpace_Base::Named fNamed; + SkGammaNamed fExpectedGamma; + } recs[] { + { SkColorSpace_Base::kSRGB_Named, kSRGB_SkGammaNamed }, + { SkColorSpace_Base::kAdobeRGB_Named, k2Dot2Curve_SkGammaNamed }, + { SkColorSpace_Base::kSRGBLinear_Named, kLinear_SkGammaNamed }, + }; + + for (auto rec : recs) { + auto cs = SkColorSpace_Base::MakeNamed(rec.fNamed); + REPORTER_ASSERT(r, cs); + if (cs) { + SkASSERT(SkColorSpace_Base::Type::kXYZ == as_CSB(cs)->type()); + SkColorSpace_XYZ* csXYZ = static_cast<SkColorSpace_XYZ*>(cs.get()); + REPORTER_ASSERT(r, rec.fExpectedGamma == csXYZ->gammaNamed()); + } + } + + SkImageInfo info = SkImageInfo::MakeS32(10, 10, kPremul_SkAlphaType); + REPORTER_ASSERT(r, info.gammaCloseToSRGB()); +} + +static void test_serialize(skiatest::Reporter* r, SkColorSpace* space, bool isNamed) { + sk_sp<SkData> data1 = space->serialize(); + + size_t bytes = space->writeToMemory(nullptr); + sk_sp<SkData> data2 = SkData::MakeUninitialized(bytes); + space->writeToMemory(data2->writable_data()); + + sk_sp<SkColorSpace> newSpace1 = SkColorSpace::Deserialize(data1->data(), data1->size()); + sk_sp<SkColorSpace> newSpace2 = SkColorSpace::Deserialize(data2->data(), data2->size()); + + if (isNamed) { + REPORTER_ASSERT(r, space == newSpace1.get()); + REPORTER_ASSERT(r, space == newSpace2.get()); + } else { + REPORTER_ASSERT(r, SkColorSpace::Equals(space, newSpace1.get())); + REPORTER_ASSERT(r, SkColorSpace::Equals(space, newSpace2.get())); + } +} + +DEF_TEST(ColorSpace_Serialize, r) { + test_serialize(r, SkColorSpace::MakeSRGB().get(), true); + test_serialize(r, SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named).get(), true); + test_serialize(r, SkColorSpace::MakeSRGBLinear().get(), true); + + sk_sp<SkData> monitorData = SkData::MakeFromFileName( + GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str()); + test_serialize(r, SkColorSpace::MakeICC(monitorData->data(), monitorData->size()).get(), false); + monitorData = SkData::MakeFromFileName( GetResourcePath("icc_profiles/HP_Z32x.icc").c_str()); + test_serialize(r, SkColorSpace::MakeICC(monitorData->data(), monitorData->size()).get(), false); + monitorData = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperLeft.icc").c_str()); + test_serialize(r, SkColorSpace::MakeICC(monitorData->data(), monitorData->size()).get(), false); + monitorData = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperRight.icc").c_str()); + test_serialize(r, SkColorSpace::MakeICC(monitorData->data(), monitorData->size()).get(), false); + + SkColorSpaceTransferFn fn; + fn.fA = 1.0f; + fn.fB = 0.0f; + fn.fC = 1.0f; + fn.fD = 0.5f; + fn.fE = 0.0f; + fn.fF = 0.0f; + fn.fG = 1.0f; + SkMatrix44 toXYZ(SkMatrix44::kIdentity_Constructor); + test_serialize(r, SkColorSpace::MakeRGB(fn, toXYZ).get(), false); +} + +DEF_TEST(ColorSpace_Equals, r) { + sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB(); + sk_sp<SkColorSpace> adobe = SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named); + sk_sp<SkData> data = SkData::MakeFromFileName( + GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str()); + sk_sp<SkColorSpace> z30 = SkColorSpace::MakeICC(data->data(), data->size()); + data = SkData::MakeFromFileName( GetResourcePath("icc_profiles/HP_Z32x.icc").c_str()); + sk_sp<SkColorSpace> z32 = SkColorSpace::MakeICC(data->data(), data->size()); + data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperLeft.icc").c_str()); + sk_sp<SkColorSpace> upperLeft = SkColorSpace::MakeICC(data->data(), data->size()); + data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperRight.icc").c_str()); + sk_sp<SkColorSpace> upperRight = SkColorSpace::MakeICC(data->data(), data->size()); + + SkColorSpaceTransferFn fn; + fn.fA = 1.0f; + fn.fB = 0.0f; + fn.fC = 1.0f; + fn.fD = 0.5f; + fn.fE = 0.0f; + fn.fF = 0.0f; + fn.fG = 1.0f; + SkMatrix44 toXYZ(SkMatrix44::kIdentity_Constructor); + sk_sp<SkColorSpace> rgb4 = SkColorSpace::MakeRGB(fn, toXYZ); + + REPORTER_ASSERT(r, SkColorSpace::Equals(nullptr, nullptr)); + REPORTER_ASSERT(r, SkColorSpace::Equals(srgb.get(), srgb.get())); + REPORTER_ASSERT(r, SkColorSpace::Equals(adobe.get(), adobe.get())); + REPORTER_ASSERT(r, SkColorSpace::Equals(z30.get(), z30.get())); + REPORTER_ASSERT(r, SkColorSpace::Equals(z32.get(), z32.get())); + REPORTER_ASSERT(r, SkColorSpace::Equals(upperLeft.get(), upperLeft.get())); + REPORTER_ASSERT(r, SkColorSpace::Equals(upperRight.get(), upperRight.get())); + REPORTER_ASSERT(r, SkColorSpace::Equals(rgb4.get(), rgb4.get())); + + REPORTER_ASSERT(r, !SkColorSpace::Equals(nullptr, srgb.get())); + REPORTER_ASSERT(r, !SkColorSpace::Equals(srgb.get(), nullptr)); + REPORTER_ASSERT(r, !SkColorSpace::Equals(adobe.get(), srgb.get())); + REPORTER_ASSERT(r, !SkColorSpace::Equals(z30.get(), srgb.get())); + REPORTER_ASSERT(r, !SkColorSpace::Equals(z32.get(), z30.get())); + REPORTER_ASSERT(r, !SkColorSpace::Equals(upperLeft.get(), srgb.get())); + REPORTER_ASSERT(r, !SkColorSpace::Equals(upperLeft.get(), upperRight.get())); + REPORTER_ASSERT(r, !SkColorSpace::Equals(z30.get(), upperRight.get())); + REPORTER_ASSERT(r, !SkColorSpace::Equals(upperRight.get(), adobe.get())); + REPORTER_ASSERT(r, !SkColorSpace::Equals(z30.get(), rgb4.get())); + REPORTER_ASSERT(r, !SkColorSpace::Equals(srgb.get(), rgb4.get())); +} + +static inline bool matrix_almost_equal(const SkMatrix44& a, const SkMatrix44& b) { + return almost_equal(a.get(0, 0), b.get(0, 0)) && + almost_equal(a.get(0, 1), b.get(0, 1)) && + almost_equal(a.get(0, 2), b.get(0, 2)) && + almost_equal(a.get(0, 3), b.get(0, 3)) && + almost_equal(a.get(1, 0), b.get(1, 0)) && + almost_equal(a.get(1, 1), b.get(1, 1)) && + almost_equal(a.get(1, 2), b.get(1, 2)) && + almost_equal(a.get(1, 3), b.get(1, 3)) && + almost_equal(a.get(2, 0), b.get(2, 0)) && + almost_equal(a.get(2, 1), b.get(2, 1)) && + almost_equal(a.get(2, 2), b.get(2, 2)) && + almost_equal(a.get(2, 3), b.get(2, 3)) && + almost_equal(a.get(3, 0), b.get(3, 0)) && + almost_equal(a.get(3, 1), b.get(3, 1)) && + almost_equal(a.get(3, 2), b.get(3, 2)) && + almost_equal(a.get(3, 3), b.get(3, 3)); +} + +static inline void check_primaries(skiatest::Reporter* r, const SkColorSpacePrimaries& primaries, + const SkMatrix44& reference) { + SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor); + bool result = primaries.toXYZD50(&toXYZ); + REPORTER_ASSERT(r, result); + REPORTER_ASSERT(r, matrix_almost_equal(toXYZ, reference)); +} + +DEF_TEST(ColorSpace_Primaries, r) { + // sRGB primaries (D65) + SkColorSpacePrimaries srgb; + srgb.fRX = 0.64f; + srgb.fRY = 0.33f; + srgb.fGX = 0.30f; + srgb.fGY = 0.60f; + srgb.fBX = 0.15f; + srgb.fBY = 0.06f; + srgb.fWX = 0.3127f; + srgb.fWY = 0.3290f; + SkMatrix44 srgbToXYZ(SkMatrix44::kUninitialized_Constructor); + bool result = srgb.toXYZD50(&srgbToXYZ); + REPORTER_ASSERT(r, result); + + sk_sp<SkColorSpace> space = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, + srgbToXYZ); + REPORTER_ASSERT(r, SkColorSpace::MakeSRGB() == space); + + // AdobeRGB primaries (D65) + SkColorSpacePrimaries adobe; + adobe.fRX = 0.64f; + adobe.fRY = 0.33f; + adobe.fGX = 0.21f; + adobe.fGY = 0.71f; + adobe.fBX = 0.15f; + adobe.fBY = 0.06f; + adobe.fWX = 0.3127f; + adobe.fWY = 0.3290f; + SkMatrix44 adobeToXYZ(SkMatrix44::kUninitialized_Constructor); + result = adobe.toXYZD50(&adobeToXYZ); + REPORTER_ASSERT(r, result); + + SkColorSpaceTransferFn fn; + fn.fA = 1.0f; + fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f; + fn.fG = 2.2f; + space = SkColorSpace::MakeRGB(fn, adobeToXYZ); + REPORTER_ASSERT(r, SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named) == space); + + // ProPhoto (D50) + SkColorSpacePrimaries proPhoto; + proPhoto.fRX = 0.7347f; + proPhoto.fRY = 0.2653f; + proPhoto.fGX = 0.1596f; + proPhoto.fGY = 0.8404f; + proPhoto.fBX = 0.0366f; + proPhoto.fBY = 0.0001f; + proPhoto.fWX = 0.34567f; + proPhoto.fWY = 0.35850f; + SkMatrix44 proToXYZ(SkMatrix44::kUninitialized_Constructor); + proToXYZ.set3x3(0.7976749f, 0.2880402f, 0.0000000f, + 0.1351917f, 0.7118741f, 0.0000000f, + 0.0313534f, 0.0000857f, 0.8252100f); + check_primaries(r, proPhoto, proToXYZ); + + // NTSC (C) + SkColorSpacePrimaries ntsc; + ntsc.fRX = 0.67f; + ntsc.fRY = 0.33f; + ntsc.fGX = 0.21f; + ntsc.fGY = 0.71f; + ntsc.fBX = 0.14f; + ntsc.fBY = 0.08f; + ntsc.fWX = 0.31006f; + ntsc.fWY = 0.31616f; + SkMatrix44 ntscToXYZ(SkMatrix44::kUninitialized_Constructor); + ntscToXYZ.set3x3(0.6343706f, 0.3109496f, -0.0011817f, + 0.1852204f, 0.5915984f, 0.0555518f, + 0.1446290f, 0.0974520f, 0.7708399f); + check_primaries(r, ntsc, ntscToXYZ); + + // DCI P3 (D65) + SkColorSpacePrimaries p3; + p3.fRX = 0.680f; + p3.fRY = 0.320f; + p3.fGX = 0.265f; + p3.fGY = 0.690f; + p3.fBX = 0.150f; + p3.fBY = 0.060f; + p3.fWX = 0.3127f; + p3.fWY = 0.3290f; + space = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, + SkColorSpace::kDCIP3_D65_Gamut); + SkMatrix44 reference(SkMatrix44::kUninitialized_Constructor); + SkAssertResult(space->toXYZD50(&reference)); + check_primaries(r, p3, reference); + + // Rec 2020 (D65) + SkColorSpacePrimaries rec2020; + rec2020.fRX = 0.708f; + rec2020.fRY = 0.292f; + rec2020.fGX = 0.170f; + rec2020.fGY = 0.797f; + rec2020.fBX = 0.131f; + rec2020.fBY = 0.046f; + rec2020.fWX = 0.3127f; + rec2020.fWY = 0.3290f; + space = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, + SkColorSpace::kRec2020_Gamut); + SkAssertResult(space->toXYZD50(&reference)); + check_primaries(r, rec2020, reference); +} + +DEF_TEST(ColorSpace_InvalidICC, r) { + // This color space has a matrix that is not D50. + sk_sp<SkData> data = GetResourceAsData("icc_profiles/SM2333SW.icc"); + if (!data) { + return; + } + sk_sp<SkColorSpace> cs = SkColorSpace::MakeICC(data->data(), data->size()); + REPORTER_ASSERT(r, !cs); + + // The color space has a color lut with only one entry in each dimension. + data = GetResourceAsData("icc_profiles/invalid_color_lut.icc"); + if (!data) { + return; + } + + cs = SkColorSpace::MakeICC(data->data(), data->size()); + REPORTER_ASSERT(r, !cs); +} + +DEF_TEST(ColorSpace_MatrixHash, r) { + sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB(); + + SkColorSpaceTransferFn fn; + fn.fA = 1.0f; + fn.fB = 0.0f; + fn.fC = 0.0f; + fn.fD = 0.0f; + fn.fE = 0.0f; + fn.fF = 0.0f; + fn.fG = 3.0f; + + SkMatrix44 srgbMat(SkMatrix44::kUninitialized_Constructor); + srgbMat.set3x3RowMajorf(gSRGB_toXYZD50); + sk_sp<SkColorSpace> strange = SkColorSpace::MakeRGB(fn, srgbMat); + + REPORTER_ASSERT(r, *as_CSB(srgb)->toXYZD50() == *as_CSB(strange)->toXYZD50()); + REPORTER_ASSERT(r, as_CSB(srgb)->toXYZD50Hash() == as_CSB(strange)->toXYZD50Hash()); +} + +DEF_TEST(ColorSpace_IsSRGB, r) { + sk_sp<SkColorSpace> srgb0 = SkColorSpace::MakeSRGB(); + + SkColorSpaceTransferFn fn; + fn.fA = 1.0f; + fn.fB = 0.0f; + fn.fC = 0.0f; + fn.fD = 0.0f; + fn.fE = 0.0f; + fn.fF = 0.0f; + fn.fG = 2.2f; + sk_sp<SkColorSpace> twoDotTwo = SkColorSpace::MakeRGB(fn, SkColorSpace::kSRGB_Gamut); + + REPORTER_ASSERT(r, srgb0->isSRGB()); + REPORTER_ASSERT(r, !twoDotTwo->isSRGB()); +}
diff --git a/src/third_party/skia/tests/ColorSpaceXformTest.cpp b/src/third_party/skia/tests/ColorSpaceXformTest.cpp new file mode 100644 index 0000000..83317d9 --- /dev/null +++ b/src/third_party/skia/tests/ColorSpaceXformTest.cpp
@@ -0,0 +1,346 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Resources.h" +#include "SkCodec.h" +#include "SkCodecPriv.h" +#include "SkColorPriv.h" +#include "SkColorSpace.h" +#include "SkColorSpace_A2B.h" +#include "SkColorSpace_Base.h" +#include "SkColorSpace_XYZ.h" +#include "SkColorSpaceXform_Base.h" +#include "Test.h" + +static constexpr int kChannels = 3; + +class ColorSpaceXformTest { +public: + static std::unique_ptr<SkColorSpaceXform> CreateIdentityXform(const sk_sp<SkGammas>& gammas) { + // Logically we can pass any matrix here. For simplicty, pass I(), i.e. D50 XYZ gamut. + sk_sp<SkColorSpace> space(new SkColorSpace_XYZ( + kNonStandard_SkGammaNamed, gammas, SkMatrix::I(), nullptr)); + + // Use special testing entry point, so we don't skip the xform, even though src == dst. + return SlowIdentityXform(static_cast<SkColorSpace_XYZ*>(space.get())); + } + + static std::unique_ptr<SkColorSpaceXform> CreateIdentityXform_A2B( + SkGammaNamed gammaNamed, const sk_sp<SkGammas>& gammas) { + std::vector<SkColorSpace_A2B::Element> srcElements; + // sRGB + const float values[16] = { + 0.4358f, 0.3853f, 0.1430f, 0.0f, + 0.2224f, 0.7170f, 0.0606f, 0.0f, + 0.0139f, 0.0971f, 0.7139f, 0.0f, + 0.0000f, 0.0000f, 0.0000f, 1.0f + }; + SkMatrix44 arbitraryMatrix{SkMatrix44::kUninitialized_Constructor}; + arbitraryMatrix.setRowMajorf(values); + if (kNonStandard_SkGammaNamed == gammaNamed) { + SkASSERT(gammas); + srcElements.push_back(SkColorSpace_A2B::Element(gammas)); + } else { + srcElements.push_back(SkColorSpace_A2B::Element(gammaNamed, kChannels)); + } + srcElements.push_back(SkColorSpace_A2B::Element(arbitraryMatrix)); + auto srcSpace = + ColorSpaceXformTest::CreateA2BSpace(SkColorSpace_A2B::PCS::kXYZ, + SkColorSpace_Base::kRGB_ICCTypeFlag, + std::move(srcElements)); + sk_sp<SkColorSpace> dstSpace(new SkColorSpace_XYZ(gammaNamed, gammas, arbitraryMatrix, + nullptr)); + + return SkColorSpaceXform::New(static_cast<SkColorSpace_A2B*>(srcSpace.get()), + static_cast<SkColorSpace_XYZ*>(dstSpace.get())); + } + + static sk_sp<SkColorSpace> CreateA2BSpace(SkColorSpace_A2B::PCS pcs, + SkColorSpace_Base::ICCTypeFlag iccType, + std::vector<SkColorSpace_A2B::Element> elements) { + return sk_sp<SkColorSpace>(new SkColorSpace_A2B(iccType, std::move(elements), + pcs, nullptr)); + } +}; + +static bool almost_equal(int x, int y, int tol=1) { + return SkTAbs(x-y) <= tol; +} + +static void test_identity_xform(skiatest::Reporter* r, const sk_sp<SkGammas>& gammas, + bool repeat) { + // Arbitrary set of 10 pixels + constexpr int width = 10; + constexpr uint32_t srcPixels[width] = { + 0xFFABCDEF, 0xFF146829, 0xFF382759, 0xFF184968, 0xFFDE8271, + 0xFF32AB52, 0xFF0383BC, 0xFF000102, 0xFFFFFFFF, 0xFFDDEEFF, }; + uint32_t dstPixels[width]; + + // Create and perform an identity xform. + std::unique_ptr<SkColorSpaceXform> xform = ColorSpaceXformTest::CreateIdentityXform(gammas); + bool result = xform->apply(select_xform_format(kN32_SkColorType), dstPixels, + SkColorSpaceXform::kBGRA_8888_ColorFormat, srcPixels, width, + kOpaque_SkAlphaType); + REPORTER_ASSERT(r, result); + + // Since the src->dst matrix is the identity, and the gamma curves match, + // the pixels should be unchanged. + for (int i = 0; i < width; i++) { + REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 0) & 0xFF), + SkGetPackedB32(dstPixels[i]))); + REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 8) & 0xFF), + SkGetPackedG32(dstPixels[i]))); + REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 16) & 0xFF), + SkGetPackedR32(dstPixels[i]))); + REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 24) & 0xFF), + SkGetPackedA32(dstPixels[i]))); + } + + if (repeat) { + // We should cache part of the transform after the run. So it is interesting + // to make sure it still runs correctly the second time. + test_identity_xform(r, gammas, false); + } +} + +static void test_identity_xform_A2B(skiatest::Reporter* r, SkGammaNamed gammaNamed, + const sk_sp<SkGammas>& gammas, int tol=1) { + // Arbitrary set of 10 pixels + constexpr int width = 10; + constexpr uint32_t srcPixels[width] = { + 0xFFABCDEF, 0xFF146829, 0xFF382759, 0xFF184968, 0xFFDE8271, + 0xFF32AB52, 0xFF0383BC, 0xFF000102, 0xFFFFFFFF, 0xFFDDEEFF, }; + uint32_t dstPixels[width]; + + // Create and perform an identity xform. + auto xform = ColorSpaceXformTest::CreateIdentityXform_A2B(gammaNamed, gammas); + bool result = xform->apply(select_xform_format(kN32_SkColorType), dstPixels, + SkColorSpaceXform::kBGRA_8888_ColorFormat, srcPixels, width, + kOpaque_SkAlphaType); + REPORTER_ASSERT(r, result); + + // Since the src->dst matrix is the identity, and the gamma curves match, + // the pixels should be ~unchanged. + for (int i = 0; i < width; i++) { + REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 0) & 0xFF), + SkGetPackedB32(dstPixels[i]), tol)); + REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 8) & 0xFF), + SkGetPackedG32(dstPixels[i]), tol)); + REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 16) & 0xFF), + SkGetPackedR32(dstPixels[i]), tol)); + REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 24) & 0xFF), + SkGetPackedA32(dstPixels[i]), tol)); + } +} + +DEF_TEST(ColorSpaceXform_TableGamma, r) { + // Lookup-table based gamma curves + constexpr size_t tableSize = 10; + void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(float) * tableSize); + sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(kChannels)); + for (int i = 0; i < kChannels; ++i) { + gammas->fType[i] = SkGammas::Type::kTable_Type; + gammas->fData[i].fTable.fSize = tableSize; + gammas->fData[i].fTable.fOffset = 0; + } + + float* table = SkTAddOffset<float>(memory, sizeof(SkGammas)); + table[0] = 0.00f; + table[1] = 0.05f; + table[2] = 0.10f; + table[3] = 0.15f; + table[4] = 0.25f; + table[5] = 0.35f; + table[6] = 0.45f; + table[7] = 0.60f; + table[8] = 0.75f; + table[9] = 1.00f; + // This table's pretty small compared to real ones in the wild (think 256), + // so we give test_identity_xform_A2B a wide tolerance. + // This lets us implement table transfer functions with a single lookup. + const int tolerance = 13; + + test_identity_xform(r, gammas, true); + test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas, tolerance); +} + +DEF_TEST(ColorSpaceXform_ParametricGamma, r) { + // Parametric gamma curves + void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(SkColorSpaceTransferFn)); + sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(kChannels)); + for (int i = 0; i < kChannels; ++i) { + gammas->fType[i] = SkGammas::Type::kParam_Type; + gammas->fData[i].fParamOffset = 0; + } + + SkColorSpaceTransferFn* params = SkTAddOffset<SkColorSpaceTransferFn> + (memory, sizeof(SkGammas)); + + // Interval. + params->fD = 0.04045f; + + // First equation: + params->fC = 1.0f / 12.92f; + params->fF = 0.0f; + + // Second equation: + // Note that the function is continuous (it's actually sRGB). + params->fA = 1.0f / 1.055f; + params->fB = 0.055f / 1.055f; + params->fE = 0.0f; + params->fG = 2.4f; + test_identity_xform(r, gammas, true); + test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas); +} + +DEF_TEST(ColorSpaceXform_ExponentialGamma, r) { + // Exponential gamma curves + sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new SkGammas(kChannels)); + for (int i = 0; i < kChannels; ++i) { + gammas->fType[i] = SkGammas::Type::kValue_Type; + gammas->fData[i].fValue = 1.4f; + } + test_identity_xform(r, gammas, true); + test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas); +} + +DEF_TEST(ColorSpaceXform_NamedGamma, r) { + sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new SkGammas(kChannels)); + gammas->fType[0] = gammas->fType[1] = gammas->fType[2] = SkGammas::Type::kNamed_Type; + gammas->fData[0].fNamed = kSRGB_SkGammaNamed; + gammas->fData[1].fNamed = k2Dot2Curve_SkGammaNamed; + gammas->fData[2].fNamed = kLinear_SkGammaNamed; + test_identity_xform(r, gammas, true); + test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas); + test_identity_xform_A2B(r, kSRGB_SkGammaNamed, nullptr); + test_identity_xform_A2B(r, k2Dot2Curve_SkGammaNamed, nullptr); + test_identity_xform_A2B(r, kLinear_SkGammaNamed, nullptr); +} + +DEF_TEST(ColorSpaceXform_NonMatchingGamma, r) { + constexpr size_t tableSize = 10; + void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(float) * tableSize + + sizeof(SkColorSpaceTransferFn)); + sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(kChannels)); + + float* table = SkTAddOffset<float>(memory, sizeof(SkGammas)); + table[0] = 0.00f; + table[1] = 0.15f; + table[2] = 0.20f; + table[3] = 0.25f; + table[4] = 0.35f; + table[5] = 0.45f; + table[6] = 0.55f; + table[7] = 0.70f; + table[8] = 0.85f; + table[9] = 1.00f; + + SkColorSpaceTransferFn* params = SkTAddOffset<SkColorSpaceTransferFn>(memory, + sizeof(SkGammas) + sizeof(float) * tableSize); + params->fA = 1.0f / 1.055f; + params->fB = 0.055f / 1.055f; + params->fC = 1.0f / 12.92f; + params->fD = 0.04045f; + params->fE = 0.0f; + params->fF = 0.0f; + params->fG = 2.4f; + + gammas->fType[0] = SkGammas::Type::kValue_Type; + gammas->fData[0].fValue = 1.2f; + + // See ColorSpaceXform_TableGamma... we've decided to allow some tolerance + // for SkJumper's implementation of tables. + const int tolerance = 12; + gammas->fType[1] = SkGammas::Type::kTable_Type; + gammas->fData[1].fTable.fSize = tableSize; + gammas->fData[1].fTable.fOffset = 0; + + gammas->fType[2] = SkGammas::Type::kParam_Type; + gammas->fData[2].fParamOffset = sizeof(float) * tableSize; + + test_identity_xform(r, gammas, true); + test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas, tolerance); +} + +DEF_TEST(ColorSpaceXform_A2BCLUT, r) { + constexpr int inputChannels = 3; + constexpr int gp = 4; // # grid points + + constexpr int numEntries = gp*gp*gp*3; + const uint8_t gridPoints[3] = {gp, gp, gp}; + void* memory = sk_malloc_throw(sizeof(SkColorLookUpTable) + sizeof(float) * numEntries); + sk_sp<SkColorLookUpTable> colorLUT(new (memory) SkColorLookUpTable(inputChannels, gridPoints)); + // make a CLUT that rotates R, G, and B ie R->G, G->B, B->R + float* table = SkTAddOffset<float>(memory, sizeof(SkColorLookUpTable)); + for (int r = 0; r < gp; ++r) { + for (int g = 0; g < gp; ++g) { + for (int b = 0; b < gp; ++b) { + table[3*(gp*gp*r + gp*g + b) + 0] = g * (1.f / (gp - 1.f)); + table[3*(gp*gp*r + gp*g + b) + 1] = b * (1.f / (gp - 1.f)); + table[3*(gp*gp*r + gp*g + b) + 2] = r * (1.f / (gp - 1.f)); + } + } + } + + // build an even distribution of pixels every (7 / 255) steps + // to test the xform on + constexpr int pixelgp = 7; + constexpr int numPixels = pixelgp*pixelgp*pixelgp; + SkAutoTMalloc<uint32_t> srcPixels(numPixels); + int srcIndex = 0; + for (int r = 0; r < pixelgp; ++r) { + for (int g = 0; g < pixelgp; ++g) { + for (int b = 0; b < pixelgp; ++b) { + const int red = (int) (r * (255.f / (pixelgp - 1.f))); + const int green = (int) (g * (255.f / (pixelgp - 1.f))); + const int blue = (int) (b * (255.f / (pixelgp - 1.f))); + srcPixels[srcIndex] = SkColorSetRGB(red, green, blue); + ++srcIndex; + } + } + } + SkAutoTMalloc<uint32_t> dstPixels(numPixels); + + // src space is identity besides CLUT + std::vector<SkColorSpace_A2B::Element> srcElements; + srcElements.push_back(SkColorSpace_A2B::Element(std::move(colorLUT))); + auto srcSpace = ColorSpaceXformTest::CreateA2BSpace(SkColorSpace_A2B::PCS::kXYZ, + SkColorSpace_Base::kRGB_ICCTypeFlag, + std::move(srcElements)); + // dst space is entirely identity + auto dstSpace = SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, SkMatrix44::I()); + auto xform = SkColorSpaceXform::New(srcSpace.get(), dstSpace.get()); + bool result = xform->apply(SkColorSpaceXform::kRGBA_8888_ColorFormat, dstPixels.get(), + SkColorSpaceXform::kRGBA_8888_ColorFormat, srcPixels.get(), + numPixels, kOpaque_SkAlphaType); + REPORTER_ASSERT(r, result); + + for (int i = 0; i < numPixels; ++i) { + REPORTER_ASSERT(r, almost_equal(SkColorGetR(srcPixels[i]), + SkColorGetG(dstPixels[i]))); + REPORTER_ASSERT(r, almost_equal(SkColorGetG(srcPixels[i]), + SkColorGetB(dstPixels[i]))); + REPORTER_ASSERT(r, almost_equal(SkColorGetB(srcPixels[i]), + SkColorGetR(dstPixels[i]))); + } +} + +DEF_TEST(SkColorSpaceXform_LoadTail, r) { + std::unique_ptr<uint64_t[]> srcPixel(new uint64_t[1]); + srcPixel[0] = 0; + uint32_t dstPixel; + sk_sp<SkColorSpace> adobe = SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named); + sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB(); + std::unique_ptr<SkColorSpaceXform> xform = SkColorSpaceXform::New(adobe.get(), srgb.get()); + + // ASAN will catch us if we read past the tail. + bool success = xform->apply(SkColorSpaceXform::kRGBA_8888_ColorFormat, &dstPixel, + SkColorSpaceXform::kRGBA_U16_BE_ColorFormat, srcPixel.get(), 1, + kUnpremul_SkAlphaType); + REPORTER_ASSERT(r, success); +} +
diff --git a/src/third_party/skia/tests/CopySurfaceTest.cpp b/src/third_party/skia/tests/CopySurfaceTest.cpp new file mode 100644 index 0000000..8dfca0c --- /dev/null +++ b/src/third_party/skia/tests/CopySurfaceTest.cpp
@@ -0,0 +1,175 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <initializer_list> +#include "Test.h" + +#if SK_SUPPORT_GPU +#include "GrContext.h" +#include "GrContextPriv.h" +#include "GrResourceProvider.h" +#include "GrSurfaceContext.h" +#include "GrSurfaceProxy.h" +#include "GrTextureProxy.h" + +#include "SkUtils.h" + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(CopySurface, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + static const int kW = 10; + static const int kH = 10; + static const size_t kRowBytes = sizeof(uint32_t) * kW; + + GrSurfaceDesc baseDesc; + baseDesc.fConfig = kRGBA_8888_GrPixelConfig; + baseDesc.fWidth = kW; + baseDesc.fHeight = kH; + + SkAutoTMalloc<uint32_t> srcPixels(kW * kH); + for (int i = 0; i < kW * kH; ++i) { + srcPixels.get()[i] = i; + } + + SkAutoTMalloc<uint32_t> dstPixels(kW * kH); + for (int i = 0; i < kW * kH; ++i) { + dstPixels.get()[i] = ~i; + } + + static const SkIRect kSrcRects[] { + { 0, 0, kW , kH }, + {-1, -1, kW+1, kH+1}, + { 1, 1, kW-1, kH-1}, + { 5, 5, 6 , 6 }, + }; + + static const SkIPoint kDstPoints[] { + { 0 , 0 }, + { 1 , 1 }, + { kW/2, kH/4}, + { kW-1, kH-1}, + { kW , kH }, + { kW+1, kH+2}, + {-1 , -1 }, + }; + + const SkImageInfo ii = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType); + + SkAutoTMalloc<uint32_t> read(kW * kH); + + for (auto sOrigin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) { + for (auto dOrigin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) { + for (auto sFlags: {kRenderTarget_GrSurfaceFlag, kNone_GrSurfaceFlags}) { + for (auto dFlags: {kRenderTarget_GrSurfaceFlag, kNone_GrSurfaceFlags}) { + for (auto srcRect : kSrcRects) { + for (auto dstPoint : kDstPoints) { + GrSurfaceDesc srcDesc = baseDesc; + srcDesc.fOrigin = sOrigin; + srcDesc.fFlags = sFlags; + GrSurfaceDesc dstDesc = baseDesc; + dstDesc.fOrigin = dOrigin; + dstDesc.fFlags = dFlags; + + sk_sp<GrTextureProxy> src(GrSurfaceProxy::MakeDeferred( + context->resourceProvider(), + srcDesc, SkBudgeted::kNo, + srcPixels.get(), + kRowBytes)); + + sk_sp<GrTextureProxy> dst(GrSurfaceProxy::MakeDeferred( + context->resourceProvider(), + dstDesc, SkBudgeted::kNo, + dstPixels.get(), + kRowBytes)); + if (!src || !dst) { + ERRORF(reporter, + "Could not create surfaces for copy surface test."); + continue; + } + + sk_sp<GrSurfaceContext> dstContext = + context->contextPriv().makeWrappedSurfaceContext(std::move(dst), + nullptr); + + bool result = dstContext->copy(src.get(), srcRect, dstPoint); + + bool expectedResult = true; + SkIPoint dstOffset = { dstPoint.fX - srcRect.fLeft, + dstPoint.fY - srcRect.fTop }; + SkIRect copiedDstRect = SkIRect::MakeXYWH(dstPoint.fX, + dstPoint.fY, + srcRect.width(), + srcRect.height()); + + SkIRect copiedSrcRect; + if (!copiedSrcRect.intersect(srcRect, SkIRect::MakeWH(kW, kH))) { + expectedResult = false; + } else { + // If the src rect was clipped, apply same clipping to each side of + // copied dst rect. + copiedDstRect.fLeft += copiedSrcRect.fLeft - srcRect.fLeft; + copiedDstRect.fTop += copiedSrcRect.fTop - srcRect.fTop; + copiedDstRect.fRight -= copiedSrcRect.fRight - srcRect.fRight; + copiedDstRect.fBottom -= copiedSrcRect.fBottom - srcRect.fBottom; + } + if (copiedDstRect.isEmpty() || + !copiedDstRect.intersect(SkIRect::MakeWH(kW, kH))) { + expectedResult = false; + } + // To make the copied src rect correct we would apply any dst clipping + // back to the src rect, but we don't use it again so don't bother. + if (expectedResult != result) { + ERRORF(reporter, "Expected return value %d from copySurface, got " + "%d.", expectedResult, result); + continue; + } + + if (!expectedResult || !result) { + continue; + } + + sk_memset32(read.get(), 0, kW * kH); + if (!dstContext->readPixels(ii, read.get(), kRowBytes, 0, 0)) { + ERRORF(reporter, "Error calling readPixels"); + continue; + } + + bool abort = false; + // Validate that pixels inside copiedDstRect received the correct value + // from src and that those outside were not modified. + for (int y = 0; y < kH && !abort; ++y) { + for (int x = 0; x < kW; ++x) { + uint32_t r = read.get()[y * kW + x]; + if (copiedDstRect.contains(x, y)) { + int sx = x - dstOffset.fX; + int sy = y - dstOffset.fY; + uint32_t s = srcPixels.get()[sy * kW + sx]; + if (s != r) { + ERRORF(reporter, "Expected dst %d,%d to contain " + "0x%08x copied from src location %d,%d. Got " + "0x%08x", x, y, s, sx, sy, r); + abort = true; + break; + } + } else { + uint32_t d = dstPixels.get()[y * kW + x]; + if (d != r) { + ERRORF(reporter, "Expected dst %d,%d to be unmodified (" + "0x%08x). Got 0x%08x", x, y, d, r); + abort = true; + break; + } + } + } + } + } + } + } + } + } + } +} +#endif
diff --git a/src/third_party/skia/tests/DFPathRendererTest.cpp b/src/third_party/skia/tests/DFPathRendererTest.cpp new file mode 100644 index 0000000..653db82 --- /dev/null +++ b/src/third_party/skia/tests/DFPathRendererTest.cpp
@@ -0,0 +1,84 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#include "SkPath.h" + +#if SK_SUPPORT_GPU +#include "GrContext.h" +#include "ops/GrSmallPathRenderer.h" + +#if 0 +// This test case including path coords and matrix taken from crbug.com/627443. +// Because of inaccuracies in large floating point values this causes the +// the path renderer to attempt to add a path DF to its atlas that is larger +// than the plot size which used to crash rather than fail gracefully. +static void test_far_from_origin(GrContext* ctx, GrRenderTargetContext* renderTargetContext, + GrPathRenderer* pr) { + SkPath path; + path.lineTo(49.0255089839f, 0.473541f); + // This extra line wasn't in the original bug but was added to fake out GrShape's special + // handling of single line segments. + path.rLineTo(0.015f, 0.015f); + static constexpr SkScalar mvals[] = {14.0348252854f, 2.13026182736f, + 13.6122547187f, 118.309922702f, + 1912337682.09f, 2105391889.87f}; + SkMatrix matrix; + matrix.setAffine(mvals); + SkMatrix inverse; + SkAssertResult(matrix.invert(&inverse)); + path.transform(inverse); + + SkStrokeRec rec(SkStrokeRec::kFill_InitStyle); + rec.setStrokeStyle(1.f); + rec.setStrokeParams(SkPaint::kRound_Cap, SkPaint::kRound_Join, 1.f); + GrStyle style(rec, nullptr); + + GrShape shape(path, style); + shape = shape.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, 1.f); + + GrPaint paint; + paint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc)); + + GrNoClip noClip; + GrPathRenderer::DrawPathArgs args{ctx, + std::move(paint), + &GrUserStencilSettings::kUnused, + renderTargetContext, + &noClip, + &matrix, + &shape, + GrAAType::kCoverage, + false}; + pr->drawPath(args); +} + +DEF_GPUTEST_FOR_ALL_GL_CONTEXTS(SmallPathRenderer, reporter, ctxInfo) { + GrContext* ctx = ctxInfo.grContext(); + // The DF PR only works with contexts that support derivatives + if (!ctx->caps()->shaderCaps()->shaderDerivativeSupport()) { + return; + } + sk_sp<GrRenderTargetContext> rtc(ctx->makeRenderTargetContext(SkBackingFit::kApprox, + 800, 800, + kRGBA_8888_GrPixelConfig, + nullptr, + 0, + kTopLeft_GrSurfaceOrigin)); + if (!rtc) { + return; + } + + GrSmallPathRenderer spr; + + ctx->flush(); + test_far_from_origin(ctx, rtc.get(), &spr); + ctx->flush(); +} +#endif +#endif
diff --git a/src/third_party/skia/tests/DashPathEffectTest.cpp b/src/third_party/skia/tests/DashPathEffectTest.cpp index 1e53058..e3a3804 100644 --- a/src/third_party/skia/tests/DashPathEffectTest.cpp +++ b/src/third_party/skia/tests/DashPathEffectTest.cpp
@@ -1,21 +1,117 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + #include "Test.h" #include "SkDashPathEffect.h" #include "SkWriteBuffer.h" +#include "SkStrokeRec.h" +#include "SkCanvas.h" +#include "SkSurface.h" // crbug.com/348821 was rooted in SkDashPathEffect refusing to flatten and unflatten itself when -// fInitialDashLength < 0 (a signal the effect is nonsense). Here we test that it flattens. +// the effect is nonsense. Here we test that it fails when passed nonsense parameters. DEF_TEST(DashPathEffectTest_crbug_348821, r) { SkScalar intervals[] = { 1.76934361e+36f, 2.80259693e-45f }; // Values from bug. const int count = 2; - SkScalar phase = SK_ScalarInfinity; // Used to force the bad fInitialDashLength = -1 path. - SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, count, phase)); + SkScalar phase = SK_ScalarInfinity; // Used to force a nonsense effect. + sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, count, phase)); - // NULL -> refuses to work with flattening framework. - REPORTER_ASSERT(r, dash->getFactory() != NULL); + REPORTER_ASSERT(r, dash == nullptr); +} - SkWriteBuffer buffer; - buffer.writeFlattenable(dash); - REPORTER_ASSERT(r, buffer.bytesWritten() > 12); // We'd write 12 if broken, >=40 if not. +// Test out the asPoint culling behavior. +DEF_TEST(DashPathEffectTest_asPoints, r) { + + const SkScalar intervals[] = { 1.0f, 1.0f }; + const int count = 2; + sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, count, 0.0f)); + + SkRect cull = SkRect::MakeWH(1.0f, 1.0f); + + const struct { + SkPoint fPts[2]; + bool fExpectedResult; + } testCases[] = { + { { { -5.0f, 0.5f }, { -4.0f, 0.5f } }, false }, // off to the left + { { { 4.0f, 0.5f }, { 5.0f, 0.5f } }, false }, // off to the right + { { { 0.5f, 4.0f }, { 0.5f, 5.0f } }, false }, // off the bottom + { { { 0.5f, -5.0f }, { 0.5f, -4.0f } }, false }, // off the top + { { { 0.5f, 0.2f }, { 0.5f, 0.8f } }, true }, // entirely inside vertical + { { { 0.2f, 0.5f }, { 0.8f, 0.5f } }, true }, // entirely inside horizontal + { { { 0.5f, -5.0f }, { 0.5f, 5.0f } }, true }, // straddles both sides vertically + { { { -5.0f, 0.5f }, { 5.0f, 0.5f } }, true }, // straddles both sides horizontally + { { { 0.5f, -5.0f }, { 0.5f, 0.5f } }, true }, // straddles top + { { { 0.5f, 5.0f }, { 0.5f, 0.5f } }, true }, // straddles bottom + { { { -5.0f, 0.5f }, { 0.5f, 0.5f } }, true }, // straddles left + { { { 5.0f, 0.5f }, { 0.5f, 0.5f } }, true }, // straddles right + { { { 0.5f, 0.5f }, { 0.5f, 0.5f } }, false }, // zero length + }; + + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(1.0f); + SkStrokeRec rec(paint); + + static const int kNumMats = 3; + SkMatrix mats[kNumMats]; + mats[0].reset(); + mats[1].setRotate(90, 0.5f, 0.5f); + mats[2].setTranslate(10.0f, 10.0f); + + for (int i = 0; i < kNumMats; ++i) { + for (int j = 0; j < (int)SK_ARRAY_COUNT(testCases); ++j) { + for (int k = 0; k < 2; ++k) { // exercise alternating endpoints + SkPathEffect::PointData results; + SkPath src; + + src.moveTo(testCases[j].fPts[k]); + src.lineTo(testCases[j].fPts[(k+1)%2]); + + bool actualResult = dash->asPoints(&results, src, rec, mats[i], &cull); + if (i < 2) { + REPORTER_ASSERT(r, actualResult == testCases[j].fExpectedResult); + } else { + // On the third pass all the lines should be outside the translated cull rect + REPORTER_ASSERT(r, !actualResult); + } + } + } + } +} + +DEF_TEST(DashPath_bug4871, r) { + SkPath path; + path.moveTo(30, 24); + path.cubicTo(30.002f, 24, 30, 24, 30, 24); + path.close(); + + SkScalar intervals[2] = { 1, 1 }; + sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, 2, 0)); + + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setPathEffect(dash); + + SkPath fill; + paint.getFillPath(path, &fill); +} + +// Verify that long lines with many dashes don't cause overflows/OOMs. +DEF_TEST(DashPathEffectTest_asPoints_limit, r) { + sk_sp<SkSurface> surface(SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(256, 256))); + SkCanvas* canvas = surface->getCanvas(); + + SkPaint p; + p.setStyle(SkPaint::kStroke_Style); + // force the bounds to outset by a large amount + p.setStrokeWidth(5.0e10f); + const SkScalar intervals[] = { 1, 1 }; + p.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 0)); + canvas->drawLine(1, 1, 1, 5.0e10f, p); }
diff --git a/src/third_party/skia/tests/DataRefTest.cpp b/src/third_party/skia/tests/DataRefTest.cpp index 6a58c16..74d511c 100644 --- a/src/third_party/skia/tests/DataRefTest.cpp +++ b/src/third_party/skia/tests/DataRefTest.cpp
@@ -8,9 +8,11 @@ #include "SkData.h" #include "SkDataTable.h" #include "SkOSFile.h" +#include "SkOSPath.h" #include "SkReadBuffer.h" #include "SkWriteBuffer.h" #include "SkStream.h" +#include "SkTArray.h" #include "Test.h" static void test_is_equal(skiatest::Reporter* reporter, @@ -25,35 +27,31 @@ } } -static void test_datatable_is_empty(skiatest::Reporter* reporter, - SkDataTable* table) { +static void test_datatable_is_empty(skiatest::Reporter* reporter, SkDataTable* table) { REPORTER_ASSERT(reporter, table->isEmpty()); REPORTER_ASSERT(reporter, 0 == table->count()); } static void test_emptytable(skiatest::Reporter* reporter) { - SkAutoTUnref<SkDataTable> table0(SkDataTable::NewEmpty()); - SkAutoTUnref<SkDataTable> table1(SkDataTable::NewCopyArrays(NULL, NULL, 0)); - SkAutoTUnref<SkDataTable> table2(SkDataTable::NewCopyArray(NULL, 0, 0)); - SkAutoTUnref<SkDataTable> table3(SkDataTable::NewArrayProc(NULL, 0, 0, - NULL, NULL)); + sk_sp<SkDataTable> table0(SkDataTable::MakeEmpty()); + sk_sp<SkDataTable> table1(SkDataTable::MakeCopyArrays(nullptr, nullptr, 0)); + sk_sp<SkDataTable> table2(SkDataTable::MakeCopyArray(nullptr, 0, 0)); + sk_sp<SkDataTable> table3(SkDataTable::MakeArrayProc(nullptr, 0, 0, nullptr, nullptr)); - test_datatable_is_empty(reporter, table0); - test_datatable_is_empty(reporter, table1); - test_datatable_is_empty(reporter, table2); - test_datatable_is_empty(reporter, table3); + test_datatable_is_empty(reporter, table0.get()); + test_datatable_is_empty(reporter, table1.get()); + test_datatable_is_empty(reporter, table2.get()); + test_datatable_is_empty(reporter, table3.get()); - test_is_equal(reporter, table0, table1); - test_is_equal(reporter, table0, table2); - test_is_equal(reporter, table0, table3); + test_is_equal(reporter, table0.get(), table1.get()); + test_is_equal(reporter, table0.get(), table2.get()); + test_is_equal(reporter, table0.get(), table3.get()); } static void test_simpletable(skiatest::Reporter* reporter) { const int idata[] = { 1, 4, 9, 16, 25, 63 }; int icount = SK_ARRAY_COUNT(idata); - SkAutoTUnref<SkDataTable> itable(SkDataTable::NewCopyArray(idata, - sizeof(idata[0]), - icount)); + sk_sp<SkDataTable> itable(SkDataTable::MakeCopyArray(idata, sizeof(idata[0]), icount)); REPORTER_ASSERT(reporter, itable->count() == icount); for (int i = 0; i < icount; ++i) { size_t size; @@ -73,8 +71,7 @@ sizes[i] = strlen(str[i]) + 1; } - SkAutoTUnref<SkDataTable> table(SkDataTable::NewCopyArrays( - (const void*const*)str, sizes, count)); + sk_sp<SkDataTable> table(SkDataTable::MakeCopyArrays((const void*const*)str, sizes, count)); REPORTER_ASSERT(reporter, table->count() == count); for (int i = 0; i < count; ++i) { @@ -89,40 +86,14 @@ } } -static void test_tablebuilder(skiatest::Reporter* reporter) { - const char* str[] = { - "", "a", "be", "see", "deigh", "ef", "ggggggggggggggggggggggggggg" - }; - int count = SK_ARRAY_COUNT(str); - - SkDataTableBuilder builder(16); - - for (int i = 0; i < count; ++i) { - builder.append(str[i], strlen(str[i]) + 1); - } - SkAutoTUnref<SkDataTable> table(builder.detachDataTable()); - - REPORTER_ASSERT(reporter, table->count() == count); - for (int i = 0; i < count; ++i) { - size_t size; - REPORTER_ASSERT(reporter, table->atSize(i) == strlen(str[i]) + 1); - REPORTER_ASSERT(reporter, !strcmp(table->atT<const char>(i, &size), - str[i])); - REPORTER_ASSERT(reporter, size == strlen(str[i]) + 1); - - const char* s = table->atStr(i); - REPORTER_ASSERT(reporter, strlen(s) == strlen(str[i])); - } -} - static void test_globaltable(skiatest::Reporter* reporter) { static const int gData[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; int count = SK_ARRAY_COUNT(gData); - SkAutoTUnref<SkDataTable> table(SkDataTable::NewArrayProc(gData, - sizeof(gData[0]), count, NULL, NULL)); + sk_sp<SkDataTable> table( + SkDataTable::MakeArrayProc(gData, sizeof(gData[0]), count, nullptr, nullptr)); REPORTER_ASSERT(reporter, table->count() == count); for (int i = 0; i < count; ++i) { @@ -137,23 +108,22 @@ test_emptytable(reporter); test_simpletable(reporter); test_vartable(reporter); - test_tablebuilder(reporter); test_globaltable(reporter); } static void* gGlobal; -static void delete_int_proc(const void* ptr, size_t len, void* context) { +static void delete_int_proc(const void* ptr, void* context) { int* data = (int*)ptr; SkASSERT(context == gGlobal); delete[] data; } -static void assert_len(skiatest::Reporter* reporter, SkData* ref, size_t len) { +static void assert_len(skiatest::Reporter* reporter, const sk_sp<SkData>& ref, size_t len) { REPORTER_ASSERT(reporter, ref->size() == len); } -static void assert_data(skiatest::Reporter* reporter, SkData* ref, +static void assert_data(skiatest::Reporter* reporter, const sk_sp<SkData>& ref, const void* data, size_t len) { REPORTER_ASSERT(reporter, ref->size() == len); REPORTER_ASSERT(reporter, !memcmp(ref->data(), data, len)); @@ -163,18 +133,18 @@ const char str[] = "Hello world"; size_t len = strlen(str); - SkAutoTUnref<SkData> r0(SkData::NewWithCopy(str, len + 1)); - SkAutoTUnref<SkData> r1(SkData::NewWithCString(str)); + sk_sp<SkData> r0(SkData::MakeWithCopy(str, len + 1)); + sk_sp<SkData> r1(SkData::MakeWithCString(str)); - REPORTER_ASSERT(reporter, r0->equals(r1)); + REPORTER_ASSERT(reporter, r0->equals(r1.get())); - SkAutoTUnref<SkData> r2(SkData::NewWithCString(NULL)); + sk_sp<SkData> r2(SkData::MakeWithCString(nullptr)); REPORTER_ASSERT(reporter, 1 == r2->size()); REPORTER_ASSERT(reporter, 0 == *r2->bytes()); } static void test_files(skiatest::Reporter* reporter) { - SkString tmpDir = skiatest::Test::GetTmpDir(); + SkString tmpDir = skiatest::GetTmpDir(); if (tmpDir.isEmpty()) { return; } @@ -191,15 +161,15 @@ writer.write(s, 26); } - SkFILE* file = sk_fopen(path.c_str(), kRead_SkFILE_Flag); - SkAutoTUnref<SkData> r1(SkData::NewFromFILE(file)); - REPORTER_ASSERT(reporter, r1.get() != NULL); + FILE* file = sk_fopen(path.c_str(), kRead_SkFILE_Flag); + sk_sp<SkData> r1(SkData::MakeFromFILE(file)); + REPORTER_ASSERT(reporter, r1.get() != nullptr); REPORTER_ASSERT(reporter, r1->size() == 26); REPORTER_ASSERT(reporter, strncmp(static_cast<const char*>(r1->data()), s, 26) == 0); int fd = sk_fileno(file); - SkAutoTUnref<SkData> r2(SkData::NewFromFD(fd)); - REPORTER_ASSERT(reporter, r2.get() != NULL); + sk_sp<SkData> r2(SkData::MakeFromFD(fd)); + REPORTER_ASSERT(reporter, r2.get() != nullptr); REPORTER_ASSERT(reporter, r2->size() == 26); REPORTER_ASSERT(reporter, strncmp(static_cast<const char*>(r2->data()), s, 26) == 0); } @@ -208,11 +178,10 @@ const char* str = "We the people, in order to form a more perfect union."; const int N = 10; - SkAutoTUnref<SkData> r0(SkData::NewEmpty()); - SkAutoTUnref<SkData> r1(SkData::NewWithCopy(str, strlen(str))); - SkAutoTUnref<SkData> r2(SkData::NewWithProc(new int[N], N*sizeof(int), - delete_int_proc, gGlobal)); - SkAutoTUnref<SkData> r3(SkData::NewSubset(r1, 7, 6)); + sk_sp<SkData> r0(SkData::MakeEmpty()); + sk_sp<SkData> r1(SkData::MakeWithCopy(str, strlen(str))); + sk_sp<SkData> r2(SkData::MakeWithProc(new int[N], N*sizeof(int), delete_int_proc, gGlobal)); + sk_sp<SkData> r3(SkData::MakeSubset(r1.get(), 7, 6)); assert_len(reporter, r0, 0); assert_len(reporter, r1, strlen(str)); @@ -222,13 +191,158 @@ assert_data(reporter, r1, str, strlen(str)); assert_data(reporter, r3, "people", 6); - SkData* tmp = SkData::NewSubset(r1, strlen(str), 10); + sk_sp<SkData> tmp(SkData::MakeSubset(r1.get(), strlen(str), 10)); assert_len(reporter, tmp, 0); - tmp->unref(); - tmp = SkData::NewSubset(r1, 0, 0); + tmp = SkData::MakeSubset(r1.get(), 0, 0); assert_len(reporter, tmp, 0); - tmp->unref(); test_cstring(reporter); test_files(reporter); } + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#include "SkRWBuffer.h" + +const char gABC[] = "abcdefghijklmnopqrstuvwxyz"; + +static void check_abcs(skiatest::Reporter* reporter, const char buffer[], size_t size) { + REPORTER_ASSERT(reporter, size % 26 == 0); + for (size_t offset = 0; offset < size; offset += 26) { + REPORTER_ASSERT(reporter, !memcmp(&buffer[offset], gABC, 26)); + } +} + +// stream should contain an integral number of copies of gABC. +static void check_alphabet_stream(skiatest::Reporter* reporter, SkStream* stream) { + REPORTER_ASSERT(reporter, stream->hasLength()); + size_t size = stream->getLength(); + REPORTER_ASSERT(reporter, size % 26 == 0); + + SkAutoTMalloc<char> storage(size); + char* array = storage.get(); + size_t bytesRead = stream->read(array, size); + REPORTER_ASSERT(reporter, bytesRead == size); + check_abcs(reporter, array, size); + + // try checking backwards + for (size_t offset = size; offset > 0; offset -= 26) { + REPORTER_ASSERT(reporter, stream->seek(offset - 26)); + REPORTER_ASSERT(reporter, stream->getPosition() == offset - 26); + REPORTER_ASSERT(reporter, stream->read(array, 26) == 26); + check_abcs(reporter, array, 26); + REPORTER_ASSERT(reporter, stream->getPosition() == offset); + } +} + +// reader should contains an integral number of copies of gABC. +static void check_alphabet_buffer(skiatest::Reporter* reporter, const SkROBuffer* reader) { + size_t size = reader->size(); + REPORTER_ASSERT(reporter, size % 26 == 0); + + SkAutoTMalloc<char> storage(size); + SkROBuffer::Iter iter(reader); + size_t offset = 0; + do { + SkASSERT(offset + iter.size() <= size); + memcpy(storage.get() + offset, iter.data(), iter.size()); + offset += iter.size(); + } while (iter.next()); + REPORTER_ASSERT(reporter, offset == size); + check_abcs(reporter, storage.get(), size); +} + +#include "SkTaskGroup.h" + +DEF_TEST(RWBuffer, reporter) { + // Knowing that the default capacity is 4096, choose N large enough so we force it to use + // multiple buffers internally. + static constexpr int N = 1000; + SkSTArray<N, sk_sp<SkROBuffer>> readers; + SkSTArray<N, std::unique_ptr<SkStream>> streams; + + { + SkRWBuffer buffer; + for (int i = 0; i < N; ++i) { + buffer.append(gABC, 26); + readers.push_back(buffer.makeROBufferSnapshot()); + streams.push_back(buffer.makeStreamSnapshot()); + } + REPORTER_ASSERT(reporter, N*26 == buffer.size()); + } + + // Verify that although the SkRWBuffer's destructor has run, the readers are still valid. + for (int i = 0; i < N; ++i) { + REPORTER_ASSERT(reporter, (i + 1) * 26U == readers[i]->size()); + check_alphabet_buffer(reporter, readers[i].get()); + check_alphabet_stream(reporter, streams[i].get()); + } +} + +DEF_TEST(RWBuffer_threaded, reporter) { + // Knowing that the default capacity is 4096, choose N large enough so we force it to use + // multiple buffers internally. + const int N = 1000; + SkTaskGroup tasks; + SkRWBuffer buffer; + for (int i = 0; i < N; ++i) { + buffer.append(gABC, 26); + sk_sp<SkROBuffer> reader = buffer.makeROBufferSnapshot(); + SkStream* stream = buffer.makeStreamSnapshot().release(); + REPORTER_ASSERT(reporter, reader->size() == buffer.size()); + REPORTER_ASSERT(reporter, stream->getLength() == buffer.size()); + + // reader's copy constructor will ref the SkROBuffer, which will be unreffed + // when the task ends. + // Ownership of stream is passed to the task, which will delete it. + tasks.add([reporter, i, reader, stream] { + REPORTER_ASSERT(reporter, (i + 1) * 26U == reader->size()); + REPORTER_ASSERT(reporter, stream->getLength() == reader->size()); + check_alphabet_buffer(reporter, reader.get()); + check_alphabet_stream(reporter, stream); + REPORTER_ASSERT(reporter, stream->rewind()); + delete stream; + }); + } + REPORTER_ASSERT(reporter, N*26 == buffer.size()); + tasks.wait(); +} + +// Tests that it is safe to call SkROBuffer::Iter::size() when exhausted. +DEF_TEST(RWBuffer_size, r) { + SkRWBuffer buffer; + buffer.append(gABC, 26); + + sk_sp<SkROBuffer> roBuffer(buffer.makeROBufferSnapshot()); + SkROBuffer::Iter iter(roBuffer.get()); + REPORTER_ASSERT(r, iter.data()); + REPORTER_ASSERT(r, iter.size() == 26); + + // There is only one block in this buffer. + REPORTER_ASSERT(r, !iter.next()); + REPORTER_ASSERT(r, 0 == iter.size()); +} + +// Tests that operations (including the destructor) are safe on an SkRWBuffer +// without any data appended. +DEF_TEST(RWBuffer_noAppend, r) { + SkRWBuffer buffer; + REPORTER_ASSERT(r, 0 == buffer.size()); + + sk_sp<SkROBuffer> roBuffer = buffer.makeROBufferSnapshot(); + REPORTER_ASSERT(r, roBuffer); + if (roBuffer) { + REPORTER_ASSERT(r, roBuffer->size() == 0); + SkROBuffer::Iter iter(roBuffer.get()); + REPORTER_ASSERT(r, iter.size() == 0); + REPORTER_ASSERT(r, !iter.data()); + REPORTER_ASSERT(r, !iter.next()); + } + + std::unique_ptr<SkStream> stream(buffer.makeStreamSnapshot()); + REPORTER_ASSERT(r, stream); + if (stream) { + REPORTER_ASSERT(r, stream->hasLength()); + REPORTER_ASSERT(r, stream->getLength() == 0); + REPORTER_ASSERT(r, stream->skip(10) == 0); + } +}
diff --git a/src/third_party/skia/tests/DeferredCanvasTest.cpp b/src/third_party/skia/tests/DeferredCanvasTest.cpp deleted file mode 100644 index d234c8c..0000000 --- a/src/third_party/skia/tests/DeferredCanvasTest.cpp +++ /dev/null
@@ -1,916 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "../src/image/SkImagePriv.h" -#include "../src/image/SkSurface_Base.h" -#include "SkBitmap.h" -#include "SkBitmapDevice.h" -#include "SkBitmapProcShader.h" -#include "SkDeferredCanvas.h" -#include "SkGradientShader.h" -#include "SkShader.h" -#include "SkSurface.h" -#include "Test.h" -#include "sk_tool_utils.h" - -#if SK_SUPPORT_GPU -#include "GrContextFactory.h" -#else -class GrContextFactory; -#endif - -static const int gWidth = 2; -static const int gHeight = 2; - -static void create(SkBitmap* bm, SkColor color) { - bm->allocN32Pixels(gWidth, gHeight); - bm->eraseColor(color); -} - -static SkSurface* createSurface(SkColor color) { - SkSurface* surface = SkSurface::NewRasterPMColor(gWidth, gHeight); - surface->getCanvas()->clear(color); - return surface; -} - -static SkPMColor read_pixel(SkSurface* surface, int x, int y) { - SkPMColor pixel = 0; - SkBitmap bitmap; - bitmap.installPixels(SkImageInfo::MakeN32Premul(1, 1), &pixel, 4); - SkCanvas canvas(bitmap); - - SkPaint paint; - paint.setXfermodeMode(SkXfermode::kSrc_Mode); - surface->draw(&canvas, -SkIntToScalar(x), -SkIntToScalar(y), &paint); - return pixel; -} - -class MockSurface : public SkSurface_Base { -public: - MockSurface(int width, int height) : SkSurface_Base(width, height, NULL) { - clearCounts(); - fBitmap.allocN32Pixels(width, height); - } - - virtual SkCanvas* onNewCanvas() SK_OVERRIDE { - return SkNEW_ARGS(SkCanvas, (fBitmap)); - } - - virtual SkSurface* onNewSurface(const SkImageInfo&) SK_OVERRIDE { - return NULL; - } - - virtual SkImage* onNewImageSnapshot() SK_OVERRIDE { - return SkNewImageFromBitmap(fBitmap, true); - } - - virtual void onCopyOnWrite(ContentChangeMode mode) SK_OVERRIDE { - if (mode == SkSurface::kDiscard_ContentChangeMode) { - fDiscardCount++; - } else { - fRetainCount++; - } - } - - void clearCounts() { - fDiscardCount = 0; - fRetainCount = 0; - } - - int fDiscardCount, fRetainCount; - SkBitmap fBitmap; -}; - -static void TestDeferredCanvasWritePixelsToSurface(skiatest::Reporter* reporter) { - SkAutoTUnref<MockSurface> surface(SkNEW_ARGS(MockSurface, (10, 10))); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - - SkBitmap srcBitmap; - srcBitmap.allocPixels(SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType)); - srcBitmap.eraseColor(SK_ColorGREEN); - // Tests below depend on this bitmap being recognized as opaque - - // Preliminary sanity check: no copy on write if no active snapshot - surface->clearCounts(); - canvas->clear(SK_ColorWHITE); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - surface->clearCounts(); - canvas->flush(); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - // Case 1: Discard notification happens upon flushing - // with an Image attached. - surface->clearCounts(); - SkAutoTUnref<SkImage> image1(canvas->newImageSnapshot()); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - surface->clearCounts(); - canvas->clear(SK_ColorWHITE); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - surface->clearCounts(); - canvas->flush(); - REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - // Case 2: Opaque writePixels - surface->clearCounts(); - SkAutoTUnref<SkImage> image2(canvas->newImageSnapshot()); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - // Case 3: writePixels that partially covers the canvas - surface->clearCounts(); - SkAutoTUnref<SkImage> image3(canvas->newImageSnapshot()); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - // Case 4: unpremultiplied opaque writePixels that entirely - // covers the canvas - surface->clearCounts(); - SkAutoTUnref<SkImage> image4(canvas->newImageSnapshot()); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - surface->clearCounts(); - canvas->writePixels(srcBitmap, 0, 0); - REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - surface->clearCounts(); - canvas->flush(); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - // Case 5: unpremultiplied opaque writePixels that partially - // covers the canvas - surface->clearCounts(); - SkAutoTUnref<SkImage> image5(canvas->newImageSnapshot()); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - surface->clearCounts(); - canvas->writePixels(srcBitmap, 5, 0); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 1 == surface->fRetainCount); - - surface->clearCounts(); - canvas->flush(); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - // Case 6: unpremultiplied opaque writePixels that entirely - // covers the canvas, preceded by clear - surface->clearCounts(); - SkAutoTUnref<SkImage> image6(canvas->newImageSnapshot()); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - surface->clearCounts(); - canvas->clear(SK_ColorWHITE); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - surface->clearCounts(); - canvas->writePixels(srcBitmap, 0, 0); - REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - surface->clearCounts(); - canvas->flush(); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - // Case 7: unpremultiplied opaque writePixels that partially - // covers the canvas, preceeded by a clear - surface->clearCounts(); - SkAutoTUnref<SkImage> image7(canvas->newImageSnapshot()); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - surface->clearCounts(); - canvas->clear(SK_ColorWHITE); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - surface->clearCounts(); - canvas->writePixels(srcBitmap, 5, 0); - REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount); // because of the clear - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - surface->clearCounts(); - canvas->flush(); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - // Case 8: unpremultiplied opaque writePixels that partially - // covers the canvas, preceeded by a drawREct that partially - // covers the canvas - surface->clearCounts(); - SkAutoTUnref<SkImage> image8(canvas->newImageSnapshot()); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - surface->clearCounts(); - SkPaint paint; - canvas->drawRect(SkRect::MakeLTRB(0, 0, 5, 5), paint); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); - - surface->clearCounts(); - canvas->writePixels(srcBitmap, 5, 0); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 1 == surface->fRetainCount); - - surface->clearCounts(); - canvas->flush(); - REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount); - REPORTER_ASSERT(reporter, 0 == surface->fRetainCount); -} - -static void TestDeferredCanvasFlush(skiatest::Reporter* reporter) { - SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF)); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - - canvas->clear(0x00000000); - - // verify that clear was deferred - REPORTER_ASSERT(reporter, 0xFFFFFFFF == read_pixel(surface, 0, 0)); - - canvas->flush(); - - // verify that clear was executed - REPORTER_ASSERT(reporter, 0 == read_pixel(surface, 0, 0)); -} - -static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) { - SkRect fullRect; - fullRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(gWidth), - SkIntToScalar(gHeight)); - SkRect partialRect; - partialRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0), - SkIntToScalar(1), SkIntToScalar(1)); - - SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF)); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - - // verify that frame is intially fresh - REPORTER_ASSERT(reporter, canvas->isFreshFrame()); - // no clearing op since last call to isFreshFrame -> not fresh - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - - // Verify that clear triggers a fresh frame - canvas->clear(0x00000000); - REPORTER_ASSERT(reporter, canvas->isFreshFrame()); - - // Verify that clear with saved state triggers a fresh frame - canvas->save(); - canvas->clear(0x00000000); - canvas->restore(); - REPORTER_ASSERT(reporter, canvas->isFreshFrame()); - - // Verify that clear within a layer does NOT trigger a fresh frame - canvas->saveLayer(NULL, NULL); - canvas->clear(0x00000000); - canvas->restore(); - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - - // Verify that a clear with clipping triggers a fresh frame - // (clear is not affected by clipping) - canvas->save(); - canvas->clipRect(partialRect, SkRegion::kIntersect_Op, false); - canvas->clear(0x00000000); - canvas->restore(); - REPORTER_ASSERT(reporter, canvas->isFreshFrame()); - - // Verify that full frame rects with different forms of opaque paint - // trigger frames to be marked as fresh - { - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - paint.setAlpha(255); - canvas->drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, canvas->isFreshFrame()); - } - { - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - paint.setAlpha(255); - paint.setXfermodeMode(SkXfermode::kSrcIn_Mode); - canvas->drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - } - { - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - SkBitmap bmp; - create(&bmp, 0xFFFFFFFF); - bmp.setAlphaType(kOpaque_SkAlphaType); - SkShader* shader = SkShader::CreateBitmapShader(bmp, - SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); - paint.setShader(shader)->unref(); - canvas->drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, canvas->isFreshFrame()); - } - - // Verify that full frame rects with different forms of non-opaque paint - // do not trigger frames to be marked as fresh - { - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - paint.setAlpha(254); - canvas->drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - } - { - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - // Defining a cone that partially overlaps the canvas - const SkPoint pt1 = SkPoint::Make(SkIntToScalar(0), SkIntToScalar(0)); - const SkScalar r1 = SkIntToScalar(1); - const SkPoint pt2 = SkPoint::Make(SkIntToScalar(10), SkIntToScalar(0)); - const SkScalar r2 = SkIntToScalar(5); - const SkColor colors[2] = {SK_ColorWHITE, SK_ColorWHITE}; - const SkScalar pos[2] = {0, SK_Scalar1}; - SkShader* shader = SkGradientShader::CreateTwoPointConical( - pt1, r1, pt2, r2, colors, pos, 2, SkShader::kClamp_TileMode); - paint.setShader(shader)->unref(); - canvas->drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - } - { - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - SkBitmap bmp; - create(&bmp, 0xFFFFFFFF); - bmp.setAlphaType(kPremul_SkAlphaType); - SkShader* shader = SkShader::CreateBitmapShader(bmp, - SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); - paint.setShader(shader)->unref(); - canvas->drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - } - - // Verify that incomplete coverage does not trigger a fresh frame - { - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - paint.setAlpha(255); - canvas->drawRect(partialRect, paint); - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - } - - // Verify that incomplete coverage due to clipping does not trigger a fresh - // frame - { - canvas->save(); - canvas->clipRect(partialRect, SkRegion::kIntersect_Op, false); - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - paint.setAlpha(255); - canvas->drawRect(fullRect, paint); - canvas->restore(); - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - } - { - canvas->save(); - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - paint.setAlpha(255); - SkPath path; - path.addCircle(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(2)); - canvas->clipPath(path, SkRegion::kIntersect_Op, false); - canvas->drawRect(fullRect, paint); - canvas->restore(); - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - } - - // Verify that stroked rect does not trigger a fresh frame - { - SkPaint paint; - paint.setStyle(SkPaint::kStroke_Style); - paint.setAlpha(255); - canvas->drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, !canvas->isFreshFrame()); - } - - // Verify kSrcMode triggers a fresh frame even with transparent color - { - SkPaint paint; - paint.setStyle(SkPaint::kFill_Style); - paint.setAlpha(100); - paint.setXfermodeMode(SkXfermode::kSrc_Mode); - canvas->drawRect(fullRect, paint); - REPORTER_ASSERT(reporter, canvas->isFreshFrame()); - } -} - -class MockDevice : public SkBitmapDevice { -public: - MockDevice(const SkBitmap& bm) : SkBitmapDevice(bm) { - fDrawBitmapCallCount = 0; - } - virtual void drawBitmap(const SkDraw&, const SkBitmap&, - const SkMatrix&, const SkPaint&) SK_OVERRIDE { - fDrawBitmapCallCount++; - } - - int fDrawBitmapCallCount; -}; - -class NotificationCounter : public SkDeferredCanvas::NotificationClient { -public: - NotificationCounter() { - fPrepareForDrawCount = fStorageAllocatedChangedCount = - fFlushedDrawCommandsCount = fSkippedPendingDrawCommandsCount = 0; - } - - virtual void prepareForDraw() SK_OVERRIDE { - fPrepareForDrawCount++; - } - virtual void storageAllocatedForRecordingChanged(size_t) SK_OVERRIDE { - fStorageAllocatedChangedCount++; - } - virtual void flushedDrawCommands() SK_OVERRIDE { - fFlushedDrawCommandsCount++; - } - virtual void skippedPendingDrawCommands() SK_OVERRIDE { - fSkippedPendingDrawCommandsCount++; - } - - int fPrepareForDrawCount; - int fStorageAllocatedChangedCount; - int fFlushedDrawCommandsCount; - int fSkippedPendingDrawCommandsCount; - -private: - typedef SkDeferredCanvas::NotificationClient INHERITED; -}; - -// Verifies that the deferred canvas triggers a flush when its memory -// limit is exceeded -static void TestDeferredCanvasMemoryLimit(skiatest::Reporter* reporter) { - SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(100, 100)); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - - NotificationCounter notificationCounter; - canvas->setNotificationClient(¬ificationCounter); - - canvas->setMaxRecordingStorage(160000); - - SkBitmap sourceImage; - // 100 by 100 image, takes 40,000 bytes in memory - sourceImage.allocN32Pixels(100, 100); - sourceImage.eraseColor(SK_ColorGREEN); - - for (int i = 0; i < 5; i++) { - sourceImage.notifyPixelsChanged(); // to force re-serialization - canvas->drawBitmap(sourceImage, 0, 0, NULL); - } - - REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount); -} - -static void TestDeferredCanvasSilentFlush(skiatest::Reporter* reporter) { - SkAutoTUnref<SkSurface> surface(createSurface(0)); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - - NotificationCounter notificationCounter; - canvas->setNotificationClient(¬ificationCounter); - - canvas->silentFlush(); // will skip the initial clear that was recorded in createSurface - - REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount); - REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount); -} - -static void TestDeferredCanvasBitmapCaching(skiatest::Reporter* reporter) { - SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(100, 100)); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - - NotificationCounter notificationCounter; - canvas->setNotificationClient(¬ificationCounter); - - const int imageCount = 2; - SkBitmap sourceImages[imageCount]; - for (int i = 0; i < imageCount; i++) { - sourceImages[i].allocN32Pixels(100, 100); - sourceImages[i].eraseColor(SK_ColorGREEN); - } - - size_t bitmapSize = sourceImages[0].getSize(); - - canvas->drawBitmap(sourceImages[0], 0, 0, NULL); - REPORTER_ASSERT(reporter, 1 == notificationCounter.fStorageAllocatedChangedCount); - // stored bitmap + drawBitmap command - REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() > bitmapSize); - - // verify that nothing can be freed at this point - REPORTER_ASSERT(reporter, 0 == canvas->freeMemoryIfPossible(~0U)); - - // verify that flush leaves image in cache - REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount); - REPORTER_ASSERT(reporter, 0 == notificationCounter.fPrepareForDrawCount); - canvas->flush(); - REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount); - REPORTER_ASSERT(reporter, 1 == notificationCounter.fPrepareForDrawCount); - REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() >= bitmapSize); - - // verify that after a flush, cached image can be freed - REPORTER_ASSERT(reporter, canvas->freeMemoryIfPossible(~0U) >= bitmapSize); - - // Verify that caching works for avoiding multiple copies of the same bitmap - canvas->drawBitmap(sourceImages[0], 0, 0, NULL); - REPORTER_ASSERT(reporter, 2 == notificationCounter.fStorageAllocatedChangedCount); - canvas->drawBitmap(sourceImages[0], 0, 0, NULL); - REPORTER_ASSERT(reporter, 2 == notificationCounter.fStorageAllocatedChangedCount); - REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount); - REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() < 2 * bitmapSize); - - // Verify partial eviction based on bytesToFree - canvas->drawBitmap(sourceImages[1], 0, 0, NULL); - REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount); - canvas->flush(); - REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount); - REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() > 2 * bitmapSize); - size_t bytesFreed = canvas->freeMemoryIfPossible(1); - REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount); - REPORTER_ASSERT(reporter, bytesFreed >= bitmapSize); - REPORTER_ASSERT(reporter, bytesFreed < 2*bitmapSize); - - // Verifiy that partial purge works, image zero is in cache but not reffed by - // a pending draw, while image 1 is locked-in. - canvas->freeMemoryIfPossible(~0U); - REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount); - canvas->drawBitmap(sourceImages[0], 0, 0, NULL); - canvas->flush(); - canvas->drawBitmap(sourceImages[1], 0, 0, NULL); - bytesFreed = canvas->freeMemoryIfPossible(~0U); - // only one bitmap should have been freed. - REPORTER_ASSERT(reporter, bytesFreed >= bitmapSize); - REPORTER_ASSERT(reporter, bytesFreed < 2*bitmapSize); - // Clear for next test - canvas->flush(); - canvas->freeMemoryIfPossible(~0U); - REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() < bitmapSize); - - // Verify the image cache is sensitive to genID bumps - canvas->drawBitmap(sourceImages[1], 0, 0, NULL); - sourceImages[1].notifyPixelsChanged(); - canvas->drawBitmap(sourceImages[1], 0, 0, NULL); - REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() > 2*bitmapSize); - - // Verify that nothing in this test caused commands to be skipped - REPORTER_ASSERT(reporter, 0 == notificationCounter.fSkippedPendingDrawCommandsCount); -} - -static void TestDeferredCanvasSkip(skiatest::Reporter* reporter) { - SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(100, 100)); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - - NotificationCounter notificationCounter; - canvas->setNotificationClient(¬ificationCounter); - canvas->clear(0x0); - REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount); - REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount); - canvas->flush(); - REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount); - REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount); - -} - -static void TestDeferredCanvasBitmapShaderNoLeak(skiatest::Reporter* reporter) { - // This is a regression test for crbug.com/155875 - // This test covers a code path that inserts bitmaps into the bitmap heap through the - // flattening of SkBitmapProcShaders. The refcount in the bitmap heap is maintained through - // the flattening and unflattening of the shader. - SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(100, 100)); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - // test will fail if nbIterations is not in sync with - // BITMAPS_TO_KEEP in SkGPipeWrite.cpp - const int nbIterations = 5; - size_t bytesAllocated = 0; - for(int pass = 0; pass < 2; ++pass) { - for(int i = 0; i < nbIterations; ++i) { - SkPaint paint; - SkBitmap paintPattern; - paintPattern.allocN32Pixels(10, 10); - paintPattern.eraseColor(SK_ColorGREEN); - paint.setShader(SkNEW_ARGS(SkBitmapProcShader, - (paintPattern, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)))->unref(); - canvas->drawPaint(paint); - canvas->flush(); - - // In the first pass, memory allocation should be monotonically increasing as - // the bitmap heap slots fill up. In the second pass memory allocation should be - // stable as bitmap heap slots get recycled. - size_t newBytesAllocated = canvas->storageAllocatedForRecording(); - if (pass == 0) { - REPORTER_ASSERT(reporter, newBytesAllocated > bytesAllocated); - bytesAllocated = newBytesAllocated; - } else { - REPORTER_ASSERT(reporter, newBytesAllocated == bytesAllocated); - } - } - } - // All cached resources should be evictable since last canvas call was flush() - canvas->freeMemoryIfPossible(~0U); - REPORTER_ASSERT(reporter, 0 == canvas->storageAllocatedForRecording()); -} - -static void TestDeferredCanvasBitmapSizeThreshold(skiatest::Reporter* reporter) { - SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(100, 100)); - - SkBitmap sourceImage; - // 100 by 100 image, takes 40,000 bytes in memory - sourceImage.allocN32Pixels(100, 100); - sourceImage.eraseColor(SK_ColorGREEN); - - // 1 under : should not store the image - { - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - canvas->setBitmapSizeThreshold(39999); - canvas->drawBitmap(sourceImage, 0, 0, NULL); - size_t newBytesAllocated = canvas->storageAllocatedForRecording(); - REPORTER_ASSERT(reporter, newBytesAllocated == 0); - } - - // exact value : should store the image - { - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - canvas->setBitmapSizeThreshold(40000); - canvas->drawBitmap(sourceImage, 0, 0, NULL); - size_t newBytesAllocated = canvas->storageAllocatedForRecording(); - REPORTER_ASSERT(reporter, newBytesAllocated > 0); - } - - // 1 over : should still store the image - { - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - canvas->setBitmapSizeThreshold(40001); - canvas->drawBitmap(sourceImage, 0, 0, NULL); - size_t newBytesAllocated = canvas->storageAllocatedForRecording(); - REPORTER_ASSERT(reporter, newBytesAllocated > 0); - } -} - - -typedef const void* PixelPtr; -// Returns an opaque pointer which, either points to a GrTexture or RAM pixel -// buffer. Used to test pointer equality do determine whether a surface points -// to the same pixel data storage as before. -static PixelPtr get_surface_ptr(SkSurface* surface, bool useGpu) { -#if SK_SUPPORT_GPU - if (useGpu) { - return surface->getCanvas()->internal_private_accessTopLayerRenderTarget()->asTexture(); - } else -#endif - { - return surface->peekPixels(NULL, NULL); - } -} - -static void TestDeferredCanvasSurface(skiatest::Reporter* reporter, GrContextFactory* factory) { - SkImageInfo imageSpec = SkImageInfo::MakeN32Premul(10, 10); - bool useGpu = SkToBool(factory); - int cnt; -#if SK_SUPPORT_GPU - if (useGpu) { - cnt = GrContextFactory::kGLContextTypeCnt; - } else { - cnt = 1; - } -#else - SkASSERT(!useGpu); - cnt = 1; -#endif - for (int i = 0; i < cnt; ++i) { - SkSurface* surface; -#if SK_SUPPORT_GPU - if (useGpu) { - GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i; - if (!GrContextFactory::IsRenderingGLContext(glCtxType)) { - continue; - } - GrContext* context = factory->get(glCtxType); - if (NULL == context) { - return; - } - - surface = SkSurface::NewRenderTarget(context, imageSpec, 0, NULL); - } else -#endif - { - surface = SkSurface::NewRaster(imageSpec); - } - SkASSERT(surface); - SkAutoTUnref<SkSurface> aur(surface); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface)); - - SkImage* image1 = canvas->newImageSnapshot(); - SkAutoTUnref<SkImage> aur_i1(image1); - PixelPtr pixels1 = get_surface_ptr(surface, useGpu); - // The following clear would normally trigger a copy on write, but - // it won't because rendering is deferred. - canvas->clear(SK_ColorBLACK); - // Obtaining a snapshot directly from the surface (as opposed to the - // SkDeferredCanvas) will not trigger a flush of deferred draw operations - // and will therefore return the same image as the previous snapshot. - SkImage* image2 = surface->newImageSnapshot(); - SkAutoTUnref<SkImage> aur_i2(image2); - // Images identical because of deferral - REPORTER_ASSERT(reporter, image1->uniqueID() == image2->uniqueID()); - // Now we obtain a snpshot via the deferred canvas, which triggers a flush. - // Because there is a pending clear, this will generate a different image. - SkImage* image3 = canvas->newImageSnapshot(); - SkAutoTUnref<SkImage> aur_i3(image3); - REPORTER_ASSERT(reporter, image1->uniqueID() != image3->uniqueID()); - // Verify that backing store is now a different buffer because of copy on - // write - PixelPtr pixels2 = get_surface_ptr(surface, useGpu); - REPORTER_ASSERT(reporter, pixels1 != pixels2); - // Verify copy-on write with a draw operation that gets deferred by - // the in order draw buffer. - SkPaint paint; - canvas->drawPaint(paint); - SkImage* image4 = canvas->newImageSnapshot(); // implicit flush - SkAutoTUnref<SkImage> aur_i4(image4); - REPORTER_ASSERT(reporter, image4->uniqueID() != image3->uniqueID()); - PixelPtr pixels3 = get_surface_ptr(surface, useGpu); - REPORTER_ASSERT(reporter, pixels2 != pixels3); - // Verify that a direct canvas flush with a pending draw does not trigger - // a copy on write when the surface is not sharing its buffer with an - // SkImage. - canvas->clear(SK_ColorWHITE); - canvas->flush(); - PixelPtr pixels4 = get_surface_ptr(surface, useGpu); - canvas->drawPaint(paint); - canvas->flush(); - PixelPtr pixels5 = get_surface_ptr(surface, useGpu); - REPORTER_ASSERT(reporter, pixels4 == pixels5); - } -} - -static void TestDeferredCanvasSetSurface(skiatest::Reporter* reporter, GrContextFactory* factory) { - SkImageInfo imageSpec = SkImageInfo::MakeN32Premul(10, 10); - SkSurface* surface; - SkSurface* alternateSurface; - bool useGpu = SkToBool(factory); - int cnt; -#if SK_SUPPORT_GPU - if (useGpu) { - cnt = GrContextFactory::kGLContextTypeCnt; - } else { - cnt = 1; - } -#else - SkASSERT(!useGpu); - cnt = 1; -#endif - - for (int i = 0; i < cnt; ++i) { -#if SK_SUPPORT_GPU - if (useGpu) { - GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i; - if (!GrContextFactory::IsRenderingGLContext(glCtxType)) { - continue; - } - GrContext* context = factory->get(glCtxType); - if (NULL == context) { - continue; - } - surface = SkSurface::NewRenderTarget(context, imageSpec, 0, NULL); - alternateSurface = SkSurface::NewRenderTarget(context, imageSpec, 0, NULL); - } else -#endif - { - surface = SkSurface::NewRaster(imageSpec); - alternateSurface = SkSurface::NewRaster(imageSpec); - } - SkASSERT(surface); - SkASSERT(alternateSurface); - SkAutoTUnref<SkSurface> aur1(surface); - SkAutoTUnref<SkSurface> aur2(alternateSurface); - PixelPtr pixels1 = get_surface_ptr(surface, useGpu); - PixelPtr pixels2 = get_surface_ptr(alternateSurface, useGpu); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface)); - SkAutoTUnref<SkImage> image1(canvas->newImageSnapshot()); - canvas->setSurface(alternateSurface); - SkAutoTUnref<SkImage> image2(canvas->newImageSnapshot()); - REPORTER_ASSERT(reporter, image1->uniqueID() != image2->uniqueID()); - // Verify that none of the above operations triggered a surface copy on write. - REPORTER_ASSERT(reporter, get_surface_ptr(surface, useGpu) == pixels1); - REPORTER_ASSERT(reporter, get_surface_ptr(alternateSurface, useGpu) == pixels2); - // Verify that a flushed draw command will trigger a copy on write on alternateSurface. - canvas->clear(SK_ColorWHITE); - canvas->flush(); - REPORTER_ASSERT(reporter, get_surface_ptr(surface, useGpu) == pixels1); - REPORTER_ASSERT(reporter, get_surface_ptr(alternateSurface, useGpu) != pixels2); - } -} - -static void TestDeferredCanvasCreateCompatibleDevice(skiatest::Reporter* reporter) { - SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(100, 100)); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - - NotificationCounter notificationCounter; - canvas->setNotificationClient(¬ificationCounter); - - SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10); - SkAutoTUnref<SkSurface> secondarySurface(canvas->newSurface(info)); - - SkRect rect = SkRect::MakeWH(5, 5); - SkPaint paint; - // After spawning a compatible canvas: - // 1) Verify that secondary canvas is usable and does not report to the notification client. - surface->getCanvas()->drawRect(rect, paint); - REPORTER_ASSERT(reporter, notificationCounter.fStorageAllocatedChangedCount == 0); - // 2) Verify that original canvas is usable and still reports to the notification client. - canvas->drawRect(rect, paint); - REPORTER_ASSERT(reporter, notificationCounter.fStorageAllocatedChangedCount == 1); -} - -static void TestDeferredCanvasGetCanvasSize(skiatest::Reporter* reporter) { - SkRect rect; - rect.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(gWidth), SkIntToScalar(gHeight)); - SkRect clip; - clip.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(1), SkIntToScalar(1)); - - SkPaint paint; - SkISize size = SkISize::Make(gWidth, gHeight); - - SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF)); - SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get())); - SkSurface* newSurface = SkSurface::NewRasterPMColor(4, 4); - SkAutoTUnref<SkSurface> aur(newSurface); - - for (int i = 0; i < 2; ++i) { - if (i == 1) { - canvas->setSurface(newSurface); - size = SkISize::Make(4, 4); - } - - // verify that canvas size is correctly initialized or set - REPORTER_ASSERT(reporter, size == canvas->getCanvasSize()); - - // Verify that clear, clip and draw the canvas will not change its size - canvas->clear(0x00000000); - canvas->clipRect(clip, SkRegion::kIntersect_Op, false); - canvas->drawRect(rect, paint); - REPORTER_ASSERT(reporter, size == canvas->getCanvasSize()); - - // Verify that flush the canvas will not change its size - canvas->flush(); - REPORTER_ASSERT(reporter, size == canvas->getCanvasSize()); - - // Verify that clear canvas with saved state will not change its size - canvas->save(); - canvas->clear(0xFFFFFFFF); - REPORTER_ASSERT(reporter, size == canvas->getCanvasSize()); - - // Verify that restore canvas state will not change its size - canvas->restore(); - REPORTER_ASSERT(reporter, size == canvas->getCanvasSize()); - - // Verify that clear within a layer will not change canvas size - canvas->saveLayer(&clip, &paint); - canvas->clear(0x00000000); - REPORTER_ASSERT(reporter, size == canvas->getCanvasSize()); - - // Verify that restore from a layer will not change canvas size - canvas->restore(); - REPORTER_ASSERT(reporter, size == canvas->getCanvasSize()); - } -} - -DEF_TEST(DeferredCanvas_CPU, reporter) { - TestDeferredCanvasFlush(reporter); - TestDeferredCanvasSilentFlush(reporter); - TestDeferredCanvasFreshFrame(reporter); - TestDeferredCanvasMemoryLimit(reporter); - TestDeferredCanvasBitmapCaching(reporter); - TestDeferredCanvasSkip(reporter); - TestDeferredCanvasBitmapShaderNoLeak(reporter); - TestDeferredCanvasBitmapSizeThreshold(reporter); - TestDeferredCanvasCreateCompatibleDevice(reporter); - TestDeferredCanvasWritePixelsToSurface(reporter); - TestDeferredCanvasGetCanvasSize(reporter); - TestDeferredCanvasSurface(reporter, NULL); - TestDeferredCanvasSetSurface(reporter, NULL); -} - -DEF_GPUTEST(DeferredCanvas_GPU, reporter, factory) { - if (factory != NULL) { - TestDeferredCanvasSurface(reporter, factory); - TestDeferredCanvasSetSurface(reporter, factory); - } -}
diff --git a/src/third_party/skia/tests/DequeTest.cpp b/src/third_party/skia/tests/DequeTest.cpp index 04b1f40..b41262f 100644 --- a/src/third_party/skia/tests/DequeTest.cpp +++ b/src/third_party/skia/tests/DequeTest.cpp
@@ -13,8 +13,8 @@ REPORTER_ASSERT(reporter, deq.empty()); REPORTER_ASSERT(reporter, 0 == deq.count()); REPORTER_ASSERT(reporter, sizeof(int) == deq.elemSize()); - REPORTER_ASSERT(reporter, NULL == deq.front()); - REPORTER_ASSERT(reporter, NULL == deq.back()); + REPORTER_ASSERT(reporter, nullptr == deq.front()); + REPORTER_ASSERT(reporter, nullptr == deq.back()); } else { REPORTER_ASSERT(reporter, !deq.empty()); REPORTER_ASSERT(reporter, count == deq.count());
diff --git a/src/third_party/skia/tests/DetermineDomainModeTest.cpp b/src/third_party/skia/tests/DetermineDomainModeTest.cpp new file mode 100644 index 0000000..add746f --- /dev/null +++ b/src/third_party/skia/tests/DetermineDomainModeTest.cpp
@@ -0,0 +1,446 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#if SK_SUPPORT_GPU + +#include "GrSurfaceProxy.h" +#include "GrTextureProducer.h" +#include "GrTextureProxy.h" + +// For DetermineDomainMode (in the MDB world) we have 4 rects: +// 1) the final instantiated backing storage (i.e., the actual GrTexture's extent) +// 2) the proxy's extent, which may or may not match the GrTexture's extent +// 3) the content rect, which can be a subset of the proxy's extent or null +// 4) the constraint rect, which can optionally be hard or soft +// This test "fuzzes" all the combinations of these rects. +class GrTextureProducer_TestAccess { +public: + using DomainMode = GrTextureProducer::DomainMode; + + static DomainMode DetermineDomainMode( + const SkRect& constraintRect, + GrTextureProducer::FilterConstraint filterConstraint, + bool coordsLimitedToConstraintRect, + GrTextureProxy* proxy, + const SkIRect* textureContentArea, + const GrSamplerParams::FilterMode* filterModeOrNullForBicubic, + SkRect* domainRect) { + return GrTextureProducer::DetermineDomainMode(constraintRect, + filterConstraint, + coordsLimitedToConstraintRect, + proxy, + textureContentArea, + filterModeOrNullForBicubic, + domainRect); + } +}; + +using DomainMode = GrTextureProducer_TestAccess::DomainMode; + +#ifdef SK_DEBUG +static bool is_irect(const SkRect& r) { + return SkScalarIsInt(r.fLeft) && SkScalarIsInt(r.fTop) && + SkScalarIsInt(r.fRight) && SkScalarIsInt(r.fBottom); +} +#endif + +static SkIRect to_irect(const SkRect& r) { + SkASSERT(is_irect(r)); + return SkIRect::MakeLTRB(SkScalarRoundToInt(r.fLeft), + SkScalarRoundToInt(r.fTop), + SkScalarRoundToInt(r.fRight), + SkScalarRoundToInt(r.fBottom)); +} + + +class RectInfo { +public: + enum Side { kLeft = 0, kTop = 1, kRight = 2, kBot = 3 }; + + enum EdgeType { + kSoft = 0, // there is data on the other side of this edge that we are allowed to sample + kHard = 1, // the backing resource ends at this edge + kBad = 2 // we can't sample across this edge + }; + + void set(const SkRect& rect, EdgeType left, EdgeType top, EdgeType right, EdgeType bot, + const char* name) { + fRect = rect; + fTypes[kLeft] = left; + fTypes[kTop] = top; + fTypes[kRight] = right; + fTypes[kBot] = bot; + fName = name; + } + + const SkRect& rect() const { return fRect; } + EdgeType edgeType(Side side) const { return fTypes[side]; } + const char* name() const { return fName; } + +#ifdef SK_DEBUG + bool isHardOrBadAllAround() const { + for (int i = 0; i < 4; ++i) { + if (kHard != fTypes[i] && kBad != fTypes[i]) { + return false; + } + } + return true; + } +#endif + + bool hasABad() const { + for (int i = 0; i < 4; ++i) { + if (kBad == fTypes[i]) { + return true; + } + } + return false; + } + +#ifdef SK_DEBUG + void print(const char* label) const { + SkDebugf("%s: %s (%.1f, %.1f, %.1f, %.1f), L: %s T: %s R: %s B: %s\n", + label, fName, + fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, + ToStr(fTypes[kLeft]), ToStr(fTypes[kTop]), + ToStr(fTypes[kRight]), ToStr(fTypes[kBot])); + } +#endif + +private: +#ifdef SK_DEBUG + static const char* ToStr(EdgeType type) { + static const char* names[] = { "soft", "hard", "bad" }; + return names[type]; + } +#endif + + RectInfo operator=(const RectInfo& other); // disallow + + SkRect fRect; + EdgeType fTypes[4]; + const char* fName; + +}; + +static sk_sp<GrTextureProxy> create_proxy(GrResourceProvider* resourceProvider, + bool isPowerOfTwo, + bool isExact, + RectInfo* rect) { + int size = isPowerOfTwo ? 128 : 100; + SkBackingFit fit = isExact ? SkBackingFit::kExact : SkBackingFit::kApprox; + + GrSurfaceDesc desc; + desc.fConfig = kRGBA_8888_GrPixelConfig; + desc.fWidth = size; + desc.fHeight = size; + + static const char* name = "proxy"; + + // Proxies are always hard on the left and top but can be bad on the right and bottom + rect->set(SkRect::MakeWH(size, size), + RectInfo::kHard, + RectInfo::kHard, + (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad, + (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad, + name); + + sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(resourceProvider, + desc, fit, + SkBudgeted::kYes); + return proxy; +} + +static RectInfo::EdgeType compute_inset_edgetype(RectInfo::EdgeType previous, + bool isInsetHard, bool coordsAreLimitedToRect, + float insetAmount, float halfFilterWidth) { + if (isInsetHard) { + if (coordsAreLimitedToRect) { + SkASSERT(halfFilterWidth >= 0.0f); + if (0.0f == halfFilterWidth) { + return RectInfo::kSoft; + } + } + + if (0.0f == insetAmount && RectInfo::kHard == previous) { + return RectInfo::kHard; + } + + return RectInfo::kBad; + } + + if (RectInfo::kHard == previous) { + return RectInfo::kHard; + } + + if (coordsAreLimitedToRect) { + SkASSERT(halfFilterWidth >= 0.0f); + if (0.0 == halfFilterWidth || insetAmount > halfFilterWidth) { + return RectInfo::kSoft; + } + } + + return previous; +} + +static const int kInsetLeft_Flag = 0x1; +static const int kInsetTop_Flag = 0x2; +static const int kInsetRight_Flag = 0x4; +static const int kInsetBot_Flag = 0x8; + +// If 'isInsetHard' is true we can't sample across the inset boundary. +// If 'areCoordsLimitedToRect' is true the client promises to never sample outside the inset. +static const SkRect* generic_inset(const RectInfo& enclosing, + RectInfo* result, + bool isInsetHard, + bool areCoordsLimitedToRect, + float insetAmount, + float halfFilterWidth, + uint32_t flags, + const char* name) { + SkRect newR = enclosing.rect(); + + RectInfo::EdgeType left = enclosing.edgeType(RectInfo::kLeft); + if (flags & kInsetLeft_Flag) { + newR.fLeft += insetAmount; + left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth); + } else { + left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect, + 0.0f, halfFilterWidth); + } + + RectInfo::EdgeType top = enclosing.edgeType(RectInfo::kTop); + if (flags & kInsetTop_Flag) { + newR.fTop += insetAmount; + top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth); + } else { + top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect, + 0.0f, halfFilterWidth); + } + + RectInfo::EdgeType right = enclosing.edgeType(RectInfo::kRight); + if (flags & kInsetRight_Flag) { + newR.fRight -= insetAmount; + right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth); + } else { + right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect, + 0.0f, halfFilterWidth); + } + + RectInfo::EdgeType bot = enclosing.edgeType(RectInfo::kBot); + if (flags & kInsetBot_Flag) { + newR.fBottom -= insetAmount; + bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth); + } else { + bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect, + 0.0f, halfFilterWidth); + } + + result->set(newR, left, top, right, bot, name); + return &result->rect(); +} + +// Make a rect that only touches the enclosing rect on the left. +static const SkRect* left_only(const RectInfo& enclosing, + RectInfo* result, + bool isInsetHard, + bool areCoordsLimitedToRect, + float insetAmount, + float halfFilterWidth) { + static const char* name = "left"; + return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth, + kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name); +} + +// Make a rect that only touches the enclosing rect on the top. +static const SkRect* top_only(const RectInfo& enclosing, + RectInfo* result, + bool isInsetHard, + bool areCoordsLimitedToRect, + float insetAmount, + float halfFilterWidth) { + static const char* name = "top"; + return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth, + kInsetLeft_Flag|kInsetRight_Flag|kInsetBot_Flag, name); +} + +// Make a rect that only touches the enclosing rect on the right. +static const SkRect* right_only(const RectInfo& enclosing, + RectInfo* result, + bool isInsetHard, + bool areCoordsLimitedToRect, + float insetAmount, + float halfFilterWidth) { + static const char* name = "right"; + return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth, + kInsetLeft_Flag|kInsetTop_Flag|kInsetBot_Flag, name); +} + +// Make a rect that only touches the enclosing rect on the bottom. +static const SkRect* bot_only(const RectInfo& enclosing, + RectInfo* result, + bool isInsetHard, + bool areCoordsLimitedToRect, + float insetAmount, + float halfFilterWidth) { + static const char* name = "bot"; + return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth, + kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag, name); +} + +// Make a rect that is inset all around. +static const SkRect* full_inset(const RectInfo& enclosing, + RectInfo* result, + bool isInsetHard, + bool areCoordsLimitedToRect, + float insetAmount, + float halfFilterWidth) { + static const char* name = "all"; + return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth, + kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name); +} + +// This is only used for content rect creation. We ensure 'result' is correct but +// return null to indicate no content area (other than what the proxy specifies). +static const SkRect* null_rect(const RectInfo& enclosing, + RectInfo* result, + bool isInsetHard, + bool areCoordsLimitedToRect, + float insetAmount, + float halfFilterWidth) { + static const char* name = "null"; + generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth, 0, name); + return nullptr; +} + +// Make a rect with no inset. This is only used for constraint rect creation. +static const SkRect* no_inset(const RectInfo& enclosing, + RectInfo* result, + bool isInsetHard, + bool areCoordsLimitedToRect, + float insetAmount, + float halfFilterWidth) { + static const char* name = "none"; + return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, + insetAmount, halfFilterWidth, 0, name); +} + +static void proxy_test(skiatest::Reporter* reporter, GrResourceProvider* resourceProvider) { + GrTextureProducer_TestAccess::DomainMode actualMode, expectedMode; + SkRect actualDomainRect; + + static const GrSamplerParams::FilterMode gModes[] = { + GrSamplerParams::kNone_FilterMode, + GrSamplerParams::kBilerp_FilterMode, + GrSamplerParams::kMipMap_FilterMode, + }; + + static const GrSamplerParams::FilterMode* gModePtrs[] = { + &gModes[0], &gModes[1], nullptr, &gModes[2] + }; + + static const float gHalfFilterWidth[] = { 0.0f, 0.5f, 1.5f, 10000.0f }; + + for (auto isPowerOfTwoSized : { true, false }) { + for (auto isExact : { true, false }) { + RectInfo outermost; + + sk_sp<GrTextureProxy> proxy = create_proxy(resourceProvider, isPowerOfTwoSized, + isExact, &outermost); + SkASSERT(outermost.isHardOrBadAllAround()); + + for (auto contentRectMaker : { left_only, top_only, right_only, + bot_only, full_inset, null_rect}) { + RectInfo contentRectStorage; + const SkRect* contentRect = (*contentRectMaker)(outermost, + &contentRectStorage, + true, false, 5.0f, -1.0f); + if (contentRect) { + // We only have content rects if they actually reduce the extent of the content + SkASSERT(!contentRect->contains(outermost.rect())); + SkASSERT(outermost.rect().contains(*contentRect)); + SkASSERT(is_irect(*contentRect)); + } + SkASSERT(contentRectStorage.isHardOrBadAllAround()); + + for (auto isConstraintRectHard : { true, false }) { + for (auto areCoordsLimitedToConstraintRect : { true, false }) { + for (int filterMode = 0; filterMode < 4; ++filterMode) { + for (auto constraintRectMaker : { left_only, top_only, right_only, + bot_only, full_inset, no_inset }) { + for (auto insetAmt : { 0.25f, 0.75f, 1.25f, 1.75f, 5.0f }) { + RectInfo constraintRectStorage; + const SkRect* constraintRect = (*constraintRectMaker)( + contentRect ? contentRectStorage : outermost, + &constraintRectStorage, + isConstraintRectHard, + areCoordsLimitedToConstraintRect, + insetAmt, + gHalfFilterWidth[filterMode]); + SkASSERT(constraintRect); // always need one of these + if (contentRect) { + SkASSERT(contentRect->contains(*constraintRect)); + } else { + SkASSERT(outermost.rect().contains(*constraintRect)); + } + + SkIRect contentIRect; + if (contentRect) { + contentIRect = to_irect(*contentRect); + } + + actualMode = GrTextureProducer_TestAccess::DetermineDomainMode( + *constraintRect, + isConstraintRectHard + ? GrTextureProducer::kYes_FilterConstraint + : GrTextureProducer::kNo_FilterConstraint, + areCoordsLimitedToConstraintRect, + proxy.get(), + contentRect ? &contentIRect : nullptr, + gModePtrs[filterMode], + &actualDomainRect); + + expectedMode = DomainMode::kNoDomain_DomainMode; + if (constraintRectStorage.hasABad()) { + if (3 == filterMode) { + expectedMode = DomainMode::kTightCopy_DomainMode; + } else { + expectedMode = DomainMode::kDomain_DomainMode; + } + } + + REPORTER_ASSERT(reporter, expectedMode == actualMode); + // TODO: add a check that the returned domain rect is correct + } + } + } + } + } + } + } + } +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DetermineDomainModeTest, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + + proxy_test(reporter, context->resourceProvider()); +} + +#endif
diff --git a/src/third_party/skia/tests/DeviceLooperTest.cpp b/src/third_party/skia/tests/DeviceLooperTest.cpp index 5735043..c8eb3c7 100644 --- a/src/third_party/skia/tests/DeviceLooperTest.cpp +++ b/src/third_party/skia/tests/DeviceLooperTest.cpp
@@ -5,13 +5,13 @@ * found in the LICENSE file. */ +#include "SkAutoPixmapStorage.h" #include "SkDeviceLooper.h" #include "SkRasterClip.h" #include "Test.h" -static void make_bm(SkBitmap* bm, int w, int h) { - bm->allocPixels(SkImageInfo::Make(w, h, kAlpha_8_SkColorType, - kPremul_SkAlphaType)); +static void make_pm(SkAutoPixmapStorage* pixmap, int w, int h) { + pixmap->alloc(SkImageInfo::Make(w, h, kAlpha_8_SkColorType, kPremul_SkAlphaType)); } static bool equal(const SkRasterClip& a, const SkRasterClip& b) { @@ -40,19 +40,19 @@ static void test_simple(skiatest::Reporter* reporter) { for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { - SkBitmap bitmap; - make_bm(&bitmap, gRec[i].fDevSize.width(), gRec[i].fDevSize.height()); + SkAutoPixmapStorage pmap; + make_pm(&pmap, gRec[i].fDevSize.width(), gRec[i].fDevSize.height()); SkRasterClip rc(gRec[i].fRCBounds); for (int aa = 0; aa <= 1; ++aa) { - SkDeviceLooper looper(bitmap, rc, gRec[i].fRect, SkToBool(aa)); + SkDeviceLooper looper(pmap, rc, gRec[i].fRect, SkToBool(aa)); bool valid = looper.next(); REPORTER_ASSERT(reporter, valid); if (valid) { - REPORTER_ASSERT(reporter, looper.getBitmap().width() == bitmap.width()); - REPORTER_ASSERT(reporter, looper.getBitmap().height() == bitmap.height()); + REPORTER_ASSERT(reporter, looper.getPixmap().width() == pmap.width()); + REPORTER_ASSERT(reporter, looper.getPixmap().height() == pmap.height()); REPORTER_ASSERT(reporter, equal(looper.getRC(), rc)); REPORTER_ASSERT(reporter, !looper.next()); @@ -62,7 +62,7 @@ { SkIRect r = rc.getBounds(); r.offset(r.width(), 0); - SkDeviceLooper looper(bitmap, rc, r, false); + SkDeviceLooper looper(pmap, rc, r, false); REPORTER_ASSERT(reporter, !looper.next()); } } @@ -109,8 +109,8 @@ const int w = gRec[i].fSize.width(); const int h = gRec[i].fSize.height(); - SkBitmap bitmap; - make_bm(&bitmap, w, h); + SkAutoPixmapStorage pmap; + make_pm(&pmap, w, h); const SkIRect rect = SkIRect::MakeWH(w, h); @@ -125,7 +125,7 @@ SkRasterClip rc; rc.op(rgn, SkRegion::kReplace_Op); - SkDeviceLooper looper(bitmap, rc, rect, gRec[i].fAA); + SkDeviceLooper looper(pmap, rc, rect, gRec[i].fAA); while (looper.next()) { REPORTER_ASSERT(reporter, !looper.getRC().isEmpty()); }
diff --git a/src/third_party/skia/tests/DeviceTest.cpp b/src/third_party/skia/tests/DeviceTest.cpp new file mode 100644 index 0000000..55c9421 --- /dev/null +++ b/src/third_party/skia/tests/DeviceTest.cpp
@@ -0,0 +1,126 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkBitmapDevice.h" +#include "SkDevice.h" +#include "SkSpecialImage.h" + +#if SK_SUPPORT_GPU +#include "SkGpuDevice.h" +#endif + +#include "Test.h" + +class DeviceTestingAccess { +public: + static sk_sp<SkSpecialImage> MakeSpecial(SkBaseDevice* dev, const SkBitmap& bm) { + return dev->makeSpecial(bm); + } + + static sk_sp<SkSpecialImage> MakeSpecial(SkBaseDevice* dev, SkImage* img) { + return dev->makeSpecial(img); + } + + static sk_sp<SkSpecialImage> SnapSpecial(SkBaseDevice* dev) { + return dev->snapSpecial(); + } +}; + +// TODO: re-enable this when Raster methods are implemented +#if 0 +DEF_TEST(SpecialImage_BitmapDevice, reporter) { + static const int kWidth = 100; + static const int kHeight = 90; + + SkImageInfo ii = SkImageInfo::MakeN32Premul(2*kWidth, 2*kHeight); + + sk_sp<SkBaseDevice> bmDev(SkBitmapDevice::Create(ii)); + + SkBitmap bm; + bm.tryAllocN32Pixels(kWidth, kHeight); + + // Create a raster-backed special image from a raster-backed SkBitmap + sk_sp<SkSpecialImage> special = DeviceTestingAccess::MakeSpecial(bmDev.get(), bm); + SkASSERT(!special->isTextureBacked()); + SkASSERT(kWidth == special->width()); + SkASSERT(kHeight == special->height()); + SkASSERT(bm.getGenerationID() == special->uniqueID()); + SkASSERT(SkIRect::MakeWH(kWidth, kHeight) == special->subset()); + + // Create a raster-backed special image from a raster-backed SkImage + sk_sp<SkImage> image(SkImage::MakeFromBitmap(bm)); + special = DeviceTestingAccess::MakeSpecial(bmDev.get(), image.get()); + SkASSERT(!special->isTextureBacked()); + SkASSERT(kWidth == special->width()); + SkASSERT(kHeight == special->height()); + SkASSERT(bm.getGenerationID() == special->uniqueID()); + SkASSERT(SkIRect::MakeWH(kWidth, kHeight) == special->subset()); + + // Snap the device as a raster-backed special image + special = DeviceTestingAccess::SnapSpecial(bmDev.get()); + SkASSERT(!special->isTextureBacked()); + SkASSERT(2*kWidth == special->width()); + SkASSERT(2*kHeight == special->height()); + SkASSERT(SkIRect::MakeWH(2*kWidth, 2*kHeight) == special->subset()); +} +#endif + + +#if SK_SUPPORT_GPU + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_GPUDevice, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + + static const int kWidth = 100; + static const int kHeight = 90; + + SkImageInfo ii = SkImageInfo::MakeN32Premul(2*kWidth, 2*kHeight); + + sk_sp<SkBaseDevice> gpuDev(SkGpuDevice::Make(context, SkBudgeted::kNo, ii, + 0, kBottomLeft_GrSurfaceOrigin, nullptr, + SkGpuDevice::kClear_InitContents)); + + SkBitmap bm; + SkAssertResult(bm.tryAllocN32Pixels(kWidth, kHeight)); + + // Create a gpu-backed special image from a raster-backed SkBitmap + sk_sp<SkSpecialImage> special = DeviceTestingAccess::MakeSpecial(gpuDev.get(), bm); + SkASSERT(special->isTextureBacked()); + SkASSERT(kWidth == special->width()); + SkASSERT(kHeight == special->height()); + SkASSERT(bm.getGenerationID() == special->uniqueID()); + SkASSERT(SkIRect::MakeWH(kWidth, kHeight) == special->subset()); + + // Create a gpu-backed special image from a raster-backed SkImage + sk_sp<SkImage> image(SkImage::MakeFromBitmap(bm)); + special = DeviceTestingAccess::MakeSpecial(gpuDev.get(), image.get()); + SkASSERT(special->isTextureBacked()); + SkASSERT(kWidth == special->width()); + SkASSERT(kHeight == special->height()); + // TODO: Hmmm, this is a bit unexpected + SkASSERT(image->uniqueID() != special->uniqueID()); + SkASSERT(SkIRect::MakeWH(kWidth, kHeight) == special->subset()); + + // Create a gpu-backed special image from a gpu-backed SkImage + SkColorSpace* legacyColorSpace = nullptr; + image = image->makeTextureImage(context, legacyColorSpace); + special = DeviceTestingAccess::MakeSpecial(gpuDev.get(), image.get()); + SkASSERT(special->isTextureBacked()); + SkASSERT(kWidth == special->width()); + SkASSERT(kHeight == special->height()); + SkASSERT(image->uniqueID() == special->uniqueID()); + SkASSERT(SkIRect::MakeWH(kWidth, kHeight) == special->subset()); + + // Snap the device as a gpu-backed special image + special = DeviceTestingAccess::SnapSpecial(gpuDev.get()); + SkASSERT(special->isTextureBacked()); + SkASSERT(2*kWidth == special->width()); + SkASSERT(2*kHeight == special->height()); + SkASSERT(SkIRect::MakeWH(2*kWidth, 2*kHeight) == special->subset()); +} + +#endif
diff --git a/src/third_party/skia/tests/DiscardableMemoryPoolTest.cpp b/src/third_party/skia/tests/DiscardableMemoryPoolTest.cpp index e0145bc..c91e827 100644 --- a/src/third_party/skia/tests/DiscardableMemoryPoolTest.cpp +++ b/src/third_party/skia/tests/DiscardableMemoryPoolTest.cpp
@@ -9,20 +9,19 @@ #include "Test.h" DEF_TEST(DiscardableMemoryPool, reporter) { - SkAutoTUnref<SkDiscardableMemoryPool> pool( - SkDiscardableMemoryPool::Create(1, NULL)); + sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Make(1)); pool->setRAMBudget(3); REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed()); - SkAutoTDelete<SkDiscardableMemory> dm1(pool->create(100)); - REPORTER_ASSERT(reporter, dm1->data() != NULL); + std::unique_ptr<SkDiscardableMemory> dm1(pool->create(100)); + REPORTER_ASSERT(reporter, dm1->data() != nullptr); REPORTER_ASSERT(reporter, 100 == pool->getRAMUsed()); dm1->unlock(); REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed()); REPORTER_ASSERT(reporter, !dm1->lock()); - SkAutoTDelete<SkDiscardableMemory> dm2(pool->create(200)); + std::unique_ptr<SkDiscardableMemory> dm2(pool->create(200)); REPORTER_ASSERT(reporter, 200 == pool->getRAMUsed()); pool->setRAMBudget(400); dm2->unlock();
diff --git a/src/third_party/skia/tests/DiscardableMemoryTest.cpp b/src/third_party/skia/tests/DiscardableMemoryTest.cpp index da36ffd..818fbea 100644 --- a/src/third_party/skia/tests/DiscardableMemoryTest.cpp +++ b/src/third_party/skia/tests/DiscardableMemoryTest.cpp
@@ -5,28 +5,56 @@ * found in the LICENSE file. */ -#include "SkDiscardableMemory.h" +#include "SkDiscardableMemoryPool.h" #include "Test.h" -DEF_TEST(DiscardableMemory, reporter) { - const char testString[] = "HELLO, WORLD!"; - const size_t len = sizeof(testString); - SkAutoTDelete<SkDiscardableMemory> dm(SkDiscardableMemory::Create(len)); - REPORTER_ASSERT(reporter, dm.get() != NULL); - if (NULL == dm.get()) { +namespace { +constexpr char kTestString[] = "HELLO, WORLD!"; +constexpr size_t kTestStringLength = sizeof(kTestString); +} + +static void test_dm(skiatest::Reporter* reporter, + SkDiscardableMemory* dm, + bool assertRelock) { + REPORTER_ASSERT(reporter, dm); + if (!dm) { return; } void* ptr = dm->data(); - REPORTER_ASSERT(reporter, ptr != NULL); - memcpy(ptr, testString, sizeof(testString)); + REPORTER_ASSERT(reporter, ptr); + if (!ptr) { + return; + } + memcpy(ptr, kTestString, sizeof(kTestString)); dm->unlock(); - bool success = dm->lock(); - REPORTER_ASSERT(reporter, success); - if (!success) { + bool relockSuccess = dm->lock(); + if (assertRelock) { + REPORTER_ASSERT(reporter, relockSuccess); + } + if (!relockSuccess) { return; } ptr = dm->data(); - REPORTER_ASSERT(reporter, 0 == memcmp(ptr, testString, len)); + REPORTER_ASSERT(reporter, ptr); + if (!ptr) { + return; + } + REPORTER_ASSERT(reporter, 0 == memcmp(ptr, kTestString, kTestStringLength)); dm->unlock(); } + +DEF_TEST(DiscardableMemory_global, reporter) { + std::unique_ptr<SkDiscardableMemory> dm(SkDiscardableMemory::Create(kTestStringLength)); + // lock() test is allowed to fail, since other threads could be + // using global pool. + test_dm(reporter, dm.get(), false); +} + +DEF_TEST(DiscardableMemory_nonglobal, reporter) { + sk_sp<SkDiscardableMemoryPool> pool( + SkDiscardableMemoryPool::Make(1024)); + std::unique_ptr<SkDiscardableMemory> dm(pool->create(kTestStringLength)); + test_dm(reporter, dm.get(), true); +} +
diff --git a/src/third_party/skia/tests/DocumentTest.cpp b/src/third_party/skia/tests/DocumentTest.cpp deleted file mode 100644 index 97aefcb..0000000 --- a/src/third_party/skia/tests/DocumentTest.cpp +++ /dev/null
@@ -1,105 +0,0 @@ -#include "Test.h" - -#include "SkCanvas.h" -#include "SkDocument.h" -#include "SkOSFile.h" -#include "SkStream.h" - -static void test_empty(skiatest::Reporter* reporter) { - SkDynamicMemoryWStream stream; - - SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&stream)); - - doc->close(); - - REPORTER_ASSERT(reporter, stream.bytesWritten() == 0); -} - -static void test_abort(skiatest::Reporter* reporter) { - SkDynamicMemoryWStream stream; - SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&stream)); - - SkCanvas* canvas = doc->beginPage(100, 100); - canvas->drawColor(SK_ColorRED); - doc->endPage(); - - doc->abort(); - - REPORTER_ASSERT(reporter, stream.bytesWritten() == 0); -} - -static void test_abortWithFile(skiatest::Reporter* reporter) { - SkString tmpDir = skiatest::Test::GetTmpDir(); - - if (tmpDir.isEmpty()) { - return; // TODO(edisonn): unfortunatelly this pattern is used in other - // tests, but if GetTmpDir() starts returning and empty dir - // allways, then all these tests will be disabled. - } - - SkString path = SkOSPath::Join(tmpDir.c_str(), "aborted.pdf"); - - // Make sure doc's destructor is called to flush. - { - SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(path.c_str())); - - SkCanvas* canvas = doc->beginPage(100, 100); - canvas->drawColor(SK_ColorRED); - doc->endPage(); - - doc->abort(); - } - - FILE* file = fopen(path.c_str(), "r"); - // The created file should be empty. - char buffer[100]; - REPORTER_ASSERT(reporter, fread(buffer, 1, 1, file) == 0); - fclose(file); -} - -static void test_file(skiatest::Reporter* reporter) { - SkString tmpDir = skiatest::Test::GetTmpDir(); - if (tmpDir.isEmpty()) { - return; // TODO(edisonn): unfortunatelly this pattern is used in other - // tests, but if GetTmpDir() starts returning and empty dir - // allways, then all these tests will be disabled. - } - - SkString path = SkOSPath::Join(tmpDir.c_str(), "file.pdf"); - - SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(path.c_str())); - - SkCanvas* canvas = doc->beginPage(100, 100); - - canvas->drawColor(SK_ColorRED); - doc->endPage(); - doc->close(); - - FILE* file = fopen(path.c_str(), "r"); - REPORTER_ASSERT(reporter, file != NULL); - char header[100]; - REPORTER_ASSERT(reporter, fread(header, 4, 1, file) != 0); - REPORTER_ASSERT(reporter, strncmp(header, "%PDF", 4) == 0); - fclose(file); -} - -static void test_close(skiatest::Reporter* reporter) { - SkDynamicMemoryWStream stream; - SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&stream)); - - SkCanvas* canvas = doc->beginPage(100, 100); - canvas->drawColor(SK_ColorRED); - doc->endPage(); - - doc->close(); - - REPORTER_ASSERT(reporter, stream.bytesWritten() != 0); -} - -DEF_TEST(document_tests, reporter) { - test_empty(reporter); - test_abort(reporter); - test_abortWithFile(reporter); - test_file(reporter); - test_close(reporter); -}
diff --git a/src/third_party/skia/tests/DrawBitmapRectTest.cpp b/src/third_party/skia/tests/DrawBitmapRectTest.cpp index 71ad2cf..9912901 100644 --- a/src/third_party/skia/tests/DrawBitmapRectTest.cpp +++ b/src/third_party/skia/tests/DrawBitmapRectTest.cpp
@@ -9,58 +9,16 @@ #include "SkCanvas.h" #include "SkData.h" #include "SkDiscardableMemoryPool.h" -#include "SkImageGeneratorPriv.h" +#include "SkImageGenerator.h" #include "SkMatrixUtils.h" #include "SkPaint.h" +#include "SkPath.h" +#include "SkPixelRef.h" #include "SkRandom.h" #include "SkShader.h" #include "SkSurface.h" #include "Test.h" -// A BitmapFactory that always fails when asked to return pixels. -class FailureImageGenerator : public SkImageGenerator { -public: - FailureImageGenerator() { } - virtual ~FailureImageGenerator() { } - -protected: - virtual bool onGetInfo(SkImageInfo* info) SK_OVERRIDE { - *info = SkImageInfo::MakeN32Premul(100, 100); - return true; - } - // default onGetPixels() returns false, which is what we want. -}; - -// crbug.com/295895 -// Crashing in skia when a pixelref fails in lockPixels -// -static void test_faulty_pixelref(skiatest::Reporter* reporter) { - // need a cache, but don't expect to use it, so the budget is not critical - SkAutoTUnref<SkDiscardableMemoryPool> pool( - SkDiscardableMemoryPool::Create(10 * 1000, NULL)); - SkBitmap bm; - bool installSuccess = SkInstallDiscardablePixelRef(SkNEW(FailureImageGenerator), &bm, pool); - REPORTER_ASSERT(reporter, installSuccess); - // now our bitmap has a pixelref, but we know it will fail to lock - - SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(200, 200)); - SkCanvas* canvas = surface->getCanvas(); - - const SkPaint::FilterLevel levels[] = { - SkPaint::kNone_FilterLevel, - SkPaint::kLow_FilterLevel, - SkPaint::kMedium_FilterLevel, - SkPaint::kHigh_FilterLevel, - }; - - SkPaint paint; - canvas->scale(2, 2); // need a scale, otherwise we may ignore filtering - for (size_t i = 0; i < SK_ARRAY_COUNT(levels); ++i) { - paint.setFilterLevel(levels[i]); - canvas->drawBitmap(bm, 0, 0, &paint); - } -} - /////////////////////////////////////////////////////////////////////////////// static void rand_matrix(SkMatrix* mat, SkRandom& rand, unsigned mask) { @@ -84,24 +42,22 @@ size->set(rand.nextU() & 0xFFFF, rand.nextU() & 0xFFFF); } -static bool treat_as_sprite(const SkMatrix& mat, const SkISize& size, - unsigned bits) { - return SkTreatAsSprite(mat, size.width(), size.height(), bits); -} - static void test_treatAsSprite(skiatest::Reporter* reporter) { - const unsigned bilerBits = kSkSubPixelBitsForBilerp; SkMatrix mat; SkISize size; SkRandom rand; - // assert: translate-only no-filter can always be treated as sprite + SkPaint noaaPaint; + SkPaint aaPaint; + aaPaint.setAntiAlias(true); + + // assert: translate-only no-aa can always be treated as sprite for (int i = 0; i < 1000; ++i) { rand_matrix(&mat, rand, SkMatrix::kTranslate_Mask); for (int j = 0; j < 1000; ++j) { rand_size(&size, rand); - REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, 0)); + REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, noaaPaint)); } } @@ -110,8 +66,8 @@ rand_matrix(&mat, rand, SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask); for (int j = 0; j < 1000; ++j) { rand_size(&size, rand); - REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, 0)); - REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits)); + REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, noaaPaint)); + REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, aaPaint)); } } @@ -119,33 +75,33 @@ const SkScalar tooMuchSubpixel = 100.1f; mat.setTranslate(tooMuchSubpixel, 0); - REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits)); + REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, aaPaint)); mat.setTranslate(0, tooMuchSubpixel); - REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits)); + REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, aaPaint)); const SkScalar tinySubPixel = 100.02f; mat.setTranslate(tinySubPixel, 0); - REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, bilerBits)); + REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, aaPaint)); mat.setTranslate(0, tinySubPixel); - REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, bilerBits)); + REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, aaPaint)); const SkScalar twoThirds = SK_Scalar1 * 2 / 3; - const SkScalar bigScale = SkScalarDiv(size.width() + twoThirds, size.width()); + const SkScalar bigScale = (size.width() + twoThirds) / size.width(); mat.setScale(bigScale, bigScale); - REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, false)); - REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits)); + REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, noaaPaint)); + REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, aaPaint)); const SkScalar oneThird = SK_Scalar1 / 3; - const SkScalar smallScale = SkScalarDiv(size.width() + oneThird, size.width()); + const SkScalar smallScale = (size.width() + oneThird) / size.width(); mat.setScale(smallScale, smallScale); - REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, false)); - REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits)); + REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, noaaPaint)); + REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, aaPaint)); const SkScalar oneFortyth = SK_Scalar1 / 40; - const SkScalar tinyScale = SkScalarDiv(size.width() + oneFortyth, size.width()); + const SkScalar tinyScale = (size.width() + oneFortyth) / size.width(); mat.setScale(tinyScale, tinyScale); - REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, false)); - REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, bilerBits)); + REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, noaaPaint)); + REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, aaPaint)); } static void assert_ifDrawnTo(skiatest::Reporter* reporter, @@ -199,11 +155,9 @@ 0.0078740157f, SkIntToScalar(239), 0, 0, SK_Scalar1); - SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, - SkShader::kRepeat_TileMode, &matrix); - SkPaint paint; - paint.setShader(s)->unref(); + paint.setShader(SkShader::MakeBitmapShader(bm, SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode, &matrix)); SkRect r = SkRect::MakeXYWH(681, 239, 695, 253); c.drawRect(r, paint); @@ -272,8 +226,6 @@ } static bool check_for_all_zeros(const SkBitmap& bm) { - SkAutoLockPixels alp(bm); - size_t count = bm.width() * bm.bytesPerPixel(); for (int y = 0; y < bm.height(); y++) { const uint8_t* ptr = reinterpret_cast<const uint8_t*>(bm.getAddr(0, y)); @@ -305,7 +257,7 @@ SkIRect srcR = { gWidth, 0, gWidth + 16, 16 }; SkRect dstR = { 0, 0, SkIntToScalar(16), SkIntToScalar(16) }; - canvas.drawBitmapRect(src, &srcR, dstR, NULL); + canvas.drawBitmapRect(src, srcR, dstR, nullptr); // ensure that we draw nothing if srcR does not intersect the bitmap REPORTER_ASSERT(reporter, check_for_all_zeros(dst)); @@ -314,5 +266,4 @@ test_giantrepeat_crbug118018(reporter); test_treatAsSprite(reporter); - test_faulty_pixelref(reporter); }
diff --git a/src/third_party/skia/tests/DrawFilterTest.cpp b/src/third_party/skia/tests/DrawFilterTest.cpp new file mode 100644 index 0000000..27c9659 --- /dev/null +++ b/src/third_party/skia/tests/DrawFilterTest.cpp
@@ -0,0 +1,49 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCanvas.h" +#include "SkDrawFilter.h" +#include "SkSurface.h" +#include "Test.h" + +#ifdef SK_SUPPORT_LEGACY_DRAWFILTER + +namespace { +class TestFilter : public SkDrawFilter { +public: + bool filter(SkPaint* p, Type) override { + return true; + } +}; +} + +/** + * canvas.setDrawFilter is defined to be local to the save/restore block, such that if you + * do the following: save / modify-drawfilter / restore, the current drawfilter should be what + * it was before the save. + */ +static void test_saverestore(skiatest::Reporter* reporter) { + auto surface(SkSurface::MakeRasterN32Premul(10, 10)); + SkCanvas* canvas = surface->getCanvas(); + + sk_sp<TestFilter> df(new TestFilter); + + REPORTER_ASSERT(reporter, nullptr == canvas->getDrawFilter()); + + canvas->save(); + canvas->setDrawFilter(df.get()); + REPORTER_ASSERT(reporter, nullptr != canvas->getDrawFilter()); + canvas->restore(); + + REPORTER_ASSERT(reporter, nullptr == canvas->getDrawFilter()); +} + +DEF_TEST(DrawFilter, reporter) { + test_saverestore(reporter); +} + +#endif
diff --git a/src/third_party/skia/tests/DrawPathTest.cpp b/src/third_party/skia/tests/DrawPathTest.cpp index 2af4224..2434bdf 100644 --- a/src/third_party/skia/tests/DrawPathTest.cpp +++ b/src/third_party/skia/tests/DrawPathTest.cpp
@@ -8,6 +8,7 @@ #include "SkBitmap.h" #include "SkCanvas.h" #include "SkDashPathEffect.h" +#include "SkStrokeRec.h" #include "SkSurface.h" #include "Test.h" @@ -17,7 +18,7 @@ SkPMColor pixel[1]; output.installPixels(SkImageInfo::MakeN32Premul(1, 1), pixel, 4); - SkSurface* surf = SkSurface::NewRasterPMColor(300, 33300); + auto surf = SkSurface::MakeRasterN32Premul(300, 33300); SkCanvas* canvas = surf->getCanvas(); SkRect r = { 0, 33000, 300, 33300 }; @@ -25,7 +26,7 @@ int y = SkScalarRoundToInt(r.top()); // check that the pixel in question starts as transparent (by the surface) - if (canvas->readPixels(&output, x, y)) { + if (canvas->readPixels(output, x, y)) { REPORTER_ASSERT(reporter, 0 == pixel[0]); } else { REPORTER_ASSERT_MESSAGE(reporter, false, "readPixels failed"); @@ -38,14 +39,13 @@ canvas->drawRect(r, paint); // Now check that it is BLACK - if (canvas->readPixels(&output, x, y)) { + if (canvas->readPixels(output, x, y)) { // don't know what swizzling PMColor did, but white should always // appear the same. REPORTER_ASSERT(reporter, 0xFFFFFFFF == pixel[0]); } else { REPORTER_ASSERT_MESSAGE(reporter, false, "readPixels failed"); } - surf->unref(); } /////////////////////////////////////////////////////////////////////////////// @@ -94,11 +94,11 @@ moveToH(&path, &data[0]); cubicToH(&path, &data[2]); - SkAutoTUnref<SkCanvas> canvas(SkCanvas::NewRasterN32(640, 480)); + auto surface(SkSurface::MakeRasterN32Premul(640, 480)); SkPaint paint; paint.setAntiAlias(true); - canvas->drawPath(path, paint); + surface->getCanvas()->drawPath(path, paint); } // This used to assert in debug builds (and crash writing bad memory in release) @@ -125,38 +125,36 @@ static void test_inversepathwithclip() { SkPath path; - path.moveTo(0, SkIntToScalar(20)); - path.quadTo(SkIntToScalar(10), SkIntToScalar(10), - SkIntToScalar(20), SkIntToScalar(20)); + path.moveTo(0, 20); + path.quadTo(10, 10, 20, 20); path.toggleInverseFillType(); SkPaint paint; - SkAutoTUnref<SkCanvas> canvas(SkCanvas::NewRasterN32(640, 480)); - canvas.get()->save(); - canvas.get()->clipRect(SkRect::MakeWH(SkIntToScalar(19), SkIntToScalar(11))); + auto surface(SkSurface::MakeRasterN32Premul(640, 480)); + SkCanvas* canvas = surface->getCanvas(); + canvas->save(); + canvas->clipRect(SkRect::MakeWH(19, 11)); paint.setAntiAlias(false); - canvas.get()->drawPath(path, paint); + canvas->drawPath(path, paint); paint.setAntiAlias(true); - canvas.get()->drawPath(path, paint); + canvas->drawPath(path, paint); - canvas.get()->restore(); + canvas->restore(); // Now do the test again, with the path flipped, so we only draw in the // top half of our bounds, and have the clip intersect our bounds at the // bottom. path.reset(); // preserves our filltype - path.moveTo(0, SkIntToScalar(10)); - path.quadTo(SkIntToScalar(10), SkIntToScalar(20), - SkIntToScalar(20), SkIntToScalar(10)); - canvas.get()->clipRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(19), - SkIntToScalar(19), SkIntToScalar(11))); + path.moveTo(0, 10); + path.quadTo(10, 20, 20, 10); + canvas->clipRect(SkRect::MakeXYWH(0, 19, 19, 11)); paint.setAntiAlias(false); - canvas.get()->drawPath(path, paint); + canvas->drawPath(path, paint); paint.setAntiAlias(true); - canvas.get()->drawPath(path, paint); + canvas->drawPath(path, paint); } static void test_bug533() { @@ -172,8 +170,8 @@ SkPaint paint; paint.setAntiAlias(true); - SkAutoTUnref<SkCanvas> canvas(SkCanvas::NewRasterN32(640, 480)); - canvas.get()->drawPath(path, paint); + auto surface(SkSurface::MakeRasterN32Premul(640, 480)); + surface->getCanvas()->drawPath(path, paint); } static void test_crbug_140642() { @@ -193,7 +191,7 @@ */ const SkScalar vals[] = { 27734, 35660, 2157846850.0f, 247 }; - SkAutoTUnref<SkDashPathEffect> dontAssert(SkDashPathEffect::Create(vals, 4, -248.135982067f)); + auto dontAssert = SkDashPathEffect::Make(vals, 4, -248.135982067f); } static void test_crbug_124652() { @@ -203,7 +201,7 @@ large values can "swamp" small ones. */ SkScalar intervals[2] = {837099584, 33450}; - SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, 2, -10)); + auto dontAssert = SkDashPathEffect::Make(intervals, 2, -10); } static void test_bigcubic() { @@ -214,8 +212,47 @@ SkPaint paint; paint.setAntiAlias(true); - SkAutoTUnref<SkCanvas> canvas(SkCanvas::NewRasterN32(640, 480)); - canvas.get()->drawPath(path, paint); + auto surface(SkSurface::MakeRasterN32Premul(640, 480)); + surface->getCanvas()->drawPath(path, paint); +} + +// asserts if halfway case is not handled +static void test_halfway() { + SkPaint paint; + SkPath path; + path.moveTo(16365.5f, 1394); + path.lineTo(16365.5f, 1387.5f); + path.quadTo(16365.5f, 1385.43f, 16367, 1383.96f); + path.quadTo(16368.4f, 1382.5f, 16370.5f, 1382.5f); + path.lineTo(16465.5f, 1382.5f); + path.quadTo(16467.6f, 1382.5f, 16469, 1383.96f); + path.quadTo(16470.5f, 1385.43f, 16470.5f, 1387.5f); + path.lineTo(16470.5f, 1394); + path.quadTo(16470.5f, 1396.07f, 16469, 1397.54f); + path.quadTo(16467.6f, 1399, 16465.5f, 1399); + path.lineTo(16370.5f, 1399); + path.quadTo(16368.4f, 1399, 16367, 1397.54f); + path.quadTo(16365.5f, 1396.07f, 16365.5f, 1394); + path.close(); + SkPath p2; + SkMatrix m; + m.reset(); + m.postTranslate(0.001f, 0.001f); + path.transform(m, &p2); + + auto surface(SkSurface::MakeRasterN32Premul(640, 480)); + SkCanvas* canvas = surface->getCanvas(); + canvas->translate(-16366, -1383); + canvas->drawPath(p2, paint); + + m.reset(); + m.postTranslate(-0.001f, -0.001f); + path.transform(m, &p2); + canvas->drawPath(p2, paint); + + m.reset(); + path.transform(m, &p2); + canvas->drawPath(p2, paint); } // we used to assert if the bounds of the device (clip) was larger than 32K @@ -224,13 +261,13 @@ static void test_giantaa() { const int W = 400; const int H = 400; - SkAutoTUnref<SkCanvas> canvas(SkCanvas::NewRasterN32(33000, 10)); + auto surface(SkSurface::MakeRasterN32Premul(33000, 10)); SkPaint paint; paint.setAntiAlias(true); SkPath path; path.addOval(SkRect::MakeXYWH(-10, -10, 20 + W, 20 + H)); - canvas.get()->drawPath(path, paint); + surface->getCanvas()->drawPath(path, paint); } // Extremely large path_length/dash_length ratios may cause infinite looping @@ -243,7 +280,7 @@ path.lineTo(5000000, 0); SkScalar intervals[] = { 0.2f, 0.2f }; - SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, 2, 0)); + sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, 2, 0)); SkPath filteredPath; SkPaint paint; @@ -263,7 +300,7 @@ path.lineTo(10000000, 0); SkScalar intervals[] = { 0.5f, 0.5f }; - SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, 2, 0)); + sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, 2, 0)); SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); @@ -271,10 +308,43 @@ SkPath filteredPath; SkStrokeRec rec(paint); - REPORTER_ASSERT(reporter, !dash->filterPath(&filteredPath, path, &rec, NULL)); + REPORTER_ASSERT(reporter, !dash->filterPath(&filteredPath, path, &rec, nullptr)); REPORTER_ASSERT(reporter, filteredPath.isEmpty()); } +// http://crbug.com/472147 +// This is a simplified version from the bug. RRect radii not properly scaled. +static void test_crbug_472147_simple(skiatest::Reporter* reporter) { + auto surface(SkSurface::MakeRasterN32Premul(1000, 1000)); + SkCanvas* canvas = surface->getCanvas(); + SkPaint p; + SkRect r = SkRect::MakeLTRB(-246.0f, 33.0f, 848.0f, 33554464.0f); + SkVector radii[4] = { + { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554430.0f }, { 120.0f, 5.0f } + }; + SkRRect rr; + rr.setRectRadii(r, radii); + canvas->drawRRect(rr, p); +} + +// http://crbug.com/472147 +// RRect radii not properly scaled. +static void test_crbug_472147_actual(skiatest::Reporter* reporter) { + auto surface(SkSurface::MakeRasterN32Premul(1000, 1000)); + SkCanvas* canvas = surface->getCanvas(); + SkPaint p; + SkRect r = SkRect::MakeLTRB(-246.0f, 33.0f, 848.0f, 33554464.0f); + SkVector radii[4] = { + { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554430.0f }, { 120.0f, 5.0f } + }; + SkRRect rr; + rr.setRectRadii(r, radii); + canvas->clipRRect(rr); + + SkRect r2 = SkRect::MakeLTRB(0, 33, 1102, 33554464); + canvas->drawRect(r2, p); +} + DEF_TEST(DrawPath, reporter) { test_giantaa(); test_bug533(); @@ -287,5 +357,8 @@ if (false) test_crbug131181(); test_infinite_dash(reporter); test_crbug_165432(reporter); + test_crbug_472147_simple(reporter); + test_crbug_472147_actual(reporter); test_big_aa_rect(reporter); + test_halfway(); }
diff --git a/src/third_party/skia/tests/DrawTextTest.cpp b/src/third_party/skia/tests/DrawTextTest.cpp index 3a96c4f..0134d05 100644 --- a/src/third_party/skia/tests/DrawTextTest.cpp +++ b/src/third_party/skia/tests/DrawTextTest.cpp
@@ -11,8 +11,10 @@ #include "SkPaint.h" #include "SkPoint.h" #include "SkRect.h" +#include "SkSurface.h" #include "SkTypes.h" #include "Test.h" +#include <math.h> static const SkColor bgColor = SK_ColorWHITE; @@ -35,9 +37,6 @@ const int xOff = itest.fLeft - iref.fLeft; const int yOff = itest.fTop - iref.fTop; - SkAutoLockPixels alpRef(ref); - SkAutoLockPixels alpTest(test); - for (int y = 0; y < test.height(); ++y) { for (int x = 0; x < test.width(); ++x) { SkColor testColor = test.getColor(x, y); @@ -74,35 +73,96 @@ create(&drawPosTextBitmap, drawPosTextRect); SkCanvas drawPosTextCanvas(drawPosTextBitmap); - for (float offsetY = 0.0f; offsetY < 1.0f; offsetY += (1.0f / 16.0f)) { - for (float offsetX = 0.0f; offsetX < 1.0f; offsetX += (1.0f / 16.0f)) { - SkPoint point = SkPoint::Make(25.0f + offsetX, - 25.0f + offsetY); + // Two test cases "A" for the normal path through the code, and " " to check the + // early return path. + const char* cases[] = {"A", " "}; + for (auto c : cases) { + for (float offsetY = 0.0f; offsetY < 1.0f; offsetY += (1.0f / 16.0f)) { + for (float offsetX = 0.0f; offsetX < 1.0f; offsetX += (1.0f / 16.0f)) { + SkPoint point = SkPoint::Make(25.0f + offsetX, + 25.0f + offsetY); - for (int align = 0; align < SkPaint::kAlignCount; ++align) { - paint.setTextAlign(static_cast<SkPaint::Align>(align)); + for (int align = 0; align < SkPaint::kAlignCount; ++align) { + paint.setTextAlign(static_cast<SkPaint::Align>(align)); - for (unsigned int flags = 0; flags < (1 << 3); ++flags) { - static const unsigned int antiAliasFlag = 1; - static const unsigned int subpixelFlag = 1 << 1; - static const unsigned int lcdFlag = 1 << 2; + for (unsigned int flags = 0; flags < (1 << 3); ++flags) { + static const unsigned int antiAliasFlag = 1; + static const unsigned int subpixelFlag = 1 << 1; + static const unsigned int lcdFlag = 1 << 2; - paint.setAntiAlias(SkToBool(flags & antiAliasFlag)); - paint.setSubpixelText(SkToBool(flags & subpixelFlag)); - paint.setLCDRenderText(SkToBool(flags & lcdFlag)); + paint.setAntiAlias(SkToBool(flags & antiAliasFlag)); + paint.setSubpixelText(SkToBool(flags & subpixelFlag)); + paint.setLCDRenderText(SkToBool(flags & lcdFlag)); - // Test: drawText and drawPosText draw the same. - drawBG(&drawTextCanvas); - drawTextCanvas.drawText("A", 1, point.fX, point.fY, paint); + // Test: drawText and drawPosText draw the same. + drawBG(&drawTextCanvas); + drawTextCanvas.drawText(c, 1, point.fX, point.fY, paint); - drawBG(&drawPosTextCanvas); - drawPosTextCanvas.drawPosText("A", 1, &point, paint); + drawBG(&drawPosTextCanvas); + drawPosTextCanvas.drawPosText(c, 1, &point, paint); - REPORTER_ASSERT(reporter, - compare(drawTextBitmap, drawTextRect, - drawPosTextBitmap, drawPosTextRect)); + REPORTER_ASSERT(reporter, + compare(drawTextBitmap, drawTextRect, + drawPosTextBitmap, drawPosTextRect)); + } } } } } } + +// Test drawing text at some unusual coordinates. +// We measure success by not crashing or asserting. +DEF_TEST(DrawText_weirdCoordinates, r) { + auto surface = SkSurface::MakeRasterN32Premul(10,10); + auto canvas = surface->getCanvas(); + + SkScalar oddballs[] = { 0.0f, (float)INFINITY, (float)NAN, 34359738368.0f }; + + for (auto x : oddballs) { + canvas->drawString("a", +x, 0.0f, SkPaint()); + canvas->drawString("a", -x, 0.0f, SkPaint()); + } + for (auto y : oddballs) { + canvas->drawString("a", 0.0f, +y, SkPaint()); + canvas->drawString("a", 0.0f, -y, SkPaint()); + } +} + +// Test drawing text with some unusual matricies. +// We measure success by not crashing or asserting. +DEF_TEST(DrawText_weirdMatricies, r) { + auto surface = SkSurface::MakeRasterN32Premul(100,100); + auto canvas = surface->getCanvas(); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setLCDRenderText(true); + + struct { + SkScalar textSize; + SkScalar matrix[9]; + } testCases[] = { + // 2x2 singular + {10, { 0, 0, 0, 0, 0, 0, 0, 0, 1}}, + {10, { 0, 0, 0, 0, 1, 0, 0, 0, 1}}, + {10, { 0, 0, 0, 1, 0, 0, 0, 0, 1}}, + {10, { 0, 0, 0, 1, 1, 0, 0, 0, 1}}, + {10, { 0, 1, 0, 0, 1, 0, 0, 0, 1}}, + {10, { 1, 0, 0, 0, 0, 0, 0, 0, 1}}, + {10, { 1, 0, 0, 1, 0, 0, 0, 0, 1}}, + {10, { 1, 1, 0, 0, 0, 0, 0, 0, 1}}, + {10, { 1, 1, 0, 1, 1, 0, 0, 0, 1}}, + // See https://bugzilla.mozilla.org/show_bug.cgi?id=1305085 . + { 1, {10, 20, 0, 20, 40, 0, 0, 0, 1}}, + }; + + for (const auto& testCase : testCases) { + paint.setTextSize(testCase.textSize); + const SkScalar(&m)[9] = testCase.matrix; + SkMatrix mat; + mat.setAll(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); + canvas->setMatrix(mat); + canvas->drawString("Hamburgefons", 10, 10, paint); + } +}
diff --git a/src/third_party/skia/tests/DynamicHashTest.cpp b/src/third_party/skia/tests/DynamicHashTest.cpp index 4a5bb85..289b332 100644 --- a/src/third_party/skia/tests/DynamicHashTest.cpp +++ b/src/third_party/skia/tests/DynamicHashTest.cpp
@@ -100,14 +100,14 @@ ASSERT(hash.countCollisions(9) == 2); // We can find our data right? - ASSERT(hash.find(1) != NULL); + ASSERT(hash.find(1) != nullptr); ASSERT(hash.find(1)->value == 2.0); - ASSERT(hash.find(5) != NULL); + ASSERT(hash.find(5) != nullptr); ASSERT(hash.find(5)->value == 3.0); // These aren't in the hash. - ASSERT(hash.find(2) == NULL); - ASSERT(hash.find(9) == NULL); + ASSERT(hash.find(2) == nullptr); + ASSERT(hash.find(9) == nullptr); } DEF_TEST(DynamicHash_remove, reporter) { @@ -123,16 +123,16 @@ hash.remove(1); // a should be marked deleted, and b should still be findable. - ASSERT(hash.find(1) == NULL); - ASSERT(hash.find(5) != NULL); + ASSERT(hash.find(1) == nullptr); + ASSERT(hash.find(5) != nullptr); ASSERT(hash.find(5)->value == 3.0); // This will go in the same slot as 'a' did before. ASSERT(hash.countCollisions(9) == 0); hash.add(&c); - ASSERT(hash.find(9) != NULL); + ASSERT(hash.find(9) != nullptr); ASSERT(hash.find(9)->value == 4.0); - ASSERT(hash.find(5) != NULL); + ASSERT(hash.find(5) != nullptr); ASSERT(hash.find(5)->value == 3.0); } @@ -161,7 +161,7 @@ for (T iter(&hash); !iter.done(); ++iter) { int key = (*iter).key; keys[count] = key; - ASSERT(hash.find(key) != NULL); + ASSERT(hash.find(key) != nullptr); ++count; } ASSERT(3 == count); @@ -177,7 +177,7 @@ int key = (*iter).key; keys[count] = key; ASSERT(key != 1); - ASSERT(hash.find(key) != NULL); + ASSERT(hash.find(key) != nullptr); ++count; } ASSERT(2 == count); @@ -215,8 +215,8 @@ ASSERT(hash.count() == 2); ASSERT(hash.capacity() == 4); - ASSERT(hash.find(1) != NULL); - ASSERT(hash.find(2) != NULL); + ASSERT(hash.find(1) != nullptr); + ASSERT(hash.find(2) != nullptr); } DEF_TEST(DynamicHash_reset, reporter) {
diff --git a/src/third_party/skia/tests/EGLImageTest.cpp b/src/third_party/skia/tests/EGLImageTest.cpp new file mode 100644 index 0000000..84a6bfd --- /dev/null +++ b/src/third_party/skia/tests/EGLImageTest.cpp
@@ -0,0 +1,175 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" +#include "TestUtils.h" +#if SK_SUPPORT_GPU +#include "GrContext.h" +#include "GrContextFactory.h" +#include "GrContextPriv.h" +#include "GrRenderTargetContext.h" +#include "GrShaderCaps.h" +#include "GrTest.h" +#include "GrTextureContext.h" +#include "gl/GLTestContext.h" +#include "gl/GrGLGpu.h" +#include "gl/GrGLUtil.h" + +using sk_gpu_test::GLTestContext; + +static void cleanup(GLTestContext* glctx0, GrGLuint texID0, GLTestContext* glctx1, GrContext* grctx1, + const GrGLTextureInfo* grbackendtex1, GrEGLImage image1) { + if (glctx1) { + glctx1->makeCurrent(); + if (grctx1) { + if (grbackendtex1) { + GrGLGpu* gpu1 = static_cast<GrGLGpu*>(grctx1->getGpu()); + GrBackendObject handle = reinterpret_cast<GrBackendObject>(grbackendtex1); + gpu1->deleteTestingOnlyBackendTexture(handle, false); + } + grctx1->unref(); + } + if (GR_EGL_NO_IMAGE != image1) { + glctx1->destroyEGLImage(image1); + } + } + + glctx0->makeCurrent(); + if (texID0) { + GR_GL_CALL(glctx0->gl(), DeleteTextures(1, &texID0)); + } +} + +DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(EGLImageTest, reporter, ctxInfo) { + GrContext* context0 = ctxInfo.grContext(); + sk_gpu_test::GLTestContext* glCtx0 = ctxInfo.glContext(); + + // Try to create a second GL context and then check if the contexts have necessary + // extensions to run this test. + + if (kGLES_GrGLStandard != glCtx0->gl()->fStandard) { + return; + } + GrGLGpu* gpu0 = static_cast<GrGLGpu*>(context0->getGpu()); + if (!gpu0->glCaps().shaderCaps()->externalTextureSupport()) { + return; + } + + std::unique_ptr<GLTestContext> glCtx1 = glCtx0->makeNew(); + if (!glCtx1) { + return; + } + GrContext* context1 = GrContext::Create(kOpenGL_GrBackend, (GrBackendContext)glCtx1->gl()); + const GrGLTextureInfo* backendTexture1 = nullptr; + GrEGLImage image = GR_EGL_NO_IMAGE; + GrGLTextureInfo externalTexture; + externalTexture.fID = 0; + + if (!context1) { + cleanup(glCtx0, externalTexture.fID, glCtx1.get(), context1, backendTexture1, image); + return; + } + + if (!glCtx1->gl()->hasExtension("EGL_KHR_image") || + !glCtx1->gl()->hasExtension("EGL_KHR_gl_texture_2D_image")) { + cleanup(glCtx0, externalTexture.fID, glCtx1.get(), context1, backendTexture1, image); + return; + } + + ///////////////////////////////// CONTEXT 1 /////////////////////////////////// + + // Use GL Context 1 to create a texture unknown to GrContext. + context1->flush(); + GrGpu* gpu1 = context1->getGpu(); + static const int kSize = 100; + backendTexture1 = reinterpret_cast<const GrGLTextureInfo*>( + gpu1->createTestingOnlyBackendTexture(nullptr, kSize, kSize, kRGBA_8888_GrPixelConfig)); + if (!backendTexture1 || !backendTexture1->fID) { + ERRORF(reporter, "Error creating texture for EGL Image"); + cleanup(glCtx0, externalTexture.fID, glCtx1.get(), context1, backendTexture1, image); + return; + } + if (GR_GL_TEXTURE_2D != backendTexture1->fTarget) { + ERRORF(reporter, "Expected backend texture to be 2D"); + cleanup(glCtx0, externalTexture.fID, glCtx1.get(), context1, backendTexture1, image); + return; + } + + // Wrap the texture in an EGLImage + image = glCtx1->texture2DToEGLImage(backendTexture1->fID); + if (GR_EGL_NO_IMAGE == image) { + ERRORF(reporter, "Error creating EGL Image from texture"); + cleanup(glCtx0, externalTexture.fID, glCtx1.get(), context1, backendTexture1, image); + return; + } + + // Populate the texture using GL context 1. Important to use TexSubImage as TexImage orphans + // the EGL image. Also, this must be done after creating the EGLImage as the texture + // contents may not be preserved when the image is created. + SkAutoTMalloc<uint32_t> pixels(kSize * kSize); + for (int i = 0; i < kSize*kSize; ++i) { + pixels.get()[i] = 0xDDAABBCC; + } + GR_GL_CALL(glCtx1->gl(), ActiveTexture(GR_GL_TEXTURE0)); + GR_GL_CALL(glCtx1->gl(), BindTexture(backendTexture1->fTarget, backendTexture1->fID)); + GR_GL_CALL(glCtx1->gl(), TexSubImage2D(backendTexture1->fTarget, 0, 0, 0, kSize, kSize, + GR_GL_RGBA, GR_GL_UNSIGNED_BYTE, pixels.get())); + GR_GL_CALL(glCtx1->gl(), Finish()); + // We've been making direct GL calls in GL context 1, let GrContext 1 know its internal + // state is invalid. + context1->resetContext(); + + ///////////////////////////////// CONTEXT 0 /////////////////////////////////// + + // Make a new texture ID in GL Context 0 from the EGL Image + glCtx0->makeCurrent(); + externalTexture.fTarget = GR_GL_TEXTURE_EXTERNAL; + externalTexture.fID = glCtx0->eglImageToExternalTexture(image); + if (0 == externalTexture.fID) { + ERRORF(reporter, "Error converting EGL Image back to texture"); + cleanup(glCtx0, externalTexture.fID, glCtx1.get(), context1, backendTexture1, image); + return; + } + + // Wrap this texture ID in a GrTexture + GrBackendTexture backendTex(kSize, kSize, kRGBA_8888_GrPixelConfig, externalTexture); + + // TODO: If I make this TopLeft origin to match resolve_origin calls for kDefault, this test + // fails on the Nexus5. Why? + sk_sp<GrTextureContext> surfaceContext = context0->contextPriv().makeBackendTextureContext( + backendTex, kBottomLeft_GrSurfaceOrigin, nullptr); + + if (!surfaceContext) { + ERRORF(reporter, "Error wrapping external texture in GrSurfaceContext."); + cleanup(glCtx0, externalTexture.fID, glCtx1.get(), context1, backendTexture1, image); + return; + } + + // Should not be able to wrap as a RT + { + sk_sp<GrRenderTargetContext> temp = + context0->contextPriv().makeBackendTextureRenderTargetContext( + backendTex, kBottomLeft_GrSurfaceOrigin, 0, nullptr); + if (temp) { + ERRORF(reporter, "Should not be able to wrap an EXTERNAL texture as a RT."); + } + } + + test_read_pixels(reporter, surfaceContext.get(), pixels.get(), "EGLImageTest-read"); + + // We should not be able to write to a EXTERNAL texture + test_write_pixels(reporter, surfaceContext.get(), false, "EGLImageTest-write"); + + // Only test RT-config + // TODO: why do we always need to draw to copy from an external texture? + test_copy_from_surface(reporter, context0, surfaceContext->asSurfaceProxy(), + pixels.get(), true, "EGLImageTest-copy"); + + cleanup(glCtx0, externalTexture.fID, glCtx1.get(), context1, backendTexture1, image); +} + +#endif
diff --git a/src/third_party/skia/tests/EmptyPathTest.cpp b/src/third_party/skia/tests/EmptyPathTest.cpp index c4f011a..5b857a0 100644 --- a/src/third_party/skia/tests/EmptyPathTest.cpp +++ b/src/third_party/skia/tests/EmptyPathTest.cpp
@@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include "SkBitmap.h" #include "SkCanvas.h" #include "SkPath.h" #include "Test.h" @@ -54,7 +55,13 @@ } } -static void iter_paint(skiatest::Reporter* reporter, const SkPath& path, bool shouldDraw) { +enum DrawCaps { + kDontDrawCaps, + kDrawCaps +}; + +static void iter_paint(skiatest::Reporter* reporter, const SkPath& path, bool shouldDraw, + DrawCaps drawCaps) { static const SkPaint::Cap gCaps[] = { SkPaint::kButt_Cap, SkPaint::kRound_Cap, @@ -73,6 +80,11 @@ for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) { for (size_t join = 0; join < SK_ARRAY_COUNT(gJoins); ++join) { for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) { + if (drawCaps && SkPaint::kButt_Cap != gCaps[cap] + && SkPaint::kFill_Style != gStyles[style]) { + continue; + } + SkPaint paint; paint.setStrokeWidth(SkIntToScalar(10)); @@ -127,10 +139,14 @@ if (doClose) { path.close(); } + /* zero length segments and close following moves draw round and square caps */ + bool allowCaps = make_L == gMakeProc[i] || make_Q == gMakeProc[i] + || make_C == gMakeProc[i] || make_MZM == gMakeProc[i]; + allowCaps |= SkToBool(doClose); for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) { path.setFillType(gFills[fill]); bool shouldDraw = path.isInverseFillType(); - iter_paint(reporter, path, shouldDraw); + iter_paint(reporter, path, shouldDraw, allowCaps ? kDrawCaps : kDontDrawCaps); } } }
diff --git a/src/third_party/skia/tests/EncodeTest.cpp b/src/third_party/skia/tests/EncodeTest.cpp new file mode 100644 index 0000000..6638c1e --- /dev/null +++ b/src/third_party/skia/tests/EncodeTest.cpp
@@ -0,0 +1,339 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Resources.h" +#include "Test.h" + +#include "SkBitmap.h" +#include "SkEncodedImageFormat.h" +#include "SkImage.h" +#include "SkJpegEncoder.h" +#include "SkPngEncoder.h" +#include "SkStream.h" +#include "SkWebpEncoder.h" + +#include "png.h" + +#include <algorithm> +#include <string> +#include <vector> + +static bool encode(SkEncodedImageFormat format, SkWStream* dst, const SkPixmap& src) { + switch (format) { + case SkEncodedImageFormat::kJPEG: + return SkJpegEncoder::Encode(dst, src, SkJpegEncoder::Options()); + case SkEncodedImageFormat::kPNG: + return SkPngEncoder::Encode(dst, src, SkPngEncoder::Options()); + default: + return false; + } +} + +static std::unique_ptr<SkEncoder> make(SkEncodedImageFormat format, SkWStream* dst, + const SkPixmap& src) { + switch (format) { + case SkEncodedImageFormat::kJPEG: + return SkJpegEncoder::Make(dst, src, SkJpegEncoder::Options()); + case SkEncodedImageFormat::kPNG: + return SkPngEncoder::Make(dst, src, SkPngEncoder::Options()); + default: + return nullptr; + } +} + +static void test_encode(skiatest::Reporter* r, SkEncodedImageFormat format) { + SkBitmap bitmap; + bool success = GetResourceAsBitmap("mandrill_128.png", &bitmap); + if (!success) { + return; + } + + SkPixmap src; + success = bitmap.peekPixels(&src); + REPORTER_ASSERT(r, success); + if (!success) { + return; + } + + SkDynamicMemoryWStream dst0, dst1, dst2, dst3; + success = encode(format, &dst0, src); + REPORTER_ASSERT(r, success); + + auto encoder1 = make(format, &dst1, src); + for (int i = 0; i < src.height(); i++) { + success = encoder1->encodeRows(1); + REPORTER_ASSERT(r, success); + } + + auto encoder2 = make(format, &dst2, src); + for (int i = 0; i < src.height(); i+=3) { + success = encoder2->encodeRows(3); + REPORTER_ASSERT(r, success); + } + + auto encoder3 = make(format, &dst3, src); + success = encoder3->encodeRows(200); + REPORTER_ASSERT(r, success); + + sk_sp<SkData> data0 = dst0.detachAsData(); + sk_sp<SkData> data1 = dst1.detachAsData(); + sk_sp<SkData> data2 = dst2.detachAsData(); + sk_sp<SkData> data3 = dst3.detachAsData(); + REPORTER_ASSERT(r, data0->equals(data1.get())); + REPORTER_ASSERT(r, data0->equals(data2.get())); + REPORTER_ASSERT(r, data0->equals(data3.get())); +} + +DEF_TEST(Encode, r) { + test_encode(r, SkEncodedImageFormat::kJPEG); + test_encode(r, SkEncodedImageFormat::kPNG); +} + +static inline bool almost_equals(SkPMColor a, SkPMColor b, int tolerance) { + if (SkTAbs((int)SkGetPackedR32(a) - (int)SkGetPackedR32(b)) > tolerance) { + return false; + } + + if (SkTAbs((int)SkGetPackedG32(a) - (int)SkGetPackedG32(b)) > tolerance) { + return false; + } + + if (SkTAbs((int)SkGetPackedB32(a) - (int)SkGetPackedB32(b)) > tolerance) { + return false; + } + + if (SkTAbs((int)SkGetPackedA32(a) - (int)SkGetPackedA32(b)) > tolerance) { + return false; + } + + return true; +} + +static inline bool almost_equals(const SkBitmap& a, const SkBitmap& b, int tolerance) { + if (a.info() != b.info()) { + return false; + } + + SkASSERT(kN32_SkColorType == a.colorType()); + for (int y = 0; y < a.height(); y++) { + for (int x = 0; x < a.width(); x++) { + if (!almost_equals(*a.getAddr32(x, y), *b.getAddr32(x, y), tolerance)) { + return false; + } + } + } + + return true; +} + +DEF_TEST(Encode_JpegDownsample, r) { + SkBitmap bitmap; + bool success = GetResourceAsBitmap("mandrill_128.png", &bitmap); + if (!success) { + return; + } + + SkPixmap src; + success = bitmap.peekPixels(&src); + REPORTER_ASSERT(r, success); + if (!success) { + return; + } + + SkDynamicMemoryWStream dst0, dst1, dst2; + SkJpegEncoder::Options options; + success = SkJpegEncoder::Encode(&dst0, src, options); + REPORTER_ASSERT(r, success); + + options.fDownsample = SkJpegEncoder::Downsample::k422; + success = SkJpegEncoder::Encode(&dst1, src, options); + REPORTER_ASSERT(r, success); + + options.fDownsample = SkJpegEncoder::Downsample::k444; + success = SkJpegEncoder::Encode(&dst2, src, options); + REPORTER_ASSERT(r, success); + + sk_sp<SkData> data0 = dst0.detachAsData(); + sk_sp<SkData> data1 = dst1.detachAsData(); + sk_sp<SkData> data2 = dst2.detachAsData(); + REPORTER_ASSERT(r, data0->size() < data1->size()); + REPORTER_ASSERT(r, data1->size() < data2->size()); + + SkBitmap bm0, bm1, bm2; + SkImage::MakeFromEncoded(data0)->asLegacyBitmap(&bm0, SkImage::kRO_LegacyBitmapMode); + SkImage::MakeFromEncoded(data1)->asLegacyBitmap(&bm1, SkImage::kRO_LegacyBitmapMode); + SkImage::MakeFromEncoded(data2)->asLegacyBitmap(&bm2, SkImage::kRO_LegacyBitmapMode); + REPORTER_ASSERT(r, almost_equals(bm0, bm1, 60)); + REPORTER_ASSERT(r, almost_equals(bm1, bm2, 60)); +} + +static inline void pushComment( + std::vector<std::string>& comments, const char* keyword, const char* text) { + comments.push_back(keyword); + comments.push_back(text); +} + +static void testPngComments(const SkPixmap& src, SkPngEncoder::Options& options, + skiatest::Reporter* r) { + std::vector<std::string> commentStrings; + pushComment(commentStrings, "key", "text"); + pushComment(commentStrings, "test", "something"); + pushComment(commentStrings, "have some", "spaces in both"); + + std::string longKey(PNG_KEYWORD_MAX_LENGTH, 'x'); +#ifdef SK_DEBUG + commentStrings.push_back(longKey); +#else + // We call SkDEBUGFAILF it the key is too long so we'll only test this in release mode. + commentStrings.push_back(longKey + "x"); +#endif + commentStrings.push_back(""); + + std::vector<const char*> commentPointers; + std::vector<size_t> commentSizes; + for(auto& str : commentStrings) { + commentPointers.push_back(str.c_str()); + commentSizes.push_back(str.length() + 1); + } + + options.fComments = SkDataTable::MakeCopyArrays((void const *const *)commentPointers.data(), + commentSizes.data(), commentStrings.size()); + + + SkDynamicMemoryWStream dst; + bool success = SkPngEncoder::Encode(&dst, src, options); + REPORTER_ASSERT(r, success); + + std::vector<char> output(dst.bytesWritten()); + dst.copyTo(output.data()); + + // Each chunk is of the form length (4 bytes), chunk type (tEXt), data, + // checksum (4 bytes). Make sure we find all of them in the encoded + // results. + const char kExpected1[] = + "\x00\x00\x00\x08tEXtkey\x00text\x9e\xe7\x66\x51"; + const char kExpected2[] = + "\x00\x00\x00\x0etEXttest\x00something\x29\xba\xef\xac"; + const char kExpected3[] = + "\x00\x00\x00\x18tEXthave some\x00spaces in both\x8d\x69\x34\x2d"; + std::string longKeyRecord = "tEXt" + longKey; // A snippet of our long key comment + std::string tooLongRecord = "tExt" + longKey + "x"; // A snippet whose key is too long + + auto search1 = std::search(output.begin(), output.end(), + kExpected1, kExpected1 + sizeof(kExpected1)); + auto search2 = std::search(output.begin(), output.end(), + kExpected2, kExpected2 + sizeof(kExpected2)); + auto search3 = std::search(output.begin(), output.end(), + kExpected3, kExpected3 + sizeof(kExpected3)); + auto search4 = std::search(output.begin(), output.end(), + longKeyRecord.begin(), longKeyRecord.end()); + auto search5 = std::search(output.begin(), output.end(), + tooLongRecord.begin(), tooLongRecord.end()); + + REPORTER_ASSERT(r, search1 != output.end()); + REPORTER_ASSERT(r, search2 != output.end()); + REPORTER_ASSERT(r, search3 != output.end()); + REPORTER_ASSERT(r, search4 != output.end()); + REPORTER_ASSERT(r, search5 == output.end()); + // Comments test ends +} + +DEF_TEST(Encode_PngOptions, r) { + SkBitmap bitmap; + bool success = GetResourceAsBitmap("mandrill_128.png", &bitmap); + if (!success) { + return; + } + + SkPixmap src; + success = bitmap.peekPixels(&src); + REPORTER_ASSERT(r, success); + if (!success) { + return; + } + + SkDynamicMemoryWStream dst0, dst1, dst2; + SkPngEncoder::Options options; + success = SkPngEncoder::Encode(&dst0, src, options); + REPORTER_ASSERT(r, success); + + options.fFilterFlags = SkPngEncoder::FilterFlag::kUp; + success = SkPngEncoder::Encode(&dst1, src, options); + REPORTER_ASSERT(r, success); + + options.fZLibLevel = 3; + success = SkPngEncoder::Encode(&dst2, src, options); + REPORTER_ASSERT(r, success); + + testPngComments(src, options, r); + + sk_sp<SkData> data0 = dst0.detachAsData(); + sk_sp<SkData> data1 = dst1.detachAsData(); + sk_sp<SkData> data2 = dst2.detachAsData(); + REPORTER_ASSERT(r, data0->size() < data1->size()); + REPORTER_ASSERT(r, data1->size() < data2->size()); + + SkBitmap bm0, bm1, bm2; + SkImage::MakeFromEncoded(data0)->asLegacyBitmap(&bm0, SkImage::kRO_LegacyBitmapMode); + SkImage::MakeFromEncoded(data1)->asLegacyBitmap(&bm1, SkImage::kRO_LegacyBitmapMode); + SkImage::MakeFromEncoded(data2)->asLegacyBitmap(&bm2, SkImage::kRO_LegacyBitmapMode); + REPORTER_ASSERT(r, almost_equals(bm0, bm1, 0)); + REPORTER_ASSERT(r, almost_equals(bm0, bm2, 0)); +} + +DEF_TEST(Encode_WebpOptions, r) { + SkBitmap bitmap; + bool success = GetResourceAsBitmap("google_chrome.ico", &bitmap); + if (!success) { + return; + } + + SkPixmap src; + success = bitmap.peekPixels(&src); + REPORTER_ASSERT(r, success); + if (!success) { + return; + } + + SkDynamicMemoryWStream dst0, dst1, dst2, dst3; + SkWebpEncoder::Options options; + options.fCompression = SkWebpEncoder::Compression::kLossless; + options.fQuality = 0.0f; + success = SkWebpEncoder::Encode(&dst0, src, options); + REPORTER_ASSERT(r, success); + + options.fQuality = 100.0f; + success = SkWebpEncoder::Encode(&dst1, src, options); + REPORTER_ASSERT(r, success); + + options.fCompression = SkWebpEncoder::Compression::kLossy; + options.fQuality = 100.0f; + success = SkWebpEncoder::Encode(&dst2, src, options); + REPORTER_ASSERT(r, success); + + options.fCompression = SkWebpEncoder::Compression::kLossy; + options.fQuality = 50.0f; + success = SkWebpEncoder::Encode(&dst3, src, options); + REPORTER_ASSERT(r, success); + + sk_sp<SkData> data0 = dst0.detachAsData(); + sk_sp<SkData> data1 = dst1.detachAsData(); + sk_sp<SkData> data2 = dst2.detachAsData(); + sk_sp<SkData> data3 = dst3.detachAsData(); + REPORTER_ASSERT(r, data0->size() > data1->size()); + REPORTER_ASSERT(r, data1->size() > data2->size()); + REPORTER_ASSERT(r, data2->size() > data3->size()); + + SkBitmap bm0, bm1, bm2, bm3; + SkImage::MakeFromEncoded(data0)->asLegacyBitmap(&bm0, SkImage::kRO_LegacyBitmapMode); + SkImage::MakeFromEncoded(data1)->asLegacyBitmap(&bm1, SkImage::kRO_LegacyBitmapMode); + SkImage::MakeFromEncoded(data2)->asLegacyBitmap(&bm2, SkImage::kRO_LegacyBitmapMode); + SkImage::MakeFromEncoded(data3)->asLegacyBitmap(&bm3, SkImage::kRO_LegacyBitmapMode); + REPORTER_ASSERT(r, almost_equals(bm0, bm1, 0)); + REPORTER_ASSERT(r, almost_equals(bm0, bm2, 90)); + REPORTER_ASSERT(r, almost_equals(bm2, bm3, 45)); +}
diff --git a/src/third_party/skia/tests/ErrorTest.cpp b/src/third_party/skia/tests/ErrorTest.cpp deleted file mode 100644 index 4802d53..0000000 --- a/src/third_party/skia/tests/ErrorTest.cpp +++ /dev/null
@@ -1,64 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkError.h" -#include "SkPath.h" -#include "SkRect.h" -#include "Test.h" - -typedef struct { - skiatest::Reporter *fReporter; - unsigned int *fIntPointer; -} ErrorContext; - -#define CHECK(errcode) \ - REPORTER_ASSERT( reporter, (err = SkGetLastError()) == errcode); \ - if (err != kNoError_SkError) \ - { \ - SkClearLastError(); \ - } - -static void cb(SkError err, void *context) { - ErrorContext *context_ptr = static_cast<ErrorContext *>(context); - REPORTER_ASSERT( context_ptr->fReporter, (*(context_ptr->fIntPointer) == 0xdeadbeef) ); -} - -DEF_TEST(Error, reporter) { - // Some previous user of this thread may have left an error laying around. - SkClearLastError(); - - SkError err; - - unsigned int test_value = 0xdeadbeef; - ErrorContext context; - context.fReporter = reporter; - context.fIntPointer = &test_value; - - SkSetErrorCallback(cb, &context); - - CHECK(kNoError_SkError); - - SkRect r = SkRect::MakeWH(50, 100); - CHECK(kNoError_SkError); - - SkPath path; - path.addRect(r); - CHECK(kNoError_SkError); - - path.addRoundRect(r, 10, 10); - CHECK(kNoError_SkError); - - // should trigger the default error callback, which just prints to the screen. - path.addRoundRect(r, -10, -10); - CHECK(kInvalidArgument_SkError); - CHECK(kNoError_SkError); - - // should trigger *our* callback. - path.addRoundRect(r, -10, -10); - CHECK(kInvalidArgument_SkError); - CHECK(kNoError_SkError); -}
diff --git a/src/third_party/skia/tests/ExifTest.cpp b/src/third_party/skia/tests/ExifTest.cpp new file mode 100644 index 0000000..4fed2b0 --- /dev/null +++ b/src/third_party/skia/tests/ExifTest.cpp
@@ -0,0 +1,29 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Resources.h" +#include "SkCodec.h" +#include "Test.h" + +DEF_TEST(ExifOrientation, r) { + std::unique_ptr<SkStream> stream(GetResourceAsStream("exif-orientation-2-ur.jpg")); + REPORTER_ASSERT(r, nullptr != stream); + if (!stream) { + return; + } + + std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release())); + REPORTER_ASSERT(r, nullptr != codec); + SkCodec::Origin origin = codec->getOrigin(); + REPORTER_ASSERT(r, SkCodec::kTopRight_Origin == origin); + + stream.reset(GetResourceAsStream("mandrill_512_q075.jpg")); + codec.reset(SkCodec::NewFromStream(stream.release())); + REPORTER_ASSERT(r, nullptr != codec); + origin = codec->getOrigin(); + REPORTER_ASSERT(r, SkCodec::kTopLeft_Origin == origin); +}
diff --git a/src/third_party/skia/tests/F16StagesTest.cpp b/src/third_party/skia/tests/F16StagesTest.cpp new file mode 100644 index 0000000..51cb861 --- /dev/null +++ b/src/third_party/skia/tests/F16StagesTest.cpp
@@ -0,0 +1,53 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkRasterPipeline.h" +#include "Test.h" + +DEF_TEST(F16Stages, r) { + // Make sure SkRasterPipeline::load_f16 and store_f16 can handle a range of + // ordinary (0<=x<=1) and interesting (x<0, x>1) values. + float floats[16] = { + 0.0f, 0.25f, 0.5f, 1.0f, + -1.25f, -0.5f, 1.25f, 2.0f, + 0,0,0,0, 0,0,0,0, // pad a bit to make sure we qualify for platform-specific code + }; + uint16_t halfs[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; + + float* f32 = floats; + uint16_t* f16 = halfs; + + { + SkRasterPipeline_<256> p; + p.append(SkRasterPipeline:: load_f32, &f32); + p.append(SkRasterPipeline::store_f16, &f16); + p.run(0,0,16/4); + } + REPORTER_ASSERT(r, f16[0] == 0x0000); + REPORTER_ASSERT(r, f16[1] == 0x3400); + REPORTER_ASSERT(r, f16[2] == 0x3800); + REPORTER_ASSERT(r, f16[3] == 0x3c00); + REPORTER_ASSERT(r, f16[4] == 0xbd00); + REPORTER_ASSERT(r, f16[5] == 0xb800); + REPORTER_ASSERT(r, f16[6] == 0x3d00); + REPORTER_ASSERT(r, f16[7] == 0x4000); + + { + SkRasterPipeline_<256> p; + p.append(SkRasterPipeline:: load_f16, &f16); + p.append(SkRasterPipeline::store_f32, &f32); + p.run(0,0,16/4); + } + REPORTER_ASSERT(r, f32[0] == 0.00f); + REPORTER_ASSERT(r, f32[1] == 0.25f); + REPORTER_ASSERT(r, f32[2] == 0.50f); + REPORTER_ASSERT(r, f32[3] == 1.00f); + REPORTER_ASSERT(r, f32[4] == -1.25f); + REPORTER_ASSERT(r, f32[5] == -0.50f); + REPORTER_ASSERT(r, f32[6] == 1.25f); + REPORTER_ASSERT(r, f32[7] == 2.00f); +}
diff --git a/src/third_party/skia/tests/FakeStreams.h b/src/third_party/skia/tests/FakeStreams.h new file mode 100644 index 0000000..6b510b8 --- /dev/null +++ b/src/third_party/skia/tests/FakeStreams.h
@@ -0,0 +1,86 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkData.h" +#include "SkStream.h" + +#ifndef FakeStreams_DEFINED +#define FakeStreams_DEFINED + +// Stream that is not an asset stream (!hasPosition() or !hasLength()) +class NotAssetMemStream : public SkStream { +public: + NotAssetMemStream(sk_sp<SkData> data) : fStream(std::move(data)) {} + + bool hasPosition() const override { + return false; + } + + bool hasLength() const override { + return false; + } + + size_t peek(void* buf, size_t bytes) const override { + return fStream.peek(buf, bytes); + } + size_t read(void* buf, size_t bytes) override { + return fStream.read(buf, bytes); + } + bool rewind() override { + return fStream.rewind(); + } + bool isAtEnd() const override { + return fStream.isAtEnd(); + } +private: + SkMemoryStream fStream; +}; + +/* + * Represents a stream without all of its data. + */ +class HaltingStream : public SkStream { +public: + HaltingStream(sk_sp<SkData> data, size_t initialLimit) + : fTotalSize(data->size()) + , fLimit(initialLimit) + , fStream(std::move(data)) + {} + + void addNewData(size_t extra) { + fLimit = SkTMin(fTotalSize, fLimit + extra); + } + + size_t read(void* buffer, size_t size) override { + if (fStream.getPosition() + size > fLimit) { + size = fLimit - fStream.getPosition(); + } + + return fStream.read(buffer, size); + } + + bool isAtEnd() const override { + return fStream.isAtEnd(); + } + + bool hasLength() const override { return true; } + size_t getLength() const override { return fLimit; } + + bool hasPosition() const override { return true; } + size_t getPosition() const override { return fStream.getPosition(); } + bool rewind() override { return fStream.rewind(); } + bool move(long offset) override { return fStream.move(offset); } + bool seek(size_t position) override { return fStream.seek(position); } + + bool isAllDataReceived() const { return fLimit == fTotalSize; } + +private: + const size_t fTotalSize; + size_t fLimit; + SkMemoryStream fStream; +}; +#endif // FakeStreams_DEFINED
diff --git a/src/third_party/skia/tests/FillPathTest.cpp b/src/third_party/skia/tests/FillPathTest.cpp index cc8d329..1f14d4f 100644 --- a/src/third_party/skia/tests/FillPathTest.cpp +++ b/src/third_party/skia/tests/FillPathTest.cpp
@@ -12,34 +12,37 @@ #include "Test.h" struct FakeBlitter : public SkBlitter { - FakeBlitter() - : m_blitCount(0) - {} + FakeBlitter() + : m_blitCount(0) { } - virtual void blitH(int x, int y, int width) { - m_blitCount++; - } + void blitH(int x, int y, int width) override { + m_blitCount++; + } - int m_blitCount; + void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) override { + SkDEBUGFAIL("blitAntiH not implemented"); + } + + int m_blitCount; }; // http://code.google.com/p/skia/issues/detail?id=87 // Lines which is not clipped by boundary based clipping, // but skipped after tessellation, should be cleared by the blitter. DEF_TEST(FillPathInverse, reporter) { - FakeBlitter blitter; - SkIRect clip; - SkPath path; - int height = 100; - int width = 200; - int expected_lines = 5; - clip.set(0, height - expected_lines, width, height); - path.moveTo(0.0f, 0.0f); - path.quadTo(SkIntToScalar(width/2), SkIntToScalar(height), + FakeBlitter blitter; + SkIRect clip; + SkPath path; + int height = 100; + int width = 200; + int expected_lines = 5; + clip.set(0, height - expected_lines, width, height); + path.moveTo(0.0f, 0.0f); + path.quadTo(SkIntToScalar(width/2), SkIntToScalar(height), SkIntToScalar(width), 0.0f); - path.close(); - path.setFillType(SkPath::kInverseWinding_FillType); - SkScan::FillPath(path, clip, &blitter); + path.close(); + path.setFillType(SkPath::kInverseWinding_FillType); + SkScan::FillPath(path, clip, &blitter); - REPORTER_ASSERT(reporter, blitter.m_blitCount == expected_lines); + REPORTER_ASSERT(reporter, blitter.m_blitCount == expected_lines); }
diff --git a/src/third_party/skia/tests/FlatDataTest.cpp b/src/third_party/skia/tests/FlatDataTest.cpp deleted file mode 100644 index 2be92b6..0000000 --- a/src/third_party/skia/tests/FlatDataTest.cpp +++ /dev/null
@@ -1,70 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkBitmap.h" -#include "SkCanvas.h" -#include "SkColor.h" -#include "SkColorFilter.h" -#include "SkGradientShader.h" -#include "SkPaint.h" -#include "SkPictureFlat.h" -#include "SkShader.h" -#include "SkXfermode.h" -#include "Test.h" - -struct SkFlattenableTraits { - static void Flatten(SkWriteBuffer& buffer, const SkFlattenable& flattenable) { - buffer.writeFlattenable(&flattenable); - } -}; - -class Controller : public SkChunkFlatController { -public: - Controller() : INHERITED(1024) { - this->INHERITED::setNamedFactorySet(SkNEW(SkNamedFactorySet))->unref(); - } -private: - typedef SkChunkFlatController INHERITED; -}; - -/** - * Verify that two SkFlatData objects that created from the same object are - * identical when using an SkNamedFactorySet. - * @param reporter Object to report failures. - * @param obj Flattenable object to be flattened. - * @param flattenProc Function that flattens objects with the same type as obj. - */ -template <typename Traits, typename T> -static void testCreate(skiatest::Reporter* reporter, const T& obj) { - Controller controller; - // No need to delete data because that will be taken care of by the controller. - SkFlatData* data1 = SkFlatData::Create<Traits>(&controller, obj, 0); - SkFlatData* data2 = SkFlatData::Create<Traits>(&controller, obj, 1); - REPORTER_ASSERT(reporter, *data1 == *data2); -} - -DEF_TEST(FlatData, reporter) { - // Test flattening SkShader - SkPoint points[2]; - points[0].set(0, 0); - points[1].set(SkIntToScalar(20), SkIntToScalar(20)); - SkColor colors[2]; - colors[0] = SK_ColorRED; - colors[1] = SK_ColorBLUE; - - SkAutoTUnref<SkShader> shader(SkGradientShader::CreateLinear(points, colors, NULL, 2, - SkShader::kRepeat_TileMode)); - testCreate<SkFlattenableTraits>(reporter, *shader); - - // Test SkColorFilter - SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateLightingFilter(SK_ColorBLUE, SK_ColorRED)); - testCreate<SkFlattenableTraits>(reporter, *cf); - - // Test SkXfermode - SkAutoTUnref<SkXfermode> xfer(SkXfermode::Create(SkXfermode::kDstOver_Mode)); - testCreate<SkFlattenableTraits>(reporter, *xfer); -}
diff --git a/src/third_party/skia/tests/FlateTest.cpp b/src/third_party/skia/tests/FlateTest.cpp deleted file mode 100644 index 75c6f3f..0000000 --- a/src/third_party/skia/tests/FlateTest.cpp +++ /dev/null
@@ -1,119 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkData.h" -#include "SkFlate.h" -#include "SkStream.h" -#include "Test.h" - -// A memory stream that reports zero size with the standard call, like -// an unseekable file stream would. -class SkZeroSizeMemStream : public SkMemoryStream { -public: - virtual size_t read(void* buffer, size_t size) { - if (buffer == NULL && size == 0) - return 0; - if (buffer == NULL && size == kGetSizeKey) - size = 0; - return SkMemoryStream::read(buffer, size); - } - - static const size_t kGetSizeKey = 0xDEADBEEF; -}; - -// Returns a deterministic data of the given size that should be -// very compressible. -static SkData* new_test_data(size_t dataSize) { - SkAutoTMalloc<uint8_t> testBuffer(dataSize); - for (size_t i = 0; i < dataSize; ++i) { - testBuffer[i] = i % 64; - } - return SkData::NewFromMalloc(testBuffer.detach(), dataSize); -} - -static void TestFlate(skiatest::Reporter* reporter, SkMemoryStream* testStream, - size_t dataSize) { - SkASSERT(testStream != NULL); - - SkAutoDataUnref testData(new_test_data(dataSize)); - SkASSERT(testData->size() == dataSize); - - testStream->setMemory(testData->data(), dataSize, /*copyData=*/ true); - SkDynamicMemoryWStream compressed; - bool deflateSuccess = SkFlate::Deflate(testStream, &compressed); - REPORTER_ASSERT(reporter, deflateSuccess); - - // Check that the input data wasn't changed. - size_t inputSize = testStream->getLength(); - if (inputSize == 0) { - inputSize = testStream->read(NULL, SkZeroSizeMemStream::kGetSizeKey); - } - REPORTER_ASSERT(reporter, dataSize == inputSize); - if (dataSize == inputSize) { - REPORTER_ASSERT(reporter, memcmp(testData->data(), - testStream->getMemoryBase(), - dataSize) == 0); - } - - size_t compressedSize = compressed.getOffset(); - - SkAutoDataUnref compressedData(compressed.copyToData()); - testStream->setData(compressedData.get()); - - SkDynamicMemoryWStream uncompressed; - bool inflateSuccess = SkFlate::Inflate(testStream, &uncompressed); - REPORTER_ASSERT(reporter, inflateSuccess); - - // Check that the input data wasn't changed. - inputSize = testStream->getLength(); - if (inputSize == 0) { - inputSize = testStream->read(NULL, SkZeroSizeMemStream::kGetSizeKey); - } - REPORTER_ASSERT(reporter, compressedSize == inputSize); - if (compressedData->size() == inputSize) { - REPORTER_ASSERT(reporter, memcmp(testStream->getMemoryBase(), - compressedData->data(), - compressedData->size()) == 0); - } - - // Check that the uncompressed data matches the source data. - SkAutoDataUnref uncompressedData(uncompressed.copyToData()); - REPORTER_ASSERT(reporter, dataSize == uncompressedData->size()); - if (dataSize == uncompressedData->size()) { - REPORTER_ASSERT(reporter, memcmp(testData->data(), - uncompressedData->data(), - dataSize) == 0); - } - - if (compressedSize < 1) { return; } - - double compressionRatio = static_cast<double>(dataSize) / compressedSize; - // Assert that some compression took place. - REPORTER_ASSERT(reporter, compressionRatio > 1.2); - - if (reporter->verbose()) { - SkDebugf("Flate Test: \t input size: " SK_SIZE_T_SPECIFIER - "\tcompressed size: " SK_SIZE_T_SPECIFIER - "\tratio: %.4g\n", - dataSize, compressedSize, compressionRatio); - } -} - -DEF_TEST(Flate, reporter) { -#ifdef SK_HAS_ZLIB - REPORTER_ASSERT(reporter, SkFlate::HaveFlate()); -#endif - if (SkFlate::HaveFlate()) { - SkMemoryStream memStream; - TestFlate(reporter, &memStream, 512); - TestFlate(reporter, &memStream, 10240); - - SkZeroSizeMemStream fileStream; - TestFlate(reporter, &fileStream, 512); - TestFlate(reporter, &fileStream, 10240); - } -}
diff --git a/src/third_party/skia/tests/FlattenDrawableTest.cpp b/src/third_party/skia/tests/FlattenDrawableTest.cpp new file mode 100644 index 0000000..9df8ba0 --- /dev/null +++ b/src/third_party/skia/tests/FlattenDrawableTest.cpp
@@ -0,0 +1,286 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCanvas.h" +#include "SkDrawable.h" +#include "SkOnce.h" +#include "SkPictureRecorder.h" +#include "SkReadBuffer.h" +#include "SkRect.h" +#include "SkStream.h" +#include "SkWriteBuffer.h" +#include "Test.h" + +class IntDrawable : public SkDrawable { +public: + IntDrawable(uint32_t a, uint32_t b, uint32_t c, uint32_t d) + : fA(a) + , fB(b) + , fC(c) + , fD(d) + {} + + void flatten(SkWriteBuffer& buffer) const override { + buffer.writeUInt(fA); + buffer.writeUInt(fB); + buffer.writeUInt(fC); + buffer.writeUInt(fD); + } + + static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) { + uint32_t a = buffer.readUInt(); + uint32_t b = buffer.readUInt(); + uint32_t c = buffer.readUInt(); + uint32_t d = buffer.readUInt(); + return sk_sp<IntDrawable>(new IntDrawable(a, b, c, d)); + } + + Factory getFactory() const override { return CreateProc; } + + uint32_t a() const { return fA; } + uint32_t b() const { return fB; } + uint32_t c() const { return fC; } + uint32_t d() const { return fD; } + + const char* getTypeName() const override { return "IntDrawable"; } + +protected: + SkRect onGetBounds() override { return SkRect::MakeEmpty(); } + void onDraw(SkCanvas*) override {} + +private: + uint32_t fA; + uint32_t fB; + uint32_t fC; + uint32_t fD; +}; + +class PaintDrawable : public SkDrawable { +public: + PaintDrawable(const SkPaint& paint) + : fPaint(paint) + {} + + void flatten(SkWriteBuffer& buffer) const override { + buffer.writePaint(fPaint); + } + + static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) { + SkPaint paint; + buffer.readPaint(&paint); + return sk_sp<PaintDrawable>(new PaintDrawable(paint)); + } + + Factory getFactory() const override { return CreateProc; } + + const SkPaint& paint() const { return fPaint; } + + const char* getTypeName() const override { return "PaintDrawable"; } + +protected: + SkRect onGetBounds() override { return SkRect::MakeEmpty(); } + void onDraw(SkCanvas*) override {} + +private: + SkPaint fPaint; +}; + +class CompoundDrawable : public SkDrawable { +public: + CompoundDrawable(uint32_t a, uint32_t b, uint32_t c, uint32_t d, const SkPaint& paint) + : fIntDrawable(new IntDrawable(a, b, c, d)) + , fPaintDrawable(new PaintDrawable(paint)) + {} + + CompoundDrawable(IntDrawable* intDrawable, PaintDrawable* paintDrawable) + : fIntDrawable(SkRef(intDrawable)) + , fPaintDrawable(SkRef(paintDrawable)) + {} + + void flatten(SkWriteBuffer& buffer) const override { + buffer.writeFlattenable(fIntDrawable.get()); + buffer.writeFlattenable(fPaintDrawable.get()); + } + + static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) { + sk_sp<SkFlattenable> intDrawable( + buffer.readFlattenable(SkFlattenable::kSkDrawable_Type)); + SkASSERT(intDrawable); + SkASSERT(!strcmp("IntDrawable", intDrawable->getTypeName())); + + sk_sp<SkFlattenable> paintDrawable( + buffer.readFlattenable(SkFlattenable::kSkDrawable_Type)); + SkASSERT(paintDrawable); + SkASSERT(!strcmp("PaintDrawable", paintDrawable->getTypeName())); + + return sk_sp<CompoundDrawable>(new CompoundDrawable((IntDrawable*) intDrawable.get(), + (PaintDrawable*) paintDrawable.get())); + } + + Factory getFactory() const override { return CreateProc; } + + IntDrawable* intDrawable() const { return fIntDrawable.get(); } + PaintDrawable* paintDrawable() const { return fPaintDrawable.get(); } + + const char* getTypeName() const override { return "CompoundDrawable"; } + +protected: + SkRect onGetBounds() override { return SkRect::MakeEmpty(); } + void onDraw(SkCanvas*) override {} + +private: + sk_sp<IntDrawable> fIntDrawable; + sk_sp<PaintDrawable> fPaintDrawable; +}; + +class RootDrawable : public SkDrawable { +public: + RootDrawable(uint32_t a, uint32_t b, uint32_t c, uint32_t d, const SkPaint& paint, + uint32_t e, uint32_t f, uint32_t g, uint32_t h, SkDrawable* drawable) + : fCompoundDrawable(new CompoundDrawable(a, b, c, d, paint)) + , fIntDrawable(new IntDrawable(e, f, g, h)) + , fDrawable(SkRef(drawable)) + {} + + RootDrawable(CompoundDrawable* compoundDrawable, IntDrawable* intDrawable, + SkDrawable* drawable) + : fCompoundDrawable(SkRef(compoundDrawable)) + , fIntDrawable(SkRef(intDrawable)) + , fDrawable(SkRef(drawable)) + {} + + void flatten(SkWriteBuffer& buffer) const override { + buffer.writeFlattenable(fCompoundDrawable.get()); + buffer.writeFlattenable(fIntDrawable.get()); + buffer.writeFlattenable(fDrawable.get()); + } + + static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) { + sk_sp<SkFlattenable> compoundDrawable( + buffer.readFlattenable(SkFlattenable::kSkDrawable_Type)); + SkASSERT(compoundDrawable); + SkASSERT(!strcmp("CompoundDrawable", compoundDrawable->getTypeName())); + + sk_sp<SkFlattenable> intDrawable( + buffer.readFlattenable(SkFlattenable::kSkDrawable_Type)); + SkASSERT(intDrawable); + SkASSERT(!strcmp("IntDrawable", intDrawable->getTypeName())); + + sk_sp<SkFlattenable> drawable( + buffer.readFlattenable(SkFlattenable::kSkDrawable_Type)); + SkASSERT(drawable); + + return sk_sp<RootDrawable>(new RootDrawable((CompoundDrawable*) compoundDrawable.get(), + (IntDrawable*) intDrawable.get(), + (SkDrawable*) drawable.get())); + } + + Factory getFactory() const override { return CreateProc; } + + CompoundDrawable* compoundDrawable() const { return fCompoundDrawable.get(); } + IntDrawable* intDrawable() const { return fIntDrawable.get(); } + SkDrawable* drawable() const { return fDrawable.get(); } + + const char* getTypeName() const override { return "RootDrawable"; } + +protected: + SkRect onGetBounds() override { return SkRect::MakeEmpty(); } + void onDraw(SkCanvas*) override {} + +private: + sk_sp<CompoundDrawable> fCompoundDrawable; + sk_sp<IntDrawable> fIntDrawable; + sk_sp<SkDrawable> fDrawable; +}; + +static void register_test_drawables(SkReadBuffer& buffer) { + buffer.setCustomFactory(SkString("IntDrawable"), IntDrawable::CreateProc); + buffer.setCustomFactory(SkString("PaintDrawable"), PaintDrawable::CreateProc); + buffer.setCustomFactory(SkString("CompoundDrawable"), CompoundDrawable::CreateProc); + buffer.setCustomFactory(SkString("RootDrawable"), RootDrawable::CreateProc); +} + +DEF_TEST(FlattenDrawable, r) { + // Create and serialize the test drawable + sk_sp<SkDrawable> drawable(new IntDrawable(1, 2, 3, 4)); + SkPaint paint; + paint.setColor(SK_ColorBLUE); + sk_sp<RootDrawable> root(new RootDrawable(5, 6, 7, 8, paint, 9, 10, 11, 12, drawable.get())); + SkBinaryWriteBuffer writeBuffer; + writeBuffer.writeFlattenable(root.get()); + + // Copy the contents of the write buffer into a read buffer + sk_sp<SkData> data = SkData::MakeUninitialized(writeBuffer.bytesWritten()); + writeBuffer.writeToMemory(data->writable_data()); + SkReadBuffer readBuffer(data->data(), data->size()); + register_test_drawables(readBuffer); + + // Deserialize and verify the drawable + sk_sp<SkDrawable> out((SkDrawable*)readBuffer.readFlattenable(SkFlattenable::kSkDrawable_Type)); + REPORTER_ASSERT(r, out); + REPORTER_ASSERT(r, !strcmp("RootDrawable", out->getTypeName())); + + RootDrawable* rootOut = (RootDrawable*) out.get(); + REPORTER_ASSERT(r, 5 == rootOut->compoundDrawable()->intDrawable()->a()); + REPORTER_ASSERT(r, 6 == rootOut->compoundDrawable()->intDrawable()->b()); + REPORTER_ASSERT(r, 7 == rootOut->compoundDrawable()->intDrawable()->c()); + REPORTER_ASSERT(r, 8 == rootOut->compoundDrawable()->intDrawable()->d()); + REPORTER_ASSERT(r, SK_ColorBLUE == + rootOut->compoundDrawable()->paintDrawable()->paint().getColor()); + REPORTER_ASSERT(r, 9 == rootOut->intDrawable()->a()); + REPORTER_ASSERT(r, 10 == rootOut->intDrawable()->b()); + REPORTER_ASSERT(r, 11 == rootOut->intDrawable()->c()); + REPORTER_ASSERT(r, 12 == rootOut->intDrawable()->d()); + + // Note that we can still recognize the generic drawable as an IntDrawable + SkDrawable* generic = rootOut->drawable(); + REPORTER_ASSERT(r, !strcmp("IntDrawable", generic->getTypeName())); + IntDrawable* integer = (IntDrawable*) generic; + REPORTER_ASSERT(r, 1 == integer->a()); + REPORTER_ASSERT(r, 2 == integer->b()); + REPORTER_ASSERT(r, 3 == integer->c()); + REPORTER_ASSERT(r, 4 == integer->d()); +} + +DEF_TEST(FlattenRecordedDrawable, r) { + // Record a set of canvas draw commands + SkPictureRecorder recorder; + SkCanvas* canvas = recorder.beginRecording(1000.0f, 1000.0f); + SkPaint paint; + paint.setColor(SK_ColorGREEN); + canvas->drawPoint(42.0f, 17.0f, paint); + paint.setColor(SK_ColorRED); + canvas->drawPaint(paint); + SkPaint textPaint; + textPaint.setColor(SK_ColorBLUE); + canvas->drawString("TEXT", 467.0f, 100.0f, textPaint); + + // Draw some drawables as well + sk_sp<SkDrawable> drawable(new IntDrawable(1, 2, 3, 4)); + sk_sp<RootDrawable> root(new RootDrawable(5, 6, 7, 8, paint, 9, 10, 11, 12, drawable.get())); + canvas->drawDrawable(root.get(), 747.0f, 242.0f); + sk_sp<PaintDrawable> paintDrawable(new PaintDrawable(paint)); + canvas->drawDrawable(paintDrawable.get(), 500.0, 500.0f); + sk_sp<CompoundDrawable> comDrawable(new CompoundDrawable(13, 14, 15, 16, textPaint)); + canvas->drawDrawable(comDrawable.get(), 10.0f, 10.0f); + + // Serialize the recorded drawable + sk_sp<SkDrawable> recordedDrawable = recorder.finishRecordingAsDrawable(); + SkBinaryWriteBuffer writeBuffer; + writeBuffer.writeFlattenable(recordedDrawable.get()); + + // Copy the contents of the write buffer into a read buffer + sk_sp<SkData> data = SkData::MakeUninitialized(writeBuffer.bytesWritten()); + writeBuffer.writeToMemory(data->writable_data()); + SkReadBuffer readBuffer(data->data(), data->size()); + register_test_drawables(readBuffer); + + // Deserialize and verify the drawable + sk_sp<SkDrawable> out((SkDrawable*)readBuffer.readFlattenable(SkFlattenable::kSkDrawable_Type)); + REPORTER_ASSERT(r, out); + REPORTER_ASSERT(r, !strcmp("SkRecordedDrawable", out->getTypeName())); +}
diff --git a/src/third_party/skia/tests/FlattenableCustomFactory.cpp b/src/third_party/skia/tests/FlattenableCustomFactory.cpp new file mode 100644 index 0000000..e83311b --- /dev/null +++ b/src/third_party/skia/tests/FlattenableCustomFactory.cpp
@@ -0,0 +1,95 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkFlattenable.h" +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" +#include "Test.h" + +class IntFlattenable : public SkFlattenable { +public: + IntFlattenable(uint32_t a, uint32_t b, uint32_t c, uint32_t d) + : fA(a) + , fB(b) + , fC(c) + , fD(d) + {} + + void flatten(SkWriteBuffer& buffer) const override { + buffer.writeUInt(fA); + buffer.writeUInt(fB); + buffer.writeUInt(fC); + buffer.writeUInt(fD); + } + + Factory getFactory() const override { return nullptr; } + + uint32_t a() const { return fA; } + uint32_t b() const { return fB; } + uint32_t c() const { return fC; } + uint32_t d() const { return fD; } + + const char* getTypeName() const override { return "IntFlattenable"; } + +private: + uint32_t fA; + uint32_t fB; + uint32_t fC; + uint32_t fD; +}; + +static sk_sp<SkFlattenable> custom_create_proc(SkReadBuffer& buffer) { + uint32_t a = buffer.readUInt(); + uint32_t b = buffer.readUInt(); + uint32_t c = buffer.readUInt(); + uint32_t d = buffer.readUInt(); + return sk_sp<SkFlattenable>(new IntFlattenable(a + 1, b + 1, c + 1, d + 1)); +} + +DEF_TEST(UnflattenWithCustomFactory, r) { + // Create and flatten the test flattenable + SkBinaryWriteBuffer writeBuffer; + sk_sp<SkFlattenable> flattenable1(new IntFlattenable(1, 2, 3, 4)); + writeBuffer.writeFlattenable(flattenable1.get()); + sk_sp<SkFlattenable> flattenable2(new IntFlattenable(2, 3, 4, 5)); + writeBuffer.writeFlattenable(flattenable2.get()); + sk_sp<SkFlattenable> flattenable3(new IntFlattenable(3, 4, 5, 6)); + writeBuffer.writeFlattenable(flattenable3.get()); + + // Copy the contents of the write buffer into a read buffer + sk_sp<SkData> data = SkData::MakeUninitialized(writeBuffer.bytesWritten()); + writeBuffer.writeToMemory(data->writable_data()); + SkReadBuffer readBuffer(data->data(), data->size()); + + // Register a custom factory with the read buffer + readBuffer.setCustomFactory(SkString("IntFlattenable"), &custom_create_proc); + + // Unflatten and verify the flattenables + sk_sp<IntFlattenable> out1((IntFlattenable*) readBuffer.readFlattenable( + SkFlattenable::kSkUnused_Type)); + REPORTER_ASSERT(r, out1); + REPORTER_ASSERT(r, 2 == out1->a()); + REPORTER_ASSERT(r, 3 == out1->b()); + REPORTER_ASSERT(r, 4 == out1->c()); + REPORTER_ASSERT(r, 5 == out1->d()); + + sk_sp<IntFlattenable> out2((IntFlattenable*) readBuffer.readFlattenable( + SkFlattenable::kSkUnused_Type)); + REPORTER_ASSERT(r, out2); + REPORTER_ASSERT(r, 3 == out2->a()); + REPORTER_ASSERT(r, 4 == out2->b()); + REPORTER_ASSERT(r, 5 == out2->c()); + REPORTER_ASSERT(r, 6 == out2->d()); + + sk_sp<IntFlattenable> out3((IntFlattenable*) readBuffer.readFlattenable( + SkFlattenable::kSkUnused_Type)); + REPORTER_ASSERT(r, out3); + REPORTER_ASSERT(r, 4 == out3->a()); + REPORTER_ASSERT(r, 5 == out3->b()); + REPORTER_ASSERT(r, 6 == out3->c()); + REPORTER_ASSERT(r, 7 == out3->d()); +}
diff --git a/src/third_party/skia/tests/FlattenableFactoryToName.cpp b/src/third_party/skia/tests/FlattenableFactoryToName.cpp new file mode 100644 index 0000000..fc47dfc --- /dev/null +++ b/src/third_party/skia/tests/FlattenableFactoryToName.cpp
@@ -0,0 +1,41 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkAlphaThresholdFilter.h" +#include "SkImage.h" +#include "SkRegion.h" +#include "Test.h" + +static void test_flattenable(skiatest::Reporter* r, + const SkFlattenable* f, + const char* desc) { + if (f) { + SkFlattenable::Factory factory = f->getFactory(); + REPORTER_ASSERT(r, factory); + if (factory) { + if (!SkFlattenable::FactoryToName(factory)) { + ERRORF(r, "SkFlattenable::FactoryToName() fails with %s.", desc); + } + } + } +} + +DEF_TEST(FlattenableFactoryToName, r) { + SkIRect rects[2]; + rects[0] = SkIRect::MakeXYWH(0, 150, 500, 200); + rects[1] = SkIRect::MakeXYWH(150, 0, 200, 500); + SkRegion region; + region.setRects(rects, 2); + sk_sp<SkImageFilter> filter(SkAlphaThresholdFilter::Make(region, 0.2f, 0.7f, nullptr)); + test_flattenable(r, filter.get(), "SkAlphaThresholdFilter()"); + + SkBitmap bm; + bm.allocN32Pixels(8, 8); + bm.eraseColor(SK_ColorCYAN); + sk_sp<SkImage> image(SkImage::MakeFromBitmap(bm)); + test_flattenable(r, image->makeShader().get(), "SkImage::newShader()"); +}
diff --git a/src/third_party/skia/tests/Float16Test.cpp b/src/third_party/skia/tests/Float16Test.cpp new file mode 100644 index 0000000..64873c3 --- /dev/null +++ b/src/third_party/skia/tests/Float16Test.cpp
@@ -0,0 +1,113 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" +#include "SkAutoPixmapStorage.h" +#include "SkColor.h" +#include "SkHalf.h" +#include "SkOpts.h" +#include "SkPixmap.h" +#include "SkPM4f.h" +#include "SkRandom.h" + +#include <cmath> + +static bool eq_within_half_float(float a, float b) { + const float kTolerance = 1.0f / (1 << (8 + 10)); + + SkHalf ha = SkFloatToHalf(a); + SkHalf hb = SkFloatToHalf(b); + float a2 = SkHalfToFloat(ha); + float b2 = SkHalfToFloat(hb); + return fabsf(a2 - b2) <= kTolerance; +} + +static bool eq_within_half_float(const SkPM4f& a, const SkPM4f& b) { + for (int i = 0; i < 4; ++i) { + if (!eq_within_half_float(a.fVec[i], b.fVec[i])) { + return false; + } + } + return true; +} + +DEF_TEST(color_half_float, reporter) { + const int w = 100; + const int h = 100; + + SkImageInfo info = SkImageInfo::Make(w, h, kRGBA_F16_SkColorType, kPremul_SkAlphaType); + + SkAutoPixmapStorage pm; + pm.alloc(info); + REPORTER_ASSERT(reporter, pm.getSafeSize() == SkToSizeT(w * h * sizeof(uint64_t))); + + SkColor4f c4 { 1, 0.5f, 0.25f, 0.5f }; + pm.erase(c4); + + SkPM4f origpm4 = c4.premul(); + for (int y = 0; y < pm.height(); ++y) { + for (int x = 0; x < pm.width(); ++x) { + SkPM4f pm4 = SkPM4f::FromF16(pm.addrF16(x, y)); + REPORTER_ASSERT(reporter, eq_within_half_float(origpm4, pm4)); + } + } +} + +static bool is_denorm(uint16_t h) { + return (h & 0x7fff) < 0x0400; +} + +static bool is_finite(uint16_t h) { + return (h & 0x7c00) != 0x7c00; +} + +DEF_TEST(SkHalfToFloat_finite_ftz, r) { + for (uint32_t h = 0; h <= 0xffff; h++) { + if (!is_finite(h)) { + // _finite_ftz() only works for values that can be represented as a finite half float. + continue; + } + + // _finite_ftz() may flush denorms to zero. 0.0f will compare == with both +0.0f and -0.0f. + float expected = SkHalfToFloat(h), + alternate = is_denorm(h) ? 0.0f : expected; + + float actual = SkHalfToFloat_finite_ftz(h)[0]; + + REPORTER_ASSERT(r, actual == expected || actual == alternate); + } +} + +DEF_TEST(SkFloatToHalf_finite_ftz, r) { +#if 0 + for (uint64_t bits = 0; bits <= 0xffffffff; bits++) { +#else + SkRandom rand; + for (int i = 0; i < 1000000; i++) { + uint32_t bits = rand.nextU(); +#endif + float f; + memcpy(&f, &bits, 4); + + uint16_t expected = SkFloatToHalf(f); + if (!is_finite(expected)) { + // _finite_ftz() only works for values that can be represented as a finite half float. + continue; + } + + uint16_t alternate = expected; + if (is_denorm(expected)) { + // _finite_ftz() may flush denorms to zero, and happens to keep the sign bit. + alternate = std::signbit(f) ? 0x8000 : 0x0000; + } + + uint16_t actual = SkFloatToHalf_finite_ftz(Sk4f{f})[0]; + // _finite_ftz() may truncate instead of rounding, so it may be one too small. + REPORTER_ASSERT(r, actual == expected || actual == expected - 1 || + actual == alternate || actual == alternate - 1); + } +}
diff --git a/src/third_party/skia/tests/FloatingPointTextureTest.cpp b/src/third_party/skia/tests/FloatingPointTextureTest.cpp index d367659..06a99a5 100644 --- a/src/third_party/skia/tests/FloatingPointTextureTest.cpp +++ b/src/third_party/skia/tests/FloatingPointTextureTest.cpp
@@ -11,71 +11,101 @@ * 32 bit floating point textures, and indeed floating point test values * have been selected to require 32 bits of precision and full IEEE conformance */ -#if SK_SUPPORT_GPU + #include <float.h> #include "Test.h" + +#if SK_SUPPORT_GPU #include "GrContext.h" -#include "GrTexture.h" -#include "GrContextFactory.h" -#include "SkGpuDevice.h" +#include "GrContextPriv.h" +#include "GrResourceProvider.h" +#include "GrTextureProxy.h" +#include "SkHalf.h" static const int DEV_W = 100, DEV_H = 100; -static const int FP_CONTROL_ARRAY_SIZE = DEV_W * DEV_H * sizeof(float); -static const float kMaxIntegerRepresentableInSPFloatingPoint = 16777216; // 2 ^ 24 - static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H); -DEF_GPUTEST(FloatingPointTextureTest, reporter, factory) { - float controlPixelData[FP_CONTROL_ARRAY_SIZE]; - float readBuffer[FP_CONTROL_ARRAY_SIZE]; - for (int i = 0; i < FP_CONTROL_ARRAY_SIZE; i += 4) { - controlPixelData[i] = FLT_MIN; - controlPixelData[i + 1] = FLT_MAX; - controlPixelData[i + 2] = FLT_EPSILON; - controlPixelData[i + 3] = kMaxIntegerRepresentableInSPFloatingPoint; +template <typename T> +void runFPTest(skiatest::Reporter* reporter, GrContext* context, + T min, T max, T epsilon, T maxInt, int arraySize, GrPixelConfig config) { + if (0 != arraySize % 4) { + REPORT_FAILURE(reporter, "(0 != arraySize % 4)", + SkString("arraySize must be divisible by 4.")); + return; + } + + SkTDArray<T> controlPixelData, readBuffer; + controlPixelData.setCount(arraySize); + readBuffer.setCount(arraySize); + + for (int i = 0; i < arraySize; i += 4) { + controlPixelData[i + 0] = min; + controlPixelData[i + 1] = max; + controlPixelData[i + 2] = epsilon; + controlPixelData[i + 3] = maxInt; } for (int origin = 0; origin < 2; ++origin) { - int glCtxTypeCnt = 1; - glCtxTypeCnt = GrContextFactory::kGLContextTypeCnt; - for (int glCtxType = 0; glCtxType < glCtxTypeCnt; ++glCtxType) { - GrTextureDesc desc; - desc.fFlags = kRenderTarget_GrTextureFlagBit; - desc.fWidth = DEV_W; - desc.fHeight = DEV_H; - desc.fConfig = kRGBA_float_GrPixelConfig; - desc.fOrigin = 0 == origin ? - kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin; - - GrContext* context = NULL; - GrContextFactory::GLContextType type = - static_cast<GrContextFactory::GLContextType>(glCtxType); - if (!GrContextFactory::IsRenderingGLContext(type)) { - continue; - } - context = factory->get(type); - if (NULL == context){ - continue; - } - - SkAutoTUnref<GrTexture> fpTexture(context->createUncachedTexture(desc, - NULL, - 0)); - - // Floating point textures are NOT supported everywhere - if (NULL == fpTexture) { - continue; - } - - // write square - context->writeTexturePixels(fpTexture, 0, 0, DEV_W, DEV_H, desc.fConfig, - controlPixelData, 0); - context->readTexturePixels(fpTexture, 0, 0, DEV_W, DEV_H, desc.fConfig, readBuffer, 0); - for (int j = 0; j < FP_CONTROL_ARRAY_SIZE; ++j) { - REPORTER_ASSERT(reporter, readBuffer[j] == controlPixelData[j]); - } + GrSurfaceDesc desc; + desc.fFlags = kRenderTarget_GrSurfaceFlag; + desc.fWidth = DEV_W; + desc.fHeight = DEV_H; + desc.fConfig = config; + desc.fOrigin = 0 == origin ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin; + sk_sp<GrTextureProxy> fpProxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), + desc, SkBudgeted::kNo, + controlPixelData.begin(), 0); + // Floating point textures are NOT supported everywhere + if (!fpProxy) { + continue; } + + sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext( + std::move(fpProxy), nullptr); + REPORTER_ASSERT(reporter, sContext); + + bool result = context->contextPriv().readSurfacePixels(sContext.get(), + 0, 0, DEV_W, DEV_H, + desc.fConfig, nullptr, + readBuffer.begin(), 0); + REPORTER_ASSERT(reporter, result); + REPORTER_ASSERT(reporter, + 0 == memcmp(readBuffer.begin(), controlPixelData.begin(), readBuffer.bytes())); } } +static const int RGBA32F_CONTROL_ARRAY_SIZE = DEV_W * DEV_H * 4; +static const float kMaxIntegerRepresentableInSPFloatingPoint = 16777216; // 2 ^ 24 + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(FloatingPointTextureTest, reporter, ctxInfo) { + runFPTest<float>(reporter, ctxInfo.grContext(), FLT_MIN, FLT_MAX, FLT_EPSILON, + kMaxIntegerRepresentableInSPFloatingPoint, + RGBA32F_CONTROL_ARRAY_SIZE, kRGBA_float_GrPixelConfig); +} + +static const int RG32F_CONTROL_ARRAY_SIZE = DEV_W * DEV_H * 2; + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(FloatingPointTextureTest_RG, reporter, ctxInfo) { + runFPTest<float>(reporter, ctxInfo.grContext(), FLT_MIN, FLT_MAX, FLT_EPSILON, + kMaxIntegerRepresentableInSPFloatingPoint, + RG32F_CONTROL_ARRAY_SIZE, kRG_float_GrPixelConfig); +} + +static const int HALF_ALPHA_CONTROL_ARRAY_SIZE = DEV_W * DEV_H * 1 /*alpha-only*/; +static const SkHalf kMaxIntegerRepresentableInHalfFloatingPoint = 0x6800; // 2 ^ 11 + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(HalfFloatAlphaTextureTest, reporter, ctxInfo) { + runFPTest<SkHalf>(reporter, ctxInfo.grContext(), SK_HalfMin, SK_HalfMax, SK_HalfEpsilon, + kMaxIntegerRepresentableInHalfFloatingPoint, + HALF_ALPHA_CONTROL_ARRAY_SIZE, kAlpha_half_GrPixelConfig); +} + +static const int HALF_RGBA_CONTROL_ARRAY_SIZE = DEV_W * DEV_H * 4 /*RGBA*/; + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(HalfFloatRGBATextureTest, reporter, ctxInfo) { + runFPTest<SkHalf>(reporter, ctxInfo.grContext(), SK_HalfMin, SK_HalfMax, SK_HalfEpsilon, + kMaxIntegerRepresentableInHalfFloatingPoint, + HALF_RGBA_CONTROL_ARRAY_SIZE, kRGBA_half_GrPixelConfig); +} + #endif
diff --git a/src/third_party/skia/tests/FontConfigParser.cpp b/src/third_party/skia/tests/FontConfigParser.cpp deleted file mode 100644 index 86b2b1d..0000000 --- a/src/third_party/skia/tests/FontConfigParser.cpp +++ /dev/null
@@ -1,115 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Resources.h" -#include "SkFontConfigParser_android.h" -#include "Test.h" - -int CountFallbacks(SkTDArray<FontFamily*> fontFamilies) { - int countOfFallbackFonts = 0; - for (int i = 0; i < fontFamilies.count(); i++) { - if (fontFamilies[i]->fIsFallbackFont) { - countOfFallbackFonts++; - } - } - return countOfFallbackFonts; -} - -void ValidateLoadedFonts(SkTDArray<FontFamily*> fontFamilies, - skiatest::Reporter* reporter) { - REPORTER_ASSERT(reporter, fontFamilies[0]->fNames.count() == 5); - REPORTER_ASSERT(reporter, !strcmp(fontFamilies[0]->fNames[0].c_str(), "sans-serif")); - REPORTER_ASSERT(reporter, - !strcmp(fontFamilies[0]->fFonts[0].fFileName.c_str(), - "Roboto-Regular.ttf")); - REPORTER_ASSERT(reporter, !fontFamilies[0]->fIsFallbackFont); -} - -void DumpLoadedFonts(SkTDArray<FontFamily*> fontFamilies) { -#if SK_DEBUG_FONTS - for (int i = 0; i < fontFamilies.count(); ++i) { - SkDebugf("Family %d:\n", i); - switch(fontFamilies[i]->fVariant) { - case SkPaintOptionsAndroid::kElegant_Variant: SkDebugf(" elegant"); break; - case SkPaintOptionsAndroid::kCompact_Variant: SkDebugf(" compact"); break; - default: break; - } - if (!fontFamilies[i]->fLanguage.getTag().isEmpty()) { - SkDebugf(" language: %s", fontFamilies[i]->fLanguage.getTag().c_str()); - } - for (int j = 0; j < fontFamilies[i]->fNames.count(); ++j) { - SkDebugf(" name %s\n", fontFamilies[i]->fNames[j].c_str()); - } - for (int j = 0; j < fontFamilies[i]->fFonts.count(); ++j) { - const FontFileInfo& ffi = fontFamilies[i]->fFonts[j]; - SkDebugf(" file (%d %s %d) %s\n", - ffi.fWeight, - ffi.fPaintOptions.getLanguage().getTag().isEmpty() ? "" : - ffi.fPaintOptions.getLanguage().getTag().c_str(), - ffi.fPaintOptions.getFontVariant(), - ffi.fFileName.c_str()); - } - } -#endif // SK_DEBUG_FONTS -} - -DEF_TEST(FontConfigParserAndroid, reporter) { - - bool resourcesMissing = false; - - SkTDArray<FontFamily*> preV17FontFamilies; - SkFontConfigParser::GetTestFontFamilies(preV17FontFamilies, - GetResourcePath("android_fonts/pre_v17/system_fonts.xml").c_str(), - GetResourcePath("android_fonts/pre_v17/fallback_fonts.xml").c_str()); - - if (preV17FontFamilies.count() > 0) { - REPORTER_ASSERT(reporter, preV17FontFamilies.count() == 14); - REPORTER_ASSERT(reporter, CountFallbacks(preV17FontFamilies) == 10); - - DumpLoadedFonts(preV17FontFamilies); - ValidateLoadedFonts(preV17FontFamilies, reporter); - } else { - resourcesMissing = true; - } - - - SkTDArray<FontFamily*> v17FontFamilies; - SkFontConfigParser::GetTestFontFamilies(v17FontFamilies, - GetResourcePath("android_fonts/v17/system_fonts.xml").c_str(), - GetResourcePath("android_fonts/v17/fallback_fonts.xml").c_str()); - - if (v17FontFamilies.count() > 0) { - REPORTER_ASSERT(reporter, v17FontFamilies.count() == 41); - REPORTER_ASSERT(reporter, CountFallbacks(v17FontFamilies) == 31); - - DumpLoadedFonts(v17FontFamilies); - ValidateLoadedFonts(v17FontFamilies, reporter); - } else { - resourcesMissing = true; - } - - - SkTDArray<FontFamily*> v22FontFamilies; - SkFontConfigParser::GetTestFontFamilies(v22FontFamilies, - GetResourcePath("android_fonts/v22/fonts.xml").c_str(), - NULL); - - if (v22FontFamilies.count() > 0) { - REPORTER_ASSERT(reporter, v22FontFamilies.count() == 53); - REPORTER_ASSERT(reporter, CountFallbacks(v22FontFamilies) == 42); - - DumpLoadedFonts(v22FontFamilies); - ValidateLoadedFonts(v22FontFamilies, reporter); - } else { - resourcesMissing = true; - } - - if (resourcesMissing) { - SkDebugf("---- Resource files missing for FontConfigParser test\n"); - } -} -
diff --git a/src/third_party/skia/tests/FontHostStreamTest.cpp b/src/third_party/skia/tests/FontHostStreamTest.cpp index d4a4924..c3bc878 100644 --- a/src/third_party/skia/tests/FontHostStreamTest.cpp +++ b/src/third_party/skia/tests/FontHostStreamTest.cpp
@@ -9,7 +9,6 @@ #include "SkCanvas.h" #include "SkColor.h" #include "SkFontDescriptor.h" -#include "SkFontHost.h" #include "SkGraphics.h" #include "SkPaint.h" #include "SkPoint.h" @@ -40,9 +39,6 @@ const int xOff = itest.fLeft - iref.fLeft; const int yOff = itest.fTop - iref.fTop; - SkAutoLockPixels alpRef(ref); - SkAutoLockPixels alpTest(test); - for (int y = 0; y < test.height(); ++y) { for (int x = 0; x < test.width(); ++x) { SkColor testColor = test.getColor(x, y); @@ -70,9 +66,7 @@ paint.setColor(SK_ColorGRAY); paint.setTextSize(SkIntToScalar(30)); - SkTypeface* fTypeface = SkTypeface::CreateFromName("Georgia", - SkTypeface::kNormal); - SkSafeUnref(paint.setTypeface(fTypeface)); + paint.setTypeface(SkTypeface::MakeFromName("Georgia", SkFontStyle())); SkIRect origRect = SkIRect::MakeWH(64, 64); SkBitmap origBitmap; @@ -88,24 +82,20 @@ // Test: origTypeface and streamTypeface from orig data draw the same drawBG(&origCanvas); - origCanvas.drawText("A", 1, point.fX, point.fY, paint); + origCanvas.drawString("A", point.fX, point.fY, paint); - SkTypeface* origTypeface = paint.getTypeface(); - SkAutoTUnref<SkTypeface> aur; - if (NULL == origTypeface) { - origTypeface = aur.reset(SkTypeface::RefDefault()); - } - + sk_sp<SkTypeface> typeface(paint.getTypeface() ? paint.refTypeface() + : SkTypeface::MakeDefault()); int ttcIndex; - SkAutoTUnref<SkStreamAsset> fontData(origTypeface->openStream(&ttcIndex)); - SkTypeface* streamTypeface = SkTypeface::CreateFromStream(fontData); + std::unique_ptr<SkStreamAsset> fontData(typeface->openStream(&ttcIndex)); + sk_sp<SkTypeface> streamTypeface(SkTypeface::MakeFromStream(fontData.release())); SkFontDescriptor desc; bool isLocalStream = false; streamTypeface->getFontDescriptor(&desc, &isLocalStream); REPORTER_ASSERT(reporter, isLocalStream); - SkSafeUnref(paint.setTypeface(streamTypeface)); + paint.setTypeface(streamTypeface); drawBG(&streamCanvas); streamCanvas.drawPosText("A", 1, &point, paint);
diff --git a/src/third_party/skia/tests/FontHostTest.cpp b/src/third_party/skia/tests/FontHostTest.cpp index 249fe7b..3d54c57 100644 --- a/src/third_party/skia/tests/FontHostTest.cpp +++ b/src/third_party/skia/tests/FontHostTest.cpp
@@ -6,6 +6,7 @@ */ #include "Resources.h" +#include "SkAutoMalloc.h" #include "SkEndian.h" #include "SkFontStream.h" #include "SkOSFile.h" @@ -31,7 +32,7 @@ // Test that getUnitsPerEm() agrees with a direct lookup in the 'head' table // (if that table is available). -static void test_unitsPerEm(skiatest::Reporter* reporter, SkTypeface* face) { +static void test_unitsPerEm(skiatest::Reporter* reporter, const sk_sp<SkTypeface>& face) { int nativeUPEM = face->getUnitsPerEm(); int tableUPEM = -1; @@ -50,7 +51,7 @@ // Test that countGlyphs() agrees with a direct lookup in the 'maxp' table // (if that table is available). -static void test_countGlyphs(skiatest::Reporter* reporter, SkTypeface* face) { +static void test_countGlyphs(skiatest::Reporter* reporter, const sk_sp<SkTypeface>& face) { int nativeGlyphs = face->countGlyphs(); int tableGlyphs = -1; @@ -86,7 +87,7 @@ }; // Test that SkPaint::textToGlyphs agrees with SkTypeface::charsToGlyphs. -static void test_charsToGlyphs(skiatest::Reporter* reporter, SkTypeface* face) { +static void test_charsToGlyphs(skiatest::Reporter* reporter, const sk_sp<SkTypeface>& face) { uint16_t paintGlyphIds[256]; uint16_t faceGlyphIds[256]; @@ -111,9 +112,8 @@ } } -static void test_fontstream(skiatest::Reporter* reporter, - SkStream* stream, int ttcIndex) { - int n = SkFontStream::GetTableTags(stream, ttcIndex, NULL); +static void test_fontstream(skiatest::Reporter* reporter, SkStream* stream, int ttcIndex) { + int n = SkFontStream::GetTableTags(stream, ttcIndex, nullptr); SkAutoTArray<SkFontTableTag> array(n); int n2 = SkFontStream::GetTableTags(stream, ttcIndex, array.get()); @@ -138,34 +138,39 @@ } } -static void test_fontstream(skiatest::Reporter* reporter, SkStream* stream) { - int count = SkFontStream::CountTTCEntries(stream); +static void test_fontstream(skiatest::Reporter* reporter) { + std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("/fonts/test.ttc")); + if (!stream) { + SkDebugf("Skipping FontHostTest::test_fontstream\n"); + return; + } + + int count = SkFontStream::CountTTCEntries(stream.get()); #ifdef DUMP_TTC_TABLES SkDebugf("CountTTCEntries %d\n", count); #endif for (int i = 0; i < count; ++i) { - test_fontstream(reporter, stream, i); + test_fontstream(reporter, stream.get(), i); } } -static void test_fontstream(skiatest::Reporter* reporter) { - // This test cannot run if there is no resource path. - SkString resourcePath = GetResourcePath(); - if (resourcePath.isEmpty()) { - SkDebugf("Could not run fontstream test because resourcePath not specified."); +static void test_symbolfont(skiatest::Reporter* reporter) { + SkUnichar c = 0xf021; + uint16_t g; + SkPaint paint; + paint.setTypeface(MakeResourceAsTypeface("/fonts/SpiderSymbol.ttf")); + paint.setTextEncoding(SkPaint::kUTF32_TextEncoding); + paint.textToGlyphs(&c, 4, &g); + + if (!paint.getTypeface()) { + SkDebugf("Skipping FontHostTest::test_symbolfont\n"); return; } - SkString filename = SkOSPath::Join(resourcePath.c_str(), "test.ttc"); - SkFILEStream stream(filename.c_str()); - if (stream.isValid()) { - test_fontstream(reporter, &stream); - } else { - SkDebugf("Could not run fontstream test because test.ttc not found."); - } + REPORTER_ASSERT(reporter, g == 3); } -static void test_tables(skiatest::Reporter* reporter, SkTypeface* face) { +static void test_tables(skiatest::Reporter* reporter, const sk_sp<SkTypeface>& face) { if (false) { // avoid bit rot, suppress warning SkFontID fontID = face->uniqueID(); REPORTER_ASSERT(reporter, fontID); @@ -210,7 +215,7 @@ static void test_tables(skiatest::Reporter* reporter) { static const char* const gNames[] = { - NULL, // default font + nullptr, // default font "Helvetica", "Arial", "Times", "Times New Roman", "Courier", "Courier New", @@ -219,7 +224,7 @@ }; for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); ++i) { - SkAutoTUnref<SkTypeface> face(SkTypeface::CreateFromName(gNames[i], SkTypeface::kNormal)); + sk_sp<SkTypeface> face(SkTypeface::MakeFromName(gNames[i], SkFontStyle())); if (face) { #ifdef DUMP_TABLES SkDebugf("%s\n", gNames[i]); @@ -238,7 +243,7 @@ */ static void test_advances(skiatest::Reporter* reporter) { static const char* const faces[] = { - NULL, // default font + nullptr, // default font "Arial", "Times", "Times New Roman", "Helvetica", "Courier", "Courier New", "Verdana", "monospace", }; @@ -273,8 +278,7 @@ char txt[] = "long.text.with.lots.of.dots."; for (size_t i = 0; i < SK_ARRAY_COUNT(faces); i++) { - SkAutoTUnref<SkTypeface> face(SkTypeface::CreateFromName(faces[i], SkTypeface::kNormal)); - paint.setTypeface(face); + paint.setTypeface(SkTypeface::MakeFromName(faces[i], SkFontStyle())); for (size_t j = 0; j < SK_ARRAY_COUNT(settings); j++) { paint.setHinting(settings[j].hinting); @@ -307,6 +311,7 @@ test_tables(reporter); test_fontstream(reporter); test_advances(reporter); + test_symbolfont(reporter); } // need tests for SkStrSearch
diff --git a/src/third_party/skia/tests/FontMgrAndroidParserTest.cpp b/src/third_party/skia/tests/FontMgrAndroidParserTest.cpp new file mode 100644 index 0000000..cbcfb3b --- /dev/null +++ b/src/third_party/skia/tests/FontMgrAndroidParserTest.cpp
@@ -0,0 +1,216 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Resources.h" +#include "SkCommandLineFlags.h" +#include "SkFixed.h" +#include "SkFontMgr_android_parser.h" +#include "Test.h" + +#include <cmath> +#include <cstdio> + +DECLARE_bool(verboseFontMgr); + +int CountFallbacks(SkTDArray<FontFamily*> fontFamilies) { + int countOfFallbackFonts = 0; + for (int i = 0; i < fontFamilies.count(); i++) { + if (fontFamilies[i]->fIsFallbackFont) { + countOfFallbackFonts++; + } + } + return countOfFallbackFonts; +} + +//https://tools.ietf.org/html/rfc5234#appendix-B.1 +static bool isALPHA(int c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); +} + +//https://tools.ietf.org/html/rfc5234#appendix-B.1 +static bool isDIGIT(int c) { + return ('0' <= c && c <= '9'); +} + +void ValidateLoadedFonts(SkTDArray<FontFamily*> fontFamilies, const char* firstExpectedFile, + skiatest::Reporter* reporter) { + REPORTER_ASSERT(reporter, fontFamilies[0]->fNames.count() == 5); + REPORTER_ASSERT(reporter, !strcmp(fontFamilies[0]->fNames[0].c_str(), "sans-serif")); + REPORTER_ASSERT(reporter, + !strcmp(fontFamilies[0]->fFonts[0].fFileName.c_str(), firstExpectedFile)); + REPORTER_ASSERT(reporter, !fontFamilies[0]->fIsFallbackFont); + + // Check that the languages are all sane. + for (int i = 0; i < fontFamilies.count(); ++i) { + const SkString& lang = fontFamilies[i]->fLanguage.getTag(); + for (size_t j = 0; j < lang.size(); ++j) { + int c = lang[j]; + REPORTER_ASSERT(reporter, isALPHA(c) || isDIGIT(c) || '-' == c); + } + } + + // All file names in the test configuration files start with a capital letter. + // This is not a general requirement, but it is true of all the test configuration data. + // Verifying ensures the filenames have been read sanely and have not been 'sliced'. + for (int i = 0; i < fontFamilies.count(); ++i) { + FontFamily& family = *fontFamilies[i]; + for (int j = 0; j < family.fFonts.count(); ++j) { + FontFileInfo& file = family.fFonts[j]; + REPORTER_ASSERT(reporter, !file.fFileName.isEmpty() && + file.fFileName[0] >= 'A' && + file.fFileName[0] <= 'Z'); + } + } +} + +void DumpLoadedFonts(SkTDArray<FontFamily*> fontFamilies, const char* label) { + if (!FLAGS_verboseFontMgr) { + return; + } + + SkDebugf("\n--- Dumping %s\n", label); + for (int i = 0; i < fontFamilies.count(); ++i) { + SkDebugf("Family %d:\n", i); + switch(fontFamilies[i]->fVariant) { + case kElegant_FontVariant: SkDebugf(" elegant\n"); break; + case kCompact_FontVariant: SkDebugf(" compact\n"); break; + default: break; + } + SkDebugf(" basePath %s\n", fontFamilies[i]->fBasePath.c_str()); + if (!fontFamilies[i]->fLanguage.getTag().isEmpty()) { + SkDebugf(" language %s\n", fontFamilies[i]->fLanguage.getTag().c_str()); + } + for (int j = 0; j < fontFamilies[i]->fNames.count(); ++j) { + SkDebugf(" name %s\n", fontFamilies[i]->fNames[j].c_str()); + } + for (int j = 0; j < fontFamilies[i]->fFonts.count(); ++j) { + const FontFileInfo& ffi = fontFamilies[i]->fFonts[j]; + SkDebugf(" file (%d) %s#%d", ffi.fWeight, ffi.fFileName.c_str(), ffi.fIndex); + for (const auto& coordinate : ffi.fVariationDesignPosition) { + SkDebugf(" @'%c%c%c%c'=%f", + (coordinate.axis >> 24) & 0xFF, + (coordinate.axis >> 16) & 0xFF, + (coordinate.axis >> 8) & 0xFF, + (coordinate.axis ) & 0xFF, + coordinate.value); + } + SkDebugf("\n"); + } + } + SkDebugf("\n\n"); +} + +template <int N, typename T> static double test_parse_fixed_r(skiatest::Reporter* reporter, + double low, double high, double inc) +{ + double SK_FixedMax_double = nextafter(1 << (sizeof(T) * CHAR_BIT - N - 1), 0.0); + double SK_FixedEpsilon_double = (1.0 / (1 << N)); + double maxError = 0; + char buffer[64]; + for (double f = low; f < high; f += inc) { + SkString s; + // 'sprintf' formatting as expected depends on the current locale being "C". + // We currently expect tests and tools to run in the "C" locale. + sprintf(buffer, "%.20f", f); + T fix; + bool b = parse_fixed<N>(buffer, &fix); + if (b) { + double f2 = fix * SK_FixedEpsilon_double; + double error = fabs(f - f2); + REPORTER_ASSERT(reporter, error <= SK_FixedEpsilon_double); + maxError = SkTMax(maxError, error); + } else { + REPORTER_ASSERT(reporter, f < -SK_FixedMax_double || SK_FixedMax_double < f); + } + } + + //SkDebugf("maxError: %.20f\n", maxError); + return maxError; +} + +static void test_parse_fixed(skiatest::Reporter* reporter) { + test_parse_fixed_r<27, int32_t>(reporter, -8.1, -7.9, 0.000001); + test_parse_fixed_r<27, int32_t>(reporter, -0.1, 0.1, 0.000001); + test_parse_fixed_r<27, int32_t>(reporter, 7.9, 8.1, 0.000001); + test_parse_fixed_r<16, int32_t>(reporter, -0.125, 0.125, 1.0 / (1 << 19)); + test_parse_fixed_r<16, int32_t>(reporter, -32768.125, -32766.875, 1.0 / (1 << 17)); + test_parse_fixed_r<16, int32_t>(reporter, 32766.875, 32768.125, 1.0 / (1 << 17)); + test_parse_fixed_r<16, int32_t>(reporter, -1.1, 1.1, 0.0001); + + SkFixed fix; + REPORTER_ASSERT(reporter, !parse_fixed<27>("-17.1", &fix)); + REPORTER_ASSERT(reporter, !parse_fixed<16>("32768", &fix)); + REPORTER_ASSERT(reporter, !parse_fixed<16>("", &fix)); + REPORTER_ASSERT(reporter, !parse_fixed<16>(".", &fix)); + REPORTER_ASSERT(reporter, !parse_fixed<16>("123.", &fix)); + REPORTER_ASSERT(reporter, !parse_fixed<16>("a", &fix)); + REPORTER_ASSERT(reporter, !parse_fixed<16>(".123a", &fix)); +} + +DEF_TEST(FontMgrAndroidParser, reporter) { + test_parse_fixed(reporter); + + bool resourcesMissing = false; + + SkTDArray<FontFamily*> preV17FontFamilies; + SkFontMgr_Android_Parser::GetCustomFontFamilies(preV17FontFamilies, + SkString("/custom/font/path/"), + GetResourcePath("android_fonts/pre_v17/system_fonts.xml").c_str(), + GetResourcePath("android_fonts/pre_v17/fallback_fonts.xml").c_str()); + + if (preV17FontFamilies.count() > 0) { + REPORTER_ASSERT(reporter, preV17FontFamilies.count() == 14); + REPORTER_ASSERT(reporter, CountFallbacks(preV17FontFamilies) == 10); + + DumpLoadedFonts(preV17FontFamilies, "pre version 17"); + ValidateLoadedFonts(preV17FontFamilies, "Roboto-Regular.ttf", reporter); + } else { + resourcesMissing = true; + } + preV17FontFamilies.deleteAll(); + + + SkTDArray<FontFamily*> v17FontFamilies; + SkFontMgr_Android_Parser::GetCustomFontFamilies(v17FontFamilies, + SkString("/custom/font/path/"), + GetResourcePath("android_fonts/v17/system_fonts.xml").c_str(), + GetResourcePath("android_fonts/v17/fallback_fonts.xml").c_str(), + GetResourcePath("android_fonts/v17").c_str()); + + if (v17FontFamilies.count() > 0) { + REPORTER_ASSERT(reporter, v17FontFamilies.count() == 56); + REPORTER_ASSERT(reporter, CountFallbacks(v17FontFamilies) == 46); + + DumpLoadedFonts(v17FontFamilies, "version 17"); + ValidateLoadedFonts(v17FontFamilies, "Roboto-Regular.ttf", reporter); + } else { + resourcesMissing = true; + } + v17FontFamilies.deleteAll(); + + + SkTDArray<FontFamily*> v22FontFamilies; + SkFontMgr_Android_Parser::GetCustomFontFamilies(v22FontFamilies, + SkString("/custom/font/path/"), + GetResourcePath("android_fonts/v22/fonts.xml").c_str(), + nullptr); + + if (v22FontFamilies.count() > 0) { + REPORTER_ASSERT(reporter, v22FontFamilies.count() == 54); + REPORTER_ASSERT(reporter, CountFallbacks(v22FontFamilies) == 42); + + DumpLoadedFonts(v22FontFamilies, "version 22"); + ValidateLoadedFonts(v22FontFamilies, "Roboto-Thin.ttf", reporter); + } else { + resourcesMissing = true; + } + v22FontFamilies.deleteAll(); + + if (resourcesMissing) { + SkDebugf("---- Resource files missing for FontConfigParser test\n"); + } +}
diff --git a/src/third_party/skia/tests/FontMgrTest.cpp b/src/third_party/skia/tests/FontMgrTest.cpp index a1dc9dc..ef420f3 100644 --- a/src/third_party/skia/tests/FontMgrTest.cpp +++ b/src/third_party/skia/tests/FontMgrTest.cpp
@@ -5,16 +5,21 @@ * found in the LICENSE file. */ +#include "SkAdvancedTypefaceMetrics.h" #include "SkCommandLineFlags.h" +#include "SkFont.h" #include "SkFontMgr.h" +#include "SkPaint.h" #include "SkTypeface.h" #include "Test.h" -#include "SkFont.h" -#include "SkPaint.h" +#include <initializer_list> +#include <limits> +#include <vector> + static void test_font(skiatest::Reporter* reporter) { uint32_t flags = 0; - SkAutoTUnref<SkFont> font(SkFont::Create(NULL, 24, SkFont::kA8_MaskType, flags)); + sk_sp<SkFont> font(SkFont::Make(nullptr, 24, SkFont::kA8_MaskType, flags)); REPORTER_ASSERT(reporter, font->getTypeface()); REPORTER_ASSERT(reporter, 24 == font->getSize()); @@ -34,7 +39,7 @@ REPORTER_ASSERT(reporter, glyphs[0] != glyphs[1]); // 'h' != 'e' REPORTER_ASSERT(reporter, glyphs[2] == glyphs[3]); // 'l' == 'l' - SkAutoTUnref<SkFont> newFont(font->cloneWithSize(36)); + sk_sp<SkFont> newFont(font->makeWithSize(36)); REPORTER_ASSERT(reporter, newFont.get()); REPORTER_ASSERT(reporter, font->getTypeface() == newFont->getTypeface()); REPORTER_ASSERT(reporter, 36 == newFont->getSize()); // double check we haven't changed @@ -42,7 +47,7 @@ SkPaint paint; paint.setTextSize(18); - font.reset(SkFont::Testing_CreateFromPaint(paint)); + font = SkFont::Testing_CreateFromPaint(paint); REPORTER_ASSERT(reporter, font.get()); REPORTER_ASSERT(reporter, font->getSize() == paint.getTextSize()); REPORTER_ASSERT(reporter, SkFont::kBW_MaskType == font->getMaskType()); @@ -59,14 +64,12 @@ }; for (size_t i = 0; i < SK_ARRAY_COUNT(inNames); ++i) { - SkAutoTUnref<SkTypeface> first(SkTypeface::CreateFromName(inNames[i], - SkTypeface::kNormal)); - if (NULL == first.get()) { + sk_sp<SkTypeface> first(SkTypeface::MakeFromName(inNames[i], SkFontStyle())); + if (nullptr == first.get()) { continue; } for (int j = 0; j < 10; ++j) { - SkAutoTUnref<SkTypeface> face(SkTypeface::CreateFromName(inNames[i], - SkTypeface::kNormal)); + sk_sp<SkTypeface> face(SkTypeface::MakeFromName(inNames[i], SkFontStyle())); #if 0 SkString name; face->getFamilyName(&name); @@ -79,15 +82,15 @@ } static void test_fontiter(skiatest::Reporter* reporter, bool verbose) { - SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault()); + sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); int count = fm->countFamilies(); for (int i = 0; i < count; ++i) { SkString fname; fm->getFamilyName(i, &fname); - SkAutoTUnref<SkFontStyleSet> fnset(fm->matchFamily(fname.c_str())); - SkAutoTUnref<SkFontStyleSet> set(fm->createStyleSet(i)); + sk_sp<SkFontStyleSet> fnset(fm->matchFamily(fname.c_str())); + sk_sp<SkFontStyleSet> set(fm->createStyleSet(i)); REPORTER_ASSERT(reporter, fnset->count() == set->count()); if (verbose) { @@ -100,12 +103,611 @@ set->getStyle(j, &fs, &sname); // REPORTER_ASSERT(reporter, sname.size() > 0); - SkAutoTUnref<SkTypeface> face(set->createTypeface(j)); + sk_sp<SkTypeface> face(set->createTypeface(j)); // REPORTER_ASSERT(reporter, face.get()); if (verbose) { SkDebugf("\t[%d] %s [%3d %d %d]\n", j, sname.c_str(), - fs.weight(), fs.width(), fs.isItalic()); + fs.weight(), fs.width(), fs.slant()); + } + } + } +} + +static void test_match(skiatest::Reporter* reporter) { + sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); + sk_sp<SkFontStyleSet> styleSet(fm->matchFamily(nullptr)); + REPORTER_ASSERT(reporter, styleSet); +} + +static void test_matchStyleCSS3(skiatest::Reporter* reporter) { + static const SkFontStyle invalidFontStyle(101, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant); + + class TestTypeface : public SkTypeface { + public: + TestTypeface(const SkFontStyle& fontStyle) : SkTypeface(fontStyle, false){} + protected: + SkStreamAsset* onOpenStream(int* ttcIndex) const override { return nullptr; } + SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&, + const SkDescriptor*) const override { + return nullptr; + } + void onFilterRec(SkScalerContextRec*) const override { } + std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override { + return nullptr; + } + void onGetFontDescriptor(SkFontDescriptor*, bool*) const override { } + virtual int onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const override { + if (glyphs && glyphCount > 0) { + sk_bzero(glyphs, glyphCount * sizeof(glyphs[0])); + } + return 0; + } + int onCountGlyphs() const override { return 0; } + int onGetUPEM() const override { return 0; } + class EmptyLocalizedStrings : public SkTypeface::LocalizedStrings { + public: + bool next(SkTypeface::LocalizedString*) override { return false; } + }; + void onGetFamilyName(SkString* familyName) const override { + familyName->reset(); + } + SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override { + return new EmptyLocalizedStrings; + } + int onGetVariationDesignPosition( + SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override + { + return 0; + } + int onGetTableTags(SkFontTableTag tags[]) const override { return 0; } + size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override { + return 0; + } + }; + + class TestFontStyleSet : public SkFontStyleSet { + public: + TestFontStyleSet(std::initializer_list<SkFontStyle> styles) : fStyles(styles) {} + int count() override { return static_cast<int>(fStyles.size()); } + void getStyle(int index, SkFontStyle* style, SkString*) override { + if (style) { + *style = fStyles[index]; + } + } + SkTypeface* createTypeface(int index) override { + if (index < 0 || this->count() <= index) { + return new TestTypeface(invalidFontStyle); + } + return new TestTypeface(fStyles[index]); + } + SkTypeface* matchStyle(const SkFontStyle& pattern) override { + return this->matchStyleCSS3(pattern); + } + private: + std::vector<SkFontStyle> fStyles; + }; + + SkFontStyle condensed_normal_100(SkFontStyle::kThin_Weight, SkFontStyle::kCondensed_Width, SkFontStyle::kUpright_Slant); + SkFontStyle condensed_normal_900(SkFontStyle::kBlack_Weight, SkFontStyle::kCondensed_Width, SkFontStyle::kUpright_Slant); + SkFontStyle condensed_italic_100(SkFontStyle::kThin_Weight, SkFontStyle::kCondensed_Width, SkFontStyle::kItalic_Slant); + SkFontStyle condensed_italic_900(SkFontStyle::kBlack_Weight, SkFontStyle::kCondensed_Width, SkFontStyle::kItalic_Slant); + SkFontStyle condensed_obliqu_100(SkFontStyle::kThin_Weight, SkFontStyle::kCondensed_Width, SkFontStyle::kOblique_Slant); + SkFontStyle condensed_obliqu_900(SkFontStyle::kBlack_Weight, SkFontStyle::kCondensed_Width, SkFontStyle::kOblique_Slant); + SkFontStyle expanded_normal_100(SkFontStyle::kThin_Weight, SkFontStyle::kExpanded_Width, SkFontStyle::kUpright_Slant); + SkFontStyle expanded_normal_900(SkFontStyle::kBlack_Weight, SkFontStyle::kExpanded_Width, SkFontStyle::kUpright_Slant); + SkFontStyle expanded_italic_100(SkFontStyle::kThin_Weight, SkFontStyle::kExpanded_Width, SkFontStyle::kItalic_Slant); + SkFontStyle expanded_italic_900(SkFontStyle::kBlack_Weight, SkFontStyle::kExpanded_Width, SkFontStyle::kItalic_Slant); + SkFontStyle expanded_obliqu_100(SkFontStyle::kThin_Weight, SkFontStyle::kExpanded_Width, SkFontStyle::kOblique_Slant); + SkFontStyle expanded_obliqu_900(SkFontStyle::kBlack_Weight, SkFontStyle::kExpanded_Width, SkFontStyle::kOblique_Slant); + + SkFontStyle normal_normal_100(SkFontStyle::kThin_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant); + SkFontStyle normal_normal_200(SkFontStyle::kExtraLight_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant); + SkFontStyle normal_normal_300(SkFontStyle::kLight_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant); + SkFontStyle normal_normal_400(SkFontStyle::kNormal_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant); + SkFontStyle normal_normal_500(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant); + SkFontStyle normal_normal_600(SkFontStyle::kSemiBold_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant); + SkFontStyle normal_normal_700(SkFontStyle::kBold_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant); + SkFontStyle normal_normal_800(SkFontStyle::kExtraBold_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant); + SkFontStyle normal_normal_900(SkFontStyle::kBlack_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant); + + struct StyleSetTest { + TestFontStyleSet styleSet; + struct Case { + SkFontStyle pattern; + SkFontStyle expectedResult; + }; + std::vector<Case> cases; + } tests[] = { + { + { condensed_normal_100,condensed_normal_900,condensed_italic_100,condensed_italic_900, + expanded_normal_100, expanded_normal_900, expanded_italic_100, expanded_italic_900 }, + { + { condensed_normal_100, condensed_normal_100 }, + { condensed_normal_900, condensed_normal_900 }, + { condensed_italic_100, condensed_italic_100 }, + { condensed_italic_900, condensed_italic_900 }, + { expanded_normal_100, expanded_normal_100 }, + { expanded_normal_900, expanded_normal_900 }, + { expanded_italic_100, expanded_italic_100 }, + { expanded_italic_900, expanded_italic_900 }, + }, + }, + + { + { condensed_normal_100,condensed_italic_100,expanded_normal_100,expanded_italic_100 }, + { + { condensed_normal_100, condensed_normal_100 }, + { condensed_normal_900, condensed_normal_100 }, + { condensed_italic_100, condensed_italic_100 }, + { condensed_italic_900, condensed_italic_100 }, + { expanded_normal_100, expanded_normal_100 }, + { expanded_normal_900, expanded_normal_100 }, + { expanded_italic_100, expanded_italic_100 }, + { expanded_italic_900, expanded_italic_100 }, + }, + }, + + { + { condensed_normal_900,condensed_italic_900,expanded_normal_900,expanded_italic_900 }, + { + { condensed_normal_100, condensed_normal_900 }, + { condensed_normal_900, condensed_normal_900 }, + { condensed_italic_100, condensed_italic_900 }, + { condensed_italic_900, condensed_italic_900 }, + { expanded_normal_100, expanded_normal_900 }, + { expanded_normal_900, expanded_normal_900 }, + { expanded_italic_100, expanded_italic_900 }, + { expanded_italic_900, expanded_italic_900 }, + }, + }, + + { + { condensed_normal_100,condensed_normal_900,expanded_normal_100,expanded_normal_900 }, + { + { condensed_normal_100, condensed_normal_100 }, + { condensed_normal_900, condensed_normal_900 }, + { condensed_italic_100, condensed_normal_100 }, + { condensed_italic_900, condensed_normal_900 }, + { expanded_normal_100, expanded_normal_100 }, + { expanded_normal_900, expanded_normal_900 }, + { expanded_italic_100, expanded_normal_100 }, + { expanded_italic_900, expanded_normal_900 }, + }, + }, + + { + { condensed_normal_100,expanded_normal_100 }, + { + { condensed_normal_100, condensed_normal_100 }, + { condensed_normal_900, condensed_normal_100 }, + { condensed_italic_100, condensed_normal_100 }, + { condensed_italic_900, condensed_normal_100 }, + { expanded_normal_100, expanded_normal_100 }, + { expanded_normal_900, expanded_normal_100 }, + { expanded_italic_100, expanded_normal_100 }, + { expanded_italic_900, expanded_normal_100 }, + }, + }, + + { + { condensed_normal_900,expanded_normal_900 }, + { + { condensed_normal_100, condensed_normal_900 }, + { condensed_normal_900, condensed_normal_900 }, + { condensed_italic_100, condensed_normal_900 }, + { condensed_italic_900, condensed_normal_900 }, + { expanded_normal_100, expanded_normal_900 }, + { expanded_normal_900, expanded_normal_900 }, + { expanded_italic_100, expanded_normal_900 }, + { expanded_italic_900, expanded_normal_900 }, + }, + }, + + { + { condensed_italic_100,condensed_italic_900,expanded_italic_100,expanded_italic_900 }, + { + { condensed_normal_100, condensed_italic_100 }, + { condensed_normal_900, condensed_italic_900 }, + { condensed_italic_100, condensed_italic_100 }, + { condensed_italic_900, condensed_italic_900 }, + { expanded_normal_100, expanded_italic_100 }, + { expanded_normal_900, expanded_italic_900 }, + { expanded_italic_100, expanded_italic_100 }, + { expanded_italic_900, expanded_italic_900 }, + }, + }, + + { + { condensed_italic_100,expanded_italic_100 }, + { + { condensed_normal_100, condensed_italic_100 }, + { condensed_normal_900, condensed_italic_100 }, + { condensed_italic_100, condensed_italic_100 }, + { condensed_italic_900, condensed_italic_100 }, + { expanded_normal_100, expanded_italic_100 }, + { expanded_normal_900, expanded_italic_100 }, + { expanded_italic_100, expanded_italic_100 }, + { expanded_italic_900, expanded_italic_100 }, + }, + }, + + { + { condensed_italic_900,expanded_italic_900 }, + { + { condensed_normal_100, condensed_italic_900 }, + { condensed_normal_900, condensed_italic_900 }, + { condensed_italic_100, condensed_italic_900 }, + { condensed_italic_900, condensed_italic_900 }, + { expanded_normal_100, expanded_italic_900 }, + { expanded_normal_900, expanded_italic_900 }, + { expanded_italic_100, expanded_italic_900 }, + { expanded_italic_900, expanded_italic_900 }, + }, + }, + + { + { condensed_normal_100,condensed_normal_900,condensed_italic_100,condensed_italic_900 }, + { + { condensed_normal_100, condensed_normal_100 }, + { condensed_normal_900, condensed_normal_900 }, + { condensed_italic_100, condensed_italic_100 }, + { condensed_italic_900, condensed_italic_900 }, + { expanded_normal_100, condensed_normal_100 }, + { expanded_normal_900, condensed_normal_900 }, + { expanded_italic_100, condensed_italic_100 }, + { expanded_italic_900, condensed_italic_900 }, + }, + }, + + { + { condensed_normal_100,condensed_italic_100 }, + { + { condensed_normal_100, condensed_normal_100 }, + { condensed_normal_900, condensed_normal_100 }, + { condensed_italic_100, condensed_italic_100 }, + { condensed_italic_900, condensed_italic_100 }, + { expanded_normal_100, condensed_normal_100 }, + { expanded_normal_900, condensed_normal_100 }, + { expanded_italic_100, condensed_italic_100 }, + { expanded_italic_900, condensed_italic_100 }, + }, + }, + + { + { condensed_normal_900,condensed_italic_900 }, + { + { condensed_normal_100, condensed_normal_900 }, + { condensed_normal_900, condensed_normal_900 }, + { condensed_italic_100, condensed_italic_900 }, + { condensed_italic_900, condensed_italic_900 }, + { expanded_normal_100, condensed_normal_900 }, + { expanded_normal_900, condensed_normal_900 }, + { expanded_italic_100, condensed_italic_900 }, + { expanded_italic_900, condensed_italic_900 }, + }, + }, + + { + { condensed_normal_100,condensed_normal_900 }, + { + { condensed_normal_100, condensed_normal_100 }, + { condensed_normal_900, condensed_normal_900 }, + { condensed_italic_100, condensed_normal_100 }, + { condensed_italic_900, condensed_normal_900 }, + { expanded_normal_100, condensed_normal_100 }, + { expanded_normal_900, condensed_normal_900 }, + { expanded_italic_100, condensed_normal_100 }, + { expanded_italic_900, condensed_normal_900 }, + }, + }, + + { + { condensed_normal_100 }, + { + { condensed_normal_100, condensed_normal_100 }, + { condensed_normal_900, condensed_normal_100 }, + { condensed_italic_100, condensed_normal_100 }, + { condensed_italic_900, condensed_normal_100 }, + { expanded_normal_100, condensed_normal_100 }, + { expanded_normal_900, condensed_normal_100 }, + { expanded_italic_100, condensed_normal_100 }, + { expanded_italic_900, condensed_normal_100 }, + }, + }, + + { + { condensed_normal_900 }, + { + { condensed_normal_100, condensed_normal_900 }, + { condensed_normal_900, condensed_normal_900 }, + { condensed_italic_100, condensed_normal_900 }, + { condensed_italic_900, condensed_normal_900 }, + { expanded_normal_100, condensed_normal_900 }, + { expanded_normal_900, condensed_normal_900 }, + { expanded_italic_100, condensed_normal_900 }, + { expanded_italic_900, condensed_normal_900 }, + }, + }, + + { + { condensed_italic_100,condensed_italic_900 }, + { + { condensed_normal_100, condensed_italic_100 }, + { condensed_normal_900, condensed_italic_900 }, + { condensed_italic_100, condensed_italic_100 }, + { condensed_italic_900, condensed_italic_900 }, + { expanded_normal_100, condensed_italic_100 }, + { expanded_normal_900, condensed_italic_900 }, + { expanded_italic_100, condensed_italic_100 }, + { expanded_italic_900, condensed_italic_900 }, + }, + }, + + { + { condensed_italic_100 }, + { + { condensed_normal_100, condensed_italic_100 }, + { condensed_normal_900, condensed_italic_100 }, + { condensed_italic_100, condensed_italic_100 }, + { condensed_italic_900, condensed_italic_100 }, + { expanded_normal_100, condensed_italic_100 }, + { expanded_normal_900, condensed_italic_100 }, + { expanded_italic_100, condensed_italic_100 }, + { expanded_italic_900, condensed_italic_100 }, + }, + }, + + { + { condensed_italic_900 }, + { + { condensed_normal_100, condensed_italic_900 }, + { condensed_normal_900, condensed_italic_900 }, + { condensed_italic_100, condensed_italic_900 }, + { condensed_italic_900, condensed_italic_900 }, + { expanded_normal_100, condensed_italic_900 }, + { expanded_normal_900, condensed_italic_900 }, + { expanded_italic_100, condensed_italic_900 }, + { expanded_italic_900, condensed_italic_900 }, + }, + }, + + { + { expanded_normal_100,expanded_normal_900, + expanded_italic_100,expanded_italic_900 }, + { + { condensed_normal_100, expanded_normal_100 }, + { condensed_normal_900, expanded_normal_900 }, + { condensed_italic_100, expanded_italic_100 }, + { condensed_italic_900, expanded_italic_900 }, + { condensed_obliqu_100, expanded_italic_100 }, + { condensed_obliqu_900, expanded_italic_900 }, + { expanded_normal_100, expanded_normal_100 }, + { expanded_normal_900, expanded_normal_900 }, + { expanded_italic_100, expanded_italic_100 }, + { expanded_italic_900, expanded_italic_900 }, + { expanded_obliqu_100, expanded_italic_100 }, + { expanded_obliqu_900, expanded_italic_900 }, + }, + }, + + { + { expanded_normal_100,expanded_italic_100 }, + { + { condensed_normal_100, expanded_normal_100 }, + { condensed_normal_900, expanded_normal_100 }, + { condensed_italic_100, expanded_italic_100 }, + { condensed_italic_900, expanded_italic_100 }, + { expanded_normal_100, expanded_normal_100 }, + { expanded_normal_900, expanded_normal_100 }, + { expanded_italic_100, expanded_italic_100 }, + { expanded_italic_900, expanded_italic_100 }, + }, + }, + + { + { expanded_normal_900,expanded_italic_900 }, + { + { condensed_normal_100, expanded_normal_900 }, + { condensed_normal_900, expanded_normal_900 }, + { condensed_italic_100, expanded_italic_900 }, + { condensed_italic_900, expanded_italic_900 }, + { expanded_normal_100, expanded_normal_900 }, + { expanded_normal_900, expanded_normal_900 }, + { expanded_italic_100, expanded_italic_900 }, + { expanded_italic_900, expanded_italic_900 }, + }, + }, + + { + { expanded_normal_100,expanded_normal_900 }, + { + { condensed_normal_100, expanded_normal_100 }, + { condensed_normal_900, expanded_normal_900 }, + { condensed_italic_100, expanded_normal_100 }, + { condensed_italic_900, expanded_normal_900 }, + { expanded_normal_100, expanded_normal_100 }, + { expanded_normal_900, expanded_normal_900 }, + { expanded_italic_100, expanded_normal_100 }, + { expanded_italic_900, expanded_normal_900 }, + }, + }, + + { + { expanded_normal_100 }, + { + { condensed_normal_100, expanded_normal_100 }, + { condensed_normal_900, expanded_normal_100 }, + { condensed_italic_100, expanded_normal_100 }, + { condensed_italic_900, expanded_normal_100 }, + { expanded_normal_100, expanded_normal_100 }, + { expanded_normal_900, expanded_normal_100 }, + { expanded_italic_100, expanded_normal_100 }, + { expanded_italic_900, expanded_normal_100 }, + }, + }, + + { + { expanded_normal_900 }, + { + { condensed_normal_100, expanded_normal_900 }, + { condensed_normal_900, expanded_normal_900 }, + { condensed_italic_100, expanded_normal_900 }, + { condensed_italic_900, expanded_normal_900 }, + { expanded_normal_100, expanded_normal_900 }, + { expanded_normal_900, expanded_normal_900 }, + { expanded_italic_100, expanded_normal_900 }, + { expanded_italic_900, expanded_normal_900 }, + }, + }, + + { + { expanded_italic_100,expanded_italic_900 }, + { + { condensed_normal_100, expanded_italic_100 }, + { condensed_normal_900, expanded_italic_900 }, + { condensed_italic_100, expanded_italic_100 }, + { condensed_italic_900, expanded_italic_900 }, + { expanded_normal_100, expanded_italic_100 }, + { expanded_normal_900, expanded_italic_900 }, + { expanded_italic_100, expanded_italic_100 }, + { expanded_italic_900, expanded_italic_900 }, + }, + }, + + { + { expanded_italic_100 }, + { + { condensed_normal_100, expanded_italic_100 }, + { condensed_normal_900, expanded_italic_100 }, + { condensed_italic_100, expanded_italic_100 }, + { condensed_italic_900, expanded_italic_100 }, + { expanded_normal_100, expanded_italic_100 }, + { expanded_normal_900, expanded_italic_100 }, + { expanded_italic_100, expanded_italic_100 }, + { expanded_italic_900, expanded_italic_100 }, + }, + }, + + { + { expanded_italic_900 }, + { + { condensed_normal_100, expanded_italic_900 }, + { condensed_normal_900, expanded_italic_900 }, + { condensed_italic_100, expanded_italic_900 }, + { condensed_italic_900, expanded_italic_900 }, + { expanded_normal_100, expanded_italic_900 }, + { expanded_normal_900, expanded_italic_900 }, + { expanded_italic_100, expanded_italic_900 }, + { expanded_italic_900, expanded_italic_900 }, + }, + }, + + { + { normal_normal_100, normal_normal_900 }, + { + { normal_normal_300, normal_normal_100 }, + { normal_normal_400, normal_normal_100 }, + { normal_normal_500, normal_normal_100 }, + { normal_normal_600, normal_normal_900 }, + }, + }, + + { + { normal_normal_100, normal_normal_400, normal_normal_900 }, + { + { normal_normal_300, normal_normal_100 }, + { normal_normal_400, normal_normal_400 }, + { normal_normal_500, normal_normal_400 }, + { normal_normal_600, normal_normal_900 }, + }, + }, + + { + { normal_normal_100, normal_normal_500, normal_normal_900 }, + { + { normal_normal_300, normal_normal_100 }, + { normal_normal_400, normal_normal_500 }, + { normal_normal_500, normal_normal_500 }, + { normal_normal_600, normal_normal_900 }, + }, + }, + + { + { }, + { + { normal_normal_300, invalidFontStyle }, + { normal_normal_400, invalidFontStyle }, + { normal_normal_500, invalidFontStyle }, + { normal_normal_600, invalidFontStyle }, + }, + }, + { + { expanded_normal_100,expanded_normal_900, + expanded_italic_100,expanded_italic_900, + expanded_obliqu_100,expanded_obliqu_900, }, + { + { condensed_normal_100, expanded_normal_100 }, + { condensed_normal_900, expanded_normal_900 }, + { condensed_italic_100, expanded_italic_100 }, + { condensed_italic_900, expanded_italic_900 }, + { condensed_obliqu_100, expanded_obliqu_100 }, + { condensed_obliqu_900, expanded_obliqu_900 }, + { expanded_normal_100, expanded_normal_100 }, + { expanded_normal_900, expanded_normal_900 }, + { expanded_italic_100, expanded_italic_100 }, + { expanded_italic_900, expanded_italic_900 }, + { expanded_obliqu_100, expanded_obliqu_100 }, + { expanded_obliqu_900, expanded_obliqu_900 }, + }, + }, + { + { expanded_normal_100,expanded_normal_900, + expanded_obliqu_100,expanded_obliqu_900, }, + { + { condensed_normal_100, expanded_normal_100 }, + { condensed_normal_900, expanded_normal_900 }, + { condensed_italic_100, expanded_obliqu_100 }, + { condensed_italic_900, expanded_obliqu_900 }, + { condensed_obliqu_100, expanded_obliqu_100 }, + { condensed_obliqu_900, expanded_obliqu_900 }, + { expanded_normal_100, expanded_normal_100 }, + { expanded_normal_900, expanded_normal_900 }, + { expanded_italic_100, expanded_obliqu_100 }, + { expanded_italic_900, expanded_obliqu_900 }, + { expanded_obliqu_100, expanded_obliqu_100 }, + { expanded_obliqu_900, expanded_obliqu_900 }, + }, + }, + { + { expanded_italic_100,expanded_italic_900, + expanded_obliqu_100,expanded_obliqu_900, }, + { + { condensed_normal_100, expanded_obliqu_100 }, + { condensed_normal_900, expanded_obliqu_900 }, + { condensed_italic_100, expanded_italic_100 }, + { condensed_italic_900, expanded_italic_900 }, + { condensed_obliqu_100, expanded_obliqu_100 }, + { condensed_obliqu_900, expanded_obliqu_900 }, + { expanded_normal_100, expanded_obliqu_100 }, + { expanded_normal_900, expanded_obliqu_900 }, + { expanded_italic_100, expanded_italic_100 }, + { expanded_italic_900, expanded_italic_900 }, + { expanded_obliqu_100, expanded_obliqu_100 }, + { expanded_obliqu_900, expanded_obliqu_900 }, + }, + }, + }; + + for (StyleSetTest& test : tests) { + for (const StyleSetTest::Case& testCase : test.cases) { + sk_sp<SkTypeface> typeface(test.styleSet.matchStyle(testCase.pattern)); + if (typeface) { + REPORTER_ASSERT(reporter, typeface->fontStyle() == testCase.expectedResult); + } else { + REPORTER_ASSERT(reporter, invalidFontStyle == testCase.expectedResult); } } } @@ -114,6 +716,8 @@ DEFINE_bool(verboseFontMgr, false, "run verbose fontmgr tests."); DEF_TEST(FontMgr, reporter) { + test_match(reporter); + test_matchStyleCSS3(reporter); test_fontiter(reporter, FLAGS_verboseFontMgr); test_alias_names(reporter); test_font(reporter);
diff --git a/src/third_party/skia/tests/FontNamesTest.cpp b/src/third_party/skia/tests/FontNamesTest.cpp index 5c314e4..e5dce32 100644 --- a/src/third_party/skia/tests/FontNamesTest.cpp +++ b/src/third_party/skia/tests/FontNamesTest.cpp
@@ -142,16 +142,16 @@ static void test_systemfonts(skiatest::Reporter* reporter, bool verbose) { static const SkFontTableTag nameTag = SkSetFourByteTag('n','a','m','e'); - SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault()); + sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); int count = SkMin32(fm->countFamilies(), MAX_FAMILIES); for (int i = 0; i < count; ++i) { - SkAutoTUnref<SkFontStyleSet> set(fm->createStyleSet(i)); + sk_sp<SkFontStyleSet> set(fm->createStyleSet(i)); for (int j = 0; j < set->count(); ++j) { SkString sname; SkFontStyle fs; set->getStyle(j, &fs, &sname); - SkAutoTUnref<SkTypeface> typeface(set->createTypeface(j)); + sk_sp<SkTypeface> typeface(set->createTypeface(j)); SkString familyName; typeface->getFamilyName(&familyName); @@ -159,7 +159,7 @@ SkDebugf("[%s]\n", familyName.c_str()); } - SkAutoTUnref<SkTypeface::LocalizedStrings> familyNamesIter( + sk_sp<SkTypeface::LocalizedStrings> familyNamesIter( typeface->createFamilyNameIterator()); SkTypeface::LocalizedString familyNameLocalized; while (familyNamesIter->next(&familyNameLocalized)) {
diff --git a/src/third_party/skia/tests/FontObjTest.cpp b/src/third_party/skia/tests/FontObjTest.cpp index c2d8f5d..44f799e 100644 --- a/src/third_party/skia/tests/FontObjTest.cpp +++ b/src/third_party/skia/tests/FontObjTest.cpp
@@ -23,7 +23,7 @@ } static void test_cachedfont(skiatest::Reporter* reporter, const SkPaint& paint) { - SkAutoTUnref<SkFont> font(SkFont::Testing_CreateFromPaint(paint)); + sk_sp<SkFont> font(SkFont::Testing_CreateFromPaint(paint)); // Currently SkFont resolves null into the default, so only test if paint's is not null if (paint.getTypeface()) { @@ -43,7 +43,7 @@ static void test_cachedfont(skiatest::Reporter* reporter) { static const char* const faces[] = { - NULL, // default font + nullptr, // default font "Arial", "Times", "Times New Roman", "Helvetica", "Courier", "Courier New", "Verdana", "monospace", }; @@ -78,8 +78,7 @@ char txt[] = "long.text.with.lots.of.dots."; for (size_t i = 0; i < SK_ARRAY_COUNT(faces); i++) { - SkAutoTUnref<SkTypeface> face(SkTypeface::CreateFromName(faces[i], SkTypeface::kNormal)); - paint.setTypeface(face); + paint.setTypeface(SkTypeface::MakeFromName(faces[i], SkFontStyle())); for (size_t j = 0; j < SK_ARRAY_COUNT(settings); j++) { paint.setHinting(settings[j].hinting); @@ -103,7 +102,7 @@ REPORTER_ASSERT(reporter, width1 == width2); - SkAutoTUnref<SkFont> font(SkFont::Testing_CreateFromPaint(paint)); + sk_sp<SkFont> font(SkFont::Testing_CreateFromPaint(paint)); SkScalar font_width1 = font->measureText(txt, strlen(txt), kUTF8_SkTextEncoding); // measureText not yet implemented... REPORTER_ASSERT(reporter, font_width1 == -1);
diff --git a/src/third_party/skia/tests/FrontBufferedStreamTest.cpp b/src/third_party/skia/tests/FrontBufferedStreamTest.cpp index cb11b12..69ff488 100644 --- a/src/third_party/skia/tests/FrontBufferedStreamTest.cpp +++ b/src/third_party/skia/tests/FrontBufferedStreamTest.cpp
@@ -5,10 +5,12 @@ * found in the LICENSE file. */ +#include "SkAutoMalloc.h" +#include "SkBitmap.h" +#include "SkCodec.h" #include "SkFrontBufferedStream.h" #include "SkRefCnt.h" #include "SkStream.h" -#include "SkTypes.h" #include "Test.h" static void test_read(skiatest::Reporter* reporter, SkStream* bufferedStream, @@ -49,72 +51,75 @@ // Tests reading the stream across boundaries of what has been buffered so far and what // the total buffer size is. static void test_incremental_buffering(skiatest::Reporter* reporter, size_t bufferSize) { - SkMemoryStream memStream(gAbcs, strlen(gAbcs), false); + // NOTE: For this and other tests in this file, we cheat and continue to refer to the + // wrapped stream, but that's okay because we know the wrapping stream has not been + // deleted yet (and we only call const methods in it). + SkMemoryStream* memStream = new SkMemoryStream(gAbcs, strlen(gAbcs), false); - SkAutoTUnref<SkStream> bufferedStream(SkFrontBufferedStream::Create(&memStream, bufferSize)); - test_hasLength(reporter, *bufferedStream.get(), memStream); + std::unique_ptr<SkStream> bufferedStream(SkFrontBufferedStream::Create(memStream, bufferSize)); + test_hasLength(reporter, *bufferedStream, *memStream); // First, test reading less than the max buffer size. - test_read(reporter, bufferedStream, gAbcs, bufferSize / 2); + test_read(reporter, bufferedStream.get(), gAbcs, bufferSize / 2); // Now test rewinding back to the beginning and reading less than what was // already buffered. - test_rewind(reporter, bufferedStream, true); - test_read(reporter, bufferedStream, gAbcs, bufferSize / 4); + test_rewind(reporter, bufferedStream.get(), true); + test_read(reporter, bufferedStream.get(), gAbcs, bufferSize / 4); // Now test reading part of what was buffered, and buffering new data. - test_read(reporter, bufferedStream, gAbcs + bufferedStream->getPosition(), bufferSize / 2); + test_read(reporter, bufferedStream.get(), gAbcs + bufferSize / 4, bufferSize / 2); // Now test reading what was buffered, buffering new data, and // reading directly from the stream. - test_rewind(reporter, bufferedStream, true); - test_read(reporter, bufferedStream, gAbcs, bufferSize << 1); + test_rewind(reporter, bufferedStream.get(), true); + test_read(reporter, bufferedStream.get(), gAbcs, bufferSize << 1); // We have reached the end of the buffer, so rewinding will fail. // This test assumes that the stream is larger than the buffer; otherwise the // result of rewind should be true. - test_rewind(reporter, bufferedStream, false); + test_rewind(reporter, bufferedStream.get(), false); } static void test_perfectly_sized_buffer(skiatest::Reporter* reporter, size_t bufferSize) { - SkMemoryStream memStream(gAbcs, strlen(gAbcs), false); - SkAutoTUnref<SkStream> bufferedStream(SkFrontBufferedStream::Create(&memStream, bufferSize)); - test_hasLength(reporter, *bufferedStream.get(), memStream); + SkMemoryStream* memStream = new SkMemoryStream(gAbcs, strlen(gAbcs), false); + std::unique_ptr<SkStream> bufferedStream(SkFrontBufferedStream::Create(memStream, bufferSize)); + test_hasLength(reporter, *bufferedStream, *memStream); // Read exactly the amount that fits in the buffer. - test_read(reporter, bufferedStream, gAbcs, bufferSize); + test_read(reporter, bufferedStream.get(), gAbcs, bufferSize); // Rewinding should succeed. - test_rewind(reporter, bufferedStream, true); + test_rewind(reporter, bufferedStream.get(), true); // Once again reading buffered info should succeed - test_read(reporter, bufferedStream, gAbcs, bufferSize); + test_read(reporter, bufferedStream.get(), gAbcs, bufferSize); // Read past the size of the buffer. At this point, we cannot return. - test_read(reporter, bufferedStream, gAbcs + bufferedStream->getPosition(), 1); - test_rewind(reporter, bufferedStream, false); + test_read(reporter, bufferedStream.get(), gAbcs + memStream->getPosition(), 1); + test_rewind(reporter, bufferedStream.get(), false); } static void test_skipping(skiatest::Reporter* reporter, size_t bufferSize) { - SkMemoryStream memStream(gAbcs, strlen(gAbcs), false); - SkAutoTUnref<SkStream> bufferedStream(SkFrontBufferedStream::Create(&memStream, bufferSize)); - test_hasLength(reporter, *bufferedStream.get(), memStream); + SkMemoryStream* memStream = new SkMemoryStream(gAbcs, strlen(gAbcs), false); + std::unique_ptr<SkStream> bufferedStream(SkFrontBufferedStream::Create(memStream, bufferSize)); + test_hasLength(reporter, *bufferedStream, *memStream); // Skip half the buffer. bufferedStream->skip(bufferSize / 2); // Rewind, then read part of the buffer, which should have been read. - test_rewind(reporter, bufferedStream, true); - test_read(reporter, bufferedStream, gAbcs, bufferSize / 4); + test_rewind(reporter, bufferedStream.get(), true); + test_read(reporter, bufferedStream.get(), gAbcs, bufferSize / 4); // Now skip beyond the buffered piece, but still within the total buffer. bufferedStream->skip(bufferSize / 2); // Test that reading will still work. - test_read(reporter, bufferedStream, gAbcs + bufferedStream->getPosition(), bufferSize / 4); + test_read(reporter, bufferedStream.get(), gAbcs + memStream->getPosition(), bufferSize / 4); - test_rewind(reporter, bufferedStream, true); - test_read(reporter, bufferedStream, gAbcs, bufferSize); + test_rewind(reporter, bufferedStream.get(), true); + test_read(reporter, bufferedStream.get(), gAbcs, bufferSize); } // A custom class whose isAtEnd behaves the way Android's stream does - since it is an adaptor to a @@ -126,7 +131,7 @@ : INHERITED(data, size, ownMemory) , fIsAtEnd(false) {} - size_t read(void* dst, size_t requested) SK_OVERRIDE { + size_t read(void* dst, size_t requested) override { size_t bytesRead = this->INHERITED::read(dst, requested); if (bytesRead < requested) { fIsAtEnd = true; @@ -134,7 +139,7 @@ return bytesRead; } - bool isAtEnd() const SK_OVERRIDE { + bool isAtEnd() const override { return fIsAtEnd; } @@ -147,18 +152,19 @@ // does not invalidate the buffer. static void test_read_beyond_buffer(skiatest::Reporter* reporter, size_t bufferSize) { // Use a stream that behaves like Android's stream. - AndroidLikeMemoryStream memStream((void*)gAbcs, bufferSize, false); + AndroidLikeMemoryStream* memStream = + new AndroidLikeMemoryStream((void*)gAbcs, bufferSize, false); // Create a buffer that matches the length of the stream. - SkAutoTUnref<SkStream> bufferedStream(SkFrontBufferedStream::Create(&memStream, bufferSize)); - test_hasLength(reporter, *bufferedStream.get(), memStream); + std::unique_ptr<SkStream> bufferedStream(SkFrontBufferedStream::Create(memStream, bufferSize)); + test_hasLength(reporter, *bufferedStream.get(), *memStream); // Attempt to read one more than the bufferSize test_read(reporter, bufferedStream.get(), gAbcs, bufferSize + 1); test_rewind(reporter, bufferedStream.get(), true); // Ensure that the initial read did not invalidate the buffer. - test_read(reporter, bufferedStream, gAbcs, bufferSize); + test_read(reporter, bufferedStream.get(), gAbcs, bufferSize); } // Dummy stream that optionally has a length and/or position. Tests that FrontBufferedStream's @@ -170,19 +176,19 @@ , fHasPosition(hasPosition) {} - virtual bool hasLength() const SK_OVERRIDE { + bool hasLength() const override { return fHasLength; } - virtual bool hasPosition() const SK_OVERRIDE { + bool hasPosition() const override { return fHasPosition; } - virtual size_t read(void*, size_t) SK_OVERRIDE { + size_t read(void*, size_t) override { return 0; } - virtual bool isAtEnd() const SK_OVERRIDE { + bool isAtEnd() const override { return true; } @@ -195,40 +201,40 @@ static void test_length_combos(skiatest::Reporter* reporter, size_t bufferSize) { for (int hasLen = 0; hasLen <= 1; hasLen++) { for (int hasPos = 0; hasPos <= 1; hasPos++) { - LengthOptionalStream stream(SkToBool(hasLen), SkToBool(hasPos)); - SkAutoTUnref<SkStream> buffered(SkFrontBufferedStream::Create(&stream, bufferSize)); - test_hasLength(reporter, *buffered.get(), stream); + LengthOptionalStream* stream = + new LengthOptionalStream(SkToBool(hasLen), SkToBool(hasPos)); + std::unique_ptr<SkStream> buffered(SkFrontBufferedStream::Create(stream, bufferSize)); + test_hasLength(reporter, *buffered.get(), *stream); } } } // Test using a stream with an initial offset. static void test_initial_offset(skiatest::Reporter* reporter, size_t bufferSize) { - SkMemoryStream memStream(gAbcs, strlen(gAbcs), false); + SkMemoryStream* memStream = new SkMemoryStream(gAbcs, strlen(gAbcs), false); // Skip a few characters into the memStream, so that bufferedStream represents an offset into // the stream it wraps. const size_t arbitraryOffset = 17; - memStream.skip(arbitraryOffset); - SkAutoTUnref<SkStream> bufferedStream(SkFrontBufferedStream::Create(&memStream, bufferSize)); + memStream->skip(arbitraryOffset); + std::unique_ptr<SkStream> bufferedStream(SkFrontBufferedStream::Create(memStream, bufferSize)); - // Since SkMemoryStream has a length and a position, bufferedStream must also. + // Since SkMemoryStream has a length, bufferedStream must also. REPORTER_ASSERT(reporter, bufferedStream->hasLength()); const size_t amountToRead = 10; const size_t bufferedLength = bufferedStream->getLength(); - size_t currentPosition = bufferedStream->getPosition(); - REPORTER_ASSERT(reporter, 0 == currentPosition); + size_t currentPosition = 0; // Read the stream in chunks. After each read, the position must match currentPosition, // which sums the amount attempted to read, unless the end of the stream has been reached. // Importantly, the end should not have been reached until currentPosition == bufferedLength. while (currentPosition < bufferedLength) { REPORTER_ASSERT(reporter, !bufferedStream->isAtEnd()); - test_read(reporter, bufferedStream, gAbcs + arbitraryOffset + currentPosition, + test_read(reporter, bufferedStream.get(), gAbcs + arbitraryOffset + currentPosition, amountToRead); currentPosition = SkTMin(currentPosition + amountToRead, bufferedLength); - REPORTER_ASSERT(reporter, bufferedStream->getPosition() == currentPosition); + REPORTER_ASSERT(reporter, memStream->getPosition() - arbitraryOffset == currentPosition); } REPORTER_ASSERT(reporter, bufferedStream->isAtEnd()); REPORTER_ASSERT(reporter, bufferedLength == currentPosition); @@ -249,3 +255,35 @@ test_buffers(reporter, 15); test_buffers(reporter, 64); } + +// Test that a FrontBufferedStream does not allow reading after the end of a stream. +// This class is a dummy SkStream which reports that it is at the end on the first +// read (simulating a failure). Then it tracks whether someone calls read() again. +class FailingStream : public SkStream { +public: + FailingStream() + : fAtEnd(false) + {} + + size_t read(void* buffer, size_t size) override { + SkASSERT(!fAtEnd); + fAtEnd = true; + return 0; + } + + bool isAtEnd() const override { + return fAtEnd; + } + +private: + bool fAtEnd; +}; + +DEF_TEST(ShortFrontBufferedStream, reporter) { + FailingStream* failingStream = new FailingStream; + std::unique_ptr<SkStreamRewindable> stream(SkFrontBufferedStream::Create(failingStream, 64)); + + // This will fail to create a codec. However, what we really want to test is that we + // won't read past the end of the stream. + std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release())); +}
diff --git a/src/third_party/skia/tests/GLInterfaceValidationTest.cpp b/src/third_party/skia/tests/GLInterfaceValidationTest.cpp deleted file mode 100755 index 797ba72..0000000 --- a/src/third_party/skia/tests/GLInterfaceValidationTest.cpp +++ /dev/null
@@ -1,39 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Test.h" - -// This is a GPU-backend specific test -#if SK_SUPPORT_GPU - -#include "GrContextFactory.h" - -DEF_GPUTEST(GLInterfaceValidation, reporter, factory) { - for (int i = 0; i <= GrContextFactory::kLastGLContextType; ++i) { - GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType)i; - // this forces the factory to make the context if it hasn't yet - factory->get(glCtxType); - SkGLContextHelper* glCtxHelper = factory->getGLContext(glCtxType); - - // We're supposed to fail the NVPR context type when we the native context that does not - // support the NVPR extension. - if (GrContextFactory::kNVPR_GLContextType == glCtxType && - factory->getGLContext(GrContextFactory::kNative_GLContextType) && - !factory->getGLContext(GrContextFactory::kNative_GLContextType)->hasExtension("GL_NV_path_rendering")) { - REPORTER_ASSERT(reporter, NULL == glCtxHelper); - continue; - } - - REPORTER_ASSERT(reporter, glCtxHelper); - if (glCtxHelper) { - const GrGLInterface* interface = glCtxHelper->gl(); - REPORTER_ASSERT(reporter, interface->validate()); - } - } -} - -#endif
diff --git a/src/third_party/skia/tests/GLProgramsTest.cpp b/src/third_party/skia/tests/GLProgramsTest.cpp index a16173b..f8cb6ef 100644 --- a/src/third_party/skia/tests/GLProgramsTest.cpp +++ b/src/third_party/skia/tests/GLProgramsTest.cpp
@@ -1,4 +1,3 @@ - /* * Copyright 2011 Google Inc. * @@ -12,384 +11,410 @@ #if SK_SUPPORT_GPU && SK_ALLOW_STATIC_GLOBAL_INITIALIZERS -#include "GrBackendProcessorFactory.h" +#include "GrAutoLocaleSetter.h" #include "GrContextFactory.h" -#include "GrOptDrawState.h" -#include "effects/GrConfigConversionEffect.h" -#include "gl/GrGLPathRendering.h" -#include "gl/GrGpuGL.h" +#include "GrContextPriv.h" +#include "GrDrawOpTest.h" +#include "GrDrawingManager.h" +#include "GrPipeline.h" +#include "GrRenderTargetContextPriv.h" +#include "GrTest.h" +#include "GrXferProcessor.h" #include "SkChecksum.h" #include "SkRandom.h" #include "Test.h" -static void get_stage_stats(const GrFragmentStage stage, bool* readsDst, - bool* readsFragPosition, bool* requiresVertexShader) { - if (stage.getFragmentProcessor()->willReadDstColor()) { - *readsDst = true; - } - if (stage.getProcessor()->willReadFragmentPosition()) { - *readsFragPosition = true; - } -} +#include "ops/GrDrawOp.h" -bool GrGLProgramDesc::setRandom(SkRandom* random, - GrGpuGL* gpu, - const GrRenderTarget* dstRenderTarget, - const GrTexture* dstCopyTexture, - const GrGeometryStage* geometryProcessor, - const GrFragmentStage* stages[], - int numColorStages, - int numCoverageStages, - int currAttribIndex, - GrGpu::DrawType drawType) { - bool isPathRendering = GrGpu::IsPathRenderingDrawType(drawType); - bool useLocalCoords = !isPathRendering && - random->nextBool() && - currAttribIndex < GrDrawState::kMaxVertexAttribCnt; +#include "effects/GrConfigConversionEffect.h" +#include "effects/GrPorterDuffXferProcessor.h" +#include "effects/GrXfermodeFragmentProcessor.h" - int numStages = numColorStages + numCoverageStages; - fKey.reset(); - - GR_STATIC_ASSERT(0 == kEffectKeyOffsetsAndLengthOffset % sizeof(uint32_t)); - - // Make room for everything up to and including the array of offsets to effect keys. - fKey.push_back_n(kEffectKeyOffsetsAndLengthOffset + 2 * sizeof(uint16_t) * (numStages + - (geometryProcessor ? 1 : 0))); - - bool dstRead = false; - bool fragPos = false; - bool vertexShader = SkToBool(geometryProcessor); - int offset = 0; - if (geometryProcessor) { - const GrGeometryStage* stage = geometryProcessor; - uint16_t* offsetAndSize = reinterpret_cast<uint16_t*>(fKey.begin() + - kEffectKeyOffsetsAndLengthOffset + - offset * 2 * sizeof(uint16_t)); - uint32_t effectKeyOffset = fKey.count(); - if (effectKeyOffset > SK_MaxU16) { - fKey.reset(); - return false; - } - GrProcessorKeyBuilder b(&fKey); - uint16_t effectKeySize; - if (!GetProcessorKey(*stage, gpu->glCaps(), useLocalCoords, &b, &effectKeySize)) { - fKey.reset(); - return false; - } - vertexShader = true; - fragPos = stage->getProcessor()->willReadFragmentPosition(); - offsetAndSize[0] = effectKeyOffset; - offsetAndSize[1] = effectKeySize; - offset++; - } - - for (int s = 0; s < numStages; ++s, ++offset) { - const GrFragmentStage* stage = stages[s]; - uint16_t* offsetAndSize = reinterpret_cast<uint16_t*>(fKey.begin() + - kEffectKeyOffsetsAndLengthOffset + - offset * 2 * sizeof(uint16_t)); - uint32_t effectKeyOffset = fKey.count(); - if (effectKeyOffset > SK_MaxU16) { - fKey.reset(); - return false; - } - GrProcessorKeyBuilder b(&fKey); - uint16_t effectKeySize; - if (!GetProcessorKey(*stages[s], gpu->glCaps(), useLocalCoords, &b, &effectKeySize)) { - fKey.reset(); - return false; - } - get_stage_stats(*stage, &dstRead, &fragPos, &vertexShader); - offsetAndSize[0] = effectKeyOffset; - offsetAndSize[1] = effectKeySize; - } - - KeyHeader* header = this->header(); - memset(header, 0, kHeaderSize); - header->fEmitsPointSize = random->nextBool(); - - header->fPositionAttributeIndex = 0; - - // if the effects have used up all off the available attributes, - // don't try to use color or coverage attributes as input - do { - header->fColorInput = static_cast<GrGLProgramDesc::ColorInput>( - random->nextULessThan(kColorInputCnt)); - } while ((GrDrawState::kMaxVertexAttribCnt <= currAttribIndex || isPathRendering) && - kAttribute_ColorInput == header->fColorInput); - header->fColorAttributeIndex = (header->fColorInput == kAttribute_ColorInput) ? - currAttribIndex++ : - -1; - - do { - header->fCoverageInput = static_cast<GrGLProgramDesc::ColorInput>( - random->nextULessThan(kColorInputCnt)); - } while ((GrDrawState::kMaxVertexAttribCnt <= currAttribIndex || isPathRendering) && - kAttribute_ColorInput == header->fCoverageInput); - header->fCoverageAttributeIndex = (header->fCoverageInput == kAttribute_ColorInput) ? - currAttribIndex++ : - -1; - bool useGS = random->nextBool(); -#if GR_GL_EXPERIMENTAL_GS - header->fExperimentalGS = gpu->caps()->geometryShaderSupport() && useGS; -#else - (void) useGS; -#endif - - header->fLocalCoordAttributeIndex = useLocalCoords ? currAttribIndex++ : -1; - - header->fColorEffectCnt = numColorStages; - header->fCoverageEffectCnt = numCoverageStages; - - if (dstRead) { - header->fDstReadKey = SkToU8(GrGLFragmentShaderBuilder::KeyForDstRead(dstCopyTexture, - gpu->glCaps())); - } else { - header->fDstReadKey = 0; - } - if (fragPos) { - header->fFragPosKey = SkToU8(GrGLFragmentShaderBuilder::KeyForFragmentPosition(dstRenderTarget, - gpu->glCaps())); - } else { - header->fFragPosKey = 0; - } - - header->fUseFragShaderOnly = isPathRendering && gpu->glPathRendering()->texturingMode() == - GrGLPathRendering::FixedFunction_TexturingMode; - header->fHasGeometryProcessor = vertexShader; - - GrOptDrawState::PrimaryOutputType primaryOutput; - GrOptDrawState::SecondaryOutputType secondaryOutput; - if (!dstRead) { - primaryOutput = GrOptDrawState::kModulate_PrimaryOutputType; - } else { - primaryOutput = static_cast<GrOptDrawState::PrimaryOutputType>( - random->nextULessThan(GrOptDrawState::kPrimaryOutputTypeCnt)); - } - - if (GrOptDrawState::kCombineWithDst_PrimaryOutputType == primaryOutput || - !gpu->caps()->dualSourceBlendingSupport()) { - secondaryOutput = GrOptDrawState::kNone_SecondaryOutputType; - } else { - secondaryOutput = static_cast<GrOptDrawState::SecondaryOutputType>( - random->nextULessThan(GrOptDrawState::kSecondaryOutputTypeCnt)); - } - - header->fPrimaryOutputType = primaryOutput; - header->fSecondaryOutputType = secondaryOutput; - - this->finalize(); - return true; -} - -// TODO clean this up, we have to do this to test geometry processors but there has got to be -// a better way. In the mean time, we actually fill out these generic vertex attribs below with -// the correct vertex attribs from the GP. We have to ensure, however, we don't try to add more -// than two attributes. -GrVertexAttrib genericVertexAttribs[] = { - { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding }, - { kVec2f_GrVertexAttribType, 0, kGeometryProcessor_GrVertexAttribBinding }, - { kVec2f_GrVertexAttribType, 0, kGeometryProcessor_GrVertexAttribBinding } -}; +#include "gl/GrGLGpu.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "glsl/GrGLSLProgramBuilder.h" /* - * convert sl type to vertexattrib type, not a complete implementation, only use for debugging + * A dummy processor which just tries to insert a massive key and verify that it can retrieve the + * whole thing correctly */ -GrVertexAttribType convert_sltype_to_attribtype(GrSLType type) { - switch (type) { - case kFloat_GrSLType: - return kFloat_GrVertexAttribType; - case kVec2f_GrSLType: - return kVec2f_GrVertexAttribType; - case kVec3f_GrSLType: - return kVec3f_GrVertexAttribType; - case kVec4f_GrSLType: - return kVec4f_GrVertexAttribType; - default: - SkFAIL("Type isn't convertible"); - return kFloat_GrVertexAttribType; +static const uint32_t kMaxKeySize = 1024; + +class GLBigKeyProcessor : public GrGLSLFragmentProcessor { +public: + void emitCode(EmitArgs& args) override { + // pass through + GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; + if (args.fInputColor) { + fragBuilder->codeAppendf("%s = %s;\n", args.fOutputColor, args.fInputColor); + } else { + fragBuilder->codeAppendf("%s = vec4(1.0);\n", args.fOutputColor); + } + } + + static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder* b) { + for (uint32_t i = 0; i < kMaxKeySize; i++) { + b->add32(i); + } + } + +private: + typedef GrGLSLFragmentProcessor INHERITED; +}; + +class BigKeyProcessor : public GrFragmentProcessor { +public: + static sk_sp<GrFragmentProcessor> Make() { + return sk_sp<GrFragmentProcessor>(new BigKeyProcessor); + } + + const char* name() const override { return "Big Ole Key"; } + + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { + return new GLBigKeyProcessor; + } + +private: + BigKeyProcessor() : INHERITED(kNone_OptimizationFlags) { this->initClassID<BigKeyProcessor>(); } + virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps, + GrProcessorKeyBuilder* b) const override { + GLBigKeyProcessor::GenKey(*this, caps, b); + } + bool onIsEqual(const GrFragmentProcessor&) const override { return true; } + + GR_DECLARE_FRAGMENT_PROCESSOR_TEST + + typedef GrFragmentProcessor INHERITED; +}; + +GR_DEFINE_FRAGMENT_PROCESSOR_TEST(BigKeyProcessor); + +#if GR_TEST_UTILS +sk_sp<GrFragmentProcessor> BigKeyProcessor::TestCreate(GrProcessorTestData*) { + return BigKeyProcessor::Make(); +} +#endif + +////////////////////////////////////////////////////////////////////////////// + +class BlockInputFragmentProcessor : public GrFragmentProcessor { +public: + static sk_sp<GrFragmentProcessor> Make(sk_sp<GrFragmentProcessor> fp) { + return sk_sp<GrFragmentProcessor>(new BlockInputFragmentProcessor(fp)); + } + + const char* name() const override { return "Block Input"; } + + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLFP; } + +private: + class GLFP : public GrGLSLFragmentProcessor { + public: + void emitCode(EmitArgs& args) override { + this->emitChild(0, args); + } + + private: + typedef GrGLSLFragmentProcessor INHERITED; + }; + + BlockInputFragmentProcessor(sk_sp<GrFragmentProcessor> child) + : INHERITED(kNone_OptimizationFlags) { + this->initClassID<BlockInputFragmentProcessor>(); + this->registerChildProcessor(std::move(child)); + } + + void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {} + + bool onIsEqual(const GrFragmentProcessor&) const override { return true; } + + typedef GrFragmentProcessor INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +/* + * Begin test code + */ +static const int kRenderTargetHeight = 1; +static const int kRenderTargetWidth = 1; + +static sk_sp<GrRenderTargetContext> random_render_target_context(GrContext* context, + SkRandom* random, + const GrCaps* caps) { + GrSurfaceOrigin origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin + : kBottomLeft_GrSurfaceOrigin; + int sampleCnt = random->nextBool() ? caps->getSampleCount(4, kRGBA_8888_GrPixelConfig) : 0; + + sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext( + SkBackingFit::kExact, + kRenderTargetWidth, + kRenderTargetHeight, + kRGBA_8888_GrPixelConfig, + nullptr, + sampleCnt, + origin)); + return renderTargetContext; +} + +#if GR_TEST_UTILS +static void set_random_xpf(GrPaint* paint, GrProcessorTestData* d) { + paint->setXPFactory(GrXPFactoryTestFactory::Get(d)); +} + +static sk_sp<GrFragmentProcessor> create_random_proc_tree(GrProcessorTestData* d, + int minLevels, int maxLevels) { + SkASSERT(1 <= minLevels); + SkASSERT(minLevels <= maxLevels); + + // Return a leaf node if maxLevels is 1 or if we randomly chose to terminate. + // If returning a leaf node, make sure that it doesn't have children (e.g. another + // GrComposeEffect) + const float terminateProbability = 0.3f; + if (1 == minLevels) { + bool terminate = (1 == maxLevels) || (d->fRandom->nextF() < terminateProbability); + if (terminate) { + sk_sp<GrFragmentProcessor> fp; + while (true) { + fp = GrProcessorTestFactory<GrFragmentProcessor>::Make(d); + SkASSERT(fp); + if (0 == fp->numChildProcessors()) { + break; + } + } + return fp; + } + } + // If we didn't terminate, choose either the left or right subtree to fulfill + // the minLevels requirement of this tree; the other child can have as few levels as it wants. + // Also choose a random xfer mode. + if (minLevels > 1) { + --minLevels; + } + sk_sp<GrFragmentProcessor> minLevelsChild(create_random_proc_tree(d, minLevels, maxLevels - 1)); + sk_sp<GrFragmentProcessor> otherChild(create_random_proc_tree(d, 1, maxLevels - 1)); + SkBlendMode mode = static_cast<SkBlendMode>(d->fRandom->nextRangeU(0, + (int)SkBlendMode::kLastMode)); + sk_sp<GrFragmentProcessor> fp; + if (d->fRandom->nextF() < 0.5f) { + fp = GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(minLevelsChild), + std::move(otherChild), mode); + SkASSERT(fp); + } else { + fp = GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(otherChild), + std::move(minLevelsChild), mode); + SkASSERT(fp); + } + return fp; +} + +static void set_random_color_coverage_stages(GrPaint* paint, + GrProcessorTestData* d, + int maxStages, + int maxTreeLevels) { + // Randomly choose to either create a linear pipeline of procs or create one proc tree + const float procTreeProbability = 0.5f; + if (d->fRandom->nextF() < procTreeProbability) { + sk_sp<GrFragmentProcessor> fp(create_random_proc_tree(d, 2, maxTreeLevels)); + if (fp) { + paint->addColorFragmentProcessor(std::move(fp)); + } + } else { + int numProcs = d->fRandom->nextULessThan(maxStages + 1); + int numColorProcs = d->fRandom->nextULessThan(numProcs + 1); + + for (int s = 0; s < numProcs;) { + sk_sp<GrFragmentProcessor> fp(GrProcessorTestFactory<GrFragmentProcessor>::Make(d)); + SkASSERT(fp); + + // finally add the stage to the correct pipeline in the drawstate + if (s < numColorProcs) { + paint->addColorFragmentProcessor(std::move(fp)); + } else { + paint->addCoverageFragmentProcessor(std::move(fp)); + } + ++s; + } } } -// TODO end test hack +static void set_random_state(GrPaint* paint, SkRandom* random) { + if (random->nextBool()) { + paint->setDisableOutputConversionToSRGB(true); + } + if (random->nextBool()) { + paint->setAllowSRGBInputs(true); + } +} -bool GrGpuGL::programUnitTest(int maxStages) { +#endif - GrTextureDesc dummyDesc; - dummyDesc.fFlags = kRenderTarget_GrTextureFlagBit; - dummyDesc.fConfig = kSkia8888_GrPixelConfig; +#if !GR_TEST_UTILS +bool GrDrawingManager::ProgramUnitTest(GrContext*, int) { return true; } +#else +bool GrDrawingManager::ProgramUnitTest(GrContext* context, int maxStages, int maxLevels) { + GrDrawingManager* drawingManager = context->contextPriv().drawingManager(); + + sk_sp<GrTextureProxy> proxies[2]; + + // setup dummy textures + GrSurfaceDesc dummyDesc; + dummyDesc.fFlags = kRenderTarget_GrSurfaceFlag; + dummyDesc.fOrigin = kBottomLeft_GrSurfaceOrigin; + dummyDesc.fConfig = kRGBA_8888_GrPixelConfig; dummyDesc.fWidth = 34; dummyDesc.fHeight = 18; - SkAutoTUnref<GrTexture> dummyTexture1(this->createTexture(dummyDesc, NULL, 0)); - dummyDesc.fFlags = kNone_GrTextureFlags; + proxies[0] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), + dummyDesc, SkBudgeted::kNo, nullptr, 0); + dummyDesc.fFlags = kNone_GrSurfaceFlags; + dummyDesc.fOrigin = kTopLeft_GrSurfaceOrigin; dummyDesc.fConfig = kAlpha_8_GrPixelConfig; dummyDesc.fWidth = 16; dummyDesc.fHeight = 22; - SkAutoTUnref<GrTexture> dummyTexture2(this->createTexture(dummyDesc, NULL, 0)); + proxies[1] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), + dummyDesc, SkBudgeted::kNo, nullptr, 0); - if (!dummyTexture1 || ! dummyTexture2) { + if (!proxies[0] || !proxies[1]) { + SkDebugf("Could not allocate dummy textures"); return false; } - static const int NUM_TESTS = 512; + // dummy scissor state + GrScissorState scissor; SkRandom random; - for (int t = 0; t < NUM_TESTS; ++t) { - -#if 0 - GrPrintf("\nTest Program %d\n-------------\n", t); - static const int stop = -1; - if (t == stop) { - int breakpointhere = 9; - } -#endif - - GrGLProgramDesc pdesc; - - int currAttribIndex = 1; // we need to always leave room for position - int currTextureCoordSet = 0; - GrTexture* dummyTextures[] = {dummyTexture1.get(), dummyTexture2.get()}; - - int numStages = random.nextULessThan(maxStages + 1); - int numColorStages = random.nextULessThan(numStages + 1); - int numCoverageStages = numStages - numColorStages; - - SkAutoSTMalloc<8, const GrFragmentStage*> stages(numStages); - - bool usePathRendering = this->glCaps().pathRenderingSupport() && random.nextBool(); - - GrGpu::DrawType drawType = usePathRendering ? GrGpu::kDrawPath_DrawType : - GrGpu::kDrawPoints_DrawType; - - SkAutoTDelete<GrGeometryStage> geometryProcessor; - bool hasGeometryProcessor = usePathRendering ? false : random.nextBool(); - if (hasGeometryProcessor) { - while (true) { - SkAutoTUnref<const GrGeometryProcessor> effect( - GrProcessorTestFactory<GrGeometryProcessor>::CreateStage(&random, this->getContext(), *this->caps(), - dummyTextures)); - SkASSERT(effect); - // Only geometryProcessor can use vertex shader - GrGeometryStage* stage = SkNEW_ARGS(GrGeometryStage, (effect.get())); - geometryProcessor.reset(stage); - - // we have to set dummy vertex attribs - const GrGeometryProcessor::VertexAttribArray& v = effect->getVertexAttribs(); - int numVertexAttribs = v.count(); - - SkASSERT(GrGeometryProcessor::kMaxVertexAttribs == 2 && - GrGeometryProcessor::kMaxVertexAttribs >= numVertexAttribs); - size_t runningStride = GrVertexAttribTypeSize(genericVertexAttribs[0].fType); - for (int i = 0; i < numVertexAttribs; i++) { - genericVertexAttribs[i + 1].fOffset = runningStride; - genericVertexAttribs[i + 1].fType = - convert_sltype_to_attribtype(v[i].getType()); - runningStride += GrVertexAttribTypeSize(genericVertexAttribs[i + 1].fType); - } - - // update the vertex attributes with the ds - GrDrawState* ds = this->drawState(); - ds->setVertexAttribs<genericVertexAttribs>(numVertexAttribs + 1, runningStride); - currAttribIndex = numVertexAttribs + 1; - break; - } - } - for (int s = 0; s < numStages;) { - SkAutoTUnref<const GrFragmentProcessor> effect( - GrProcessorTestFactory<GrFragmentProcessor>::CreateStage( - &random, - this->getContext(), - *this->caps(), - dummyTextures)); - SkASSERT(effect); - - // If adding this effect would exceed the max texture coord set count then generate a - // new random effect. - if (usePathRendering && this->glPathRendering()->texturingMode() == - GrGLPathRendering::FixedFunction_TexturingMode) {; - int numTransforms = effect->numTransforms(); - if (currTextureCoordSet + numTransforms > this->glCaps().maxFixedFunctionTextureCoords()) { - continue; - } - currTextureCoordSet += numTransforms; - } - GrFragmentStage* stage = SkNEW_ARGS(GrFragmentStage, (effect.get())); - - stages[s] = stage; - ++s; - } - const GrTexture* dstTexture = random.nextBool() ? dummyTextures[0] : dummyTextures[1]; - if (!pdesc.setRandom(&random, - this, - dummyTextures[0]->asRenderTarget(), - dstTexture, - geometryProcessor.get(), - stages.get(), - numColorStages, - numCoverageStages, - currAttribIndex, - drawType)) { + static const int NUM_TESTS = 1024; + for (int t = 0; t < NUM_TESTS; t++) { + // setup random render target(can fail) + sk_sp<GrRenderTargetContext> renderTargetContext(random_render_target_context( + context, &random, context->caps())); + if (!renderTargetContext) { + SkDebugf("Could not allocate renderTargetContext"); return false; } - SkAutoTUnref<GrGLProgram> program(GrGLProgram::Create(this, - pdesc, - geometryProcessor.get(), - stages, - stages + numColorStages)); - for (int s = 0; s < numStages; ++s) { - SkDELETE(stages[s]); - } - if (NULL == program.get()) { - return false; - } - - // We have to reset the drawstate because we might have added a gp - this->drawState()->reset(); + GrPaint paint; + GrProcessorTestData ptd(&random, context, renderTargetContext.get(), proxies); + set_random_color_coverage_stages(&paint, &ptd, maxStages, maxLevels); + set_random_xpf(&paint, &ptd); + set_random_state(&paint, &random); + GrDrawRandomOp(&random, renderTargetContext.get(), std::move(paint)); } + // Flush everything, test passes if flush is successful(ie, no asserts are hit, no crashes) + drawingManager->flush(nullptr); + + // Validate that GrFPs work correctly without an input. + sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext( + SkBackingFit::kExact, + kRenderTargetWidth, + kRenderTargetHeight, + kRGBA_8888_GrPixelConfig, + nullptr)); + if (!renderTargetContext) { + SkDebugf("Could not allocate a renderTargetContext"); + return false; + } + + int fpFactoryCnt = GrProcessorTestFactory<GrFragmentProcessor>::Count(); + for (int i = 0; i < fpFactoryCnt; ++i) { + // Since FP factories internally randomize, call each 10 times. + for (int j = 0; j < 10; ++j) { + GrProcessorTestData ptd(&random, context, renderTargetContext.get(), proxies); + + GrPaint paint; + paint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc)); + sk_sp<GrFragmentProcessor> fp( + GrProcessorTestFactory<GrFragmentProcessor>::MakeIdx(i, &ptd)); + sk_sp<GrFragmentProcessor> blockFP( + BlockInputFragmentProcessor::Make(std::move(fp))); + paint.addColorFragmentProcessor(std::move(blockFP)); + GrDrawRandomOp(&random, renderTargetContext.get(), std::move(paint)); + drawingManager->flush(nullptr); + } + } + return true; } - -DEF_GPUTEST(GLPrograms, reporter, factory) { - for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) { - GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(type)); - if (context) { - GrGpuGL* gpu = static_cast<GrGpuGL*>(context->getGpu()); - int maxStages = 6; -#if SK_ANGLE - // Some long shaders run out of temporary registers in the D3D compiler on ANGLE. - if (type == GrContextFactory::kANGLE_GLContextType) { - maxStages = 3; - } #endif - REPORTER_ASSERT(reporter, gpu->programUnitTest(maxStages)); + +static int get_glprograms_max_stages(const sk_gpu_test::ContextInfo& ctxInfo) { + GrContext* context = ctxInfo.grContext(); + GrGLGpu* gpu = static_cast<GrGLGpu*>(context->getGpu()); + int maxStages = 6; + if (kGLES_GrGLStandard == gpu->glStandard()) { + // We've had issues with driver crashes and HW limits being exceeded with many effects on + // Android devices. We have passes on ARM devices with the default number of stages. + // TODO When we run ES 3.00 GLSL in more places, test again +#ifdef SK_BUILD_FOR_ANDROID + if (kARM_GrGLVendor != gpu->ctxInfo().vendor()) { + maxStages = 1; } +#endif + // On iOS we can exceed the maximum number of varyings. http://skbug.com/6627. +#ifdef SK_BUILD_FOR_IOS + maxStages = 3; +#endif } + if (ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_D3D9_ES2_ContextType || + ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_D3D11_ES2_ContextType) { + // On Angle D3D we will hit a limit of out variables if we use too many stages. + maxStages = 3; + } + return maxStages; } -// This is evil evil evil. The linker may throw away whole translation units as dead code if it -// thinks none of the functions are called. It will do this even if there are static initializers -// in the unit that could pass pointers to functions from the unit out to other translation units! -// We force some of the effects that would otherwise be discarded to link here. +static int get_glprograms_max_levels(const sk_gpu_test::ContextInfo& ctxInfo) { + // A full tree with 5 levels (31 nodes) may cause a program that exceeds shader limits + // (e.g. uniform or varying limits); maxTreeLevels should be a number from 1 to 4 inclusive. + int maxTreeLevels = 4; + // On iOS we can exceed the maximum number of varyings. http://skbug.com/6627. +#ifdef SK_BUILD_FOR_IOS + maxTreeLevels = 2; +#endif + if (ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_D3D9_ES2_ContextType || + ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_D3D11_ES2_ContextType) { + // On Angle D3D we will hit a limit of out variables if we use too many stages. + maxTreeLevels = 2; + } + return maxTreeLevels; +} -#include "SkAlphaThresholdFilter.h" -#include "SkColorMatrixFilter.h" -#include "SkLightingImageFilter.h" -#include "SkMagnifierImageFilter.h" +static void test_glprograms(skiatest::Reporter* reporter, const sk_gpu_test::ContextInfo& ctxInfo) { + int maxStages = get_glprograms_max_stages(ctxInfo); + if (maxStages == 0) { + return; + } + int maxLevels = get_glprograms_max_levels(ctxInfo); + if (maxLevels == 0) { + return; + } -void forceLinking(); + // Disable this test on ANGLE D3D9 configurations. We keep hitting a D3D compiler bug. + // See skbug.com/6842 and anglebug.com/2098 + if (sk_gpu_test::GrContextFactory::kANGLE_D3D9_ES2_ContextType == ctxInfo.type()) { + return; + } -void forceLinking() { - SkLightingImageFilter::CreateDistantLitDiffuse(SkPoint3(0,0,0), 0, 0, 0); - SkAlphaThresholdFilter::Create(SkRegion(), .5f, .5f); - SkAutoTUnref<SkImageFilter> mag(SkMagnifierImageFilter::Create( - SkRect::MakeWH(SK_Scalar1, SK_Scalar1), SK_Scalar1)); - GrConfigConversionEffect::Create(NULL, - false, - GrConfigConversionEffect::kNone_PMConversion, - SkMatrix::I()); - SkScalar matrix[20]; - SkAutoTUnref<SkColorMatrixFilter> cmf(SkColorMatrixFilter::Create(matrix)); + REPORTER_ASSERT(reporter, GrDrawingManager::ProgramUnitTest(ctxInfo.grContext(), maxStages, + maxLevels)); +} + +DEF_GPUTEST(GLPrograms, reporter, /*factory*/) { + // Set a locale that would cause shader compilation to fail because of , as decimal separator. + // skbug 3330 +#ifdef SK_BUILD_FOR_WIN + GrAutoLocaleSetter als("sv-SE"); +#else + GrAutoLocaleSetter als("sv_SE.UTF-8"); +#endif + + // We suppress prints to avoid spew + GrContextOptions opts; + opts.fSuppressPrints = true; + sk_gpu_test::GrContextFactory debugFactory(opts); + skiatest::RunWithGPUTestContexts(test_glprograms, &skiatest::IsRenderingGLContextType, reporter, + &debugFactory); } #endif
diff --git a/src/third_party/skia/tests/GeometryTest.cpp b/src/third_party/skia/tests/GeometryTest.cpp index 5151b70..4d955b5 100644 --- a/src/third_party/skia/tests/GeometryTest.cpp +++ b/src/third_party/skia/tests/GeometryTest.cpp
@@ -7,6 +7,8 @@ #include "SkGeometry.h" #include "Test.h" +#include "SkRandom.h" +#include <array> static bool nearly_equal(const SkPoint& a, const SkPoint& b) { return SkScalarNearlyEqual(a.fX, b.fX) && SkScalarNearlyEqual(a.fY, b.fY); @@ -33,6 +35,195 @@ } } +static void check_pairs(skiatest::Reporter* reporter, int index, SkScalar t, const char name[], + SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1) { + bool eq = SkScalarNearlyEqual(x0, x1) && SkScalarNearlyEqual(y0, y1); + if (!eq) { + SkDebugf("%s [%d %g] p0 [%10.8f %10.8f] p1 [%10.8f %10.8f]\n", + name, index, t, x0, y0, x1, y1); + REPORTER_ASSERT(reporter, eq); + } +} + +static void test_evalquadat(skiatest::Reporter* reporter) { + SkRandom rand; + for (int i = 0; i < 1000; ++i) { + SkPoint pts[3]; + for (int j = 0; j < 3; ++j) { + pts[j].set(rand.nextSScalar1() * 100, rand.nextSScalar1() * 100); + } + const SkScalar dt = SK_Scalar1 / 128; + SkScalar t = dt; + for (int j = 1; j < 128; ++j) { + SkPoint r0; + SkEvalQuadAt(pts, t, &r0); + SkPoint r1 = SkEvalQuadAt(pts, t); + check_pairs(reporter, i, t, "quad-pos", r0.fX, r0.fY, r1.fX, r1.fY); + + SkVector v0; + SkEvalQuadAt(pts, t, nullptr, &v0); + SkVector v1 = SkEvalQuadTangentAt(pts, t); + check_pairs(reporter, i, t, "quad-tan", v0.fX, v0.fY, v1.fX, v1.fY); + + t += dt; + } + } +} + +static void test_conic_eval_pos(skiatest::Reporter* reporter, const SkConic& conic, SkScalar t) { + SkPoint p0, p1; + conic.evalAt(t, &p0, nullptr); + p1 = conic.evalAt(t); + check_pairs(reporter, 0, t, "conic-pos", p0.fX, p0.fY, p1.fX, p1.fY); +} + +static void test_conic_eval_tan(skiatest::Reporter* reporter, const SkConic& conic, SkScalar t) { + SkVector v0, v1; + conic.evalAt(t, nullptr, &v0); + v1 = conic.evalTangentAt(t); + check_pairs(reporter, 0, t, "conic-tan", v0.fX, v0.fY, v1.fX, v1.fY); +} + +static void test_conic(skiatest::Reporter* reporter) { + SkRandom rand; + for (int i = 0; i < 1000; ++i) { + SkPoint pts[3]; + for (int j = 0; j < 3; ++j) { + pts[j].set(rand.nextSScalar1() * 100, rand.nextSScalar1() * 100); + } + for (int k = 0; k < 10; ++k) { + SkScalar w = rand.nextUScalar1() * 2; + SkConic conic(pts, w); + + const SkScalar dt = SK_Scalar1 / 128; + SkScalar t = dt; + for (int j = 1; j < 128; ++j) { + test_conic_eval_pos(reporter, conic, t); + test_conic_eval_tan(reporter, conic, t); + t += dt; + } + } + } +} + +static void test_quad_tangents(skiatest::Reporter* reporter) { + SkPoint pts[] = { + {10, 20}, {10, 20}, {20, 30}, + {10, 20}, {15, 25}, {20, 30}, + {10, 20}, {20, 30}, {20, 30}, + }; + int count = (int) SK_ARRAY_COUNT(pts) / 3; + for (int index = 0; index < count; ++index) { + SkConic conic(&pts[index * 3], 0.707f); + SkVector start = SkEvalQuadTangentAt(&pts[index * 3], 0); + SkVector mid = SkEvalQuadTangentAt(&pts[index * 3], .5f); + SkVector end = SkEvalQuadTangentAt(&pts[index * 3], 1); + REPORTER_ASSERT(reporter, start.fX && start.fY); + REPORTER_ASSERT(reporter, mid.fX && mid.fY); + REPORTER_ASSERT(reporter, end.fX && end.fY); + REPORTER_ASSERT(reporter, SkScalarNearlyZero(start.cross(mid))); + REPORTER_ASSERT(reporter, SkScalarNearlyZero(mid.cross(end))); + } +} + +static void test_conic_tangents(skiatest::Reporter* reporter) { + SkPoint pts[] = { + { 10, 20}, {10, 20}, {20, 30}, + { 10, 20}, {15, 25}, {20, 30}, + { 10, 20}, {20, 30}, {20, 30} + }; + int count = (int) SK_ARRAY_COUNT(pts) / 3; + for (int index = 0; index < count; ++index) { + SkConic conic(&pts[index * 3], 0.707f); + SkVector start = conic.evalTangentAt(0); + SkVector mid = conic.evalTangentAt(.5f); + SkVector end = conic.evalTangentAt(1); + REPORTER_ASSERT(reporter, start.fX && start.fY); + REPORTER_ASSERT(reporter, mid.fX && mid.fY); + REPORTER_ASSERT(reporter, end.fX && end.fY); + REPORTER_ASSERT(reporter, SkScalarNearlyZero(start.cross(mid))); + REPORTER_ASSERT(reporter, SkScalarNearlyZero(mid.cross(end))); + } +} + +static void test_this_conic_to_quad(skiatest::Reporter* r, const SkPoint pts[3], SkScalar w) { + SkAutoConicToQuads quadder; + const SkPoint* qpts = quadder.computeQuads(pts, w, 0.25); + const int qcount = quadder.countQuads(); + const int pcount = qcount * 2 + 1; + + REPORTER_ASSERT(r, SkPointsAreFinite(qpts, pcount)); +} + +/** + * We need to ensure that when a conic is approximated by quads, that we always return finite + * values in the quads. + * + * Inspired by crbug_627414 + */ +static void test_conic_to_quads(skiatest::Reporter* reporter) { + const SkPoint triples[] = { + { 0, 0 }, { 1, 0 }, { 1, 1 }, + { 0, 0 }, { 3.58732e-43f, 2.72084f }, { 3.00392f, 3.00392f }, + { 0, 0 }, { 100000, 0 }, { 100000, 100000 }, + { 0, 0 }, { 1e30f, 0 }, { 1e30f, 1e30f }, + }; + const int N = sizeof(triples) / sizeof(SkPoint); + + for (int i = 0; i < N; i += 3) { + const SkPoint* pts = &triples[i]; + + SkRect bounds; + bounds.set(pts, 3); + + SkScalar w = 1e30f; + do { + w *= 2; + test_this_conic_to_quad(reporter, pts, w); + } while (SkScalarIsFinite(w)); + test_this_conic_to_quad(reporter, pts, SK_ScalarNaN); + } +} + +static void test_cubic_tangents(skiatest::Reporter* reporter) { + SkPoint pts[] = { + { 10, 20}, {10, 20}, {20, 30}, {30, 40}, + { 10, 20}, {15, 25}, {20, 30}, {30, 40}, + { 10, 20}, {20, 30}, {30, 40}, {30, 40}, + }; + int count = (int) SK_ARRAY_COUNT(pts) / 4; + for (int index = 0; index < count; ++index) { + SkConic conic(&pts[index * 3], 0.707f); + SkVector start, mid, end; + SkEvalCubicAt(&pts[index * 4], 0, nullptr, &start, nullptr); + SkEvalCubicAt(&pts[index * 4], .5f, nullptr, &mid, nullptr); + SkEvalCubicAt(&pts[index * 4], 1, nullptr, &end, nullptr); + REPORTER_ASSERT(reporter, start.fX && start.fY); + REPORTER_ASSERT(reporter, mid.fX && mid.fY); + REPORTER_ASSERT(reporter, end.fX && end.fY); + REPORTER_ASSERT(reporter, SkScalarNearlyZero(start.cross(mid))); + REPORTER_ASSERT(reporter, SkScalarNearlyZero(mid.cross(end))); + } +} + +static void check_cubic_type(skiatest::Reporter* reporter, + const std::array<SkPoint, 4>& bezierPoints, SkCubicType expectedType) { + SkCubicType actualType = SkClassifyCubic(bezierPoints.data()); + REPORTER_ASSERT(reporter, actualType == expectedType); +} + +static void test_classify_cubic(skiatest::Reporter* reporter) { + check_cubic_type(reporter, {{{149.325f, 107.705f}, {149.325f, 103.783f}, + {151.638f, 100.127f}, {156.263f, 96.736f}}}, + SkCubicType::kQuadratic); + check_cubic_type(reporter, {{{225.694f, 223.15f}, {209.831f, 224.837f}, + {195.994f, 230.237f}, {184.181f, 239.35f}}}, + SkCubicType::kQuadratic); + check_cubic_type(reporter, {{{4.873f, 5.581f}, {5.083f, 5.2783f}, + {5.182f, 4.8593f}, {5.177f, 4.3242f}}}, + SkCubicType::kSerpentine); +} + DEF_TEST(Geometry, reporter) { SkPoint pts[3], dst[5]; @@ -44,18 +235,22 @@ REPORTER_ASSERT(reporter, count == 1 || count == 2); pts[0].set(0, 0); - pts[1].set(SkIntToScalar(3), 0); - pts[2].set(SkIntToScalar(3), SkIntToScalar(3)); + pts[1].set(3, 0); + pts[2].set(3, 3); SkConvertQuadToCubic(pts, dst); const SkPoint cubic[] = { - { 0, 0, }, - { SkIntToScalar(2), 0, }, - { SkIntToScalar(3), SkIntToScalar(1), }, - { SkIntToScalar(3), SkIntToScalar(3) }, + { 0, 0, }, { 2, 0, }, { 3, 1, }, { 3, 3 }, }; for (int i = 0; i < 4; ++i) { REPORTER_ASSERT(reporter, nearly_equal(cubic[i], dst[i])); } testChopCubic(reporter); + test_evalquadat(reporter); + test_conic(reporter); + test_cubic_tangents(reporter); + test_quad_tangents(reporter); + test_conic_tangents(reporter); + test_conic_to_quads(reporter); + test_classify_cubic(reporter); }
diff --git a/src/third_party/skia/tests/GifTest.cpp b/src/third_party/skia/tests/GifTest.cpp index e2e0384..5749df1 100644 --- a/src/third_party/skia/tests/GifTest.cpp +++ b/src/third_party/skia/tests/GifTest.cpp
@@ -5,22 +5,16 @@ * found in the LICENSE file. */ -// This tests out GIF decoder (SkImageDecoder_libgif.cpp) -// It is not used on these platforms: -#if (!defined(SK_BUILD_FOR_WIN32)) && \ - (!defined(SK_BUILD_FOR_IOS)) && \ - (!defined(SK_BUILD_FOR_MAC)) - +#include "CodecPriv.h" +#include "Resources.h" +#include "SkAndroidCodec.h" #include "SkBitmap.h" #include "SkData.h" -#include "SkForceLinking.h" #include "SkImage.h" -#include "SkImageDecoder.h" #include "SkStream.h" +#include "SkTypes.h" #include "Test.h" -__SK_FORCE_IMAGE_DECODER_LINKING; - static unsigned char gGIFData[] = { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x03, 0x00, 0x03, 0x00, 0xe3, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, @@ -32,9 +26,18 @@ }; static unsigned char gGIFDataNoColormap[] = { - 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x21, 0xf9, 0x04, 0x01, 0x0a, 0x00, 0x01, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x4c, 0x01, 0x00, 0x3b + // Header + 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, + // Screen descriptor + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + // Graphics control extension + 0x21, 0xf9, 0x04, 0x01, 0x0a, 0x00, 0x01, 0x00, + // Image descriptor + 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + // Image data + 0x02, 0x02, 0x4c, 0x01, 0x00, + // Trailer + 0x3b }; static unsigned char gInterlacedGIF[] = { @@ -52,8 +55,7 @@ void* data, size_t size) { SkBitmap bm; - bool imageDecodeSuccess = SkImageDecoder::DecodeMemory( - data, size, &bm); + bool imageDecodeSuccess = decode_memory(data, size, &bm); REPORTER_ASSERT(r, imageDecodeSuccess); REPORTER_ASSERT(r, bm.width() == 1); REPORTER_ASSERT(r, bm.height() == 1); @@ -64,8 +66,7 @@ } static void test_gif_data(skiatest::Reporter* r, void* data, size_t size) { SkBitmap bm; - bool imageDecodeSuccess = SkImageDecoder::DecodeMemory( - data, size, &bm); + bool imageDecodeSuccess = decode_memory(data, size, &bm); REPORTER_ASSERT(r, imageDecodeSuccess); REPORTER_ASSERT(r, bm.width() == 3); REPORTER_ASSERT(r, bm.height() == 3); @@ -82,12 +83,20 @@ REPORTER_ASSERT(r, bm.getColor(2, 2) == 0xff0000ff); } } +static void test_gif_data_dims(skiatest::Reporter* r, void* data, size_t size, int width, + int height) { + SkBitmap bm; + bool imageDecodeSuccess = decode_memory(data, size, &bm); + REPORTER_ASSERT(r, imageDecodeSuccess); + REPORTER_ASSERT(r, bm.width() == width); + REPORTER_ASSERT(r, bm.height() == height); + REPORTER_ASSERT(r, !(bm.empty())); +} static void test_interlaced_gif_data(skiatest::Reporter* r, void* data, size_t size) { SkBitmap bm; - bool imageDecodeSuccess = SkImageDecoder::DecodeMemory( - data, size, &bm); + bool imageDecodeSuccess = decode_memory(data, size, &bm); REPORTER_ASSERT(r, imageDecodeSuccess); REPORTER_ASSERT(r, bm.width() == 9); REPORTER_ASSERT(r, bm.height() == 9); @@ -119,8 +128,7 @@ void* data, size_t size) { SkBitmap bm; - bool imageDecodeSuccess = SkImageDecoder::DecodeMemory( - data, size, &bm); + bool imageDecodeSuccess = decode_memory(data, size, &bm); REPORTER_ASSERT(r, imageDecodeSuccess); REPORTER_ASSERT(r, bm.width() == 3); REPORTER_ASSERT(r, bm.height() == 3); @@ -136,7 +144,7 @@ } /** - This test will test the ability of the SkImageDecoder to deal with + This test will test the ability of the SkCodec to deal with GIF files which have been mangled somehow. We want to display as much of the GIF as possible. */ @@ -148,10 +156,6 @@ unsigned char badData[sizeof(gGIFData)]; - /* If you set the environment variable - skia_images_gif_suppressDecoderWarnings to 'false', you will - see warnings on stderr. This is a feature. */ - memcpy(badData, gGIFData, sizeof(gGIFData)); badData[6] = 0x01; // image too wide test_gif_data(reporter, static_cast<void *>(badData), sizeof(gGIFData)); @@ -164,29 +168,50 @@ memcpy(badData, gGIFData, sizeof(gGIFData)); badData[62] = 0x01; // image shifted right - test_gif_data(reporter, static_cast<void *>(badData), sizeof(gGIFData)); - // "libgif warning [shifting image left to fit]" + test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 4, 3); memcpy(badData, gGIFData, sizeof(gGIFData)); badData[64] = 0x01; // image shifted down - test_gif_data(reporter, static_cast<void *>(badData), sizeof(gGIFData)); - // "libgif warning [shifting image up to fit]" + test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 3, 4); memcpy(badData, gGIFData, sizeof(gGIFData)); - badData[62] = 0xff; // image shifted left - badData[63] = 0xff; // 2's complement -1 short - test_gif_data(reporter, static_cast<void *>(badData), sizeof(gGIFData)); - // "libgif warning [shifting image left to fit]" + badData[62] = 0xff; // image shifted right + badData[63] = 0xff; + test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 3 + 0xFFFF, 3); memcpy(badData, gGIFData, sizeof(gGIFData)); - badData[64] = 0xff; // image shifted up - badData[65] = 0xff; // 2's complement -1 short - test_gif_data(reporter, static_cast<void *>(badData), sizeof(gGIFData)); - // "libgif warning [shifting image up to fit]" + badData[64] = 0xff; // image shifted down + badData[65] = 0xff; + test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 3, 3 + 0xFFFF); test_gif_data_no_colormap(reporter, static_cast<void *>(gGIFDataNoColormap), sizeof(gGIFDataNoColormap)); - // "libgif warning [missing colormap]" + + // Since there is no color map, we do not even need to parse the image data + // to know that we should draw transparent. Truncate the file before the + // data. This should still succeed. + test_gif_data_no_colormap(reporter, static_cast<void *>(gGIFDataNoColormap), 31); + + // Likewise, incremental decoding should succeed here. + { + sk_sp<SkData> data = SkData::MakeWithoutCopy(gGIFDataNoColormap, 31); + std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data)); + REPORTER_ASSERT(reporter, codec); + if (codec) { + auto info = codec->getInfo().makeColorType(kN32_SkColorType); + SkBitmap bm; + bm.allocPixels(info); + REPORTER_ASSERT(reporter, SkCodec::kSuccess == codec->startIncrementalDecode( + info, bm.getPixels(), bm.rowBytes())); + REPORTER_ASSERT(reporter, SkCodec::kSuccess == codec->incrementalDecode()); + REPORTER_ASSERT(reporter, bm.width() == 1); + REPORTER_ASSERT(reporter, bm.height() == 1); + REPORTER_ASSERT(reporter, !(bm.empty())); + if (!(bm.empty())) { + REPORTER_ASSERT(reporter, bm.getColor(0, 0) == 0x00000000); + } + } + } // test short Gif. 80 is missing a few bytes. test_gif_data_short(reporter, static_cast<void *>(gGIFData), 80); @@ -197,4 +222,63 @@ // "libgif warning [interlace DGifGetLine]" } -#endif // !(SK_BUILD_FOR_WIN32||SK_BUILD_FOR_IOS||SK_BUILD_FOR_MAC) +// Regression test for decoding a gif image with sampleSize of 4, which was +// previously crashing. +DEF_TEST(Gif_Sampled, r) { + std::unique_ptr<SkFILEStream> stream( + new SkFILEStream(GetResourcePath("test640x479.gif").c_str())); + REPORTER_ASSERT(r, stream->isValid()); + if (!stream->isValid()) { + return; + } + + std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(stream.release())); + REPORTER_ASSERT(r, codec); + if (!codec) { + return; + } + + SkAndroidCodec::AndroidOptions options; + options.fSampleSize = 4; + + SkBitmap bm; + bm.allocPixels(codec->getInfo()); + const SkCodec::Result result = codec->getAndroidPixels(codec->getInfo(), bm.getPixels(), + bm.rowBytes(), &options); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); +} + +// If a GIF file is truncated before the header for the first image is defined, +// we should not create an SkCodec. +DEF_TEST(Codec_GifTruncated, r) { + sk_sp<SkData> data(GetResourceAsData("test640x479.gif")); + if (!data) { + return; + } + + // This is right before the header for the first image. + data = SkData::MakeSubset(data.get(), 0, 446); + std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data)); + REPORTER_ASSERT(r, !codec); +} + +DEF_TEST(Codec_GifTruncated2, r) { + sk_sp<SkData> data(GetResourceAsData("box.gif")); + if (!data) { + return; + } + + // This is after the header, but before the color table. + data = SkData::MakeSubset(data.get(), 0, 23); + std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data)); + if (!codec) { + ERRORF(r, "Failed to create codec with partial data"); + return; + } + + // Although we correctly created a codec, no frame is + // complete enough that it has its metadata. Returning 0 + // ensures that Chromium will not try to create a frame + // too early. + REPORTER_ASSERT(r, codec->getFrameCount() == 0); +}
diff --git a/src/third_party/skia/tests/GpuColorFilterTest.cpp b/src/third_party/skia/tests/GpuColorFilterTest.cpp deleted file mode 100644 index 202756b..0000000 --- a/src/third_party/skia/tests/GpuColorFilterTest.cpp +++ /dev/null
@@ -1,124 +0,0 @@ - -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#if SK_SUPPORT_GPU - -#include "GrContext.h" -#include "GrContextFactory.h" -#include "GrProcessor.h" -#include "SkColorFilter.h" -#include "SkGr.h" -#include "Test.h" - -static GrColor filterColor(const GrColor& color, uint32_t flags) { - uint32_t mask = 0; - if (flags & kR_GrColorComponentFlag) { - mask = 0xFF << GrColor_SHIFT_R; - } - if (flags & kG_GrColorComponentFlag) { - mask |= 0xFF << GrColor_SHIFT_G; - } - if (flags & kB_GrColorComponentFlag) { - mask |= 0xFF << GrColor_SHIFT_B; - } - if (flags & kA_GrColorComponentFlag) { - mask |= 0xFF << GrColor_SHIFT_A; - } - return color & mask; -} - -static void test_getConstantColorComponents(skiatest::Reporter* reporter, GrContext* grContext) { - struct GetConstantComponentTestCase { - // "Shape drawn with" - uint32_t inputComponents; // "rgb of", "red of", "alpha of", ... - GrColor inputColor; // "[color]" - - SkColor filterColor; // "with filter color [color]" - SkXfermode::Mode filterMode; // "in mode [mode]" - - // "produces" - uint32_t outputComponents; // "rgb of", "red of", "alpha of", ... - GrColor outputColor; // "[color]" - }; - - // Shorthands. - enum { - kR = kR_GrColorComponentFlag, - kG = kG_GrColorComponentFlag, - kB = kB_GrColorComponentFlag, - kA = kA_GrColorComponentFlag, - kRGB = kRGB_GrColorComponentFlags, - kRGBA = kRGBA_GrColorComponentFlags - }; - - // Note: below, SkColors are non-premultiplied, where as GrColors are premultiplied. - - const SkColor c1 = SkColorSetARGB(200, 200, 200, 200); - const SkColor c2 = SkColorSetARGB(60, 60, 60, 60); - const GrColor gr_c1 = SkColor2GrColor(c1); - const GrColor gr_c2 = SkColor2GrColor(c2); - - const GrColor gr_black = GrColorPackRGBA(0, 0, 0, 0); - const GrColor gr_white = GrColorPackRGBA(255, 255, 255, 255); - const GrColor gr_whiteTrans = GrColorPackRGBA(128, 128, 128, 128); - - GetConstantComponentTestCase filterTests[] = { - // A color filtered with Clear produces black. - { kRGBA, gr_white, SK_ColorBLACK, SkXfermode::kClear_Mode, kRGBA, gr_black }, - { kRGBA, gr_c1, SK_ColorWHITE, SkXfermode::kClear_Mode, kRGBA, gr_black }, - { kR, gr_white, c1, SkXfermode::kClear_Mode, kRGBA, gr_black }, - - // A color filtered with a color in mode Src, produces the filter color. - { kRGBA, gr_c2, c1, SkXfermode::kSrc_Mode, kRGBA, gr_c1 }, - { kA, gr_c1, c1, SkXfermode::kSrc_Mode, kRGBA, gr_c1 }, - - // A color filtered with SrcOver produces a color. - { kRGBA, gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kSrcOver_Mode, kRGBA, GrColorPackRGBA(164, 164, 164, 192)}, - // An unknown color with known alpha filtered with SrcOver produces an unknown color with known alpha. - { kA , gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kSrcOver_Mode, kA , GrColorPackRGBA(0, 0, 0, 192)}, - // A color with unknown alpha filtered with SrcOver produces a color with unknown alpha. - { kRGB , gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kSrcOver_Mode, kRGB, GrColorPackRGBA(164, 164, 164, 0)}, - - // A color filtered with DstOver produces a color. - { kRGBA, gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kDstOver_Mode, kRGBA, GrColorPackRGBA(178, 178, 178, 192)}, - // An unknown color with known alpha filtered with DstOver produces an unknown color with known alpha. - { kA , gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kDstOver_Mode, kA , GrColorPackRGBA(0, 0, 0, 192)}, - // A color with unknown alpha filtered with DstOver produces an unknown color. - { kRGB , gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kDstOver_Mode, 0 , gr_black}, - - // An unknown color with known alpha and red component filtered with Multiply produces an unknown color with known red and alpha. - { kR|kA , gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kModulate_Mode, kR|kA, GrColorPackRGBA(50, 0, 0, 64) } - }; - - for (size_t i = 0; i < SK_ARRAY_COUNT(filterTests); ++i) { - const GetConstantComponentTestCase& test = filterTests[i]; - SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(test.filterColor, test.filterMode)); - SkAutoTUnref<GrFragmentProcessor> effect(cf->asFragmentProcessor(grContext)); - GrColor color = test.inputColor; - uint32_t components = test.inputComponents; - effect->getConstantColorComponents(&color, &components); - - REPORTER_ASSERT(reporter, filterColor(color, components) == test.outputColor); - REPORTER_ASSERT(reporter, test.outputComponents == components); - } -} - -DEF_GPUTEST(GpuColorFilter, reporter, factory) { - for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) { - GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type); - - GrContext* grContext = factory->get(glType); - if (NULL == grContext) { - continue; - } - - test_getConstantColorComponents(reporter, grContext); - } -} - -#endif
diff --git a/src/third_party/skia/tests/GpuDrawPathTest.cpp b/src/third_party/skia/tests/GpuDrawPathTest.cpp index 3e47a05..83641cb 100644 --- a/src/third_party/skia/tests/GpuDrawPathTest.cpp +++ b/src/third_party/skia/tests/GpuDrawPathTest.cpp
@@ -5,27 +5,35 @@ * found in the LICENSE file. */ +#include "SkTypes.h" + #if SK_SUPPORT_GPU #include "GrContext.h" -#include "GrContextFactory.h" +#include "GrPath.h" +#include "GrShape.h" #include "SkBitmap.h" #include "SkCanvas.h" #include "SkColor.h" #include "SkPaint.h" +#include "SkPath.h" +#include "SkDashPathEffect.h" #include "SkRRect.h" #include "SkRect.h" #include "SkSurface.h" #include "Test.h" +#include <initializer_list> + static void test_drawPathEmpty(skiatest::Reporter*, SkCanvas* canvas) { // Filling an empty path should not crash. SkPaint paint; - canvas->drawRect(SkRect(), paint); + SkRect emptyRect = SkRect::MakeEmpty(); + canvas->drawRect(emptyRect, paint); canvas->drawPath(SkPath(), paint); - canvas->drawOval(SkRect(), paint); - canvas->drawRect(SkRect(), paint); - canvas->drawRRect(SkRRect(), paint); + canvas->drawOval(emptyRect, paint); + canvas->drawRect(emptyRect, paint); + canvas->drawRRect(SkRRect::MakeRect(emptyRect), paint); // Stroking an empty path should not crash. paint.setAntiAlias(true); @@ -33,32 +41,125 @@ paint.setColor(SK_ColorGRAY); paint.setStrokeWidth(SkIntToScalar(20)); paint.setStrokeJoin(SkPaint::kRound_Join); - canvas->drawRect(SkRect(), paint); + canvas->drawRect(emptyRect, paint); canvas->drawPath(SkPath(), paint); - canvas->drawOval(SkRect(), paint); - canvas->drawRect(SkRect(), paint); - canvas->drawRRect(SkRRect(), paint); + canvas->drawOval(emptyRect, paint); + canvas->drawRect(emptyRect, paint); + canvas->drawRRect(SkRRect::MakeRect(emptyRect), paint); } +static void fill_and_stroke(SkCanvas* canvas, const SkPath& p1, const SkPath& p2, + sk_sp<SkPathEffect> effect) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setPathEffect(effect); -DEF_GPUTEST(GpuDrawPath, reporter, factory) { - return; + canvas->drawPath(p1, paint); + canvas->drawPath(p2, paint); - for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) { - GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type); + paint.setStyle(SkPaint::kStroke_Style); + canvas->drawPath(p1, paint); + canvas->drawPath(p2, paint); +} - GrContext* grContext = factory->get(glType); - if (NULL == grContext) { - continue; - } - static const int sampleCounts[] = { 0, 4, 16 }; +static void test_drawSameRectOvals(skiatest::Reporter*, SkCanvas* canvas) { + // Drawing ovals with similar bounds but different points order should not crash. - for (size_t i = 0; i < SK_ARRAY_COUNT(sampleCounts); ++i) { + SkPath oval1, oval2; + const SkRect rect = SkRect::MakeWH(100, 50); + oval1.addOval(rect, SkPath::kCW_Direction); + oval2.addOval(rect, SkPath::kCCW_Direction); + + fill_and_stroke(canvas, oval1, oval2, nullptr); + + const SkScalar intervals[] = { 1, 1 }; + fill_and_stroke(canvas, oval1, oval2, SkDashPathEffect::Make(intervals, 2, 0)); +} + +DEF_GPUTEST_FOR_ALL_GL_CONTEXTS(GpuDrawPath, reporter, ctxInfo) { + for (auto& test_func : { &test_drawPathEmpty, &test_drawSameRectOvals }) { + for (auto& sampleCount : {0, 4, 16}) { SkImageInfo info = SkImageInfo::MakeN32Premul(255, 255); - - SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(grContext, info, - sampleCounts[i], NULL)); - test_drawPathEmpty(reporter, surface->getCanvas()); + auto surface( + SkSurface::MakeRenderTarget(ctxInfo.grContext(), SkBudgeted::kNo, info, + sampleCount, nullptr)); + if (!surface) { + continue; + } + test_func(reporter, surface->getCanvas()); + } + } +} + +DEF_GPUTEST(GrPathKeys, reporter, /*factory*/) { + SkPaint strokePaint; + strokePaint.setStyle(SkPaint::kStroke_Style); + strokePaint.setStrokeWidth(10.f); + GrStyle styles[] = { + GrStyle::SimpleFill(), + GrStyle::SimpleHairline(), + GrStyle(strokePaint) + }; + + for (const GrStyle& style : styles) { + // Keys should not ignore conic weights. + SkPath path1, path2; + SkPoint p0 = SkPoint::Make(100, 0); + SkPoint p1 = SkPoint::Make(100, 100); + + path1.conicTo(p0, p1, .5f); + path2.conicTo(p0, p1, .7f); + + GrUniqueKey key1, key2; + // We expect these small paths to be keyed based on their data. + bool isVolatile; + GrPath::ComputeKey(GrShape(path1, GrStyle::SimpleFill()), &key1, &isVolatile); + REPORTER_ASSERT(reporter, !isVolatile); + REPORTER_ASSERT(reporter, key1.isValid()); + GrPath::ComputeKey(GrShape(path2, GrStyle::SimpleFill()), &key2, &isVolatile); + REPORTER_ASSERT(reporter, !isVolatile); + REPORTER_ASSERT(reporter, key1.isValid()); + REPORTER_ASSERT(reporter, key1 != key2); + { + GrUniqueKey tempKey; + path1.setIsVolatile(true); + GrPath::ComputeKey(GrShape(path1, style), &key1, &isVolatile); + REPORTER_ASSERT(reporter, isVolatile); + REPORTER_ASSERT(reporter, !tempKey.isValid()); + } + + // Ensure that recreating the GrShape doesn't change the key. + { + GrUniqueKey tempKey; + GrPath::ComputeKey(GrShape(path2, GrStyle::SimpleFill()), &tempKey, &isVolatile); + REPORTER_ASSERT(reporter, key2 == tempKey); + } + + // Try a large path that is too big to be keyed off its data. + SkPath path3; + SkPath path4; + for (int i = 0; i < 1000; ++i) { + SkScalar s = SkIntToScalar(i); + path3.conicTo(s, 3.f * s / 4, s + 1.f, s, 0.5f + s / 2000.f); + path4.conicTo(s, 3.f * s / 4, s + 1.f, s, 0.3f + s / 2000.f); + } + + GrUniqueKey key3, key4; + // These aren't marked volatile and so should have keys + GrPath::ComputeKey(GrShape(path3, style), &key3, &isVolatile); + REPORTER_ASSERT(reporter, !isVolatile); + REPORTER_ASSERT(reporter, key3.isValid()); + GrPath::ComputeKey(GrShape(path4, style), &key4, &isVolatile); + REPORTER_ASSERT(reporter, !isVolatile); + REPORTER_ASSERT(reporter, key4.isValid()); + REPORTER_ASSERT(reporter, key3 != key4); + + { + GrUniqueKey tempKey; + path3.setIsVolatile(true); + GrPath::ComputeKey(GrShape(path3, style), &key1, &isVolatile); + REPORTER_ASSERT(reporter, isVolatile); + REPORTER_ASSERT(reporter, !tempKey.isValid()); } } }
diff --git a/src/third_party/skia/tests/GpuLayerCacheTest.cpp b/src/third_party/skia/tests/GpuLayerCacheTest.cpp index c9b88f5..7419f17 100644 --- a/src/third_party/skia/tests/GpuLayerCacheTest.cpp +++ b/src/third_party/skia/tests/GpuLayerCacheTest.cpp
@@ -5,22 +5,46 @@ * found in the LICENSE file. */ +// Disabling this test since it is for the layer hoister which is current disabled. +// The test fails when we add a discard to a newly created render target. +#if 0 + #if SK_SUPPORT_GPU #include "GrContext.h" -#include "GrContextFactory.h" #include "GrLayerCache.h" +#include "GrResourceCache.h" #include "SkPictureRecorder.h" #include "Test.h" class TestingAccess { public: + static int NumPlots() { + return GrLayerCache::kNumPlotsX * GrLayerCache::kNumPlotsY; + } + static SkISize PlotSize() { + return SkISize::Make(GrLayerCache::kAtlasTextureWidth / GrLayerCache::kNumPlotsX, + GrLayerCache::kAtlasTextureHeight / GrLayerCache::kNumPlotsY); + } + + static GrTexture* GetBackingTexture(GrLayerCache* cache) { + return cache->fAtlas->getTextureOrNull(); + } + static int NumLayers(GrLayerCache* cache) { return cache->numLayers(); } static void Purge(GrLayerCache* cache, uint32_t pictureID) { cache->purge(pictureID); } + static int Uses(GrCachedLayer* layer) { + return layer->uses(); + } + static GrCachedLayer* Find(GrLayerCache* cache, uint32_t pictureID, + const SkMatrix& initialMat, + const int* key, int keySize) { + return cache->findLayer(pictureID, initialMat, key, keySize); + } }; // Add several layers to the cache @@ -31,14 +55,17 @@ int idOffset) { for (int i = 0; i < numToAdd; ++i) { - GrCachedLayer* layer = cache->findLayerOrCreate(picture.uniqueID(), - idOffset+i+1, idOffset+i+2, - SkIPoint::Make(0, 0), + int key[1] = { idOffset+i+1 }; + GrCachedLayer* layer = cache->findLayerOrCreate(picture.uniqueID(), + idOffset+i+1, idOffset+i+2, + SkIRect::MakeEmpty(), + SkIRect::MakeEmpty(), SkMatrix::I(), - NULL); + key, 1, + nullptr); REPORTER_ASSERT(reporter, layer); - GrCachedLayer* temp = cache->findLayer(picture.uniqueID(), idOffset+i+1, idOffset+i+2, - SkIPoint::Make(0, 0), SkMatrix::I()); + GrCachedLayer* temp = TestingAccess::Find(cache, picture.uniqueID(), SkMatrix::I(), + key, 1); REPORTER_ASSERT(reporter, temp == layer); REPORTER_ASSERT(reporter, TestingAccess::NumLayers(cache) == idOffset + i + 1); @@ -46,169 +73,302 @@ REPORTER_ASSERT(reporter, picture.uniqueID() == layer->pictureID()); REPORTER_ASSERT(reporter, layer->start() == idOffset + i + 1); REPORTER_ASSERT(reporter, layer->stop() == idOffset + i + 2); - REPORTER_ASSERT(reporter, layer->ctm() == SkMatrix::I()); - REPORTER_ASSERT(reporter, NULL == layer->texture()); - REPORTER_ASSERT(reporter, NULL == layer->paint()); + REPORTER_ASSERT(reporter, !layer->texture()); + REPORTER_ASSERT(reporter, !layer->paint()); REPORTER_ASSERT(reporter, !layer->isAtlased()); } - - cache->trackPicture(&picture); } static void lock_layer(skiatest::Reporter* reporter, GrLayerCache* cache, GrCachedLayer* layer) { - // Make the layer 512x512 (so it can be atlased) - GrTextureDesc desc; - desc.fWidth = 512; - desc.fHeight = 512; + // Make each layer big enough to consume one whole plot in the atlas + GrSurfaceDesc desc; + desc.fFlags = kRenderTarget_GrSurfaceFlag; + desc.fWidth = TestingAccess::PlotSize().fWidth; + desc.fHeight = TestingAccess::PlotSize().fHeight; desc.fConfig = kSkia8888_GrPixelConfig; - bool needsRerendering = cache->lock(layer, desc, false); + bool needsRerendering; + bool inAtlas = cache->tryToAtlas(layer, desc, &needsRerendering); + if (!inAtlas) { + cache->lock(layer, desc, &needsRerendering); + } REPORTER_ASSERT(reporter, needsRerendering); - needsRerendering = cache->lock(layer, desc, false); + cache->lock(layer, desc, &needsRerendering); REPORTER_ASSERT(reporter, !needsRerendering); REPORTER_ASSERT(reporter, layer->texture()); REPORTER_ASSERT(reporter, layer->locked()); + + cache->addUse(layer); + + REPORTER_ASSERT(reporter, 1 == TestingAccess::Uses(layer)); } // This test case exercises the public API of the GrLayerCache class. // In particular it checks its interaction with the resource cache (w.r.t. // locking & unlocking textures). // TODO: need to add checks on VRAM usage! -DEF_GPUTEST(GpuLayerCache, reporter, factory) { - static const int kInitialNumLayers = 5; +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GpuLayerCache, reporter, ctxInfo) { + // Add one more layer than can fit in the atlas + static const int kInitialNumLayers = TestingAccess::NumPlots() + 1; - for (int i= 0; i < GrContextFactory::kGLContextTypeCnt; ++i) { - GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i; +#if GR_CACHE_STATS + GrResourceCache::Stats stats; +#endif - if (!GrContextFactory::IsRenderingGLContext(glCtxType)) { - continue; - } + sk_sp<SkPicture> picture; - GrContext* context = factory->get(glCtxType); - - if (NULL == context) { - continue; - } - + { SkPictureRecorder recorder; - recorder.beginRecording(1, 1); - SkAutoTUnref<const SkPicture> picture(recorder.endRecording()); - - GrLayerCache cache(context); - - create_layers(reporter, &cache, *picture, kInitialNumLayers, 0); - - for (int i = 0; i < kInitialNumLayers; ++i) { - GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, i+2, - SkIPoint::Make(0, 0), SkMatrix::I()); - REPORTER_ASSERT(reporter, layer); - - lock_layer(reporter, &cache, layer); - - // The first 4 layers should be in the atlas (and thus have non-empty - // rects) - if (i < 4) { - REPORTER_ASSERT(reporter, layer->isAtlased()); - } else { - // The 5th layer couldn't fit in the atlas - REPORTER_ASSERT(reporter, !layer->isAtlased()); - } - } - - // Unlock the textures - for (int i = 0; i < kInitialNumLayers; ++i) { - GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, i+2, - SkIPoint::Make(0, 0), SkMatrix::I()); - REPORTER_ASSERT(reporter, layer); - cache.unlock(layer); - } - - for (int i = 0; i < kInitialNumLayers; ++i) { - GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, i+2, - SkIPoint::Make(0, 0), SkMatrix::I()); - REPORTER_ASSERT(reporter, layer); - - REPORTER_ASSERT(reporter, !layer->locked()); - // The first 4 layers should still be in the atlas. - if (i < 4) { - REPORTER_ASSERT(reporter, layer->texture()); - REPORTER_ASSERT(reporter, layer->isAtlased()); - } else { - // The final layer should be unlocked. - REPORTER_ASSERT(reporter, NULL == layer->texture()); - REPORTER_ASSERT(reporter, !layer->isAtlased()); - } - } - - { - // Add an additional layer. Since all the layers are unlocked this - // will force out the first atlased layer - create_layers(reporter, &cache, *picture, 1, kInitialNumLayers); - GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), - kInitialNumLayers+1, kInitialNumLayers+2, - SkIPoint::Make(0, 0), SkMatrix::I()); - REPORTER_ASSERT(reporter, layer); - - lock_layer(reporter, &cache, layer); - cache.unlock(layer); - } - - for (int i = 0; i < kInitialNumLayers+1; ++i) { - GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, i+2, - SkIPoint::Make(0, 0), SkMatrix::I()); - // 3 old layers plus the new one should be in the atlas. - if (1 == i || 2 == i || 3 == i || 5 == i) { - REPORTER_ASSERT(reporter, layer); - REPORTER_ASSERT(reporter, !layer->locked()); - REPORTER_ASSERT(reporter, layer->texture()); - REPORTER_ASSERT(reporter, layer->isAtlased()); - } else if (4 == i) { - // The one that was never atlased should still be around - REPORTER_ASSERT(reporter, layer); - - REPORTER_ASSERT(reporter, NULL == layer->texture()); - REPORTER_ASSERT(reporter, !layer->isAtlased()); - } else { - // The one bumped out of the atlas (i.e., 0) should be gone - REPORTER_ASSERT(reporter, NULL == layer); - } - } - - //-------------------------------------------------------------------- - // Free them all SkGpuDevice-style. This will not free up the - // atlas' texture but will eliminate all the layers. - TestingAccess::Purge(&cache, picture->uniqueID()); - - REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0); - // TODO: add VRAM/resource cache check here - - //-------------------------------------------------------------------- - // Test out the GrContext-style purge. This should remove all the layers - // and the atlas. - // Re-create the layers - create_layers(reporter, &cache, *picture, kInitialNumLayers, 0); - - // Free them again GrContext-style. This should free up everything. - cache.freeAll(); - - REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0); - // TODO: add VRAM/resource cache check here - - //-------------------------------------------------------------------- - // Test out the MessageBus-style purge. This will not free the atlas - // but should eliminate the free-floating layers. - create_layers(reporter, &cache, *picture, kInitialNumLayers, 0); - - picture.reset(NULL); - cache.processDeletedPictures(); - - REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0); - // TODO: add VRAM/resource cache check here + SkCanvas* c = recorder.beginRecording(1, 1); + // Draw something, anything, to prevent an empty-picture optimization, + // which is a singleton and never purged. + c->drawRect(SkRect::MakeWH(1,1), SkPaint()); + picture = recorder.finishRecordingAsPicture(); } + + GrResourceCache* resourceCache = ctxInfo.grContext()->getResourceCache(); + + GrLayerCache cache(ctxInfo.grContext()); + + create_layers(reporter, &cache, *picture, kInitialNumLayers, 0); + + for (int i = 0; i < kInitialNumLayers; ++i) { + int key[1] = { i + 1 }; + GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(), + key, 1); + REPORTER_ASSERT(reporter, layer); + + lock_layer(reporter, &cache, layer); + +#if GR_CACHE_STATS + resourceCache->getStats(&stats); +#endif + + // The first 4 layers should be in the atlas (and thus have non-empty rects) + if (i < TestingAccess::NumPlots()) { + REPORTER_ASSERT(reporter, layer->isAtlased()); +#if GR_CACHE_STATS + REPORTER_ASSERT(reporter, 1 == stats.fTotal); +#endif + } else { + // The 5th layer couldn't fit in the atlas + REPORTER_ASSERT(reporter, !layer->isAtlased()); +#if GR_CACHE_STATS + REPORTER_ASSERT(reporter, 2 == stats.fTotal); +#endif + } + } + + // Unlock the textures + for (int i = 0; i < kInitialNumLayers; ++i) { + int key[1] = { i+1 }; + + GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(), + key, 1); + REPORTER_ASSERT(reporter, layer); + cache.removeUse(layer); + } + +#if GR_CACHE_STATS + resourceCache->getStats(&stats); + REPORTER_ASSERT(reporter, 2 == stats.fTotal); + // The floating layer is purgeable the cache is not + REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable); + REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable); +#endif + + for (int i = 0; i < kInitialNumLayers; ++i) { + int key[1] = { i+1 }; + + GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(), + key, 1); + REPORTER_ASSERT(reporter, layer); + + // All the layers should be unlocked + REPORTER_ASSERT(reporter, !layer->locked()); + + // When hoisted layers aren't cached they are aggressively removed + // from the atlas +#if GR_CACHE_HOISTED_LAYERS + // The first 4 layers should still be in the atlas. + if (i < 4) { + REPORTER_ASSERT(reporter, layer->texture()); + REPORTER_ASSERT(reporter, layer->isAtlased()); + } else { +#endif + // The final layer should not be atlased. + REPORTER_ASSERT(reporter, !layer->texture()); + REPORTER_ASSERT(reporter, !layer->isAtlased()); +#if GR_CACHE_HOISTED_LAYERS + } +#endif + } + + // Let go of the backing texture + cache.end(); + REPORTER_ASSERT(reporter, nullptr == TestingAccess::GetBackingTexture(&cache)); + +#if GR_CACHE_STATS + resourceCache->getStats(&stats); + REPORTER_ASSERT(reporter, 2 == stats.fTotal); + // Now both the floater and the atlas are purgeable + REPORTER_ASSERT(reporter, 2 == stats.fNumPurgeable); +#endif + + // re-attach to the backing texture + cache.begin(); + REPORTER_ASSERT(reporter, TestingAccess::GetBackingTexture(&cache)); + +#if GR_CACHE_STATS + resourceCache->getStats(&stats); + REPORTER_ASSERT(reporter, 2 == stats.fTotal); + // The atlas is restored to being non-purgeable + REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable); + REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable); +#endif + + { + int key[1] = { kInitialNumLayers+1 }; + + // Add an additional layer. Since all the layers are unlocked this + // will force out the first atlased layer + create_layers(reporter, &cache, *picture, 1, kInitialNumLayers); + GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(), + key, 1); + REPORTER_ASSERT(reporter, layer); + + lock_layer(reporter, &cache, layer); + cache.removeUse(layer); + } + + for (int i = 0; i < kInitialNumLayers+1; ++i) { + int key[1] = { i+1 }; + + GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(), + key, 1); +#if GR_CACHE_HOISTED_LAYERS + // 3 old layers plus the new one should be in the atlas. + if (1 == i || 2 == i || 3 == i || 5 == i) { + REPORTER_ASSERT(reporter, layer); + REPORTER_ASSERT(reporter, !layer->locked()); + REPORTER_ASSERT(reporter, layer->texture()); + REPORTER_ASSERT(reporter, layer->isAtlased()); + } else if (4 == i) { +#endif + // The one that was never atlased should still be around + REPORTER_ASSERT(reporter, layer); + + REPORTER_ASSERT(reporter, !layer->texture()); + REPORTER_ASSERT(reporter, !layer->isAtlased()); +#if GR_CACHE_HOISTED_LAYERS + } else { + // The one bumped out of the atlas (i.e., 0) should be gone + REPORTER_ASSERT(reporter, nullptr == layer); + } +#endif + } + + //-------------------------------------------------------------------- + // Free them all SkGpuDevice-style. This will not free up the + // atlas' texture but will eliminate all the layers. + TestingAccess::Purge(&cache, picture->uniqueID()); + + REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0); + +#if GR_CACHE_STATS + resourceCache->getStats(&stats); + REPORTER_ASSERT(reporter, 2 == stats.fTotal); + // Atlas isn't purgeable + REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable); + REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable); +#endif + + //-------------------------------------------------------------------- + // Test out the GrContext-style purge. This should remove all the layers + // and the atlas. + // Re-create the layers + create_layers(reporter, &cache, *picture, kInitialNumLayers, 0); + + // Free them again GrContext-style. This should free up everything. + cache.freeAll(); + + REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0); + + REPORTER_ASSERT(reporter, nullptr == TestingAccess::GetBackingTexture(&cache)); + +#if GR_CACHE_STATS + resourceCache->getStats(&stats); + REPORTER_ASSERT(reporter, 2 == stats.fTotal); + REPORTER_ASSERT(reporter, 2 == stats.fNumPurgeable); +#endif + + // Purge the resource cache ... + resourceCache->purgeAllUnlocked(); + +#if GR_CACHE_STATS + resourceCache->getStats(&stats); + REPORTER_ASSERT(reporter, 0 == stats.fTotal); +#endif + + // and try to re-attach to the backing texture. This should fail + cache.begin(); + REPORTER_ASSERT(reporter, nullptr == TestingAccess::GetBackingTexture(&cache)); + + //-------------------------------------------------------------------- + // Test out the MessageBus-style purge. This will not free the atlas + // but should eliminate the free-floating layers. + create_layers(reporter, &cache, *picture, kInitialNumLayers, 0); + + // Allocate/use the layers + for (int i = 0; i < kInitialNumLayers; ++i) { + int key[1] = { i + 1 }; + GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(), + key, 1); + REPORTER_ASSERT(reporter, layer); + + lock_layer(reporter, &cache, layer); + } + +#if GR_CACHE_STATS + resourceCache->getStats(&stats); + REPORTER_ASSERT(reporter, 2 == stats.fTotal); + REPORTER_ASSERT(reporter, 2 == stats.fNumNonPurgeable); +#endif + + // Unlock the textures + for (int i = 0; i < kInitialNumLayers; ++i) { + int key[1] = { i+1 }; + + GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(), + key, 1); + REPORTER_ASSERT(reporter, layer); + cache.removeUse(layer); + } + + picture.reset(nullptr); + cache.processDeletedPictures(); + + REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0); + +#if GR_CACHE_STATS + resourceCache->getStats(&stats); + REPORTER_ASSERT(reporter, 2 == stats.fTotal); + REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable); + REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable); +#endif + + cache.end(); + +#if GR_CACHE_STATS + resourceCache->getStats(&stats); + REPORTER_ASSERT(reporter, 2 == stats.fTotal); + REPORTER_ASSERT(reporter, 2 == stats.fNumPurgeable); +#endif } #endif +#endif
diff --git a/src/third_party/skia/tests/GpuSampleLocationsTest.cpp b/src/third_party/skia/tests/GpuSampleLocationsTest.cpp new file mode 100644 index 0000000..6c5d4a6 --- /dev/null +++ b/src/third_party/skia/tests/GpuSampleLocationsTest.cpp
@@ -0,0 +1,199 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTypes.h" +#include "SkPoint.h" +#include "Test.h" +#include <vector> + +#if SK_SUPPORT_GPU + +#include "GrAppliedClip.h" +#include "GrRenderTargetContext.h" +#include "GrRenderTargetPriv.h" +#include "GrTypesPriv.h" +#include "gl/GrGLGpu.h" +#include "gl/debug/DebugGLTestContext.h" + +typedef std::vector<SkPoint> SamplePattern; + +static const SamplePattern kTestPatterns[] = { + SamplePattern{ // Intel on mac, msaa8, offscreen. + {0.562500, 0.312500}, + {0.437500, 0.687500}, + {0.812500, 0.562500}, + {0.312500, 0.187500}, + {0.187500, 0.812500}, + {0.062500, 0.437500}, + {0.687500, 0.937500}, + {0.937500, 0.062500} + }, + + SamplePattern{ // Intel on mac, msaa8, on-screen. + {0.562500, 0.687500}, + {0.437500, 0.312500}, + {0.812500, 0.437500}, + {0.312500, 0.812500}, + {0.187500, 0.187500}, + {0.062500, 0.562500}, + {0.687500, 0.062500}, + {0.937500, 0.937500} + }, + + SamplePattern{ // NVIDIA, msaa16. + {0.062500, 0.000000}, + {0.250000, 0.125000}, + {0.187500, 0.375000}, + {0.437500, 0.312500}, + {0.500000, 0.062500}, + {0.687500, 0.187500}, + {0.750000, 0.437500}, + {0.937500, 0.250000}, + {0.000000, 0.500000}, + {0.312500, 0.625000}, + {0.125000, 0.750000}, + {0.375000, 0.875000}, + {0.562500, 0.562500}, + {0.812500, 0.687500}, + {0.625000, 0.812500}, + {0.875000, 0.937500} + }, + + SamplePattern{ // NVIDIA, mixed samples, 16:1. + {0.250000, 0.125000}, + {0.625000, 0.812500}, + {0.500000, 0.062500}, + {0.812500, 0.687500}, + {0.187500, 0.375000}, + {0.875000, 0.937500}, + {0.125000, 0.750000}, + {0.750000, 0.437500}, + {0.937500, 0.250000}, + {0.312500, 0.625000}, + {0.437500, 0.312500}, + {0.000000, 0.500000}, + {0.375000, 0.875000}, + {0.687500, 0.187500}, + {0.062500, 0.000000}, + {0.562500, 0.562500} + } +}; +constexpr int numTestPatterns = SK_ARRAY_COUNT(kTestPatterns); + +class TestSampleLocationsInterface : public SkNoncopyable { +public: + virtual void overrideSamplePattern(const SamplePattern&) = 0; + virtual ~TestSampleLocationsInterface() {} +}; + +void assert_equal(skiatest::Reporter* reporter, const SamplePattern& pattern, + const GrGpu::MultisampleSpecs& specs, bool flipY) { + GrAlwaysAssert(specs.fSampleLocations); + if ((int)pattern.size() != specs.fEffectiveSampleCnt) { + REPORT_FAILURE(reporter, "", SkString("Sample pattern has wrong number of samples.")); + return; + } + for (int i = 0; i < specs.fEffectiveSampleCnt; ++i) { + SkPoint expectedLocation = specs.fSampleLocations[i]; + if (flipY) { + expectedLocation.fY = 1 - expectedLocation.fY; + } + if (pattern[i] != expectedLocation) { + REPORT_FAILURE(reporter, "", SkString("Sample pattern has wrong sample location.")); + return; + } + } +} + +void test_sampleLocations(skiatest::Reporter* reporter, TestSampleLocationsInterface* testInterface, + GrContext* ctx) { + SkRandom rand; + sk_sp<GrRenderTargetContext> bottomUps[numTestPatterns]; + sk_sp<GrRenderTargetContext> topDowns[numTestPatterns]; + for (int i = 0; i < numTestPatterns; ++i) { + int numSamples = (int)kTestPatterns[i].size(); + GrAlwaysAssert(numSamples > 1 && SkIsPow2(numSamples)); + bottomUps[i] = ctx->makeDeferredRenderTargetContextWithFallback( + SkBackingFit::kExact, 100, 100, kRGBA_8888_GrPixelConfig, nullptr, + rand.nextRangeU(1 + numSamples / 2, numSamples), + kBottomLeft_GrSurfaceOrigin); + topDowns[i] = ctx->makeDeferredRenderTargetContextWithFallback( + SkBackingFit::kExact, 100, 100, kRGBA_8888_GrPixelConfig, nullptr, + rand.nextRangeU(1 + numSamples / 2, numSamples), + kTopLeft_GrSurfaceOrigin); + } + + // Ensure all sample locations get queried and/or cached properly. + for (int repeat = 0; repeat < 2; ++repeat) { + for (int i = 0; i < numTestPatterns; ++i) { + testInterface->overrideSamplePattern(kTestPatterns[i]); + for (GrRenderTargetContext* rtc : {bottomUps[i].get(), topDowns[i].get()}) { + GrPipeline dummyPipeline(rtc->accessRenderTarget(), + GrPipeline::ScissorState::kDisabled, + SkBlendMode::kSrcOver); + GrRenderTarget* rt = rtc->accessRenderTarget(); + assert_equal(reporter, kTestPatterns[i], + rt->renderTargetPriv().getMultisampleSpecs(dummyPipeline), + kBottomLeft_GrSurfaceOrigin == rt->origin()); + } + } + } + +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +class GLTestSampleLocationsInterface : public TestSampleLocationsInterface, public GrGLInterface { +public: + GLTestSampleLocationsInterface() : fTestContext(sk_gpu_test::CreateDebugGLTestContext()) { + fStandard = fTestContext->gl()->fStandard; + fExtensions = fTestContext->gl()->fExtensions; + fFunctions = fTestContext->gl()->fFunctions; + + fFunctions.fGetIntegerv = [&](GrGLenum pname, GrGLint* params) { + GrAlwaysAssert(GR_GL_EFFECTIVE_RASTER_SAMPLES != pname); + if (GR_GL_SAMPLES == pname) { + GrAlwaysAssert(!fSamplePattern.empty()); + *params = (int)fSamplePattern.size(); + } else { + fTestContext->gl()->fFunctions.fGetIntegerv(pname, params); + } + }; + + fFunctions.fGetMultisamplefv = [&](GrGLenum pname, GrGLuint index, GrGLfloat* val) { + GrAlwaysAssert(GR_GL_SAMPLE_POSITION == pname); + val[0] = fSamplePattern[index].fX; + val[1] = fSamplePattern[index].fY; + }; + } + + operator GrBackendContext() { + return reinterpret_cast<GrBackendContext>(static_cast<GrGLInterface*>(this)); + } + + void overrideSamplePattern(const SamplePattern& newPattern) override { + fSamplePattern = newPattern; + } + +private: + std::unique_ptr<sk_gpu_test::GLTestContext> fTestContext; + SamplePattern fSamplePattern; +}; + +DEF_GPUTEST(GLSampleLocations, reporter, /*factory*/) { + GLTestSampleLocationsInterface testInterface; + sk_sp<GrContext> ctx(GrContext::Create(kOpenGL_GrBackend, testInterface)); + + // This test relies on at least 2 samples. + int supportedSample = ctx->caps()->getSampleCount(2, kRGBA_8888_GrPixelConfig); + if (supportedSample < 2) { + return; + } + test_sampleLocations(reporter, &testInterface, ctx.get()); +} + +#endif
diff --git a/src/third_party/skia/tests/GrBinHashKeyTest.cpp b/src/third_party/skia/tests/GrBinHashKeyTest.cpp deleted file mode 100644 index 605fd9f..0000000 --- a/src/third_party/skia/tests/GrBinHashKeyTest.cpp +++ /dev/null
@@ -1,48 +0,0 @@ -/* - * Copyright 2010 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -// This is a GPU-backend specific test -#if SK_SUPPORT_GPU - -#include "GrMurmur3HashKey.h" -#include "GrBinHashKey.h" - -#include "Test.h" - -template<typename KeyType> static void TestHash(skiatest::Reporter* reporter) { - const char* testStringA_ = "abcdABCD"; - const char* testStringB_ = "abcdBBCD"; - const uint32_t* testStringA = reinterpret_cast<const uint32_t*>(testStringA_); - const uint32_t* testStringB = reinterpret_cast<const uint32_t*>(testStringB_); - - KeyType keyA; - keyA.setKeyData(testStringA); - // test copy constructor and comparison - KeyType keyA2(keyA); - REPORTER_ASSERT(reporter, keyA == keyA2); - REPORTER_ASSERT(reporter, keyA.getHash() == keyA2.getHash()); - // test re-init - keyA2.setKeyData(testStringA); - REPORTER_ASSERT(reporter, keyA == keyA2); - REPORTER_ASSERT(reporter, keyA.getHash() == keyA2.getHash()); - // test sorting - KeyType keyB; - keyB.setKeyData(testStringB); - REPORTER_ASSERT(reporter, keyA.getHash() != keyB.getHash()); -} - - -DEF_TEST(GrBinHashKey, reporter) { - enum { - kDataLenUsedForKey = 8 - }; - - TestHash<GrBinHashKey<kDataLenUsedForKey> >(reporter); - TestHash<GrMurmur3HashKey<kDataLenUsedForKey> >(reporter); -} - -#endif
diff --git a/src/third_party/skia/tests/GrContextAbandonTest.cpp b/src/third_party/skia/tests/GrContextAbandonTest.cpp new file mode 100644 index 0000000..03e4512 --- /dev/null +++ b/src/third_party/skia/tests/GrContextAbandonTest.cpp
@@ -0,0 +1,53 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTypes.h" + +#if SK_SUPPORT_GPU + +#include "GrContextFactory.h" +#include "Test.h" + +using namespace sk_gpu_test; + +DEF_GPUTEST(GrContext_abandonContext, reporter, /*factory*/) { + for (int testType = 0; testType < 6; ++testType) { + for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) { + GrContextFactory testFactory; + GrContextFactory::ContextType ctxType = (GrContextFactory::ContextType) i; + ContextInfo info = testFactory.getContextInfo(ctxType); + if (GrContext* context = info.grContext()) { + switch (testType) { + case 0: + context->abandonContext(); + break; + case 1: + context->releaseResourcesAndAbandonContext(); + break; + case 2: + context->abandonContext(); + context->abandonContext(); + break; + case 3: + context->abandonContext(); + context->releaseResourcesAndAbandonContext(); + break; + case 4: + context->releaseResourcesAndAbandonContext(); + context->abandonContext(); + break; + case 5: + context->releaseResourcesAndAbandonContext(); + context->releaseResourcesAndAbandonContext(); + break; + } + } + } + } +} + +#endif
diff --git a/src/third_party/skia/tests/GrContextFactoryTest.cpp b/src/third_party/skia/tests/GrContextFactoryTest.cpp index 1d884ac..d4d5667 100644 --- a/src/third_party/skia/tests/GrContextFactoryTest.cpp +++ b/src/third_party/skia/tests/GrContextFactoryTest.cpp
@@ -5,28 +5,138 @@ * found in the LICENSE file. */ +#include "SkTypes.h" + #if SK_SUPPORT_GPU #include "GrContextFactory.h" +#include "GrCaps.h" #include "Test.h" -DEF_GPUTEST(GrContextFactory, reporter, factory) { - // Reset in case some other test has been using it first. - factory->destroyContexts(); +using namespace sk_gpu_test; - // Before we ask for a context, we expect the GL context to not be there. - REPORTER_ASSERT(reporter, - NULL == factory->getGLContext(GrContextFactory::kNull_GLContextType)); +DEF_GPUTEST(GrContextFactory_NVPRContextOptionHasPathRenderingSupport, reporter, /*factory*/) { + // Test that if NVPR is requested, the context always has path rendering + // or the context creation fails. + GrContextFactory testFactory; + // Test that if NVPR is possible, caps are in sync. + for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) { + GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i); + GrContext* context = testFactory.get(ctxType, + GrContextFactory::ContextOverrides::kRequireNVPRSupport); + if (!context) { + continue; + } + REPORTER_ASSERT( + reporter, + context->caps()->shaderCaps()->pathRenderingSupport()); + } +} - // After we ask for a context, we expect that the GL context to be there. - factory->get(GrContextFactory::kNull_GLContextType); - REPORTER_ASSERT(reporter, - factory->getGLContext(GrContextFactory::kNull_GLContextType) != NULL); +DEF_GPUTEST(GrContextFactory_NoPathRenderingIfNVPRDisabled, reporter, /*factory*/) { + // Test that if NVPR is explicitly disabled, the context has no path rendering support. - // If we did not ask for a context with the particular GL context, we would - // expect the particular GL context to not be there. - REPORTER_ASSERT(reporter, - NULL == factory->getGLContext(GrContextFactory::kDebug_GLContextType)); + GrContextFactory testFactory; + for (int i = 0; i <= GrContextFactory::kLastContextType; ++i) { + GrContextFactory::ContextType ctxType = (GrContextFactory::ContextType)i; + GrContext* context = + testFactory.get(ctxType, GrContextFactory::ContextOverrides::kDisableNVPR); + if (context) { + REPORTER_ASSERT( + reporter, + !context->caps()->shaderCaps()->pathRenderingSupport()); + } + } +} + +DEF_GPUTEST(GrContextFactory_RequiredSRGBSupport, reporter, /*factory*/) { + // Test that if sRGB support is requested, the context always has that capability + // or the context creation fails. Also test that if the creation fails, a context + // created without that flag would not have had sRGB support. + GrContextFactory testFactory; + // Test that if sRGB is requested, caps are in sync. + for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) { + GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i); + GrContext* context = + testFactory.get(ctxType, GrContextFactory::ContextOverrides::kRequireSRGBSupport); + + if (context) { + REPORTER_ASSERT(reporter, context->caps()->srgbSupport()); + } else { + context = testFactory.get(ctxType); + if (context) { + REPORTER_ASSERT(reporter, !context->caps()->srgbSupport()); + } + } + } +} + +DEF_GPUTEST(GrContextFactory_abandon, reporter, /*factory*/) { + GrContextFactory testFactory; + for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) { + GrContextFactory::ContextType ctxType = (GrContextFactory::ContextType) i; + ContextInfo info1 = testFactory.getContextInfo(ctxType); + if (!info1.grContext()) { + continue; + } + REPORTER_ASSERT(reporter, info1.testContext()); + // Ref for comparison. The API does not explicitly say that this stays alive. + info1.grContext()->ref(); + testFactory.abandonContexts(); + + // Test that we get different context after abandon. + ContextInfo info2 = testFactory.getContextInfo(ctxType); + REPORTER_ASSERT(reporter, info2.grContext()); + REPORTER_ASSERT(reporter, info2.testContext()); + + REPORTER_ASSERT(reporter, info1.grContext() != info2.grContext()); + // The GL context should also change, but it also could get the same address. + + info1.grContext()->unref(); + } +} + +DEF_GPUTEST(GrContextFactory_sharedContexts, reporter, /*factory*/) { + GrContextFactory testFactory; + + for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) { + GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i); + ContextInfo info1 = testFactory.getContextInfo(ctxType); + if (!info1.grContext()) { + continue; + } + + // Ref for passing in. The API does not explicitly say that this stays alive. + info1.grContext()->ref(); + testFactory.abandonContexts(); + + // Test that creating a context in a share group with an abandoned context fails. + ContextInfo info2 = testFactory.getSharedContextInfo(info1.grContext()); + REPORTER_ASSERT(reporter, !info2.grContext()); + info1.grContext()->unref(); + + // Create a new base context + ContextInfo info3 = testFactory.getContextInfo(ctxType); + if (!info3.grContext()) { + // Vulkan NexusPlayer bot fails here. Sigh. + continue; + } + + // Creating a context in a share group may fail, but should never crash. + ContextInfo info4 = testFactory.getSharedContextInfo(info3.grContext()); + if (!info4.grContext()) { + continue; + } + REPORTER_ASSERT(reporter, info3.grContext() != info4.grContext()); + REPORTER_ASSERT(reporter, info3.testContext() != info4.testContext()); + + // Passing a different index should create a new (unique) context. + ContextInfo info5 = testFactory.getSharedContextInfo(info3.grContext(), 1); + REPORTER_ASSERT(reporter, info5.grContext()); + REPORTER_ASSERT(reporter, info5.testContext()); + REPORTER_ASSERT(reporter, info5.grContext() != info4.grContext()); + REPORTER_ASSERT(reporter, info5.testContext() != info4.testContext()); + } } #endif
diff --git a/src/third_party/skia/tests/GrDrawTargetTest.cpp b/src/third_party/skia/tests/GrDrawTargetTest.cpp index c032d88..c8492bc 100644 --- a/src/third_party/skia/tests/GrDrawTargetTest.cpp +++ b/src/third_party/skia/tests/GrDrawTargetTest.cpp
@@ -1,4 +1,3 @@ - /* * Copyright 2013 Google Inc. * @@ -6,31 +5,19 @@ * found in the LICENSE file. */ +#include "Test.h" #if SK_SUPPORT_GPU +#include "GrCaps.h" #include "GrContext.h" -#include "GrContextFactory.h" -#include "GrDrawTargetCaps.h" #include "GrGpu.h" -#include "Test.h" -static void test_print(skiatest::Reporter*, const GrDrawTargetCaps* caps) { +DEF_GPUTEST_FOR_ALL_CONTEXTS(GrDrawTargetPrint, reporter, ctxInfo) { // This used to assert. - SkString result = caps->dump(); + SkString result = ctxInfo.grContext()->caps()->dump(); SkASSERT(!result.isEmpty()); -} - -DEF_GPUTEST(GrDrawTarget, reporter, factory) { - for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) { - GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type); - - GrContext* grContext = factory->get(glType); - if (NULL == grContext) { - continue; - } - - test_print(reporter, grContext->getGpu()->caps()); - } + SkString shaderResult = ctxInfo.grContext()->caps()->shaderCaps()->dump(); + SkASSERT(!shaderResult.isEmpty()); } #endif
diff --git a/src/third_party/skia/tests/GrGLSLPrettyPrintTest.cpp b/src/third_party/skia/tests/GrGLSLPrettyPrintTest.cpp deleted file mode 100644 index 9977488..0000000 --- a/src/third_party/skia/tests/GrGLSLPrettyPrintTest.cpp +++ /dev/null
@@ -1,92 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#if SK_SUPPORT_GPU -#include "Test.h" -#include "gl/GrGLSLPrettyPrint.h" - -#define ASSERT(x) REPORTER_ASSERT(r, x) - -const SkString input1("#this is not a realshader\nvec4 some stuff;outside of a function;" - "int i(int b, int c) { { some stuff;} fake block; //comments\n return i;}" - "void main()" - "{nowin a function;{indenting;{abit more;dreadedfor((;;)(;)((;;);)){doingstuff" - ";for(;;;){and more stufff;mixed garbage\n\n\t\t\t\t\n/*using this" - " comment\n is" - " dangerous\ndo so at your own\n risk*/;\n\n\t\t\t\n" - "//a comment\n}}a; little ; love; for ; leading; spaces;} " - "an struct = { int a; int b; };" - "int[5] arr = int[5](1,2,3,4,5);} some code at the bottom; for(;;) {} }"); - -const SkString output1( - " 1\t#this is not a realshader\n" - " 2\tvec4 some stuff;\n" - " 3\toutside of a function;\n" - " 4\tint i(int b, int c) \n" - " 5\t{\n" - " 6\t\t{\n" - " 7\t\t\tsome stuff;\n" - " 8\t\t}\n" - " 9\t\tfake block;\n" - " 10\t\t//comments\n" - " 11\t\treturn i;\n" - " 12\t}\n" - " 13\tvoid main()\n" - " 14\t{\n" - " 15\t\tnowin a function;\n" - " 16\t\t{\n" - " 17\t\t\tindenting;\n" - " 18\t\t\t{\n" - " 19\t\t\t\tabit more;\n" - " 20\t\t\t\tdreadedfor((;;)(;)((;;);))\n" - " 21\t\t\t\t{\n" - " 22\t\t\t\t\tdoingstuff;\n" - " 23\t\t\t\t\tfor(;;;)\n" - " 24\t\t\t\t\t{\n" - " 25\t\t\t\t\t\tand more stufff;\n" - " 26\t\t\t\t\t\tmixed garbage/*using this comment\n" - " 27\t\t\t\t\t\t is dangerous\n" - " 28\t\t\t\t\t\tdo so at your own\n" - " 29\t\t\t\t\t\t risk*/;\n" - " 30\t\t\t\t\t\t//a comment\n" - " 31\t\t\t\t\t}\n" - " 32\t\t\t\t}\n" - " 33\t\t\t\ta;\n" - " 34\t\t\t\tlittle ;\n" - " 35\t\t\t\tlove;\n" - " 36\t\t\t\tfor ;\n" - " 37\t\t\t\tleading;\n" - " 38\t\t\t\tspaces;\n" - " 39\t\t\t}\n" - " 40\t\t\tan struct = \n" - " 41\t\t\t{\n" - " 42\t\t\t\tint a;\n" - " 43\t\t\t\tint b;\n" - " 44\t\t\t}\n" - " 45\t\t\t;\n" - " 46\t\t\tint[5] arr = int[5](1,2,3,4,5);\n" - " 47\t\t}\n" - " 48\t\tsome code at the bottom;\n" - " 49\t\tfor(;;) \n" - " 50\t\t{\n" - " 51\t\t}\n" - " 52\t}\n" - " 53\t"); - -const SkString input2("{;;{{{{;;;{{{{{{{{{{{###\n##\n#####(((((((((((((unbalanced verything;;;" - "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}" - ";;;;;;/////"); - -DEF_TEST(GrGLSLPrettyPrint, r) { - SkString test = GrGLSLPrettyPrint::PrettyPrintGLSL(input1, true); - ASSERT(output1 == test); - - // Just test we don't crash with garbage input - ASSERT(GrGLSLPrettyPrint::PrettyPrintGLSL(input2, true).c_str() != NULL); -} - -#endif
diff --git a/src/third_party/skia/tests/GrMemoryPoolTest.cpp b/src/third_party/skia/tests/GrMemoryPoolTest.cpp index 0848d97..4eb5ca7 100644 --- a/src/third_party/skia/tests/GrMemoryPoolTest.cpp +++ b/src/third_party/skia/tests/GrMemoryPoolTest.cpp
@@ -9,8 +9,8 @@ // This is a GPU-backend specific test #if SK_SUPPORT_GPU #include "GrMemoryPool.h" -#include "SkInstCnt.h" #include "SkRandom.h" +#include "SkTArray.h" #include "SkTDArray.h" #include "SkTemplates.h" @@ -19,14 +19,14 @@ // that can be set and checked. class A { public: - A() {}; + A() {} virtual void setValues(int v) { fChar = static_cast<char>(v); } virtual bool checkValues(int v) { return fChar == static_cast<char>(v); } - virtual ~A() {}; + virtual ~A() {} void* operator new(size_t size) { if (!gPool.get()) { @@ -44,35 +44,27 @@ } } - SK_DECLARE_INST_COUNT_ROOT(A); - static A* Create(SkRandom* r); static void SetAllocator(size_t preallocSize, size_t minAllocSize) { -#if SK_ENABLE_INST_COUNT - SkASSERT(0 == GetInstanceCount()); -#endif GrMemoryPool* pool = new GrMemoryPool(preallocSize, minAllocSize); gPool.reset(pool); } static void ResetAllocator() { -#if SK_ENABLE_INST_COUNT - SkASSERT(0 == GetInstanceCount()); -#endif - gPool.reset(NULL); + gPool.reset(nullptr); } private: - static SkAutoTDelete<GrMemoryPool> gPool; + static std::unique_ptr<GrMemoryPool> gPool; char fChar; }; -SkAutoTDelete<GrMemoryPool> A::gPool; +std::unique_ptr<GrMemoryPool> A::gPool; class B : public A { public: - B() {}; + B() {} virtual void setValues(int v) { fDouble = static_cast<double>(v); this->INHERITED::setValues(v); @@ -81,7 +73,7 @@ return fDouble == static_cast<double>(v) && this->INHERITED::checkValues(v); } - virtual ~B() {}; + virtual ~B() {} private: double fDouble; @@ -91,7 +83,7 @@ class C : public A { public: - C() {}; + C() {} virtual void setValues(int v) { fInt64 = static_cast<int64_t>(v); this->INHERITED::setValues(v); @@ -100,7 +92,7 @@ return fInt64 == static_cast<int64_t>(v) && this->INHERITED::checkValues(v); } - virtual ~C() {}; + virtual ~C() {} private: int64_t fInt64; @@ -115,12 +107,12 @@ fB = new B(); } virtual void setValues(int v) { - fVoidStar = reinterpret_cast<void*>(v); + fVoidStar = reinterpret_cast<void*>(static_cast<intptr_t>(v)); this->INHERITED::setValues(v); fB->setValues(v); } virtual bool checkValues(int v) { - return fVoidStar == reinterpret_cast<void*>(v) && + return fVoidStar == reinterpret_cast<void*>(static_cast<intptr_t>(v)) && fB->checkValues(v) && this->INHERITED::checkValues(v); } @@ -173,7 +165,7 @@ return new E; default: // suppress warning - return NULL; + return nullptr; } } @@ -232,11 +224,178 @@ REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue)); delete rec.fInstance; } -#if SK_ENABLE_INST_COUNT - REPORTER_ASSERT(reporter, !A::GetInstanceCount()); -#endif } } } +// GrMemoryPool requires that it's empty at the point of destruction. This helps +// achieving that by releasing all added memory in the destructor. +class AutoPoolReleaser { +public: + AutoPoolReleaser(GrMemoryPool& pool): fPool(pool) { + } + ~AutoPoolReleaser() { + for (void* ptr: fAllocated) { + fPool.release(ptr); + } + } + void add(void* ptr) { + fAllocated.push_back(ptr); + } +private: + GrMemoryPool& fPool; + SkTArray<void*> fAllocated; +}; + +DEF_TEST(GrMemoryPoolAPI, reporter) { + constexpr size_t kSmallestMinAllocSize = GrMemoryPool::kSmallestMinAllocSize; + + // Allocates memory until pool adds a new block (pool.size() changes). + auto allocateMemory = [](GrMemoryPool& pool, AutoPoolReleaser& r) { + size_t origPoolSize = pool.size(); + while (pool.size() == origPoolSize) { + r.add(pool.allocate(31)); + } + }; + + // Effective prealloc space capacity is >= kSmallestMinAllocSize. + { + GrMemoryPool pool(0, 0); + REPORTER_ASSERT(reporter, pool.preallocSize() == kSmallestMinAllocSize); + } + + // Effective prealloc space capacity is >= minAllocSize. + { + constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 2; + GrMemoryPool pool(kSmallestMinAllocSize, kMinAllocSize); + REPORTER_ASSERT(reporter, pool.preallocSize() == kMinAllocSize); + } + + // Effective block size capacity >= kSmallestMinAllocSize. + { + GrMemoryPool pool(kSmallestMinAllocSize, kSmallestMinAllocSize / 2); + AutoPoolReleaser r(pool); + + allocateMemory(pool, r); + REPORTER_ASSERT(reporter, pool.size() == kSmallestMinAllocSize); + } + + // Pool allocates exactly preallocSize on creation. + { + constexpr size_t kPreallocSize = kSmallestMinAllocSize * 5; + GrMemoryPool pool(kPreallocSize, 0); + REPORTER_ASSERT(reporter, pool.preallocSize() == kPreallocSize); + } + + // Pool allocates exactly minAllocSize when it expands. + { + constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 7; + GrMemoryPool pool(0, kMinAllocSize); + AutoPoolReleaser r(pool); + + allocateMemory(pool, r); + REPORTER_ASSERT(reporter, pool.size() == kMinAllocSize); + + allocateMemory(pool, r); + REPORTER_ASSERT(reporter, pool.size() == 2 * kMinAllocSize); + } + + // When asked to allocate amount > minAllocSize, pool allocates larger block + // to accommodate all internal structures. + { + constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 2; + GrMemoryPool pool(kSmallestMinAllocSize, kMinAllocSize); + AutoPoolReleaser r(pool); + + REPORTER_ASSERT(reporter, pool.size() == 0); + + constexpr size_t hugeSize = 10 * kMinAllocSize; + r.add(pool.allocate(hugeSize)); + REPORTER_ASSERT(reporter, pool.size() > hugeSize); + + // Block size allocated to accommodate huge request doesn't include any extra + // space, so next allocation request allocates a new block. + size_t hugeBlockSize = pool.size(); + r.add(pool.allocate(0)); + REPORTER_ASSERT(reporter, pool.size() == hugeBlockSize + kMinAllocSize); + } +} + +DEF_TEST(GrObjectMemoryPoolAPI, reporter) { + struct Data { + int value[5]; + }; + using DataObjectPool = GrObjectMemoryPool<Data>; + constexpr size_t kSmallestMinAllocCount = DataObjectPool::kSmallestMinAllocCount; + + // Allocates objects until pool adds a new block (pool.size() changes). + // Returns number of objects that fit into the current block (i.e. before pool.size() + // changed; newly allocated block always ends up with one object allocated from it). + auto allocateObjects = [](DataObjectPool& pool, AutoPoolReleaser& r) -> size_t { + size_t count = 0; + size_t origPoolSize = pool.size(); + while (pool.size() == origPoolSize) { + r.add(pool.allocate()); + count++; + } + return count - 1; + }; + + // Effective prealloc space capacity is >= kSmallestMinAllocCount. + { + DataObjectPool pool(kSmallestMinAllocCount / 3, 0); + AutoPoolReleaser r(pool); + + size_t preallocCount = allocateObjects(pool, r); + REPORTER_ASSERT(reporter, preallocCount == kSmallestMinAllocCount); + } + + // Effective prealloc space capacity is >= minAllocCount. + { + DataObjectPool pool(kSmallestMinAllocCount, 2 * kSmallestMinAllocCount); + AutoPoolReleaser r(pool); + + size_t preallocCount = allocateObjects(pool, r); + REPORTER_ASSERT(reporter, preallocCount == 2 * kSmallestMinAllocCount); + } + + // Effective block capacity is >= kSmallestMinAllocCount. + { + DataObjectPool pool(kSmallestMinAllocCount, kSmallestMinAllocCount / 2); + AutoPoolReleaser r(pool); + + // Fill prealloc space + allocateObjects(pool, r); + + size_t minAllocCount = 1 + allocateObjects(pool, r); + REPORTER_ASSERT(reporter, minAllocCount == kSmallestMinAllocCount); + } + + // Pool allocates space for exactly preallocCount objects on creation. + { + constexpr size_t kPreallocCount = kSmallestMinAllocCount * 7 / 3; + DataObjectPool pool(kPreallocCount, 0); + AutoPoolReleaser r(pool); + + size_t preallocCount = allocateObjects(pool, r); + REPORTER_ASSERT(reporter, preallocCount == kPreallocCount); + } + + // Pool allocates space for minAllocCount objects when it adds a new block. + { + constexpr size_t kMinAllocCount = kSmallestMinAllocCount * 11 / 3; + DataObjectPool pool(0, kMinAllocCount); + AutoPoolReleaser r(pool); + + // Fill prealloc space + allocateObjects(pool, r); + + size_t firstBlockCount = 1 + allocateObjects(pool, r); + REPORTER_ASSERT(reporter, firstBlockCount == kMinAllocCount); + + size_t secondBlockCount = 1 + allocateObjects(pool, r); + REPORTER_ASSERT(reporter, secondBlockCount == kMinAllocCount); + } +} + #endif
diff --git a/src/third_party/skia/tests/GrMeshTest.cpp b/src/third_party/skia/tests/GrMeshTest.cpp new file mode 100644 index 0000000..0e67acf --- /dev/null +++ b/src/third_party/skia/tests/GrMeshTest.cpp
@@ -0,0 +1,408 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTypes.h" +#include "Test.h" + +#if SK_SUPPORT_GPU + +#include "GrContext.h" +#include "GrGeometryProcessor.h" +#include "GrGpuCommandBuffer.h" +#include "GrOpFlushState.h" +#include "GrRenderTargetContext.h" +#include "GrRenderTargetContextPriv.h" +#include "GrResourceProvider.h" +#include "GrResourceKey.h" +#include "SkMakeUnique.h" +#include "glsl/GrGLSLVertexShaderBuilder.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "glsl/GrGLSLGeometryProcessor.h" +#include "glsl/GrGLSLVarying.h" +#include <array> +#include <vector> + + +GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey); + +static constexpr int kBoxSize = 2; +static constexpr int kBoxCountY = 8; +static constexpr int kBoxCountX = 8; +static constexpr int kBoxCount = kBoxCountY * kBoxCountX; + +static constexpr int kImageWidth = kBoxCountY * kBoxSize; +static constexpr int kImageHeight = kBoxCountX * kBoxSize; + +static constexpr int kIndexPatternRepeatCount = 3; +constexpr uint16_t kIndexPattern[6] = {0, 1, 2, 1, 2, 3}; + + +class DrawMeshHelper { +public: + DrawMeshHelper(GrOpFlushState* state) : fState(state) {} + + sk_sp<const GrBuffer> getIndexBuffer(); + + template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const SkTArray<T>& data) { + return this->makeVertexBuffer(data.begin(), data.count()); + } + template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const std::vector<T>& data) { + return this->makeVertexBuffer(data.data(), data.size()); + } + template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const T* data, int count); + + void drawMesh(const GrMesh& mesh); + +private: + GrOpFlushState* fState; +}; + +struct Box { + float fX, fY; + GrColor fColor; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * This is a GPU-backend specific test. It tries to test all possible usecases of GrMesh. The test + * works by drawing checkerboards of colored boxes, reading back the pixels, and comparing with + * expected results. The boxes are drawn on integer boundaries and the (opaque) colors are chosen + * from the set (r,g,b) = (0,255)^3, so the GPU renderings ought to produce exact matches. + */ + +static void run_test(const char* testName, skiatest::Reporter*, const sk_sp<GrRenderTargetContext>&, + const SkBitmap& gold, std::function<void(DrawMeshHelper*)> testFn); + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrMeshTest, reporter, ctxInfo) { + GrContext* const context = ctxInfo.grContext(); + + sk_sp<GrRenderTargetContext> rtc( + context->makeDeferredRenderTargetContext(SkBackingFit::kExact, kImageWidth, kImageHeight, + kRGBA_8888_GrPixelConfig, nullptr)); + if (!rtc) { + ERRORF(reporter, "could not create render target context."); + return; + } + + SkTArray<Box> boxes; + SkTArray<std::array<Box, 4>> vertexData; + SkBitmap gold; + + // ---- setup ---------- + + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); + gold.allocN32Pixels(kImageWidth, kImageHeight); + + SkCanvas goldCanvas(gold); + + for (int y = 0; y < kBoxCountY; ++y) { + for (int x = 0; x < kBoxCountX; ++x) { + int c = y + x; + int rgb[3] = {-(c & 1) & 0xff, -((c >> 1) & 1) & 0xff, -((c >> 2) & 1) & 0xff}; + + const Box box = boxes.push_back() = { + float(x * kBoxSize), + float(y * kBoxSize), + GrColorPackRGBA(rgb[0], rgb[1], rgb[2], 255) + }; + + std::array<Box, 4>& boxVertices = vertexData.push_back(); + for (int i = 0; i < 4; ++i) { + boxVertices[i] = { + box.fX + (i/2) * kBoxSize, + box.fY + (i%2) * kBoxSize, + box.fColor + }; + } + + paint.setARGB(255, rgb[0], rgb[1], rgb[2]); + goldCanvas.drawRect(SkRect::MakeXYWH(box.fX, box.fY, kBoxSize, kBoxSize), paint); + } + } + + goldCanvas.flush(); + + // ---- tests ---------- + +#define VALIDATE(buff) \ + if (!buff) { \ + ERRORF(reporter, #buff " is null."); \ + return; \ + } + + run_test("setNonIndexedNonInstanced", reporter, rtc, gold, [&](DrawMeshHelper* helper) { + SkTArray<Box> expandedVertexData; + for (int i = 0; i < kBoxCount; ++i) { + for (int j = 0; j < 6; ++j) { + expandedVertexData.push_back(vertexData[i][kIndexPattern[j]]); + } + } + + // Draw boxes one line at a time to exercise base vertex. + auto vbuff = helper->makeVertexBuffer(expandedVertexData); + VALIDATE(vbuff); + for (int y = 0; y < kBoxCountY; ++y) { + GrMesh mesh(GrPrimitiveType::kTriangles); + mesh.setNonIndexedNonInstanced(kBoxCountX * 6); + mesh.setVertexData(vbuff.get(), y * kBoxCountX * 6); + helper->drawMesh(mesh); + } + }); + + run_test("setIndexed", reporter, rtc, gold, [&](DrawMeshHelper* helper) { + auto ibuff = helper->getIndexBuffer(); + VALIDATE(ibuff); + auto vbuff = helper->makeVertexBuffer(vertexData); + VALIDATE(vbuff); + int baseRepetition = 0; + int i = 0; + + // Start at various repetitions within the patterned index buffer to exercise base index. + while (i < kBoxCount) { + GR_STATIC_ASSERT(kIndexPatternRepeatCount >= 3); + int repetitionCount = SkTMin(3 - baseRepetition, kBoxCount - i); + + GrMesh mesh(GrPrimitiveType::kTriangles); + mesh.setIndexed(ibuff.get(), repetitionCount * 6, baseRepetition * 6, + baseRepetition * 4, (baseRepetition + repetitionCount) * 4 - 1); + mesh.setVertexData(vbuff.get(), (i - baseRepetition) * 4); + helper->drawMesh(mesh); + + baseRepetition = (baseRepetition + 1) % 3; + i += repetitionCount; + } + }); + + run_test("setIndexedPatterned", reporter, rtc, gold, [&](DrawMeshHelper* helper) { + auto ibuff = helper->getIndexBuffer(); + VALIDATE(ibuff); + auto vbuff = helper->makeVertexBuffer(vertexData); + VALIDATE(vbuff); + + // Draw boxes one line at a time to exercise base vertex. setIndexedPatterned does not + // support a base index. + for (int y = 0; y < kBoxCountY; ++y) { + GrMesh mesh(GrPrimitiveType::kTriangles); + mesh.setIndexedPatterned(ibuff.get(), 6, 4, kBoxCountX, kIndexPatternRepeatCount); + mesh.setVertexData(vbuff.get(), y * kBoxCountX * 4); + helper->drawMesh(mesh); + } + }); + + for (bool indexed : {false, true}) { + if (!context->caps()->instanceAttribSupport()) { + break; + } + + run_test(indexed ? "setIndexedInstanced" : "setInstanced", + reporter, rtc, gold, [&](DrawMeshHelper* helper) { + auto idxbuff = indexed ? helper->getIndexBuffer() : nullptr; + auto instbuff = helper->makeVertexBuffer(boxes); + VALIDATE(instbuff); + auto vbuff = helper->makeVertexBuffer(std::vector<float>{0,0, 0,1, 1,0, 1,1}); + VALIDATE(vbuff); + auto vbuff2 = helper->makeVertexBuffer( // for testing base vertex. + std::vector<float>{-1,-1, -1,-1, 0,0, 0,1, 1,0, 1,1}); + VALIDATE(vbuff2); + + // Draw boxes one line at a time to exercise base instance, base vertex, and null vertex + // buffer. setIndexedInstanced intentionally does not support a base index. + for (int y = 0; y < kBoxCountY; ++y) { + GrMesh mesh(indexed ? GrPrimitiveType::kTriangles + : GrPrimitiveType::kTriangleStrip); + if (indexed) { + VALIDATE(idxbuff); + mesh.setIndexedInstanced(idxbuff.get(), 6, + instbuff.get(), kBoxCountX, y * kBoxCountX); + } else { + mesh.setInstanced(instbuff.get(), kBoxCountX, y * kBoxCountX, 4); + } + switch (y % 3) { + case 0: + if (context->caps()->shaderCaps()->vertexIDSupport()) { + if (y % 2) { + // We don't need this call because it's the initial state of GrMesh. + mesh.setVertexData(nullptr); + } + break; + } + // Fallthru. + case 1: + mesh.setVertexData(vbuff.get()); + break; + case 2: + mesh.setVertexData(vbuff2.get(), 2); + break; + } + helper->drawMesh(mesh); + } + }); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +class GrMeshTestOp : public GrDrawOp { +public: + DEFINE_OP_CLASS_ID + + GrMeshTestOp(std::function<void(DrawMeshHelper*)> testFn) + : INHERITED(ClassID()) + , fTestFn(testFn) { + this->setBounds(SkRect::MakeIWH(kImageWidth, kImageHeight), + HasAABloat::kNo, IsZeroArea::kNo); + } + +private: + const char* name() const override { return "GrMeshTestOp"; } + FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } + RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*) override { + return RequiresDstTexture::kNo; + } + bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; } + void onPrepare(GrOpFlushState*) override {} + void onExecute(GrOpFlushState* state) override { + DrawMeshHelper helper(state); + fTestFn(&helper); + } + + std::function<void(DrawMeshHelper*)> fTestFn; + + typedef GrDrawOp INHERITED; +}; + +class GrMeshTestProcessor : public GrGeometryProcessor { +public: + GrMeshTestProcessor(bool instanced, bool hasVertexBuffer) + : fInstanceLocation(nullptr) + , fVertex(nullptr) + , fColor(nullptr) { + if (instanced) { + fInstanceLocation = &this->addInstanceAttrib("location", kVec2f_GrVertexAttribType); + if (hasVertexBuffer) { + fVertex = &this->addVertexAttrib("vertex", kVec2f_GrVertexAttribType); + } + fColor = &this->addInstanceAttrib("color", kVec4ub_GrVertexAttribType); + } else { + fVertex = &this->addVertexAttrib("vertex", kVec2f_GrVertexAttribType); + fColor = &this->addVertexAttrib("color", kVec4ub_GrVertexAttribType); + } + this->initClassID<GrMeshTestProcessor>(); + } + + const char* name() const override { return "GrMeshTest Processor"; } + + void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final { + b->add32(SkToBool(fInstanceLocation)); + b->add32(SkToBool(fVertex)); + } + + GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final; + +protected: + const Attribute* fInstanceLocation; + const Attribute* fVertex; + const Attribute* fColor; + + friend class GLSLMeshTestProcessor; + typedef GrGeometryProcessor INHERITED; +}; + +class GLSLMeshTestProcessor : public GrGLSLGeometryProcessor { + void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&, + FPCoordTransformIter&& transformIter) final {} + + void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final { + const GrMeshTestProcessor& mp = args.fGP.cast<GrMeshTestProcessor>(); + + GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + varyingHandler->emitAttributes(mp); + varyingHandler->addPassThroughAttribute(mp.fColor, args.fOutputColor); + + GrGLSLVertexBuilder* v = args.fVertBuilder; + if (!mp.fInstanceLocation) { + v->codeAppendf("vec2 vertex = %s;", mp.fVertex->fName); + } else { + if (mp.fVertex) { + v->codeAppendf("vec2 offset = %s;", mp.fVertex->fName); + } else { + v->codeAppend ("vec2 offset = vec2(sk_VertexID / 2, sk_VertexID % 2);"); + } + v->codeAppendf("vec2 vertex = %s + offset * %i;", + mp.fInstanceLocation->fName, kBoxSize); + } + gpArgs->fPositionVar.set(kVec2f_GrSLType, "vertex"); + + GrGLSLPPFragmentBuilder* f = args.fFragBuilder; + f->codeAppendf("%s = vec4(1);", args.fOutputCoverage); + } +}; + +GrGLSLPrimitiveProcessor* GrMeshTestProcessor::createGLSLInstance(const GrShaderCaps&) const { + return new GLSLMeshTestProcessor; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +template<typename T> +sk_sp<const GrBuffer> DrawMeshHelper::makeVertexBuffer(const T* data, int count) { + return sk_sp<const GrBuffer>( + fState->resourceProvider()->createBuffer( + count * sizeof(T), kVertex_GrBufferType, kDynamic_GrAccessPattern, + GrResourceProvider::kNoPendingIO_Flag | + GrResourceProvider::kRequireGpuMemory_Flag, data)); +} + +sk_sp<const GrBuffer> DrawMeshHelper::getIndexBuffer() { + GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey); + return sk_sp<const GrBuffer>( + fState->resourceProvider()->findOrCreatePatternedIndexBuffer( + kIndexPattern, 6, kIndexPatternRepeatCount, 4, gIndexBufferKey)); +} + +void DrawMeshHelper::drawMesh(const GrMesh& mesh) { + GrRenderTarget* rt = fState->drawOpArgs().fRenderTarget; + GrPipeline pipeline(rt, GrPipeline::ScissorState::kDisabled, SkBlendMode::kSrc); + GrMeshTestProcessor mtp(mesh.isInstanced(), mesh.hasVertexData()); + fState->commandBuffer()->draw(pipeline, mtp, &mesh, nullptr, 1, + SkRect::MakeIWH(kImageWidth, kImageHeight)); +} + +static void run_test(const char* testName, skiatest::Reporter* reporter, + const sk_sp<GrRenderTargetContext>& rtc, const SkBitmap& gold, + std::function<void(DrawMeshHelper*)> testFn) { + const int w = gold.width(), h = gold.height(), rowBytes = gold.rowBytes(); + const uint32_t* goldPx = reinterpret_cast<const uint32_t*>(gold.getPixels()); + if (h != rtc->height() || w != rtc->width()) { + ERRORF(reporter, "[%s] expectation and rtc not compatible (?).", testName); + return; + } + if (sizeof(uint32_t) * kImageWidth != gold.rowBytes()) { + ERRORF(reporter, "unexpected row bytes in gold image.", testName); + return; + } + + SkAutoSTMalloc<kImageHeight * kImageWidth, uint32_t> resultPx(h * rowBytes); + rtc->clear(nullptr, 0xbaaaaaad, true); + rtc->priv().testingOnly_addDrawOp(skstd::make_unique<GrMeshTestOp>(testFn)); + rtc->readPixels(gold.info(), resultPx, rowBytes, 0, 0, 0); + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + uint32_t expected = goldPx[y * kImageWidth + x]; + uint32_t actual = resultPx[y * kImageWidth + x]; + if (expected != actual) { + ERRORF(reporter, "[%s] pixel (%i,%i): got 0x%x expected 0x%x", + testName, x, y, actual, expected); + return; + } + } + } +} + +#endif
diff --git a/src/third_party/skia/tests/GrOrderedSetTest.cpp b/src/third_party/skia/tests/GrOrderedSetTest.cpp deleted file mode 100644 index 7b3db9d..0000000 --- a/src/third_party/skia/tests/GrOrderedSetTest.cpp +++ /dev/null
@@ -1,149 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkRandom.h" -#include "Test.h" -// This is a GPU-backend specific test -#if SK_SUPPORT_GPU -#include "GrOrderedSet.h" - -typedef GrOrderedSet<int> Set; -typedef GrOrderedSet<const char*, GrStrLess> Set2; - -DEF_TEST(GrOrderedSet, reporter) { - Set set; - - REPORTER_ASSERT(reporter, set.empty()); - - SkRandom r; - - int count[1000] = {0}; - // add 10K ints - for (int i = 0; i < 10000; ++i) { - int x = r.nextU() % 1000; - Set::Iter xi = set.insert(x); - REPORTER_ASSERT(reporter, *xi == x); - REPORTER_ASSERT(reporter, !set.empty()); - count[x] = 1; - } - set.insert(0); - count[0] = 1; - set.insert(999); - count[999] = 1; - int totalCount = 0; - for (int i = 0; i < 1000; ++i) { - totalCount += count[i]; - } - REPORTER_ASSERT(reporter, *set.begin() == 0); - REPORTER_ASSERT(reporter, *set.last() == 999); - REPORTER_ASSERT(reporter, --(++set.begin()) == set.begin()); - REPORTER_ASSERT(reporter, --set.end() == set.last()); - REPORTER_ASSERT(reporter, set.count() == totalCount); - - int c = 0; - // check that we iterate through the correct number of - // elements and they are properly sorted. - for (Set::Iter a = set.begin(); set.end() != a; ++a) { - Set::Iter b = a; - ++b; - ++c; - REPORTER_ASSERT(reporter, b == set.end() || *a <= *b); - } - REPORTER_ASSERT(reporter, c == set.count()); - - // check that the set finds all ints and only ints added to set - for (int i = 0; i < 1000; ++i) { - bool existsFind = set.find(i) != set.end(); - bool existsCount = 0 != count[i]; - REPORTER_ASSERT(reporter, existsFind == existsCount); - } - // remove all the ints between 100 and 200. - for (int i = 100; i < 200; ++i) { - set.remove(set.find(i)); - if (1 == count[i]) { - count[i] = 0; - --totalCount; - } - REPORTER_ASSERT(reporter, set.count() == totalCount); - REPORTER_ASSERT(reporter, set.find(i) == set.end()); - } - // remove the 0 entry. (tests removing begin()) - REPORTER_ASSERT(reporter, *set.begin() == 0); - REPORTER_ASSERT(reporter, *(--set.end()) == 999); - set.remove(set.find(0)); - count[0] = 0; - --totalCount; - REPORTER_ASSERT(reporter, set.count() == totalCount); - REPORTER_ASSERT(reporter, set.find(0) == set.end()); - REPORTER_ASSERT(reporter, 0 < *set.begin()); - - // remove all the 999 entries (tests removing last()). - set.remove(set.find(999)); - count[999] = 0; - --totalCount; - REPORTER_ASSERT(reporter, set.count() == totalCount); - REPORTER_ASSERT(reporter, set.find(999) == set.end()); - REPORTER_ASSERT(reporter, 999 > *(--set.end())); - REPORTER_ASSERT(reporter, set.last() == --set.end()); - - // Make sure iteration still goes through correct number of entries - // and is still sorted correctly. - c = 0; - for (Set::Iter a = set.begin(); set.end() != a; ++a) { - Set::Iter b = a; - ++b; - ++c; - REPORTER_ASSERT(reporter, b == set.end() || *a <= *b); - } - REPORTER_ASSERT(reporter, c == set.count()); - - // repeat check that the set finds all ints and only ints added to set - for (int i = 0; i < 1000; ++i) { - bool existsFind = set.find(i) != set.end(); - bool existsCount = 0 != count[i]; - REPORTER_ASSERT(reporter, existsFind == existsCount); - } - - // remove all entries - while (!set.empty()) { - set.remove(set.begin()); - } - - // test reset on empty set. - set.reset(); - REPORTER_ASSERT(reporter, set.empty()); - - - // test using c strings - const char* char1 = "dog"; - const char* char2 = "cat"; - const char* char3 = "dog"; - - Set2 set2; - - set2.insert("ape"); - set2.insert(char1); - set2.insert(char2); - set2.insert(char3); - set2.insert("ant"); - set2.insert("cat"); - - REPORTER_ASSERT(reporter, set2.count() == 4); - REPORTER_ASSERT(reporter, set2.find("dog") == set2.last()); - REPORTER_ASSERT(reporter, set2.find("cat") != set2.end()); - REPORTER_ASSERT(reporter, set2.find("ant") == set2.begin()); - REPORTER_ASSERT(reporter, set2.find("bug") == set2.end()); - - set2.remove(set2.find("ant")); - REPORTER_ASSERT(reporter, set2.find("ant") == set2.end()); - REPORTER_ASSERT(reporter, set2.count() == 3); - - set2.reset(); - REPORTER_ASSERT(reporter, set2.empty()); -} - -#endif
diff --git a/src/third_party/skia/tests/GrPipelineDynamicStateTest.cpp b/src/third_party/skia/tests/GrPipelineDynamicStateTest.cpp new file mode 100644 index 0000000..007702a --- /dev/null +++ b/src/third_party/skia/tests/GrPipelineDynamicStateTest.cpp
@@ -0,0 +1,222 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTypes.h" +#include "Test.h" + +#if SK_SUPPORT_GPU + +#include "GrContext.h" +#include "GrColor.h" +#include "GrGeometryProcessor.h" +#include "GrGpuCommandBuffer.h" +#include "GrOpFlushState.h" +#include "GrRenderTargetContext.h" +#include "GrRenderTargetContextPriv.h" +#include "GrResourceProvider.h" +#include "SkMakeUnique.h" +#include "glsl/GrGLSLVertexShaderBuilder.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "glsl/GrGLSLGeometryProcessor.h" +#include "glsl/GrGLSLVarying.h" + +/** + * This is a GPU-backend specific test for dynamic pipeline state. It draws boxes using dynamic + * scissor rectangles then reads back the result to verify a successful test. + */ + +using ScissorState = GrPipeline::ScissorState; + +static constexpr int kScreenSize = 6; +static constexpr int kNumMeshes = 4; +static constexpr int kScreenSplitX = kScreenSize/2; +static constexpr int kScreenSplitY = kScreenSize/2; + +static const GrPipeline::DynamicState kDynamicStates[kNumMeshes] = { + {SkIRect::MakeLTRB(0, 0, kScreenSplitX, kScreenSplitY)}, + {SkIRect::MakeLTRB(0, kScreenSplitY, kScreenSplitX, kScreenSize)}, + {SkIRect::MakeLTRB(kScreenSplitX, 0, kScreenSize, kScreenSplitY)}, + {SkIRect::MakeLTRB(kScreenSplitX, kScreenSplitY, kScreenSize, kScreenSize)}, +}; + +static const GrColor kMeshColors[kNumMeshes] { + GrColorPackRGBA(255, 0, 0, 255), + GrColorPackRGBA(0, 255, 0, 255), + GrColorPackRGBA(0, 0, 255, 255), + GrColorPackRGBA(0, 0, 0, 255) +}; + +struct Vertex { + float fX; + float fY; + GrColor fColor; +}; + +class GrPipelineDynamicStateTestProcessor : public GrGeometryProcessor { +public: + GrPipelineDynamicStateTestProcessor() + : fVertex(this->addVertexAttrib("vertex", kVec2f_GrVertexAttribType)) + , fColor(this->addVertexAttrib("color", kVec4ub_GrVertexAttribType)) { + this->initClassID<GrPipelineDynamicStateTestProcessor>(); + } + + const char* name() const override { return "GrPipelineDynamicStateTest Processor"; } + + void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {} + + GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final; + +protected: + const Attribute& fVertex; + const Attribute& fColor; + + friend class GLSLPipelineDynamicStateTestProcessor; + typedef GrGeometryProcessor INHERITED; +}; + +class GLSLPipelineDynamicStateTestProcessor : public GrGLSLGeometryProcessor { + void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&, + FPCoordTransformIter&& transformIter) final {} + + void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final { + const GrPipelineDynamicStateTestProcessor& mp = + args.fGP.cast<GrPipelineDynamicStateTestProcessor>(); + + GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + varyingHandler->emitAttributes(mp); + varyingHandler->addPassThroughAttribute(&mp.fColor, args.fOutputColor); + + GrGLSLVertexBuilder* v = args.fVertBuilder; + v->codeAppendf("vec2 vertex = %s;", mp.fVertex.fName); + gpArgs->fPositionVar.set(kVec2f_GrSLType, "vertex"); + + GrGLSLPPFragmentBuilder* f = args.fFragBuilder; + f->codeAppendf("%s = vec4(1);", args.fOutputCoverage); + } +}; + +GrGLSLPrimitiveProcessor* +GrPipelineDynamicStateTestProcessor::createGLSLInstance(const GrShaderCaps&) const { + return new GLSLPipelineDynamicStateTestProcessor; +} + +class GrPipelineDynamicStateTestOp : public GrDrawOp { +public: + DEFINE_OP_CLASS_ID + + GrPipelineDynamicStateTestOp(ScissorState scissorState, sk_sp<const GrBuffer> vbuff) + : INHERITED(ClassID()) + , fScissorState(scissorState) + , fVertexBuffer(std::move(vbuff)) { + this->setBounds(SkRect::MakeIWH(kScreenSize, kScreenSize), + HasAABloat::kNo, IsZeroArea::kNo); + } + +private: + const char* name() const override { return "GrPipelineDynamicStateTestOp"; } + FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } + RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*) override { + return RequiresDstTexture::kNo; + } + bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; } + void onPrepare(GrOpFlushState*) override {} + void onExecute(GrOpFlushState* state) override { + GrRenderTarget* rt = state->drawOpArgs().fRenderTarget; + GrPipeline pipeline(rt, fScissorState, SkBlendMode::kSrc); + SkSTArray<kNumMeshes, GrMesh> meshes; + for (int i = 0; i < kNumMeshes; ++i) { + GrMesh& mesh = meshes.emplace_back(GrPrimitiveType::kTriangleStrip); + mesh.setNonIndexedNonInstanced(4); + mesh.setVertexData(fVertexBuffer.get(), 4 * i); + } + state->commandBuffer()->draw(pipeline, GrPipelineDynamicStateTestProcessor(), + meshes.begin(), kDynamicStates, 4, + SkRect::MakeIWH(kScreenSize, kScreenSize)); + } + + ScissorState fScissorState; + const sk_sp<const GrBuffer> fVertexBuffer; + + typedef GrDrawOp INHERITED; +}; + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrPipelineDynamicStateTest, reporter, ctxInfo) { + GrContext* const context = ctxInfo.grContext(); + GrResourceProvider* rp = context->resourceProvider(); + + sk_sp<GrRenderTargetContext> rtc( + context->makeDeferredRenderTargetContext(SkBackingFit::kExact, kScreenSize, kScreenSize, + kRGBA_8888_GrPixelConfig, nullptr)); + if (!rtc) { + ERRORF(reporter, "could not create render target context."); + return; + } + + constexpr float d = (float) kScreenSize; + Vertex vdata[kNumMeshes * 4] = { + {0, 0, kMeshColors[0]}, + {0, d, kMeshColors[0]}, + {d, 0, kMeshColors[0]}, + {d, d, kMeshColors[0]}, + + {0, 0, kMeshColors[1]}, + {0, d, kMeshColors[1]}, + {d, 0, kMeshColors[1]}, + {d, d, kMeshColors[1]}, + + {0, 0, kMeshColors[2]}, + {0, d, kMeshColors[2]}, + {d, 0, kMeshColors[2]}, + {d, d, kMeshColors[2]}, + + {0, 0, kMeshColors[3]}, + {0, d, kMeshColors[3]}, + {d, 0, kMeshColors[3]}, + {d, d, kMeshColors[3]} + }; + + sk_sp<const GrBuffer> vbuff(rp->createBuffer(sizeof(vdata), kVertex_GrBufferType, + kDynamic_GrAccessPattern, + GrResourceProvider::kNoPendingIO_Flag | + GrResourceProvider::kRequireGpuMemory_Flag, + vdata)); + if (!vbuff) { + ERRORF(reporter, "vbuff is null."); + return; + } + + uint32_t resultPx[kScreenSize * kScreenSize]; + + for (ScissorState scissorState : {ScissorState::kEnabled, ScissorState::kDisabled}) { + rtc->clear(nullptr, 0xbaaaaaad, true); + rtc->priv().testingOnly_addDrawOp( + skstd::make_unique<GrPipelineDynamicStateTestOp>(scissorState, vbuff)); + rtc->readPixels(SkImageInfo::Make(kScreenSize, kScreenSize, + kRGBA_8888_SkColorType, kPremul_SkAlphaType), + resultPx, 4 * kScreenSize, 0, 0, 0); + for (int y = 0; y < kScreenSize; ++y) { + for (int x = 0; x < kScreenSize; ++x) { + int expectedColorIdx; + if (ScissorState::kEnabled == scissorState) { + expectedColorIdx = (x < kScreenSplitX ? 0 : 2) + (y < kScreenSplitY ? 0 : 1); + } else { + expectedColorIdx = kNumMeshes - 1; + } + uint32_t expected = kMeshColors[expectedColorIdx]; + uint32_t actual = resultPx[y * kScreenSize + x]; + if (expected != actual) { + ERRORF(reporter, "[scissor=%s] pixel (%i,%i): got 0x%x expected 0x%x", + ScissorState::kEnabled == scissorState ? "enabled" : "disabled", x, y, + actual, expected); + return; + } + } + } + } +} + +#endif
diff --git a/src/third_party/skia/tests/GrPorterDuffTest.cpp b/src/third_party/skia/tests/GrPorterDuffTest.cpp new file mode 100644 index 0000000..37972d9 --- /dev/null +++ b/src/third_party/skia/tests/GrPorterDuffTest.cpp
@@ -0,0 +1,1102 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#if SK_SUPPORT_GPU + +#include "GrContextFactory.h" +#include "GrContextOptions.h" +#include "GrContextPriv.h" +#include "GrGpu.h" +#include "GrTest.h" +#include "GrXferProcessor.h" +#include "effects/GrPorterDuffXferProcessor.h" +#include "gl/GrGLCaps.h" +#include "ops/GrMeshDrawOp.h" + +//////////////////////////////////////////////////////////////////////////////// + +static void test_color_unknown_with_coverage(skiatest::Reporter* reporter, const GrCaps& caps); +static void test_color_not_opaque_no_coverage(skiatest::Reporter* reporter, const GrCaps& caps); +static void test_color_opaque_with_coverage(skiatest::Reporter* reporter, const GrCaps& caps); +static void test_color_opaque_no_coverage(skiatest::Reporter* reporter, const GrCaps& caps); +static void test_lcd_coverage(skiatest::Reporter* reporter, const GrCaps& caps); +static void test_lcd_coverage_fallback_case(skiatest::Reporter* reporter, const GrCaps& caps); + +DEF_GPUTEST_FOR_NULLGL_CONTEXT(GrPorterDuff, reporter, ctxInfo) { + const GrCaps& caps = *ctxInfo.grContext()->getGpu()->caps(); + if (!caps.shaderCaps()->dualSourceBlendingSupport()) { + SkFAIL("Null context does not support dual source blending."); + return; + } + + test_color_unknown_with_coverage(reporter, caps); + test_color_not_opaque_no_coverage(reporter, caps); + test_color_opaque_with_coverage(reporter, caps); + test_color_opaque_no_coverage(reporter, caps); + test_lcd_coverage(reporter, caps); + test_lcd_coverage_fallback_case(reporter, caps); +} + +//////////////////////////////////////////////////////////////////////////////// + +#define TEST_ASSERT(...) REPORTER_ASSERT(reporter, __VA_ARGS__) + +enum { + kNone_OutputType, + kCoverage_OutputType, + kModulate_OutputType, + kSAModulate_OutputType, + kISAModulate_OutputType, + kISCModulate_OutputType +}; +static const int kInvalid_OutputType = -1; + +static GrProcessorSet::Analysis do_analysis(const GrXPFactory* xpf, + const GrProcessorAnalysisColor& colorInput, + GrProcessorAnalysisCoverage coverageInput, + const GrCaps& caps) { + GrPaint paint; + paint.setXPFactory(xpf); + GrProcessorSet procs(std::move(paint)); + GrColor overrideColor; + GrProcessorSet::Analysis analysis = + procs.finalize(colorInput, coverageInput, nullptr, false, caps, &overrideColor); + return analysis; +} + +class GrPorterDuffTest { +public: + struct XPInfo { + XPInfo(skiatest::Reporter* reporter, SkBlendMode xfermode, const GrCaps& caps, + GrProcessorAnalysisColor inputColor, GrProcessorAnalysisCoverage inputCoverage) { + const GrXPFactory* xpf = GrPorterDuffXPFactory::Get(xfermode); + + bool isLCD = GrProcessorAnalysisCoverage::kLCD == inputCoverage; + + GrProcessorSet::Analysis analysis = do_analysis(xpf, inputColor, inputCoverage, caps); + fCompatibleWithCoverageAsAlpha = analysis.isCompatibleWithCoverageAsAlpha(); + fCanCombineOverlappedStencilAndCover = analysis.canCombineOverlappedStencilAndCover(); + fIgnoresInputColor = analysis.inputColorIsIgnored(); + sk_sp<const GrXferProcessor> xp( + GrXPFactory::MakeXferProcessor(xpf, inputColor, inputCoverage, false, caps)); + TEST_ASSERT(!analysis.requiresDstTexture() || + (isLCD && + !caps.shaderCaps()->dstReadInShaderSupport() && + (SkBlendMode::kSrcOver != xfermode || + !inputColor.isOpaque()))); + GetXPOutputTypes(xp.get(), &fPrimaryOutputType, &fSecondaryOutputType); + xp->getBlendInfo(&fBlendInfo); + TEST_ASSERT(!xp->willReadDstColor() || + (isLCD && (SkBlendMode::kSrcOver != xfermode || + !inputColor.isOpaque()))); + TEST_ASSERT(xp->hasSecondaryOutput() == GrBlendCoeffRefsSrc2(fBlendInfo.fDstBlend)); + } + + bool fCanCombineOverlappedStencilAndCover; + bool fCompatibleWithCoverageAsAlpha; + bool fIgnoresInputColor; + int fPrimaryOutputType; + int fSecondaryOutputType; + GrXferProcessor::BlendInfo fBlendInfo; + }; + + static void GetXPOutputTypes(const GrXferProcessor* xp, int* outPrimary, int* outSecondary) { + GrPorterDuffXPFactory::TestGetXPOutputTypes(xp, outPrimary, outSecondary); + } +}; + +static void test_lcd_coverage(skiatest::Reporter* reporter, const GrCaps& caps) { + GrProcessorAnalysisColor inputColor = GrProcessorAnalysisColor::Opaque::kYes; + GrProcessorAnalysisCoverage inputCoverage = GrProcessorAnalysisCoverage::kLCD; + + for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) { + SkBlendMode xfermode = static_cast<SkBlendMode>(m); + const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, inputColor, inputCoverage); + switch (xfermode) { + case SkBlendMode::kClear: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrc: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDst: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcOver: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kSAModulate_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kIS2C_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstOver: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcIn: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstIn: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcOut: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstOut: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcATop: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstATop: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kXor: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kPlus: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kModulate: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kScreen: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + default: + ERRORF(reporter, "Invalid xfermode."); + break; + } + } +} +static void test_color_unknown_with_coverage(skiatest::Reporter* reporter, const GrCaps& caps) { + GrProcessorAnalysisColor inputColor = GrProcessorAnalysisColor::Opaque::kNo; + GrProcessorAnalysisCoverage inputCoverage = GrProcessorAnalysisCoverage::kSingleChannel; + + for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) { + SkBlendMode xfermode = static_cast<SkBlendMode>(m); + const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, inputColor, inputCoverage); + switch (xfermode) { + case SkBlendMode::kClear: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kCoverage_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrc: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kCoverage_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kIS2A_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDst: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(!xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcOver: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstOver: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcIn: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kCoverage_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kIS2A_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstIn: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kISAModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcOut: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kCoverage_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kIS2A_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstOut: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcATop: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstATop: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kISAModulate_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kIS2C_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kXor: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kPlus: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kModulate: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kISCModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kScreen: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + default: + ERRORF(reporter, "Invalid xfermode."); + break; + } + } +} + +static void test_color_not_opaque_no_coverage(skiatest::Reporter* reporter, const GrCaps& caps) { + GrProcessorAnalysisColor inputColor(GrColorPackRGBA(229, 0, 154, 240)); + GrProcessorAnalysisCoverage inputCoverage = GrProcessorAnalysisCoverage::kNone; + + for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) { + SkBlendMode xfermode = static_cast<SkBlendMode>(m); + const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, inputColor, inputCoverage); + switch (xfermode) { + case SkBlendMode::kClear: + TEST_ASSERT(xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrc: + TEST_ASSERT(xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDst: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(!xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcOver: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstOver: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcIn: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstIn: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kSA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcOut: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstOut: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcATop: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstATop: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kSA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kXor: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kPlus: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kModulate: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kSC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kScreen: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + default: + ERRORF(reporter, "Invalid xfermode."); + break; + } + } +} + +static void test_color_opaque_with_coverage(skiatest::Reporter* reporter, const GrCaps& caps) { + GrProcessorAnalysisColor inputColor = GrProcessorAnalysisColor::Opaque::kYes; + GrProcessorAnalysisCoverage inputCoverage = GrProcessorAnalysisCoverage::kSingleChannel; + + for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) { + SkBlendMode xfermode = static_cast<SkBlendMode>(m); + const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, inputColor, inputCoverage); + switch (xfermode) { + case SkBlendMode::kClear: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kCoverage_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrc: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDst: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(!xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcOver: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstOver: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcIn: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstIn: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(!xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcOut: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstOut: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kCoverage_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcATop: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstATop: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kXor: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kPlus: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kModulate: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(kISCModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kScreen: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + default: + ERRORF(reporter, "Invalid xfermode."); + break; + } + } +} + +static void test_color_opaque_no_coverage(skiatest::Reporter* reporter, const GrCaps& caps) { + GrProcessorAnalysisColor inputColor = GrProcessorAnalysisColor::Opaque::kYes; + GrProcessorAnalysisCoverage inputCoverage = GrProcessorAnalysisCoverage::kNone; + + for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) { + SkBlendMode xfermode = static_cast<SkBlendMode>(m); + const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, inputColor, inputCoverage); + + switch (xfermode) { + case SkBlendMode::kClear: + TEST_ASSERT(xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrc: + TEST_ASSERT(xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDst: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(!xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcOver: + // We don't specialize opaque src-over. See note in GrPorterDuffXferProcessor.cpp + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstOver: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcIn: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstIn: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(!xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcOut: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstOut: + TEST_ASSERT(xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kSrcATop: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kDstATop: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kXor: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kPlus: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kModulate: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kSC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + case SkBlendMode::kScreen: + TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover); + TEST_ASSERT(!xpi.fIgnoresInputColor); + TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha); + TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType); + TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType); + TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation); + TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend); + TEST_ASSERT(kISC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend); + TEST_ASSERT(xpi.fBlendInfo.fWriteColor); + break; + default: + ERRORF(reporter, "Invalid xfermode."); + break; + } + } +} + +static void test_lcd_coverage_fallback_case(skiatest::Reporter* reporter, const GrCaps& caps) { + const GrXPFactory* xpf = GrPorterDuffXPFactory::Get(SkBlendMode::kSrcOver); + GrProcessorAnalysisColor color = GrColorPackRGBA(123, 45, 67, 255); + GrProcessorAnalysisCoverage coverage = GrProcessorAnalysisCoverage::kLCD; + TEST_ASSERT(!(GrXPFactory::GetAnalysisProperties(xpf, color, coverage, caps) & + GrXPFactory::AnalysisProperties::kRequiresDstTexture)); + sk_sp<const GrXferProcessor> xp_opaque( + GrXPFactory::MakeXferProcessor(xpf, color, coverage, false, caps)); + if (!xp_opaque) { + ERRORF(reporter, "Failed to create an XP with LCD coverage."); + return; + } + + GrXferProcessor::BlendInfo blendInfo; + xp_opaque->getBlendInfo(&blendInfo); + TEST_ASSERT(blendInfo.fWriteColor); + + // Test with non-opaque alpha + color = GrColorPackRGBA(123, 45, 67, 221); + coverage = GrProcessorAnalysisCoverage::kLCD; + TEST_ASSERT(!(GrXPFactory::GetAnalysisProperties(xpf, color, coverage, caps) & + GrXPFactory::AnalysisProperties::kRequiresDstTexture)); + sk_sp<const GrXferProcessor> xp( + GrXPFactory::MakeXferProcessor(xpf, color, coverage, false, caps)); + if (!xp) { + ERRORF(reporter, "Failed to create an XP with LCD coverage."); + return; + } + + xp->getBlendInfo(&blendInfo); + TEST_ASSERT(blendInfo.fWriteColor); +} + +DEF_GPUTEST(PorterDuffNoDualSourceBlending, reporter, /*factory*/) { + GrContextOptions opts; + opts.fSuppressDualSourceBlending = true; + sk_gpu_test::GrContextFactory mockFactory(opts); + GrContext* ctx = mockFactory.get(sk_gpu_test::GrContextFactory::kNullGL_ContextType); + if (!ctx) { + SkFAIL("Failed to create null context without ARB_blend_func_extended."); + return; + } + + const GrCaps& caps = *ctx->caps(); + if (caps.shaderCaps()->dualSourceBlendingSupport()) { + SkFAIL("Null context failed to honor request for no ARB_blend_func_extended."); + return; + } + + GrBackendObject backendTexHandle = + ctx->getGpu()->createTestingOnlyBackendTexture(nullptr, 100, 100, kRGBA_8888_GrPixelConfig); + GrBackendTexture backendTex = GrTest::CreateBackendTexture(ctx->contextPriv().getBackend(), + 100, + 100, + kRGBA_8888_GrPixelConfig, + backendTexHandle); + + GrXferProcessor::DstProxy fakeDstProxy; + { + sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeWrappedBackend(ctx, backendTex, + kTopLeft_GrSurfaceOrigin); + fakeDstProxy.setProxy(std::move(proxy)); + } + + static const GrProcessorAnalysisColor colorInputs[] = { + GrProcessorAnalysisColor::Opaque::kNo, GrProcessorAnalysisColor::Opaque::kYes, + GrProcessorAnalysisColor(GrColorPackRGBA(0, 82, 17, 100)), + GrProcessorAnalysisColor(GrColorPackRGBA(0, 82, 17, 255))}; + + for (const auto& colorInput : colorInputs) { + for (GrProcessorAnalysisCoverage coverageType : + {GrProcessorAnalysisCoverage::kSingleChannel, GrProcessorAnalysisCoverage::kNone}) { + for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) { + SkBlendMode xfermode = static_cast<SkBlendMode>(m); + const GrXPFactory* xpf = GrPorterDuffXPFactory::Get(xfermode); + sk_sp<const GrXferProcessor> xp( + GrXPFactory::MakeXferProcessor(xpf, colorInput, coverageType, false, caps)); + if (!xp) { + ERRORF(reporter, "Failed to create an XP without dual source blending."); + return; + } + TEST_ASSERT(!xp->hasSecondaryOutput()); + } + } + } + ctx->getGpu()->deleteTestingOnlyBackendTexture(backendTexHandle); +} + +#endif
diff --git a/src/third_party/skia/tests/GrRedBlackTreeTest.cpp b/src/third_party/skia/tests/GrRedBlackTreeTest.cpp deleted file mode 100644 index c517cf2..0000000 --- a/src/third_party/skia/tests/GrRedBlackTreeTest.cpp +++ /dev/null
@@ -1,185 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -// This is a GPU-backend specific test -#if SK_SUPPORT_GPU - -#include "GrRedBlackTree.h" -#include "SkRandom.h" -#include "Test.h" - -typedef GrRedBlackTree<int> Tree; - -DEF_TEST(GrRedBlackTree, reporter) { - Tree tree; - - SkRandom r; - - int count[100] = {0}; - // add 10K ints - for (int i = 0; i < 10000; ++i) { - int x = r.nextU() % 100; - Tree::Iter xi = tree.insert(x); - REPORTER_ASSERT(reporter, *xi == x); - ++count[x]; - } - - tree.insert(0); - ++count[0]; - tree.insert(99); - ++count[99]; - REPORTER_ASSERT(reporter, *tree.begin() == 0); - REPORTER_ASSERT(reporter, *tree.last() == 99); - REPORTER_ASSERT(reporter, --(++tree.begin()) == tree.begin()); - REPORTER_ASSERT(reporter, --tree.end() == tree.last()); - REPORTER_ASSERT(reporter, tree.count() == 10002); - - int c = 0; - // check that we iterate through the correct number of - // elements and they are properly sorted. - for (Tree::Iter a = tree.begin(); tree.end() != a; ++a) { - Tree::Iter b = a; - ++b; - ++c; - REPORTER_ASSERT(reporter, b == tree.end() || *a <= *b); - } - REPORTER_ASSERT(reporter, c == tree.count()); - - // check that the tree reports the correct number of each int - // and that we can iterate through them correctly both forward - // and backward. - for (int i = 0; i < 100; ++i) { - int c; - c = tree.countOf(i); - REPORTER_ASSERT(reporter, c == count[i]); - c = 0; - Tree::Iter iter = tree.findFirst(i); - while (iter != tree.end() && *iter == i) { - ++c; - ++iter; - } - REPORTER_ASSERT(reporter, count[i] == c); - c = 0; - iter = tree.findLast(i); - if (iter != tree.end()) { - do { - if (*iter == i) { - ++c; - } else { - break; - } - if (iter != tree.begin()) { - --iter; - } else { - break; - } - } while (true); - } - REPORTER_ASSERT(reporter, c == count[i]); - } - // remove all the ints between 25 and 74. Randomly chose to remove - // the first, last, or any entry for each. - for (int i = 25; i < 75; ++i) { - while (0 != tree.countOf(i)) { - --count[i]; - int x = r.nextU() % 3; - Tree::Iter iter; - switch (x) { - case 0: - iter = tree.findFirst(i); - break; - case 1: - iter = tree.findLast(i); - break; - case 2: - default: - iter = tree.find(i); - break; - } - tree.remove(iter); - } - REPORTER_ASSERT(reporter, 0 == count[i]); - REPORTER_ASSERT(reporter, tree.findFirst(i) == tree.end()); - REPORTER_ASSERT(reporter, tree.findLast(i) == tree.end()); - REPORTER_ASSERT(reporter, tree.find(i) == tree.end()); - } - // remove all of the 0 entries. (tests removing begin()) - REPORTER_ASSERT(reporter, *tree.begin() == 0); - REPORTER_ASSERT(reporter, *(--tree.end()) == 99); - while (0 != tree.countOf(0)) { - --count[0]; - tree.remove(tree.find(0)); - } - REPORTER_ASSERT(reporter, 0 == count[0]); - REPORTER_ASSERT(reporter, tree.findFirst(0) == tree.end()); - REPORTER_ASSERT(reporter, tree.findLast(0) == tree.end()); - REPORTER_ASSERT(reporter, tree.find(0) == tree.end()); - REPORTER_ASSERT(reporter, 0 < *tree.begin()); - - // remove all the 99 entries (tests removing last()). - while (0 != tree.countOf(99)) { - --count[99]; - tree.remove(tree.find(99)); - } - REPORTER_ASSERT(reporter, 0 == count[99]); - REPORTER_ASSERT(reporter, tree.findFirst(99) == tree.end()); - REPORTER_ASSERT(reporter, tree.findLast(99) == tree.end()); - REPORTER_ASSERT(reporter, tree.find(99) == tree.end()); - REPORTER_ASSERT(reporter, 99 > *(--tree.end())); - REPORTER_ASSERT(reporter, tree.last() == --tree.end()); - - // Make sure iteration still goes through correct number of entries - // and is still sorted correctly. - c = 0; - for (Tree::Iter a = tree.begin(); tree.end() != a; ++a) { - Tree::Iter b = a; - ++b; - ++c; - REPORTER_ASSERT(reporter, b == tree.end() || *a <= *b); - } - REPORTER_ASSERT(reporter, c == tree.count()); - - // repeat check that correct number of each entry is in the tree - // and iterates correctly both forward and backward. - for (int i = 0; i < 100; ++i) { - REPORTER_ASSERT(reporter, tree.countOf(i) == count[i]); - int c = 0; - Tree::Iter iter = tree.findFirst(i); - while (iter != tree.end() && *iter == i) { - ++c; - ++iter; - } - REPORTER_ASSERT(reporter, count[i] == c); - c = 0; - iter = tree.findLast(i); - if (iter != tree.end()) { - do { - if (*iter == i) { - ++c; - } else { - break; - } - if (iter != tree.begin()) { - --iter; - } else { - break; - } - } while (true); - } - REPORTER_ASSERT(reporter, count[i] == c); - } - - // remove all entries - while (!tree.empty()) { - tree.remove(tree.begin()); - } - - // test reset on empty tree. - tree.reset(); -} - -#endif
diff --git a/src/third_party/skia/tests/GrSKSLPrettyPrintTest.cpp b/src/third_party/skia/tests/GrSKSLPrettyPrintTest.cpp new file mode 100644 index 0000000..ec5dddb --- /dev/null +++ b/src/third_party/skia/tests/GrSKSLPrettyPrintTest.cpp
@@ -0,0 +1,124 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#if SK_SUPPORT_GPU +#include "GrSKSLPrettyPrint.h" + +#define ASSERT(x) REPORTER_ASSERT(r, x) + +const SkString input1("#this is not a realshader\nvec4 some stuff;outside of a function;" + "int i(int b, int c) { { some stuff;} fake block; //comments\n return i;}" + "void main()"); +const SkString input2("{nowin a function;{indenting;{abit more;dreadedfor((;;)(;)((;;);)){" + "doingstuff" + ";for(;;;){and more stufff;mixed garbage\n\n\t\t\t\t\n/*using this" + " comment\n is"); +const SkString input3(" dangerous\ndo so at your own\n risk*/;\n\n\t\t\t\n" + "//a comment"); +const SkString input4("breaking in comment"); +const SkString input5("continuing the comment"); +const SkString input6("\n}}a; little ; love; for ; leading; spaces;} " + "an struct = { int a; int b; };" + "int[5] arr = int[5](1,2,3,4,5);} some code at the bottom; for(;;) {} }"); + +const SkString output1( + " 1\t#this is not a realshader\n" + " 2\tvec4 some stuff;\n" + " 3\toutside of a function;\n" + " 4\tint i(int b, int c) \n" + " 5\t{\n" + " 6\t\t{\n" + " 7\t\t\tsome stuff;\n" + " 8\t\t}\n" + " 9\t\tfake block;\n" + " 10\t\t//comments\n" + " 11\t\treturn i;\n" + " 12\t}\n" + " 13\tvoid main()\n" + " 14\t{\n" + " 15\t\tnowin a function;\n" + " 16\t\t{\n" + " 17\t\t\tindenting;\n" + " 18\t\t\t{\n" + " 19\t\t\t\tabit more;\n" + " 20\t\t\t\tdreadedfor((;;)(;)((;;);))\n" + " 21\t\t\t\t{\n" + " 22\t\t\t\t\tdoingstuff;\n" + " 23\t\t\t\t\tfor(;;;)\n" + " 24\t\t\t\t\t{\n" + " 25\t\t\t\t\t\tand more stufff;\n" + " 26\t\t\t\t\t\tmixed garbage/*using this comment\n" + " 27\t\t\t\t\t\t is dangerous\n" + " 28\t\t\t\t\t\tdo so at your own\n" + " 29\t\t\t\t\t\t risk*/;\n" + " 30\t\t\t\t\t\t//a commentbreaking in commentcontinuing the comment\n" + " 31\t\t\t\t\t}\n" + " 32\t\t\t\t}\n" + " 33\t\t\t\ta;\n" + " 34\t\t\t\tlittle ;\n" + " 35\t\t\t\tlove;\n" + " 36\t\t\t\tfor ;\n" + " 37\t\t\t\tleading;\n" + " 38\t\t\t\tspaces;\n" + " 39\t\t\t}\n" + " 40\t\t\tan struct = \n" + " 41\t\t\t{\n" + " 42\t\t\t\tint a;\n" + " 43\t\t\t\tint b;\n" + " 44\t\t\t}\n" + " 45\t\t\t;\n" + " 46\t\t\tint[5] arr = int[5](1,2,3,4,5);\n" + " 47\t\t}\n" + " 48\t\tsome code at the bottom;\n" + " 49\t\tfor(;;) \n" + " 50\t\t{\n" + " 51\t\t}\n" + " 52\t}\n" + " 53\t"); + +const SkString neg1("{;;{{{{;;;{{{{{{{{{{{"); +const SkString neg2("###\n##\n#####(((((((((((((unbalanced verything;;;"); +const SkString neg3("}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}" + ";;;;;;/////"); + +DEF_TEST(GrSKSLPrettyPrint, r) { + SkTArray<const char*> testStr; + SkTArray<int> lengths; + testStr.push_back(input1.c_str()); + lengths.push_back((int)input1.size()); + testStr.push_back(input2.c_str()); + lengths.push_back((int)input2.size()); + testStr.push_back(input3.c_str()); + lengths.push_back((int)input3.size()); + testStr.push_back(input4.c_str()); + lengths.push_back((int)input4.size()); + testStr.push_back(input5.c_str()); + lengths.push_back((int)input5.size()); + testStr.push_back(input6.c_str()); + lengths.push_back((int)input6.size()); + + SkString test = GrSKSLPrettyPrint::PrettyPrint(testStr.begin(), lengths.begin(), + testStr.count(), true); + ASSERT(output1 == test); + + testStr.reset(); + lengths.reset(); + testStr.push_back(neg1.c_str()); + lengths.push_back((int)neg1.size()); + testStr.push_back(neg2.c_str()); + lengths.push_back((int)neg2.size()); + testStr.push_back(neg3.c_str()); + lengths.push_back((int)neg3.size()); + + // Just test we don't crash with garbage input + ASSERT(GrSKSLPrettyPrint::PrettyPrint(testStr.begin(), lengths.begin(), 1, + true).c_str() != nullptr); +} + +#endif
diff --git a/src/third_party/skia/tests/GrShapeTest.cpp b/src/third_party/skia/tests/GrShapeTest.cpp new file mode 100644 index 0000000..dae71c1 --- /dev/null +++ b/src/third_party/skia/tests/GrShapeTest.cpp
@@ -0,0 +1,1971 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <initializer_list> +#include <functional> +#include "Test.h" +#if SK_SUPPORT_GPU +#include "GrShape.h" +#include "SkCanvas.h" +#include "SkDashPathEffect.h" +#include "SkPath.h" +#include "SkPathOps.h" +#include "SkSurface.h" +#include "SkClipOpPriv.h" + +using Key = SkTArray<uint32_t>; + +static bool make_key(Key* key, const GrShape& shape) { + int size = shape.unstyledKeySize(); + if (size <= 0) { + key->reset(0); + return false; + } + SkASSERT(size); + key->reset(size); + shape.writeUnstyledKey(key->begin()); + return true; +} + +static bool paths_fill_same(const SkPath& a, const SkPath& b) { + SkPath pathXor; + Op(a, b, SkPathOp::kXOR_SkPathOp, &pathXor); + return pathXor.isEmpty(); +} + +static bool test_bounds_by_rasterizing(const SkPath& path, const SkRect& bounds) { + // We test the bounds by rasterizing the path into a kRes by kRes grid. The bounds is + // mapped to the range kRes/4 to 3*kRes/4 in x and y. A difference clip is used to avoid + // rendering within the bounds (with a tolerance). Then we render the path and check that + // everything got clipped out. + static constexpr int kRes = 2000; + // This tolerance is in units of 1/kRes fractions of the bounds width/height. + static constexpr int kTol = 0; + GR_STATIC_ASSERT(kRes % 4 == 0); + SkImageInfo info = SkImageInfo::MakeA8(kRes, kRes); + sk_sp<SkSurface> surface = SkSurface::MakeRaster(info); + surface->getCanvas()->clear(0x0); + SkRect clip = SkRect::MakeXYWH(kRes/4, kRes/4, kRes/2, kRes/2); + SkMatrix matrix; + matrix.setRectToRect(bounds, clip, SkMatrix::kFill_ScaleToFit); + clip.outset(SkIntToScalar(kTol), SkIntToScalar(kTol)); + surface->getCanvas()->clipRect(clip, kDifference_SkClipOp); + surface->getCanvas()->concat(matrix); + SkPaint whitePaint; + whitePaint.setColor(SK_ColorWHITE); + surface->getCanvas()->drawPath(path, whitePaint); + SkPixmap pixmap; + surface->getCanvas()->peekPixels(&pixmap); +#if defined(SK_BUILD_FOR_WIN) + // The static constexpr version in #else causes cl.exe to crash. + const uint8_t* kZeros = reinterpret_cast<uint8_t*>(calloc(kRes, 1)); +#else + static constexpr uint8_t kZeros[kRes] = {0}; +#endif + for (int y = 0; y < kRes; ++y) { + const uint8_t* row = pixmap.addr8(0, y); + if (0 != memcmp(kZeros, row, kRes)) { + return false; + } + } +#ifdef SK_BUILD_FOR_WIN + free(const_cast<uint8_t*>(kZeros)); +#endif + return true; +} + +namespace { +/** + * Geo is a factory for creating a GrShape from another representation. It also answers some + * questions about expected behavior for GrShape given the inputs. + */ +class Geo { +public: + virtual ~Geo() {} + virtual GrShape makeShape(const SkPaint&) const = 0; + virtual SkPath path() const = 0; + // These functions allow tests to check for special cases where style gets + // applied by GrShape in its constructor (without calling GrShape::applyStyle). + // These unfortunately rely on knowing details of GrShape's implementation. + // These predicates are factored out here to avoid littering the rest of the + // test code with GrShape implementation details. + virtual bool fillChangesGeom() const { return false; } + virtual bool strokeIsConvertedToFill() const { return false; } + virtual bool strokeAndFillIsConvertedToFill(const SkPaint&) const { return false; } + // Is this something we expect GrShape to recognize as something simpler than a path. + virtual bool isNonPath(const SkPaint& paint) const { return true; } +}; + +class RectGeo : public Geo { +public: + RectGeo(const SkRect& rect) : fRect(rect) {} + + SkPath path() const override { + SkPath path; + path.addRect(fRect); + return path; + } + + GrShape makeShape(const SkPaint& paint) const override { + return GrShape(fRect, paint); + } + + bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override { + SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style); + // Converted to an outset rectangle. + return paint.getStrokeJoin() == SkPaint::kMiter_Join && + paint.getStrokeMiter() >= SK_ScalarSqrt2; + } + +private: + SkRect fRect; +}; + +class RRectGeo : public Geo { +public: + RRectGeo(const SkRRect& rrect) : fRRect(rrect) {} + + GrShape makeShape(const SkPaint& paint) const override { + return GrShape(fRRect, paint); + } + + SkPath path() const override { + SkPath path; + path.addRRect(fRRect); + return path; + } + + bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override { + SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style); + if (fRRect.isRect()) { + return RectGeo(fRRect.rect()).strokeAndFillIsConvertedToFill(paint); + } + return false; + } + +private: + SkRRect fRRect; +}; + +class PathGeo : public Geo { +public: + enum class Invert { kNo, kYes }; + + PathGeo(const SkPath& path, Invert invert) : fPath(path) { + SkASSERT(!path.isInverseFillType()); + if (Invert::kYes == invert) { + if (fPath.getFillType() == SkPath::kEvenOdd_FillType) { + fPath.setFillType(SkPath::kInverseEvenOdd_FillType); + } else { + SkASSERT(fPath.getFillType() == SkPath::kWinding_FillType); + fPath.setFillType(SkPath::kInverseWinding_FillType); + } + } + } + + GrShape makeShape(const SkPaint& paint) const override { + return GrShape(fPath, paint); + } + + SkPath path() const override { return fPath; } + + bool fillChangesGeom() const override { + // unclosed rects get closed. Lines get turned into empty geometry + return this->isUnclosedRect() || (fPath.isLine(nullptr) && !fPath.isInverseFillType()); + } + + bool strokeIsConvertedToFill() const override { + return this->isAxisAlignedLine(); + } + + bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override { + SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style); + if (this->isAxisAlignedLine()) { + // The fill is ignored (zero area) and the stroke is converted to a rrect. + return true; + } + SkRect rect; + unsigned start; + SkPath::Direction dir; + if (SkPathPriv::IsSimpleClosedRect(fPath, &rect, &dir, &start)) { + return RectGeo(rect).strokeAndFillIsConvertedToFill(paint); + } + return false; + } + + bool isNonPath(const SkPaint& paint) const override { + return fPath.isLine(nullptr) || fPath.isEmpty(); + } + +private: + bool isAxisAlignedLine() const { + SkPoint pts[2]; + if (!fPath.isLine(pts)) { + return false; + } + return pts[0].fX == pts[1].fX || pts[0].fY == pts[1].fY; + } + + bool isUnclosedRect() const { + bool closed; + return fPath.isRect(nullptr, &closed, nullptr) && !closed; + } + + SkPath fPath; +}; + +class RRectPathGeo : public PathGeo { +public: + enum class RRectForStroke { kNo, kYes }; + + RRectPathGeo(const SkPath& path, const SkRRect& equivalentRRect, RRectForStroke rrectForStroke, + Invert invert) + : PathGeo(path, invert) + , fRRect(equivalentRRect) + , fRRectForStroke(rrectForStroke) {} + + RRectPathGeo(const SkPath& path, const SkRect& equivalentRect, RRectForStroke rrectForStroke, + Invert invert) + : RRectPathGeo(path, SkRRect::MakeRect(equivalentRect), rrectForStroke, invert) {} + + bool isNonPath(const SkPaint& paint) const override { + if (SkPaint::kFill_Style == paint.getStyle() || RRectForStroke::kYes == fRRectForStroke) { + return true; + } + return false; + } + + const SkRRect& rrect() const { return fRRect; } + +private: + SkRRect fRRect; + RRectForStroke fRRectForStroke; +}; + +class TestCase { +public: + TestCase(const Geo& geo, const SkPaint& paint, skiatest::Reporter* r, + SkScalar scale = SK_Scalar1) : fBase(geo.makeShape(paint)) { + this->init(r, scale); + } + + template<typename... ShapeArgs> + TestCase(skiatest::Reporter* r, ShapeArgs... shapeArgs) + : fBase(shapeArgs...) { + this->init(r, SK_Scalar1); + } + + TestCase(const GrShape& shape, skiatest::Reporter* r, SkScalar scale = SK_Scalar1) + : fBase(shape) { + this->init(r, scale); + } + + struct SelfExpectations { + bool fPEHasEffect; + bool fPEHasValidKey; + bool fStrokeApplies; + }; + + void testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const; + + enum ComparisonExpecation { + kAllDifferent_ComparisonExpecation, + kSameUpToPE_ComparisonExpecation, + kSameUpToStroke_ComparisonExpecation, + kAllSame_ComparisonExpecation, + }; + + void compare(skiatest::Reporter*, const TestCase& that, ComparisonExpecation) const; + + const GrShape& baseShape() const { return fBase; } + const GrShape& appliedPathEffectShape() const { return fAppliedPE; } + const GrShape& appliedFullStyleShape() const { return fAppliedFull; } + + // The returned array's count will be 0 if the key shape has no key. + const Key& baseKey() const { return fBaseKey; } + const Key& appliedPathEffectKey() const { return fAppliedPEKey; } + const Key& appliedFullStyleKey() const { return fAppliedFullKey; } + const Key& appliedPathEffectThenStrokeKey() const { return fAppliedPEThenStrokeKey; } + +private: + static void CheckBounds(skiatest::Reporter* r, const GrShape& shape, const SkRect& bounds) { + SkPath path; + shape.asPath(&path); + // If the bounds are empty, the path ought to be as well. + if (bounds.fLeft > bounds.fRight || bounds.fTop > bounds.fBottom) { + REPORTER_ASSERT(r, path.isEmpty()); + return; + } + if (path.isEmpty()) { + return; + } + // The bounds API explicitly calls out that it does not consider inverseness. + SkPath p = path; + p.setFillType(SkPath::ConvertToNonInverseFillType(path.getFillType())); + REPORTER_ASSERT(r, test_bounds_by_rasterizing(p, bounds)); + } + + void init(skiatest::Reporter* r, SkScalar scale) { + fAppliedPE = fBase.applyStyle(GrStyle::Apply::kPathEffectOnly, scale); + fAppliedPEThenStroke = fAppliedPE.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, + scale); + fAppliedFull = fBase.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, scale); + + make_key(&fBaseKey, fBase); + make_key(&fAppliedPEKey, fAppliedPE); + make_key(&fAppliedPEThenStrokeKey, fAppliedPEThenStroke); + make_key(&fAppliedFullKey, fAppliedFull); + + // Applying the path effect and then the stroke should always be the same as applying + // both in one go. + REPORTER_ASSERT(r, fAppliedPEThenStrokeKey == fAppliedFullKey); + SkPath a, b; + fAppliedPEThenStroke.asPath(&a); + fAppliedFull.asPath(&b); + // If the output of the path effect is a rrect then it is possible for a and b to be + // different paths that fill identically. The reason is that fAppliedFull will do this: + // base -> apply path effect -> rrect_as_path -> stroke -> stroked_rrect_as_path + // fAppliedPEThenStroke will have converted the rrect_as_path back to a rrect. However, + // now that there is no longer a path effect, the direction and starting index get + // canonicalized before the stroke. + if (fAppliedPE.asRRect(nullptr, nullptr, nullptr, nullptr)) { + REPORTER_ASSERT(r, paths_fill_same(a, b)); + } else { + REPORTER_ASSERT(r, a == b); + } + REPORTER_ASSERT(r, fAppliedFull.isEmpty() == fAppliedPEThenStroke.isEmpty()); + + SkPath path; + fBase.asPath(&path); + REPORTER_ASSERT(r, path.isEmpty() == fBase.isEmpty()); + REPORTER_ASSERT(r, path.getSegmentMasks() == fBase.segmentMask()); + fAppliedPE.asPath(&path); + REPORTER_ASSERT(r, path.isEmpty() == fAppliedPE.isEmpty()); + REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedPE.segmentMask()); + fAppliedFull.asPath(&path); + REPORTER_ASSERT(r, path.isEmpty() == fAppliedFull.isEmpty()); + REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedFull.segmentMask()); + + CheckBounds(r, fBase, fBase.bounds()); + CheckBounds(r, fAppliedPE, fAppliedPE.bounds()); + CheckBounds(r, fAppliedPEThenStroke, fAppliedPEThenStroke.bounds()); + CheckBounds(r, fAppliedFull, fAppliedFull.bounds()); + SkRect styledBounds = fBase.styledBounds(); + CheckBounds(r, fAppliedFull, styledBounds); + styledBounds = fAppliedPE.styledBounds(); + CheckBounds(r, fAppliedFull, styledBounds); + + // Check that the same path is produced when style is applied by GrShape and GrStyle. + SkPath preStyle; + SkPath postPathEffect; + SkPath postAllStyle; + + fBase.asPath(&preStyle); + SkStrokeRec postPEStrokeRec(SkStrokeRec::kFill_InitStyle); + if (fBase.style().applyPathEffectToPath(&postPathEffect, &postPEStrokeRec, preStyle, + scale)) { + // run postPathEffect through GrShape to get any geometry reductions that would have + // occurred to fAppliedPE. + GrShape(postPathEffect, GrStyle(postPEStrokeRec, nullptr)).asPath(&postPathEffect); + + SkPath testPath; + fAppliedPE.asPath(&testPath); + REPORTER_ASSERT(r, testPath == postPathEffect); + REPORTER_ASSERT(r, postPEStrokeRec.hasEqualEffect(fAppliedPE.style().strokeRec())); + } + SkStrokeRec::InitStyle fillOrHairline; + if (fBase.style().applyToPath(&postAllStyle, &fillOrHairline, preStyle, scale)) { + SkPath testPath; + fAppliedFull.asPath(&testPath); + if (fBase.style().hasPathEffect()) { + // Because GrShape always does two-stage application when there is a path effect + // there may be a reduction/canonicalization step between the path effect and + // strokerec not reflected in postAllStyle since it applied both the path effect + // and strokerec without analyzing the intermediate path. + REPORTER_ASSERT(r, paths_fill_same(postAllStyle, testPath)); + } else { + // Make sure that postAllStyle sees any reductions/canonicalizations that GrShape + // would apply. + GrShape(postAllStyle, GrStyle(fillOrHairline)).asPath(&postAllStyle); + REPORTER_ASSERT(r, testPath == postAllStyle); + } + + if (fillOrHairline == SkStrokeRec::kFill_InitStyle) { + REPORTER_ASSERT(r, fAppliedFull.style().isSimpleFill()); + } else { + REPORTER_ASSERT(r, fAppliedFull.style().isSimpleHairline()); + } + } + } + + GrShape fBase; + GrShape fAppliedPE; + GrShape fAppliedPEThenStroke; + GrShape fAppliedFull; + + Key fBaseKey; + Key fAppliedPEKey; + Key fAppliedPEThenStrokeKey; + Key fAppliedFullKey; +}; + +void TestCase::testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const { + // The base's key should always be valid (unless the path is volatile) + REPORTER_ASSERT(reporter, fBaseKey.count()); + if (expectations.fPEHasEffect) { + REPORTER_ASSERT(reporter, fBaseKey != fAppliedPEKey); + REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedPEKey.count())); + REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey); + REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedFullKey.count())); + if (expectations.fStrokeApplies && expectations.fPEHasValidKey) { + REPORTER_ASSERT(reporter, fAppliedPEKey != fAppliedFullKey); + REPORTER_ASSERT(reporter, SkToBool(fAppliedFullKey.count())); + } + } else { + REPORTER_ASSERT(reporter, fBaseKey == fAppliedPEKey); + SkPath a, b; + fBase.asPath(&a); + fAppliedPE.asPath(&b); + REPORTER_ASSERT(reporter, a == b); + if (expectations.fStrokeApplies) { + REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey); + } else { + REPORTER_ASSERT(reporter, fBaseKey == fAppliedFullKey); + } + } +} + +static bool can_interchange_winding_and_even_odd_fill(const GrShape& shape) { + SkPath path; + shape.asPath(&path); + if (shape.style().hasNonDashPathEffect()) { + return false; + } + const SkStrokeRec::Style strokeRecStyle = shape.style().strokeRec().getStyle(); + return strokeRecStyle == SkStrokeRec::kStroke_Style || + strokeRecStyle == SkStrokeRec::kHairline_Style || + (shape.style().isSimpleFill() && path.isConvex()); +} + +static void check_equivalence(skiatest::Reporter* r, const GrShape& a, const GrShape& b, + const Key& keyA, const Key& keyB) { + // GrShape only respects the input winding direction and start point for rrect shapes + // when there is a path effect. Thus, if there are two GrShapes representing the same rrect + // but one has a path effect in its style and the other doesn't then asPath() and the unstyled + // key will differ. GrShape will have canonicalized the direction and start point for the shape + // without the path effect. If *both* have path effects then they should have both preserved + // the direction and starting point. + + // The asRRect() output params are all initialized just to silence compiler warnings about + // uninitialized variables. + SkRRect rrectA = SkRRect::MakeEmpty(), rrectB = SkRRect::MakeEmpty(); + SkPath::Direction dirA = SkPath::kCW_Direction, dirB = SkPath::kCW_Direction; + unsigned startA = ~0U, startB = ~0U; + bool invertedA = true, invertedB = true; + + bool aIsRRect = a.asRRect(&rrectA, &dirA, &startA, &invertedA); + bool bIsRRect = b.asRRect(&rrectB, &dirB, &startB, &invertedB); + bool aHasPE = a.style().hasPathEffect(); + bool bHasPE = b.style().hasPathEffect(); + bool allowSameRRectButDiffStartAndDir = (aIsRRect && bIsRRect) && (aHasPE != bHasPE); + // GrShape will close paths with simple fill style. + bool allowedClosednessDiff = (a.style().isSimpleFill() != b.style().isSimpleFill()); + SkPath pathA, pathB; + a.asPath(&pathA); + b.asPath(&pathB); + + // Having a dash path effect can allow 'a' but not 'b' to turn a inverse fill type into a + // non-inverse fill type (or vice versa). + bool ignoreInversenessDifference = false; + if (pathA.isInverseFillType() != pathB.isInverseFillType()) { + const GrShape* s1 = pathA.isInverseFillType() ? &a : &b; + const GrShape* s2 = pathA.isInverseFillType() ? &b : &a; + bool canDropInverse1 = s1->style().isDashed(); + bool canDropInverse2 = s2->style().isDashed(); + ignoreInversenessDifference = (canDropInverse1 != canDropInverse2); + } + bool ignoreWindingVsEvenOdd = false; + if (SkPath::ConvertToNonInverseFillType(pathA.getFillType()) != + SkPath::ConvertToNonInverseFillType(pathB.getFillType())) { + bool aCanChange = can_interchange_winding_and_even_odd_fill(a); + bool bCanChange = can_interchange_winding_and_even_odd_fill(b); + if (aCanChange != bCanChange) { + ignoreWindingVsEvenOdd = true; + } + } + if (allowSameRRectButDiffStartAndDir) { + REPORTER_ASSERT(r, rrectA == rrectB); + REPORTER_ASSERT(r, paths_fill_same(pathA, pathB)); + REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB); + } else { + SkPath pA = pathA; + SkPath pB = pathB; + REPORTER_ASSERT(r, a.inverseFilled() == pA.isInverseFillType()); + REPORTER_ASSERT(r, b.inverseFilled() == pB.isInverseFillType()); + if (ignoreInversenessDifference) { + pA.setFillType(SkPath::ConvertToNonInverseFillType(pathA.getFillType())); + pB.setFillType(SkPath::ConvertToNonInverseFillType(pathB.getFillType())); + } + if (ignoreWindingVsEvenOdd) { + pA.setFillType(pA.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType + : SkPath::kEvenOdd_FillType); + pB.setFillType(pB.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType + : SkPath::kEvenOdd_FillType); + } + if (!ignoreInversenessDifference && !ignoreWindingVsEvenOdd) { + REPORTER_ASSERT(r, keyA == keyB); + } else { + REPORTER_ASSERT(r, keyA != keyB); + } + if (allowedClosednessDiff) { + // GrShape will close paths with simple fill style. Make the non-filled path closed + // so that the comparision will succeed. Make sure both are closed before comparing. + pA.close(); + pB.close(); + } + REPORTER_ASSERT(r, pA == pB); + REPORTER_ASSERT(r, aIsRRect == bIsRRect); + if (aIsRRect) { + REPORTER_ASSERT(r, rrectA == rrectB); + REPORTER_ASSERT(r, dirA == dirB); + REPORTER_ASSERT(r, startA == startB); + REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB); + } + } + REPORTER_ASSERT(r, a.isEmpty() == b.isEmpty()); + REPORTER_ASSERT(r, allowedClosednessDiff || a.knownToBeClosed() == b.knownToBeClosed()); + // closedness can affect convexity. + REPORTER_ASSERT(r, allowedClosednessDiff || a.knownToBeConvex() == b.knownToBeConvex()); + if (a.knownToBeConvex()) { + REPORTER_ASSERT(r, pathA.isConvex()); + } + if (b.knownToBeConvex()) { + REPORTER_ASSERT(r, pathB.isConvex()); + } + REPORTER_ASSERT(r, a.bounds() == b.bounds()); + REPORTER_ASSERT(r, a.segmentMask() == b.segmentMask()); + // Init these to suppress warnings. + SkPoint pts[4] {{0, 0,}, {0, 0}, {0, 0}, {0, 0}} ; + bool invertedLine[2] {true, true}; + REPORTER_ASSERT(r, a.asLine(pts, &invertedLine[0]) == b.asLine(pts + 2, &invertedLine[1])); + // mayBeInverseFilledAfterStyling() is allowed to differ if one has a arbitrary PE and the other + // doesn't (since the PE can set any fill type on its output path). + // Moreover, dash style explicitly ignores inverseness. So if one is dashed but not the other + // then they may disagree about inverseness. + if (a.style().hasNonDashPathEffect() == b.style().hasNonDashPathEffect() && + a.style().isDashed() == b.style().isDashed()) { + REPORTER_ASSERT(r, a.mayBeInverseFilledAfterStyling() == + b.mayBeInverseFilledAfterStyling()); + } + if (a.asLine(nullptr, nullptr)) { + REPORTER_ASSERT(r, pts[2] == pts[0] && pts[3] == pts[1]); + REPORTER_ASSERT(r, ignoreInversenessDifference || invertedLine[0] == invertedLine[1]); + REPORTER_ASSERT(r, invertedLine[0] == a.inverseFilled()); + REPORTER_ASSERT(r, invertedLine[1] == b.inverseFilled()); + } + REPORTER_ASSERT(r, ignoreInversenessDifference || a.inverseFilled() == b.inverseFilled()); +} + +void TestCase::compare(skiatest::Reporter* r, const TestCase& that, + ComparisonExpecation expectation) const { + SkPath a, b; + switch (expectation) { + case kAllDifferent_ComparisonExpecation: + REPORTER_ASSERT(r, fBaseKey != that.fBaseKey); + REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey); + REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey); + break; + case kSameUpToPE_ComparisonExpecation: + check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey); + REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey); + REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey); + break; + case kSameUpToStroke_ComparisonExpecation: + check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey); + check_equivalence(r, fAppliedPE, that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey); + REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey); + break; + case kAllSame_ComparisonExpecation: + check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey); + check_equivalence(r, fAppliedPE, that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey); + check_equivalence(r, fAppliedFull, that.fAppliedFull, fAppliedFullKey, + that.fAppliedFullKey); + break; + } +} +} // namespace + +static sk_sp<SkPathEffect> make_dash() { + static const SkScalar kIntervals[] = { 0.25, 3.f, 0.5, 2.f }; + static const SkScalar kPhase = 0.75; + return SkDashPathEffect::Make(kIntervals, SK_ARRAY_COUNT(kIntervals), kPhase); +} + +static sk_sp<SkPathEffect> make_null_dash() { + static const SkScalar kNullIntervals[] = {0, 0, 0, 0, 0, 0}; + return SkDashPathEffect::Make(kNullIntervals, SK_ARRAY_COUNT(kNullIntervals), 0.f); +} + +static void test_basic(skiatest::Reporter* reporter, const Geo& geo) { + sk_sp<SkPathEffect> dashPE = make_dash(); + + TestCase::SelfExpectations expectations; + SkPaint fill; + + TestCase fillCase(geo, fill, reporter); + expectations.fPEHasEffect = false; + expectations.fPEHasValidKey = false; + expectations.fStrokeApplies = false; + fillCase.testExpectations(reporter, expectations); + // Test that another GrShape instance built from the same primitive is the same. + TestCase(geo, fill, reporter).compare(reporter, fillCase, + TestCase::kAllSame_ComparisonExpecation); + + SkPaint stroke2RoundBevel; + stroke2RoundBevel.setStyle(SkPaint::kStroke_Style); + stroke2RoundBevel.setStrokeCap(SkPaint::kRound_Cap); + stroke2RoundBevel.setStrokeJoin(SkPaint::kBevel_Join); + stroke2RoundBevel.setStrokeWidth(2.f); + TestCase stroke2RoundBevelCase(geo, stroke2RoundBevel, reporter); + expectations.fPEHasValidKey = true; + expectations.fPEHasEffect = false; + expectations.fStrokeApplies = !geo.strokeIsConvertedToFill(); + stroke2RoundBevelCase.testExpectations(reporter, expectations); + TestCase(geo, stroke2RoundBevel, reporter).compare(reporter, stroke2RoundBevelCase, + TestCase::kAllSame_ComparisonExpecation); + + SkPaint stroke2RoundBevelDash = stroke2RoundBevel; + stroke2RoundBevelDash.setPathEffect(make_dash()); + TestCase stroke2RoundBevelDashCase(geo, stroke2RoundBevelDash, reporter); + expectations.fPEHasValidKey = true; + expectations.fPEHasEffect = true; + expectations.fStrokeApplies = true; + stroke2RoundBevelDashCase.testExpectations(reporter, expectations); + TestCase(geo, stroke2RoundBevelDash, reporter).compare(reporter, stroke2RoundBevelDashCase, + TestCase::kAllSame_ComparisonExpecation); + + if (geo.fillChangesGeom() || geo.strokeIsConvertedToFill()) { + fillCase.compare(reporter, stroke2RoundBevelCase, + TestCase::kAllDifferent_ComparisonExpecation); + fillCase.compare(reporter, stroke2RoundBevelDashCase, + TestCase::kAllDifferent_ComparisonExpecation); + } else { + fillCase.compare(reporter, stroke2RoundBevelCase, + TestCase::kSameUpToStroke_ComparisonExpecation); + fillCase.compare(reporter, stroke2RoundBevelDashCase, + TestCase::kSameUpToPE_ComparisonExpecation); + } + if (geo.strokeIsConvertedToFill()) { + stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase, + TestCase::kAllDifferent_ComparisonExpecation); + } else { + stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase, + TestCase::kSameUpToPE_ComparisonExpecation); + } + + // Stroke and fill cases + SkPaint stroke2RoundBevelAndFill = stroke2RoundBevel; + stroke2RoundBevelAndFill.setStyle(SkPaint::kStrokeAndFill_Style); + TestCase stroke2RoundBevelAndFillCase(geo, stroke2RoundBevelAndFill, reporter); + expectations.fPEHasValidKey = true; + expectations.fPEHasEffect = false; + expectations.fStrokeApplies = !geo.strokeIsConvertedToFill(); + stroke2RoundBevelAndFillCase.testExpectations(reporter, expectations); + TestCase(geo, stroke2RoundBevelAndFill, reporter).compare(reporter, + stroke2RoundBevelAndFillCase, TestCase::kAllSame_ComparisonExpecation); + + SkPaint stroke2RoundBevelAndFillDash = stroke2RoundBevelDash; + stroke2RoundBevelAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style); + TestCase stroke2RoundBevelAndFillDashCase(geo, stroke2RoundBevelAndFillDash, reporter); + expectations.fPEHasValidKey = true; + expectations.fPEHasEffect = false; + expectations.fStrokeApplies = !geo.strokeIsConvertedToFill(); + stroke2RoundBevelAndFillDashCase.testExpectations(reporter, expectations); + TestCase(geo, stroke2RoundBevelAndFillDash, reporter).compare( + reporter, stroke2RoundBevelAndFillDashCase, TestCase::kAllSame_ComparisonExpecation); + stroke2RoundBevelAndFillDashCase.compare(reporter, stroke2RoundBevelAndFillCase, + TestCase::kAllSame_ComparisonExpecation); + + SkPaint hairline; + hairline.setStyle(SkPaint::kStroke_Style); + hairline.setStrokeWidth(0.f); + TestCase hairlineCase(geo, hairline, reporter); + // Since hairline style doesn't change the SkPath data, it is keyed identically to fill (except + // in the line and unclosed rect cases). + if (geo.fillChangesGeom()) { + hairlineCase.compare(reporter, fillCase, TestCase::kAllDifferent_ComparisonExpecation); + } else { + hairlineCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation); + } + REPORTER_ASSERT(reporter, hairlineCase.baseShape().style().isSimpleHairline()); + REPORTER_ASSERT(reporter, hairlineCase.appliedFullStyleShape().style().isSimpleHairline()); + REPORTER_ASSERT(reporter, hairlineCase.appliedPathEffectShape().style().isSimpleHairline()); + +} + +static void test_scale(skiatest::Reporter* reporter, const Geo& geo) { + sk_sp<SkPathEffect> dashPE = make_dash(); + + static const SkScalar kS1 = 1.f; + static const SkScalar kS2 = 2.f; + + SkPaint fill; + TestCase fillCase1(geo, fill, reporter, kS1); + TestCase fillCase2(geo, fill, reporter, kS2); + // Scale doesn't affect fills. + fillCase1.compare(reporter, fillCase2, TestCase::kAllSame_ComparisonExpecation); + + SkPaint hairline; + hairline.setStyle(SkPaint::kStroke_Style); + hairline.setStrokeWidth(0.f); + TestCase hairlineCase1(geo, hairline, reporter, kS1); + TestCase hairlineCase2(geo, hairline, reporter, kS2); + // Scale doesn't affect hairlines. + hairlineCase1.compare(reporter, hairlineCase2, TestCase::kAllSame_ComparisonExpecation); + + SkPaint stroke; + stroke.setStyle(SkPaint::kStroke_Style); + stroke.setStrokeWidth(2.f); + TestCase strokeCase1(geo, stroke, reporter, kS1); + TestCase strokeCase2(geo, stroke, reporter, kS2); + // Scale affects the stroke + if (geo.strokeIsConvertedToFill()) { + REPORTER_ASSERT(reporter, !strokeCase1.baseShape().style().applies()); + strokeCase1.compare(reporter, strokeCase2, TestCase::kAllSame_ComparisonExpecation); + } else { + strokeCase1.compare(reporter, strokeCase2, TestCase::kSameUpToStroke_ComparisonExpecation); + } + + SkPaint strokeDash = stroke; + strokeDash.setPathEffect(make_dash()); + TestCase strokeDashCase1(geo, strokeDash, reporter, kS1); + TestCase strokeDashCase2(geo, strokeDash, reporter, kS2); + // Scale affects the dash and the stroke. + strokeDashCase1.compare(reporter, strokeDashCase2, + TestCase::kSameUpToPE_ComparisonExpecation); + + // Stroke and fill cases + SkPaint strokeAndFill = stroke; + strokeAndFill.setStyle(SkPaint::kStrokeAndFill_Style); + TestCase strokeAndFillCase1(geo, strokeAndFill, reporter, kS1); + TestCase strokeAndFillCase2(geo, strokeAndFill, reporter, kS2); + SkPaint strokeAndFillDash = strokeDash; + strokeAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style); + // Dash is ignored for stroke and fill + TestCase strokeAndFillDashCase1(geo, strokeAndFillDash, reporter, kS1); + TestCase strokeAndFillDashCase2(geo, strokeAndFillDash, reporter, kS2); + // Scale affects the stroke, but check to make sure this didn't become a simpler shape (e.g. + // stroke-and-filled rect can become a rect), in which case the scale shouldn't matter and the + // geometries should agree. + if (geo.strokeAndFillIsConvertedToFill(strokeAndFillDash)) { + REPORTER_ASSERT(reporter, !strokeAndFillCase1.baseShape().style().applies()); + strokeAndFillCase1.compare(reporter, strokeAndFillCase2, + TestCase::kAllSame_ComparisonExpecation); + strokeAndFillDashCase1.compare(reporter, strokeAndFillDashCase2, + TestCase::kAllSame_ComparisonExpecation); + } else { + strokeAndFillCase1.compare(reporter, strokeAndFillCase2, + TestCase::kSameUpToStroke_ComparisonExpecation); + } + strokeAndFillDashCase1.compare(reporter, strokeAndFillCase1, + TestCase::kAllSame_ComparisonExpecation); + strokeAndFillDashCase2.compare(reporter, strokeAndFillCase2, + TestCase::kAllSame_ComparisonExpecation); +} + +template <typename T> +static void test_stroke_param_impl(skiatest::Reporter* reporter, const Geo& geo, + std::function<void(SkPaint*, T)> setter, T a, T b, + bool paramAffectsStroke, + bool paramAffectsDashAndStroke) { + // Set the stroke width so that we don't get hairline. However, call the setter afterward so + // that it can override the stroke width. + SkPaint strokeA; + strokeA.setStyle(SkPaint::kStroke_Style); + strokeA.setStrokeWidth(2.f); + setter(&strokeA, a); + SkPaint strokeB; + strokeB.setStyle(SkPaint::kStroke_Style); + strokeB.setStrokeWidth(2.f); + setter(&strokeB, b); + + TestCase strokeACase(geo, strokeA, reporter); + TestCase strokeBCase(geo, strokeB, reporter); + if (paramAffectsStroke) { + // If stroking is immediately incorporated into a geometric transformation then the base + // shapes will differ. + if (geo.strokeIsConvertedToFill()) { + strokeACase.compare(reporter, strokeBCase, + TestCase::kAllDifferent_ComparisonExpecation); + } else { + strokeACase.compare(reporter, strokeBCase, + TestCase::kSameUpToStroke_ComparisonExpecation); + } + } else { + strokeACase.compare(reporter, strokeBCase, TestCase::kAllSame_ComparisonExpecation); + } + + SkPaint strokeAndFillA = strokeA; + SkPaint strokeAndFillB = strokeB; + strokeAndFillA.setStyle(SkPaint::kStrokeAndFill_Style); + strokeAndFillB.setStyle(SkPaint::kStrokeAndFill_Style); + TestCase strokeAndFillACase(geo, strokeAndFillA, reporter); + TestCase strokeAndFillBCase(geo, strokeAndFillB, reporter); + if (paramAffectsStroke) { + // If stroking is immediately incorporated into a geometric transformation then the base + // shapes will differ. + if (geo.strokeAndFillIsConvertedToFill(strokeAndFillA) || + geo.strokeAndFillIsConvertedToFill(strokeAndFillB)) { + strokeAndFillACase.compare(reporter, strokeAndFillBCase, + TestCase::kAllDifferent_ComparisonExpecation); + } else { + strokeAndFillACase.compare(reporter, strokeAndFillBCase, + TestCase::kSameUpToStroke_ComparisonExpecation); + } + } else { + strokeAndFillACase.compare(reporter, strokeAndFillBCase, + TestCase::kAllSame_ComparisonExpecation); + } + + // Make sure stroking params don't affect fill style. + SkPaint fillA = strokeA, fillB = strokeB; + fillA.setStyle(SkPaint::kFill_Style); + fillB.setStyle(SkPaint::kFill_Style); + TestCase fillACase(geo, fillA, reporter); + TestCase fillBCase(geo, fillB, reporter); + fillACase.compare(reporter, fillBCase, TestCase::kAllSame_ComparisonExpecation); + + // Make sure just applying the dash but not stroke gives the same key for both stroking + // variations. + SkPaint dashA = strokeA, dashB = strokeB; + dashA.setPathEffect(make_dash()); + dashB.setPathEffect(make_dash()); + TestCase dashACase(geo, dashA, reporter); + TestCase dashBCase(geo, dashB, reporter); + if (paramAffectsDashAndStroke) { + dashACase.compare(reporter, dashBCase, TestCase::kSameUpToStroke_ComparisonExpecation); + } else { + dashACase.compare(reporter, dashBCase, TestCase::kAllSame_ComparisonExpecation); + } +} + +template <typename T> +static void test_stroke_param(skiatest::Reporter* reporter, const Geo& geo, + std::function<void(SkPaint*, T)> setter, T a, T b) { + test_stroke_param_impl(reporter, geo, setter, a, b, true, true); +}; + +static void test_stroke_cap(skiatest::Reporter* reporter, const Geo& geo) { + SkPaint hairline; + hairline.setStrokeWidth(0); + hairline.setStyle(SkPaint::kStroke_Style); + GrShape shape = geo.makeShape(hairline); + // The cap should only affect shapes that may be open. + bool affectsStroke = !shape.knownToBeClosed(); + // Dashing adds ends that need caps. + bool affectsDashAndStroke = true; + test_stroke_param_impl<SkPaint::Cap>( + reporter, + geo, + [](SkPaint* p, SkPaint::Cap c) { p->setStrokeCap(c);}, + SkPaint::kButt_Cap, SkPaint::kRound_Cap, + affectsStroke, + affectsDashAndStroke); +}; + +static bool shape_known_not_to_have_joins(const GrShape& shape) { + return shape.asLine(nullptr, nullptr) || shape.isEmpty(); +} + +static void test_stroke_join(skiatest::Reporter* reporter, const Geo& geo) { + SkPaint hairline; + hairline.setStrokeWidth(0); + hairline.setStyle(SkPaint::kStroke_Style); + GrShape shape = geo.makeShape(hairline); + // GrShape recognizes certain types don't have joins and will prevent the join type from + // affecting the style key. + // Dashing doesn't add additional joins. However, GrShape currently loses track of this + // after applying the dash. + bool affectsStroke = !shape_known_not_to_have_joins(shape); + test_stroke_param_impl<SkPaint::Join>( + reporter, + geo, + [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);}, + SkPaint::kRound_Join, SkPaint::kBevel_Join, + affectsStroke, true); +}; + +static void test_miter_limit(skiatest::Reporter* reporter, const Geo& geo) { + auto setMiterJoinAndLimit = [](SkPaint* p, SkScalar miter) { + p->setStrokeJoin(SkPaint::kMiter_Join); + p->setStrokeMiter(miter); + }; + + auto setOtherJoinAndLimit = [](SkPaint* p, SkScalar miter) { + p->setStrokeJoin(SkPaint::kRound_Join); + p->setStrokeMiter(miter); + }; + + SkPaint hairline; + hairline.setStrokeWidth(0); + hairline.setStyle(SkPaint::kStroke_Style); + GrShape shape = geo.makeShape(hairline); + bool mayHaveJoins = !shape_known_not_to_have_joins(shape); + + // The miter limit should affect stroked and dashed-stroked cases when the join type is + // miter. + test_stroke_param_impl<SkScalar>( + reporter, + geo, + setMiterJoinAndLimit, + 0.5f, 0.75f, + mayHaveJoins, + true); + + // The miter limit should not affect stroked and dashed-stroked cases when the join type is + // not miter. + test_stroke_param_impl<SkScalar>( + reporter, + geo, + setOtherJoinAndLimit, + 0.5f, 0.75f, + false, + false); +} + +static void test_dash_fill(skiatest::Reporter* reporter, const Geo& geo) { + // A dash with no stroke should have no effect + using DashFactoryFn = sk_sp<SkPathEffect>(*)(); + for (DashFactoryFn md : {&make_dash, &make_null_dash}) { + SkPaint dashFill; + dashFill.setPathEffect((*md)()); + TestCase dashFillCase(geo, dashFill, reporter); + + TestCase fillCase(geo, SkPaint(), reporter); + dashFillCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation); + } +} + +void test_null_dash(skiatest::Reporter* reporter, const Geo& geo) { + SkPaint fill; + SkPaint stroke; + stroke.setStyle(SkPaint::kStroke_Style); + stroke.setStrokeWidth(1.f); + SkPaint dash; + dash.setStyle(SkPaint::kStroke_Style); + dash.setStrokeWidth(1.f); + dash.setPathEffect(make_dash()); + SkPaint nullDash; + nullDash.setStyle(SkPaint::kStroke_Style); + nullDash.setStrokeWidth(1.f); + nullDash.setPathEffect(make_null_dash()); + + TestCase fillCase(geo, fill, reporter); + TestCase strokeCase(geo, stroke, reporter); + TestCase dashCase(geo, dash, reporter); + TestCase nullDashCase(geo, nullDash, reporter); + + // We expect the null dash to be ignored so nullDashCase should match strokeCase, always. + nullDashCase.compare(reporter, strokeCase, TestCase::kAllSame_ComparisonExpecation); + // Check whether the fillCase or strokeCase/nullDashCase would undergo a geometric tranformation + // on construction in order to determine how to compare the fill and stroke. + if (geo.fillChangesGeom() || geo.strokeIsConvertedToFill()) { + nullDashCase.compare(reporter, fillCase, TestCase::kAllDifferent_ComparisonExpecation); + } else { + nullDashCase.compare(reporter, fillCase, TestCase::kSameUpToStroke_ComparisonExpecation); + } + // In the null dash case we may immediately convert to a fill, but not for the normal dash case. + if (geo.strokeIsConvertedToFill()) { + nullDashCase.compare(reporter, dashCase, TestCase::kAllDifferent_ComparisonExpecation); + } else { + nullDashCase.compare(reporter, dashCase, TestCase::kSameUpToPE_ComparisonExpecation); + } +} + +void test_path_effect_makes_rrect(skiatest::Reporter* reporter, const Geo& geo) { + /** + * This path effect takes any input path and turns it into a rrect. It passes through stroke + * info. + */ + class RRectPathEffect : SkPathEffect { + public: + static const SkRRect& RRect() { + static const SkRRect kRRect = SkRRect::MakeRectXY(SkRect::MakeWH(12, 12), 3, 5); + return kRRect; + } + + bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, + const SkRect* cullR) const override { + dst->reset(); + dst->addRRect(RRect()); + return true; + } + void computeFastBounds(SkRect* dst, const SkRect& src) const override { + *dst = RRect().getBounds(); + } + static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new RRectPathEffect); } + Factory getFactory() const override { return nullptr; } + void toString(SkString*) const override {} + private: + RRectPathEffect() {} + }; + + SkPaint fill; + TestCase fillGeoCase(geo, fill, reporter); + + SkPaint pe; + pe.setPathEffect(RRectPathEffect::Make()); + TestCase geoPECase(geo, pe, reporter); + + SkPaint peStroke; + peStroke.setPathEffect(RRectPathEffect::Make()); + peStroke.setStrokeWidth(2.f); + peStroke.setStyle(SkPaint::kStroke_Style); + TestCase geoPEStrokeCase(geo, peStroke, reporter); + + // Check whether constructing the filled case would cause the base shape to have a different + // geometry (because of a geometric transformation upon initial GrShape construction). + if (geo.fillChangesGeom()) { + fillGeoCase.compare(reporter, geoPECase, TestCase::kAllDifferent_ComparisonExpecation); + fillGeoCase.compare(reporter, geoPEStrokeCase, + TestCase::kAllDifferent_ComparisonExpecation); + } else { + fillGeoCase.compare(reporter, geoPECase, TestCase::kSameUpToPE_ComparisonExpecation); + fillGeoCase.compare(reporter, geoPEStrokeCase, TestCase::kSameUpToPE_ComparisonExpecation); + } + geoPECase.compare(reporter, geoPEStrokeCase, + TestCase::kSameUpToStroke_ComparisonExpecation); + + TestCase rrectFillCase(reporter, RRectPathEffect::RRect(), fill); + SkPaint stroke = peStroke; + stroke.setPathEffect(nullptr); + TestCase rrectStrokeCase(reporter, RRectPathEffect::RRect(), stroke); + + SkRRect rrect; + // Applying the path effect should make a SkRRect shape. There is no further stroking in the + // geoPECase, so the full style should be the same as just the PE. + REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectShape().asRRect(&rrect, nullptr, nullptr, + nullptr)); + REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect()); + REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectKey() == rrectFillCase.baseKey()); + + REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleShape().asRRect(&rrect, nullptr, nullptr, + nullptr)); + REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect()); + REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleKey() == rrectFillCase.baseKey()); + + // In the PE+stroke case applying the full style should be the same as just stroking the rrect. + REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().asRRect(&rrect, nullptr, + nullptr, nullptr)); + REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect()); + REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == rrectFillCase.baseKey()); + + REPORTER_ASSERT(reporter, !geoPEStrokeCase.appliedFullStyleShape().asRRect(&rrect, nullptr, + nullptr, nullptr)); + REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() == + rrectStrokeCase.appliedFullStyleKey()); +} + +void test_unknown_path_effect(skiatest::Reporter* reporter, const Geo& geo) { + /** + * This path effect just adds two lineTos to the input path. + */ + class AddLineTosPathEffect : SkPathEffect { + public: + bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, + const SkRect* cullR) const override { + *dst = src; + // To avoid triggering data-based keying of paths with few verbs we add many segments. + for (int i = 0; i < 100; ++i) { + dst->lineTo(SkIntToScalar(i), SkIntToScalar(i)); + } + return true; + } + void computeFastBounds(SkRect* dst, const SkRect& src) const override { + *dst = src; + dst->growToInclude(0, 0); + dst->growToInclude(100, 100); + } + static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new AddLineTosPathEffect); } + Factory getFactory() const override { return nullptr; } + void toString(SkString*) const override {} + private: + AddLineTosPathEffect() {} + }; + + // This path effect should make the keys invalid when it is applied. We only produce a path + // effect key for dash path effects. So the only way another arbitrary path effect can produce + // a styled result with a key is to produce a non-path shape that has a purely geometric key. + SkPaint peStroke; + peStroke.setPathEffect(AddLineTosPathEffect::Make()); + peStroke.setStrokeWidth(2.f); + peStroke.setStyle(SkPaint::kStroke_Style); + TestCase geoPEStrokeCase(geo, peStroke, reporter); + TestCase::SelfExpectations expectations; + expectations.fPEHasEffect = true; + expectations.fPEHasValidKey = false; + expectations.fStrokeApplies = true; + geoPEStrokeCase.testExpectations(reporter, expectations); +} + +void test_make_hairline_path_effect(skiatest::Reporter* reporter, const Geo& geo) { + /** + * This path effect just changes the stroke rec to hairline. + */ + class MakeHairlinePathEffect : SkPathEffect { + public: + bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* strokeRec, + const SkRect* cullR) const override { + *dst = src; + strokeRec->setHairlineStyle(); + return true; + } + void computeFastBounds(SkRect* dst, const SkRect& src) const override { *dst = src; } + static sk_sp<SkPathEffect> Make() { + return sk_sp<SkPathEffect>(new MakeHairlinePathEffect); + } + Factory getFactory() const override { return nullptr; } + void toString(SkString*) const override {} + private: + MakeHairlinePathEffect() {} + }; + + SkPaint fill; + SkPaint pe; + pe.setPathEffect(MakeHairlinePathEffect::Make()); + + TestCase peCase(geo, pe, reporter); + + SkPath a, b, c; + peCase.baseShape().asPath(&a); + peCase.appliedPathEffectShape().asPath(&b); + peCase.appliedFullStyleShape().asPath(&c); + if (geo.isNonPath(pe)) { + // RRect types can have a change in start index or direction after the PE is applied. This + // is because once the PE is applied, GrShape may canonicalize the dir and index since it + // is not germane to the styling any longer. + // Instead we just check that the paths would fill the same both before and after styling. + REPORTER_ASSERT(reporter, paths_fill_same(a, b)); + REPORTER_ASSERT(reporter, paths_fill_same(a, c)); + } else { + // The base shape cannot perform canonicalization on the path's fill type because of an + // unknown path effect. However, after the path effect is applied the resulting hairline + // shape will canonicalize the path fill type since hairlines (and stroking in general) + // don't distinguish between even/odd and non-zero winding. + a.setFillType(b.getFillType()); + REPORTER_ASSERT(reporter, a == b); + REPORTER_ASSERT(reporter, a == c); + // If the resulting path is small enough then it will have a key. + REPORTER_ASSERT(reporter, paths_fill_same(a, b)); + REPORTER_ASSERT(reporter, paths_fill_same(a, c)); + REPORTER_ASSERT(reporter, peCase.appliedPathEffectKey().empty()); + REPORTER_ASSERT(reporter, peCase.appliedFullStyleKey().empty()); + } + REPORTER_ASSERT(reporter, peCase.appliedPathEffectShape().style().isSimpleHairline()); + REPORTER_ASSERT(reporter, peCase.appliedFullStyleShape().style().isSimpleHairline()); +} + +void test_volatile_path(skiatest::Reporter* reporter, const Geo& geo) { + SkPath vPath = geo.path(); + vPath.setIsVolatile(true); + + SkPaint dashAndStroke; + dashAndStroke.setPathEffect(make_dash()); + dashAndStroke.setStrokeWidth(2.f); + dashAndStroke.setStyle(SkPaint::kStroke_Style); + TestCase volatileCase(reporter, vPath, dashAndStroke); + // We expect a shape made from a volatile path to have a key iff the shape is recognized + // as a specialized geometry. + if (geo.isNonPath(dashAndStroke)) { + REPORTER_ASSERT(reporter, SkToBool(volatileCase.baseKey().count())); + // In this case all the keys should be identical to the non-volatile case. + TestCase nonVolatileCase(reporter, geo.path(), dashAndStroke); + volatileCase.compare(reporter, nonVolatileCase, TestCase::kAllSame_ComparisonExpecation); + } else { + // None of the keys should be valid. + REPORTER_ASSERT(reporter, !SkToBool(volatileCase.baseKey().count())); + REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectKey().count())); + REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedFullStyleKey().count())); + REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectThenStrokeKey().count())); + } +} + +void test_path_effect_makes_empty_shape(skiatest::Reporter* reporter, const Geo& geo) { + /** + * This path effect returns an empty path. + */ + class EmptyPathEffect : SkPathEffect { + public: + bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, + const SkRect* cullR) const override { + dst->reset(); + return true; + } + void computeFastBounds(SkRect* dst, const SkRect& src) const override { + dst->setEmpty(); + } + static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new EmptyPathEffect); } + Factory getFactory() const override { return nullptr; } + void toString(SkString*) const override {} + private: + EmptyPathEffect() {} + }; + + SkPath emptyPath; + GrShape emptyShape(emptyPath); + Key emptyKey; + make_key(&emptyKey, emptyShape); + REPORTER_ASSERT(reporter, emptyShape.isEmpty()); + + SkPaint pe; + pe.setPathEffect(EmptyPathEffect::Make()); + TestCase geoCase(geo, pe, reporter); + REPORTER_ASSERT(reporter, geoCase.appliedFullStyleKey() == emptyKey); + REPORTER_ASSERT(reporter, geoCase.appliedPathEffectKey() == emptyKey); + REPORTER_ASSERT(reporter, geoCase.appliedPathEffectThenStrokeKey() == emptyKey); + REPORTER_ASSERT(reporter, geoCase.appliedPathEffectShape().isEmpty()); + REPORTER_ASSERT(reporter, geoCase.appliedFullStyleShape().isEmpty()); + + SkPaint peStroke; + peStroke.setPathEffect(EmptyPathEffect::Make()); + peStroke.setStrokeWidth(2.f); + peStroke.setStyle(SkPaint::kStroke_Style); + TestCase geoPEStrokeCase(geo, peStroke, reporter); + REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() == emptyKey); + REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == emptyKey); + REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectThenStrokeKey() == emptyKey); + REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().isEmpty()); + REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleShape().isEmpty()); +} + +void test_path_effect_fails(skiatest::Reporter* reporter, const Geo& geo) { + /** + * This path effect always fails to apply. + */ + class FailurePathEffect : SkPathEffect { + public: + bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, + const SkRect* cullR) const override { + return false; + } + void computeFastBounds(SkRect* dst, const SkRect& src) const override { + *dst = src; + } + static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new FailurePathEffect); } + Factory getFactory() const override { return nullptr; } + void toString(SkString*) const override {} + private: + FailurePathEffect() {} + }; + + SkPaint fill; + TestCase fillCase(geo, fill, reporter); + + SkPaint pe; + pe.setPathEffect(FailurePathEffect::Make()); + TestCase peCase(geo, pe, reporter); + + SkPaint stroke; + stroke.setStrokeWidth(2.f); + stroke.setStyle(SkPaint::kStroke_Style); + TestCase strokeCase(geo, stroke, reporter); + + SkPaint peStroke = stroke; + peStroke.setPathEffect(FailurePathEffect::Make()); + TestCase peStrokeCase(geo, peStroke, reporter); + + // In general the path effect failure can cause some of the TestCase::compare() tests to fail + // for at least two reasons: 1) We will initially treat the shape as unkeyable because of the + // path effect, but then when the path effect fails we can key it. 2) GrShape will change its + // mind about whether a unclosed rect is actually rect. The path effect initially bars us from + // closing it but after the effect fails we can (for the fill+pe case). This causes different + // routes through GrShape to have equivalent but different representations of the path (closed + // or not) but that fill the same. + SkPath a; + SkPath b; + fillCase.appliedPathEffectShape().asPath(&a); + peCase.appliedPathEffectShape().asPath(&b); + REPORTER_ASSERT(reporter, paths_fill_same(a, b)); + + fillCase.appliedFullStyleShape().asPath(&a); + peCase.appliedFullStyleShape().asPath(&b); + REPORTER_ASSERT(reporter, paths_fill_same(a, b)); + + strokeCase.appliedPathEffectShape().asPath(&a); + peStrokeCase.appliedPathEffectShape().asPath(&b); + REPORTER_ASSERT(reporter, paths_fill_same(a, b)); + + strokeCase.appliedFullStyleShape().asPath(&a); + peStrokeCase.appliedFullStyleShape().asPath(&b); + REPORTER_ASSERT(reporter, paths_fill_same(a, b)); +} + +void test_empty_shape(skiatest::Reporter* reporter) { + SkPath emptyPath; + SkPaint fill; + TestCase fillEmptyCase(reporter, emptyPath, fill); + REPORTER_ASSERT(reporter, fillEmptyCase.baseShape().isEmpty()); + REPORTER_ASSERT(reporter, fillEmptyCase.appliedPathEffectShape().isEmpty()); + REPORTER_ASSERT(reporter, fillEmptyCase.appliedFullStyleShape().isEmpty()); + + Key emptyKey(fillEmptyCase.baseKey()); + REPORTER_ASSERT(reporter, emptyKey.count()); + TestCase::SelfExpectations expectations; + expectations.fStrokeApplies = false; + expectations.fPEHasEffect = false; + // This will test whether applying style preserves emptiness + fillEmptyCase.testExpectations(reporter, expectations); + + // Stroking an empty path should have no effect + SkPath emptyPath2; + SkPaint stroke; + stroke.setStrokeWidth(2.f); + stroke.setStyle(SkPaint::kStroke_Style); + TestCase strokeEmptyCase(reporter, emptyPath2, stroke); + strokeEmptyCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation); + + // Dashing and stroking an empty path should have no effect + SkPath emptyPath3; + SkPaint dashAndStroke; + dashAndStroke.setPathEffect(make_dash()); + dashAndStroke.setStrokeWidth(2.f); + dashAndStroke.setStyle(SkPaint::kStroke_Style); + TestCase dashAndStrokeEmptyCase(reporter, emptyPath3, dashAndStroke); + dashAndStrokeEmptyCase.compare(reporter, fillEmptyCase, + TestCase::kAllSame_ComparisonExpecation); + + // A shape made from an empty rrect should behave the same as an empty path. + SkRRect emptyRRect = SkRRect::MakeRect(SkRect::MakeEmpty()); + REPORTER_ASSERT(reporter, emptyRRect.getType() == SkRRect::kEmpty_Type); + TestCase dashAndStrokeEmptyRRectCase(reporter, emptyRRect, dashAndStroke); + dashAndStrokeEmptyRRectCase.compare(reporter, fillEmptyCase, + TestCase::kAllSame_ComparisonExpecation); + + // Same for a rect. + SkRect emptyRect = SkRect::MakeEmpty(); + TestCase dashAndStrokeEmptyRectCase(reporter, emptyRect, dashAndStroke); + dashAndStrokeEmptyRectCase.compare(reporter, fillEmptyCase, + TestCase::kAllSame_ComparisonExpecation); +} + +// rect and oval types have rrect start indices that collapse to the same point. Here we select the +// canonical point in these cases. +unsigned canonicalize_rrect_start(int s, const SkRRect& rrect) { + switch (rrect.getType()) { + case SkRRect::kRect_Type: + return (s + 1) & 0b110; + case SkRRect::kOval_Type: + return s & 0b110; + default: + return s; + } +} + +void test_rrect(skiatest::Reporter* r, const SkRRect& rrect) { + enum Style { + kFill, + kStroke, + kHairline, + kStrokeAndFill + }; + + // SkStrokeRec has no default cons., so init with kFill before calling the setters below. + SkStrokeRec strokeRecs[4] { SkStrokeRec::kFill_InitStyle, SkStrokeRec::kFill_InitStyle, + SkStrokeRec::kFill_InitStyle, SkStrokeRec::kFill_InitStyle}; + strokeRecs[kFill].setFillStyle(); + strokeRecs[kStroke].setStrokeStyle(2.f); + strokeRecs[kHairline].setHairlineStyle(); + strokeRecs[kStrokeAndFill].setStrokeStyle(3.f, true); + // Use a bevel join to avoid complications of stroke+filled rects becoming filled rects before + // applyStyle() is called. + strokeRecs[kStrokeAndFill].setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 1.f); + sk_sp<SkPathEffect> dashEffect = make_dash(); + + static constexpr Style kStyleCnt = static_cast<Style>(SK_ARRAY_COUNT(strokeRecs)); + + auto index = [](bool inverted, + SkPath::Direction dir, + unsigned start, + Style style, + bool dash) -> int { + return inverted * (2 * 8 * kStyleCnt * 2) + + dir * ( 8 * kStyleCnt * 2) + + start * ( kStyleCnt * 2) + + style * ( 2) + + dash; + }; + static const SkPath::Direction kSecondDirection = static_cast<SkPath::Direction>(1); + const int cnt = index(true, kSecondDirection, 7, static_cast<Style>(kStyleCnt - 1), true) + 1; + SkAutoTArray<GrShape> shapes(cnt); + for (bool inverted : {false, true}) { + for (SkPath::Direction dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (unsigned start = 0; start < 8; ++start) { + for (Style style : {kFill, kStroke, kHairline, kStrokeAndFill}) { + for (bool dash : {false, true}) { + sk_sp<SkPathEffect> pe = dash ? dashEffect : nullptr; + shapes[index(inverted, dir, start, style, dash)] = + GrShape(rrect, dir, start, SkToBool(inverted), + GrStyle(strokeRecs[style], std::move(pe))); + } + } + } + } + } + + // Get the keys for some example shape instances that we'll use for comparision against the + // rest. + static constexpr SkPath::Direction kExamplesDir = SkPath::kCW_Direction; + static constexpr unsigned kExamplesStart = 0; + const GrShape& exampleFillCase = shapes[index(false, kExamplesDir, kExamplesStart, kFill, + false)]; + Key exampleFillCaseKey; + make_key(&exampleFillCaseKey, exampleFillCase); + + const GrShape& exampleStrokeAndFillCase = shapes[index(false, kExamplesDir, kExamplesStart, + kStrokeAndFill, false)]; + Key exampleStrokeAndFillCaseKey; + make_key(&exampleStrokeAndFillCaseKey, exampleStrokeAndFillCase); + + const GrShape& exampleInvFillCase = shapes[index(true, kExamplesDir, kExamplesStart, kFill, + false)]; + Key exampleInvFillCaseKey; + make_key(&exampleInvFillCaseKey, exampleInvFillCase); + + const GrShape& exampleInvStrokeAndFillCase = shapes[index(true, kExamplesDir, kExamplesStart, + kStrokeAndFill, false)]; + Key exampleInvStrokeAndFillCaseKey; + make_key(&exampleInvStrokeAndFillCaseKey, exampleInvStrokeAndFillCase); + + const GrShape& exampleStrokeCase = shapes[index(false, kExamplesDir, kExamplesStart, kStroke, + false)]; + Key exampleStrokeCaseKey; + make_key(&exampleStrokeCaseKey, exampleStrokeCase); + + const GrShape& exampleInvStrokeCase = shapes[index(true, kExamplesDir, kExamplesStart, kStroke, + false)]; + Key exampleInvStrokeCaseKey; + make_key(&exampleInvStrokeCaseKey, exampleInvStrokeCase); + + const GrShape& exampleHairlineCase = shapes[index(false, kExamplesDir, kExamplesStart, + kHairline, false)]; + Key exampleHairlineCaseKey; + make_key(&exampleHairlineCaseKey, exampleHairlineCase); + + const GrShape& exampleInvHairlineCase = shapes[index(true, kExamplesDir, kExamplesStart, + kHairline, false)]; + Key exampleInvHairlineCaseKey; + make_key(&exampleInvHairlineCaseKey, exampleInvHairlineCase); + + // These are dummy initializations to suppress warnings. + SkRRect queryRR = SkRRect::MakeEmpty(); + SkPath::Direction queryDir = SkPath::kCW_Direction; + unsigned queryStart = ~0U; + bool queryInverted = true; + + REPORTER_ASSERT(r, exampleFillCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted)); + REPORTER_ASSERT(r, queryRR == rrect); + REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir); + REPORTER_ASSERT(r, 0 == queryStart); + REPORTER_ASSERT(r, !queryInverted); + + REPORTER_ASSERT(r, exampleInvFillCase.asRRect(&queryRR, &queryDir, &queryStart, + &queryInverted)); + REPORTER_ASSERT(r, queryRR == rrect); + REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir); + REPORTER_ASSERT(r, 0 == queryStart); + REPORTER_ASSERT(r, queryInverted); + + REPORTER_ASSERT(r, exampleStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart, + &queryInverted)); + REPORTER_ASSERT(r, queryRR == rrect); + REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir); + REPORTER_ASSERT(r, 0 == queryStart); + REPORTER_ASSERT(r, !queryInverted); + + REPORTER_ASSERT(r, exampleInvStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart, + &queryInverted)); + REPORTER_ASSERT(r, queryRR == rrect); + REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir); + REPORTER_ASSERT(r, 0 == queryStart); + REPORTER_ASSERT(r, queryInverted); + + REPORTER_ASSERT(r, exampleHairlineCase.asRRect(&queryRR, &queryDir, &queryStart, + &queryInverted)); + REPORTER_ASSERT(r, queryRR == rrect); + REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir); + REPORTER_ASSERT(r, 0 == queryStart); + REPORTER_ASSERT(r, !queryInverted); + + REPORTER_ASSERT(r, exampleInvHairlineCase.asRRect(&queryRR, &queryDir, &queryStart, + &queryInverted)); + REPORTER_ASSERT(r, queryRR == rrect); + REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir); + REPORTER_ASSERT(r, 0 == queryStart); + REPORTER_ASSERT(r, queryInverted); + + REPORTER_ASSERT(r, exampleStrokeCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted)); + REPORTER_ASSERT(r, queryRR == rrect); + REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir); + REPORTER_ASSERT(r, 0 == queryStart); + REPORTER_ASSERT(r, !queryInverted); + + REPORTER_ASSERT(r, exampleInvStrokeCase.asRRect(&queryRR, &queryDir, &queryStart, + &queryInverted)); + REPORTER_ASSERT(r, queryRR == rrect); + REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir); + REPORTER_ASSERT(r, 0 == queryStart); + REPORTER_ASSERT(r, queryInverted); + + // Remember that the key reflects the geometry before styling is applied. + REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvFillCaseKey); + REPORTER_ASSERT(r, exampleFillCaseKey == exampleStrokeAndFillCaseKey); + REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvStrokeAndFillCaseKey); + REPORTER_ASSERT(r, exampleFillCaseKey == exampleStrokeCaseKey); + REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvStrokeCaseKey); + REPORTER_ASSERT(r, exampleFillCaseKey == exampleHairlineCaseKey); + REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvHairlineCaseKey); + REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvFillCaseKey); + REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvStrokeCaseKey); + REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvHairlineCaseKey); + + for (bool inverted : {false, true}) { + for (SkPath::Direction dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (unsigned start = 0; start < 8; ++start) { + for (bool dash : {false, true}) { + const GrShape& fillCase = shapes[index(inverted, dir, start, kFill, dash)]; + Key fillCaseKey; + make_key(&fillCaseKey, fillCase); + + const GrShape& strokeAndFillCase = shapes[index(inverted, dir, start, + kStrokeAndFill, dash)]; + Key strokeAndFillCaseKey; + make_key(&strokeAndFillCaseKey, strokeAndFillCase); + + // Both fill and stroke-and-fill shapes must respect the inverseness and both + // ignore dashing. + REPORTER_ASSERT(r, !fillCase.style().pathEffect()); + REPORTER_ASSERT(r, !strokeAndFillCase.style().pathEffect()); + TestCase a(fillCase, r); + TestCase b(inverted ? exampleInvFillCase : exampleFillCase, r); + TestCase c(strokeAndFillCase, r); + TestCase d(inverted ? exampleInvStrokeAndFillCase + : exampleStrokeAndFillCase, r); + a.compare(r, b, TestCase::kAllSame_ComparisonExpecation); + c.compare(r, d, TestCase::kAllSame_ComparisonExpecation); + + const GrShape& strokeCase = shapes[index(inverted, dir, start, kStroke, dash)]; + const GrShape& hairlineCase = shapes[index(inverted, dir, start, kHairline, + dash)]; + + TestCase e(strokeCase, r); + TestCase g(hairlineCase, r); + + // Both hairline and stroke shapes must respect the dashing. + if (dash) { + // Dashing always ignores the inverseness. skbug.com/5421 + TestCase f(exampleStrokeCase, r); + TestCase h(exampleHairlineCase, r); + unsigned expectedStart = canonicalize_rrect_start(start, rrect); + REPORTER_ASSERT(r, strokeCase.style().pathEffect()); + REPORTER_ASSERT(r, hairlineCase.style().pathEffect()); + + REPORTER_ASSERT(r, strokeCase.asRRect(&queryRR, &queryDir, &queryStart, + &queryInverted)); + REPORTER_ASSERT(r, queryRR == rrect); + REPORTER_ASSERT(r, queryDir == dir); + REPORTER_ASSERT(r, queryStart == expectedStart); + REPORTER_ASSERT(r, !queryInverted); + REPORTER_ASSERT(r, hairlineCase.asRRect(&queryRR, &queryDir, &queryStart, + &queryInverted)); + REPORTER_ASSERT(r, queryRR == rrect); + REPORTER_ASSERT(r, queryDir == dir); + REPORTER_ASSERT(r, queryStart == expectedStart); + REPORTER_ASSERT(r, !queryInverted); + + // The pre-style case for the dash will match the non-dash example iff the + // dir and start match (dir=cw, start=0). + if (0 == expectedStart && SkPath::kCW_Direction == dir) { + e.compare(r, f, TestCase::kSameUpToPE_ComparisonExpecation); + g.compare(r, h, TestCase::kSameUpToPE_ComparisonExpecation); + } else { + e.compare(r, f, TestCase::kAllDifferent_ComparisonExpecation); + g.compare(r, h, TestCase::kAllDifferent_ComparisonExpecation); + } + } else { + TestCase f(inverted ? exampleInvStrokeCase : exampleStrokeCase, r); + TestCase h(inverted ? exampleInvHairlineCase : exampleHairlineCase, r); + REPORTER_ASSERT(r, !strokeCase.style().pathEffect()); + REPORTER_ASSERT(r, !hairlineCase.style().pathEffect()); + e.compare(r, f, TestCase::kAllSame_ComparisonExpecation); + g.compare(r, h, TestCase::kAllSame_ComparisonExpecation); + } + } + } + } + } +} + +void test_lines(skiatest::Reporter* r) { + static constexpr SkPoint kA { 1, 1}; + static constexpr SkPoint kB { 5, -9}; + static constexpr SkPoint kC {-3, 17}; + + SkPath lineAB; + lineAB.moveTo(kA); + lineAB.lineTo(kB); + + SkPath lineBA; + lineBA.moveTo(kB); + lineBA.lineTo(kA); + + SkPath lineAC; + lineAC.moveTo(kB); + lineAC.lineTo(kC); + + SkPath invLineAB = lineAB; + invLineAB.setFillType(SkPath::kInverseEvenOdd_FillType); + + SkPaint fill; + SkPaint stroke; + stroke.setStyle(SkPaint::kStroke_Style); + stroke.setStrokeWidth(2.f); + SkPaint hairline; + hairline.setStyle(SkPaint::kStroke_Style); + hairline.setStrokeWidth(0.f); + SkPaint dash = stroke; + dash.setPathEffect(make_dash()); + + TestCase fillAB(r, lineAB, fill); + TestCase fillEmpty(r, SkPath(), fill); + fillAB.compare(r, fillEmpty, TestCase::kAllSame_ComparisonExpecation); + REPORTER_ASSERT(r, !fillAB.baseShape().asLine(nullptr, nullptr)); + + TestCase strokeAB(r, lineAB, stroke); + TestCase strokeBA(r, lineBA, stroke); + TestCase strokeAC(r, lineAC, stroke); + + TestCase hairlineAB(r, lineAB, hairline); + TestCase hairlineBA(r, lineBA, hairline); + TestCase hairlineAC(r, lineAC, hairline); + + TestCase dashAB(r, lineAB, dash); + TestCase dashBA(r, lineBA, dash); + TestCase dashAC(r, lineAC, dash); + + strokeAB.compare(r, fillAB, TestCase::kAllDifferent_ComparisonExpecation); + + strokeAB.compare(r, strokeBA, TestCase::kAllSame_ComparisonExpecation); + strokeAB.compare(r, strokeAC, TestCase::kAllDifferent_ComparisonExpecation); + + hairlineAB.compare(r, hairlineBA, TestCase::kAllSame_ComparisonExpecation); + hairlineAB.compare(r, hairlineAC, TestCase::kAllDifferent_ComparisonExpecation); + + dashAB.compare(r, dashBA, TestCase::kAllDifferent_ComparisonExpecation); + dashAB.compare(r, dashAC, TestCase::kAllDifferent_ComparisonExpecation); + + strokeAB.compare(r, hairlineAB, TestCase::kSameUpToStroke_ComparisonExpecation); + + // One of dashAB or dashBA should have the same line as strokeAB. It depends upon how + // GrShape canonicalizes line endpoints (when it can, i.e. when not dashed). + bool canonicalizeAsAB; + SkPoint canonicalPts[2] {kA, kB}; + // Init these to suppress warnings. + bool inverted = true; + SkPoint pts[2] {{0, 0}, {0, 0}}; + REPORTER_ASSERT(r, strokeAB.baseShape().asLine(pts, &inverted) && !inverted); + if (pts[0] == kA && pts[1] == kB) { + canonicalizeAsAB = true; + } else if (pts[1] == kA && pts[0] == kB) { + canonicalizeAsAB = false; + SkTSwap(canonicalPts[0], canonicalPts[1]); + } else { + ERRORF(r, "Should return pts (a,b) or (b, a)"); + return; + }; + + strokeAB.compare(r, canonicalizeAsAB ? dashAB : dashBA, + TestCase::kSameUpToPE_ComparisonExpecation); + REPORTER_ASSERT(r, strokeAB.baseShape().asLine(pts, &inverted) && !inverted && + pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]); + REPORTER_ASSERT(r, hairlineAB.baseShape().asLine(pts, &inverted) && !inverted && + pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]); + REPORTER_ASSERT(r, dashAB.baseShape().asLine(pts, &inverted) && !inverted && + pts[0] == kA && pts[1] == kB); + REPORTER_ASSERT(r, dashBA.baseShape().asLine(pts, &inverted) && !inverted && + pts[0] == kB && pts[1] == kA); + + + TestCase strokeInvAB(r, invLineAB, stroke); + TestCase hairlineInvAB(r, invLineAB, hairline); + TestCase dashInvAB(r, invLineAB, dash); + strokeInvAB.compare(r, strokeAB, TestCase::kAllDifferent_ComparisonExpecation); + hairlineInvAB.compare(r, hairlineAB, TestCase::kAllDifferent_ComparisonExpecation); + // Dashing ignores inverse. + dashInvAB.compare(r, dashAB, TestCase::kAllSame_ComparisonExpecation); + + REPORTER_ASSERT(r, strokeInvAB.baseShape().asLine(pts, &inverted) && inverted && + pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]); + REPORTER_ASSERT(r, hairlineInvAB.baseShape().asLine(pts, &inverted) && inverted && + pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]); + // Dashing ignores inverse. + REPORTER_ASSERT(r, dashInvAB.baseShape().asLine(pts, &inverted) && !inverted && + pts[0] == kA && pts[1] == kB); + +} + +static void test_stroked_lines(skiatest::Reporter* r) { + // Paints to try + SkPaint buttCap; + buttCap.setStyle(SkPaint::kStroke_Style); + buttCap.setStrokeWidth(4); + buttCap.setStrokeCap(SkPaint::kButt_Cap); + + SkPaint squareCap = buttCap; + squareCap.setStrokeCap(SkPaint::kSquare_Cap); + + SkPaint roundCap = buttCap; + roundCap.setStrokeCap(SkPaint::kRound_Cap); + + // vertical + SkPath linePath; + linePath.moveTo(4, 4); + linePath.lineTo(4, 5); + + SkPaint fill; + + TestCase(r, linePath, buttCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 4, 6, 5), fill), + TestCase::kAllSame_ComparisonExpecation); + + TestCase(r, linePath, squareCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 2, 6, 7), fill), + TestCase::kAllSame_ComparisonExpecation); + + TestCase(r, linePath, roundCap).compare(r, + TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 7), 2, 2), fill), + TestCase::kAllSame_ComparisonExpecation); + + // horizontal + linePath.reset(); + linePath.moveTo(4, 4); + linePath.lineTo(5, 4); + + TestCase(r, linePath, buttCap).compare(r, TestCase(r, SkRect::MakeLTRB(4, 2, 5, 6), fill), + TestCase::kAllSame_ComparisonExpecation); + TestCase(r, linePath, squareCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 2, 7, 6), fill), + TestCase::kAllSame_ComparisonExpecation); + TestCase(r, linePath, roundCap).compare(r, + TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 7, 6), 2, 2), fill), + TestCase::kAllSame_ComparisonExpecation); + + // point + linePath.reset(); + linePath.moveTo(4, 4); + linePath.lineTo(4, 4); + + TestCase(r, linePath, buttCap).compare(r, TestCase(r, SkRect::MakeEmpty(), fill), + TestCase::kAllSame_ComparisonExpecation); + TestCase(r, linePath, squareCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 2, 6, 6), fill), + TestCase::kAllSame_ComparisonExpecation); + TestCase(r, linePath, roundCap).compare(r, + TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 6), 2, 2), fill), + TestCase::kAllSame_ComparisonExpecation); +} + +static void test_short_path_keys(skiatest::Reporter* r) { + SkPaint paints[4]; + paints[1].setStyle(SkPaint::kStroke_Style); + paints[1].setStrokeWidth(5.f); + paints[2].setStyle(SkPaint::kStroke_Style); + paints[2].setStrokeWidth(0.f); + paints[3].setStyle(SkPaint::kStrokeAndFill_Style); + paints[3].setStrokeWidth(5.f); + + auto compare = [r, &paints] (const SkPath& pathA, const SkPath& pathB, + TestCase::ComparisonExpecation expectation) { + SkPath volatileA = pathA; + SkPath volatileB = pathB; + volatileA.setIsVolatile(true); + volatileB.setIsVolatile(true); + for (const SkPaint& paint : paints) { + REPORTER_ASSERT(r, !GrShape(volatileA, paint).hasUnstyledKey()); + REPORTER_ASSERT(r, !GrShape(volatileB, paint).hasUnstyledKey()); + for (PathGeo::Invert invert : {PathGeo::Invert::kNo, PathGeo::Invert::kYes}) { + TestCase caseA(PathGeo(pathA, invert), paint, r); + TestCase caseB(PathGeo(pathB, invert), paint, r); + caseA.compare(r, caseB, expectation); + } + } + }; + + SkPath pathA; + SkPath pathB; + + // Two identical paths + pathA.lineTo(10.f, 10.f); + pathA.conicTo(20.f, 20.f, 20.f, 30.f, 0.7f); + + pathB.lineTo(10.f, 10.f); + pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.7f); + compare(pathA, pathB, TestCase::kAllSame_ComparisonExpecation); + + // Give path b a different point + pathB.reset(); + pathB.lineTo(10.f, 10.f); + pathB.conicTo(21.f, 20.f, 20.f, 30.f, 0.7f); + compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation); + + // Give path b a different conic weight + pathB.reset(); + pathB.lineTo(10.f, 10.f); + pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.6f); + compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation); + + // Give path b an extra lineTo verb + pathB.reset(); + pathB.lineTo(10.f, 10.f); + pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.6f); + pathB.lineTo(50.f, 50.f); + compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation); + + // Give path b a close + pathB.reset(); + pathB.lineTo(10.f, 10.f); + pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.7f); + pathB.close(); + compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation); +} + +DEF_TEST(GrShape, reporter) { + SkTArray<std::unique_ptr<Geo>> geos; + SkTArray<std::unique_ptr<RRectPathGeo>> rrectPathGeos; + + for (auto r : { SkRect::MakeWH(10, 20), + SkRect::MakeWH(-10, -20), + SkRect::MakeWH(-10, 20), + SkRect::MakeWH(10, -20)}) { + geos.emplace_back(new RectGeo(r)); + SkPath rectPath; + rectPath.addRect(r); + geos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes, + PathGeo::Invert::kNo)); + geos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes, + PathGeo::Invert::kYes)); + rrectPathGeos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes, + PathGeo::Invert::kNo)); + } + for (auto rr : { SkRRect::MakeRect(SkRect::MakeWH(10, 10)), + SkRRect::MakeRectXY(SkRect::MakeWH(10, 10), 3, 4), + SkRRect::MakeOval(SkRect::MakeWH(20, 20))}) { + geos.emplace_back(new RRectGeo(rr)); + test_rrect(reporter, rr); + SkPath rectPath; + rectPath.addRRect(rr); + geos.emplace_back(new RRectPathGeo(rectPath, rr, RRectPathGeo::RRectForStroke::kYes, + PathGeo::Invert::kNo)); + geos.emplace_back(new RRectPathGeo(rectPath, rr, RRectPathGeo::RRectForStroke::kYes, + PathGeo::Invert::kYes)); + rrectPathGeos.emplace_back(new RRectPathGeo(rectPath, rr, + RRectPathGeo::RRectForStroke::kYes, + PathGeo::Invert::kNo)); + } + + SkPath openRectPath; + openRectPath.moveTo(0, 0); + openRectPath.lineTo(10, 0); + openRectPath.lineTo(10, 10); + openRectPath.lineTo(0, 10); + geos.emplace_back(new RRectPathGeo(openRectPath, SkRect::MakeWH(10, 10), + RRectPathGeo::RRectForStroke::kNo, PathGeo::Invert::kNo)); + geos.emplace_back(new RRectPathGeo(openRectPath, SkRect::MakeWH(10, 10), + RRectPathGeo::RRectForStroke::kNo, PathGeo::Invert::kYes)); + rrectPathGeos.emplace_back(new RRectPathGeo(openRectPath, SkRect::MakeWH(10, 10), + RRectPathGeo::RRectForStroke::kNo, + PathGeo::Invert::kNo)); + + SkPath quadPath; + quadPath.quadTo(10, 10, 5, 8); + geos.emplace_back(new PathGeo(quadPath, PathGeo::Invert::kNo)); + geos.emplace_back(new PathGeo(quadPath, PathGeo::Invert::kYes)); + + SkPath linePath; + linePath.lineTo(10, 10); + geos.emplace_back(new PathGeo(linePath, PathGeo::Invert::kNo)); + geos.emplace_back(new PathGeo(linePath, PathGeo::Invert::kYes)); + + // Horizontal and vertical paths become rrects when stroked. + SkPath vLinePath; + vLinePath.lineTo(0, 10); + geos.emplace_back(new PathGeo(vLinePath, PathGeo::Invert::kNo)); + geos.emplace_back(new PathGeo(vLinePath, PathGeo::Invert::kYes)); + + SkPath hLinePath; + hLinePath.lineTo(10, 0); + geos.emplace_back(new PathGeo(hLinePath, PathGeo::Invert::kNo)); + geos.emplace_back(new PathGeo(hLinePath, PathGeo::Invert::kYes)); + + for (int i = 0; i < geos.count(); ++i) { + test_basic(reporter, *geos[i]); + test_scale(reporter, *geos[i]); + test_dash_fill(reporter, *geos[i]); + test_null_dash(reporter, *geos[i]); + // Test modifying various stroke params. + test_stroke_param<SkScalar>( + reporter, *geos[i], + [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);}, + SkIntToScalar(2), SkIntToScalar(4)); + test_stroke_join(reporter, *geos[i]); + test_stroke_cap(reporter, *geos[i]); + test_miter_limit(reporter, *geos[i]); + test_path_effect_makes_rrect(reporter, *geos[i]); + test_unknown_path_effect(reporter, *geos[i]); + test_path_effect_makes_empty_shape(reporter, *geos[i]); + test_path_effect_fails(reporter, *geos[i]); + test_make_hairline_path_effect(reporter, *geos[i]); + test_volatile_path(reporter, *geos[i]); + } + + for (int i = 0; i < rrectPathGeos.count(); ++i) { + const RRectPathGeo& rrgeo = *rrectPathGeos[i]; + SkPaint fillPaint; + TestCase fillPathCase(reporter, rrgeo.path(), fillPaint); + SkRRect rrect; + REPORTER_ASSERT(reporter, rrgeo.isNonPath(fillPaint) == + fillPathCase.baseShape().asRRect(&rrect, nullptr, nullptr, + nullptr)); + if (rrgeo.isNonPath(fillPaint)) { + TestCase fillPathCase2(reporter, rrgeo.path(), fillPaint); + REPORTER_ASSERT(reporter, rrect == rrgeo.rrect()); + TestCase fillRRectCase(reporter, rrect, fillPaint); + fillPathCase2.compare(reporter, fillRRectCase, + TestCase::kAllSame_ComparisonExpecation); + } + SkPaint strokePaint; + strokePaint.setStrokeWidth(3.f); + strokePaint.setStyle(SkPaint::kStroke_Style); + TestCase strokePathCase(reporter, rrgeo.path(), strokePaint); + if (rrgeo.isNonPath(strokePaint)) { + REPORTER_ASSERT(reporter, strokePathCase.baseShape().asRRect(&rrect, nullptr, nullptr, + nullptr)); + REPORTER_ASSERT(reporter, rrect == rrgeo.rrect()); + TestCase strokeRRectCase(reporter, rrect, strokePaint); + strokePathCase.compare(reporter, strokeRRectCase, + TestCase::kAllSame_ComparisonExpecation); + } + } + + // Test a volatile empty path. + test_volatile_path(reporter, PathGeo(SkPath(), PathGeo::Invert::kNo)); + + test_empty_shape(reporter); + + test_lines(reporter); + + test_stroked_lines(reporter); + + test_short_path_keys(reporter); +} + +#endif
diff --git a/src/third_party/skia/tests/GrSurfaceTest.cpp b/src/third_party/skia/tests/GrSurfaceTest.cpp index a2ed629..d67f103 100644 --- a/src/third_party/skia/tests/GrSurfaceTest.cpp +++ b/src/third_party/skia/tests/GrSurfaceTest.cpp
@@ -5,57 +5,240 @@ * found in the LICENSE file. */ +#include "SkTypes.h" + #if SK_SUPPORT_GPU #include "GrContext.h" -#include "GrContextFactory.h" -#include "GrRenderTarget.h" +#include "GrContextPriv.h" +#include "GrGpu.h" +#include "GrResourceProvider.h" +#include "GrTest.h" #include "GrTexture.h" -#include "SkTypes.h" +#include "GrSurfacePriv.h" +#include "SkMipMap.h" #include "Test.h" -DEF_GPUTEST(GrSurface, reporter, factory) { - GrContext* context = factory->get(GrContextFactory::kNull_GLContextType); - if (context) { - GrTextureDesc desc; - desc.fConfig = kSkia8888_GrPixelConfig; - desc.fFlags = kRenderTarget_GrTextureFlagBit; - desc.fWidth = 256; - desc.fHeight = 256; - desc.fSampleCnt = 0; - GrSurface* texRT1 = context->createUncachedTexture(desc, NULL, 0); - GrSurface* texRT2 = context->createUncachedTexture(desc, NULL, 0); - desc.fFlags = kNone_GrTextureFlags; - GrSurface* tex1 = context->createUncachedTexture(desc, NULL, 0); +// Tests that GrSurface::asTexture(), GrSurface::asRenderTarget(), and static upcasting of texture +// and render targets to GrSurface all work as expected. +DEF_GPUTEST_FOR_NULLGL_CONTEXT(GrSurface, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + GrSurfaceDesc desc; + desc.fConfig = kRGBA_8888_GrPixelConfig; + desc.fFlags = kRenderTarget_GrSurfaceFlag; + desc.fWidth = 256; + desc.fHeight = 256; + desc.fSampleCnt = 0; + sk_sp<GrSurface> texRT1 = context->resourceProvider()->createTexture(desc, SkBudgeted::kNo); - REPORTER_ASSERT(reporter, texRT1->isSameAs(texRT1)); - REPORTER_ASSERT(reporter, texRT1->isSameAs(texRT1->asRenderTarget())); - REPORTER_ASSERT(reporter, texRT1->asRenderTarget()->isSameAs(texRT1)); - REPORTER_ASSERT(reporter, !texRT2->isSameAs(texRT1)); - REPORTER_ASSERT(reporter, !texRT2->asRenderTarget()->isSameAs(texRT1)); - REPORTER_ASSERT(reporter, !texRT2->isSameAs(texRT1->asRenderTarget())); - REPORTER_ASSERT(reporter, !texRT2->isSameAs(tex1)); - REPORTER_ASSERT(reporter, !texRT2->asRenderTarget()->isSameAs(tex1)); + REPORTER_ASSERT(reporter, texRT1.get() == texRT1->asRenderTarget()); + REPORTER_ASSERT(reporter, texRT1.get() == texRT1->asTexture()); + REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT1->asRenderTarget()) == + texRT1->asTexture()); + REPORTER_ASSERT(reporter, texRT1->asRenderTarget() == + static_cast<GrSurface*>(texRT1->asTexture())); + REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT1->asRenderTarget()) == + static_cast<GrSurface*>(texRT1->asTexture())); - GrBackendTextureDesc backendDesc; - backendDesc.fConfig = kSkia8888_GrPixelConfig; - backendDesc.fFlags = kRenderTarget_GrBackendTextureFlag; - backendDesc.fWidth = 256; - backendDesc.fHeight = 256; - backendDesc.fSampleCnt = 0; - backendDesc.fTextureHandle = 5; - GrSurface* externalTexRT = context->wrapBackendTexture(backendDesc); - REPORTER_ASSERT(reporter, externalTexRT->isSameAs(externalTexRT)); - REPORTER_ASSERT(reporter, externalTexRT->isSameAs(externalTexRT->asRenderTarget())); - REPORTER_ASSERT(reporter, externalTexRT->asRenderTarget()->isSameAs(externalTexRT)); - REPORTER_ASSERT(reporter, !externalTexRT->isSameAs(texRT1)); - REPORTER_ASSERT(reporter, !externalTexRT->asRenderTarget()->isSameAs(texRT1)); + desc.fFlags = kNone_GrSurfaceFlags; + sk_sp<GrTexture> tex1 = context->resourceProvider()->createTexture(desc, SkBudgeted::kNo); + REPORTER_ASSERT(reporter, nullptr == tex1->asRenderTarget()); + REPORTER_ASSERT(reporter, tex1.get() == tex1->asTexture()); + REPORTER_ASSERT(reporter, static_cast<GrSurface*>(tex1.get()) == tex1->asTexture()); - texRT1->unref(); - texRT2->unref(); - tex1->unref(); - externalTexRT->unref(); + GrBackendObject backendTexHandle = context->getGpu()->createTestingOnlyBackendTexture( + nullptr, 256, 256, kRGBA_8888_GrPixelConfig); + GrBackendTexture backendTex = GrTest::CreateBackendTexture(context->contextPriv().getBackend(), + 256, + 256, + kRGBA_8888_GrPixelConfig, + backendTexHandle); + + sk_sp<GrSurface> texRT2 = context->resourceProvider()->wrapRenderableBackendTexture( + backendTex, kTopLeft_GrSurfaceOrigin, 0, kBorrow_GrWrapOwnership); + + REPORTER_ASSERT(reporter, texRT2.get() == texRT2->asRenderTarget()); + REPORTER_ASSERT(reporter, texRT2.get() == texRT2->asTexture()); + REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT2->asRenderTarget()) == + texRT2->asTexture()); + REPORTER_ASSERT(reporter, texRT2->asRenderTarget() == + static_cast<GrSurface*>(texRT2->asTexture())); + REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT2->asRenderTarget()) == + static_cast<GrSurface*>(texRT2->asTexture())); + + context->getGpu()->deleteTestingOnlyBackendTexture(backendTexHandle); +} + +// This test checks that the isConfigTexturable and isConfigRenderable are +// consistent with createTexture's result. +DEF_GPUTEST_FOR_ALL_CONTEXTS(GrSurfaceRenderability, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + GrResourceProvider* resourceProvider = context->resourceProvider(); + const GrCaps* caps = context->caps(); + + GrPixelConfig configs[] = { + kUnknown_GrPixelConfig, + kAlpha_8_GrPixelConfig, + kGray_8_GrPixelConfig, + kRGB_565_GrPixelConfig, + kRGBA_4444_GrPixelConfig, + kRGBA_8888_GrPixelConfig, + kBGRA_8888_GrPixelConfig, + kSRGBA_8888_GrPixelConfig, + kSBGRA_8888_GrPixelConfig, + kRGBA_8888_sint_GrPixelConfig, + kRGBA_float_GrPixelConfig, + kRG_float_GrPixelConfig, + kAlpha_half_GrPixelConfig, + kRGBA_half_GrPixelConfig, + }; + SkASSERT(kGrPixelConfigCnt == SK_ARRAY_COUNT(configs)); + + GrSurfaceDesc desc; + desc.fWidth = 64; + desc.fHeight = 64; + + // Enough space for the first mip of our largest pixel config + const size_t pixelBufferSize = desc.fWidth * desc.fHeight * + GrBytesPerPixel(kRGBA_float_GrPixelConfig); + std::unique_ptr<char[]> pixelData(new char[pixelBufferSize]); + memset(pixelData.get(), 0, pixelBufferSize); + + // We re-use the same mip level objects (with updated pointers and rowBytes) for each config + const int levelCount = SkMipMap::ComputeLevelCount(desc.fWidth, desc.fHeight) + 1; + std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[levelCount]); + + for (GrPixelConfig config : configs) { + for (GrSurfaceOrigin origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) { + desc.fFlags = kNone_GrSurfaceFlags; + desc.fOrigin = origin; + desc.fSampleCnt = 0; + desc.fConfig = config; + + sk_sp<GrSurface> tex = resourceProvider->createTexture(desc, SkBudgeted::kNo); + REPORTER_ASSERT(reporter, SkToBool(tex.get()) == caps->isConfigTexturable(desc.fConfig)); + + size_t rowBytes = desc.fWidth * GrBytesPerPixel(desc.fConfig); + for (int i = 0; i < levelCount; ++i) { + texels[i].fPixels = pixelData.get(); + texels[i].fRowBytes = rowBytes >> i; + } + sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferredMipMap(resourceProvider, + desc, SkBudgeted::kNo, + texels.get(), + levelCount); + REPORTER_ASSERT(reporter, SkToBool(proxy.get()) == + (caps->isConfigTexturable(desc.fConfig) && + caps->mipMapSupport() && + !GrPixelConfigIsSint(desc.fConfig))); + + desc.fFlags = kRenderTarget_GrSurfaceFlag; + tex = resourceProvider->createTexture(desc, SkBudgeted::kNo); + REPORTER_ASSERT(reporter, SkToBool(tex.get()) == caps->isConfigRenderable(config, false)); + + desc.fSampleCnt = 4; + tex = resourceProvider->createTexture(desc, SkBudgeted::kNo); + REPORTER_ASSERT(reporter, SkToBool(tex.get()) == caps->isConfigRenderable(config, true)); + } } } +#include "GrDrawingManager.h" +#include "GrSurfaceProxy.h" +#include "GrTextureContext.h" + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(InitialTextureClear, reporter, context_info) { + static constexpr int kSize = 100; + GrSurfaceDesc desc; + desc.fWidth = desc.fHeight = kSize; + std::unique_ptr<uint32_t[]> data(new uint32_t[kSize * kSize]); + GrContext* context = context_info.grContext(); + for (int c = 0; c <= kLast_GrPixelConfig; ++c) { + desc.fConfig = static_cast<GrPixelConfig>(c); + if (!context_info.grContext()->caps()->isConfigTexturable(desc.fConfig)) { + continue; + } + desc.fFlags = kPerformInitialClear_GrSurfaceFlag; + for (bool rt : {false, true}) { + if (rt && !context->caps()->isConfigRenderable(desc.fConfig, false)) { + continue; + } + desc.fFlags |= rt ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags; + for (bool mipped : {false, true}) { + desc.fIsMipMapped = mipped; + for (GrSurfaceOrigin origin : + {kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) { + desc.fOrigin = origin; + for (bool approx : {false, true}) { + auto resourceProvider = context->resourceProvider(); + // Try directly creating the texture. + // Do this twice in an attempt to hit the cache on the second time through. + for (int i = 0; i < 2; ++i) { + sk_sp<GrTexture> tex; + if (approx) { + tex = sk_sp<GrTexture>( + resourceProvider->createApproxTexture(desc, 0)); + } else { + tex = resourceProvider->createTexture(desc, SkBudgeted::kYes); + } + if (!tex) { + continue; + } + auto proxy = GrSurfaceProxy::MakeWrapped(std::move(tex)); + auto texCtx = context->contextPriv().makeWrappedSurfaceContext( + std::move(proxy), nullptr); + SkImageInfo info = SkImageInfo::Make( + kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType); + memset(data.get(), 0xAB, kSize * kSize * sizeof(uint32_t)); + if (texCtx->readPixels(info, data.get(), 0, 0, 0)) { + uint32_t cmp = GrPixelConfigIsOpaque(desc.fConfig) ? 0xFF000000 : 0; + for (int i = 0; i < kSize * kSize; ++i) { + if (cmp != data.get()[i]) { + ERRORF(reporter, "Failed on config %d", desc.fConfig); + break; + } + } + } + memset(data.get(), 0xBC, kSize * kSize * sizeof(uint32_t)); + // Here we overwrite the texture so that the second time through we + // test against recycling without reclearing. + if (0 == i) { + texCtx->writePixels(info, data.get(), 0, 0, 0); + } + } + context->purgeAllUnlockedResources(); + + // Try creating the texture as a deferred proxy. + for (int i = 0; i < 2; ++i) { + auto surfCtx = context->contextPriv().makeDeferredSurfaceContext( + desc, approx ? SkBackingFit::kApprox : SkBackingFit::kExact, + SkBudgeted::kYes); + if (!surfCtx) { + continue; + } + SkImageInfo info = SkImageInfo::Make( + kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType); + memset(data.get(), 0xAB, kSize * kSize * sizeof(uint32_t)); + if (surfCtx->readPixels(info, data.get(), 0, 0, 0)) { + uint32_t cmp = GrPixelConfigIsOpaque(desc.fConfig) ? 0xFF000000 : 0; + for (int i = 0; i < kSize * kSize; ++i) { + if (cmp != data.get()[i]) { + ERRORF(reporter, "Failed on config %d", desc.fConfig); + break; + } + } + } + // Here we overwrite the texture so that the second time through we + // test against recycling without reclearing. + if (0 == i) { + surfCtx->writePixels(info, data.get(), 0, 0, 0); + } + } + context->purgeAllUnlockedResources(); + } + } + } + } + } +} #endif
diff --git a/src/third_party/skia/tests/GrTBSearchTest.cpp b/src/third_party/skia/tests/GrTBSearchTest.cpp deleted file mode 100644 index d057807..0000000 --- a/src/third_party/skia/tests/GrTBSearchTest.cpp +++ /dev/null
@@ -1,40 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -// This is a GPU-backend specific test -#if SK_SUPPORT_GPU - -#include "Test.h" - -// If we aren't inheriting these as #defines from elsewhere, -// clang demands they be declared before we #include the template -// that relies on them. -static bool LT(const int& elem, int value) { - return elem < value; -} -static bool EQ(const int& elem, int value) { - return elem == value; -} - -#include "GrTBSearch.h" - -DEF_TEST(GrTBSearch, reporter) { - const int array[] = { - 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99 - }; - - for (int n = 0; n < static_cast<int>(SK_ARRAY_COUNT(array)); ++n) { - for (int i = 0; i < n; i++) { - int index = GrTBSearch<int, int>(array, n, array[i]); - REPORTER_ASSERT(reporter, index == (int) i); - index = GrTBSearch<int, int>(array, n, -array[i]); - REPORTER_ASSERT(reporter, index < 0); - } - } -} - -#endif
diff --git a/src/third_party/skia/tests/GrTRecorderTest.cpp b/src/third_party/skia/tests/GrTRecorderTest.cpp new file mode 100644 index 0000000..0be0f68 --- /dev/null +++ b/src/third_party/skia/tests/GrTRecorderTest.cpp
@@ -0,0 +1,296 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkMatrix.h" +#include "SkRandom.h" +#include "SkString.h" +#include "Test.h" + +#if SK_SUPPORT_GPU + +#include "GrTRecorder.h" + +//////////////////////////////////////////////////////////////////////////////// + +static int activeRecorderItems = 0; + +class IntWrapper { +public: + IntWrapper() {} + IntWrapper(int value) : fValue(value) {} + operator int() { return fValue; } +private: + int fValue; +}; + +static void test_empty_back_and_pop(skiatest::Reporter* reporter) { + SkRandom rand; + for (int data = 0; data < 2; ++data) { + // Do this with different starting sizes to have different alignment between blocks and pops. + // pops. We want to test poping the first guy off, guys in the middle of the block, and the + // first guy on a non-head block. + for (int j = 0; j < 8; ++j) { + GrTRecorder<IntWrapper, int> recorder(j); + + REPORTER_ASSERT(reporter, recorder.empty()); + + for (int i = 0; i < 100; ++i) { + if (data) { + REPORTER_ASSERT(reporter, i == *GrNEW_APPEND_TO_RECORDER(recorder, + IntWrapper, (i))); + } else { + REPORTER_ASSERT(reporter, i == + *GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder, + IntWrapper, (i), + rand.nextULessThan(10))); + } + REPORTER_ASSERT(reporter, !recorder.empty()); + REPORTER_ASSERT(reporter, i == recorder.back()); + if (0 == (i % 7)) { + recorder.pop_back(); + if (i > 0) { + REPORTER_ASSERT(reporter, !recorder.empty()); + REPORTER_ASSERT(reporter, i-1 == recorder.back()); + } + } + } + + REPORTER_ASSERT(reporter, !recorder.empty()); + recorder.reset(); + REPORTER_ASSERT(reporter, recorder.empty()); + } + } +} + +struct ExtraData { + typedef GrTRecorder<ExtraData, int> Recorder; + + ExtraData(int i) : fData(i) { + int* extraData = this->extraData(); + for (int j = 0; j < i; j++) { + extraData[j] = i; + } + ++activeRecorderItems; + } + ~ExtraData() { + --activeRecorderItems; + } + int* extraData() { + return reinterpret_cast<int*>(Recorder::GetDataForItem(this)); + } + int fData; +}; + +static void test_extra_data(skiatest::Reporter* reporter) { + ExtraData::Recorder recorder(0); + for (int i = 0; i < 100; ++i) { + GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder, ExtraData, (i), i * sizeof(int)); + } + REPORTER_ASSERT(reporter, 100 == activeRecorderItems); + + ExtraData::Recorder::Iter iter(recorder); + for (int i = 0; i < 100; ++i) { + REPORTER_ASSERT(reporter, iter.next()); + REPORTER_ASSERT(reporter, i == iter->fData); + for (int j = 0; j < i; j++) { + REPORTER_ASSERT(reporter, i == iter->extraData()[j]); + } + } + REPORTER_ASSERT(reporter, !iter.next()); + + ExtraData::Recorder::ReverseIter reverseIter(recorder); + for (int i = 99; i >= 0; --i) { + REPORTER_ASSERT(reporter, i == reverseIter->fData); + for (int j = 0; j < i; j++) { + REPORTER_ASSERT(reporter, i == reverseIter->extraData()[j]); + } + REPORTER_ASSERT(reporter, reverseIter.previous() == !!i); + } + + recorder.reset(); + REPORTER_ASSERT(reporter, 0 == activeRecorderItems); +} + +enum ClassType { + kBase_ClassType, + kSubclass_ClassType, + kSubSubclass_ClassType, + kSubclassExtraData_ClassType, + kSubclassEmpty_ClassType, + + kNumClassTypes +}; + +class Base { +public: + typedef GrTRecorder<Base, void*> Recorder; + + Base() { + fMatrix.reset(); + ++activeRecorderItems; + } + + virtual ~Base() { --activeRecorderItems; } + + virtual ClassType getType() { return kBase_ClassType; } + + virtual void validate(skiatest::Reporter* reporter) const { + REPORTER_ASSERT(reporter, fMatrix.isIdentity()); + } + +private: + SkMatrix fMatrix; +}; + +class Subclass : public Base { +public: + Subclass() : fString("Lorem ipsum dolor sit amet") {} + + virtual ClassType getType() { return kSubclass_ClassType; } + + virtual void validate(skiatest::Reporter* reporter) const { + Base::validate(reporter); + REPORTER_ASSERT(reporter, !strcmp("Lorem ipsum dolor sit amet", fString.c_str())); + } + +private: + SkString fString; +}; + +class SubSubclass : public Subclass { +public: + SubSubclass() : fInt(1234), fFloat(1.234f) {} + + virtual ClassType getType() { return kSubSubclass_ClassType; } + + virtual void validate(skiatest::Reporter* reporter) const { + Subclass::validate(reporter); + REPORTER_ASSERT(reporter, 1234 == fInt); + REPORTER_ASSERT(reporter, 1.234f == fFloat); + } + +private: + int fInt; + float fFloat; +}; + +class SubclassExtraData : public Base { +public: + SubclassExtraData(int length) : fLength(length) { + int* data = reinterpret_cast<int*>(Recorder::GetDataForItem(this)); + for (int i = 0; i < fLength; ++i) { + data[i] = ValueAt(i); + } + } + + virtual ClassType getType() { return kSubclassExtraData_ClassType; } + + virtual void validate(skiatest::Reporter* reporter) const { + Base::validate(reporter); + const int* data = reinterpret_cast<const int*>(Recorder::GetDataForItem(this)); + for (int i = 0; i < fLength; ++i) { + REPORTER_ASSERT(reporter, ValueAt(i) == data[i]); + } + } + +private: + static int ValueAt(uint64_t i) { return static_cast<int>(123456789 + 987654321 * i); } + int fLength; +}; + +class SubclassEmpty : public Base { +public: + virtual ClassType getType() { return kSubclassEmpty_ClassType; } +}; + +class Order { +public: + Order() { this->reset(); } + void reset() { fCurrent = 0; } + ClassType next() { + fCurrent = 1664525 * fCurrent + 1013904223; + return static_cast<ClassType>(fCurrent % kNumClassTypes); + } +private: + uint32_t fCurrent; +}; +static void test_subclasses_iters(skiatest::Reporter*, Order&, Base::Recorder::Iter&, + Base::Recorder::ReverseIter&, int = 0); +static void test_subclasses(skiatest::Reporter* reporter) { + Base::Recorder recorder(1024); + + Order order; + for (int i = 0; i < 1000; i++) { + switch (order.next()) { + case kBase_ClassType: + GrNEW_APPEND_TO_RECORDER(recorder, Base, ()); + break; + + case kSubclass_ClassType: + GrNEW_APPEND_TO_RECORDER(recorder, Subclass, ()); + break; + + case kSubSubclass_ClassType: + GrNEW_APPEND_TO_RECORDER(recorder, SubSubclass, ()); + break; + + case kSubclassExtraData_ClassType: + GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder, SubclassExtraData, (i), sizeof(int) * i); + break; + + case kSubclassEmpty_ClassType: + GrNEW_APPEND_TO_RECORDER(recorder, SubclassEmpty, ()); + break; + + default: + ERRORF(reporter, "Invalid class type"); + break; + } + } + REPORTER_ASSERT(reporter, 1000 == activeRecorderItems); + + order.reset(); + Base::Recorder::Iter iter(recorder); + Base::Recorder::ReverseIter reverseIter(recorder); + + test_subclasses_iters(reporter, order, iter, reverseIter); + + REPORTER_ASSERT(reporter, !iter.next()); + + // Don't reset the recorder. It should automatically destruct all its items. +} +static void test_subclasses_iters(skiatest::Reporter* reporter, Order& order, + Base::Recorder::Iter& iter, + Base::Recorder::ReverseIter& reverseIter, int i) { + if (i >= 1000) { + return; + } + + ClassType classType = order.next(); + + REPORTER_ASSERT(reporter, iter.next()); + REPORTER_ASSERT(reporter, classType == iter->getType()); + iter->validate(reporter); + + test_subclasses_iters(reporter, order, iter, reverseIter, i + 1); + + REPORTER_ASSERT(reporter, classType == reverseIter->getType()); + reverseIter->validate(reporter); + REPORTER_ASSERT(reporter, reverseIter.previous() == !!i); +} + +DEF_GPUTEST(GrTRecorder, reporter, factory) { + test_empty_back_and_pop(reporter); + + test_extra_data(reporter); + REPORTER_ASSERT(reporter, 0 == activeRecorderItems); // test_extra_data should call reset(). + + test_subclasses(reporter); + REPORTER_ASSERT(reporter, 0 == activeRecorderItems); // Ensure ~GrTRecorder invokes dtors. +} + +#endif
diff --git a/src/third_party/skia/tests/GrTextureMipMapInvalidationTest.cpp b/src/third_party/skia/tests/GrTextureMipMapInvalidationTest.cpp new file mode 100644 index 0000000..c9c4d7e --- /dev/null +++ b/src/third_party/skia/tests/GrTextureMipMapInvalidationTest.cpp
@@ -0,0 +1,57 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTypes.h" + +#if SK_SUPPORT_GPU + +#include "GrContext.h" +#include "GrTexture.h" +#include "GrTexturePriv.h" +#include "SkCanvas.h" +#include "SkImage_Base.h" +#include "SkSurface.h" +#include "Test.h" + +// Tests that MIP maps are created and invalidated as expected when drawing to and from GrTextures. +DEF_GPUTEST_FOR_NULLGL_CONTEXT(GrTextureMipMapInvalidationTest, reporter, ctxInfo) { + auto isMipped = [] (SkSurface* surf) { + return surf->makeImageSnapshot()->getTexture()->texturePriv().hasMipMaps(); + }; + + auto mipsAreDirty = [] (SkSurface* surf) { + return surf->makeImageSnapshot()->getTexture()->texturePriv().mipMapsAreDirty(); + }; + + GrContext* context = ctxInfo.grContext(); + auto info = SkImageInfo::MakeN32Premul(256, 256); + auto surf1 = SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info); + auto surf2 = SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info); + // Draw something just in case we ever had a solid color optimization + surf1->getCanvas()->drawCircle(128, 128, 50, SkPaint()); + surf1->getCanvas()->flush(); + + // No mipmaps initially + REPORTER_ASSERT(reporter, !isMipped(surf1.get())); + + // Painting with downscale and medium filter quality should result in mipmap creation + SkPaint paint; + paint.setFilterQuality(kMedium_SkFilterQuality); + surf2->getCanvas()->scale(0.2f, 0.2f); + surf2->getCanvas()->drawImage(surf1->makeImageSnapshot(), 0, 0, &paint); + surf2->getCanvas()->flush(); + REPORTER_ASSERT(reporter, isMipped(surf1.get())); + REPORTER_ASSERT(reporter, !mipsAreDirty(surf1.get())); + + // Changing the contents of the surface should invalidate the mipmap, but not de-allocate + surf1->getCanvas()->drawCircle(128, 128, 100, SkPaint()); + surf1->getCanvas()->flush(); + REPORTER_ASSERT(reporter, isMipped(surf1.get())); + REPORTER_ASSERT(reporter, mipsAreDirty(surf1.get())); +} + +#endif
diff --git a/src/third_party/skia/tests/GradientTest.cpp b/src/third_party/skia/tests/GradientTest.cpp index e6fd7b9..3c62cb9 100644 --- a/src/third_party/skia/tests/GradientTest.cpp +++ b/src/third_party/skia/tests/GradientTest.cpp
@@ -6,12 +6,39 @@ */ #include "SkCanvas.h" +#include "SkColorPriv.h" #include "SkColorShader.h" #include "SkGradientShader.h" #include "SkShader.h" +#include "SkSurface.h" #include "SkTemplates.h" +#include "SkTLazy.h" #include "Test.h" +// https://code.google.com/p/chromium/issues/detail?id=448299 +// Giant (inverse) matrix causes overflow when converting/computing using 32.32 +// Before the fix, we would assert (and then crash). +static void test_big_grad(skiatest::Reporter* reporter) { + const SkColor colors[] = { SK_ColorRED, SK_ColorBLUE }; + const SkPoint pts[] = {{ 15, 14.7112684f }, { 0.709064007f, 12.6108112f }}; + SkPaint paint; + paint.setShader(SkGradientShader::MakeLinear(pts, colors, nullptr, 2, + SkShader::kClamp_TileMode)); + + SkBitmap bm; + bm.allocN32Pixels(2000, 1); + SkCanvas c(bm); + + const SkScalar affine[] = { + 1.06608627e-06f, 4.26434525e-07f, 6.2855f, 2.6611f, 273.4393f, 244.0046f + }; + SkMatrix matrix; + matrix.setAffine(affine); + c.concat(matrix); + + c.drawPaint(paint); +} + struct GradRec { int fColorCount; const SkColor* fColors; @@ -20,7 +47,7 @@ const SkScalar* fRadius; // 2 SkShader::TileMode fTileMode; - void gradCheck(skiatest::Reporter* reporter, SkShader* shader, + void gradCheck(skiatest::Reporter* reporter, const sk_sp<SkShader>& shader, SkShader::GradientInfo* info, SkShader::GradientType gt) const { SkAutoTMalloc<SkColor> colorStorage(fColorCount); @@ -41,90 +68,70 @@ }; -static void none_gradproc(skiatest::Reporter* reporter, const GradRec&) { - SkAutoTUnref<SkShader> s(SkShader::CreateEmptyShader()); - REPORTER_ASSERT(reporter, SkShader::kNone_GradientType == s->asAGradient(NULL)); +static void none_gradproc(skiatest::Reporter* reporter, const GradRec&, const GradRec&) { + sk_sp<SkShader> s(SkShader::MakeEmptyShader()); + REPORTER_ASSERT(reporter, SkShader::kNone_GradientType == s->asAGradient(nullptr)); } -static void color_gradproc(skiatest::Reporter* reporter, const GradRec& rec) { - SkAutoTUnref<SkShader> s(new SkColorShader(rec.fColors[0])); - REPORTER_ASSERT(reporter, SkShader::kColor_GradientType == s->asAGradient(NULL)); +static void color_gradproc(skiatest::Reporter* reporter, const GradRec& rec, const GradRec&) { + sk_sp<SkShader> s(new SkColorShader(rec.fColors[0])); + REPORTER_ASSERT(reporter, SkShader::kColor_GradientType == s->asAGradient(nullptr)); SkShader::GradientInfo info; - info.fColors = NULL; + info.fColors = nullptr; info.fColorCount = 0; s->asAGradient(&info); REPORTER_ASSERT(reporter, 1 == info.fColorCount); } -static void linear_gradproc(skiatest::Reporter* reporter, const GradRec& rec) { - SkAutoTUnref<SkShader> s(SkGradientShader::CreateLinear(rec.fPoint, - rec.fColors, - rec.fPos, - rec.fColorCount, - rec.fTileMode)); +static void linear_gradproc(skiatest::Reporter* reporter, const GradRec& buildRec, + const GradRec& checkRec) { + sk_sp<SkShader> s(SkGradientShader::MakeLinear(buildRec.fPoint, buildRec.fColors, buildRec.fPos, + buildRec.fColorCount, buildRec.fTileMode)); SkShader::GradientInfo info; - rec.gradCheck(reporter, s, &info, SkShader::kLinear_GradientType); - REPORTER_ASSERT(reporter, !memcmp(info.fPoint, rec.fPoint, 2 * sizeof(SkPoint))); + checkRec.gradCheck(reporter, s, &info, SkShader::kLinear_GradientType); + REPORTER_ASSERT(reporter, !memcmp(info.fPoint, checkRec.fPoint, 2 * sizeof(SkPoint))); } -static void radial_gradproc(skiatest::Reporter* reporter, const GradRec& rec) { - SkAutoTUnref<SkShader> s(SkGradientShader::CreateRadial(rec.fPoint[0], - rec.fRadius[0], - rec.fColors, - rec.fPos, - rec.fColorCount, - rec.fTileMode)); +static void radial_gradproc(skiatest::Reporter* reporter, const GradRec& buildRec, + const GradRec& checkRec) { + sk_sp<SkShader> s(SkGradientShader::MakeRadial(buildRec.fPoint[0], buildRec.fRadius[0], + buildRec.fColors, buildRec.fPos, + buildRec.fColorCount, buildRec.fTileMode)); SkShader::GradientInfo info; - rec.gradCheck(reporter, s, &info, SkShader::kRadial_GradientType); - REPORTER_ASSERT(reporter, info.fPoint[0] == rec.fPoint[0]); - REPORTER_ASSERT(reporter, info.fRadius[0] == rec.fRadius[0]); + checkRec.gradCheck(reporter, s, &info, SkShader::kRadial_GradientType); + REPORTER_ASSERT(reporter, info.fPoint[0] == checkRec.fPoint[0]); + REPORTER_ASSERT(reporter, info.fRadius[0] == checkRec.fRadius[0]); } -static void radial2_gradproc(skiatest::Reporter* reporter, const GradRec& rec) { - SkAutoTUnref<SkShader> s(SkGradientShader::CreateTwoPointRadial(rec.fPoint[0], - rec.fRadius[0], - rec.fPoint[1], - rec.fRadius[1], - rec.fColors, - rec.fPos, - rec.fColorCount, - rec.fTileMode)); +static void sweep_gradproc(skiatest::Reporter* reporter, const GradRec& buildRec, + const GradRec& checkRec) { + sk_sp<SkShader> s(SkGradientShader::MakeSweep(buildRec.fPoint[0].fX, buildRec.fPoint[0].fY, + buildRec.fColors, buildRec.fPos, + buildRec.fColorCount)); SkShader::GradientInfo info; - rec.gradCheck(reporter, s, &info, SkShader::kRadial2_GradientType); - REPORTER_ASSERT(reporter, !memcmp(info.fPoint, rec.fPoint, 2 * sizeof(SkPoint))); - REPORTER_ASSERT(reporter, !memcmp(info.fRadius, rec.fRadius, 2 * sizeof(SkScalar))); + checkRec.gradCheck(reporter, s, &info, SkShader::kSweep_GradientType); + REPORTER_ASSERT(reporter, info.fPoint[0] == checkRec.fPoint[0]); } -static void sweep_gradproc(skiatest::Reporter* reporter, const GradRec& rec) { - SkAutoTUnref<SkShader> s(SkGradientShader::CreateSweep(rec.fPoint[0].fX, - rec.fPoint[0].fY, - rec.fColors, - rec.fPos, - rec.fColorCount)); +static void conical_gradproc(skiatest::Reporter* reporter, const GradRec& buildRec, + const GradRec& checkRec) { + sk_sp<SkShader> s(SkGradientShader::MakeTwoPointConical(buildRec.fPoint[0], + buildRec.fRadius[0], + buildRec.fPoint[1], + buildRec.fRadius[1], + buildRec.fColors, + buildRec.fPos, + buildRec.fColorCount, + buildRec.fTileMode)); SkShader::GradientInfo info; - rec.gradCheck(reporter, s, &info, SkShader::kSweep_GradientType); - REPORTER_ASSERT(reporter, info.fPoint[0] == rec.fPoint[0]); -} - -static void conical_gradproc(skiatest::Reporter* reporter, const GradRec& rec) { - SkAutoTUnref<SkShader> s(SkGradientShader::CreateTwoPointConical(rec.fPoint[0], - rec.fRadius[0], - rec.fPoint[1], - rec.fRadius[1], - rec.fColors, - rec.fPos, - rec.fColorCount, - rec.fTileMode)); - - SkShader::GradientInfo info; - rec.gradCheck(reporter, s, &info, SkShader::kConical_GradientType); - REPORTER_ASSERT(reporter, !memcmp(info.fPoint, rec.fPoint, 2 * sizeof(SkPoint))); - REPORTER_ASSERT(reporter, !memcmp(info.fRadius, rec.fRadius, 2 * sizeof(SkScalar))); + checkRec.gradCheck(reporter, s, &info, SkShader::kConical_GradientType); + REPORTER_ASSERT(reporter, !memcmp(info.fPoint, checkRec.fPoint, 2 * sizeof(SkPoint))); + REPORTER_ASSERT(reporter, !memcmp(info.fRadius, checkRec.fRadius, 2 * sizeof(SkScalar))); } // Ensure that repeated color gradients behave like drawing a single color @@ -135,18 +142,12 @@ }; SkColor colors[] = { SK_ColorBLUE, SK_ColorBLUE }; const SkScalar pos[] = { 0, SK_Scalar1 }; - SkAutoTUnref<SkShader> s(SkGradientShader::CreateLinear(pts, - colors, - pos, - 2, - SkShader::kClamp_TileMode)); + SkPaint paint; + paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 2, SkShader::kClamp_TileMode)); SkBitmap outBitmap; outBitmap.allocN32Pixels(10, 1); - SkPaint paint; - paint.setShader(s.get()); SkCanvas canvas(outBitmap); canvas.drawPaint(paint); - SkAutoLockPixels alp(outBitmap); for (int i = 0; i < 10; i++) { // The following is commented out because it currently fails // Related bug: https://code.google.com/p/skia/issues/detail?id=1098 @@ -155,7 +156,7 @@ } } -typedef void (*GradProc)(skiatest::Reporter* reporter, const GradRec&); +typedef void (*GradProc)(skiatest::Reporter* reporter, const GradRec&, const GradRec&); static void TestGradientShaders(skiatest::Reporter* reporter) { static const SkColor gColors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE }; @@ -179,17 +180,371 @@ color_gradproc, linear_gradproc, radial_gradproc, - radial2_gradproc, sweep_gradproc, conical_gradproc, }; for (size_t i = 0; i < SK_ARRAY_COUNT(gProcs); ++i) { - gProcs[i](reporter, rec); + gProcs[i](reporter, rec, rec); + } +} + +static void TestGradientOptimization(skiatest::Reporter* reporter) { + static const struct { + GradProc fProc; + bool fIsClampRestricted; + } gProcInfo[] = { + { linear_gradproc , false }, + { radial_gradproc , false }, + { sweep_gradproc , true }, // sweep is funky in that it always pretends to be kClamp. + { conical_gradproc, false }, + }; + + static const SkColor gC_00[] = { 0xff000000, 0xff000000 }; + static const SkColor gC_01[] = { 0xff000000, 0xffffffff }; + static const SkColor gC_11[] = { 0xffffffff, 0xffffffff }; + static const SkColor gC_001[] = { 0xff000000, 0xff000000, 0xffffffff }; + static const SkColor gC_011[] = { 0xff000000, 0xffffffff, 0xffffffff }; + static const SkColor gC_0011[] = { 0xff000000, 0xff000000, 0xffffffff, 0xffffffff }; + + static const SkScalar gP_01[] = { 0, 1 }; + static const SkScalar gP_001[] = { 0, 0, 1 }; + static const SkScalar gP_011[] = { 0, 1, 1 }; + static const SkScalar gP_0x1[] = { 0, .5f, 1 }; + static const SkScalar gP_0011[] = { 0, 0, 1, 1 }; + + static const SkPoint gPts[] = { {0, 0}, {1, 1} }; + static const SkScalar gRadii[] = { 1, 2 }; + + static const struct { + const SkColor* fCol; + const SkScalar* fPos; + int fCount; + + const SkColor* fExpectedCol; + const SkScalar* fExpectedPos; + int fExpectedCount; + bool fRequiresNonClamp; + } gTests[] = { + { gC_001, gP_001, 3, gC_01, gP_01, 2, false }, + { gC_001, gP_011, 3, gC_00, gP_01, 2, true }, + { gC_001, gP_0x1, 3, gC_001, gP_0x1, 3, false }, + { gC_001, nullptr, 3, gC_001, gP_0x1, 3, false }, + + { gC_011, gP_001, 3, gC_11, gP_01, 2, true }, + { gC_011, gP_011, 3, gC_01, gP_01, 2, false }, + { gC_011, gP_0x1, 3, gC_011, gP_0x1, 3, false }, + { gC_011, nullptr, 3, gC_011, gP_0x1, 3, false }, + + { gC_0011, gP_0011, 4, gC_0011, gP_0011, 4, false }, + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gProcInfo); ++i) { + for (int mode = 0; mode < SkShader::kTileModeCount; ++mode) { + if (gProcInfo[i].fIsClampRestricted && mode != SkShader::kClamp_TileMode) { + continue; + } + + for (size_t t = 0; t < SK_ARRAY_COUNT(gTests); ++t) { + GradRec rec; + rec.fColorCount = gTests[t].fCount; + rec.fColors = gTests[t].fCol; + rec.fPos = gTests[t].fPos; + rec.fTileMode = static_cast<SkShader::TileMode>(mode); + rec.fPoint = gPts; + rec.fRadius = gRadii; + + GradRec expected = rec; + if (!gTests[t].fRequiresNonClamp || mode != SkShader::kClamp_TileMode) { + expected.fColorCount = gTests[t].fExpectedCount; + expected.fColors = gTests[t].fExpectedCol; + expected.fPos = gTests[t].fExpectedPos; + } + + gProcInfo[i].fProc(reporter, rec, expected); + } + } + } +} + +static void test_nearly_vertical(skiatest::Reporter* reporter) { + auto surface(SkSurface::MakeRasterN32Premul(200, 200)); + + const SkPoint pts[] = {{ 100, 50 }, { 100.0001f, 50000 }}; + const SkColor colors[] = { SK_ColorBLACK, SK_ColorWHITE }; + const SkScalar pos[] = { 0, 1 }; + SkPaint paint; + paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 2, SkShader::kClamp_TileMode)); + + surface->getCanvas()->drawPaint(paint); +} + +static void test_vertical(skiatest::Reporter* reporter) { + auto surface(SkSurface::MakeRasterN32Premul(200, 200)); + + const SkPoint pts[] = {{ 100, 50 }, { 100, 50 }}; + const SkColor colors[] = { SK_ColorBLACK, SK_ColorWHITE }; + const SkScalar pos[] = { 0, 1 }; + SkPaint paint; + paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 2, SkShader::kClamp_TileMode)); + + surface->getCanvas()->drawPaint(paint); +} + +// A linear gradient interval can, due to numerical imprecision (likely in the divide) +// finish an interval with the final fx not landing outside of [p0...p1]. +// The old code had an assert which this test triggered. +// We now explicitly clamp the resulting fx value. +static void test_linear_fuzz(skiatest::Reporter* reporter) { + auto surface(SkSurface::MakeRasterN32Premul(1300, 630)); + + const SkPoint pts[] = {{ 179.5f, -179.5f }, { 1074.5f, 715.5f }}; + const SkColor colors[] = { SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE }; + const SkScalar pos[] = {0, 0.200000003f, 0.800000012f, 1 }; + + SkPaint paint; + paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 4, SkShader::kClamp_TileMode)); + + SkRect r = {0, 83, 1254, 620}; + surface->getCanvas()->drawRect(r, paint); +} + +// https://bugs.chromium.org/p/skia/issues/detail?id=5023 +// We should still shade pixels for which the radius is exactly 0. +static void test_two_point_conical_zero_radius(skiatest::Reporter* reporter) { + auto surface(SkSurface::MakeRasterN32Premul(5, 5)); + surface->getCanvas()->clear(SK_ColorRED); + + const SkColor colors[] = { SK_ColorGREEN, SK_ColorBLUE }; + SkPaint p; + p.setShader(SkGradientShader::MakeTwoPointConical( + SkPoint::Make(2.5f, 2.5f), 0, + SkPoint::Make(3.0f, 3.0f), 10, + colors, nullptr, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode)); + surface->getCanvas()->drawPaint(p); + + // r == 0 for the center pixel. + // verify that we draw it (no red bleed) + SkPMColor centerPMColor; + surface->readPixels(SkImageInfo::MakeN32Premul(1, 1), ¢erPMColor, sizeof(SkPMColor), 2, 2); + REPORTER_ASSERT(reporter, SkGetPackedR32(centerPMColor) == 0); +} + +// http://crbug.com/599458 +static void test_clamping_overflow(skiatest::Reporter*) { + SkPaint p; + const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN }; + const SkPoint pts1[] = { SkPoint::Make(1001, 1000001), SkPoint::Make(1000.99f, 1000000) }; + + p.setShader(SkGradientShader::MakeLinear(pts1, colors, nullptr, 2, SkShader::kClamp_TileMode)); + + sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(50, 50)); + surface->getCanvas()->scale(100, 100); + surface->getCanvas()->drawPaint(p); + + const SkPoint pts2[] = { SkPoint::Make(10000.99f, 1000000), SkPoint::Make(10001, 1000001) }; + p.setShader(SkGradientShader::MakeLinear(pts2, colors, nullptr, 2, SkShader::kClamp_TileMode)); + surface->getCanvas()->drawPaint(p); + + // Passes if we don't trigger asserts. +} + +// http://crbug.com/636194 +static void test_degenerate_linear(skiatest::Reporter*) { + SkPaint p; + const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN }; + const SkPoint pts[] = { + SkPoint::Make(-46058024627067344430605278824628224.0f, 0), + SkPoint::Make(SK_ScalarMax, 0) + }; + + p.setShader(SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkShader::kClamp_TileMode)); + sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(50, 50)); + surface->getCanvas()->drawPaint(p); + + // Passes if we don't trigger asserts. +} + +// "Interesting" fuzzer values. +static void test_linear_fuzzer(skiatest::Reporter*) { + static const SkColor gColors0[] = { 0x30303030, 0x30303030 }; + static const SkColor gColors1[] = { 0x30303030, 0x30303030, 0x30303030 }; + + static const SkScalar gPos1[] = { 0, 0, 1 }; + + static const SkScalar gMatrix0[9] = { + 6.40969056e-10f, 0 , 6.40969056e-10f, + 0 , 4.42539023e-39f, 6.40969056e-10f, + 0 , 0 , 1 + }; + static const SkScalar gMatrix1[9] = { + -2.75294113f , 6.40969056e-10f, 6.40969056e-10f, + 6.40969056e-10f, 6.40969056e-10f, -3.32810161e+24f, + 6.40969056e-10f, 6.40969056e-10f, 0 + }; + static const SkScalar gMatrix2[9] = { + 7.93481258e+17f, 6.40969056e-10f, 6.40969056e-10f, + 6.40969056e-10f, 6.40969056e-10f, 6.40969056e-10f, + 6.40969056e-10f, 6.40969056e-10f, 0.688235283f + }; + static const SkScalar gMatrix3[9] = { + 1.89180674e+11f, 6.40969056e-10f, 6.40969056e-10f, + 6.40969056e-10f, 6.40969056e-10f, 6.40969056e-10f, + 6.40969056e-10f, 11276.0469f , 8.12524808e+20f + }; + + static const struct { + SkPoint fPts[2]; + const SkColor* fColors; + const SkScalar* fPos; + int fCount; + SkShader::TileMode fTileMode; + uint32_t fFlags; + const SkScalar* fLocalMatrix; + const SkScalar* fGlobalMatrix; + } gConfigs[] = { + { + {{0, -2.752941f}, {0, 0}}, + gColors0, + nullptr, + SK_ARRAY_COUNT(gColors0), + SkShader::kClamp_TileMode, + 0, + gMatrix0, + nullptr + }, + { + {{4.42539023e-39f, -4.42539023e-39f}, {9.78041162e-15f, 4.42539023e-39f}}, + gColors1, + gPos1, + SK_ARRAY_COUNT(gColors1), + SkShader::kClamp_TileMode, + 0, + nullptr, + gMatrix1 + }, + { + {{4.42539023e-39f, 6.40969056e-10f}, {6.40969056e-10f, 1.49237238e-19f}}, + gColors1, + gPos1, + SK_ARRAY_COUNT(gColors1), + SkShader::kClamp_TileMode, + 0, + nullptr, + gMatrix2 + }, + { + {{6.40969056e-10f, 6.40969056e-10f}, {6.40969056e-10f, -0.688235283f}}, + gColors0, + nullptr, + SK_ARRAY_COUNT(gColors0), + SkShader::kClamp_TileMode, + 0, + gMatrix3, + nullptr + }, + }; + + sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB(); + SkColorSpace* colorSpaces[] = { + nullptr, // hits the legacy gradient impl + srgb.get(), // triggers 4f/raster-pipeline + }; + + SkPaint paint; + + for (auto colorSpace : colorSpaces) { + + sk_sp<SkSurface> surface = SkSurface::MakeRaster(SkImageInfo::Make(100, 100, + kN32_SkColorType, + kPremul_SkAlphaType, + sk_ref_sp(colorSpace))); + SkCanvas* canvas = surface->getCanvas(); + + for (const auto& config : gConfigs) { + SkAutoCanvasRestore acr(canvas, false); + SkTLazy<SkMatrix> localMatrix; + if (config.fLocalMatrix) { + localMatrix.init(); + localMatrix.get()->set9(config.fLocalMatrix); + } + + paint.setShader(SkGradientShader::MakeLinear(config.fPts, + config.fColors, + config.fPos, + config.fCount, + config.fTileMode, + config.fFlags, + localMatrix.getMaybeNull())); + if (config.fGlobalMatrix) { + SkMatrix m; + m.set9(config.fGlobalMatrix); + canvas->save(); + canvas->concat(m); + } + + canvas->drawPaint(paint); + } + } +} + +static void test_sweep_fuzzer(skiatest::Reporter*) { + static const SkColor gColors0[] = { 0x30303030, 0x30303030, 0x30303030 }; + static const SkScalar gPos0[] = { -47919293023455565225163489280.0f, 0, 1 }; + static const SkScalar gMatrix0[9] = { + 1.12116716e-13f, 0 , 8.50489682e+16f, + 4.1917041e-41f , 3.51369881e-23f, -2.54344271e-26f, + 9.61111907e+17f, -3.35263808e-29f, -1.35659403e+14f + }; + static const struct { + SkPoint fCenter; + const SkColor* fColors; + const SkScalar* fPos; + int fCount; + const SkScalar* fGlobalMatrix; + } gConfigs[] = { + { + { 0, 0 }, + gColors0, + gPos0, + SK_ARRAY_COUNT(gColors0), + gMatrix0 + }, + }; + + sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(100, 100); + SkCanvas* canvas = surface->getCanvas(); + SkPaint paint; + + for (const auto& config : gConfigs) { + paint.setShader(SkGradientShader::MakeSweep(config.fCenter.x(), + config.fCenter.y(), + config.fColors, + config.fPos, + config.fCount)); + + SkAutoCanvasRestore acr(canvas, false); + if (config.fGlobalMatrix) { + SkMatrix m; + m.set9(config.fGlobalMatrix); + canvas->save(); + canvas->concat(m); + } + canvas->drawPaint(paint); } } DEF_TEST(Gradient, reporter) { TestGradientShaders(reporter); + TestGradientOptimization(reporter); TestConstantGradient(reporter); + test_big_grad(reporter); + test_nearly_vertical(reporter); + test_vertical(reporter); + test_linear_fuzz(reporter); + test_two_point_conical_zero_radius(reporter); + test_clamping_overflow(reporter); + test_degenerate_linear(reporter); + test_linear_fuzzer(reporter); + test_sweep_fuzzer(reporter); }
diff --git a/src/third_party/skia/tests/HSVRoundTripTest.cpp b/src/third_party/skia/tests/HSVRoundTripTest.cpp new file mode 100644 index 0000000..4d25895 --- /dev/null +++ b/src/third_party/skia/tests/HSVRoundTripTest.cpp
@@ -0,0 +1,30 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#include "SkColor.h" + +DEF_TEST(ColorToHSVRoundTrip, reporter) { + SkScalar hsv[3]; + for (U8CPU r = 0; r <= 255; r++) { + for (U8CPU g = 0; g <= 255; g++) { + for (U8CPU b = 0; b <= 255; b++) { + SkColor color = SkColorSetARGBInline(0xFF, r, g, b); + SkColorToHSV(color, hsv); + SkColor result = SkHSVToColor(0xFF, hsv); + if (result != color) { + ERRORF(reporter, "HSV roundtrip mismatch!\n" + "\toriginal: %X\n" + "\tHSV: %f, %f, %f\n" + "\tresult: %X\n", + color, hsv[0], hsv[1], hsv[2], result); + } + } + } + } +}
diff --git a/src/third_party/skia/tests/HashTest.cpp b/src/third_party/skia/tests/HashTest.cpp new file mode 100644 index 0000000..667f8ea --- /dev/null +++ b/src/third_party/skia/tests/HashTest.cpp
@@ -0,0 +1,179 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkChecksum.h" +#include "SkRefCnt.h" +#include "SkString.h" +#include "SkTHash.h" +#include "Test.h" + +// Tests use of const foreach(). map.count() is of course the better way to do this. +static int count(const SkTHashMap<int, double>& map) { + int n = 0; + map.foreach([&n](int, double) { n++; }); + return n; +} + +DEF_TEST(HashMap, r) { + SkTHashMap<int, double> map; + + map.set(3, 4.0); + REPORTER_ASSERT(r, map.count() == 1); + + REPORTER_ASSERT(r, map.approxBytesUsed() > 0); + + double* found = map.find(3); + REPORTER_ASSERT(r, found); + REPORTER_ASSERT(r, *found == 4.0); + + map.foreach([](int key, double* d){ *d = -key; }); + REPORTER_ASSERT(r, count(map) == 1); + + found = map.find(3); + REPORTER_ASSERT(r, found); + REPORTER_ASSERT(r, *found == -3.0); + + REPORTER_ASSERT(r, !map.find(2)); + + const int N = 20; + + for (int i = 0; i < N; i++) { + map.set(i, 2.0*i); + } + for (int i = 0; i < N; i++) { + double* found = map.find(i); + REPORTER_ASSERT(r, found); + REPORTER_ASSERT(r, *found == i*2.0); + } + for (int i = N; i < 2*N; i++) { + REPORTER_ASSERT(r, !map.find(i)); + } + + REPORTER_ASSERT(r, map.count() == N); + + for (int i = 0; i < N/2; i++) { + map.remove(i); + } + for (int i = 0; i < N; i++) { + double* found = map.find(i); + REPORTER_ASSERT(r, (found == nullptr) == (i < N/2)); + } + REPORTER_ASSERT(r, map.count() == N/2); + + map.reset(); + REPORTER_ASSERT(r, map.count() == 0); + + { + // Test that we don't leave dangling values in empty slots. + SkTHashMap<int, sk_sp<SkRefCnt>> refMap; + auto ref = sk_make_sp<SkRefCnt>(); + REPORTER_ASSERT(r, ref->unique()); + + refMap.set(0, ref); + REPORTER_ASSERT(r, refMap.count() == 1); + REPORTER_ASSERT(r, !ref->unique()); + + refMap.remove(0); + REPORTER_ASSERT(r, refMap.count() == 0); + REPORTER_ASSERT(r, ref->unique()); + } +} + +DEF_TEST(HashSet, r) { + SkTHashSet<SkString> set; + + set.add(SkString("Hello")); + set.add(SkString("World")); + + REPORTER_ASSERT(r, set.count() == 2); + + REPORTER_ASSERT(r, set.contains(SkString("Hello"))); + REPORTER_ASSERT(r, set.contains(SkString("World"))); + REPORTER_ASSERT(r, !set.contains(SkString("Goodbye"))); + + REPORTER_ASSERT(r, set.find(SkString("Hello"))); + REPORTER_ASSERT(r, *set.find(SkString("Hello")) == SkString("Hello")); + + set.remove(SkString("Hello")); + REPORTER_ASSERT(r, !set.contains(SkString("Hello"))); + REPORTER_ASSERT(r, set.count() == 1); + + set.reset(); + REPORTER_ASSERT(r, set.count() == 0); +} + +namespace { + +class CopyCounter { +public: + CopyCounter() : fID(0), fCounter(nullptr) {} + + CopyCounter(uint32_t id, uint32_t* counter) : fID(id), fCounter(counter) {} + + CopyCounter(const CopyCounter& other) + : fID(other.fID) + , fCounter(other.fCounter) { + SkASSERT(fCounter); + *fCounter += 1; + } + + void operator=(const CopyCounter& other) { + fID = other.fID; + fCounter = other.fCounter; + *fCounter += 1; + } + + CopyCounter(CopyCounter&& other) { *this = std::move(other); } + void operator=(CopyCounter&& other) { + fID = other.fID; + fCounter = other.fCounter; + } + + + bool operator==(const CopyCounter& other) const { + return fID == other.fID; + } + +private: + uint32_t fID; + uint32_t* fCounter; +}; + +struct HashCopyCounter { + uint32_t operator()(const CopyCounter&) const { + return 0; // let them collide, what do we care? + } +}; + +} + +DEF_TEST(HashSetCopyCounter, r) { + SkTHashSet<CopyCounter, HashCopyCounter> set; + + uint32_t globalCounter = 0; + CopyCounter copyCounter1(1, &globalCounter); + CopyCounter copyCounter2(2, &globalCounter); + REPORTER_ASSERT(r, globalCounter == 0); + + set.add(copyCounter1); + REPORTER_ASSERT(r, globalCounter == 1); + REPORTER_ASSERT(r, set.contains(copyCounter1)); + REPORTER_ASSERT(r, globalCounter == 1); + set.add(copyCounter1); + // We allow copies for same-value adds for now. + REPORTER_ASSERT(r, globalCounter == 2); + + set.add(copyCounter2); + REPORTER_ASSERT(r, globalCounter == 3); + REPORTER_ASSERT(r, set.contains(copyCounter1)); + REPORTER_ASSERT(r, set.contains(copyCounter2)); + REPORTER_ASSERT(r, globalCounter == 3); + set.add(copyCounter1); + set.add(copyCounter2); + // We allow copies for same-value adds for now. + REPORTER_ASSERT(r, globalCounter == 5); +}
diff --git a/src/third_party/skia/tests/HighContrastFilterTest.cpp b/src/third_party/skia/tests/HighContrastFilterTest.cpp new file mode 100644 index 0000000..7c3fc84 --- /dev/null +++ b/src/third_party/skia/tests/HighContrastFilterTest.cpp
@@ -0,0 +1,90 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkHighContrastFilter.h" +#include "Test.h" + +DEF_TEST(HighContrastFilter_FilterImage, reporter) { + SkHighContrastConfig config; + config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness; + + int w = 10, h = 10; + SkBitmap filterResult, paintResult; + + filterResult.allocN32Pixels(w, h); + SkCanvas canvasFilter(filterResult); + canvasFilter.clear(0x00000000); + + paintResult.allocN32Pixels(w, h); + SkCanvas canvasPaint(paintResult); + canvasPaint.clear(0x00000000); + + SkPaint paint; + paint.setColor(SK_ColorBLUE); + SkRect r = SkRect::MakeLTRB(SkIntToScalar(2), SkIntToScalar(2), + SkIntToScalar(8), SkIntToScalar(8)); + canvasPaint.drawRect(r, paint); + + paint.setColorFilter(SkHighContrastFilter::Make(config)); + canvasFilter.drawRect(r, paint); + + for (int y = r.top(); y < r.bottom(); ++y) { + for (int x = r.left(); x < r.right(); ++x) { + SkColor paintColor = paintResult.getColor(x, y); + SkColor filterColor = filterResult.getColor(x, y); + REPORTER_ASSERT( + reporter, filterColor == + paint.getColorFilter()->filterColor(paintColor)); + } + } +} + +DEF_TEST(HighContrastFilter_SanityCheck, reporter) { + SkHighContrastConfig config; + config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness; + sk_sp<SkColorFilter> filter = SkHighContrastFilter::Make(config); + + SkColor white_inverted = filter->filterColor(SK_ColorWHITE); + REPORTER_ASSERT(reporter, white_inverted == SK_ColorBLACK); + + SkColor black_inverted = filter->filterColor(SK_ColorBLACK); + REPORTER_ASSERT(reporter, black_inverted == SK_ColorWHITE); +} + +DEF_TEST(HighContrastFilter_InvalidInputs, reporter) { + SkHighContrastConfig config; + REPORTER_ASSERT(reporter, config.isValid()); + + // Valid invert style + config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertBrightness; + REPORTER_ASSERT(reporter, config.isValid()); + config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness; + REPORTER_ASSERT(reporter, config.isValid()); + sk_sp<SkColorFilter> filter = SkHighContrastFilter::Make(config); + REPORTER_ASSERT(reporter, filter); + + // Invalid invert style + config.fInvertStyle = static_cast<SkHighContrastConfig::InvertStyle>(999); + REPORTER_ASSERT(reporter, !config.isValid()); + filter = SkHighContrastFilter::Make(config); + REPORTER_ASSERT(reporter, !filter); + + // Valid contrast + config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertBrightness; + config.fContrast = 0.5f; + REPORTER_ASSERT(reporter, config.isValid()); + filter = SkHighContrastFilter::Make(config); + REPORTER_ASSERT(reporter, filter); + + // Invalid contrast + config.fContrast = 1.1f; + REPORTER_ASSERT(reporter, !config.isValid()); + filter = SkHighContrastFilter::Make(config); + REPORTER_ASSERT(reporter, !filter); +}
diff --git a/src/third_party/skia/tests/ICCTest.cpp b/src/third_party/skia/tests/ICCTest.cpp new file mode 100644 index 0000000..4bff654 --- /dev/null +++ b/src/third_party/skia/tests/ICCTest.cpp
@@ -0,0 +1,298 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Resources.h" +#include "SkColorSpace.h" +#include "SkColorSpacePriv.h" +#include "SkColorSpace_XYZ.h" +#include "SkData.h" +#include "SkICC.h" +#include "SkICCPriv.h" +#include "SkMatrix44.h" +#include "SkStream.h" +#include "Test.h" + +static bool almost_equal(float a, float b) { + return SkTAbs(a - b) < 0.001f; +} + +static inline void test_to_xyz_d50(skiatest::Reporter* r, SkICC* icc, bool shouldSucceed, + const float* reference) { + SkMatrix44 result(SkMatrix44::kUninitialized_Constructor); + REPORTER_ASSERT(r, shouldSucceed == icc->toXYZD50(&result)); + if (shouldSucceed) { + float resultVals[16]; + result.asColMajorf(resultVals); + for (int i = 0; i < 16; i++) { + REPORTER_ASSERT(r, almost_equal(resultVals[i], reference[i])); + } + } +} + +DEF_TEST(ICC_ToXYZD50, r) { + const float z30Reference[16] = { + 0.59825f, 0.27103f, 0.00603f, 0.0f, 0.22243f, 0.67447f, 0.07368f, 0.0f, 0.14352f, 0.05449f, + 0.74519f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, + }; + + sk_sp<SkData> data = SkData::MakeFromFileName( + GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str()); + sk_sp<SkICC> z30 = SkICC::Make(data->data(), data->size()); + test_to_xyz_d50(r, z30.get(), true, z30Reference); + + const float z32Reference[16] = { + 0.61583f, 0.28789f, 0.00513f, 0.0f, 0.20428f, 0.66972f, 0.06609f, 0.0f, 0.14409f, 0.04237f, + 0.75368f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, + }; + + data = SkData::MakeFromFileName( GetResourcePath("icc_profiles/HP_Z32x.icc").c_str()); + sk_sp<SkICC> z32 = SkICC::Make(data->data(), data->size()); + test_to_xyz_d50(r, z32.get(), true, z32Reference); + + data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperLeft.icc").c_str()); + sk_sp<SkICC> upperLeft = SkICC::Make(data->data(), data->size()); + test_to_xyz_d50(r, upperLeft.get(), false, z32Reference); + + data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperRight.icc").c_str()); + sk_sp<SkICC> upperRight = SkICC::Make(data->data(), data->size()); + test_to_xyz_d50(r, upperRight.get(), false, z32Reference); +} + +static inline void test_is_numerical_transfer_fn(skiatest::Reporter* r, SkICC* icc, + bool shouldSucceed, + const SkColorSpaceTransferFn& reference) { + SkColorSpaceTransferFn result; + REPORTER_ASSERT(r, shouldSucceed == icc->isNumericalTransferFn(&result)); + if (shouldSucceed) { + REPORTER_ASSERT(r, 0 == memcmp(&result, &reference, sizeof(SkColorSpaceTransferFn))); + } +} + +DEF_TEST(ICC_IsNumericalTransferFn, r) { + SkColorSpaceTransferFn referenceFn; + referenceFn.fA = 1.0f; + referenceFn.fB = 0.0f; + referenceFn.fC = 0.0f; + referenceFn.fD = 0.0f; + referenceFn.fE = 0.0f; + referenceFn.fF = 0.0f; + referenceFn.fG = 2.2f; + + sk_sp<SkData> data = SkData::MakeFromFileName( + GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str()); + sk_sp<SkICC> z30 = SkICC::Make(data->data(), data->size()); + test_is_numerical_transfer_fn(r, z30.get(), true, referenceFn); + + data = SkData::MakeFromFileName( GetResourcePath("icc_profiles/HP_Z32x.icc").c_str()); + sk_sp<SkICC> z32 = SkICC::Make(data->data(), data->size()); + test_is_numerical_transfer_fn(r, z32.get(), true, referenceFn); + + data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperLeft.icc").c_str()); + sk_sp<SkICC> upperLeft = SkICC::Make(data->data(), data->size()); + test_is_numerical_transfer_fn(r, upperLeft.get(), false, referenceFn); + + data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperRight.icc").c_str()); + sk_sp<SkICC> upperRight = SkICC::Make(data->data(), data->size()); + test_is_numerical_transfer_fn(r, upperRight.get(), false, referenceFn); +} + +static inline void test_write_icc(skiatest::Reporter* r, const SkColorSpaceTransferFn& fn, + const SkMatrix44& toXYZD50, SkColorSpace* reference, + bool writeToFile) { + sk_sp<SkData> profile = SkICC::WriteToICC(fn, toXYZD50); + if (writeToFile) { + SkFILEWStream stream("out.icc"); + stream.write(profile->data(), profile->size()); + } + + sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeICC(profile->data(), profile->size()); + REPORTER_ASSERT(r, SkColorSpace::Equals(reference, colorSpace.get())); +} + +DEF_TEST(ICC_WriteICC, r) { + SkColorSpaceTransferFn adobeFn; + adobeFn.fA = 1.0f; + adobeFn.fB = 0.0f; + adobeFn.fC = 0.0f; + adobeFn.fD = 0.0f; + adobeFn.fE = 0.0f; + adobeFn.fF = 0.0f; + adobeFn.fG = 2.2f; + SkMatrix44 adobeMatrix(SkMatrix44::kUninitialized_Constructor); + adobeMatrix.set3x3RowMajorf(gAdobeRGB_toXYZD50); + test_write_icc(r, adobeFn, adobeMatrix, + SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named).get(), false); + + SkColorSpaceTransferFn srgbFn; + srgbFn.fA = 1.0f / 1.055f; + srgbFn.fB = 0.055f / 1.055f; + srgbFn.fC = 1.0f / 12.92f; + srgbFn.fD = 0.04045f; + srgbFn.fE = 0.0f; + srgbFn.fF = 0.0f; + srgbFn.fG = 2.4f; + SkMatrix44 srgbMatrix(SkMatrix44::kUninitialized_Constructor); + srgbMatrix.set3x3RowMajorf(gSRGB_toXYZD50); + test_write_icc(r, srgbFn, srgbMatrix, SkColorSpace::MakeSRGB().get(), + false); + + SkString adobeTag = SkICCGetColorProfileTag(adobeFn, adobeMatrix); + SkString srgbTag = SkICCGetColorProfileTag(srgbFn, srgbMatrix); + REPORTER_ASSERT(r, adobeTag != srgbTag); + REPORTER_ASSERT(r, srgbTag.equals("sRGB")); + REPORTER_ASSERT(r, adobeTag.equals("AdobeRGB")); +} + +static inline void test_raw_transfer_fn(skiatest::Reporter* r, SkICC* icc) { + SkICC::Tables tables; + bool result = icc->rawTransferFnData(&tables); + REPORTER_ASSERT(r, result); + + REPORTER_ASSERT(r, 0.0f == tables.red()[0]); + REPORTER_ASSERT(r, 0.0f == tables.green()[0]); + REPORTER_ASSERT(r, 0.0f == tables.blue()[0]); + REPORTER_ASSERT(r, 1.0f == tables.red()[tables.fRed.fCount - 1]); + REPORTER_ASSERT(r, 1.0f == tables.green()[tables.fGreen.fCount - 1]); + REPORTER_ASSERT(r, 1.0f == tables.blue()[tables.fBlue.fCount - 1]); +} + +class ICCTest { +public: + static sk_sp<SkICC> MakeICC(sk_sp<SkColorSpace> space) { + return sk_sp<SkICC>(new SkICC(std::move(space))); + } + static sk_sp<SkICC> MakeICC(sk_sp<SkGammas> gammas) { + return MakeICC(sk_sp<SkColorSpace>(new SkColorSpace_XYZ( + kNonStandard_SkGammaNamed, std::move(gammas), + SkMatrix44(SkMatrix44::kIdentity_Constructor), nullptr))); + } +}; + +DEF_TEST(ICC_RawTransferFns, r) { + sk_sp<SkICC> srgb = ICCTest::MakeICC(SkColorSpace::MakeSRGB()); + test_raw_transfer_fn(r, srgb.get()); + + sk_sp<SkICC> adobe = + ICCTest::MakeICC(SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named)); + test_raw_transfer_fn(r, adobe.get()); + + // Lookup-table based gamma curves + constexpr size_t tableSize = 10; + void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(float) * tableSize); + sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(3)); + for (int i = 0; i < 3; ++i) { + gammas->fType[i] = SkGammas::Type::kTable_Type; + gammas->fData[i].fTable.fSize = tableSize; + gammas->fData[i].fTable.fOffset = 0; + } + + float* table = SkTAddOffset<float>(memory, sizeof(SkGammas)); + table[0] = 0.00f; + table[1] = 0.05f; + table[2] = 0.10f; + table[3] = 0.15f; + table[4] = 0.25f; + table[5] = 0.35f; + table[6] = 0.45f; + table[7] = 0.60f; + table[8] = 0.75f; + table[9] = 1.00f; + sk_sp<SkICC> tbl = ICCTest::MakeICC(gammas); + test_raw_transfer_fn(r, tbl.get()); + + // Parametric gamma curves + memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(SkColorSpaceTransferFn)); + gammas = sk_sp<SkGammas>(new (memory) SkGammas(3)); + for (int i = 0; i < 3; ++i) { + gammas->fType[i] = SkGammas::Type::kParam_Type; + gammas->fData[i].fParamOffset = 0; + } + + SkColorSpaceTransferFn* params = SkTAddOffset<SkColorSpaceTransferFn> + (memory, sizeof(SkGammas)); + + // Interval. + params->fD = 0.04045f; + + // First equation: + params->fC = 1.0f / 12.92f; + params->fF = 0.0f; + + // Second equation: + // Note that the function is continuous (it's actually sRGB). + params->fA = 1.0f / 1.055f; + params->fB = 0.055f / 1.055f; + params->fE = 0.0f; + params->fG = 2.4f; + sk_sp<SkICC> param = ICCTest::MakeICC(gammas); + test_raw_transfer_fn(r, param.get()); + + // Exponential gamma curves + gammas = sk_sp<SkGammas>(new SkGammas(3)); + for (int i = 0; i < 3; ++i) { + gammas->fType[i] = SkGammas::Type::kValue_Type; + gammas->fData[i].fValue = 1.4f; + } + sk_sp<SkICC> exp = ICCTest::MakeICC(gammas); + test_raw_transfer_fn(r, exp.get()); + + gammas = sk_sp<SkGammas>(new SkGammas(3)); + gammas->fType[0] = gammas->fType[1] = gammas->fType[2] = SkGammas::Type::kNamed_Type; + gammas->fData[0].fNamed = kSRGB_SkGammaNamed; + gammas->fData[1].fNamed = k2Dot2Curve_SkGammaNamed; + gammas->fData[2].fNamed = kLinear_SkGammaNamed; + sk_sp<SkICC> named = ICCTest::MakeICC(gammas); + test_raw_transfer_fn(r, named.get()); + + memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(float) * tableSize + + sizeof(SkColorSpaceTransferFn)); + gammas = sk_sp<SkGammas>(new (memory) SkGammas(3)); + + table = SkTAddOffset<float>(memory, sizeof(SkGammas)); + table[0] = 0.00f; + table[1] = 0.15f; + table[2] = 0.20f; + table[3] = 0.25f; + table[4] = 0.35f; + table[5] = 0.45f; + table[6] = 0.55f; + table[7] = 0.70f; + table[8] = 0.85f; + table[9] = 1.00f; + + params = SkTAddOffset<SkColorSpaceTransferFn>(memory, + sizeof(SkGammas) + sizeof(float) * tableSize); + params->fA = 1.0f / 1.055f; + params->fB = 0.055f / 1.055f; + params->fC = 1.0f / 12.92f; + params->fD = 0.04045f; + params->fE = 0.0f; + params->fF = 0.0f; + params->fG = 2.4f; + + gammas->fType[0] = SkGammas::Type::kValue_Type; + gammas->fData[0].fValue = 1.2f; + + gammas->fType[1] = SkGammas::Type::kTable_Type; + gammas->fData[1].fTable.fSize = tableSize; + gammas->fData[1].fTable.fOffset = 0; + + gammas->fType[2] = SkGammas::Type::kParam_Type; + gammas->fData[2].fParamOffset = sizeof(float) * tableSize; + sk_sp<SkICC> nonstd = ICCTest::MakeICC(gammas); + test_raw_transfer_fn(r, nonstd.get()); + + // Reverse order of table and exponent + gammas->fType[1] = SkGammas::Type::kValue_Type; + gammas->fData[1].fValue = 1.2f; + + gammas->fType[0] = SkGammas::Type::kTable_Type; + gammas->fData[0].fTable.fSize = tableSize; + gammas->fData[0].fTable.fOffset = 0; + sk_sp<SkICC> nonstd2 = ICCTest::MakeICC(gammas); + test_raw_transfer_fn(r, nonstd2.get()); +}
diff --git a/src/third_party/skia/tests/ImageCacheTest.cpp b/src/third_party/skia/tests/ImageCacheTest.cpp index 9f893bb..457df91 100644 --- a/src/third_party/skia/tests/ImageCacheTest.cpp +++ b/src/third_party/skia/tests/ImageCacheTest.cpp
@@ -12,11 +12,10 @@ namespace { static void* gGlobalAddress; struct TestingKey : public SkResourceCache::Key { - void* fPtr; intptr_t fValue; - TestingKey(intptr_t value) : fPtr(&gGlobalAddress), fValue(value) { - this->init(sizeof(fPtr) + sizeof(fValue)); + TestingKey(intptr_t value, uint64_t sharedID = 0) : fValue(value) { + this->init(&gGlobalAddress, sharedID, sizeof(fValue)); } }; struct TestingRec : public SkResourceCache::Rec { @@ -25,13 +24,15 @@ TestingKey fKey; intptr_t fValue; - virtual const Key& getKey() const SK_OVERRIDE { return fKey; } - virtual size_t bytesUsed() const SK_OVERRIDE { return sizeof(fKey) + sizeof(fValue); } + const Key& getKey() const override { return fKey; } + size_t bytesUsed() const override { return sizeof(fKey) + sizeof(fValue); } + const char* getCategory() const override { return "test_cache"; } + SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; } static bool Visitor(const SkResourceCache::Rec& baseRec, void* context) { const TestingRec& rec = static_cast<const TestingRec&>(baseRec); intptr_t* result = (intptr_t*)context; - + *result = rec.fValue; return true; } @@ -49,7 +50,7 @@ REPORTER_ASSERT(reporter, !cache.find(key, TestingRec::Visitor, &value)); REPORTER_ASSERT(reporter, -1 == value); - cache.add(SkNEW_ARGS(TestingRec, (key, i))); + cache.add(new TestingRec(key, i)); REPORTER_ASSERT(reporter, cache.find(key, TestingRec::Visitor, &value)); REPORTER_ASSERT(reporter, i == value); @@ -57,9 +58,9 @@ if (testPurge) { // stress test, should trigger purges - for (size_t i = 0; i < COUNT * 100; ++i) { + for (int i = 0; i < COUNT * 100; ++i) { TestingKey key(i); - cache.add(SkNEW_ARGS(TestingRec, (key, i))); + cache.add(new TestingRec(key, i)); } } @@ -72,6 +73,38 @@ cache.setTotalByteLimit(0); } +static void test_cache_purge_shared_id(skiatest::Reporter* reporter, SkResourceCache& cache) { + for (int i = 0; i < COUNT; ++i) { + TestingKey key(i, i & 1); // every other key will have a 1 for its sharedID + cache.add(new TestingRec(key, i)); + } + + // Ensure that everyone is present + for (int i = 0; i < COUNT; ++i) { + TestingKey key(i, i & 1); // every other key will have a 1 for its sharedID + intptr_t value = -1; + + REPORTER_ASSERT(reporter, cache.find(key, TestingRec::Visitor, &value)); + REPORTER_ASSERT(reporter, value == i); + } + + // Now purge the ones that had a non-zero sharedID (the odd-indexed ones) + cache.purgeSharedID(1); + + // Ensure that only the even ones are still present + for (int i = 0; i < COUNT; ++i) { + TestingKey key(i, i & 1); // every other key will have a 1 for its sharedID + intptr_t value = -1; + + if (i & 1) { + REPORTER_ASSERT(reporter, !cache.find(key, TestingRec::Visitor, &value)); + } else { + REPORTER_ASSERT(reporter, cache.find(key, TestingRec::Visitor, &value)); + REPORTER_ASSERT(reporter, value == i); + } + } +} + #include "SkDiscardableMemoryPool.h" static SkDiscardableMemoryPool* gPool; @@ -88,8 +121,7 @@ test_cache(reporter, cache, true); } { - SkAutoTUnref<SkDiscardableMemoryPool> pool( - SkDiscardableMemoryPool::Create(defLimit, NULL)); + sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Make(defLimit)); gPool = pool.get(); SkResourceCache cache(pool_factory); test_cache(reporter, cache, true); @@ -98,6 +130,10 @@ SkResourceCache cache(SkDiscardableMemory::Create); test_cache(reporter, cache, false); } + { + SkResourceCache cache(defLimit); + test_cache_purge_shared_id(reporter, cache); + } } DEF_TEST(ImageCache_doubleAdd, r) { @@ -106,8 +142,8 @@ TestingKey key(1); - cache.add(SkNEW_ARGS(TestingRec, (key, 2))); - cache.add(SkNEW_ARGS(TestingRec, (key, 3))); + cache.add(new TestingRec(key, 2)); + cache.add(new TestingRec(key, 3)); // Lookup can return either value. intptr_t value = -1;
diff --git a/src/third_party/skia/tests/ImageDecodingTest.cpp b/src/third_party/skia/tests/ImageDecodingTest.cpp deleted file mode 100644 index 30665a6..0000000 --- a/src/third_party/skia/tests/ImageDecodingTest.cpp +++ /dev/null
@@ -1,819 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Resources.h" -#include "SkBitmap.h" -#include "SkCanvas.h" -#include "SkColor.h" -#include "SkColorPriv.h" -#include "SkData.h" -#include "SkDecodingImageGenerator.h" -#include "SkDiscardableMemoryPool.h" -#include "SkForceLinking.h" -#include "SkGradientShader.h" -#include "SkImageDecoder.h" -#include "SkImageEncoder.h" -#include "SkImageGeneratorPriv.h" -#include "SkImagePriv.h" -#include "SkOSFile.h" -#include "SkPoint.h" -#include "SkShader.h" -#include "SkStream.h" -#include "SkString.h" -#include "Test.h" - -__SK_FORCE_IMAGE_DECODER_LINKING; - -/** - * Interprets c as an unpremultiplied color, and returns the - * premultiplied equivalent. - */ -static SkPMColor premultiply_unpmcolor(SkPMColor c) { - U8CPU a = SkGetPackedA32(c); - U8CPU r = SkGetPackedR32(c); - U8CPU g = SkGetPackedG32(c); - U8CPU b = SkGetPackedB32(c); - return SkPreMultiplyARGB(a, r, g, b); -} - -/** - * Return true if this stream format should be skipped, due - * to do being an opaque format or not a valid format. - */ -static bool skip_image_format(SkImageDecoder::Format format) { - switch (format) { - case SkImageDecoder::kPNG_Format: - case SkImageDecoder::kWEBP_Format: - return false; - // Skip unknown since it will not be decoded anyway. - case SkImageDecoder::kUnknown_Format: - // Technically ICO and BMP supports alpha channels, but our image - // decoders do not, so skip them as well. - case SkImageDecoder::kICO_Format: - case SkImageDecoder::kBMP_Format: - // KTX and ASTC are texture formats so it's not particularly clear how to - // decode the alpha from them. - case SkImageDecoder::kKTX_Format: - case SkImageDecoder::kASTC_Format: - // The rest of these are opaque. - case SkImageDecoder::kPKM_Format: - case SkImageDecoder::kWBMP_Format: - case SkImageDecoder::kGIF_Format: - case SkImageDecoder::kJPEG_Format: - return true; - } - SkASSERT(false); - return true; -} - -/** - * Test decoding an image in premultiplied mode and unpremultiplied mode and compare - * them. - */ -static void compare_unpremul(skiatest::Reporter* reporter, const SkString& filename) { - // Decode a resource: - SkBitmap bm8888; - SkBitmap bm8888Unpremul; - - SkFILEStream stream(filename.c_str()); - - SkImageDecoder::Format format = SkImageDecoder::GetStreamFormat(&stream); - if (skip_image_format(format)) { - return; - } - - SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream)); - if (NULL == decoder.get()) { - SkDebugf("couldn't decode %s\n", filename.c_str()); - return; - } - - bool success = decoder->decode(&stream, &bm8888, kN32_SkColorType, - SkImageDecoder::kDecodePixels_Mode); - if (!success) { - return; - } - - success = stream.rewind(); - REPORTER_ASSERT(reporter, success); - if (!success) { - return; - } - - decoder->setRequireUnpremultipliedColors(true); - success = decoder->decode(&stream, &bm8888Unpremul, kN32_SkColorType, - SkImageDecoder::kDecodePixels_Mode); - if (!success) { - return; - } - - bool dimensionsMatch = bm8888.width() == bm8888Unpremul.width() - && bm8888.height() == bm8888Unpremul.height(); - REPORTER_ASSERT(reporter, dimensionsMatch); - if (!dimensionsMatch) { - return; - } - - // Only do the comparison if the two bitmaps are both 8888. - if (bm8888.colorType() != kN32_SkColorType || bm8888Unpremul.colorType() != kN32_SkColorType) { - return; - } - - // Now compare the two bitmaps. - for (int i = 0; i < bm8888.width(); ++i) { - for (int j = 0; j < bm8888.height(); ++j) { - // "c0" is the color of the premultiplied bitmap at (i, j). - const SkPMColor c0 = *bm8888.getAddr32(i, j); - // "c1" is the result of premultiplying the color of the unpremultiplied - // bitmap at (i, j). - const SkPMColor c1 = premultiply_unpmcolor(*bm8888Unpremul.getAddr32(i, j)); - // Compute the difference for each component. - int da = SkAbs32(SkGetPackedA32(c0) - SkGetPackedA32(c1)); - int dr = SkAbs32(SkGetPackedR32(c0) - SkGetPackedR32(c1)); - int dg = SkAbs32(SkGetPackedG32(c0) - SkGetPackedG32(c1)); - int db = SkAbs32(SkGetPackedB32(c0) - SkGetPackedB32(c1)); - - // Alpha component must be exactly the same. - REPORTER_ASSERT(reporter, 0 == da); - - // Color components may not match exactly due to rounding error. - REPORTER_ASSERT(reporter, dr <= 1); - REPORTER_ASSERT(reporter, dg <= 1); - REPORTER_ASSERT(reporter, db <= 1); - } - } -} - -static void test_unpremul(skiatest::Reporter* reporter) { - // This test cannot run if there is no resource path. - SkString resourcePath = GetResourcePath(); - if (resourcePath.isEmpty()) { - SkDebugf("Could not run unpremul test because resourcePath not specified."); - return; - } - SkOSFile::Iter iter(resourcePath.c_str()); - SkString basename; - if (iter.next(&basename)) { - do { - SkString filename = SkOSPath::Join(resourcePath.c_str(), basename.c_str()); - // SkDebugf("about to decode \"%s\"\n", filename.c_str()); - compare_unpremul(reporter, filename); - } while (iter.next(&basename)); - } else { - SkDebugf("Failed to find any files :(\n"); - } -} - -#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX) -// Test that the alpha type is what we expect. -static void test_alphaType(skiatest::Reporter* reporter, const SkString& filename, - bool requireUnpremul) { - SkBitmap bm; - SkFILEStream stream(filename.c_str()); - - SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream)); - if (NULL == decoder.get()) { - return; - } - - decoder->setRequireUnpremultipliedColors(requireUnpremul); - - // Decode just the bounds. This should always succeed. - bool success = decoder->decode(&stream, &bm, kN32_SkColorType, - SkImageDecoder::kDecodeBounds_Mode); - REPORTER_ASSERT(reporter, success); - if (!success) { - return; - } - - // Keep track of the alpha type for testing later. If the full decode - // succeeds, the alpha type should be the same, unless the full decode - // determined that the alpha type should actually be opaque, which may - // not be known when only decoding the bounds. - const SkAlphaType boundsAlphaType = bm.alphaType(); - - // rewind should always succeed on SkFILEStream. - success = stream.rewind(); - REPORTER_ASSERT(reporter, success); - if (!success) { - return; - } - - success = decoder->decode(&stream, &bm, kN32_SkColorType, SkImageDecoder::kDecodePixels_Mode); - - if (!success) { - // When the decoder is set to require unpremul, if it does not support - // unpremul it will fail. This is the only reason the decode should - // fail (since we know the files we are using to test can be decoded). - REPORTER_ASSERT(reporter, requireUnpremul); - return; - } - - // The bounds decode should return with either the requested - // premul/unpremul or opaque, if that value could be determined when only - // decoding the bounds. - if (requireUnpremul) { - REPORTER_ASSERT(reporter, kUnpremul_SkAlphaType == boundsAlphaType - || kOpaque_SkAlphaType == boundsAlphaType); - } else { - REPORTER_ASSERT(reporter, kPremul_SkAlphaType == boundsAlphaType - || kOpaque_SkAlphaType == boundsAlphaType); - } - - // When decoding the full image, the alpha type should match the one - // returned by the bounds decode, unless the full decode determined that - // the alpha type is actually opaque. - REPORTER_ASSERT(reporter, bm.alphaType() == boundsAlphaType - || bm.alphaType() == kOpaque_SkAlphaType); -} - -DEF_TEST(ImageDecoding_alphaType, reporter) { - SkString resourcePath = GetResourcePath(); - if (resourcePath.isEmpty()) { - SkDebugf("Could not run alphaType test because resourcePath not specified."); - return; - } - - SkOSFile::Iter iter(resourcePath.c_str()); - SkString basename; - if (iter.next(&basename)) { - do { - SkString filename = SkOSPath::Join(resourcePath.c_str(), basename.c_str()); - for (int truth = 0; truth <= 1; ++truth) { - test_alphaType(reporter, filename, SkToBool(truth)); - } - } while (iter.next(&basename)); - } else { - SkDebugf("Failed to find any files :(\n"); - } - -} - -// Using known images, test that decoding into unpremul and premul behave as expected. -DEF_TEST(ImageDecoding_unpremul, reporter) { - SkString resourcePath = GetResourcePath(); - if (resourcePath.isEmpty()) { - SkDebugf("Could not run unpremul test because resourcePath not specified."); - return; - } - const char* root = "half-transparent-white-pixel"; - const char* suffixes[] = { ".png", ".webp" }; - - for (size_t i = 0; i < SK_ARRAY_COUNT(suffixes); ++i) { - SkString basename = SkStringPrintf("%s%s", root, suffixes[i]); - SkString fullName = SkOSPath::Join(resourcePath.c_str(), basename.c_str()); - - SkBitmap bm; - SkFILEStream stream(fullName.c_str()); - - if (!stream.isValid()) { - SkDebugf("file %s missing from resource directoy %s\n", - basename.c_str(), resourcePath.c_str()); - continue; - } - - // This should never fail since we know the images we're decoding. - SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream)); - REPORTER_ASSERT(reporter, decoder.get()); - if (NULL == decoder.get()) { - continue; - } - - // Test unpremultiplied. We know what color this should result in. - decoder->setRequireUnpremultipliedColors(true); - bool success = decoder->decode(&stream, &bm, kN32_SkColorType, - SkImageDecoder::kDecodePixels_Mode); - REPORTER_ASSERT(reporter, success); - if (!success) { - continue; - } - - REPORTER_ASSERT(reporter, bm.width() == 1 && bm.height() == 1); - { - SkAutoLockPixels alp(bm); - REPORTER_ASSERT(reporter, bm.getAddr32(0, 0)[0] == 0x7fffffff); - } - - success = stream.rewind(); - REPORTER_ASSERT(reporter, success); - if (!success) { - continue; - } - - // Test premultiplied. Once again, we know which color this should - // result in. - decoder->setRequireUnpremultipliedColors(false); - success = decoder->decode(&stream, &bm, kN32_SkColorType, - SkImageDecoder::kDecodePixels_Mode); - REPORTER_ASSERT(reporter, success); - if (!success) { - continue; - } - - REPORTER_ASSERT(reporter, bm.width() == 1 && bm.height() == 1); - { - SkAutoLockPixels alp(bm); - REPORTER_ASSERT(reporter, bm.getAddr32(0, 0)[0] == 0x7f7f7f7f); - } - } -} -#endif // SK_BUILD_FOR_UNIX/ANDROID skbug.com/2388 - -#ifdef SK_DEBUG -// Create a stream containing a bitmap encoded to Type type. -static SkMemoryStream* create_image_stream(SkImageEncoder::Type type) { - SkBitmap bm; - const int size = 50; - bm.allocN32Pixels(size, size); - SkCanvas canvas(bm); - SkPoint points[2] = { - { SkIntToScalar(0), SkIntToScalar(0) }, - { SkIntToScalar(size), SkIntToScalar(size) } - }; - SkColor colors[2] = { SK_ColorWHITE, SK_ColorBLUE }; - SkShader* shader = SkGradientShader::CreateLinear(points, colors, NULL, - SK_ARRAY_COUNT(colors), - SkShader::kClamp_TileMode); - SkPaint paint; - paint.setShader(shader)->unref(); - canvas.drawPaint(paint); - // Now encode it to a stream. - SkAutoTUnref<SkData> data(SkImageEncoder::EncodeData(bm, type, 100)); - if (NULL == data.get()) { - return NULL; - } - return SkNEW_ARGS(SkMemoryStream, (data.get())); -} - -// For every format that supports tile based decoding, ensure that -// calling decodeSubset will not fail if the caller has unreffed the -// stream provided in buildTileIndex. -// Only runs in debug mode since we are testing for a crash. -static void test_stream_life() { - const SkImageEncoder::Type gTypes[] = { -#ifdef SK_BUILD_FOR_ANDROID - SkImageEncoder::kJPEG_Type, - SkImageEncoder::kPNG_Type, -#endif - SkImageEncoder::kWEBP_Type, - }; - for (size_t i = 0; i < SK_ARRAY_COUNT(gTypes); ++i) { - // SkDebugf("encoding to %i\n", i); - SkAutoTUnref<SkMemoryStream> stream(create_image_stream(gTypes[i])); - if (NULL == stream.get()) { - SkDebugf("no stream\n"); - continue; - } - SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream)); - if (NULL == decoder.get()) { - SkDebugf("no decoder\n"); - continue; - } - int width, height; - if (!decoder->buildTileIndex(stream.get(), &width, &height)) { - SkDebugf("could not build a tile index\n"); - continue; - } - // Now unref the stream to make sure it survives - stream.reset(NULL); - SkBitmap bm; - decoder->decodeSubset(&bm, SkIRect::MakeWH(width, height), kN32_SkColorType); - } -} - -// Test inside SkScaledBitmapSampler.cpp -extern void test_row_proc_choice(); - -#endif // SK_DEBUG - -DEF_TEST(ImageDecoding, reporter) { - test_unpremul(reporter); -#ifdef SK_DEBUG - test_stream_life(); - test_row_proc_choice(); -#endif -} - -// expected output for 8x8 bitmap -static const int kExpectedWidth = 8; -static const int kExpectedHeight = 8; -static const SkColor kExpectedPixels[] = { - 0xffbba570, 0xff395f5d, 0xffe25c39, 0xff197666, - 0xff3cba27, 0xffdefcb0, 0xffc13874, 0xfffa0093, - 0xffbda60e, 0xffc01db6, 0xff2bd688, 0xff9362d4, - 0xffc641b2, 0xffa5cede, 0xff606eba, 0xff8f4bf3, - 0xff3bf742, 0xff8f02a8, 0xff5509df, 0xffc7027e, - 0xff24aa8a, 0xff886c96, 0xff625481, 0xff403689, - 0xffc52152, 0xff78ccd6, 0xffdcb4ab, 0xff09d27d, - 0xffca00f3, 0xff605d47, 0xff446fb2, 0xff576e46, - 0xff273df9, 0xffb41a83, 0xfff812c3, 0xffccab67, - 0xff034218, 0xff7db9a7, 0xff821048, 0xfffe4ab4, - 0xff6fac98, 0xff941d27, 0xff5fe411, 0xfffbb283, - 0xffd86e99, 0xff169162, 0xff71128c, 0xff39cab4, - 0xffa7fe63, 0xff4c956b, 0xffbc22e0, 0xffb272e4, - 0xff129f4a, 0xffe34513, 0xff3d3742, 0xffbd190a, - 0xffb07222, 0xff2e23f8, 0xfff089d9, 0xffb35738, - 0xffa86022, 0xff3340fe, 0xff95fe71, 0xff6a71df -}; -SK_COMPILE_ASSERT((kExpectedWidth * kExpectedHeight) - == SK_ARRAY_COUNT(kExpectedPixels), array_size_mismatch); - -DEF_TEST(WebP, reporter) { - const unsigned char encodedWebP[] = { - 0x52, 0x49, 0x46, 0x46, 0x2c, 0x01, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50, - 0x56, 0x50, 0x38, 0x4c, 0x20, 0x01, 0x00, 0x00, 0x2f, 0x07, 0xc0, 0x01, - 0x00, 0xff, 0x01, 0x45, 0x03, 0x00, 0xe2, 0xd5, 0xae, 0x60, 0x2b, 0xad, - 0xd9, 0x68, 0x76, 0xb6, 0x8d, 0x6a, 0x1d, 0xc0, 0xe6, 0x19, 0xd6, 0x16, - 0xb7, 0xb4, 0xef, 0xcf, 0xc3, 0x15, 0x6c, 0xb3, 0xbd, 0x77, 0x0d, 0x85, - 0x6d, 0x1b, 0xa9, 0xb1, 0x2b, 0xdc, 0x3d, 0x83, 0xdb, 0x00, 0x00, 0xc8, - 0x26, 0xe5, 0x01, 0x99, 0x8a, 0xd5, 0xdd, 0xfc, 0x82, 0xcd, 0xcd, 0x9a, - 0x8c, 0x13, 0xcc, 0x1b, 0xba, 0xf5, 0x05, 0xdb, 0xee, 0x6a, 0xdb, 0x38, - 0x60, 0xfe, 0x43, 0x2c, 0xd4, 0x6a, 0x99, 0x4d, 0xc6, 0xc0, 0xd3, 0x28, - 0x1b, 0xc1, 0xb1, 0x17, 0x4e, 0x43, 0x0e, 0x3d, 0x27, 0xe9, 0xe4, 0x84, - 0x4f, 0x24, 0x62, 0x69, 0x85, 0x43, 0x8d, 0xc2, 0x04, 0x00, 0x07, 0x59, - 0x60, 0xfd, 0x8b, 0x4d, 0x60, 0x32, 0x72, 0xcf, 0x88, 0x0c, 0x2f, 0x2f, - 0xad, 0x62, 0xbd, 0x27, 0x09, 0x16, 0x70, 0x78, 0x6c, 0xd9, 0x82, 0xef, - 0x1a, 0xa2, 0xcc, 0xf0, 0xf1, 0x6f, 0xd8, 0x78, 0x2e, 0x39, 0xa1, 0xcf, - 0x14, 0x4b, 0x89, 0xb4, 0x1b, 0x48, 0x15, 0x7c, 0x48, 0x6f, 0x8c, 0x20, - 0xb7, 0x00, 0xcf, 0xfc, 0xdb, 0xd0, 0xe9, 0xe7, 0x42, 0x09, 0xa4, 0x03, - 0x40, 0xac, 0xda, 0x40, 0x01, 0x00, 0x5f, 0xa1, 0x3d, 0x64, 0xe1, 0xf4, - 0x03, 0x45, 0x29, 0xe0, 0xe2, 0x4a, 0xc3, 0xa2, 0xe8, 0xe0, 0x25, 0x12, - 0x74, 0xc6, 0xe8, 0xfb, 0x93, 0x4f, 0x9f, 0x5e, 0xc0, 0xa6, 0x91, 0x1b, - 0xa4, 0x24, 0x82, 0xc3, 0x61, 0x07, 0x4c, 0x49, 0x4f, 0x53, 0xae, 0x5f, - 0x5d, 0x39, 0x36, 0xc0, 0x5b, 0x57, 0x54, 0x60, 0x10, 0x00, 0x00, 0xd1, - 0x68, 0xb6, 0x6d, 0xdb, 0x36, 0x22, 0xfa, 0x1f, 0x35, 0x75, 0x22, 0xec, - 0x31, 0xbc, 0x5d, 0x8f, 0x87, 0x53, 0xa2, 0x05, 0x8c, 0x2f, 0xcd, 0xa8, - 0xa7, 0xf3, 0xa3, 0xbd, 0x83, 0x8b, 0x2a, 0xc8, 0x58, 0xf5, 0xac, 0x80, - 0xe3, 0xfe, 0x66, 0xa4, 0x7c, 0x1b, 0x6c, 0xd1, 0xa9, 0xd8, 0x14, 0xd0, - 0xc5, 0xb5, 0x39, 0x71, 0x97, 0x19, 0x19, 0x1b - }; - SkAutoDataUnref encoded(SkData::NewWithCopy(encodedWebP, - sizeof(encodedWebP))); - SkBitmap bm; - - bool success = SkInstallDiscardablePixelRef( - SkDecodingImageGenerator::Create(encoded, - SkDecodingImageGenerator::Options()), &bm); - - REPORTER_ASSERT(reporter, success); - if (!success) { - return; - } - SkAutoLockPixels alp(bm); - - bool rightSize = ((kExpectedWidth == bm.width()) - && (kExpectedHeight == bm.height())); - REPORTER_ASSERT(reporter, rightSize); - if (rightSize) { - bool error = false; - const SkColor* correctPixel = kExpectedPixels; - for (int y = 0; y < bm.height(); ++y) { - for (int x = 0; x < bm.width(); ++x) { - error |= (*correctPixel != bm.getColor(x, y)); - ++correctPixel; - } - } - REPORTER_ASSERT(reporter, !error); - } -} - -//////////////////////////////////////////////////////////////////////////////// - -// example of how Android will do this inside their BitmapFactory -static SkPixelRef* install_pixel_ref(SkBitmap* bitmap, - SkStreamRewindable* stream, - int sampleSize, bool ditherImage) { - SkASSERT(bitmap != NULL); - SkASSERT(stream != NULL); - SkASSERT(stream->rewind()); - SkASSERT(stream->unique()); - SkColorType colorType = bitmap->colorType(); - SkDecodingImageGenerator::Options opts(sampleSize, ditherImage, colorType); - if (SkInstallDiscardablePixelRef( - SkDecodingImageGenerator::Create(stream, opts), bitmap)) { - return bitmap->pixelRef(); - } - return NULL; -} -/** - * A test for the SkDecodingImageGenerator::Create and - * SkInstallDiscardablePixelRef functions. - */ -DEF_TEST(ImprovedBitmapFactory, reporter) { - SkString pngFilename = GetResourcePath("randPixels.png"); - SkAutoTUnref<SkStreamRewindable> stream(SkStream::NewFromFile(pngFilename.c_str())); - if (sk_exists(pngFilename.c_str())) { - SkBitmap bm; - SkAssertResult(bm.setInfo(SkImageInfo::MakeN32Premul(1, 1))); - REPORTER_ASSERT(reporter, - install_pixel_ref(&bm, stream.detach(), 1, true)); - SkAutoLockPixels alp(bm); - REPORTER_ASSERT(reporter, bm.getPixels()); - } -} - - -//////////////////////////////////////////////////////////////////////////////// - -#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX) -static inline bool check_rounding(int value, int dividend, int divisor) { - // returns true if the value is greater than floor(dividend/divisor) - // and less than SkNextPow2(ceil(dividend - divisor)) - return (((divisor * value) > (dividend - divisor)) - && value <= SkNextPow2(((dividend - 1) / divisor) + 1)); -} -#endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX - - -#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A) - #define kBackwards_SkColorType kRGBA_8888_SkColorType -#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A) - #define kBackwards_SkColorType kBGRA_8888_SkColorType -#else - #error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order" -#endif - -static inline const char* SkColorType_to_string(SkColorType colorType) { - switch(colorType) { - case kAlpha_8_SkColorType: return "Alpha_8"; - case kRGB_565_SkColorType: return "RGB_565"; - case kARGB_4444_SkColorType: return "ARGB_4444"; - case kN32_SkColorType: return "N32"; - case kBackwards_SkColorType: return "Backwards"; - case kIndex_8_SkColorType: return "Index_8"; - default: return "ERROR"; - } -} - -static inline const char* options_colorType( - const SkDecodingImageGenerator::Options& opts) { - if (opts.fUseRequestedColorType) { - return SkColorType_to_string(opts.fRequestedColorType); - } else { - return "(none)"; - } -} - -static inline const char* yn(bool value) { - if (value) { - return "yes"; - } else { - return "no"; - } -} - -/** - * Given either a SkStream or a SkData, try to decode the encoded - * image using the specified options and report errors. - */ -static void test_options(skiatest::Reporter* reporter, - const SkDecodingImageGenerator::Options& opts, - SkStreamRewindable* encodedStream, - SkData* encodedData, - bool useData, - const SkString& path) { - SkBitmap bm; - bool success = false; - if (useData) { - if (NULL == encodedData) { - return; - } - success = SkInstallDiscardablePixelRef( - SkDecodingImageGenerator::Create(encodedData, opts), &bm); - } else { - if (NULL == encodedStream) { - return; - } - success = SkInstallDiscardablePixelRef( - SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts), &bm); - } - if (!success) { - if (opts.fUseRequestedColorType - && (kARGB_4444_SkColorType == opts.fRequestedColorType)) { - return; // Ignore known conversion inabilities. - } - // If we get here, it's a failure and we will need more - // information about why it failed. - ERRORF(reporter, "Bounds decode failed [sampleSize=%d dither=%s " - "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage), - options_colorType(opts), path.c_str()); - return; - } - #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX) - // Android is the only system that use Skia's image decoders in - // production. For now, we'll only verify that samplesize works - // on systems where it already is known to work. - REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight, - opts.fSampleSize)); - REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth, - opts.fSampleSize)); - // The ImageDecoder API doesn't guarantee that SampleSize does - // anything at all, but the decoders that this test excercises all - // produce an output size in the following range: - // (((sample_size * out_size) > (in_size - sample_size)) - // && out_size <= SkNextPow2(((in_size - 1) / sample_size) + 1)); - #endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX - SkAutoLockPixels alp(bm); - if (bm.getPixels() == NULL) { - ERRORF(reporter, "Pixel decode failed [sampleSize=%d dither=%s " - "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage), - options_colorType(opts), path.c_str()); - return; - } - - SkColorType requestedColorType = opts.fRequestedColorType; - REPORTER_ASSERT(reporter, - (!opts.fUseRequestedColorType) - || (bm.colorType() == requestedColorType)); - - // Condition under which we should check the decoding results: - if ((kN32_SkColorType == bm.colorType()) - && (!path.endsWith(".jpg")) // lossy - && (opts.fSampleSize == 1)) { // scaled - const SkColor* correctPixels = kExpectedPixels; - SkASSERT(bm.height() == kExpectedHeight); - SkASSERT(bm.width() == kExpectedWidth); - int pixelErrors = 0; - for (int y = 0; y < bm.height(); ++y) { - for (int x = 0; x < bm.width(); ++x) { - if (*correctPixels != bm.getColor(x, y)) { - ++pixelErrors; - } - ++correctPixels; - } - } - if (pixelErrors != 0) { - ERRORF(reporter, "Pixel-level mismatch (%d of %d) " - "[sampleSize=%d dither=%s colorType=%s %s]", - pixelErrors, kExpectedHeight * kExpectedWidth, - opts.fSampleSize, yn(opts.fDitherImage), - options_colorType(opts), path.c_str()); - } - } -} - -/** - * SkDecodingImageGenerator has an Options struct which lets the - * client of the generator set sample size, dithering, and bitmap - * config. This test loops through many possible options and tries - * them on a set of 5 small encoded images (each in a different - * format). We test both SkData and SkStreamRewindable decoding. - */ -DEF_TEST(ImageDecoderOptions, reporter) { - const char* files[] = { - "randPixels.bmp", - "randPixels.jpg", - "randPixels.png", - "randPixels.webp", - #if !defined(SK_BUILD_FOR_WIN) - // TODO(halcanary): Find out why this fails sometimes. - "randPixels.gif", - #endif - }; - - SkString resourceDir = GetResourcePath(); - if (!sk_exists(resourceDir.c_str())) { - return; - } - - int scaleList[] = {1, 2, 3, 4}; - bool ditherList[] = {true, false}; - SkColorType colorList[] = { - kAlpha_8_SkColorType, - kRGB_565_SkColorType, - kARGB_4444_SkColorType, // Most decoders will fail on 4444. - kN32_SkColorType - // Note that indexed color is left out of the list. Lazy - // decoding doesn't do indexed color. - }; - const bool useDataList[] = {true, false}; - - for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) { - SkString path = SkOSPath::Join(resourceDir.c_str(), files[fidx]); - if (!sk_exists(path.c_str())) { - continue; - } - - SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str())); - REPORTER_ASSERT(reporter, encodedData.get() != NULL); - SkAutoTUnref<SkStreamRewindable> encodedStream( - SkStream::NewFromFile(path.c_str())); - REPORTER_ASSERT(reporter, encodedStream.get() != NULL); - - for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) { - for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) { - for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) { - for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) { - SkDecodingImageGenerator::Options opts(scaleList[i], - ditherList[j], - colorList[k]); - test_options(reporter, opts, encodedStream, encodedData, - useDataList[m], path); - - } - SkDecodingImageGenerator::Options options(scaleList[i], - ditherList[j]); - test_options(reporter, options, encodedStream, encodedData, - useDataList[m], path); - } - } - } - } -} - -DEF_TEST(DiscardablePixelRef_SecondLockColorTableCheck, r) { - SkString resourceDir = GetResourcePath(); - SkString path = SkOSPath::Join(resourceDir.c_str(), "randPixels.gif"); - if (!sk_exists(path.c_str())) { - return; - } - SkAutoDataUnref encoded(SkData::NewFromFileName(path.c_str())); - SkBitmap bitmap; - if (!SkInstallDiscardablePixelRef( - SkDecodingImageGenerator::Create( - encoded, SkDecodingImageGenerator::Options()), &bitmap)) { - #ifndef SK_BUILD_FOR_WIN - ERRORF(r, "SkInstallDiscardablePixelRef [randPixels.gif] failed."); - #endif - return; - } - if (kIndex_8_SkColorType != bitmap.colorType()) { - return; - } - { - SkAutoLockPixels alp(bitmap); - REPORTER_ASSERT(r, bitmap.getColorTable() && "first pass"); - } - { - SkAutoLockPixels alp(bitmap); - REPORTER_ASSERT(r, bitmap.getColorTable() && "second pass"); - } -} - - -//////////////////////////////////////////////////////////////////////////////// -namespace { -class SingleAllocator : public SkBitmap::Allocator { -public: - SingleAllocator(void* p, size_t s) : fPixels(p), fSize(s) { } - ~SingleAllocator() {} - // If the pixels in fPixels are big enough, use them. - virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE { - SkASSERT(bm); - if (bm->info().getSafeSize(bm->rowBytes()) <= fSize) { - bm->setPixels(fPixels, ct); - fPixels = NULL; - fSize = 0; - return true; - } - return bm->tryAllocPixels(NULL, ct); - } - bool ready() { return fPixels != NULL; } -private: - void* fPixels; - size_t fSize; -}; -} // namespace - -/* This tests for a bug in libjpeg where INT32 is typedefed to long - and memory can be written to outside of the array. */ -DEF_TEST(ImageDecoding_JpegOverwrite, r) { - SkString resourceDir = GetResourcePath(); - SkString path = SkOSPath::Join(resourceDir.c_str(), "randPixels.jpg"); - SkAutoTUnref<SkStreamAsset> stream( - SkStream::NewFromFile(path.c_str())); - if (!stream.get()) { - SkDebugf("\nPath '%s' missing.\n", path.c_str()); - return; - } - SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream)); - if (NULL == decoder.get()) { - ERRORF(r, "\nSkImageDecoder::Factory failed.\n"); - return; - } - SkAssertResult(stream->rewind()); - - static const uint16_t sentinal = 0xBEEF; - static const int pixelCount = 16; - SkAutoTMalloc<uint16_t> pixels(pixelCount + 1); - // pixels.get() should be 4-byte aligned. - // This is necessary to reproduce the bug. - - pixels[pixelCount] = sentinal; // This value should not be changed. - - SkAutoTUnref<SingleAllocator> allocator( - SkNEW_ARGS(SingleAllocator, - ((void*)pixels.get(), sizeof(uint16_t) * pixelCount))); - decoder->setAllocator(allocator); - decoder->setSampleSize(2); - SkBitmap bitmap; - bool success = decoder->decode(stream, &bitmap, kRGB_565_SkColorType, - SkImageDecoder::kDecodePixels_Mode); - REPORTER_ASSERT(r, success); - REPORTER_ASSERT(r, !allocator->ready()); // Decoder used correct memory - REPORTER_ASSERT(r, sentinal == pixels[pixelCount]); -}
diff --git a/src/third_party/skia/tests/ImageFilterCacheTest.cpp b/src/third_party/skia/tests/ImageFilterCacheTest.cpp new file mode 100644 index 0000000..d718181 --- /dev/null +++ b/src/third_party/skia/tests/ImageFilterCacheTest.cpp
@@ -0,0 +1,278 @@ + /* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#include "SkBitmap.h" +#include "SkImage.h" +#include "SkImageFilter.h" +#include "SkImageFilterCache.h" +#include "SkMatrix.h" +#include "SkSpecialImage.h" + +static const int kSmallerSize = 10; +static const int kPad = 3; +static const int kFullSize = kSmallerSize + 2 * kPad; + +static SkBitmap create_bm() { + SkBitmap bm; + bm.allocN32Pixels(kFullSize, kFullSize, true); + bm.eraseColor(SK_ColorTRANSPARENT); + return bm; +} + +// Ensure the cache can return a cached image +static void test_find_existing(skiatest::Reporter* reporter, + const sk_sp<SkSpecialImage>& image, + const sk_sp<SkSpecialImage>& subset) { + static const size_t kCacheSize = 1000000; + sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize)); + + SkIRect clip = SkIRect::MakeWH(100, 100); + SkImageFilterCacheKey key1(0, SkMatrix::I(), clip, image->uniqueID(), image->subset()); + SkImageFilterCacheKey key2(0, SkMatrix::I(), clip, subset->uniqueID(), subset->subset()); + + SkIPoint offset = SkIPoint::Make(3, 4); + cache->set(key1, image.get(), offset, nullptr); + + SkIPoint foundOffset; + + sk_sp<SkSpecialImage> foundImage = cache->get(key1, &foundOffset); + REPORTER_ASSERT(reporter, foundImage); + REPORTER_ASSERT(reporter, offset == foundOffset); + + REPORTER_ASSERT(reporter, !cache->get(key2, &foundOffset)); +} + +// If either id is different or the clip or the matrix are different the +// cached image won't be found. Even if it is caching the same bitmap. +static void test_dont_find_if_diff_key(skiatest::Reporter* reporter, + const sk_sp<SkSpecialImage>& image, + const sk_sp<SkSpecialImage>& subset) { + static const size_t kCacheSize = 1000000; + sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize)); + + SkIRect clip1 = SkIRect::MakeWH(100, 100); + SkIRect clip2 = SkIRect::MakeWH(200, 200); + SkImageFilterCacheKey key0(0, SkMatrix::I(), clip1, image->uniqueID(), image->subset()); + SkImageFilterCacheKey key1(1, SkMatrix::I(), clip1, image->uniqueID(), image->subset()); + SkImageFilterCacheKey key2(0, SkMatrix::MakeTrans(5, 5), clip1, + image->uniqueID(), image->subset()); + SkImageFilterCacheKey key3(0, SkMatrix::I(), clip2, image->uniqueID(), image->subset()); + SkImageFilterCacheKey key4(0, SkMatrix::I(), clip1, subset->uniqueID(), subset->subset()); + + SkIPoint offset = SkIPoint::Make(3, 4); + cache->set(key0, image.get(), offset, nullptr); + + SkIPoint foundOffset; + REPORTER_ASSERT(reporter, !cache->get(key1, &foundOffset)); + REPORTER_ASSERT(reporter, !cache->get(key2, &foundOffset)); + REPORTER_ASSERT(reporter, !cache->get(key3, &foundOffset)); + REPORTER_ASSERT(reporter, !cache->get(key4, &foundOffset)); +} + +// Test purging when the max cache size is exceeded +static void test_internal_purge(skiatest::Reporter* reporter, const sk_sp<SkSpecialImage>& image) { + SkASSERT(image->getSize()); + const size_t kCacheSize = image->getSize() + 10; + sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize)); + + SkIRect clip = SkIRect::MakeWH(100, 100); + SkImageFilterCacheKey key1(0, SkMatrix::I(), clip, image->uniqueID(), image->subset()); + SkImageFilterCacheKey key2(1, SkMatrix::I(), clip, image->uniqueID(), image->subset()); + + SkIPoint offset = SkIPoint::Make(3, 4); + cache->set(key1, image.get(), offset, nullptr); + + SkIPoint foundOffset; + + REPORTER_ASSERT(reporter, cache->get(key1, &foundOffset)); + + // This should knock the first one out of the cache + cache->set(key2, image.get(), offset, nullptr); + + REPORTER_ASSERT(reporter, cache->get(key2, &foundOffset)); + REPORTER_ASSERT(reporter, !cache->get(key1, &foundOffset)); +} + +// Exercise the purgeByKey and purge methods +static void test_explicit_purging(skiatest::Reporter* reporter, + const sk_sp<SkSpecialImage>& image, + const sk_sp<SkSpecialImage>& subset) { + static const size_t kCacheSize = 1000000; + sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize)); + + SkIRect clip = SkIRect::MakeWH(100, 100); + SkImageFilterCacheKey key1(0, SkMatrix::I(), clip, image->uniqueID(), image->subset()); + SkImageFilterCacheKey key2(1, SkMatrix::I(), clip, subset->uniqueID(), image->subset()); + + SkIPoint offset = SkIPoint::Make(3, 4); + cache->set(key1, image.get(), offset, nullptr); + cache->set(key2, image.get(), offset, nullptr); + SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->count());) + + SkIPoint foundOffset; + + REPORTER_ASSERT(reporter, cache->get(key1, &foundOffset)); + REPORTER_ASSERT(reporter, cache->get(key2, &foundOffset)); + + cache->purgeByKeys(&key1, 1); + SkDEBUGCODE(REPORTER_ASSERT(reporter, 1 == cache->count());) + + REPORTER_ASSERT(reporter, !cache->get(key1, &foundOffset)); + REPORTER_ASSERT(reporter, cache->get(key2, &foundOffset)); + + cache->purge(); + SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->count());) + + REPORTER_ASSERT(reporter, !cache->get(key1, &foundOffset)); + REPORTER_ASSERT(reporter, !cache->get(key2, &foundOffset)); +} + +DEF_TEST(ImageFilterCache_RasterBacked, reporter) { + SkBitmap srcBM = create_bm(); + + const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize); + + sk_sp<SkSpecialImage> fullImg(SkSpecialImage::MakeFromRaster(full, srcBM)); + + const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); + + sk_sp<SkSpecialImage> subsetImg(SkSpecialImage::MakeFromRaster(subset, srcBM)); + + test_find_existing(reporter, fullImg, subsetImg); + test_dont_find_if_diff_key(reporter, fullImg, subsetImg); + test_internal_purge(reporter, fullImg); + test_explicit_purging(reporter, fullImg, subsetImg); +} + + +// Shared test code for both the raster and gpu-backed image cases +static void test_image_backed(skiatest::Reporter* reporter, const sk_sp<SkImage>& srcImage) { + const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize); + SkColorSpace* legacyColorSpace = nullptr; + + sk_sp<SkSpecialImage> fullImg(SkSpecialImage::MakeFromImage(full, srcImage, legacyColorSpace)); + + const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); + + sk_sp<SkSpecialImage> subsetImg(SkSpecialImage::MakeFromImage(subset, srcImage, + legacyColorSpace)); + + test_find_existing(reporter, fullImg, subsetImg); + test_dont_find_if_diff_key(reporter, fullImg, subsetImg); + test_internal_purge(reporter, fullImg); + test_explicit_purging(reporter, fullImg, subsetImg); +} + +DEF_TEST(ImageFilterCache_ImageBackedRaster, reporter) { + SkBitmap srcBM = create_bm(); + + sk_sp<SkImage> srcImage(SkImage::MakeFromBitmap(srcBM)); + + test_image_backed(reporter, srcImage); +} + +#if SK_SUPPORT_GPU +#include "GrContext.h" +#include "GrContextPriv.h" +#include "GrResourceProvider.h" +#include "GrSurfaceProxyPriv.h" +#include "GrTest.h" +#include "GrTexture.h" +#include "GrTextureProxy.h" + +static sk_sp<GrTextureProxy> create_proxy(GrResourceProvider* resourceProvider) { + SkBitmap srcBM = create_bm(); + + GrSurfaceDesc desc; + desc.fConfig = kRGBA_8888_GrPixelConfig; + desc.fFlags = kNone_GrSurfaceFlags; + desc.fWidth = kFullSize; + desc.fHeight = kFullSize; + + return GrSurfaceProxy::MakeDeferred(resourceProvider, + desc, SkBudgeted::kYes, + srcBM.getPixels(), + srcBM.rowBytes()); +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_ImageBackedGPU, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + + sk_sp<GrTextureProxy> srcProxy(create_proxy(context->resourceProvider())); + if (!srcProxy) { + return; + } + + if (!srcProxy->instantiate(context->resourceProvider())) { + return; + } + GrTexture* tex = srcProxy->priv().peekTexture(); + + GrBackendTexture backendTex = GrTest::CreateBackendTexture(context->contextPriv().getBackend(), + kFullSize, + kFullSize, + kRGBA_8888_GrPixelConfig, + tex->getTextureHandle()); + GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin; + sk_sp<SkImage> srcImage(SkImage::MakeFromTexture(context, + backendTex, + texOrigin, + kPremul_SkAlphaType, nullptr)); + if (!srcImage) { + return; + } + + GrSurfaceOrigin readBackOrigin; + GrBackendObject readBackHandle = srcImage->getTextureHandle(false, &readBackOrigin); + // TODO: Make it so we can check this (see skbug.com/5019) +#if 0 + if (readBackHandle != tex->getTextureHandle()) { + ERRORF(reporter, "backend mismatch %d %d\n", + (int)readBackHandle, (int)tex->getTextureHandle()); + } + REPORTER_ASSERT(reporter, readBackHandle == tex->getTextureHandle()); +#else + REPORTER_ASSERT(reporter, SkToBool(readBackHandle)); +#endif + if (readBackOrigin != texOrigin) { + ERRORF(reporter, "origin mismatch %d %d\n", readBackOrigin, texOrigin); + } + REPORTER_ASSERT(reporter, readBackOrigin == texOrigin); + + test_image_backed(reporter, srcImage); +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_GPUBacked, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + + sk_sp<GrTextureProxy> srcProxy(create_proxy(context->resourceProvider())); + if (!srcProxy) { + return; + } + + const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize); + + sk_sp<SkSpecialImage> fullImg(SkSpecialImage::MakeDeferredFromGpu( + context, full, + kNeedNewImageUniqueID_SpecialImage, + srcProxy, nullptr)); + + const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); + + sk_sp<SkSpecialImage> subsetImg(SkSpecialImage::MakeDeferredFromGpu( + context, subset, + kNeedNewImageUniqueID_SpecialImage, + srcProxy, nullptr)); + + test_find_existing(reporter, fullImg, subsetImg); + test_dont_find_if_diff_key(reporter, fullImg, subsetImg); + test_internal_purge(reporter, fullImg); + test_explicit_purging(reporter, fullImg, subsetImg); +} +#endif
diff --git a/src/third_party/skia/tests/ImageFilterTest.cpp b/src/third_party/skia/tests/ImageFilterTest.cpp index d0fa93f..69b31f1 100644 --- a/src/third_party/skia/tests/ImageFilterTest.cpp +++ b/src/third_party/skia/tests/ImageFilterTest.cpp
@@ -6,35 +6,41 @@ */ #include "SkBitmap.h" -#include "SkBitmapDevice.h" -#include "SkBitmapSource.h" #include "SkBlurImageFilter.h" #include "SkCanvas.h" #include "SkColorFilterImageFilter.h" #include "SkColorMatrixFilter.h" -#include "SkDeviceImageFilterProxy.h" +#include "SkColorSpaceXformer.h" +#include "SkComposeImageFilter.h" #include "SkDisplacementMapEffect.h" #include "SkDropShadowImageFilter.h" #include "SkFlattenableSerialization.h" #include "SkGradientShader.h" +#include "SkImage.h" +#include "SkImageSource.h" #include "SkLightingImageFilter.h" #include "SkMatrixConvolutionImageFilter.h" -#include "SkMatrixImageFilter.h" #include "SkMergeImageFilter.h" #include "SkMorphologyImageFilter.h" #include "SkOffsetImageFilter.h" +#include "SkPaintImageFilter.h" +#include "SkPerlinNoiseShader.h" #include "SkPicture.h" #include "SkPictureImageFilter.h" #include "SkPictureRecorder.h" +#include "SkPoint3.h" #include "SkReadBuffer.h" #include "SkRect.h" +#include "SkSpecialImage.h" +#include "SkSpecialSurface.h" +#include "SkSurface.h" +#include "SkTableColorFilter.h" #include "SkTileImageFilter.h" #include "SkXfermodeImageFilter.h" #include "Test.h" #if SK_SUPPORT_GPU -#include "GrContextFactory.h" -#include "SkGpuDevice.h" +#include "GrContext.h" #endif static const int kBitmapSize = 4; @@ -43,53 +49,262 @@ class MatrixTestImageFilter : public SkImageFilter { public: - MatrixTestImageFilter(skiatest::Reporter* reporter, const SkMatrix& expectedMatrix) - : SkImageFilter(0, NULL), fReporter(reporter), fExpectedMatrix(expectedMatrix) { + static sk_sp<SkImageFilter> Make(skiatest::Reporter* reporter, + const SkMatrix& expectedMatrix) { + return sk_sp<SkImageFilter>(new MatrixTestImageFilter(reporter, expectedMatrix)); } - virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context& ctx, - SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE { - REPORTER_ASSERT(fReporter, ctx.ctm() == fExpectedMatrix); - return true; - } - + SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(MatrixTestImageFilter) protected: -#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING - explicit MatrixTestImageFilter(SkReadBuffer& buffer) : SkImageFilter(0, NULL) { - fReporter = static_cast<skiatest::Reporter*>(buffer.readFunctionPtr()); - buffer.readMatrix(&fExpectedMatrix); + sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context& ctx, + SkIPoint* offset) const override { + REPORTER_ASSERT(fReporter, ctx.ctm() == fExpectedMatrix); + offset->fX = offset->fY = 0; + return sk_ref_sp<SkSpecialImage>(source); } -#endif + sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override { + return sk_ref_sp(const_cast<MatrixTestImageFilter*>(this)); + } - virtual void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE { - this->INHERITED::flatten(buffer); - buffer.writeFunctionPtr(fReporter); - buffer.writeMatrix(fExpectedMatrix); + void flatten(SkWriteBuffer& buffer) const override { + SkDEBUGFAIL("Should never get here"); } private: + MatrixTestImageFilter(skiatest::Reporter* reporter, const SkMatrix& expectedMatrix) + : INHERITED(nullptr, 0, nullptr) + , fReporter(reporter) + , fExpectedMatrix(expectedMatrix) { + } + skiatest::Reporter* fReporter; SkMatrix fExpectedMatrix; - + typedef SkImageFilter INHERITED; }; +class FailImageFilter : public SkImageFilter { +public: + FailImageFilter() : SkImageFilter(nullptr, 0, nullptr) { } + + sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, + const Context& ctx, + SkIPoint* offset) const override { + return nullptr; + } + sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override { + return nullptr; + } + + SK_TO_STRING_OVERRIDE() + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(FailImageFilter) + +private: + typedef SkImageFilter INHERITED; +}; + +sk_sp<SkFlattenable> FailImageFilter::CreateProc(SkReadBuffer& buffer) { + SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0); + return sk_sp<SkFlattenable>(new FailImageFilter()); +} + +#ifndef SK_IGNORE_TO_STRING +void FailImageFilter::toString(SkString* str) const { + str->appendf("FailImageFilter: ("); + str->append(")"); +} +#endif + +void draw_gradient_circle(SkCanvas* canvas, int width, int height) { + SkScalar x = SkIntToScalar(width / 2); + SkScalar y = SkIntToScalar(height / 2); + SkScalar radius = SkMinScalar(x, y) * 0.8f; + canvas->clear(0x00000000); + SkColor colors[2]; + colors[0] = SK_ColorWHITE; + colors[1] = SK_ColorBLACK; + sk_sp<SkShader> shader( + SkGradientShader::MakeRadial(SkPoint::Make(x, y), radius, colors, nullptr, 2, + SkShader::kClamp_TileMode) + ); + SkPaint paint; + paint.setShader(shader); + canvas->drawCircle(x, y, radius, paint); +} + +SkBitmap make_gradient_circle(int width, int height) { + SkBitmap bitmap; + bitmap.allocN32Pixels(width, height); + SkCanvas canvas(bitmap); + draw_gradient_circle(&canvas, width, height); + return bitmap; +} + +class FilterList { +public: + FilterList(sk_sp<SkImageFilter> input, const SkImageFilter::CropRect* cropRect = nullptr) { + SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1); + const SkScalar five = SkIntToScalar(5); + + { + sk_sp<SkColorFilter> cf(SkColorFilter::MakeModeFilter(SK_ColorRED, + SkBlendMode::kSrcIn)); + + this->addFilter("color filter", + SkColorFilterImageFilter::Make(std::move(cf), input, cropRect)); + } + + { + sk_sp<SkImage> gradientImage(SkImage::MakeFromBitmap(make_gradient_circle(64, 64))); + sk_sp<SkImageFilter> gradientSource(SkImageSource::Make(std::move(gradientImage))); + + this->addFilter("displacement map", + SkDisplacementMapEffect::Make(SkDisplacementMapEffect::kR_ChannelSelectorType, + SkDisplacementMapEffect::kB_ChannelSelectorType, + 20.0f, + std::move(gradientSource), input, cropRect)); + } + + this->addFilter("blur", SkBlurImageFilter::Make(SK_Scalar1, + SK_Scalar1, + input, + cropRect)); + this->addFilter("drop shadow", SkDropShadowImageFilter::Make( + SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_ColorGREEN, + SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode, + input, cropRect)); + this->addFilter("diffuse lighting", + SkLightingImageFilter::MakePointLitDiffuse(location, SK_ColorGREEN, 0, 0, + input, cropRect)); + this->addFilter("specular lighting", + SkLightingImageFilter::MakePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0, + input, cropRect)); + { + SkScalar kernel[9] = { + SkIntToScalar(1), SkIntToScalar(1), SkIntToScalar(1), + SkIntToScalar(1), SkIntToScalar(-7), SkIntToScalar(1), + SkIntToScalar(1), SkIntToScalar(1), SkIntToScalar(1), + }; + const SkISize kernelSize = SkISize::Make(3, 3); + const SkScalar gain = SK_Scalar1, bias = 0; + + this->addFilter("matrix convolution", + SkMatrixConvolutionImageFilter::Make( + kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1), + SkMatrixConvolutionImageFilter::kRepeat_TileMode, false, + input, cropRect)); + } + + this->addFilter("merge", SkMergeImageFilter::Make(input, input, cropRect)); + + { + SkPaint greenColorShaderPaint; + greenColorShaderPaint.setShader(SkShader::MakeColorShader(SK_ColorGREEN)); + + SkImageFilter::CropRect leftSideCropRect(SkRect::MakeXYWH(0, 0, 32, 64)); + sk_sp<SkImageFilter> paintFilterLeft(SkPaintImageFilter::Make(greenColorShaderPaint, + &leftSideCropRect)); + SkImageFilter::CropRect rightSideCropRect(SkRect::MakeXYWH(32, 0, 32, 64)); + sk_sp<SkImageFilter> paintFilterRight(SkPaintImageFilter::Make(greenColorShaderPaint, + &rightSideCropRect)); + + + this->addFilter("merge with disjoint inputs", SkMergeImageFilter::Make( + std::move(paintFilterLeft), std::move(paintFilterRight), cropRect)); + } + + this->addFilter("offset", + SkOffsetImageFilter::Make(SK_Scalar1, SK_Scalar1, input, + cropRect)); + this->addFilter("dilate", SkDilateImageFilter::Make(3, 2, input, cropRect)); + this->addFilter("erode", SkErodeImageFilter::Make(2, 3, input, cropRect)); + this->addFilter("tile", SkTileImageFilter::Make( + SkRect::MakeXYWH(0, 0, 50, 50), + cropRect ? cropRect->rect() : SkRect::MakeXYWH(0, 0, 100, 100), + input)); + + if (!cropRect) { + SkMatrix matrix; + + matrix.setTranslate(SK_Scalar1, SK_Scalar1); + matrix.postRotate(SkIntToScalar(45), SK_Scalar1, SK_Scalar1); + + this->addFilter("matrix", + SkImageFilter::MakeMatrixFilter(matrix, kLow_SkFilterQuality, input)); + } + { + sk_sp<SkImageFilter> blur(SkBlurImageFilter::Make(five, five, input)); + + this->addFilter("blur and offset", SkOffsetImageFilter::Make(five, five, + std::move(blur), + cropRect)); + } + { + SkPictureRecorder recorder; + SkCanvas* recordingCanvas = recorder.beginRecording(64, 64); + + SkPaint greenPaint; + greenPaint.setColor(SK_ColorGREEN); + recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeXYWH(10, 10, 30, 20)), greenPaint); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); + sk_sp<SkImageFilter> pictureFilter(SkPictureImageFilter::Make(std::move(picture))); + + this->addFilter("picture and blur", SkBlurImageFilter::Make(five, five, + std::move(pictureFilter), + cropRect)); + } + { + SkPaint paint; + paint.setShader(SkPerlinNoiseShader::MakeTurbulence(SK_Scalar1, SK_Scalar1, 1, 0)); + sk_sp<SkImageFilter> paintFilter(SkPaintImageFilter::Make(paint)); + + this->addFilter("paint and blur", SkBlurImageFilter::Make(five, five, + std::move(paintFilter), + cropRect)); + } + this->addFilter("xfermode", SkXfermodeImageFilter::Make(SkBlendMode::kSrc, input, input, + cropRect)); + } + int count() const { return fFilters.count(); } + SkImageFilter* getFilter(int index) const { return fFilters[index].fFilter.get(); } + const char* getName(int index) const { return fFilters[index].fName; } +private: + struct Filter { + Filter() : fName(nullptr) {} + Filter(const char* name, sk_sp<SkImageFilter> filter) + : fName(name) + , fFilter(std::move(filter)) { + } + const char* fName; + sk_sp<SkImageFilter> fFilter; + }; + void addFilter(const char* name, sk_sp<SkImageFilter> filter) { + fFilters.push_back(Filter(name, std::move(filter))); + } + + SkTArray<Filter> fFilters; +}; + } -SkFlattenable* MatrixTestImageFilter::CreateProc(SkReadBuffer& buffer) { - SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); - skiatest::Reporter* reporter = (skiatest::Reporter*)buffer.readFunctionPtr(); - SkMatrix matrix; - buffer.readMatrix(&matrix); - return SkNEW_ARGS(MatrixTestImageFilter, (reporter, matrix)); +sk_sp<SkFlattenable> MatrixTestImageFilter::CreateProc(SkReadBuffer& buffer) { + SkDEBUGFAIL("Should never get here"); + return nullptr; } -static void make_small_bitmap(SkBitmap& bitmap) { - bitmap.allocN32Pixels(kBitmapSize, kBitmapSize); - SkCanvas canvas(bitmap); - canvas.clear(0x00000000); +#ifndef SK_IGNORE_TO_STRING +void MatrixTestImageFilter::toString(SkString* str) const { + str->appendf("MatrixTestImageFilter: ("); + str->append(")"); +} +#endif + +static sk_sp<SkImage> make_small_image() { + auto surface(SkSurface::MakeRasterN32Premul(kBitmapSize, kBitmapSize)); + SkCanvas* canvas = surface->getCanvas(); + canvas->clear(0x00000000); SkPaint darkPaint; darkPaint.setColor(0xFF804020); SkPaint lightPaint; @@ -97,77 +312,158 @@ const int i = kBitmapSize / 4; for (int y = 0; y < kBitmapSize; y += i) { for (int x = 0; x < kBitmapSize; x += i) { - canvas.save(); - canvas.translate(SkIntToScalar(x), SkIntToScalar(y)); - canvas.drawRect(SkRect::MakeXYWH(0, 0, + canvas->save(); + canvas->translate(SkIntToScalar(x), SkIntToScalar(y)); + canvas->drawRect(SkRect::MakeXYWH(0, 0, SkIntToScalar(i), SkIntToScalar(i)), darkPaint); - canvas.drawRect(SkRect::MakeXYWH(SkIntToScalar(i), + canvas->drawRect(SkRect::MakeXYWH(SkIntToScalar(i), 0, SkIntToScalar(i), SkIntToScalar(i)), lightPaint); - canvas.drawRect(SkRect::MakeXYWH(0, + canvas->drawRect(SkRect::MakeXYWH(0, SkIntToScalar(i), SkIntToScalar(i), SkIntToScalar(i)), lightPaint); - canvas.drawRect(SkRect::MakeXYWH(SkIntToScalar(i), + canvas->drawRect(SkRect::MakeXYWH(SkIntToScalar(i), SkIntToScalar(i), SkIntToScalar(i), SkIntToScalar(i)), darkPaint); - canvas.restore(); + canvas->restore(); } } + + return surface->makeImageSnapshot(); } -static SkImageFilter* make_scale(float amount, SkImageFilter* input = NULL) { +static sk_sp<SkImageFilter> make_scale(float amount, sk_sp<SkImageFilter> input) { SkScalar s = amount; SkScalar matrix[20] = { s, 0, 0, 0, 0, 0, s, 0, 0, 0, 0, 0, s, 0, 0, 0, 0, 0, s, 0 }; - SkAutoTUnref<SkColorFilter> filter(SkColorMatrixFilter::Create(matrix)); - return SkColorFilterImageFilter::Create(filter, input); + sk_sp<SkColorFilter> filter(SkColorFilter::MakeMatrixFilterRowMajor255(matrix)); + return SkColorFilterImageFilter::Make(std::move(filter), std::move(input)); } -static SkImageFilter* make_grayscale(SkImageFilter* input = NULL, const SkImageFilter::CropRect* cropRect = NULL) { +static sk_sp<SkImageFilter> make_grayscale(sk_sp<SkImageFilter> input, + const SkImageFilter::CropRect* cropRect) { SkScalar matrix[20]; memset(matrix, 0, 20 * sizeof(SkScalar)); matrix[0] = matrix[5] = matrix[10] = 0.2126f; matrix[1] = matrix[6] = matrix[11] = 0.7152f; matrix[2] = matrix[7] = matrix[12] = 0.0722f; matrix[18] = 1.0f; - SkAutoTUnref<SkColorFilter> filter(SkColorMatrixFilter::Create(matrix)); - return SkColorFilterImageFilter::Create(filter, input, cropRect); + sk_sp<SkColorFilter> filter(SkColorFilter::MakeMatrixFilterRowMajor255(matrix)); + return SkColorFilterImageFilter::Make(std::move(filter), std::move(input), cropRect); } +static sk_sp<SkImageFilter> make_blue(sk_sp<SkImageFilter> input, + const SkImageFilter::CropRect* cropRect) { + sk_sp<SkColorFilter> filter(SkColorFilter::MakeModeFilter(SK_ColorBLUE, + SkBlendMode::kSrcIn)); + return SkColorFilterImageFilter::Make(std::move(filter), std::move(input), cropRect); +} + +static sk_sp<SkSpecialSurface> create_empty_special_surface(GrContext* context, int widthHeight) { +#if SK_SUPPORT_GPU + if (context) { + return SkSpecialSurface::MakeRenderTarget(context, + widthHeight, widthHeight, + kRGBA_8888_GrPixelConfig, nullptr); + } else +#endif + { + const SkImageInfo info = SkImageInfo::MakeN32(widthHeight, widthHeight, + kOpaque_SkAlphaType); + return SkSpecialSurface::MakeRaster(info); + } +} + +static sk_sp<SkSurface> create_surface(GrContext* context, int width, int height) { + const SkImageInfo info = SkImageInfo::MakeN32(width, height, kOpaque_SkAlphaType); +#if SK_SUPPORT_GPU + if (context) { + return SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info); + } else +#endif + { + return SkSurface::MakeRaster(info); + } +} + +static sk_sp<SkSpecialImage> create_empty_special_image(GrContext* context, int widthHeight) { + sk_sp<SkSpecialSurface> surf(create_empty_special_surface(context, widthHeight)); + + SkASSERT(surf); + + SkCanvas* canvas = surf->getCanvas(); + SkASSERT(canvas); + + canvas->clear(0x0); + + return surf->makeImageSnapshot(); +} + + DEF_TEST(ImageFilter, reporter) { { - // Check that two non-clipping color matrices concatenate into a single filter. - SkAutoTUnref<SkImageFilter> halfBrightness(make_scale(0.5f)); - SkAutoTUnref<SkImageFilter> quarterBrightness(make_scale(0.5f, halfBrightness)); - REPORTER_ASSERT(reporter, NULL == quarterBrightness->getInput(0)); + // Check that two non-clipping color-matrice-filters concatenate into a single filter. + sk_sp<SkImageFilter> halfBrightness(make_scale(0.5f, nullptr)); + sk_sp<SkImageFilter> quarterBrightness(make_scale(0.5f, std::move(halfBrightness))); + REPORTER_ASSERT(reporter, nullptr == quarterBrightness->getInput(0)); + SkColorFilter* cf; + REPORTER_ASSERT(reporter, quarterBrightness->asColorFilter(&cf)); + REPORTER_ASSERT(reporter, cf->asColorMatrix(nullptr)); + cf->unref(); } { - // Check that a clipping color matrix followed by a grayscale does not concatenate into a single filter. - SkAutoTUnref<SkImageFilter> doubleBrightness(make_scale(2.0f)); - SkAutoTUnref<SkImageFilter> halfBrightness(make_scale(0.5f, doubleBrightness)); - REPORTER_ASSERT(reporter, halfBrightness->getInput(0)); + // Check that a clipping color-matrice-filter followed by a color-matrice-filters + // concatenates into a single filter, but not a matrixfilter (due to clamping). + sk_sp<SkImageFilter> doubleBrightness(make_scale(2.0f, nullptr)); + sk_sp<SkImageFilter> halfBrightness(make_scale(0.5f, std::move(doubleBrightness))); + REPORTER_ASSERT(reporter, nullptr == halfBrightness->getInput(0)); + SkColorFilter* cf; + REPORTER_ASSERT(reporter, halfBrightness->asColorFilter(&cf)); + REPORTER_ASSERT(reporter, !cf->asColorMatrix(nullptr)); + cf->unref(); } { // Check that a color filter image filter without a crop rect can be // expressed as a color filter. - SkAutoTUnref<SkImageFilter> gray(make_grayscale()); - REPORTER_ASSERT(reporter, true == gray->asColorFilter(NULL)); + sk_sp<SkImageFilter> gray(make_grayscale(nullptr, nullptr)); + REPORTER_ASSERT(reporter, true == gray->asColorFilter(nullptr)); + } + + { + // Check that a colorfilterimage filter without a crop rect but with an input + // that is another colorfilterimage can be expressed as a colorfilter (composed). + sk_sp<SkImageFilter> mode(make_blue(nullptr, nullptr)); + sk_sp<SkImageFilter> gray(make_grayscale(std::move(mode), nullptr)); + REPORTER_ASSERT(reporter, true == gray->asColorFilter(nullptr)); + } + + { + // Test that if we exceed the limit of what ComposeColorFilter can combine, we still + // can build the DAG and won't assert if we call asColorFilter. + sk_sp<SkImageFilter> filter(make_blue(nullptr, nullptr)); + const int kWayTooManyForComposeColorFilter = 100; + for (int i = 0; i < kWayTooManyForComposeColorFilter; ++i) { + filter = make_blue(filter, nullptr); + // the first few of these will succeed, but after we hit the internal limit, + // it will then return false. + (void)filter->asColorFilter(nullptr); + } } { // Check that a color filter image filter with a crop rect cannot // be expressed as a color filter. SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(0, 0, 100, 100)); - SkAutoTUnref<SkImageFilter> grayWithCrop(make_grayscale(NULL, &cropRect)); - REPORTER_ASSERT(reporter, false == grayWithCrop->asColorFilter(NULL)); + sk_sp<SkImageFilter> grayWithCrop(make_grayscale(nullptr, &cropRect)); + REPORTER_ASSERT(reporter, false == grayWithCrop->asColorFilter(nullptr)); } { @@ -177,17 +473,19 @@ blueToRedMatrix[2] = blueToRedMatrix[18] = SK_Scalar1; SkScalar redToGreenMatrix[20] = { 0 }; redToGreenMatrix[5] = redToGreenMatrix[18] = SK_Scalar1; - SkAutoTUnref<SkColorFilter> blueToRed(SkColorMatrixFilter::Create(blueToRedMatrix)); - SkAutoTUnref<SkImageFilter> filter1(SkColorFilterImageFilter::Create(blueToRed.get())); - SkAutoTUnref<SkColorFilter> redToGreen(SkColorMatrixFilter::Create(redToGreenMatrix)); - SkAutoTUnref<SkImageFilter> filter2(SkColorFilterImageFilter::Create(redToGreen.get(), filter1.get())); + sk_sp<SkColorFilter> blueToRed(SkColorFilter::MakeMatrixFilterRowMajor255(blueToRedMatrix)); + sk_sp<SkImageFilter> filter1(SkColorFilterImageFilter::Make(std::move(blueToRed), + nullptr)); + sk_sp<SkColorFilter> redToGreen(SkColorFilter::MakeMatrixFilterRowMajor255(redToGreenMatrix)); + sk_sp<SkImageFilter> filter2(SkColorFilterImageFilter::Make(std::move(redToGreen), + std::move(filter1))); SkBitmap result; result.allocN32Pixels(kBitmapSize, kBitmapSize); SkPaint paint; paint.setColor(SK_ColorBLUE); - paint.setImageFilter(filter2.get()); + paint.setImageFilter(std::move(filter2)); SkCanvas canvas(result); canvas.clear(0x0); SkRect rect = SkRect::Make(SkIRect::MakeWH(kBitmapSize, kBitmapSize)); @@ -199,25 +497,25 @@ { // Tests pass by not asserting - SkBitmap bitmap, result; - make_small_bitmap(bitmap); + sk_sp<SkImage> image(make_small_image()); + SkBitmap result; result.allocN32Pixels(kBitmapSize, kBitmapSize); { // This tests for : // 1 ) location at (0,0,1) - SkPoint3 location(0, 0, SK_Scalar1); + SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1); // 2 ) location and target at same value - SkPoint3 target(location.fX, location.fY, location.fZ); + SkPoint3 target = SkPoint3::Make(location.fX, location.fY, location.fZ); // 3 ) large negative specular exponent value SkScalar specularExponent = -1000; - SkAutoTUnref<SkImageFilter> bmSrc(SkBitmapSource::Create(bitmap)); + sk_sp<SkImageFilter> bmSrc(SkImageSource::Make(std::move(image))); SkPaint paint; - paint.setImageFilter(SkLightingImageFilter::CreateSpotLitSpecular( + paint.setImageFilter(SkLightingImageFilter::MakeSpotLitSpecular( location, target, specularExponent, 180, 0xFFFFFFFF, SK_Scalar1, SK_Scalar1, SK_Scalar1, - bmSrc))->unref(); + std::move(bmSrc))); SkCanvas canvas(result); SkRect r = SkRect::MakeWH(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize)); @@ -226,130 +524,97 @@ } } -static void test_crop_rects(SkBaseDevice* device, skiatest::Reporter* reporter) { +static void test_crop_rects(skiatest::Reporter* reporter, + GrContext* context) { // Check that all filters offset to their absolute crop rect, // unaffected by the input crop rect. // Tests pass by not asserting. - SkBitmap bitmap; - bitmap.allocN32Pixels(100, 100); - bitmap.eraseARGB(0, 0, 0, 0); - SkDeviceImageFilterProxy proxy(device); + sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, 100)); + SkASSERT(srcImg); SkImageFilter::CropRect inputCropRect(SkRect::MakeXYWH(8, 13, 80, 80)); SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(20, 30, 60, 60)); - SkAutoTUnref<SkImageFilter> input(make_grayscale(NULL, &inputCropRect)); + sk_sp<SkImageFilter> input(make_grayscale(nullptr, &inputCropRect)); - SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kSrcIn_Mode)); - SkPoint3 location(0, 0, SK_Scalar1); - SkPoint3 target(SK_Scalar1, SK_Scalar1, SK_Scalar1); - SkScalar kernel[9] = { - SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1), - SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1), - SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1), - }; - SkISize kernelSize = SkISize::Make(3, 3); - SkScalar gain = SK_Scalar1, bias = 0; + FilterList filters(input, &cropRect); - SkImageFilter* filters[] = { - SkColorFilterImageFilter::Create(cf.get(), input.get(), &cropRect), - SkDisplacementMapEffect::Create(SkDisplacementMapEffect::kR_ChannelSelectorType, - SkDisplacementMapEffect::kB_ChannelSelectorType, - 40.0f, input.get(), input.get(), &cropRect), - SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect), - SkDropShadowImageFilter::Create(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_ColorGREEN, input.get(), &cropRect), - SkLightingImageFilter::CreatePointLitDiffuse(location, SK_ColorGREEN, 0, 0, input.get(), &cropRect), - SkLightingImageFilter::CreatePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0, input.get(), &cropRect), - SkMatrixConvolutionImageFilter::Create(kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1), SkMatrixConvolutionImageFilter::kRepeat_TileMode, false, input.get(), &cropRect), - SkMergeImageFilter::Create(input.get(), input.get(), SkXfermode::kSrcOver_Mode, &cropRect), - SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect), - SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect), - SkDilateImageFilter::Create(3, 2, input.get(), &cropRect), - SkErodeImageFilter::Create(2, 3, input.get(), &cropRect), - SkTileImageFilter::Create(inputCropRect.rect(), cropRect.rect(), input.get()), - SkXfermodeImageFilter::Create(SkXfermode::Create(SkXfermode::kSrcOver_Mode), input.get(), input.get(), &cropRect), - }; - - for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) { - SkImageFilter* filter = filters[i]; - SkBitmap result; + for (int i = 0; i < filters.count(); ++i) { + SkImageFilter* filter = filters.getFilter(i); SkIPoint offset; - SkString str; - str.printf("filter %d", static_cast<int>(i)); - SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeLargest(), NULL); - REPORTER_ASSERT_MESSAGE(reporter, filter->filterImage(&proxy, bitmap, ctx, - &result, &offset), str.c_str()); - REPORTER_ASSERT_MESSAGE(reporter, offset.fX == 20 && offset.fY == 30, str.c_str()); - } - - for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) { - SkSafeUnref(filters[i]); + SkImageFilter::OutputProperties noColorSpace(nullptr); + SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, noColorSpace); + sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg.get(), ctx, &offset)); + REPORTER_ASSERT_MESSAGE(reporter, resultImg, filters.getName(i)); + REPORTER_ASSERT_MESSAGE(reporter, offset.fX == 20 && offset.fY == 30, filters.getName(i)); } } -static SkBitmap make_gradient_circle(int width, int height) { - SkBitmap bitmap; - SkScalar x = SkIntToScalar(width / 2); - SkScalar y = SkIntToScalar(height / 2); - SkScalar radius = SkMinScalar(x, y) * 0.8f; - bitmap.allocN32Pixels(width, height); - SkCanvas canvas(bitmap); - canvas.clear(0x00000000); - SkColor colors[2]; - colors[0] = SK_ColorWHITE; - colors[1] = SK_ColorBLACK; - SkAutoTUnref<SkShader> shader( - SkGradientShader::CreateRadial(SkPoint::Make(x, y), radius, colors, NULL, 2, - SkShader::kClamp_TileMode) - ); - SkPaint paint; - paint.setShader(shader); - canvas.drawCircle(x, y, radius, paint); - return bitmap; -} - -static void test_negative_blur_sigma(SkBaseDevice* device, skiatest::Reporter* reporter) { +static void test_negative_blur_sigma(skiatest::Reporter* reporter, + GrContext* context) { // Check that SkBlurImageFilter will accept a negative sigma, either in // the given arguments or after CTM application. - int width = 32, height = 32; - SkDeviceImageFilterProxy proxy(device); - SkScalar five = SkIntToScalar(5); + const int width = 32, height = 32; + const SkScalar five = SkIntToScalar(5); - SkAutoTUnref<SkBlurImageFilter> positiveFilter( - SkBlurImageFilter::Create(five, five) - ); - - SkAutoTUnref<SkBlurImageFilter> negativeFilter( - SkBlurImageFilter::Create(-five, five) - ); + sk_sp<SkImageFilter> positiveFilter(SkBlurImageFilter::Make(five, five, nullptr)); + sk_sp<SkImageFilter> negativeFilter(SkBlurImageFilter::Make(-five, five, nullptr)); SkBitmap gradient = make_gradient_circle(width, height); - SkBitmap positiveResult1, negativeResult1; - SkBitmap positiveResult2, negativeResult2; + sk_sp<SkSpecialImage> imgSrc(SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(width, height), + gradient)); + SkIPoint offset; - SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeLargest(), NULL); - positiveFilter->filterImage(&proxy, gradient, ctx, &positiveResult1, &offset); - negativeFilter->filterImage(&proxy, gradient, ctx, &negativeResult1, &offset); + SkImageFilter::OutputProperties noColorSpace(nullptr); + SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(32, 32), nullptr, noColorSpace); + + sk_sp<SkSpecialImage> positiveResult1(positiveFilter->filterImage(imgSrc.get(), ctx, &offset)); + REPORTER_ASSERT(reporter, positiveResult1); + + sk_sp<SkSpecialImage> negativeResult1(negativeFilter->filterImage(imgSrc.get(), ctx, &offset)); + REPORTER_ASSERT(reporter, negativeResult1); + SkMatrix negativeScale; negativeScale.setScale(-SK_Scalar1, SK_Scalar1); - SkImageFilter::Context negativeCTX(negativeScale, SkIRect::MakeLargest(), NULL); - positiveFilter->filterImage(&proxy, gradient, negativeCTX, &negativeResult2, &offset); - negativeFilter->filterImage(&proxy, gradient, negativeCTX, &positiveResult2, &offset); - SkAutoLockPixels lockP1(positiveResult1); - SkAutoLockPixels lockP2(positiveResult2); - SkAutoLockPixels lockN1(negativeResult1); - SkAutoLockPixels lockN2(negativeResult2); + SkImageFilter::Context negativeCTX(negativeScale, SkIRect::MakeWH(32, 32), nullptr, + noColorSpace); + + sk_sp<SkSpecialImage> negativeResult2(positiveFilter->filterImage(imgSrc.get(), + negativeCTX, + &offset)); + REPORTER_ASSERT(reporter, negativeResult2); + + sk_sp<SkSpecialImage> positiveResult2(negativeFilter->filterImage(imgSrc.get(), + negativeCTX, + &offset)); + REPORTER_ASSERT(reporter, positiveResult2); + + + SkBitmap positiveResultBM1, positiveResultBM2; + SkBitmap negativeResultBM1, negativeResultBM2; + + REPORTER_ASSERT(reporter, positiveResult1->getROPixels(&positiveResultBM1)); + REPORTER_ASSERT(reporter, positiveResult2->getROPixels(&positiveResultBM2)); + REPORTER_ASSERT(reporter, negativeResult1->getROPixels(&negativeResultBM1)); + REPORTER_ASSERT(reporter, negativeResult2->getROPixels(&negativeResultBM2)); + for (int y = 0; y < height; y++) { - int diffs = memcmp(positiveResult1.getAddr32(0, y), negativeResult1.getAddr32(0, y), positiveResult1.rowBytes()); + int diffs = memcmp(positiveResultBM1.getAddr32(0, y), + negativeResultBM1.getAddr32(0, y), + positiveResultBM1.rowBytes()); REPORTER_ASSERT(reporter, !diffs); if (diffs) { break; } - diffs = memcmp(positiveResult1.getAddr32(0, y), negativeResult2.getAddr32(0, y), positiveResult1.rowBytes()); + diffs = memcmp(positiveResultBM1.getAddr32(0, y), + negativeResultBM2.getAddr32(0, y), + positiveResultBM1.rowBytes()); REPORTER_ASSERT(reporter, !diffs); if (diffs) { break; } - diffs = memcmp(positiveResult1.getAddr32(0, y), positiveResult2.getAddr32(0, y), positiveResult1.rowBytes()); + diffs = memcmp(positiveResultBM1.getAddr32(0, y), + positiveResultBM2.getAddr32(0, y), + positiveResultBM1.rowBytes()); REPORTER_ASSERT(reporter, !diffs); if (diffs) { break; @@ -357,80 +622,101 @@ } } -DEF_TEST(TestNegativeBlurSigma, reporter) { - SkBitmap temp; - temp.allocN32Pixels(100, 100); - SkBitmapDevice device(temp); - test_negative_blur_sigma(&device, reporter); +DEF_TEST(ImageFilterNegativeBlurSigma, reporter) { + test_negative_blur_sigma(reporter, nullptr); } +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterNegativeBlurSigma_Gpu, reporter, ctxInfo) { + test_negative_blur_sigma(reporter, ctxInfo.grContext()); +} +#endif + +static void test_zero_blur_sigma(skiatest::Reporter* reporter, GrContext* context) { + // Check that SkBlurImageFilter with a zero sigma and a non-zero srcOffset works correctly. + SkImageFilter::CropRect cropRect(SkRect::Make(SkIRect::MakeXYWH(5, 0, 5, 10))); + sk_sp<SkImageFilter> input(SkOffsetImageFilter::Make(0, 0, nullptr, &cropRect)); + sk_sp<SkImageFilter> filter(SkBlurImageFilter::Make(0, 0, std::move(input), &cropRect)); + + sk_sp<SkSpecialSurface> surf(create_empty_special_surface(context, 10)); + surf->getCanvas()->clear(SK_ColorGREEN); + sk_sp<SkSpecialImage> image(surf->makeImageSnapshot()); + + SkIPoint offset; + SkImageFilter::OutputProperties noColorSpace(nullptr); + SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(32, 32), nullptr, noColorSpace); + + sk_sp<SkSpecialImage> result(filter->filterImage(image.get(), ctx, &offset)); + REPORTER_ASSERT(reporter, offset.fX == 5 && offset.fY == 0); + REPORTER_ASSERT(reporter, result); + REPORTER_ASSERT(reporter, result->width() == 5 && result->height() == 10); + + SkBitmap resultBM; + + REPORTER_ASSERT(reporter, result->getROPixels(&resultBM)); + + for (int y = 0; y < resultBM.height(); y++) { + for (int x = 0; x < resultBM.width(); x++) { + bool diff = *resultBM.getAddr32(x, y) != SK_ColorGREEN; + REPORTER_ASSERT(reporter, !diff); + if (diff) { + break; + } + } + } +} + +DEF_TEST(ImageFilterZeroBlurSigma, reporter) { + test_zero_blur_sigma(reporter, nullptr); +} + +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterZeroBlurSigma_Gpu, reporter, ctxInfo) { + test_zero_blur_sigma(reporter, ctxInfo.grContext()); +} +#endif + + +// Tests that, even when an upstream filter has returned null (due to failure or clipping), a +// downstream filter that affects transparent black still does so even with a nullptr input. +static void test_fail_affects_transparent_black(skiatest::Reporter* reporter, GrContext* context) { + sk_sp<FailImageFilter> failFilter(new FailImageFilter()); + sk_sp<SkSpecialImage> source(create_empty_special_image(context, 5)); + SkImageFilter::OutputProperties noColorSpace(nullptr); + SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(0, 0, 1, 1), nullptr, noColorSpace); + sk_sp<SkColorFilter> green(SkColorFilter::MakeModeFilter(SK_ColorGREEN, SkBlendMode::kSrc)); + SkASSERT(green->affectsTransparentBlack()); + sk_sp<SkImageFilter> greenFilter(SkColorFilterImageFilter::Make(std::move(green), + std::move(failFilter))); + SkIPoint offset; + sk_sp<SkSpecialImage> result(greenFilter->filterImage(source.get(), ctx, &offset)); + REPORTER_ASSERT(reporter, nullptr != result.get()); + if (result.get()) { + SkBitmap resultBM; + REPORTER_ASSERT(reporter, result->getROPixels(&resultBM)); + REPORTER_ASSERT(reporter, *resultBM.getAddr32(0, 0) == SK_ColorGREEN); + } +} + +DEF_TEST(ImageFilterFailAffectsTransparentBlack, reporter) { + test_fail_affects_transparent_black(reporter, nullptr); +} + +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterFailAffectsTransparentBlack_Gpu, reporter, ctxInfo) { + test_fail_affects_transparent_black(reporter, ctxInfo.grContext()); +} +#endif + DEF_TEST(ImageFilterDrawTiled, reporter) { // Check that all filters when drawn tiled (with subsequent clip rects) exactly // match the same filters drawn with a single full-canvas bitmap draw. // Tests pass by not asserting. - SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kSrcIn_Mode)); - SkPoint3 location(0, 0, SK_Scalar1); - SkPoint3 target(SK_Scalar1, SK_Scalar1, SK_Scalar1); - SkScalar kernel[9] = { - SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1), - SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1), - SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1), - }; - SkISize kernelSize = SkISize::Make(3, 3); - SkScalar gain = SK_Scalar1, bias = 0; - SkScalar five = SkIntToScalar(5); - - SkAutoTUnref<SkImageFilter> gradient_source(SkBitmapSource::Create(make_gradient_circle(64, 64))); - SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(five, five)); - SkMatrix matrix; - - matrix.setTranslate(SK_Scalar1, SK_Scalar1); - matrix.postRotate(SkIntToScalar(45), SK_Scalar1, SK_Scalar1); - - SkRTreeFactory factory; - SkPictureRecorder recorder; - SkCanvas* recordingCanvas = recorder.beginRecording(64, 64, &factory, 0); - - SkPaint greenPaint; - greenPaint.setColor(SK_ColorGREEN); - recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeXYWH(10, 10, 30, 20)), greenPaint); - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); - SkAutoTUnref<SkImageFilter> pictureFilter(SkPictureImageFilter::Create(picture.get())); - - struct { - const char* fName; - SkImageFilter* fFilter; - } filters[] = { - { "color filter", SkColorFilterImageFilter::Create(cf.get()) }, - { "displacement map", SkDisplacementMapEffect::Create( - SkDisplacementMapEffect::kR_ChannelSelectorType, - SkDisplacementMapEffect::kB_ChannelSelectorType, - 20.0f, gradient_source.get()) }, - { "blur", SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1) }, - { "drop shadow", SkDropShadowImageFilter::Create( - SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_ColorGREEN) }, - { "diffuse lighting", SkLightingImageFilter::CreatePointLitDiffuse( - location, SK_ColorGREEN, 0, 0) }, - { "specular lighting", - SkLightingImageFilter::CreatePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0) }, - { "matrix convolution", - SkMatrixConvolutionImageFilter::Create( - kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1), - SkMatrixConvolutionImageFilter::kRepeat_TileMode, false) }, - { "merge", SkMergeImageFilter::Create(NULL, NULL, SkXfermode::kSrcOver_Mode) }, - { "offset", SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1) }, - { "dilate", SkDilateImageFilter::Create(3, 2) }, - { "erode", SkErodeImageFilter::Create(2, 3) }, - { "tile", SkTileImageFilter::Create(SkRect::MakeXYWH(0, 0, 50, 50), - SkRect::MakeXYWH(0, 0, 100, 100), NULL) }, - { "matrix", SkMatrixImageFilter::Create(matrix, SkPaint::kLow_FilterLevel) }, - { "blur and offset", SkOffsetImageFilter::Create(five, five, blur.get()) }, - { "picture and blur", SkBlurImageFilter::Create(five, five, pictureFilter.get()) }, - }; + FilterList filters(nullptr); SkBitmap untiledResult, tiledResult; - int width = 64, height = 64; + const int width = 64, height = 64; untiledResult.allocN32Pixels(width, height); tiledResult.allocN32Pixels(width, height); SkCanvas tiledCanvas(tiledResult); @@ -438,11 +724,11 @@ int tileSize = 8; for (int scale = 1; scale <= 2; ++scale) { - for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) { + for (int i = 0; i < filters.count(); ++i) { tiledCanvas.clear(0); untiledCanvas.clear(0); SkPaint paint; - paint.setImageFilter(filters[i].fFilter); + paint.setImageFilter(sk_ref_sp(filters.getFilter(i))); paint.setTextSize(SkIntToScalar(height)); paint.setColor(SK_ColorWHITE); SkString str; @@ -450,14 +736,14 @@ SkScalar ypos = SkIntToScalar(height); untiledCanvas.save(); untiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale)); - untiledCanvas.drawText(text, strlen(text), 0, ypos, paint); + untiledCanvas.drawString(text, 0, ypos, paint); untiledCanvas.restore(); for (int y = 0; y < height; y += tileSize) { for (int x = 0; x < width; x += tileSize) { tiledCanvas.save(); tiledCanvas.clipRect(SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize))); tiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale)); - tiledCanvas.drawText(text, strlen(text), 0, ypos, paint); + tiledCanvas.drawString(text, 0, ypos, paint); tiledCanvas.restore(); } } @@ -465,40 +751,38 @@ tiledCanvas.flush(); for (int y = 0; y < height; y++) { int diffs = memcmp(untiledResult.getAddr32(0, y), tiledResult.getAddr32(0, y), untiledResult.rowBytes()); - REPORTER_ASSERT_MESSAGE(reporter, !diffs, filters[i].fName); + REPORTER_ASSERT_MESSAGE(reporter, !diffs, filters.getName(i)); if (diffs) { break; } } } } - - for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) { - SkSafeUnref(filters[i].fFilter); - } } -static void draw_saveLayer_picture(int width, int height, int tileSize, +static void draw_saveLayer_picture(int width, int height, int tileSize, SkBBHFactory* factory, SkBitmap* result) { SkMatrix matrix; matrix.setTranslate(SkIntToScalar(50), 0); - SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorWHITE, SkXfermode::kSrc_Mode)); - SkAutoTUnref<SkImageFilter> cfif(SkColorFilterImageFilter::Create(cf.get())); - SkAutoTUnref<SkImageFilter> imageFilter(SkMatrixImageFilter::Create(matrix, SkPaint::kNone_FilterLevel, cfif.get())); + sk_sp<SkColorFilter> cf(SkColorFilter::MakeModeFilter(SK_ColorWHITE, SkBlendMode::kSrc)); + sk_sp<SkImageFilter> cfif(SkColorFilterImageFilter::Make(std::move(cf), nullptr)); + sk_sp<SkImageFilter> imageFilter(SkImageFilter::MakeMatrixFilter(matrix, + kNone_SkFilterQuality, + std::move(cfif))); SkPaint paint; - paint.setImageFilter(imageFilter.get()); + paint.setImageFilter(std::move(imageFilter)); SkPictureRecorder recorder; SkRect bounds = SkRect::Make(SkIRect::MakeXYWH(0, 0, 50, 50)); - SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width), - SkIntToScalar(height), + SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width), + SkIntToScalar(height), factory, 0); recordingCanvas->translate(-55, 0); recordingCanvas->saveLayer(&bounds, &paint); recordingCanvas->restore(); - SkAutoTUnref<SkPicture> picture1(recorder.endRecording()); + sk_sp<SkPicture> picture1(recorder.finishRecordingAsPicture()); result->allocN32Pixels(width, height); SkCanvas canvas(*result); @@ -518,7 +802,7 @@ SkRTreeFactory factory; draw_saveLayer_picture(width, height, tileSize, &factory, &result1); - draw_saveLayer_picture(width, height, tileSize, NULL, &result2); + draw_saveLayer_picture(width, height, tileSize, nullptr, &result2); for (int y = 0; y < height; y++) { int diffs = memcmp(result1.getAddr32(0, y), result2.getAddr32(0, y), result1.rowBytes()); @@ -529,56 +813,180 @@ } } -static SkImageFilter* makeBlur(SkImageFilter* input = NULL) { - return SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, input); +static sk_sp<SkImageFilter> make_blur(sk_sp<SkImageFilter> input) { + return SkBlurImageFilter::Make(SK_Scalar1, SK_Scalar1, std::move(input)); } -static SkImageFilter* makeDropShadow(SkImageFilter* input = NULL) { - return SkDropShadowImageFilter::Create( +static sk_sp<SkImageFilter> make_drop_shadow(sk_sp<SkImageFilter> input) { + return SkDropShadowImageFilter::Make( SkIntToScalar(100), SkIntToScalar(100), SkIntToScalar(10), SkIntToScalar(10), - SK_ColorBLUE, input); + SK_ColorBLUE, SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode, + std::move(input)); } DEF_TEST(ImageFilterBlurThenShadowBounds, reporter) { - SkAutoTUnref<SkImageFilter> filter1(makeBlur()); - SkAutoTUnref<SkImageFilter> filter2(makeDropShadow(filter1.get())); + sk_sp<SkImageFilter> filter1(make_blur(nullptr)); + sk_sp<SkImageFilter> filter2(make_drop_shadow(std::move(filter1))); SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100); SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236); - filter2->filterBounds(bounds, SkMatrix::I(), &bounds); + bounds = filter2->filterBounds(bounds, SkMatrix::I()); REPORTER_ASSERT(reporter, bounds == expectedBounds); } DEF_TEST(ImageFilterShadowThenBlurBounds, reporter) { - SkAutoTUnref<SkImageFilter> filter1(makeDropShadow()); - SkAutoTUnref<SkImageFilter> filter2(makeBlur(filter1.get())); + sk_sp<SkImageFilter> filter1(make_drop_shadow(nullptr)); + sk_sp<SkImageFilter> filter2(make_blur(std::move(filter1))); SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100); SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236); - filter2->filterBounds(bounds, SkMatrix::I(), &bounds); + bounds = filter2->filterBounds(bounds, SkMatrix::I()); REPORTER_ASSERT(reporter, bounds == expectedBounds); } DEF_TEST(ImageFilterDilateThenBlurBounds, reporter) { - SkAutoTUnref<SkImageFilter> filter1(SkDilateImageFilter::Create(2, 2)); - SkAutoTUnref<SkImageFilter> filter2(makeDropShadow(filter1.get())); + sk_sp<SkImageFilter> filter1(SkDilateImageFilter::Make(2, 2, nullptr)); + sk_sp<SkImageFilter> filter2(make_drop_shadow(std::move(filter1))); SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100); SkIRect expectedBounds = SkIRect::MakeXYWH(-132, -132, 234, 234); - filter2->filterBounds(bounds, SkMatrix::I(), &bounds); + bounds = filter2->filterBounds(bounds, SkMatrix::I()); REPORTER_ASSERT(reporter, bounds == expectedBounds); } +DEF_TEST(ImageFilterScaledBlurRadius, reporter) { + // Each blur should spread 3*sigma, so 3 for the blur and 30 for the shadow + // (before the CTM). Bounds should be computed correctly in the presence of + // a (possibly negative) scale. + sk_sp<SkImageFilter> blur(make_blur(nullptr)); + sk_sp<SkImageFilter> dropShadow(make_drop_shadow(nullptr)); + { + // Uniform scale by 2. + SkMatrix scaleMatrix; + scaleMatrix.setScale(2, 2); + SkIRect bounds = SkIRect::MakeLTRB(0, 0, 200, 200); + + SkIRect expectedBlurBounds = SkIRect::MakeLTRB(-6, -6, 206, 206); + SkIRect blurBounds = blur->filterBounds( + bounds, scaleMatrix, SkImageFilter::kForward_MapDirection); + REPORTER_ASSERT(reporter, blurBounds == expectedBlurBounds); + SkIRect reverseBlurBounds = blur->filterBounds( + bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection); + REPORTER_ASSERT(reporter, reverseBlurBounds == expectedBlurBounds); + + SkIRect expectedShadowBounds = SkIRect::MakeLTRB(0, 0, 460, 460); + SkIRect shadowBounds = dropShadow->filterBounds( + bounds, scaleMatrix, SkImageFilter::kForward_MapDirection); + REPORTER_ASSERT(reporter, shadowBounds == expectedShadowBounds); + SkIRect expectedReverseShadowBounds = + SkIRect::MakeLTRB(-260, -260, 200, 200); + SkIRect reverseShadowBounds = dropShadow->filterBounds( + bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection); + REPORTER_ASSERT(reporter, + reverseShadowBounds == expectedReverseShadowBounds); + } + { + // Vertical flip. + SkMatrix scaleMatrix; + scaleMatrix.setScale(1, -1); + SkIRect bounds = SkIRect::MakeLTRB(0, -100, 100, 0); + + SkIRect expectedBlurBounds = SkIRect::MakeLTRB(-3, -103, 103, 3); + SkIRect blurBounds = blur->filterBounds( + bounds, scaleMatrix, SkImageFilter::kForward_MapDirection); + REPORTER_ASSERT(reporter, blurBounds == expectedBlurBounds); + SkIRect reverseBlurBounds = blur->filterBounds( + bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection); + REPORTER_ASSERT(reporter, reverseBlurBounds == expectedBlurBounds); + + SkIRect expectedShadowBounds = SkIRect::MakeLTRB(0, -230, 230, 0); + SkIRect shadowBounds = dropShadow->filterBounds( + bounds, scaleMatrix, SkImageFilter::kForward_MapDirection); + REPORTER_ASSERT(reporter, shadowBounds == expectedShadowBounds); + SkIRect expectedReverseShadowBounds = + SkIRect::MakeLTRB(-130, -100, 100, 130); + SkIRect reverseShadowBounds = dropShadow->filterBounds( + bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection); + REPORTER_ASSERT(reporter, + reverseShadowBounds == expectedReverseShadowBounds); + } +} + +DEF_TEST(ImageFilterComposedBlurFastBounds, reporter) { + sk_sp<SkImageFilter> filter1(make_blur(nullptr)); + sk_sp<SkImageFilter> filter2(make_blur(nullptr)); + sk_sp<SkImageFilter> composedFilter(SkComposeImageFilter::Make(std::move(filter1), + std::move(filter2))); + + SkRect boundsSrc = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100)); + SkRect expectedBounds = SkRect::MakeXYWH( + SkIntToScalar(-6), SkIntToScalar(-6), SkIntToScalar(112), SkIntToScalar(112)); + SkRect boundsDst = composedFilter->computeFastBounds(boundsSrc); + + REPORTER_ASSERT(reporter, boundsDst == expectedBounds); +} + +DEF_TEST(ImageFilterUnionBounds, reporter) { + sk_sp<SkImageFilter> offset(SkOffsetImageFilter::Make(50, 0, nullptr)); + // Regardless of which order they appear in, the image filter bounds should + // be combined correctly. + { + sk_sp<SkImageFilter> composite(SkXfermodeImageFilter::Make(SkBlendMode::kSrcOver, offset)); + SkRect bounds = SkRect::MakeWH(100, 100); + // Intentionally aliasing here, as that's what the real callers do. + bounds = composite->computeFastBounds(bounds); + REPORTER_ASSERT(reporter, bounds == SkRect::MakeWH(150, 100)); + } + { + sk_sp<SkImageFilter> composite(SkXfermodeImageFilter::Make(SkBlendMode::kSrcOver, nullptr, + offset, nullptr)); + SkRect bounds = SkRect::MakeWH(100, 100); + // Intentionally aliasing here, as that's what the real callers do. + bounds = composite->computeFastBounds(bounds); + REPORTER_ASSERT(reporter, bounds == SkRect::MakeWH(150, 100)); + } +} + +static void test_imagefilter_merge_result_size(skiatest::Reporter* reporter, GrContext* context) { + SkBitmap greenBM; + greenBM.allocN32Pixels(20, 20); + greenBM.eraseColor(SK_ColorGREEN); + sk_sp<SkImage> greenImage(SkImage::MakeFromBitmap(greenBM)); + sk_sp<SkImageFilter> source(SkImageSource::Make(std::move(greenImage))); + sk_sp<SkImageFilter> merge(SkMergeImageFilter::Make(source, source)); + + sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, 1)); + + SkImageFilter::OutputProperties noColorSpace(nullptr); + SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(0, 0, 100, 100), nullptr, + noColorSpace); + SkIPoint offset; + + sk_sp<SkSpecialImage> resultImg(merge->filterImage(srcImg.get(), ctx, &offset)); + REPORTER_ASSERT(reporter, resultImg); + + REPORTER_ASSERT(reporter, resultImg->width() == 20 && resultImg->height() == 20); +} + +DEF_TEST(ImageFilterMergeResultSize, reporter) { + test_imagefilter_merge_result_size(reporter, nullptr); +} + +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterMergeResultSize_Gpu, reporter, ctxInfo) { + test_imagefilter_merge_result_size(reporter, ctxInfo.grContext()); +} +#endif + static void draw_blurred_rect(SkCanvas* canvas) { - SkAutoTUnref<SkImageFilter> filter(SkBlurImageFilter::Create(SkIntToScalar(8), 0)); SkPaint filterPaint; filterPaint.setColor(SK_ColorWHITE); - filterPaint.setImageFilter(filter); - canvas->saveLayer(NULL, &filterPaint); + filterPaint.setImageFilter(SkBlurImageFilter::Make(SkIntToScalar(8), 0, nullptr)); + canvas->saveLayer(nullptr, &filterPaint); SkPaint whitePaint; whitePaint.setColor(SK_ColorWHITE); canvas->drawRect(SkRect::Make(SkIRect::MakeWH(4, 4)), whitePaint); @@ -615,21 +1023,21 @@ SkPictureRecorder recorder1, recorder2; // The only difference between these two pictures is that one has RTree aceleration. - SkCanvas* recordingCanvas1 = recorder1.beginRecording(SkIntToScalar(width), - SkIntToScalar(height), - NULL, 0); - SkCanvas* recordingCanvas2 = recorder2.beginRecording(SkIntToScalar(width), - SkIntToScalar(height), + SkCanvas* recordingCanvas1 = recorder1.beginRecording(SkIntToScalar(width), + SkIntToScalar(height), + nullptr, 0); + SkCanvas* recordingCanvas2 = recorder2.beginRecording(SkIntToScalar(width), + SkIntToScalar(height), &factory, 0); draw_blurred_rect(recordingCanvas1); draw_blurred_rect(recordingCanvas2); - SkAutoTUnref<SkPicture> picture1(recorder1.endRecording()); - SkAutoTUnref<SkPicture> picture2(recorder2.endRecording()); + sk_sp<SkPicture> picture1(recorder1.finishRecordingAsPicture()); + sk_sp<SkPicture> picture2(recorder2.finishRecordingAsPicture()); for (int y = 0; y < height; y += tileSize) { for (int x = 0; x < width; x += tileSize) { SkRect tileRect = SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize)); - draw_picture_clipped(&canvas1, tileRect, picture1); - draw_picture_clipped(&canvas2, tileRect, picture2); + draw_picture_clipped(&canvas1, tileRect, picture1.get()); + draw_picture_clipped(&canvas2, tileRect, picture2.get()); } } for (int y = 0; y < height; y++) { @@ -650,10 +1058,11 @@ SkScalar gain = SK_Scalar1, bias = 0; SkIPoint kernelOffset = SkIPoint::Make(0, 0); - SkAutoTUnref<SkImageFilter> filter( - SkMatrixConvolutionImageFilter::Create( - kernelSize, kernel, gain, bias, kernelOffset, - SkMatrixConvolutionImageFilter::kRepeat_TileMode, false)); + sk_sp<SkImageFilter> filter(SkMatrixConvolutionImageFilter::Make( + kernelSize, kernel, + gain, bias, kernelOffset, + SkMatrixConvolutionImageFilter::kRepeat_TileMode, + false, nullptr)); SkBitmap result; int width = 16, height = 16; @@ -662,7 +1071,7 @@ canvas.clear(0); SkPaint paint; - paint.setImageFilter(filter); + paint.setImageFilter(std::move(filter)); SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height)); canvas.drawRect(rect, paint); } @@ -677,10 +1086,10 @@ SkScalar gain = SK_Scalar1, bias = 0; SkIPoint kernelOffset = SkIPoint::Make(2, 0); - SkAutoTUnref<SkImageFilter> filter( - SkMatrixConvolutionImageFilter::Create( - kernelSize, kernel, gain, bias, kernelOffset, - SkMatrixConvolutionImageFilter::kClamp_TileMode, true)); + sk_sp<SkImageFilter> filter(SkMatrixConvolutionImageFilter::Make( + kernelSize, kernel, gain, bias, kernelOffset, + SkMatrixConvolutionImageFilter::kClamp_TileMode, + true, nullptr)); SkBitmap result; @@ -690,7 +1099,7 @@ canvas.clear(0); SkPaint filterPaint; - filterPaint.setImageFilter(filter); + filterPaint.setImageFilter(std::move(filter)); SkRect bounds = SkRect::MakeWH(1, 10); SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height)); SkPaint rectPaint; @@ -699,18 +1108,64 @@ canvas.restore(); } -DEF_TEST(ImageFilterCropRect, reporter) { - SkBitmap temp; - temp.allocN32Pixels(100, 100); - SkBitmapDevice device(temp); - test_crop_rects(&device, reporter); +static void test_big_kernel(skiatest::Reporter* reporter, GrContext* context) { + // Check that a kernel that is too big for the GPU still works + SkScalar identityKernel[49] = { + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 + }; + SkISize kernelSize = SkISize::Make(7, 7); + SkScalar gain = SK_Scalar1, bias = 0; + SkIPoint kernelOffset = SkIPoint::Make(0, 0); + + sk_sp<SkImageFilter> filter(SkMatrixConvolutionImageFilter::Make( + kernelSize, identityKernel, gain, bias, kernelOffset, + SkMatrixConvolutionImageFilter::kClamp_TileMode, + true, nullptr)); + + sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, 100)); + SkASSERT(srcImg); + + SkIPoint offset; + SkImageFilter::OutputProperties noColorSpace(nullptr); + SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, noColorSpace); + sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg.get(), ctx, &offset)); + REPORTER_ASSERT(reporter, resultImg); + REPORTER_ASSERT(reporter, SkToBool(context) == resultImg->isTextureBacked()); + REPORTER_ASSERT(reporter, resultImg->width() == 100 && resultImg->height() == 100); + REPORTER_ASSERT(reporter, offset.fX == 0 && offset.fY == 0); } +DEF_TEST(ImageFilterMatrixConvolutionBigKernel, reporter) { + test_big_kernel(reporter, nullptr); +} + +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterMatrixConvolutionBigKernel_Gpu, + reporter, ctxInfo) { + test_big_kernel(reporter, ctxInfo.grContext()); +} +#endif + +DEF_TEST(ImageFilterCropRect, reporter) { + test_crop_rects(reporter, nullptr); +} + +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCropRect_Gpu, reporter, ctxInfo) { + test_crop_rects(reporter, ctxInfo.grContext()); +} +#endif + DEF_TEST(ImageFilterMatrix, reporter) { SkBitmap temp; temp.allocN32Pixels(100, 100); - SkBitmapDevice device(temp); - SkCanvas canvas(&device); + SkCanvas canvas(temp); canvas.scale(SkIntToScalar(2), SkIntToScalar(2)); SkMatrix expectedMatrix = canvas.getTotalMatrix(); @@ -720,10 +1175,8 @@ SkCanvas* recordingCanvas = recorder.beginRecording(100, 100, &factory, 0); SkPaint paint; - SkAutoTUnref<MatrixTestImageFilter> imageFilter( - new MatrixTestImageFilter(reporter, expectedMatrix)); - paint.setImageFilter(imageFilter.get()); - recordingCanvas->saveLayer(NULL, &paint); + paint.setImageFilter(MatrixTestImageFilter::Make(reporter, expectedMatrix)); + recordingCanvas->saveLayer(nullptr, &paint); SkPaint solidPaint; solidPaint.setColor(0xFFFFFFFF); recordingCanvas->save(); @@ -731,9 +1184,8 @@ recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(100, 100)), solidPaint); recordingCanvas->restore(); // scale recordingCanvas->restore(); // saveLayer - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); - canvas.drawPicture(picture); + canvas.drawPicture(recorder.finishRecordingAsPicture()); } DEF_TEST(ImageFilterCrossProcessPictureImageFilter, reporter) { @@ -745,28 +1197,26 @@ SkPaint greenPaint; greenPaint.setColor(SK_ColorGREEN); recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint); - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); // Wrap that SkPicture in an SkPictureImageFilter. - SkAutoTUnref<SkImageFilter> imageFilter( - SkPictureImageFilter::Create(picture.get())); + sk_sp<SkImageFilter> imageFilter(SkPictureImageFilter::Make(picture)); // Check that SkPictureImageFilter successfully serializes its contained // SkPicture when not in cross-process mode. SkPaint paint; - paint.setImageFilter(imageFilter.get()); + paint.setImageFilter(imageFilter); SkPictureRecorder outerRecorder; SkCanvas* outerCanvas = outerRecorder.beginRecording(1, 1, &factory, 0); SkPaint redPaintWithFilter; redPaintWithFilter.setColor(SK_ColorRED); - redPaintWithFilter.setImageFilter(imageFilter.get()); + redPaintWithFilter.setImageFilter(imageFilter); outerCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), redPaintWithFilter); - SkAutoTUnref<SkPicture> outerPicture(outerRecorder.endRecording()); + sk_sp<SkPicture> outerPicture(outerRecorder.finishRecordingAsPicture()); SkBitmap bitmap; bitmap.allocN32Pixels(1, 1); - SkBitmapDevice device(bitmap); - SkCanvas canvas(&device); + SkCanvas canvas(bitmap); // The result here should be green, since the filter replaces the primitive's red interior. canvas.clear(0x0); @@ -777,75 +1227,87 @@ // Check that, for now, SkPictureImageFilter does not serialize or // deserialize its contained picture when the filter is serialized // cross-process. Do this by "laundering" it through SkValidatingReadBuffer. - SkAutoTUnref<SkData> data(SkValidatingSerializeFlattenable(imageFilter.get())); - SkAutoTUnref<SkFlattenable> flattenable(SkValidatingDeserializeFlattenable( - data->data(), data->size(), SkImageFilter::GetFlattenableType())); - SkImageFilter* unflattenedFilter = static_cast<SkImageFilter*>(flattenable.get()); + sk_sp<SkData> data(SkValidatingSerializeFlattenable(imageFilter.get())); + sk_sp<SkImageFilter> unflattenedFilter = SkValidatingDeserializeImageFilter(data->data(), + data->size()); redPaintWithFilter.setImageFilter(unflattenedFilter); SkPictureRecorder crossProcessRecorder; SkCanvas* crossProcessCanvas = crossProcessRecorder.beginRecording(1, 1, &factory, 0); crossProcessCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), redPaintWithFilter); - SkAutoTUnref<SkPicture> crossProcessPicture(crossProcessRecorder.endRecording()); + sk_sp<SkPicture> crossProcessPicture(crossProcessRecorder.finishRecordingAsPicture()); canvas.clear(0x0); canvas.drawPicture(crossProcessPicture); pixel = *bitmap.getAddr32(0, 0); - // The result here should not be green, since the filter draws nothing. - REPORTER_ASSERT(reporter, pixel != SK_ColorGREEN); + // If the security precautions are enabled, the result here should not be green, since the + // filter draws nothing. + REPORTER_ASSERT(reporter, SkPicture::PictureIOSecurityPrecautionsEnabled() + ? pixel != SK_ColorGREEN : pixel == SK_ColorGREEN); +} + +static void test_clipped_picture_imagefilter(skiatest::Reporter* reporter, GrContext* context) { + sk_sp<SkPicture> picture; + + { + SkRTreeFactory factory; + SkPictureRecorder recorder; + SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory, 0); + + // Create an SkPicture which simply draws a green 1x1 rectangle. + SkPaint greenPaint; + greenPaint.setColor(SK_ColorGREEN); + recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint); + picture = recorder.finishRecordingAsPicture(); + } + + sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, 2)); + + sk_sp<SkImageFilter> imageFilter(SkPictureImageFilter::Make(picture)); + + SkIPoint offset; + SkImageFilter::OutputProperties noColorSpace(nullptr); + SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(1, 1, 1, 1), nullptr, noColorSpace); + + sk_sp<SkSpecialImage> resultImage(imageFilter->filterImage(srcImg.get(), ctx, &offset)); + REPORTER_ASSERT(reporter, !resultImage); } DEF_TEST(ImageFilterClippedPictureImageFilter, reporter) { - SkRTreeFactory factory; - SkPictureRecorder recorder; - SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory, 0); - - // Create an SkPicture which simply draws a green 1x1 rectangle. - SkPaint greenPaint; - greenPaint.setColor(SK_ColorGREEN); - recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint); - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); - - SkAutoTUnref<SkImageFilter> imageFilter( - SkPictureImageFilter::Create(picture.get())); - - SkBitmap result; - SkIPoint offset; - SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(1, 1, 1, 1), NULL); - SkBitmap bitmap; - bitmap.allocN32Pixels(2, 2); - SkBitmapDevice device(bitmap); - SkDeviceImageFilterProxy proxy(&device); - REPORTER_ASSERT(reporter, !imageFilter->filterImage(&proxy, bitmap, ctx, &result, &offset)); + test_clipped_picture_imagefilter(reporter, nullptr); } +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterClippedPictureImageFilter_Gpu, reporter, ctxInfo) { + test_clipped_picture_imagefilter(reporter, ctxInfo.grContext()); +} +#endif + DEF_TEST(ImageFilterEmptySaveLayer, reporter) { // Even when there's an empty saveLayer()/restore(), ensure that an image // filter or color filter which affects transparent black still draws. SkBitmap bitmap; bitmap.allocN32Pixels(10, 10); - SkBitmapDevice device(bitmap); - SkCanvas canvas(&device); + SkCanvas canvas(bitmap); SkRTreeFactory factory; SkPictureRecorder recorder; - SkAutoTUnref<SkColorFilter> green( - SkColorFilter::CreateModeFilter(SK_ColorGREEN, SkXfermode::kSrc_Mode)); - SkAutoTUnref<SkColorFilterImageFilter> imageFilter( - SkColorFilterImageFilter::Create(green.get())); + sk_sp<SkColorFilter> green(SkColorFilter::MakeModeFilter(SK_ColorGREEN, + SkBlendMode::kSrc)); + sk_sp<SkImageFilter> imageFilter(SkColorFilterImageFilter::Make(green, nullptr)); SkPaint imageFilterPaint; - imageFilterPaint.setImageFilter(imageFilter.get()); + imageFilterPaint.setImageFilter(std::move(imageFilter)); SkPaint colorFilterPaint; - colorFilterPaint.setColorFilter(green.get()); + colorFilterPaint.setColorFilter(green); SkRect bounds = SkRect::MakeWH(10, 10); SkCanvas* recordingCanvas = recorder.beginRecording(10, 10, &factory, 0); recordingCanvas->saveLayer(&bounds, &imageFilterPaint); recordingCanvas->restore(); - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); canvas.clear(0); canvas.drawPicture(picture); @@ -853,9 +1315,9 @@ REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN); recordingCanvas = recorder.beginRecording(10, 10, &factory, 0); - recordingCanvas->saveLayer(NULL, &imageFilterPaint); + recordingCanvas->saveLayer(nullptr, &imageFilterPaint); recordingCanvas->restore(); - SkAutoTUnref<SkPicture> picture2(recorder.endRecording()); + sk_sp<SkPicture> picture2(recorder.finishRecordingAsPicture()); canvas.clear(0); canvas.drawPicture(picture2); @@ -865,7 +1327,7 @@ recordingCanvas = recorder.beginRecording(10, 10, &factory, 0); recordingCanvas->saveLayer(&bounds, &colorFilterPaint); recordingCanvas->restore(); - SkAutoTUnref<SkPicture> picture3(recorder.endRecording()); + sk_sp<SkPicture> picture3(recorder.finishRecordingAsPicture()); canvas.clear(0); canvas.drawPicture(picture3); @@ -873,133 +1335,131 @@ REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN); } -static void test_huge_blur(SkBaseDevice* device, skiatest::Reporter* reporter) { - SkCanvas canvas(device); - +static void test_huge_blur(SkCanvas* canvas, skiatest::Reporter* reporter) { SkBitmap bitmap; bitmap.allocN32Pixels(100, 100); bitmap.eraseARGB(0, 0, 0, 0); // Check that a blur with an insane radius does not crash or assert. - SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(SkIntToScalar(1<<30), SkIntToScalar(1<<30))); - SkPaint paint; - paint.setImageFilter(blur); - canvas.drawSprite(bitmap, 0, 0, &paint); + paint.setImageFilter(SkBlurImageFilter::Make(SkIntToScalar(1<<30), + SkIntToScalar(1<<30), + nullptr)); + canvas->drawBitmap(bitmap, 0, 0, &paint); } DEF_TEST(HugeBlurImageFilter, reporter) { SkBitmap temp; temp.allocN32Pixels(100, 100); - SkBitmapDevice device(temp); - test_huge_blur(&device, reporter); + SkCanvas canvas(temp); + test_huge_blur(&canvas, reporter); } -DEF_TEST(MatrixConvolutionSanityTest, reporter) { +DEF_TEST(ImageFilterMatrixConvolutionSanityTest, reporter) { SkScalar kernel[1] = { 0 }; SkScalar gain = SK_Scalar1, bias = 0; SkIPoint kernelOffset = SkIPoint::Make(1, 1); - // Check that an enormous (non-allocatable) kernel gives a NULL filter. - SkAutoTUnref<SkImageFilter> conv(SkMatrixConvolutionImageFilter::Create( + // Check that an enormous (non-allocatable) kernel gives a nullptr filter. + sk_sp<SkImageFilter> conv(SkMatrixConvolutionImageFilter::Make( SkISize::Make(1<<30, 1<<30), kernel, gain, bias, kernelOffset, SkMatrixConvolutionImageFilter::kRepeat_TileMode, - false)); + false, + nullptr)); - REPORTER_ASSERT(reporter, NULL == conv.get()); + REPORTER_ASSERT(reporter, nullptr == conv.get()); - // Check that a NULL kernel gives a NULL filter. - conv.reset(SkMatrixConvolutionImageFilter::Create( + // Check that a nullptr kernel gives a nullptr filter. + conv = SkMatrixConvolutionImageFilter::Make( SkISize::Make(1, 1), - NULL, + nullptr, gain, bias, kernelOffset, SkMatrixConvolutionImageFilter::kRepeat_TileMode, - false)); + false, + nullptr); - REPORTER_ASSERT(reporter, NULL == conv.get()); + REPORTER_ASSERT(reporter, nullptr == conv.get()); - // Check that a kernel width < 1 gives a NULL filter. - conv.reset(SkMatrixConvolutionImageFilter::Create( + // Check that a kernel width < 1 gives a nullptr filter. + conv = SkMatrixConvolutionImageFilter::Make( SkISize::Make(0, 1), kernel, gain, bias, kernelOffset, SkMatrixConvolutionImageFilter::kRepeat_TileMode, - false)); + false, + nullptr); - REPORTER_ASSERT(reporter, NULL == conv.get()); + REPORTER_ASSERT(reporter, nullptr == conv.get()); - // Check that kernel height < 1 gives a NULL filter. - conv.reset(SkMatrixConvolutionImageFilter::Create( + // Check that kernel height < 1 gives a nullptr filter. + conv = SkMatrixConvolutionImageFilter::Make( SkISize::Make(1, -1), kernel, gain, bias, kernelOffset, SkMatrixConvolutionImageFilter::kRepeat_TileMode, - false)); + false, + nullptr); - REPORTER_ASSERT(reporter, NULL == conv.get()); + REPORTER_ASSERT(reporter, nullptr == conv.get()); } -static void test_xfermode_cropped_input(SkBaseDevice* device, skiatest::Reporter* reporter) { - SkCanvas canvas(device); - canvas.clear(0); +static void test_xfermode_cropped_input(SkCanvas* canvas, skiatest::Reporter* reporter) { + canvas->clear(0); SkBitmap bitmap; bitmap.allocN32Pixels(1, 1); bitmap.eraseARGB(255, 255, 255, 255); - SkAutoTUnref<SkColorFilter> green( - SkColorFilter::CreateModeFilter(SK_ColorGREEN, SkXfermode::kSrcIn_Mode)); - SkAutoTUnref<SkColorFilterImageFilter> greenFilter( - SkColorFilterImageFilter::Create(green.get())); + sk_sp<SkColorFilter> green(SkColorFilter::MakeModeFilter(SK_ColorGREEN, + SkBlendMode::kSrcIn)); + sk_sp<SkImageFilter> greenFilter(SkColorFilterImageFilter::Make(green, nullptr)); SkImageFilter::CropRect cropRect(SkRect::MakeEmpty()); - SkAutoTUnref<SkColorFilterImageFilter> croppedOut( - SkColorFilterImageFilter::Create(green.get(), NULL, &cropRect)); + sk_sp<SkImageFilter> croppedOut(SkColorFilterImageFilter::Make(green, nullptr, &cropRect)); // Check that an xfermode image filter whose input has been cropped out still draws the other // input. Also check that drawing with both inputs cropped out doesn't cause a GPU warning. - SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrcOver_Mode); - SkAutoTUnref<SkImageFilter> xfermodeNoFg( - SkXfermodeImageFilter::Create(mode, greenFilter, croppedOut)); - SkAutoTUnref<SkImageFilter> xfermodeNoBg( - SkXfermodeImageFilter::Create(mode, croppedOut, greenFilter)); - SkAutoTUnref<SkImageFilter> xfermodeNoFgNoBg( - SkXfermodeImageFilter::Create(mode, croppedOut, croppedOut)); + SkBlendMode mode = SkBlendMode::kSrcOver; + sk_sp<SkImageFilter> xfermodeNoFg(SkXfermodeImageFilter::Make(mode, greenFilter, + croppedOut, nullptr)); + sk_sp<SkImageFilter> xfermodeNoBg(SkXfermodeImageFilter::Make(mode, croppedOut, + greenFilter, nullptr)); + sk_sp<SkImageFilter> xfermodeNoFgNoBg(SkXfermodeImageFilter::Make(mode, croppedOut, + croppedOut, nullptr)); SkPaint paint; - paint.setImageFilter(xfermodeNoFg); - canvas.drawSprite(bitmap, 0, 0, &paint); + paint.setImageFilter(std::move(xfermodeNoFg)); + canvas->drawBitmap(bitmap, 0, 0, &paint); // drawSprite uint32_t pixel; - SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1); - canvas.readPixels(info, &pixel, 4, 0, 0); + SkImageInfo info = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType); + canvas->readPixels(info, &pixel, 4, 0, 0); REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN); - paint.setImageFilter(xfermodeNoBg); - canvas.drawSprite(bitmap, 0, 0, &paint); - canvas.readPixels(info, &pixel, 4, 0, 0); + paint.setImageFilter(std::move(xfermodeNoBg)); + canvas->drawBitmap(bitmap, 0, 0, &paint); // drawSprite + canvas->readPixels(info, &pixel, 4, 0, 0); REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN); - paint.setImageFilter(xfermodeNoFgNoBg); - canvas.drawSprite(bitmap, 0, 0, &paint); - canvas.readPixels(info, &pixel, 4, 0, 0); + paint.setImageFilter(std::move(xfermodeNoFgNoBg)); + canvas->drawBitmap(bitmap, 0, 0, &paint); // drawSprite + canvas->readPixels(info, &pixel, 4, 0, 0); REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN); } DEF_TEST(ImageFilterNestedSaveLayer, reporter) { SkBitmap temp; temp.allocN32Pixels(50, 50); - SkBitmapDevice device(temp); - SkCanvas canvas(&device); + SkCanvas canvas(temp); canvas.clear(0x0); SkBitmap bitmap; @@ -1009,15 +1469,15 @@ SkMatrix matrix; matrix.setScale(SkIntToScalar(2), SkIntToScalar(2)); matrix.postTranslate(SkIntToScalar(-20), SkIntToScalar(-20)); - SkAutoTUnref<SkImageFilter> matrixFilter( - SkMatrixImageFilter::Create(matrix, SkPaint::kLow_FilterLevel)); + sk_sp<SkImageFilter> matrixFilter( + SkImageFilter::MakeMatrixFilter(matrix, kLow_SkFilterQuality, nullptr)); // Test that saveLayer() with a filter nested inside another saveLayer() applies the // correct offset to the filter matrix. SkRect bounds1 = SkRect::MakeXYWH(10, 10, 30, 30); - canvas.saveLayer(&bounds1, NULL); + canvas.saveLayer(&bounds1, nullptr); SkPaint filterPaint; - filterPaint.setImageFilter(matrixFilter); + filterPaint.setImageFilter(std::move(matrixFilter)); SkRect bounds2 = SkRect::MakeXYWH(20, 20, 10, 10); canvas.saveLayer(&bounds2, &filterPaint); SkPaint greenPaint; @@ -1029,7 +1489,7 @@ strokePaint.setStyle(SkPaint::kStroke_Style); strokePaint.setColor(SK_ColorRED); - SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1); + SkImageInfo info = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType); uint32_t pixel; canvas.readPixels(info, &pixel, 4, 25, 25); REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN); @@ -1038,8 +1498,8 @@ // correct offset to the filter matrix. canvas.clear(0x0); canvas.readPixels(info, &pixel, 4, 25, 25); - canvas.saveLayer(&bounds1, NULL); - canvas.drawSprite(bitmap, 20, 20, &filterPaint); + canvas.saveLayer(&bounds1, nullptr); + canvas.drawBitmap(bitmap, 20, 20, &filterPaint); // drawSprite canvas.restore(); canvas.readPixels(info, &pixel, 4, 25, 25); @@ -1049,46 +1509,416 @@ DEF_TEST(XfermodeImageFilterCroppedInput, reporter) { SkBitmap temp; temp.allocN32Pixels(100, 100); - SkBitmapDevice device(temp); - test_xfermode_cropped_input(&device, reporter); + SkCanvas canvas(temp); + test_xfermode_cropped_input(&canvas, reporter); +} + +static void test_composed_imagefilter_offset(skiatest::Reporter* reporter, GrContext* context) { + sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, 100)); + + SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(1, 0, 20, 20)); + sk_sp<SkImageFilter> offsetFilter(SkOffsetImageFilter::Make(0, 0, nullptr, &cropRect)); + sk_sp<SkImageFilter> blurFilter(SkBlurImageFilter::Make(SK_Scalar1, SK_Scalar1, + nullptr, &cropRect)); + sk_sp<SkImageFilter> composedFilter(SkComposeImageFilter::Make(std::move(blurFilter), + std::move(offsetFilter))); + SkIPoint offset; + SkImageFilter::OutputProperties noColorSpace(nullptr); + SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, noColorSpace); + + sk_sp<SkSpecialImage> resultImg(composedFilter->filterImage(srcImg.get(), ctx, &offset)); + REPORTER_ASSERT(reporter, resultImg); + REPORTER_ASSERT(reporter, offset.fX == 1 && offset.fY == 0); +} + +DEF_TEST(ComposedImageFilterOffset, reporter) { + test_composed_imagefilter_offset(reporter, nullptr); } #if SK_SUPPORT_GPU -const SkSurfaceProps gProps = SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType); - -DEF_GPUTEST(ImageFilterCropRectGPU, reporter, factory) { - GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0)); - SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context, - SkImageInfo::MakeN32Premul(100, 100), - gProps, - 0)); - test_crop_rects(device, reporter); -} - -DEF_GPUTEST(HugeBlurImageFilterGPU, reporter, factory) { - GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0)); - SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context, - SkImageInfo::MakeN32Premul(100, 100), - gProps, - 0)); - test_huge_blur(device, reporter); -} - -DEF_GPUTEST(XfermodeImageFilterCroppedInputGPU, reporter, factory) { - GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0)); - SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context, - SkImageInfo::MakeN32Premul(1, 1), - gProps, - 0)); - test_xfermode_cropped_input(device, reporter); -} - -DEF_GPUTEST(TestNegativeBlurSigmaGPU, reporter, factory) { - GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0)); - SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context, - SkImageInfo::MakeN32Premul(1, 1), - gProps, - 0)); - test_negative_blur_sigma(device, reporter); +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ComposedImageFilterOffset_Gpu, reporter, ctxInfo) { + test_composed_imagefilter_offset(reporter, ctxInfo.grContext()); } #endif + +static void test_composed_imagefilter_bounds(skiatest::Reporter* reporter, GrContext* context) { + // The bounds passed to the inner filter must be filtered by the outer + // filter, so that the inner filter produces the pixels that the outer + // filter requires as input. This matters if the outer filter moves pixels. + // Here, accounting for the outer offset is necessary so that the green + // pixels of the picture are not clipped. + + SkPictureRecorder recorder; + SkCanvas* recordingCanvas = recorder.beginRecording(SkRect::MakeWH(200, 100)); + recordingCanvas->clipRect(SkRect::MakeXYWH(100, 0, 100, 100)); + recordingCanvas->clear(SK_ColorGREEN); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); + sk_sp<SkImageFilter> pictureFilter(SkPictureImageFilter::Make(picture)); + SkImageFilter::CropRect cropRect(SkRect::MakeWH(100, 100)); + sk_sp<SkImageFilter> offsetFilter(SkOffsetImageFilter::Make(-100, 0, nullptr, &cropRect)); + sk_sp<SkImageFilter> composedFilter(SkComposeImageFilter::Make(std::move(offsetFilter), + std::move(pictureFilter))); + + sk_sp<SkSpecialImage> sourceImage(create_empty_special_image(context, 100)); + SkImageFilter::OutputProperties noColorSpace(nullptr); + SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, noColorSpace); + SkIPoint offset; + sk_sp<SkSpecialImage> result(composedFilter->filterImage(sourceImage.get(), ctx, &offset)); + REPORTER_ASSERT(reporter, offset.isZero()); + REPORTER_ASSERT(reporter, result); + REPORTER_ASSERT(reporter, result->subset().size() == SkISize::Make(100, 100)); + + SkBitmap resultBM; + REPORTER_ASSERT(reporter, result->getROPixels(&resultBM)); + REPORTER_ASSERT(reporter, resultBM.getColor(50, 50) == SK_ColorGREEN); +} + +DEF_TEST(ComposedImageFilterBounds, reporter) { + test_composed_imagefilter_bounds(reporter, nullptr); +} + +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ComposedImageFilterBounds_Gpu, reporter, ctxInfo) { + test_composed_imagefilter_bounds(reporter, ctxInfo.grContext()); +} +#endif + +static void test_partial_crop_rect(skiatest::Reporter* reporter, GrContext* context) { + sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, 100)); + + SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(100, 0, 20, 30), + SkImageFilter::CropRect::kHasWidth_CropEdge | SkImageFilter::CropRect::kHasHeight_CropEdge); + sk_sp<SkImageFilter> filter(make_grayscale(nullptr, &cropRect)); + SkIPoint offset; + SkImageFilter::OutputProperties noColorSpace(nullptr); + SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, noColorSpace); + + sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg.get(), ctx, &offset)); + REPORTER_ASSERT(reporter, resultImg); + + REPORTER_ASSERT(reporter, offset.fX == 0); + REPORTER_ASSERT(reporter, offset.fY == 0); + REPORTER_ASSERT(reporter, resultImg->width() == 20); + REPORTER_ASSERT(reporter, resultImg->height() == 30); +} + +DEF_TEST(ImageFilterPartialCropRect, reporter) { + test_partial_crop_rect(reporter, nullptr); +} + +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterPartialCropRect_Gpu, reporter, ctxInfo) { + test_partial_crop_rect(reporter, ctxInfo.grContext()); +} +#endif + +DEF_TEST(ImageFilterCanComputeFastBounds, reporter) { + + { + SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1); + sk_sp<SkImageFilter> lighting(SkLightingImageFilter::MakePointLitDiffuse(location, + SK_ColorGREEN, + 0, 0, nullptr)); + REPORTER_ASSERT(reporter, !lighting->canComputeFastBounds()); + } + + { + sk_sp<SkImageFilter> gray(make_grayscale(nullptr, nullptr)); + REPORTER_ASSERT(reporter, gray->canComputeFastBounds()); + { + SkColorFilter* grayCF; + REPORTER_ASSERT(reporter, gray->asAColorFilter(&grayCF)); + REPORTER_ASSERT(reporter, !grayCF->affectsTransparentBlack()); + grayCF->unref(); + } + REPORTER_ASSERT(reporter, gray->canComputeFastBounds()); + + sk_sp<SkImageFilter> grayBlur(SkBlurImageFilter::Make(SK_Scalar1, SK_Scalar1, + std::move(gray))); + REPORTER_ASSERT(reporter, grayBlur->canComputeFastBounds()); + } + + { + SkScalar greenMatrix[20] = { 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1 }; + sk_sp<SkColorFilter> greenCF(SkColorFilter::MakeMatrixFilterRowMajor255(greenMatrix)); + sk_sp<SkImageFilter> green(SkColorFilterImageFilter::Make(greenCF, nullptr)); + + REPORTER_ASSERT(reporter, greenCF->affectsTransparentBlack()); + REPORTER_ASSERT(reporter, !green->canComputeFastBounds()); + + sk_sp<SkImageFilter> greenBlur(SkBlurImageFilter::Make(SK_Scalar1, SK_Scalar1, + std::move(green))); + REPORTER_ASSERT(reporter, !greenBlur->canComputeFastBounds()); + } + + uint8_t allOne[256], identity[256]; + for (int i = 0; i < 256; ++i) { + identity[i] = i; + allOne[i] = 255; + } + + sk_sp<SkColorFilter> identityCF(SkTableColorFilter::MakeARGB(identity, identity, + identity, allOne)); + sk_sp<SkImageFilter> identityFilter(SkColorFilterImageFilter::Make(identityCF, nullptr)); + REPORTER_ASSERT(reporter, !identityCF->affectsTransparentBlack()); + REPORTER_ASSERT(reporter, identityFilter->canComputeFastBounds()); + + sk_sp<SkColorFilter> forceOpaqueCF(SkTableColorFilter::MakeARGB(allOne, identity, + identity, identity)); + sk_sp<SkImageFilter> forceOpaque(SkColorFilterImageFilter::Make(forceOpaqueCF, nullptr)); + REPORTER_ASSERT(reporter, forceOpaqueCF->affectsTransparentBlack()); + REPORTER_ASSERT(reporter, !forceOpaque->canComputeFastBounds()); +} + +// Verify that SkImageSource survives serialization +DEF_TEST(ImageFilterImageSourceSerialization, reporter) { + auto surface(SkSurface::MakeRasterN32Premul(10, 10)); + surface->getCanvas()->clear(SK_ColorGREEN); + sk_sp<SkImage> image(surface->makeImageSnapshot()); + sk_sp<SkImageFilter> filter(SkImageSource::Make(std::move(image))); + + sk_sp<SkData> data(SkValidatingSerializeFlattenable(filter.get())); + sk_sp<SkImageFilter> unflattenedFilter = SkValidatingDeserializeImageFilter(data->data(), + data->size()); + REPORTER_ASSERT(reporter, unflattenedFilter); + + SkBitmap bm; + bm.allocN32Pixels(10, 10); + bm.eraseColor(SK_ColorBLUE); + SkPaint paint; + paint.setColor(SK_ColorRED); + paint.setImageFilter(unflattenedFilter); + + SkCanvas canvas(bm); + canvas.drawRect(SkRect::MakeWH(10, 10), paint); + REPORTER_ASSERT(reporter, *bm.getAddr32(0, 0) == SkPreMultiplyColor(SK_ColorGREEN)); +} + +static void test_large_blur_input(skiatest::Reporter* reporter, SkCanvas* canvas) { + SkBitmap largeBmp; + int largeW = 5000; + int largeH = 5000; +#if SK_SUPPORT_GPU + // If we're GPU-backed make the bitmap too large to be converted into a texture. + if (GrContext* ctx = canvas->getGrContext()) { + largeW = ctx->caps()->maxTextureSize() + 1; + } +#endif + + largeBmp.allocN32Pixels(largeW, largeH); + largeBmp.eraseColor(0); + if (!largeBmp.getPixels()) { + ERRORF(reporter, "Failed to allocate large bmp."); + return; + } + + sk_sp<SkImage> largeImage(SkImage::MakeFromBitmap(largeBmp)); + if (!largeImage) { + ERRORF(reporter, "Failed to create large image."); + return; + } + + sk_sp<SkImageFilter> largeSource(SkImageSource::Make(std::move(largeImage))); + if (!largeSource) { + ERRORF(reporter, "Failed to create large SkImageSource."); + return; + } + + sk_sp<SkImageFilter> blur(SkBlurImageFilter::Make(10.f, 10.f, std::move(largeSource))); + if (!blur) { + ERRORF(reporter, "Failed to create SkBlurImageFilter."); + return; + } + + SkPaint paint; + paint.setImageFilter(std::move(blur)); + + // This should not crash (http://crbug.com/570479). + canvas->drawRect(SkRect::MakeIWH(largeW, largeH), paint); +} + +DEF_TEST(ImageFilterBlurLargeImage, reporter) { + auto surface(SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(100, 100))); + test_large_blur_input(reporter, surface->getCanvas()); +} + +static void test_make_with_filter(skiatest::Reporter* reporter, GrContext* context) { + sk_sp<SkSurface> surface(create_surface(context, 192, 128)); + surface->getCanvas()->clear(SK_ColorRED); + SkPaint bluePaint; + bluePaint.setColor(SK_ColorBLUE); + SkIRect subset = SkIRect::MakeXYWH(25, 20, 50, 50); + surface->getCanvas()->drawRect(SkRect::Make(subset), bluePaint); + sk_sp<SkImage> sourceImage = surface->makeImageSnapshot(); + + sk_sp<SkImageFilter> filter = make_grayscale(nullptr, nullptr); + SkIRect clipBounds = SkIRect::MakeXYWH(30, 35, 100, 100); + SkIRect outSubset; + SkIPoint offset; + sk_sp<SkImage> result; + + result = sourceImage->makeWithFilter(nullptr, subset, clipBounds, &outSubset, &offset); + REPORTER_ASSERT(reporter, !result); + + result = sourceImage->makeWithFilter(filter.get(), subset, clipBounds, nullptr, &offset); + REPORTER_ASSERT(reporter, !result); + + result = sourceImage->makeWithFilter(filter.get(), subset, clipBounds, &outSubset, nullptr); + REPORTER_ASSERT(reporter, !result); + + SkIRect bigSubset = SkIRect::MakeXYWH(-10000, -10000, 20000, 20000); + result = sourceImage->makeWithFilter(filter.get(), bigSubset, clipBounds, &outSubset, &offset); + REPORTER_ASSERT(reporter, !result); + + SkIRect empty = SkIRect::MakeEmpty(); + result = sourceImage->makeWithFilter(filter.get(), empty, clipBounds, &outSubset, &offset); + REPORTER_ASSERT(reporter, !result); + + result = sourceImage->makeWithFilter(filter.get(), subset, empty, &outSubset, &offset); + REPORTER_ASSERT(reporter, !result); + + SkIRect leftField = SkIRect::MakeXYWH(-1000, 0, 100, 100); + result = sourceImage->makeWithFilter(filter.get(), subset, leftField, &outSubset, &offset); + REPORTER_ASSERT(reporter, !result); + + result = sourceImage->makeWithFilter(filter.get(), subset, clipBounds, &outSubset, &offset); + + REPORTER_ASSERT(reporter, result); + REPORTER_ASSERT(reporter, result->bounds().contains(outSubset)); + SkIRect destRect = SkIRect::MakeXYWH(offset.x(), offset.y(), + outSubset.width(), outSubset.height()); + REPORTER_ASSERT(reporter, clipBounds.contains(destRect)); + + // In GPU-mode, this case creates a special image with a backing size that differs from + // the content size + { + clipBounds.setXYWH(0, 0, 170, 100); + subset.setXYWH(0, 0, 160, 90); + + filter = SkXfermodeImageFilter::Make(SkBlendMode::kSrc, nullptr); + result = sourceImage->makeWithFilter(filter.get(), subset, clipBounds, &outSubset, &offset); + REPORTER_ASSERT(reporter, result); + } +} + +DEF_TEST(ImageFilterMakeWithFilter, reporter) { + test_make_with_filter(reporter, nullptr); +} + +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterMakeWithFilter_Gpu, reporter, ctxInfo) { + test_make_with_filter(reporter, ctxInfo.grContext()); +} +#endif + +#if SK_SUPPORT_GPU + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterHugeBlur_Gpu, reporter, ctxInfo) { + + sk_sp<SkSurface> surf(SkSurface::MakeRenderTarget(ctxInfo.grContext(), + SkBudgeted::kNo, + SkImageInfo::MakeN32Premul(100, 100))); + + + SkCanvas* canvas = surf->getCanvas(); + + test_huge_blur(canvas, reporter); +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(XfermodeImageFilterCroppedInput_Gpu, reporter, ctxInfo) { + sk_sp<SkSurface> surf(SkSurface::MakeRenderTarget( + ctxInfo.grContext(), + SkBudgeted::kNo, + SkImageInfo::Make(1, 1, kRGBA_8888_SkColorType, kPremul_SkAlphaType))); + + SkCanvas* canvas = surf->getCanvas(); + + test_xfermode_cropped_input(canvas, reporter); +} + +DEF_GPUTEST_FOR_ALL_CONTEXTS(ImageFilterBlurLargeImage_Gpu, reporter, ctxInfo) { + auto surface(SkSurface::MakeRenderTarget( + ctxInfo.grContext(), SkBudgeted::kYes, + SkImageInfo::Make(100, 100, kRGBA_8888_SkColorType, kPremul_SkAlphaType))); + test_large_blur_input(reporter, surface->getCanvas()); +} +#endif + +/* + * Test that colorfilterimagefilter does not require its CTM to be decomposed when it has more + * than just scale/translate, but that other filters do. + */ +DEF_TEST(ImageFilterComplexCTM, reporter) { + // just need a colorfilter to exercise the corresponding imagefilter + sk_sp<SkColorFilter> cf = SkColorFilter::MakeModeFilter(SK_ColorRED, SkBlendMode::kSrcATop); + sk_sp<SkImageFilter> cfif = SkColorFilterImageFilter::Make(cf, nullptr); // can handle + sk_sp<SkImageFilter> blif = SkBlurImageFilter::Make(3, 3, nullptr); // cannot handle + + struct { + sk_sp<SkImageFilter> fFilter; + bool fExpectCanHandle; + } recs[] = { + { cfif, true }, + { SkColorFilterImageFilter::Make(cf, cfif), true }, + { SkMergeImageFilter::Make(cfif, cfif), true }, + { SkComposeImageFilter::Make(cfif, cfif), true }, + + { blif, false }, + { SkBlurImageFilter::Make(3, 3, cfif), false }, + { SkColorFilterImageFilter::Make(cf, blif), false }, + { SkMergeImageFilter::Make(cfif, blif), false }, + { SkComposeImageFilter::Make(blif, cfif), false }, + }; + + for (const auto& rec : recs) { + const bool canHandle = rec.fFilter->canHandleComplexCTM(); + REPORTER_ASSERT(reporter, canHandle == rec.fExpectCanHandle); + } +} + +// Test that transforming the filter DAG doesn't clone shared nodes multiple times. +DEF_TEST(ImageFilterColorSpaceDAG, reporter) { + + // Helper for counting makeColorSpace() clones. + class TestFilter final : public SkImageFilter { + public: + TestFilter() : INHERITED(nullptr, 0, nullptr) {} + +#ifndef SK_IGNORE_TO_STRING + void toString(SkString*) const override {} +#endif + Factory getFactory() const override { return nullptr; } + + size_t cloneCount() const { return fCloneCount; } + + protected: + sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* src, const Context&, + SkIPoint* offset) const override { + return nullptr; + } + sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override { + fCloneCount++; + return sk_ref_sp(const_cast<TestFilter*>(this)); + } + + private: + typedef SkImageFilter INHERITED; + + mutable size_t fCloneCount = 0; + }; + + auto filter = sk_make_sp<TestFilter>(); + REPORTER_ASSERT(reporter, filter->cloneCount() == 0u); + + // Build a DAG referencing the filter twice. + auto complexFilter = SkMergeImageFilter::Make(filter, SkOffsetImageFilter::Make(1, 1, filter)); + REPORTER_ASSERT(reporter, filter->cloneCount() == 0u); + + auto xformer = SkColorSpaceXformer::Make(SkColorSpace::MakeSRGB()); + auto xformedFilter = xformer->apply(complexFilter.get()); + + REPORTER_ASSERT(reporter, filter->cloneCount() == 1u); +}
diff --git a/src/third_party/skia/tests/ImageFrom565Bitmap.cpp b/src/third_party/skia/tests/ImageFrom565Bitmap.cpp new file mode 100644 index 0000000..53f65df --- /dev/null +++ b/src/third_party/skia/tests/ImageFrom565Bitmap.cpp
@@ -0,0 +1,19 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkBitmap.h" +#include "SkImageInfo.h" +#include "SkImage.h" +#include "Test.h" + +DEF_TEST(ImageFrom565Bitmap, r) { + SkBitmap bm; + bm.allocPixels(SkImageInfo::Make( + 5, 7, kRGB_565_SkColorType, kOpaque_SkAlphaType)); + bm.eraseColor(SK_ColorBLACK); + REPORTER_ASSERT(r, SkImage::MakeFromBitmap(bm) != nullptr); +}
diff --git a/src/third_party/skia/tests/ImageGeneratorTest.cpp b/src/third_party/skia/tests/ImageGeneratorTest.cpp index 1f960ea..5cfd410 100644 --- a/src/third_party/skia/tests/ImageGeneratorTest.cpp +++ b/src/third_party/skia/tests/ImageGeneratorTest.cpp
@@ -5,30 +5,151 @@ * found in the LICENSE file. */ +#include "SkData.h" +#include "SkCanvas.h" +#include "SkGraphics.h" #include "SkImageGenerator.h" #include "Test.h" +static bool gMyFactoryWasCalled; + +static std::unique_ptr<SkImageGenerator> my_factory(sk_sp<SkData>) { + gMyFactoryWasCalled = true; + return nullptr; +} + +static void test_imagegenerator_factory(skiatest::Reporter* reporter) { + // just need a non-empty data to test things + sk_sp<SkData> data(SkData::MakeWithCString("test_imagegenerator_factory")); + + gMyFactoryWasCalled = false; + + REPORTER_ASSERT(reporter, !gMyFactoryWasCalled); + + std::unique_ptr<SkImageGenerator> gen = SkImageGenerator::MakeFromEncoded(data); + REPORTER_ASSERT(reporter, nullptr == gen); + REPORTER_ASSERT(reporter, !gMyFactoryWasCalled); + + // Test is racy, in that it hopes no other thread is changing this global... + auto prev = SkGraphics::SetImageGeneratorFromEncodedDataFactory(my_factory); + gen = SkImageGenerator::MakeFromEncoded(data); + REPORTER_ASSERT(reporter, nullptr == gen); + REPORTER_ASSERT(reporter, gMyFactoryWasCalled); + SkGraphics::SetImageGeneratorFromEncodedDataFactory(prev); +} + +class MyImageGenerator : public SkImageGenerator { +public: + MyImageGenerator() : SkImageGenerator(SkImageInfo::MakeN32Premul(0, 0)) {} +}; + DEF_TEST(ImageGenerator, reporter) { - SkImageGenerator ig; - SkISize sizes[3]; - sizes[0] = SkISize::Make(200, 200); - sizes[1] = SkISize::Make(100, 100); - sizes[2] = SkISize::Make( 50, 50); - void* planes[3] = { NULL }; - size_t rowBytes[3] = { 0 }; + MyImageGenerator ig; + SkYUVSizeInfo sizeInfo; + sizeInfo.fSizes[SkYUVSizeInfo::kY] = SkISize::Make(200, 200); + sizeInfo.fSizes[SkYUVSizeInfo::kU] = SkISize::Make(100, 100); + sizeInfo.fSizes[SkYUVSizeInfo::kV] = SkISize::Make( 50, 50); + sizeInfo.fWidthBytes[SkYUVSizeInfo::kY] = 0; + sizeInfo.fWidthBytes[SkYUVSizeInfo::kU] = 0; + sizeInfo.fWidthBytes[SkYUVSizeInfo::kV] = 0; + void* planes[3] = { nullptr }; SkYUVColorSpace colorSpace; // Check that the YUV decoding API does not cause any crashes - ig.getYUV8Planes(sizes, NULL, NULL, &colorSpace); - ig.getYUV8Planes(sizes, NULL, NULL, NULL); - ig.getYUV8Planes(sizes, planes, NULL, NULL); - ig.getYUV8Planes(sizes, NULL, rowBytes, NULL); - ig.getYUV8Planes(sizes, planes, rowBytes, NULL); - ig.getYUV8Planes(sizes, planes, rowBytes, &colorSpace); - + ig.queryYUV8(&sizeInfo, nullptr); + ig.queryYUV8(&sizeInfo, &colorSpace); + sizeInfo.fWidthBytes[SkYUVSizeInfo::kY] = 250; + sizeInfo.fWidthBytes[SkYUVSizeInfo::kU] = 250; + sizeInfo.fWidthBytes[SkYUVSizeInfo::kV] = 250; int dummy; - planes[0] = planes[1] = planes[2] = &dummy; - rowBytes[0] = rowBytes[1] = rowBytes[2] = 250; + planes[SkYUVSizeInfo::kY] = planes[SkYUVSizeInfo::kU] = planes[SkYUVSizeInfo::kV] = &dummy; + ig.getYUV8Planes(sizeInfo, planes); - ig.getYUV8Planes(sizes, planes, rowBytes, &colorSpace); + // Suppressed due to https://code.google.com/p/skia/issues/detail?id=4339 + if (false) { + test_imagegenerator_factory(reporter); + } +} + +#include "SkAutoMalloc.h" +#include "SkPictureRecorder.h" + +static sk_sp<SkPicture> make_picture() { + SkPictureRecorder recorder; + recorder.beginRecording(100, 100)->drawColor(SK_ColorRED); + return recorder.finishRecordingAsPicture(); +} + +DEF_TEST(PictureImageGenerator, reporter) { + const struct { + SkColorType fColorType; + SkAlphaType fAlphaType; + bool fExpectSuccess; + } recs[] = { + { kRGBA_8888_SkColorType, kPremul_SkAlphaType, kRGBA_8888_SkColorType == kN32_SkColorType }, + { kBGRA_8888_SkColorType, kPremul_SkAlphaType, kBGRA_8888_SkColorType == kN32_SkColorType }, + { kRGBA_F16_SkColorType, kPremul_SkAlphaType, true }, + + { kRGBA_8888_SkColorType, kUnpremul_SkAlphaType, false }, + { kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, false }, + { kRGBA_F16_SkColorType, kUnpremul_SkAlphaType, false }, + }; + + auto colorspace = SkColorSpace::MakeSRGB(); + auto picture = make_picture(); + auto gen = SkImageGenerator::MakeFromPicture({100, 100}, picture, nullptr, nullptr, + SkImage::BitDepth::kU8, colorspace); + + // worst case for all requests + SkAutoMalloc storage(100 * 100 * SkColorTypeBytesPerPixel(kRGBA_F16_SkColorType)); + + for (const auto& rec : recs) { + SkImageInfo info = SkImageInfo::Make(100, 100, rec.fColorType, rec.fAlphaType, colorspace); + bool success = gen->getPixels(info, storage.get(), info.minRowBytes()); + REPORTER_ASSERT(reporter, success == rec.fExpectSuccess); + } +} + +#include "SkImagePriv.h" + +DEF_TEST(ColorXformGenerator, r) { + SkBitmap a, b, c, d, e; + SkImageInfo info = SkImageInfo::MakeS32(1, 1, kPremul_SkAlphaType); + a.allocPixels(info); + b.allocPixels(info.makeColorSpace(nullptr)); + c.allocPixels(info.makeColorSpace(SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, + SkColorSpace::kRec2020_Gamut))); + d.allocPixels(info.makeColorSpace(SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, + SkColorSpace::kAdobeRGB_Gamut))); + e.allocPixels(info); + a.eraseColor(0); + b.eraseColor(1); + c.eraseColor(2); + d.eraseColor(3); + e.eraseColor(4); + + sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB(); + sk_sp<SkImage> ia = SkMakeImageInColorSpace(a, srgb, 0); + sk_sp<SkImage> ib = SkMakeImageInColorSpace(b, srgb, b.getGenerationID()); + sk_sp<SkImage> ic = SkMakeImageInColorSpace(c, srgb, c.getGenerationID()); + sk_sp<SkImage> id = SkMakeImageInColorSpace(d, srgb, 0); + sk_sp<SkImage> ie = SkMakeImageInColorSpace(e, srgb, e.getGenerationID(), + kAlways_SkCopyPixelsMode); + + // Equal because sRGB->sRGB is a no-op. + REPORTER_ASSERT(r, ia->uniqueID() == a.getGenerationID()); + + // Equal because nullptr->sRGB is a no-op (nullptr is treated as sRGB), and because + // we pass the explicit id that we want. In the no-op case, the implementation + // actually asserts that if we pass an id, it must match the id on the bitmap. + REPORTER_ASSERT(r, ib->uniqueID() == b.getGenerationID()); + + // Equal because we pass in an explicit id. + REPORTER_ASSERT(r, ic->uniqueID() == c.getGenerationID()); + + // Not equal because sRGB->Adobe is not a no-op and we do not pass an explicit id. + REPORTER_ASSERT(r, id->uniqueID() != d.getGenerationID()); + + // Equal because we pass in an explicit id. Forcing a copy, but still want the id respected. + REPORTER_ASSERT(r, ie->uniqueID() == e.getGenerationID()); }
diff --git a/src/third_party/skia/tests/ImageIsOpaqueTest.cpp b/src/third_party/skia/tests/ImageIsOpaqueTest.cpp index 3fe5b3d..bf3dea6 100644 --- a/src/third_party/skia/tests/ImageIsOpaqueTest.cpp +++ b/src/third_party/skia/tests/ImageIsOpaqueTest.cpp
@@ -6,56 +6,134 @@ */ #include "SkTypes.h" -#if SK_SUPPORT_GPU -#include "GrContextFactory.h" -#endif -#include "SkImage.h" -#include "SkSurface.h" - +#include "Resources.h" #include "Test.h" -static void check_isopaque(skiatest::Reporter* reporter, SkSurface* surface, bool expectedOpaque) { - SkAutoTUnref<SkImage> image(surface->newImageSnapshot()); +#if SK_SUPPORT_GPU +#include "GrContext.h" +#endif +#include "SkCanvas.h" +#include "SkColorSpace_Base.h" +#include "SkImage.h" +#include "SkSurface.h" +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" + +static void test_flatten(skiatest::Reporter* reporter, const SkImageInfo& info) { + // Need a safe amount of 4-byte aligned storage. Note that one of the test ICC profiles + // is ~7500 bytes. + const size_t storageBytes = 8000; + SkAutoTMalloc<uint32_t> storage(storageBytes / sizeof(uint32_t)); + SkBinaryWriteBuffer wb(storage.get(), storageBytes); + info.flatten(wb); + SkASSERT(wb.bytesWritten() < storageBytes); + + SkReadBuffer rb(storage.get(), wb.bytesWritten()); + + // pick a noisy byte pattern, so we ensure that unflatten sets all of our fields + SkImageInfo info2 = SkImageInfo::Make(0xB8, 0xB8, (SkColorType) 0xB8, (SkAlphaType) 0xB8); + + info2.unflatten(rb); + REPORTER_ASSERT(reporter, rb.offset() == wb.bytesWritten()); + + REPORTER_ASSERT(reporter, info == info2); +} + +DEF_TEST(ImageInfo_flattening, reporter) { + sk_sp<SkData> data = + SkData::MakeFromFileName(GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str()); + sk_sp<SkColorSpace> space0 = SkColorSpace::MakeICC(data->data(), data->size()); + data = SkData::MakeFromFileName( GetResourcePath("icc_profiles/HP_Z32x.icc").c_str()); + sk_sp<SkColorSpace> space1 = SkColorSpace::MakeICC(data->data(), data->size()); + data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperLeft.icc").c_str()); + sk_sp<SkColorSpace> space2 = SkColorSpace::MakeICC(data->data(), data->size()); + data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperRight.icc").c_str()); + sk_sp<SkColorSpace> space3 = SkColorSpace::MakeICC(data->data(), data->size()); + + sk_sp<SkColorSpace> spaces[] = { + nullptr, + SkColorSpace::MakeSRGB(), + SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named), + space0, + space1, + space2, + space3, + }; + + for (int ct = 0; ct <= kLastEnum_SkColorType; ++ct) { + for (int at = 0; at <= kLastEnum_SkAlphaType; ++at) { + for (auto& cs : spaces) { + SkImageInfo info = SkImageInfo::Make(100, 200, + static_cast<SkColorType>(ct), + static_cast<SkAlphaType>(at), + cs); + test_flatten(reporter, info); + } + } + } +} + +static void check_isopaque(skiatest::Reporter* reporter, const sk_sp<SkSurface>& surface, + bool expectedOpaque) { + sk_sp<SkImage> image(surface->makeImageSnapshot()); REPORTER_ASSERT(reporter, image->isOpaque() == expectedOpaque); } DEF_TEST(ImageIsOpaqueTest, reporter) { SkImageInfo infoTransparent = SkImageInfo::MakeN32Premul(5, 5); - SkAutoTUnref<SkSurface> surfaceTransparent(SkSurface::NewRaster(infoTransparent)); + auto surfaceTransparent(SkSurface::MakeRaster(infoTransparent)); check_isopaque(reporter, surfaceTransparent, false); SkImageInfo infoOpaque = SkImageInfo::MakeN32(5, 5, kOpaque_SkAlphaType); - SkAutoTUnref<SkSurface> surfaceOpaque(SkSurface::NewRaster(infoOpaque)); + auto surfaceOpaque(SkSurface::MakeRaster(infoOpaque)); check_isopaque(reporter, surfaceOpaque, true); } #if SK_SUPPORT_GPU -DEF_GPUTEST(ImageIsOpaqueTest_GPU, reporter, factory) { - for (int i = 0; i < GrContextFactory::kGLContextTypeCnt; ++i) { - GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i; +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageIsOpaqueTest_Gpu, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + SkImageInfo infoTransparent = SkImageInfo::MakeN32Premul(5, 5); + auto surfaceTransparent(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, infoTransparent)); + check_isopaque(reporter, surfaceTransparent, false); - if (!GrContextFactory::IsRenderingGLContext(glCtxType)) { - continue; - } + SkImageInfo infoOpaque = SkImageInfo::MakeN32(5, 5, kOpaque_SkAlphaType); + auto surfaceOpaque(SkSurface::MakeRenderTarget(context,SkBudgeted::kNo, infoOpaque)); - GrContext* context = factory->get(glCtxType); - - if (NULL == context) { - continue; - } - - SkImageInfo infoTransparent = SkImageInfo::MakeN32Premul(5, 5); - SkAutoTUnref<SkSurface> surfaceTransparent(SkSurface::NewRenderTarget(context, infoTransparent)); - check_isopaque(reporter, surfaceTransparent, false); - - SkImageInfo infoOpaque = SkImageInfo::MakeN32(5, 5, kOpaque_SkAlphaType); - SkAutoTUnref<SkSurface> surfaceOpaque(SkSurface::NewRenderTarget(context, infoOpaque)); -#if 0 - // this is failing right now : TODO fix me - check_isopaque(reporter, surfaceOpaque, true); -#endif - } + check_isopaque(reporter, surfaceOpaque, true); } #endif + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#include "SkPictureRecorder.h" + +static sk_sp<SkPicture> make_picture() { + SkPictureRecorder recorder; + SkCanvas* canvas = recorder.beginRecording({ 0, 0, 10, 10 }); + canvas->drawColor(SK_ColorRED); + return recorder.finishRecordingAsPicture(); +} + +DEF_TEST(Image_isAlphaOnly, reporter) { + SkPMColor pmColors = 0; + SkPixmap pmap = { + SkImageInfo::MakeN32Premul(1, 1), + &pmColors, + sizeof(pmColors) + }; + for (auto& image : { + SkImage::MakeRasterCopy(pmap), + GetResourceAsImage("mandrill_128.png"), + GetResourceAsImage("color_wheel.jpg"), + SkImage::MakeFromPicture(make_picture(), { 10, 10 }, nullptr, nullptr, + SkImage::BitDepth::kU8, + SkColorSpace::MakeSRGB()), + }) + { + REPORTER_ASSERT(reporter, image->isAlphaOnly() == false); + } + + REPORTER_ASSERT(reporter, SkImage::MakeRasterCopy({ + SkImageInfo::MakeA8(1, 1), (uint8_t*)&pmColors, 1})->isAlphaOnly() == true); +}
diff --git a/src/third_party/skia/tests/ImageNewShaderTest.cpp b/src/third_party/skia/tests/ImageNewShaderTest.cpp index dfc5a24..fe01f04 100644 --- a/src/third_party/skia/tests/ImageNewShaderTest.cpp +++ b/src/third_party/skia/tests/ImageNewShaderTest.cpp
@@ -5,26 +5,23 @@ * found in the LICENSE file. */ -#include "SkTypes.h" -#if SK_SUPPORT_GPU -#include "GrContextFactory.h" -#endif #include "SkCanvas.h" #include "SkImage.h" #include "SkShader.h" #include "SkSurface.h" - +#include "SkTypes.h" #include "Test.h" -void testBitmapEquality(skiatest::Reporter* reporter, SkBitmap& bm1, SkBitmap& bm2) { - SkAutoLockPixels lockBm1(bm1); - SkAutoLockPixels lockBm2(bm2); +#if SK_SUPPORT_GPU +#include "GrContext.h" +#endif +static void test_bitmap_equality(skiatest::Reporter* reporter, SkBitmap& bm1, SkBitmap& bm2) { REPORTER_ASSERT(reporter, bm1.getSize() == bm2.getSize()); REPORTER_ASSERT(reporter, 0 == memcmp(bm1.getPixels(), bm2.getPixels(), bm1.getSize())); } -void paintSource(SkSurface* sourceSurface) { +static void paint_source(SkSurface* sourceSurface) { SkCanvas* sourceCanvas = sourceSurface->getCanvas(); sourceCanvas->clear(0xFFDEDEDE); @@ -41,13 +38,14 @@ sourceCanvas->drawRect(rect, paintColor); } -void runShaderTest(skiatest::Reporter* reporter, SkSurface* sourceSurface, SkSurface* destinationSurface, SkImageInfo& info) { - paintSource(sourceSurface); +static void run_shader_test(skiatest::Reporter* reporter, SkSurface* sourceSurface, + SkSurface* destinationSurface, SkImageInfo& info) { + paint_source(sourceSurface); - SkAutoTUnref<SkImage> sourceImage(sourceSurface->newImageSnapshot()); - SkAutoTUnref<SkShader> sourceShader(sourceImage->newShader( + sk_sp<SkImage> sourceImage(sourceSurface->makeImageSnapshot()); + sk_sp<SkShader> sourceShader = sourceImage->makeShader( SkShader::kRepeat_TileMode, - SkShader::kRepeat_TileMode)); + SkShader::kRepeat_TileMode); SkPaint paint; paint.setShader(sourceShader); @@ -56,27 +54,25 @@ destinationCanvas->clear(SK_ColorTRANSPARENT); destinationCanvas->drawPaint(paint); - SkIRect rect = SkIRect::MakeWH(info.width(), info.height()); - SkBitmap bmOrig; - sourceSurface->getCanvas()->readPixels(rect, &bmOrig); + bmOrig.allocN32Pixels(info.width(), info.height()); + sourceSurface->getCanvas()->readPixels(bmOrig, 0, 0); SkBitmap bm; - destinationCanvas->readPixels(rect, &bm); + bm.allocN32Pixels(info.width(), info.height()); + destinationCanvas->readPixels(bm, 0, 0); - testBitmapEquality(reporter, bmOrig, bm); - - + test_bitmap_equality(reporter, bmOrig, bm); // Test with a translated shader SkMatrix matrix; matrix.setTranslate(SkIntToScalar(-1), SkIntToScalar(0)); - SkAutoTUnref<SkShader> sourceShaderTranslated(sourceImage->newShader( + sk_sp<SkShader> sourceShaderTranslated = sourceImage->makeShader( SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, - &matrix)); + &matrix); destinationCanvas->clear(SK_ColorTRANSPARENT); @@ -86,11 +82,11 @@ destinationCanvas->drawPaint(paintTranslated); SkBitmap bmt; - destinationCanvas->readPixels(rect, &bmt); + bmt.allocN32Pixels(info.width(), info.height()); + destinationCanvas->readPixels(bmt, 0, 0); // Test correctness { - SkAutoLockPixels lockBm(bmt); for (int y = 0; y < info.height(); y++) { REPORTER_ASSERT(reporter, 0xFFFF0000 == bmt.getColor(0, y)); @@ -104,64 +100,50 @@ DEF_TEST(ImageNewShader, reporter) { SkImageInfo info = SkImageInfo::MakeN32Premul(5, 5); - SkAutoTUnref<SkSurface> sourceSurface(SkSurface::NewRaster(info)); - SkAutoTUnref<SkSurface> destinationSurface(SkSurface::NewRaster(info)); + auto sourceSurface(SkSurface::MakeRaster(info)); + auto destinationSurface(SkSurface::MakeRaster(info)); - runShaderTest(reporter, sourceSurface.get(), destinationSurface.get(), info); + run_shader_test(reporter, sourceSurface.get(), destinationSurface.get(), info); } #if SK_SUPPORT_GPU -void gpuToGpu(skiatest::Reporter* reporter, GrContext* context) { +static void gpu_to_gpu(skiatest::Reporter* reporter, GrContext* context) { SkImageInfo info = SkImageInfo::MakeN32Premul(5, 5); - SkAutoTUnref<SkSurface> sourceSurface(SkSurface::NewRenderTarget(context, info)); - SkAutoTUnref<SkSurface> destinationSurface(SkSurface::NewRenderTarget(context, info)); + auto sourceSurface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info)); + auto destinationSurface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info)); - runShaderTest(reporter, sourceSurface.get(), destinationSurface.get(), info); + run_shader_test(reporter, sourceSurface.get(), destinationSurface.get(), info); } -void gpuToRaster(skiatest::Reporter* reporter, GrContext* context) { +static void gpu_to_raster(skiatest::Reporter* reporter, GrContext* context) { SkImageInfo info = SkImageInfo::MakeN32Premul(5, 5); - SkAutoTUnref<SkSurface> sourceSurface(SkSurface::NewRenderTarget(context, info)); - SkAutoTUnref<SkSurface> destinationSurface(SkSurface::NewRaster(info)); + auto sourceSurface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info)); + auto destinationSurface(SkSurface::MakeRaster(info)); - runShaderTest(reporter, sourceSurface.get(), destinationSurface.get(), info); + run_shader_test(reporter, sourceSurface.get(), destinationSurface.get(), info); } -void rasterToGpu(skiatest::Reporter* reporter, GrContext* context) { +static void raster_to_gpu(skiatest::Reporter* reporter, GrContext* context) { SkImageInfo info = SkImageInfo::MakeN32Premul(5, 5); - SkAutoTUnref<SkSurface> sourceSurface(SkSurface::NewRaster(info)); - SkAutoTUnref<SkSurface> destinationSurface(SkSurface::NewRenderTarget(context, info)); + auto sourceSurface(SkSurface::MakeRaster(info)); + auto destinationSurface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info)); - runShaderTest(reporter, sourceSurface.get(), destinationSurface.get(), info); + run_shader_test(reporter, sourceSurface.get(), destinationSurface.get(), info); } -DEF_GPUTEST(ImageNewShader_GPU, reporter, factory) { - for (int i = 0; i < GrContextFactory::kGLContextTypeCnt; ++i) { - GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i; +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageNewShader_GPU, reporter, ctxInfo) { + // GPU -> GPU + gpu_to_gpu(reporter, ctxInfo.grContext()); - if (!GrContextFactory::IsRenderingGLContext(glCtxType)) { - continue; - } + // GPU -> RASTER + gpu_to_raster(reporter, ctxInfo.grContext()); - GrContext* context = factory->get(glCtxType); - - if (NULL == context) { - continue; - } - - // GPU -> GPU - gpuToGpu(reporter, context); - - // GPU -> RASTER - gpuToRaster(reporter, context); - - // RASTER -> GPU - rasterToGpu(reporter, context); - } + // RASTER -> GPU + raster_to_gpu(reporter, ctxInfo.grContext()); } #endif
diff --git a/src/third_party/skia/tests/ImageStorageTest.cpp b/src/third_party/skia/tests/ImageStorageTest.cpp new file mode 100644 index 0000000..f55553c --- /dev/null +++ b/src/third_party/skia/tests/ImageStorageTest.cpp
@@ -0,0 +1,161 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#if SK_SUPPORT_GPU + +#include "GrClip.h" +#include "GrFragmentProcessor.h" +#include "GrRenderTargetContext.h" +#include "GrTexture.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageStorageLoad, reporter, ctxInfo) { + class TestFP : public GrFragmentProcessor { + public: + static sk_sp<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy, + GrSLMemoryModel mm, + GrSLRestrict restrict) { + return sk_sp<GrFragmentProcessor>(new TestFP(std::move(proxy), mm, restrict)); + } + + const char* name() const override { return "Image Load Test FP"; } + + private: + TestFP(sk_sp<GrTextureProxy> proxy, GrSLMemoryModel mm, GrSLRestrict restrict) + : INHERITED(kNone_OptimizationFlags) + , fImageStorageAccess(std::move(proxy), kRead_GrIOType, mm, restrict) { + this->initClassID<TestFP>(); + this->addImageStorageAccess(&fImageStorageAccess); + } + + void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {} + + bool onIsEqual(const GrFragmentProcessor& that) const override { return true; } + + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { + class GLSLProcessor : public GrGLSLFragmentProcessor { + public: + GLSLProcessor() = default; + void emitCode(EmitArgs& args) override { + const TestFP& tfp = args.fFp.cast<TestFP>(); + GrGLSLFPFragmentBuilder* fb = args.fFragBuilder; + SkString imageLoadStr; + fb->codeAppend("highp vec2 coord = sk_FragCoord.xy;"); + fb->appendImageStorageLoad(&imageLoadStr, args.fImageStorages[0], + "ivec2(coord)"); + if (GrPixelConfigIsSint(tfp.fImageStorageAccess.peekTexture()->config())) { + // Map the signed bytes so that when then get read back as unorm values they + // will have their original bit pattern. + fb->codeAppendf("highp ivec4 ivals = %s;", imageLoadStr.c_str()); + // NV gives a linker error for this: + // fb->codeAppend("ivals += + // "mix(ivec4(0), ivec4(256), lessThan(ivals, ivec4(0)));"); + fb->codeAppend("if (ivals.r < 0) { ivals.r += 256; }"); + fb->codeAppend("if (ivals.g < 0) { ivals.g += 256; }"); + fb->codeAppend("if (ivals.b < 0) { ivals.b += 256; }"); + fb->codeAppend("if (ivals.a < 0) { ivals.a += 256; }"); + fb->codeAppendf("%s = vec4(ivals)/255;", args.fOutputColor); + } else { + fb->codeAppendf("%s = %s;", args.fOutputColor, imageLoadStr.c_str()); + } + } + }; + return new GLSLProcessor; + } + + ImageStorageAccess fImageStorageAccess; + typedef GrFragmentProcessor INHERITED; + }; + + static constexpr int kS = 256; + GrContext* context = ctxInfo.grContext(); + if (context->caps()->shaderCaps()->maxFragmentImageStorages() < 1) { + return; + } + + std::unique_ptr<uint32_t[]> data(new uint32_t[kS * kS]); + for (int j = 0; j < kS; ++j) { + for (int i = 0; i < kS; ++i) { + data[i + kS * j] = GrColorPackRGBA(i, j, 0, 0); + } + } + + std::unique_ptr<uint32_t[]> idata(new uint32_t[kS * kS]); + for (int j = 0; j < kS; ++j) { + for (int i = 0; i < kS; ++i) { + int8_t r = i - 128; + int8_t g = j - 128; + int8_t b = -128; + int8_t a = -128; + idata[i + kS * j] = ((uint8_t)a << 24) | ((uint8_t)b << 16) | + ((uint8_t)g << 8) | (uint8_t)r; + } + } + + // Currently image accesses always have "top left" semantics. + GrSurfaceDesc desc; + desc.fOrigin = kTopLeft_GrSurfaceOrigin; + desc.fWidth = kS; + desc.fHeight = kS; + struct { + GrPixelConfig fConfig; + std::unique_ptr<uint32_t[]> fData; + } tests[] = { + { + kRGBA_8888_GrPixelConfig, + std::move(data) + }, + { + kRGBA_8888_sint_GrPixelConfig, + std::move(idata) + }, + }; + for (const auto& test : tests) { + // This test should work with any memory model and with or without restrict + for (auto mm : {GrSLMemoryModel::kNone, + GrSLMemoryModel::kCoherent, + GrSLMemoryModel::kVolatile}) { + for (auto restrict : {GrSLRestrict::kNo, GrSLRestrict::kYes}) { + if (!context->caps()->canConfigBeImageStorage(test.fConfig)) { + continue; + } + desc.fConfig = test.fConfig; + sk_sp<GrTextureProxy> imageStorageTexture = + GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, + SkBudgeted::kYes, test.fData.get(), 0); + + sk_sp<GrRenderTargetContext> rtContext = + context->makeDeferredRenderTargetContext(SkBackingFit::kExact, kS, kS, + kRGBA_8888_GrPixelConfig, nullptr); + GrPaint paint; + paint.setPorterDuffXPFactory(SkBlendMode::kSrc); + paint.addColorFragmentProcessor(TestFP::Make(imageStorageTexture, mm, restrict)); + rtContext->drawPaint(GrNoClip(), std::move(paint), SkMatrix::I()); + std::unique_ptr<uint32_t[]> readData(new uint32_t[kS * kS]); + SkImageInfo info = SkImageInfo::Make(kS, kS, kRGBA_8888_SkColorType, + kPremul_SkAlphaType); + rtContext->readPixels(info, readData.get(), 0, 0, 0); + int failed = false; + for (int j = 0; j < kS && !failed; ++j) { + for (int i = 0; i < kS && !failed; ++i) { + uint32_t d = test.fData[j * kS + i]; + uint32_t rd = readData[j * kS + i]; + if (d != rd) { + failed = true; + ERRORF(reporter, "Expected 0x%08x, got 0x%08x at %d, %d.", d, rd, i, j); + } + } + } + } + } + } +} + +#endif
diff --git a/src/third_party/skia/tests/ImageTest.cpp b/src/third_party/skia/tests/ImageTest.cpp new file mode 100644 index 0000000..69b2a22 --- /dev/null +++ b/src/third_party/skia/tests/ImageTest.cpp
@@ -0,0 +1,1326 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <functional> +#include <initializer_list> +#include <vector> + +#include "SkAutoPixmapStorage.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkColorSpacePriv.h" +#include "SkData.h" +#include "SkImageEncoder.h" +#include "SkImageGenerator.h" +#include "SkImage_Base.h" +#include "SkImagePriv.h" +#include "SkMakeUnique.h" +#include "SkPicture.h" +#include "SkPictureRecorder.h" +#include "SkPixelSerializer.h" +#include "SkRRect.h" +#include "SkStream.h" +#include "SkSurface.h" +#include "SkUtils.h" +#include "Test.h" + +#include "Resources.h" +#include "sk_tool_utils.h" + +#if SK_SUPPORT_GPU +#include "GrContextPriv.h" +#include "GrGpu.h" +#include "GrResourceCache.h" +#include "GrTest.h" +#include "GrTexture.h" +#endif + +using namespace sk_gpu_test; + +SkImageInfo read_pixels_info(SkImage* image) { + if (as_IB(image)->onImageInfo().colorSpace()) { + return SkImageInfo::MakeS32(image->width(), image->height(), image->alphaType()); + } + + return SkImageInfo::MakeN32(image->width(), image->height(), image->alphaType()); +} + +static void assert_equal(skiatest::Reporter* reporter, SkImage* a, const SkIRect* subsetA, + SkImage* b) { + const int widthA = subsetA ? subsetA->width() : a->width(); + const int heightA = subsetA ? subsetA->height() : a->height(); + + REPORTER_ASSERT(reporter, widthA == b->width()); + REPORTER_ASSERT(reporter, heightA == b->height()); + + // see https://bug.skia.org/3965 + //REPORTER_ASSERT(reporter, a->isOpaque() == b->isOpaque()); + + SkAutoPixmapStorage pmapA, pmapB; + pmapA.alloc(read_pixels_info(a)); + pmapB.alloc(read_pixels_info(b)); + + const int srcX = subsetA ? subsetA->x() : 0; + const int srcY = subsetA ? subsetA->y() : 0; + + REPORTER_ASSERT(reporter, a->readPixels(pmapA, srcX, srcY)); + REPORTER_ASSERT(reporter, b->readPixels(pmapB, 0, 0)); + + const size_t widthBytes = widthA * 4; + for (int y = 0; y < heightA; ++y) { + REPORTER_ASSERT(reporter, !memcmp(pmapA.addr32(0, y), pmapB.addr32(0, y), widthBytes)); + } +} +static void draw_image_test_pattern(SkCanvas* canvas) { + canvas->clear(SK_ColorWHITE); + SkPaint paint; + paint.setColor(SK_ColorBLACK); + canvas->drawRect(SkRect::MakeXYWH(5, 5, 10, 10), paint); +} +static sk_sp<SkImage> create_image() { + const SkImageInfo info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType); + auto surface(SkSurface::MakeRaster(info)); + draw_image_test_pattern(surface->getCanvas()); + return surface->makeImageSnapshot(); +} +static sk_sp<SkData> create_image_data(SkImageInfo* info) { + *info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType); + const size_t rowBytes = info->minRowBytes(); + sk_sp<SkData> data(SkData::MakeUninitialized(rowBytes * info->height())); + { + SkBitmap bm; + bm.installPixels(*info, data->writable_data(), rowBytes); + SkCanvas canvas(bm); + draw_image_test_pattern(&canvas); + } + return data; +} +static sk_sp<SkImage> create_data_image() { + SkImageInfo info; + sk_sp<SkData> data(create_image_data(&info)); + return SkImage::MakeRasterData(info, std::move(data), info.minRowBytes()); +} +#if SK_SUPPORT_GPU // not gpu-specific but currently only used in GPU tests +static sk_sp<SkImage> create_image_large(int maxTextureSize) { + const SkImageInfo info = SkImageInfo::MakeN32(maxTextureSize + 1, 32, kOpaque_SkAlphaType); + auto surface(SkSurface::MakeRaster(info)); + surface->getCanvas()->clear(SK_ColorWHITE); + SkPaint paint; + paint.setColor(SK_ColorBLACK); + surface->getCanvas()->drawRect(SkRect::MakeXYWH(4000, 2, 28000, 30), paint); + return surface->makeImageSnapshot(); +} +static sk_sp<SkImage> create_picture_image() { + SkPictureRecorder recorder; + SkCanvas* canvas = recorder.beginRecording(10, 10); + canvas->clear(SK_ColorCYAN); + return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(), SkISize::Make(10, 10), + nullptr, nullptr, SkImage::BitDepth::kU8, + SkColorSpace::MakeSRGB()); +}; +#endif +// Want to ensure that our Release is called when the owning image is destroyed +struct RasterDataHolder { + RasterDataHolder() : fReleaseCount(0) {} + sk_sp<SkData> fData; + int fReleaseCount; + static void Release(const void* pixels, void* context) { + RasterDataHolder* self = static_cast<RasterDataHolder*>(context); + self->fReleaseCount++; + self->fData.reset(); + } +}; +static sk_sp<SkImage> create_rasterproc_image(RasterDataHolder* dataHolder) { + SkASSERT(dataHolder); + SkImageInfo info; + dataHolder->fData = create_image_data(&info); + return SkImage::MakeFromRaster(SkPixmap(info, dataHolder->fData->data(), info.minRowBytes()), + RasterDataHolder::Release, dataHolder); +} +static sk_sp<SkImage> create_codec_image() { + SkImageInfo info; + sk_sp<SkData> data(create_image_data(&info)); + SkBitmap bitmap; + bitmap.installPixels(info, data->writable_data(), info.minRowBytes()); + sk_sp<SkData> src(sk_tool_utils::EncodeImageToData(bitmap, SkEncodedImageFormat::kPNG, 100)); + return SkImage::MakeFromEncoded(std::move(src)); +} +#if SK_SUPPORT_GPU +static sk_sp<SkImage> create_gpu_image(GrContext* context) { + const SkImageInfo info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType); + auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info)); + draw_image_test_pattern(surface->getCanvas()); + return surface->makeImageSnapshot(); +} +#endif + +static void test_encode(skiatest::Reporter* reporter, SkImage* image) { + const SkIRect ir = SkIRect::MakeXYWH(5, 5, 10, 10); + sk_sp<SkData> origEncoded = image->encodeToData(); + REPORTER_ASSERT(reporter, origEncoded); + REPORTER_ASSERT(reporter, origEncoded->size() > 0); + + sk_sp<SkImage> decoded(SkImage::MakeFromEncoded(origEncoded)); + if (!decoded) { + ERRORF(reporter, "failed to decode image!"); + return; + } + REPORTER_ASSERT(reporter, decoded); + assert_equal(reporter, image, nullptr, decoded.get()); + + // Now see if we can instantiate an image from a subset of the surface/origEncoded + + decoded = SkImage::MakeFromEncoded(origEncoded, &ir); + REPORTER_ASSERT(reporter, decoded); + assert_equal(reporter, image, &ir, decoded.get()); +} + +DEF_TEST(ImageEncode, reporter) { + test_encode(reporter, create_image().get()); +} + +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageEncode_Gpu, reporter, ctxInfo) { + test_encode(reporter, create_gpu_image(ctxInfo.grContext()).get()); +} +#endif + +DEF_TEST(Image_MakeFromRasterBitmap, reporter) { + const struct { + SkCopyPixelsMode fCPM; + bool fExpectSameAsMutable; + bool fExpectSameAsImmutable; + } recs[] = { + { kIfMutable_SkCopyPixelsMode, false, true }, + { kAlways_SkCopyPixelsMode, false, false }, + { kNever_SkCopyPixelsMode, true, true }, + }; + for (auto rec : recs) { + SkPixmap pm; + SkBitmap bm; + bm.allocN32Pixels(100, 100); + + auto img = SkMakeImageFromRasterBitmap(bm, rec.fCPM); + REPORTER_ASSERT(reporter, img->peekPixels(&pm)); + const bool sameMutable = pm.addr32(0, 0) == bm.getAddr32(0, 0); + REPORTER_ASSERT(reporter, rec.fExpectSameAsMutable == sameMutable); + REPORTER_ASSERT(reporter, (bm.getGenerationID() == img->uniqueID()) == sameMutable); + + bm.notifyPixelsChanged(); // force a new generation ID + + bm.setImmutable(); + img = SkMakeImageFromRasterBitmap(bm, rec.fCPM); + REPORTER_ASSERT(reporter, img->peekPixels(&pm)); + const bool sameImmutable = pm.addr32(0, 0) == bm.getAddr32(0, 0); + REPORTER_ASSERT(reporter, rec.fExpectSameAsImmutable == sameImmutable); + REPORTER_ASSERT(reporter, (bm.getGenerationID() == img->uniqueID()) == sameImmutable); + } +} + +namespace { + +const char* kSerializedData = "serialized"; + +class MockSerializer : public SkPixelSerializer { +public: + MockSerializer(sk_sp<SkData> (*func)()) : fFunc(func), fDidEncode(false) { } + + bool didEncode() const { return fDidEncode; } + +protected: + bool onUseEncodedData(const void*, size_t) override { + return false; + } + + SkData* onEncode(const SkPixmap&) override { + fDidEncode = true; + return fFunc().release(); + } + +private: + sk_sp<SkData> (*fFunc)(); + bool fDidEncode; + + typedef SkPixelSerializer INHERITED; +}; + +} // anonymous namespace + +// Test that SkImage encoding observes custom pixel serializers. +DEF_TEST(Image_Encode_Serializer, reporter) { + MockSerializer serializer([]() -> sk_sp<SkData> { + return SkData::MakeWithCString(kSerializedData); + }); + sk_sp<SkImage> image(create_image()); + sk_sp<SkData> encoded = image->encodeToData(&serializer); + sk_sp<SkData> reference(SkData::MakeWithCString(kSerializedData)); + + REPORTER_ASSERT(reporter, serializer.didEncode()); + REPORTER_ASSERT(reporter, encoded); + REPORTER_ASSERT(reporter, encoded->size() > 0); + REPORTER_ASSERT(reporter, encoded->equals(reference.get())); +} + +// Test that image encoding failures do not break picture serialization/deserialization. +DEF_TEST(Image_Serialize_Encoding_Failure, reporter) { + auto surface(SkSurface::MakeRasterN32Premul(100, 100)); + surface->getCanvas()->clear(SK_ColorGREEN); + sk_sp<SkImage> image(surface->makeImageSnapshot()); + REPORTER_ASSERT(reporter, image); + + SkPictureRecorder recorder; + SkCanvas* canvas = recorder.beginRecording(100, 100); + canvas->drawImage(image, 0, 0); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); + REPORTER_ASSERT(reporter, picture); + REPORTER_ASSERT(reporter, picture->approximateOpCount() > 0); + + MockSerializer emptySerializer([]() -> sk_sp<SkData> { return SkData::MakeEmpty(); }); + MockSerializer nullSerializer([]() -> sk_sp<SkData> { return nullptr; }); + MockSerializer* serializers[] = { &emptySerializer, &nullSerializer }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(serializers); ++i) { + SkDynamicMemoryWStream wstream; + REPORTER_ASSERT(reporter, !serializers[i]->didEncode()); + picture->serialize(&wstream, serializers[i]); + REPORTER_ASSERT(reporter, serializers[i]->didEncode()); + + std::unique_ptr<SkStream> rstream(wstream.detachAsStream()); + sk_sp<SkPicture> deserialized(SkPicture::MakeFromStream(rstream.get())); + REPORTER_ASSERT(reporter, deserialized); + REPORTER_ASSERT(reporter, deserialized->approximateOpCount() > 0); + } +} + +// Test that a draw that only partially covers the drawing surface isn't +// interpreted as covering the entire drawing surface (i.e., exercise one of the +// conditions of SkCanvas::wouldOverwriteEntireSurface()). +DEF_TEST(Image_RetainSnapshot, reporter) { + const SkPMColor red = SkPackARGB32(0xFF, 0xFF, 0, 0); + const SkPMColor green = SkPackARGB32(0xFF, 0, 0xFF, 0); + SkImageInfo info = SkImageInfo::MakeN32Premul(2, 2); + auto surface(SkSurface::MakeRaster(info)); + surface->getCanvas()->clear(0xFF00FF00); + + SkPMColor pixels[4]; + memset(pixels, 0xFF, sizeof(pixels)); // init with values we don't expect + const SkImageInfo dstInfo = SkImageInfo::MakeN32Premul(2, 2); + const size_t dstRowBytes = 2 * sizeof(SkPMColor); + + sk_sp<SkImage> image1(surface->makeImageSnapshot()); + REPORTER_ASSERT(reporter, image1->readPixels(dstInfo, pixels, dstRowBytes, 0, 0)); + for (size_t i = 0; i < SK_ARRAY_COUNT(pixels); ++i) { + REPORTER_ASSERT(reporter, pixels[i] == green); + } + + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); + paint.setColor(SK_ColorRED); + + surface->getCanvas()->drawRect(SkRect::MakeXYWH(1, 1, 1, 1), paint); + + sk_sp<SkImage> image2(surface->makeImageSnapshot()); + REPORTER_ASSERT(reporter, image2->readPixels(dstInfo, pixels, dstRowBytes, 0, 0)); + REPORTER_ASSERT(reporter, pixels[0] == green); + REPORTER_ASSERT(reporter, pixels[1] == green); + REPORTER_ASSERT(reporter, pixels[2] == green); + REPORTER_ASSERT(reporter, pixels[3] == red); +} + +///////////////////////////////////////////////////////////////////////////////////////////////// + +static void make_bitmap_mutable(SkBitmap* bm) { + bm->allocN32Pixels(10, 10); +} + +static void make_bitmap_immutable(SkBitmap* bm) { + bm->allocN32Pixels(10, 10); + bm->setImmutable(); +} + +DEF_TEST(image_newfrombitmap, reporter) { + const struct { + void (*fMakeProc)(SkBitmap*); + bool fExpectPeekSuccess; + bool fExpectSharedID; + bool fExpectLazy; + } rec[] = { + { make_bitmap_mutable, true, false, false }, + { make_bitmap_immutable, true, true, false }, + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(rec); ++i) { + SkBitmap bm; + rec[i].fMakeProc(&bm); + + sk_sp<SkImage> image(SkImage::MakeFromBitmap(bm)); + SkPixmap pmap; + + const bool sharedID = (image->uniqueID() == bm.getGenerationID()); + REPORTER_ASSERT(reporter, sharedID == rec[i].fExpectSharedID); + + const bool peekSuccess = image->peekPixels(&pmap); + REPORTER_ASSERT(reporter, peekSuccess == rec[i].fExpectPeekSuccess); + + const bool lazy = image->isLazyGenerated(); + REPORTER_ASSERT(reporter, lazy == rec[i].fExpectLazy); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#if SK_SUPPORT_GPU + +#include "SkBitmapCache.h" + +/* + * This tests the caching (and preemptive purge) of the raster equivalent of a gpu-image. + * We cache it for performance when drawing into a raster surface. + * + * A cleaner test would know if each drawImage call triggered a read-back from the gpu, + * but we don't have that facility (at the moment) so we use a little internal knowledge + * of *how* the raster version is cached, and look for that. + */ +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(c, reporter, ctxInfo) { + SkImageInfo info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType); + sk_sp<SkImage> image(create_gpu_image(ctxInfo.grContext())); + const uint32_t uniqueID = image->uniqueID(); + const auto desc = SkBitmapCacheDesc::Make(image.get()); + + auto surface(SkSurface::MakeRaster(info)); + + // now we can test drawing a gpu-backed image into a cpu-backed surface + + { + SkBitmap cachedBitmap; + REPORTER_ASSERT(reporter, !SkBitmapCache::Find(desc, &cachedBitmap)); + } + + surface->getCanvas()->drawImage(image, 0, 0); + { + SkBitmap cachedBitmap; + if (SkBitmapCache::Find(desc, &cachedBitmap)) { + REPORTER_ASSERT(reporter, cachedBitmap.getGenerationID() == uniqueID); + REPORTER_ASSERT(reporter, cachedBitmap.isImmutable()); + REPORTER_ASSERT(reporter, cachedBitmap.getPixels()); + } else { + // unexpected, but not really a bug, since the cache is global and this test may be + // run w/ other threads competing for its budget. + SkDebugf("SkImage_Gpu2Cpu : cachedBitmap was already purged\n"); + } + } + + image.reset(nullptr); + { + SkBitmap cachedBitmap; + REPORTER_ASSERT(reporter, !SkBitmapCache::Find(desc, &cachedBitmap)); + } +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkImage_makeTextureImage, reporter, contextInfo) { + GrContext* context = contextInfo.grContext(); + sk_gpu_test::TestContext* testContext = contextInfo.testContext(); + GrContextFactory otherFactory; + ContextInfo otherContextInfo = otherFactory.getContextInfo(contextInfo.type()); + testContext->makeCurrent(); + + std::function<sk_sp<SkImage>()> imageFactories[] = { + create_image, + create_codec_image, + create_data_image, + // Create an image from a picture. + create_picture_image, + // Create a texture image. + [context] { return create_gpu_image(context); }, + // Create a texture image in a another GrContext. + [testContext, otherContextInfo] { + otherContextInfo.testContext()->makeCurrent(); + sk_sp<SkImage> otherContextImage = create_gpu_image(otherContextInfo.grContext()); + testContext->makeCurrent(); + return otherContextImage; + } + }; + + sk_sp<SkColorSpace> dstColorSpaces[] ={ + nullptr, + SkColorSpace::MakeSRGB(), + }; + + for (auto& dstColorSpace : dstColorSpaces) { + for (auto factory : imageFactories) { + sk_sp<SkImage> image(factory()); + if (!image) { + ERRORF(reporter, "Error creating image."); + continue; + } + + sk_sp<SkImage> texImage(image->makeTextureImage(context, dstColorSpace.get())); + if (!texImage) { + GrContext* imageContext = as_IB(image)->context(); + + // We expect to fail if image comes from a different GrContext. + if (!image->isTextureBacked() || imageContext == context) { + ERRORF(reporter, "makeTextureImage failed."); + } + continue; + } + if (!texImage->isTextureBacked()) { + ERRORF(reporter, "makeTextureImage returned non-texture image."); + continue; + } + if (image->isTextureBacked()) { + GrSurfaceProxy* origProxy = as_IB(image)->peekProxy(); + GrSurfaceProxy* copyProxy = as_IB(texImage)->peekProxy(); + + if (origProxy->underlyingUniqueID() != copyProxy->underlyingUniqueID()) { + ERRORF(reporter, "makeTextureImage made unnecessary texture copy."); + } + } + if (image->width() != texImage->width() || image->height() != texImage->height()) { + ERRORF(reporter, "makeTextureImage changed the image size."); + } + if (image->alphaType() != texImage->alphaType()) { + ERRORF(reporter, "makeTextureImage changed image alpha type."); + } + } + } +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkImage_makeNonTextureImage, reporter, contextInfo) { + GrContext* context = contextInfo.grContext(); + + std::function<sk_sp<SkImage>()> imageFactories[] = { + create_image, + create_codec_image, + create_data_image, + create_picture_image, + [context] { return create_gpu_image(context); }, + }; + SkColorSpace* legacyColorSpace = nullptr; + for (auto factory : imageFactories) { + sk_sp<SkImage> image = factory(); + if (!image->isTextureBacked()) { + REPORTER_ASSERT(reporter, image->makeNonTextureImage().get() == image.get()); + if (!(image = image->makeTextureImage(context, legacyColorSpace))) { + continue; + } + } + auto rasterImage = image->makeNonTextureImage(); + if (!rasterImage) { + ERRORF(reporter, "makeNonTextureImage failed for texture-backed image."); + } + REPORTER_ASSERT(reporter, !rasterImage->isTextureBacked()); + assert_equal(reporter, image.get(), nullptr, rasterImage.get()); + } +} + +DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SkImage_drawAbandonedGpuImage, reporter, contextInfo) { + auto context = contextInfo.grContext(); + auto image = create_gpu_image(context); + auto info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType); + auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info)); + image->getTexture()->abandon(); + surface->getCanvas()->drawImage(image, 0, 0); +} + +#endif + +class EmptyGenerator : public SkImageGenerator { +public: + EmptyGenerator() : SkImageGenerator(SkImageInfo::MakeN32Premul(0, 0)) {} +}; + +DEF_TEST(ImageEmpty, reporter) { + const SkImageInfo info = SkImageInfo::Make(0, 0, kN32_SkColorType, kPremul_SkAlphaType); + SkPixmap pmap(info, nullptr, 0); + REPORTER_ASSERT(reporter, nullptr == SkImage::MakeRasterCopy(pmap)); + REPORTER_ASSERT(reporter, nullptr == SkImage::MakeRasterData(info, nullptr, 0)); + REPORTER_ASSERT(reporter, nullptr == SkImage::MakeFromRaster(pmap, nullptr, nullptr)); + REPORTER_ASSERT(reporter, nullptr == SkImage::MakeFromGenerator( + skstd::make_unique<EmptyGenerator>())); +} + +DEF_TEST(ImageDataRef, reporter) { + SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1); + size_t rowBytes = info.minRowBytes(); + size_t size = info.getSafeSize(rowBytes); + sk_sp<SkData> data = SkData::MakeUninitialized(size); + REPORTER_ASSERT(reporter, data->unique()); + sk_sp<SkImage> image = SkImage::MakeRasterData(info, data, rowBytes); + REPORTER_ASSERT(reporter, !data->unique()); + image.reset(); + REPORTER_ASSERT(reporter, data->unique()); +} + +static bool has_pixels(const SkPMColor pixels[], int count, SkPMColor expected) { + for (int i = 0; i < count; ++i) { + if (pixels[i] != expected) { + return false; + } + } + return true; +} + +static void image_test_read_pixels(skiatest::Reporter* reporter, SkImage* image) { + if (!image) { + ERRORF(reporter, "Failed to create image!"); + return; + } + const SkPMColor expected = SkPreMultiplyColor(SK_ColorWHITE); + const SkPMColor notExpected = ~expected; + + const int w = 2, h = 2; + const size_t rowBytes = w * sizeof(SkPMColor); + SkPMColor pixels[w*h]; + + SkImageInfo info; + + info = SkImageInfo::MakeUnknown(w, h); + REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, 0)); + + // out-of-bounds should fail + info = SkImageInfo::MakeN32Premul(w, h); + REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, -w, 0)); + REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, -h)); + REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, image->width(), 0)); + REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, image->height())); + + // top-left should succeed + sk_memset32(pixels, notExpected, w*h); + REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, 0, 0)); + REPORTER_ASSERT(reporter, has_pixels(pixels, w*h, expected)); + + // bottom-right should succeed + sk_memset32(pixels, notExpected, w*h); + REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, + image->width() - w, image->height() - h)); + REPORTER_ASSERT(reporter, has_pixels(pixels, w*h, expected)); + + // partial top-left should succeed + sk_memset32(pixels, notExpected, w*h); + REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, -1, -1)); + REPORTER_ASSERT(reporter, pixels[3] == expected); + REPORTER_ASSERT(reporter, has_pixels(pixels, w*h - 1, notExpected)); + + // partial bottom-right should succeed + sk_memset32(pixels, notExpected, w*h); + REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, + image->width() - 1, image->height() - 1)); + REPORTER_ASSERT(reporter, pixels[0] == expected); + REPORTER_ASSERT(reporter, has_pixels(&pixels[1], w*h - 1, notExpected)); +} +DEF_TEST(ImageReadPixels, reporter) { + sk_sp<SkImage> image(create_image()); + image_test_read_pixels(reporter, image.get()); + + image = create_data_image(); + image_test_read_pixels(reporter, image.get()); + + RasterDataHolder dataHolder; + image = create_rasterproc_image(&dataHolder); + image_test_read_pixels(reporter, image.get()); + image.reset(); + REPORTER_ASSERT(reporter, 1 == dataHolder.fReleaseCount); + + image = create_codec_image(); + image_test_read_pixels(reporter, image.get()); +} +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageReadPixels_Gpu, reporter, ctxInfo) { + image_test_read_pixels(reporter, create_gpu_image(ctxInfo.grContext()).get()); +} +#endif + +static void check_legacy_bitmap(skiatest::Reporter* reporter, const SkImage* image, + const SkBitmap& bitmap, SkImage::LegacyBitmapMode mode) { + REPORTER_ASSERT(reporter, image->width() == bitmap.width()); + REPORTER_ASSERT(reporter, image->height() == bitmap.height()); + REPORTER_ASSERT(reporter, image->alphaType() == bitmap.alphaType()); + + if (SkImage::kRO_LegacyBitmapMode == mode) { + REPORTER_ASSERT(reporter, bitmap.isImmutable()); + } + + REPORTER_ASSERT(reporter, bitmap.getPixels()); + + const SkImageInfo info = SkImageInfo::MakeN32(1, 1, bitmap.alphaType()); + SkPMColor imageColor; + REPORTER_ASSERT(reporter, image->readPixels(info, &imageColor, sizeof(SkPMColor), 0, 0)); + REPORTER_ASSERT(reporter, imageColor == *bitmap.getAddr32(0, 0)); +} + +static void test_legacy_bitmap(skiatest::Reporter* reporter, const SkImage* image, SkImage::LegacyBitmapMode mode) { + if (!image) { + ERRORF(reporter, "Failed to create image."); + return; + } + SkBitmap bitmap; + REPORTER_ASSERT(reporter, image->asLegacyBitmap(&bitmap, mode)); + check_legacy_bitmap(reporter, image, bitmap, mode); + + // Test subsetting to exercise the rowBytes logic. + SkBitmap tmp; + REPORTER_ASSERT(reporter, bitmap.extractSubset(&tmp, SkIRect::MakeWH(image->width() / 2, + image->height() / 2))); + sk_sp<SkImage> subsetImage(SkImage::MakeFromBitmap(tmp)); + REPORTER_ASSERT(reporter, subsetImage.get()); + + SkBitmap subsetBitmap; + REPORTER_ASSERT(reporter, subsetImage->asLegacyBitmap(&subsetBitmap, mode)); + check_legacy_bitmap(reporter, subsetImage.get(), subsetBitmap, mode); +} +DEF_TEST(ImageLegacyBitmap, reporter) { + const SkImage::LegacyBitmapMode modes[] = { + SkImage::kRO_LegacyBitmapMode, + SkImage::kRW_LegacyBitmapMode, + }; + for (auto& mode : modes) { + sk_sp<SkImage> image(create_image()); + test_legacy_bitmap(reporter, image.get(), mode); + + image = create_data_image(); + test_legacy_bitmap(reporter, image.get(), mode); + + RasterDataHolder dataHolder; + image = create_rasterproc_image(&dataHolder); + test_legacy_bitmap(reporter, image.get(), mode); + image.reset(); + REPORTER_ASSERT(reporter, 1 == dataHolder.fReleaseCount); + + image = create_codec_image(); + test_legacy_bitmap(reporter, image.get(), mode); + } +} +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageLegacyBitmap_Gpu, reporter, ctxInfo) { + const SkImage::LegacyBitmapMode modes[] = { + SkImage::kRO_LegacyBitmapMode, + SkImage::kRW_LegacyBitmapMode, + }; + for (auto& mode : modes) { + sk_sp<SkImage> image(create_gpu_image(ctxInfo.grContext())); + test_legacy_bitmap(reporter, image.get(), mode); + } +} +#endif + +static void test_peek(skiatest::Reporter* reporter, SkImage* image, bool expectPeekSuccess) { + if (!image) { + ERRORF(reporter, "Failed to create image!"); + return; + } + SkPixmap pm; + bool success = image->peekPixels(&pm); + REPORTER_ASSERT(reporter, expectPeekSuccess == success); + if (success) { + const SkImageInfo& info = pm.info(); + REPORTER_ASSERT(reporter, 20 == info.width()); + REPORTER_ASSERT(reporter, 20 == info.height()); + REPORTER_ASSERT(reporter, kN32_SkColorType == info.colorType()); + REPORTER_ASSERT(reporter, kPremul_SkAlphaType == info.alphaType() || + kOpaque_SkAlphaType == info.alphaType()); + REPORTER_ASSERT(reporter, info.minRowBytes() <= pm.rowBytes()); + REPORTER_ASSERT(reporter, SkPreMultiplyColor(SK_ColorWHITE) == *pm.addr32(0, 0)); + } +} +DEF_TEST(ImagePeek, reporter) { + sk_sp<SkImage> image(create_image()); + test_peek(reporter, image.get(), true); + + image = create_data_image(); + test_peek(reporter, image.get(), true); + + RasterDataHolder dataHolder; + image = create_rasterproc_image(&dataHolder); + test_peek(reporter, image.get(), true); + image.reset(); + REPORTER_ASSERT(reporter, 1 == dataHolder.fReleaseCount); + + image = create_codec_image(); + test_peek(reporter, image.get(), false); +} +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImagePeek_Gpu, reporter, ctxInfo) { + sk_sp<SkImage> image(create_gpu_image(ctxInfo.grContext())); + test_peek(reporter, image.get(), false); +} +#endif + +#if SK_SUPPORT_GPU +struct TextureReleaseChecker { + TextureReleaseChecker() : fReleaseCount(0) {} + int fReleaseCount; + static void Release(void* self) { + static_cast<TextureReleaseChecker*>(self)->fReleaseCount++; + } +}; + +DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SkImage_NewFromTextureRelease, reporter, ctxInfo) { + const int kWidth = 10; + const int kHeight = 10; + std::unique_ptr<uint32_t[]> pixels(new uint32_t[kWidth * kHeight]); + + GrContext* ctx = ctxInfo.grContext(); + + GrBackendObject backendTexHandle = + ctxInfo.grContext()->getGpu()->createTestingOnlyBackendTexture( + pixels.get(), kWidth, kHeight, kRGBA_8888_GrPixelConfig, true); + + GrBackendTexture backendTex = GrTest::CreateBackendTexture(ctx->contextPriv().getBackend(), + kWidth, + kHeight, + kRGBA_8888_GrPixelConfig, + backendTexHandle); + + TextureReleaseChecker releaseChecker; + GrSurfaceOrigin texOrigin = kBottomLeft_GrSurfaceOrigin; + sk_sp<SkImage> refImg( + SkImage::MakeFromTexture(ctx, backendTex, texOrigin, kPremul_SkAlphaType, nullptr, + TextureReleaseChecker::Release, &releaseChecker)); + + GrSurfaceOrigin readBackOrigin; + GrBackendObject readBackHandle = refImg->getTextureHandle(false, &readBackOrigin); + // TODO: Make it so we can check this (see skbug.com/5019) +#if 0 + if (*readBackHandle != *(backendTexHandle)) { + ERRORF(reporter, "backend mismatch %d %d\n", + (int)readBackHandle, (int)backendTexHandle); + } + REPORTER_ASSERT(reporter, readBackHandle == backendTexHandle); +#else + REPORTER_ASSERT(reporter, SkToBool(readBackHandle)); +#endif + if (readBackOrigin != texOrigin) { + ERRORF(reporter, "origin mismatch %d %d\n", readBackOrigin, texOrigin); + } + REPORTER_ASSERT(reporter, readBackOrigin == texOrigin); + + // Now exercise the release proc + REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount); + refImg.reset(nullptr); // force a release of the image + REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount); + + ctxInfo.grContext()->getGpu()->deleteTestingOnlyBackendTexture(backendTexHandle); +} + +DEF_GPUTEST(SkImage_MakeCrossContextRelease, reporter, /*factory*/) { + GrContextFactory testFactory; + + sk_sp<SkData> data = GetResourceAsData("mandrill_128.png"); + SkASSERT(data.get()); + + for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) { + GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i); + ContextInfo ctxInfo = testFactory.getContextInfo(ctxType); + GrContext* ctx = ctxInfo.grContext(); + if (!ctx) { + continue; + } + + // If we don't have proper support for this feature, the factory will fallback to returning + // codec-backed images. Those will "work", but some of our checks will fail because we + // expect the cross-context images not to work on multiple contexts at once. + if (!ctx->caps()->crossContextTextureSupport()) { + continue; + } + + // We test three lifetime patterns for a single context: + // 1) Create image, free image + // 2) Create image, draw, flush, free image + // 3) Create image, draw, free image, flush + // ... and then repeat the last two patterns with drawing on a second* context: + // 4) Create image, draw*, flush*, free image + // 5) Create image, draw*, free iamge, flush* + + // Case #1: Create image, free image + { + sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr)); + refImg.reset(nullptr); // force a release of the image + } + + SkImageInfo info = SkImageInfo::MakeN32Premul(128, 128); + sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info); + SkCanvas* canvas = surface->getCanvas(); + + // Case #2: Create image, draw, flush, free image + { + sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr)); + + canvas->drawImage(refImg, 0, 0); + canvas->flush(); + + refImg.reset(nullptr); // force a release of the image + } + + // Case #3: Create image, draw, free image, flush + { + sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr)); + + canvas->drawImage(refImg, 0, 0); + refImg.reset(nullptr); // force a release of the image + + canvas->flush(); + } + + // Configure second context + sk_gpu_test::TestContext* testContext = ctxInfo.testContext(); + + ContextInfo otherContextInfo = testFactory.getSharedContextInfo(ctx); + GrContext* otherCtx = otherContextInfo.grContext(); + sk_gpu_test::TestContext* otherTestContext = otherContextInfo.testContext(); + + // Creating a context in a share group may fail + if (!otherCtx) { + continue; + } + + surface = SkSurface::MakeRenderTarget(otherCtx, SkBudgeted::kNo, info); + canvas = surface->getCanvas(); + + // Case #4: Create image, draw*, flush*, free image + { + testContext->makeCurrent(); + sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr)); + + otherTestContext->makeCurrent(); + canvas->drawImage(refImg, 0, 0); + canvas->flush(); + + testContext->makeCurrent(); + refImg.reset(nullptr); // force a release of the image + } + + // Case #5: Create image, draw*, free image, flush* + { + testContext->makeCurrent(); + sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr)); + + otherTestContext->makeCurrent(); + canvas->drawImage(refImg, 0, 0); + + testContext->makeCurrent(); + refImg.reset(nullptr); // force a release of the image + + otherTestContext->makeCurrent(); + canvas->flush(); + } + + // Case #6: Verify that only one context can be using the image at a time + { + testContext->makeCurrent(); + sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr)); + + // Any context should be able to borrow the texture at this point + sk_sp<SkColorSpace> texColorSpace; + sk_sp<GrTextureProxy> proxy = as_IB(refImg)->asTextureProxyRef( + ctx, GrSamplerParams::ClampNoFilter(), nullptr, &texColorSpace, nullptr); + REPORTER_ASSERT(reporter, proxy); + + // But once it's borrowed, no other context should be able to borrow + otherTestContext->makeCurrent(); + sk_sp<GrTextureProxy> otherProxy = as_IB(refImg)->asTextureProxyRef( + otherCtx, GrSamplerParams::ClampNoFilter(), nullptr, &texColorSpace, nullptr); + REPORTER_ASSERT(reporter, !otherProxy); + + // Original context (that's already borrowing) should be okay + testContext->makeCurrent(); + sk_sp<GrTextureProxy> proxySecondRef = as_IB(refImg)->asTextureProxyRef( + ctx, GrSamplerParams::ClampNoFilter(), nullptr, &texColorSpace, nullptr); + REPORTER_ASSERT(reporter, proxySecondRef); + + // Releae all refs from the original context + proxy.reset(nullptr); + proxySecondRef.reset(nullptr); + + // Now we should be able to borrow the texture from the other context + otherTestContext->makeCurrent(); + otherProxy = as_IB(refImg)->asTextureProxyRef( + otherCtx, GrSamplerParams::ClampNoFilter(), nullptr, &texColorSpace, nullptr); + REPORTER_ASSERT(reporter, otherProxy); + + // Release everything + otherProxy.reset(nullptr); + refImg.reset(nullptr); + } + } +} + +static void check_images_same(skiatest::Reporter* reporter, const SkImage* a, const SkImage* b) { + if (a->width() != b->width() || a->height() != b->height()) { + ERRORF(reporter, "Images must have the same size"); + return; + } + if (a->alphaType() != b->alphaType()) { + ERRORF(reporter, "Images must have the same alpha type"); + return; + } + + SkImageInfo info = SkImageInfo::MakeN32Premul(a->width(), a->height()); + SkAutoPixmapStorage apm; + SkAutoPixmapStorage bpm; + + apm.alloc(info); + bpm.alloc(info); + + if (!a->readPixels(apm, 0, 0)) { + ERRORF(reporter, "Could not read image a's pixels"); + return; + } + if (!b->readPixels(bpm, 0, 0)) { + ERRORF(reporter, "Could not read image b's pixels"); + return; + } + + for (auto y = 0; y < info.height(); ++y) { + for (auto x = 0; x < info.width(); ++x) { + uint32_t pixelA = *apm.addr32(x, y); + uint32_t pixelB = *bpm.addr32(x, y); + if (pixelA != pixelB) { + ERRORF(reporter, "Expected image pixels to be the same. At %d,%d 0x%08x != 0x%08x", + x, y, pixelA, pixelB); + return; + } + } + } +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DeferredTextureImage, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + sk_gpu_test::TestContext* testContext = ctxInfo.testContext(); + sk_sp<GrContextThreadSafeProxy> proxy = context->threadSafeProxy(); + + GrContextFactory otherFactory; + ContextInfo otherContextInfo = otherFactory.getContextInfo(ctxInfo.type()); + + testContext->makeCurrent(); + REPORTER_ASSERT(reporter, proxy); + auto createLarge = [context] { + return create_image_large(context->caps()->maxTextureSize()); + }; + struct { + std::function<sk_sp<SkImage> ()> fImageFactory; + std::vector<SkImage::DeferredTextureImageUsageParams> fParams; + sk_sp<SkColorSpace> fColorSpace; + SkColorType fColorType; + SkFilterQuality fExpectedQuality; + int fExpectedScaleFactor; + bool fExpectation; + } testCases[] = { + { create_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}}, + nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, true }, + { create_codec_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}}, + nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, true }, + { create_data_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}}, + nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, true }, + { create_picture_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}}, + nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false }, + { [context] { return create_gpu_image(context); }, + {{SkMatrix::I(), kNone_SkFilterQuality, 0}}, + nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false }, + // Create a texture image in a another GrContext. + { [testContext, otherContextInfo] { + otherContextInfo.testContext()->makeCurrent(); + sk_sp<SkImage> otherContextImage = create_gpu_image(otherContextInfo.grContext()); + testContext->makeCurrent(); + return otherContextImage; + }, {{SkMatrix::I(), kNone_SkFilterQuality, 0}}, + nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false }, + // Create an image that is too large to upload. + { createLarge, {{SkMatrix::I(), kNone_SkFilterQuality, 0}}, + nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false }, + // Create an image that is too large, but is scaled to an acceptable size. + { createLarge, {{SkMatrix::I(), kMedium_SkFilterQuality, 4}}, + nullptr, kN32_SkColorType, kMedium_SkFilterQuality, 16, true}, + // Create an image with multiple low filter qualities, make sure we round up. + { createLarge, {{SkMatrix::I(), kNone_SkFilterQuality, 4}, + {SkMatrix::I(), kMedium_SkFilterQuality, 4}}, + nullptr, kN32_SkColorType, kMedium_SkFilterQuality, 16, true}, + // Create an image with multiple prescale levels, make sure we chose the minimum scale. + { createLarge, {{SkMatrix::I(), kMedium_SkFilterQuality, 5}, + {SkMatrix::I(), kMedium_SkFilterQuality, 4}}, + nullptr, kN32_SkColorType, kMedium_SkFilterQuality, 16, true}, + // Create a images which are decoded to a 4444 backing. + { create_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}}, + nullptr, kARGB_4444_SkColorType, kNone_SkFilterQuality, 1, true }, + { create_codec_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}}, + nullptr, kARGB_4444_SkColorType, kNone_SkFilterQuality, 1, true }, + { create_data_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}}, + nullptr, kARGB_4444_SkColorType, kNone_SkFilterQuality, 1, true }, + // Valid SkColorSpace and SkColorType. + { create_data_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}}, + SkColorSpace::MakeSRGB(), kN32_SkColorType, kNone_SkFilterQuality, 1, true }, + // Invalid SkColorSpace and SkColorType. + { create_data_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}}, + SkColorSpace::MakeSRGB(), kARGB_4444_SkColorType, kNone_SkFilterQuality, 1, false }, + }; + + + for (auto testCase : testCases) { + sk_sp<SkImage> image(testCase.fImageFactory()); + if (!image) { + ERRORF(reporter, "Failed to create image!"); + continue; + } + + size_t size = image->getDeferredTextureImageData(*proxy, testCase.fParams.data(), + static_cast<int>(testCase.fParams.size()), + nullptr, testCase.fColorSpace.get(), + testCase.fColorType); + static const char *const kFS[] = { "fail", "succeed" }; + if (SkToBool(size) != testCase.fExpectation) { + ERRORF(reporter, "This image was expected to %s but did not.", + kFS[testCase.fExpectation]); + } + if (size) { + void* buffer = sk_malloc_throw(size); + void* misaligned = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(buffer) + 3); + if (image->getDeferredTextureImageData(*proxy, testCase.fParams.data(), + static_cast<int>(testCase.fParams.size()), + misaligned, testCase.fColorSpace.get(), + testCase.fColorType)) { + ERRORF(reporter, "Should fail when buffer is misaligned."); + } + if (!image->getDeferredTextureImageData(*proxy, testCase.fParams.data(), + static_cast<int>(testCase.fParams.size()), + buffer, testCase.fColorSpace.get(), + testCase.fColorType)) { + ERRORF(reporter, "deferred image size succeeded but creation failed."); + } else { + for (auto budgeted : { SkBudgeted::kNo, SkBudgeted::kYes }) { + sk_sp<SkImage> newImage( + SkImage::MakeFromDeferredTextureImageData(context, buffer, budgeted)); + REPORTER_ASSERT(reporter, newImage != nullptr); + if (newImage) { + // Scale the image in software for comparison. + SkImageInfo scaled_info = SkImageInfo::MakeN32( + image->width() / testCase.fExpectedScaleFactor, + image->height() / testCase.fExpectedScaleFactor, + image->alphaType()); + SkAutoPixmapStorage scaled; + scaled.alloc(scaled_info); + image->scalePixels(scaled, testCase.fExpectedQuality); + sk_sp<SkImage> scaledImage = SkImage::MakeRasterCopy(scaled); + check_images_same(reporter, scaledImage.get(), newImage.get()); + } + // The other context should not be able to create images from texture data + // created by the original context. + sk_sp<SkImage> newImage2(SkImage::MakeFromDeferredTextureImageData( + otherContextInfo.grContext(), buffer, budgeted)); + REPORTER_ASSERT(reporter, !newImage2); + testContext->makeCurrent(); + } + } + sk_free(buffer); + } + } +} +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static sk_sp<SkImage> create_picture_image(sk_sp<SkColorSpace> space) { + SkPictureRecorder recorder; + SkCanvas* canvas = recorder.beginRecording(10, 10); + canvas->clear(SK_ColorCYAN); + return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(), SkISize::Make(10, 10), + nullptr, nullptr, SkImage::BitDepth::kU8, std::move(space)); +}; + +static inline bool almost_equal(int a, int b) { + return SkTAbs(a - b) <= 1; +} + +DEF_TEST(Image_ColorSpace, r) { + sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB(); + sk_sp<SkImage> image = GetResourceAsImage("mandrill_512_q075.jpg"); + REPORTER_ASSERT(r, srgb.get() == image->colorSpace()); + + image = GetResourceAsImage("webp-color-profile-lossy.webp"); + SkColorSpaceTransferFn fn; + bool success = image->colorSpace()->isNumericalTransferFn(&fn); + REPORTER_ASSERT(r, success); + REPORTER_ASSERT(r, color_space_almost_equal(1.8f, fn.fG)); + + sk_sp<SkColorSpace> rec2020 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, + SkColorSpace::kRec2020_Gamut); + image = create_picture_image(rec2020); + REPORTER_ASSERT(r, SkColorSpace::Equals(rec2020.get(), image->colorSpace())); + + SkBitmap bitmap; + SkImageInfo info = SkImageInfo::MakeN32(10, 10, kPremul_SkAlphaType, rec2020); + bitmap.allocPixels(info); + image = SkImage::MakeFromBitmap(bitmap); + REPORTER_ASSERT(r, SkColorSpace::Equals(rec2020.get(), image->colorSpace())); + + sk_sp<SkSurface> surface = SkSurface::MakeRaster( + SkImageInfo::MakeN32Premul(SkISize::Make(10, 10))); + image = surface->makeImageSnapshot(); + REPORTER_ASSERT(r, nullptr == image->colorSpace()); + + surface = SkSurface::MakeRaster(info); + image = surface->makeImageSnapshot(); + REPORTER_ASSERT(r, SkColorSpace::Equals(rec2020.get(), image->colorSpace())); +} + +DEF_TEST(Image_makeColorSpace, r) { + sk_sp<SkColorSpace> p3 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, + SkColorSpace::kDCIP3_D65_Gamut); + SkColorSpaceTransferFn fn; + fn.fA = 1.f; fn.fB = 0.f; fn.fC = 0.f; fn.fD = 0.f; fn.fE = 0.f; fn.fF = 0.f; fn.fG = 1.8f; + sk_sp<SkColorSpace> adobeGamut = SkColorSpace::MakeRGB(fn, SkColorSpace::kAdobeRGB_Gamut); + + SkBitmap srgbBitmap; + srgbBitmap.allocPixels(SkImageInfo::MakeS32(1, 1, kOpaque_SkAlphaType)); + *srgbBitmap.getAddr32(0, 0) = SkSwizzle_RGBA_to_PMColor(0xFF604020); + srgbBitmap.setImmutable(); + sk_sp<SkImage> srgbImage = SkImage::MakeFromBitmap(srgbBitmap); + sk_sp<SkImage> p3Image = srgbImage->makeColorSpace(p3, SkTransferFunctionBehavior::kIgnore); + SkBitmap p3Bitmap; + bool success = p3Image->asLegacyBitmap(&p3Bitmap, SkImage::kRO_LegacyBitmapMode); + REPORTER_ASSERT(r, success); + REPORTER_ASSERT(r, almost_equal(0x28, SkGetPackedR32(*p3Bitmap.getAddr32(0, 0)))); + REPORTER_ASSERT(r, almost_equal(0x40, SkGetPackedG32(*p3Bitmap.getAddr32(0, 0)))); + REPORTER_ASSERT(r, almost_equal(0x5E, SkGetPackedB32(*p3Bitmap.getAddr32(0, 0)))); + + sk_sp<SkImage> adobeImage = srgbImage->makeColorSpace(adobeGamut, + SkTransferFunctionBehavior::kIgnore); + SkBitmap adobeBitmap; + success = adobeImage->asLegacyBitmap(&adobeBitmap, SkImage::kRO_LegacyBitmapMode); + REPORTER_ASSERT(r, success); + REPORTER_ASSERT(r, almost_equal(0x21, SkGetPackedR32(*adobeBitmap.getAddr32(0, 0)))); + REPORTER_ASSERT(r, almost_equal(0x31, SkGetPackedG32(*adobeBitmap.getAddr32(0, 0)))); + REPORTER_ASSERT(r, almost_equal(0x4C, SkGetPackedB32(*adobeBitmap.getAddr32(0, 0)))); + + srgbImage = GetResourceAsImage("1x1.png"); + p3Image = srgbImage->makeColorSpace(p3, SkTransferFunctionBehavior::kIgnore); + success = p3Image->asLegacyBitmap(&p3Bitmap, SkImage::kRO_LegacyBitmapMode); + REPORTER_ASSERT(r, success); + REPORTER_ASSERT(r, almost_equal(0x8B, SkGetPackedR32(*p3Bitmap.getAddr32(0, 0)))); + REPORTER_ASSERT(r, almost_equal(0x82, SkGetPackedG32(*p3Bitmap.getAddr32(0, 0)))); + REPORTER_ASSERT(r, almost_equal(0x77, SkGetPackedB32(*p3Bitmap.getAddr32(0, 0)))); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static void make_all_premul(SkBitmap* bm) { + bm->allocPixels(SkImageInfo::MakeN32(256, 256, kPremul_SkAlphaType)); + for (int a = 0; a < 256; ++a) { + for (int r = 0; r < 256; ++r) { + // make all valid premul combinations + int c = SkTMin(a, r); + *bm->getAddr32(a, r) = SkPackARGB32(a, c, c, c); + } + } +} + +static bool equal(const SkBitmap& a, const SkBitmap& b) { + SkASSERT(a.width() == b.width()); + SkASSERT(a.height() == b.height()); + for (int y = 0; y < a.height(); ++y) { + for (int x = 0; x < a.width(); ++x) { + SkPMColor pa = *a.getAddr32(x, y); + SkPMColor pb = *b.getAddr32(x, y); + if (pa != pb) { + return false; + } + } + } + return true; +} + +DEF_TEST(image_roundtrip_encode, reporter) { + SkBitmap bm0; + make_all_premul(&bm0); + + auto img0 = SkImage::MakeFromBitmap(bm0); + sk_sp<SkData> data = img0->encodeToData(SkEncodedImageFormat::kPNG, 100); + auto img1 = SkImage::MakeFromEncoded(data); + + SkBitmap bm1; + bm1.allocPixels(SkImageInfo::MakeN32(256, 256, kPremul_SkAlphaType)); + img1->readPixels(bm1.info(), bm1.getPixels(), bm1.rowBytes(), 0, 0); + + REPORTER_ASSERT(reporter, equal(bm0, bm1)); +} + +DEF_TEST(image_roundtrip_premul, reporter) { + SkBitmap bm0; + make_all_premul(&bm0); + + SkBitmap bm1; + bm1.allocPixels(SkImageInfo::MakeN32(256, 256, kUnpremul_SkAlphaType)); + bm0.readPixels(bm1.info(), bm1.getPixels(), bm1.rowBytes(), 0, 0); + + SkBitmap bm2; + bm2.allocPixels(SkImageInfo::MakeN32(256, 256, kPremul_SkAlphaType)); + bm1.readPixels(bm2.info(), bm2.getPixels(), bm2.rowBytes(), 0, 0); + + REPORTER_ASSERT(reporter, equal(bm0, bm2)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static void check_scaled_pixels(skiatest::Reporter* reporter, SkPixmap* pmap, uint32_t expected) { + // Verify that all pixels contain the original test color + for (auto y = 0; y < pmap->height(); ++y) { + for (auto x = 0; x < pmap->width(); ++x) { + uint32_t pixel = *pmap->addr32(x, y); + if (pixel != expected) { + ERRORF(reporter, "Expected scaled pixels to be the same. At %d,%d 0x%08x != 0x%08x", + x, y, pixel, expected); + return; + } + } + } +} + +static void test_scale_pixels(skiatest::Reporter* reporter, const SkImage* image, + uint32_t expected) { + SkImageInfo info = SkImageInfo::MakeN32Premul(image->width() * 2, image->height() * 2); + + // Make sure to test kDisallow first, so we don't just get a cache hit in that case + for (auto chint : { SkImage::kDisallow_CachingHint, SkImage::kAllow_CachingHint }) { + SkAutoPixmapStorage scaled; + scaled.alloc(info); + if (!image->scalePixels(scaled, kLow_SkFilterQuality, chint)) { + ERRORF(reporter, "Failed to scale image"); + continue; + } + + check_scaled_pixels(reporter, &scaled, expected); + } +} + +DEF_TEST(ImageScalePixels, reporter) { + const SkPMColor pmRed = SkPackARGB32(0xFF, 0xFF, 0, 0); + const SkColor red = SK_ColorRED; + + // Test raster image + SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1); + sk_sp<SkSurface> surface = SkSurface::MakeRaster(info); + surface->getCanvas()->clear(red); + sk_sp<SkImage> rasterImage = surface->makeImageSnapshot(); + test_scale_pixels(reporter, rasterImage.get(), pmRed); + + // Test encoded image + sk_sp<SkData> data = rasterImage->encodeToData(); + sk_sp<SkImage> codecImage = SkImage::MakeFromEncoded(data); + test_scale_pixels(reporter, codecImage.get(), pmRed); +} + +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageScalePixels_Gpu, reporter, ctxInfo) { + const SkPMColor pmRed = SkPackARGB32(0xFF, 0xFF, 0, 0); + const SkColor red = SK_ColorRED; + + SkImageInfo info = SkImageInfo::MakeN32Premul(16, 16); + sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctxInfo.grContext(), SkBudgeted::kNo, + info); + surface->getCanvas()->clear(red); + sk_sp<SkImage> gpuImage = surface->makeImageSnapshot(); + test_scale_pixels(reporter, gpuImage.get(), pmRed); +} +#endif
diff --git a/src/third_party/skia/tests/IndexedPngOverflowTest.cpp b/src/third_party/skia/tests/IndexedPngOverflowTest.cpp new file mode 100644 index 0000000..1a48b64 --- /dev/null +++ b/src/third_party/skia/tests/IndexedPngOverflowTest.cpp
@@ -0,0 +1,40 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "CodecPriv.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkImageInfo.h" +#include "SkSurface.h" +#include "Test.h" + +// A 20x1 image with 8 bits per pixel and a palette size of 2. Pixel values are 255, 254... Run +// this test with ASAN to make sure we don't try to access before/after any palette-sized buffers. +unsigned char gPng[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x08, 0x03, 0x00, 0x00, 0x00, 0xe9, 0x4c, 0x7e, 0x17, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x0f, 0x01, 0xb9, 0x8f, 0x00, 0x00, 0x00, 0x06, 0x50, 0x4c, + 0x54, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x67, 0xb9, 0xcf, + 0x00, 0x00, 0x00, 0x20, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xfd, + 0x07, 0x01, 0x00, 0x20, 0x08, 0x00, 0x41, 0xbc, 0x5b, 0xe8, 0xdf, 0x97, + 0x99, 0xe3, 0x92, 0xa0, 0xf2, 0xdf, 0x3d, 0x7b, 0x0d, 0xda, 0x04, 0x1c, + 0x03, 0xad, 0x00, 0x38, 0x5c, 0x2e, 0xad, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + +DEF_TEST(IndexedPngOverflow, reporter) { + SkBitmap image; + bool success = decode_memory(gPng, sizeof(gPng), &image); + REPORTER_ASSERT(reporter, success); + + auto surface(SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(20, 1))); + SkCanvas* canvas = surface->getCanvas(); + SkRect destRect = SkRect::MakeXYWH(0, 0, 20, 1); + canvas->drawBitmapRect(image, destRect, nullptr); +}
diff --git a/src/third_party/skia/tests/InsetConvexPolyTest.cpp b/src/third_party/skia/tests/InsetConvexPolyTest.cpp new file mode 100644 index 0000000..9c1349c --- /dev/null +++ b/src/third_party/skia/tests/InsetConvexPolyTest.cpp
@@ -0,0 +1,129 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "Test.h" +#include "SkInsetConvexPolygon.h" + +static bool is_convex(const SkTDArray<SkPoint>& poly) { + if (poly.count() < 3) { + return false; + } + + SkVector v0 = poly[0] - poly[poly.count() - 1]; + SkVector v1 = poly[1] - poly[poly.count() - 1]; + SkScalar winding = v0.cross(v1); + + for (int i = 0; i < poly.count()-1; ++i) { + int j = i + 1; + int k = (i + 2) % poly.count(); + + SkVector v0 = poly[j] - poly[i]; + SkVector v1 = poly[k] - poly[i]; + SkScalar perpDot = v0.cross(v1); + if (winding*perpDot < 0) { + return false; + } + } + + return true; +} + +DEF_TEST(InsetConvexPoly, reporter) { + SkTDArray<SkPoint> rrectPoly; + + // round rect + *rrectPoly.push() = SkPoint::Make(-100, 55); + *rrectPoly.push() = SkPoint::Make(100, 55); + *rrectPoly.push() = SkPoint::Make(100 + 2.5f, 50 + 4.330127f); + *rrectPoly.push() = SkPoint::Make(100 + 3.535534f, 50 + 3.535534f); + *rrectPoly.push() = SkPoint::Make(100 + 4.330127f, 50 + 2.5f); + *rrectPoly.push() = SkPoint::Make(105, 50); + *rrectPoly.push() = SkPoint::Make(105, -50); + *rrectPoly.push() = SkPoint::Make(100 + 4.330127f, -50 - 2.5f); + *rrectPoly.push() = SkPoint::Make(100 + 3.535534f, -50 - 3.535534f); + *rrectPoly.push() = SkPoint::Make(100 + 2.5f, -50 - 4.330127f); + *rrectPoly.push() = SkPoint::Make(100, -55); + *rrectPoly.push() = SkPoint::Make(-100, -55); + *rrectPoly.push() = SkPoint::Make(-100 - 2.5f, -50 - 4.330127f); + *rrectPoly.push() = SkPoint::Make(-100 - 3.535534f, -50 - 3.535534f); + *rrectPoly.push() = SkPoint::Make(-100 - 4.330127f, -50 - 2.5f); + *rrectPoly.push() = SkPoint::Make(-105, -50); + *rrectPoly.push() = SkPoint::Make(-105, 50); + *rrectPoly.push() = SkPoint::Make(-100 - 4.330127f, 50 + 2.5f); + *rrectPoly.push() = SkPoint::Make(-100 - 3.535534f, 50 + 3.535534f); + *rrectPoly.push() = SkPoint::Make(-100 - 2.5f, 50 + 4.330127f); + REPORTER_ASSERT(reporter, is_convex(rrectPoly)); + + // inset a little + SkTDArray<SkPoint> insetPoly; + bool result = SkInsetConvexPolygon(&rrectPoly[0], rrectPoly.count(), 3, &insetPoly); + REPORTER_ASSERT(reporter, result); + REPORTER_ASSERT(reporter, is_convex(insetPoly)); + + // inset to rect + result = SkInsetConvexPolygon(&rrectPoly[0], rrectPoly.count(), 10, &insetPoly); + REPORTER_ASSERT(reporter, result); + REPORTER_ASSERT(reporter, is_convex(insetPoly)); + REPORTER_ASSERT(reporter, insetPoly.count() == 4); + if (insetPoly.count() == 4) { + REPORTER_ASSERT(reporter, insetPoly[0].equals(-95, 45)); + REPORTER_ASSERT(reporter, insetPoly[1].equals(95, 45)); + REPORTER_ASSERT(reporter, insetPoly[2].equals(95, -45)); + REPORTER_ASSERT(reporter, insetPoly[3].equals(-95, -45)); + } + + // just to full inset + // fails, but outputs a line segment + result = SkInsetConvexPolygon(&rrectPoly[0], rrectPoly.count(), 55, &insetPoly); + REPORTER_ASSERT(reporter, !result); + REPORTER_ASSERT(reporter, !is_convex(insetPoly)); + REPORTER_ASSERT(reporter, insetPoly.count() == 2); + if (insetPoly.count() == 2) { + REPORTER_ASSERT(reporter, insetPoly[0].equals(-50, 0)); + REPORTER_ASSERT(reporter, insetPoly[1].equals(50, 0)); + } + + // past full inset + result = SkInsetConvexPolygon(&rrectPoly[0], rrectPoly.count(), 75, &insetPoly); + REPORTER_ASSERT(reporter, !result); + REPORTER_ASSERT(reporter, insetPoly.count() == 0); + + // troublesome case + SkTDArray<SkPoint> clippedRRectPoly; + *clippedRRectPoly.push() = SkPoint::Make(335.928101f, 428.219055f); + *clippedRRectPoly.push() = SkPoint::Make(330.414459f, 423.034912f); + *clippedRRectPoly.push() = SkPoint::Make(325.749084f, 417.395508f); + *clippedRRectPoly.push() = SkPoint::Make(321.931946f, 411.300842f); + *clippedRRectPoly.push() = SkPoint::Make(318.963074f, 404.750977f); + *clippedRRectPoly.push() = SkPoint::Make(316.842468f, 397.745850f); + *clippedRRectPoly.push() = SkPoint::Make(315.570068f, 390.285522f); + *clippedRRectPoly.push() = SkPoint::Make(315.145966f, 382.369965f); + *clippedRRectPoly.push() = SkPoint::Make(315.570068f, 374.454346f); + *clippedRRectPoly.push() = SkPoint::Make(316.842468f, 366.994019f); + *clippedRRectPoly.push() = SkPoint::Make(318.963074f, 359.988892f); + *clippedRRectPoly.push() = SkPoint::Make(321.931946f, 353.439056f); + *clippedRRectPoly.push() = SkPoint::Make(325.749084f, 347.344421f); + *clippedRRectPoly.push() = SkPoint::Make(330.414459f, 341.705017f); + *clippedRRectPoly.push() = SkPoint::Make(335.928101f, 336.520813f); + *clippedRRectPoly.push() = SkPoint::Make(342.289948f, 331.791901f); + *clippedRRectPoly.push() = SkPoint::Make(377.312134f, 331.791901f); + *clippedRRectPoly.push() = SkPoint::Make(381.195313f, 332.532593f); + *clippedRRectPoly.push() = SkPoint::Make(384.464935f, 334.754700f); + *clippedRRectPoly.push() = SkPoint::Make(386.687042f, 338.024292f); + *clippedRRectPoly.push() = SkPoint::Make(387.427765f, 341.907532f); + *clippedRRectPoly.push() = SkPoint::Make(387.427765f, 422.832367f); + *clippedRRectPoly.push() = SkPoint::Make(386.687042f, 426.715576f); + *clippedRRectPoly.push() = SkPoint::Make(384.464935f, 429.985168f); + *clippedRRectPoly.push() = SkPoint::Make(381.195313f, 432.207275f); + *clippedRRectPoly.push() = SkPoint::Make(377.312134f, 432.947998f); + *clippedRRectPoly.push() = SkPoint::Make(342.289948f, 432.947998f); + REPORTER_ASSERT(reporter, is_convex(clippedRRectPoly)); + + result = SkInsetConvexPolygon(&clippedRRectPoly[0], clippedRRectPoly.count(), 32.3699417f, + &insetPoly); + REPORTER_ASSERT(reporter, result); + REPORTER_ASSERT(reporter, is_convex(insetPoly)); +}
diff --git a/src/third_party/skia/tests/IntTextureTest.cpp b/src/third_party/skia/tests/IntTextureTest.cpp new file mode 100644 index 0000000..ccdc304 --- /dev/null +++ b/src/third_party/skia/tests/IntTextureTest.cpp
@@ -0,0 +1,290 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#if SK_SUPPORT_GPU +#include "GrClip.h" +#include "GrContext.h" +#include "GrContextPriv.h" +#include "GrRenderTargetContext.h" +#include "GrResourceProvider.h" +#include "GrTexture.h" +#include "effects/GrSimpleTextureEffect.h" + +template <typename I> +static SK_WHEN(std::is_integral<I>::value && 4 == sizeof(I), void) +check_pixels(skiatest::Reporter* reporter, int w, int h, const I exepctedData[], + const I actualData[], const char* testName) { + for (int j = 0; j < h; ++j) { + for (int i = 0; i < w; ++i) { + I expected = exepctedData[j * w + i]; + I actual = actualData[j * w + i]; + if (expected != actual) { + ERRORF(reporter, "[%s] Expected 0x08%x, got 0x%08x at %d, %d.", testName, expected, + actual, i, j); + return; + } + } + } +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(IntTexture, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + if (!context->caps()->isConfigTexturable(kRGBA_8888_sint_GrPixelConfig)) { + return; + } + static const int kS = UINT8_MAX + 1; + static const size_t kRowBytes = kS * sizeof(int32_t); + + GrSurfaceDesc desc; + desc.fOrigin = kTopLeft_GrSurfaceOrigin; + desc.fConfig = kRGBA_8888_sint_GrPixelConfig; + desc.fWidth = kS; + desc.fHeight = kS; + + std::unique_ptr<int32_t[]> testData(new int32_t[kS * kS]); + for (int j = 0; j < kS; ++j) { + for (int i = 0; i < kS; ++i) { + uint32_t r = i - INT8_MIN; + uint32_t g = j - INT8_MIN; + uint32_t b = INT8_MAX - r; + uint32_t a = INT8_MAX - g; + testData.get()[j * kS + i] = (a << 24) | (b << 16) | (g << 8) | r; + } + } + + // Test that attempting to create a integer texture with multiple MIP levels fails. + { + GrMipLevel levels[2]; + levels[0].fPixels = testData.get(); + levels[0].fRowBytes = kRowBytes; + levels[1].fPixels = testData.get(); + levels[1].fRowBytes = (kS / 2) * sizeof(int32_t); + + sk_sp<GrTextureProxy> temp(GrSurfaceProxy::MakeDeferredMipMap(context->resourceProvider(), + desc, + SkBudgeted::kYes, + levels, 2)); + REPORTER_ASSERT(reporter, !temp); + } + + // Test that we can create an integer texture. + sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), + desc, SkBudgeted::kYes, + testData.get(), + kRowBytes); + REPORTER_ASSERT(reporter, proxy); + if (!proxy) { + return; + } + + sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext( + std::move(proxy), nullptr); + if (!sContext) { + return; + } + + std::unique_ptr<int32_t[]> readData(new int32_t[kS * kS]); + // Test that reading to a non-integer config fails. + { + bool success = context->contextPriv().readSurfacePixels(sContext.get(), + 0, 0, kS, kS, + kRGBA_8888_GrPixelConfig, + nullptr, readData.get()); + REPORTER_ASSERT(reporter, !success); + } + { + std::unique_ptr<uint16_t[]> halfData(new uint16_t[4 * kS * kS]); + bool success = context->contextPriv().readSurfacePixels(sContext.get(), + 0, 0, kS, kS, + kRGBA_half_GrPixelConfig, + nullptr, halfData.get()); + REPORTER_ASSERT(reporter, !success); + } + { + // Can read back as ints. (ES only requires being able to read back into 32bit ints which + // we don't support. Right now this test is counting on GR_RGBA_INTEGER/GL_BYTE being the + // implementation-dependent second format). + sk_bzero(readData.get(), sizeof(int32_t) * kS * kS); + bool success = context->contextPriv().readSurfacePixels(sContext.get(), + 0, 0, kS, kS, + kRGBA_8888_sint_GrPixelConfig, + nullptr, readData.get()); + REPORTER_ASSERT(reporter, success); + if (success) { + check_pixels(reporter, kS, kS, testData.get(), readData.get(), "readPixels"); + } + } + { + // readPixels should fail if we attempt to use the unpremul flag with an integer texture. + bool success = context->contextPriv().readSurfacePixels( + sContext.get(), + 0, 0, kS, kS, + kRGBA_8888_sint_GrPixelConfig, + nullptr, readData.get(), 0, + GrContextPriv::kUnpremul_PixelOpsFlag); + REPORTER_ASSERT(reporter, !success); + } + + // Test that copying from one integer texture to another succeeds. + { + sk_sp<GrSurfaceContext> dstContext(GrSurfaceProxy::TestCopy(context, desc, + sContext->asSurfaceProxy())); + REPORTER_ASSERT(reporter, dstContext); + if (!dstContext || !dstContext->asTextureProxy()) { + return; + } + + sk_bzero(readData.get(), sizeof(int32_t) * kS * kS); + bool success = context->contextPriv().readSurfacePixels(dstContext.get(), 0, 0, kS, kS, + kRGBA_8888_sint_GrPixelConfig, + nullptr, readData.get()); + REPORTER_ASSERT(reporter, success); + if (success) { + check_pixels(reporter, kS, kS, testData.get(), readData.get(), "copyIntegerToInteger"); + } + } + + + // Test that copying to a non-integer (8888) texture fails. + { + GrSurfaceDesc nonIntDesc = desc; + nonIntDesc.fConfig = kRGBA_8888_GrPixelConfig; + + sk_sp<GrSurfaceContext> dstContext(GrSurfaceProxy::TestCopy(context, nonIntDesc, + sContext->asSurfaceProxy())); + REPORTER_ASSERT(reporter, !dstContext); + } + + // Test that copying to a non-integer (RGBA_half) texture fails. + if (context->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig)) { + GrSurfaceDesc nonIntDesc = desc; + nonIntDesc.fConfig = kRGBA_half_GrPixelConfig; + + sk_sp<GrSurfaceContext> dstContext(GrSurfaceProxy::TestCopy(context, nonIntDesc, + sContext->asSurfaceProxy())); + REPORTER_ASSERT(reporter, !dstContext); + } + + // We overwrite the top left quarter of the texture with the bottom right quarter of the + // original data. + const void* bottomRightQuarter = testData.get() + kS / 2 * kS + kS / 2; + + { + // Can't write pixels from a non-int config. + bool success = context->contextPriv().writeSurfacePixels(sContext.get(), + 0, 0, kS/2, kS/2, + kRGBA_8888_GrPixelConfig, nullptr, + bottomRightQuarter, kRowBytes); + REPORTER_ASSERT(reporter, !success); + } + { + // Can't use unpremul flag. + bool success = context->contextPriv().writeSurfacePixels( + sContext.get(), + 0, 0, kS/2, kS/2, + kRGBA_8888_sint_GrPixelConfig, + nullptr, + bottomRightQuarter, kRowBytes, + GrContextPriv::kUnpremul_PixelOpsFlag); + REPORTER_ASSERT(reporter, !success); + } + { + bool success = context->contextPriv().writeSurfacePixels(sContext.get(), + 0, 0, kS/2, kS/2, + kRGBA_8888_sint_GrPixelConfig, + nullptr, + bottomRightQuarter, kRowBytes); + REPORTER_ASSERT(reporter, success); + if (!success) { + return; + } + + sk_bzero(readData.get(), sizeof(int32_t) * kS * kS); + success = context->contextPriv().readSurfacePixels(sContext.get(), + 0, 0, kS, kS, + kRGBA_8888_sint_GrPixelConfig, + nullptr, readData.get(), 0); + REPORTER_ASSERT(reporter, success); + if (!success) { + return; + } + std::unique_ptr<int32_t[]> overwrittenTestData(new int32_t[kS * kS]); + memcpy(overwrittenTestData.get(), testData.get(), sizeof(int32_t) * kS * kS); + char* dst = (char*)overwrittenTestData.get(); + char* src = (char*)(testData.get() + kS/2 * kS + kS/2); + for (int i = 0; i < kS/2; ++i) { + memcpy(dst, src, sizeof(int32_t) * kS/2); + dst += kRowBytes; + src += kRowBytes; + } + check_pixels(reporter, kS, kS, overwrittenTestData.get(), readData.get(), "overwrite"); + } + + // Test drawing from the integer texture to a fixed point texture. To avoid any premul issues + // we init the int texture with 0s and 1s and make alpha always be 1. We expect that 1s turn + // into 0xffs and zeros stay zero. + std::unique_ptr<uint32_t[]> expectedData(new uint32_t[kS * kS]); + std::unique_ptr<uint32_t[]> actualData(new uint32_t[kS * kS]); + for (int i = 0; i < kS*kS; ++i) { + int32_t a = 0x1; + int32_t b = ((i & 0x1) ? 1 : 0); + int32_t g = ((i & 0x1) ? 0 : 1); + int32_t r = ((i & 0x2) ? 1 : 0); + testData.get()[i] = (a << 24) | (b << 16) | (g << 8) | r; + expectedData.get()[i] = ((0xFF * a) << 24) | ((0xFF * b) << 16) | + ((0xFF * g) << 8) | (0xFF * r); + } + context->contextPriv().writeSurfacePixels(sContext.get(), + 0, 0, kS, kS, + kRGBA_8888_sint_GrPixelConfig, nullptr, + testData.get(), 0); + + sk_sp<GrRenderTargetContext> rtContext = context->makeDeferredRenderTargetContext( + SkBackingFit::kExact, kS, kS, kRGBA_8888_GrPixelConfig, nullptr); + + struct { + GrSamplerParams::FilterMode fMode; + const char* fName; + } kNamedFilters[] ={ + { GrSamplerParams::kNone_FilterMode, "filter-none" }, + { GrSamplerParams::kBilerp_FilterMode, "filter-bilerp" }, + { GrSamplerParams::kMipMap_FilterMode, "filter-mipmap" } + }; + + for (auto filter : kNamedFilters) { + sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(sContext->asTextureProxyRef(), + nullptr, + SkMatrix::I(), + filter.fMode)); + REPORTER_ASSERT(reporter, fp); + if (!fp) { + return; + } + rtContext->clear(nullptr, 0xDDAABBCC, true); + GrPaint paint; + paint.setPorterDuffXPFactory(SkBlendMode::kSrc); + paint.addColorFragmentProcessor(fp); + rtContext->drawPaint(GrNoClip(), std::move(paint), SkMatrix::I()); + SkImageInfo readInfo = SkImageInfo::Make(kS, kS, kRGBA_8888_SkColorType, + kPremul_SkAlphaType); + rtContext->readPixels(readInfo, actualData.get(), 0, 0, 0); + check_pixels(reporter, kS, kS, expectedData.get(), actualData.get(), filter.fName); + } + + { + // No rendering to integer textures. + GrSurfaceDesc intRTDesc = desc; + intRTDesc.fFlags = kRenderTarget_GrSurfaceFlag; + sk_sp<GrTexture> temp(context->resourceProvider()->createTexture(intRTDesc, + SkBudgeted::kYes)); + REPORTER_ASSERT(reporter, !temp); + } +} + +#endif
diff --git a/src/third_party/skia/tests/InterpolatorTest.cpp b/src/third_party/skia/tests/InterpolatorTest.cpp index 3cfd19f..8ba1bda 100644 --- a/src/third_party/skia/tests/InterpolatorTest.cpp +++ b/src/third_party/skia/tests/InterpolatorTest.cpp
@@ -58,4 +58,34 @@ result = inter.timeToValues(175, v); REPORTER_ASSERT(reporter, result == SkInterpolator::kNormal_Result); + for (SkScalar val = -0.1f; val <= 1.1f; val += 0.1f) { + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkTPin(0.f, val, 1.f), + SkUnitCubicInterp(val, 1.f/3, 1.f/3, 2.f/3, 2.f/3))); + } + + // These numbers come from + // http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag. + const SkScalar testTransitions[][4] = { + { 0.25f, 0.1f, 0.25f, 1 }, // ease + { 0.42f, 0, 1, 1 }, // ease in + { 0, 0, 0.58f, 1 }, // ease out + { 0.42f, 0, 0.58f, 1 }, // ease in out + }; + + const SkScalar expectedOutput[][5] = { + { 0.0947876f, 0.513367f, 0.80249f, 0.940796f, 0.994263f }, // ease + { 0.0170288f, 0.129639f, 0.31543f, 0.554749f, 0.839417f }, // ease in + { 0.160583f, 0.445251f, 0.684692f, 0.870361f, 0.982971f }, // ease out + { 0.0197144f, 0.187439f, 0.500122f, 0.812561f, 0.980286f }, // ease in out + }; + + int i = 0; + for (const SkScalar* t : testTransitions) { + int j = 0; + for (SkScalar val = 0.1f; val < 1; val += 0.2f) { + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(expectedOutput[i][j++], + SkUnitCubicInterp(val, t[0], t[1], t[2], t[3]))); + } + ++i; + } }
diff --git a/src/third_party/skia/tests/InvalidIndexedPngTest.cpp b/src/third_party/skia/tests/InvalidIndexedPngTest.cpp new file mode 100644 index 0000000..5d9632d --- /dev/null +++ b/src/third_party/skia/tests/InvalidIndexedPngTest.cpp
@@ -0,0 +1,36 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "CodecPriv.h" +#include "SkBitmap.h" +#include "Test.h" + +// A valid 1x1 indexed PNG. +unsigned char gPngData[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x08, 0x03, 0x00, 0x00, 0x00, 0x28, 0xcb, 0x34, 0xbb, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x0f, 0x01, 0xb9, 0x8f, 0x00, 0x00, 0x00, 0x06, 0x50, 0x4c, + 0x54, 0x45, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xd2, 0x87, 0xef, 0x71, + 0x00, 0x00, 0x00, 0x13, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xfd, + 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xf8, 0xaf, 0x16, 0x46, 0x00, + 0x02, 0x00, 0x01, 0x32, 0x60, 0xf7, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x49, + 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + +// Attempt to decode an invalid PNG that has a palette. Mostly we're looking to +// make sure we don't leak memory since libpng uses setjmp for error handling so +// it's very easy to accidentally skip destructors when a failure happens. +// As a result, we do not have any REPORTER_ASSERT statements +DEF_TEST(InvalidIndexedPng, reporter) { + SkBitmap image; + // Make our PNG invalid by changing a byte. + gPngData[sizeof(gPngData) - 1] = 1; + + decode_memory(gPngData, sizeof(gPngData), &image); +}
diff --git a/src/third_party/skia/tests/IsClosedSingleContourTest.cpp b/src/third_party/skia/tests/IsClosedSingleContourTest.cpp new file mode 100644 index 0000000..b550998 --- /dev/null +++ b/src/third_party/skia/tests/IsClosedSingleContourTest.cpp
@@ -0,0 +1,59 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "Test.h" +#include "SkPathPriv.h" + +DEF_TEST(IsClosedSingleContourTest, reporter) { + SkPath p; + REPORTER_ASSERT(reporter, !SkPathPriv::IsClosedSingleContour(p)); + + p.reset(); + p.close(); + REPORTER_ASSERT(reporter, !SkPathPriv::IsClosedSingleContour(p)); + + p.reset(); + p.moveTo(10, 10); + p.close(); + REPORTER_ASSERT(reporter, SkPathPriv::IsClosedSingleContour(p)); + + p.reset(); + p.moveTo(10, 10); + p.lineTo(20, 20); + p.close(); + REPORTER_ASSERT(reporter, SkPathPriv::IsClosedSingleContour(p)); + + p.reset(); + p.moveTo(10, 10); + p.lineTo(20, 20); + p.quadTo(30, 30, 40, 40); + p.cubicTo(50, 50, 60, 60, 70, 70); + p.conicTo(30, 30, 40, 40, 0.5); + p.close(); + REPORTER_ASSERT(reporter, SkPathPriv::IsClosedSingleContour(p)); + + p.reset(); + p.moveTo(10, 10); + p.lineTo(20, 20); + p.lineTo(20, 30); + REPORTER_ASSERT(reporter, !SkPathPriv::IsClosedSingleContour(p)); + + p.reset(); + p.moveTo(10, 10); + p.lineTo(20, 20); + p.moveTo(10, 10); + p.lineTo(20, 30); + p.close(); + REPORTER_ASSERT(reporter, !SkPathPriv::IsClosedSingleContour(p)); + + p.reset(); + p.moveTo(10, 10); + p.lineTo(20, 20); + p.close(); + p.lineTo(20, 30); + p.close(); + REPORTER_ASSERT(reporter, !SkPathPriv::IsClosedSingleContour(p)); +}
diff --git a/src/third_party/skia/tests/JpegTest.cpp b/src/third_party/skia/tests/JpegTest.cpp deleted file mode 100644 index f8784a2..0000000 --- a/src/third_party/skia/tests/JpegTest.cpp +++ /dev/null
@@ -1,454 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkBitmap.h" -#include "SkData.h" -#include "SkForceLinking.h" -#include "SkImage.h" -#include "SkImageDecoder.h" -#include "SkStream.h" -#include "Test.h" - -__SK_FORCE_IMAGE_DECODER_LINKING; - -#define JPEG_TEST_WRITE_TO_FILE_FOR_DEBUGGING 0 // do not do this for - // normal unit testing. -static unsigned char goodJpegImage[] = { -0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, -0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x8F, -0x00, 0x8F, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, -0x00, 0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, -0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07, -0x0C, 0x08, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0B, -0x0B, 0x09, 0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, -0x0F, 0x11, 0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, -0x14, 0x1A, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, -0x1A, 0x1D, 0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, -0x22, 0x24, 0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, -0x1E, 0xFF, 0xDB, 0x00, 0x43, 0x01, 0x05, 0x05, -0x05, 0x07, 0x06, 0x07, 0x0E, 0x08, 0x08, 0x0E, -0x1E, 0x14, 0x11, 0x14, 0x1E, 0x1E, 0x1E, 0x1E, -0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, -0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, -0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, -0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, -0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, -0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0xFF, 0xC0, -0x00, 0x11, 0x08, 0x00, 0x80, 0x00, 0x80, 0x03, -0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, -0x01, 0xFF, 0xC4, 0x00, 0x18, 0x00, 0x01, 0x01, -0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, -0x08, 0x06, 0x05, 0xFF, 0xC4, 0x00, 0x4C, 0x10, -0x00, 0x00, 0x01, 0x07, 0x08, 0x05, 0x08, 0x05, -0x0A, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x07, 0x11, -0x05, 0x08, 0x12, 0x13, 0x14, 0x15, 0x38, 0xB4, -0x16, 0x17, 0x21, 0x31, 0x84, 0x18, 0x22, 0x23, -0x24, 0x58, 0xA5, 0xA6, 0xD2, 0x32, 0x51, 0x56, -0x61, 0xD3, 0x28, 0x33, 0x41, 0x48, 0x67, 0x85, -0x86, 0xC3, 0xE4, 0xF0, 0x25, 0x49, 0x55, 0x09, -0x34, 0x35, 0x36, 0x53, 0x68, 0x72, 0x81, 0xA7, -0xE2, 0xFF, 0xC4, 0x00, 0x14, 0x01, 0x01, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, -0xC4, 0x00, 0x14, 0x11, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xDA, 0x00, -0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, -0x00, 0x3F, 0x00, 0xD9, 0x62, 0x10, 0x80, 0x40, -0x65, 0xED, 0x62, 0x75, 0xC8, 0x7D, 0xFF, 0x00, -0x92, 0x30, 0x33, 0x01, 0x97, 0xB5, 0x89, 0xD7, -0x21, 0xF7, 0xFE, 0x48, 0xC0, 0x0C, 0xC2, 0x10, -0x80, 0x40, 0x66, 0x64, 0xB8, 0x62, 0x64, 0x78, -0xDC, 0xEA, 0x70, 0xCC, 0x06, 0x66, 0x4B, 0x86, -0x26, 0x47, 0x8D, 0xCE, 0xA7, 0x00, 0xCC, 0x21, -0x08, 0x04, 0x31, 0x9F, 0xF2, 0xC5, 0xFD, 0xFF, -0x00, 0x5A, 0x1B, 0x30, 0x63, 0x3F, 0xE5, 0x8B, -0xFB, 0xFE, 0xB4, 0x03, 0x66, 0x01, 0x99, 0x92, -0xE1, 0x89, 0x91, 0xE3, 0x73, 0xA9, 0xC3, 0x30, -0x19, 0x99, 0x2E, 0x18, 0x99, 0x1E, 0x37, 0x3A, -0x9C, 0x03, 0x30, 0x84, 0x33, 0x33, 0x92, 0x55, -0x7E, 0xCF, 0x29, 0xD8, 0x49, 0x0D, 0xAE, 0xBD, -0xAE, 0xAB, 0xC6, 0xBB, 0xAA, 0x68, 0x92, 0x92, -0x6A, 0xBA, 0xB4, 0xE9, 0x11, 0x7A, 0x7C, 0xD8, -0xC6, 0x84, 0x77, 0x12, 0x11, 0x87, 0xBC, 0x07, -0x67, 0xAC, 0x47, 0xED, 0xD9, 0xD3, 0xC6, 0xAA, -0x5E, 0x51, 0x6B, 0x11, 0xFB, 0x76, 0x74, 0xF1, -0xAA, 0x97, 0x94, 0x33, 0x08, 0x00, 0xCE, 0xB1, -0x1F, 0xB7, 0x67, 0x4F, 0x1A, 0xA9, 0x79, 0x41, -0x9B, 0xC4, 0x6C, 0xDE, 0xC2, 0xCB, 0xF6, 0x75, -0x92, 0x84, 0xA0, 0xE5, 0xEC, 0x12, 0xB2, 0x9D, -0xEF, 0x76, 0xC9, 0xBA, 0x50, 0xAA, 0x92, 0xF1, -0xA6, 0xAA, 0x69, 0x12, 0xF4, 0xA4, 0x36, 0x8A, -0x2A, 0xB3, 0x60, 0x77, 0x3A, 0x34, 0xA3, 0x02, -0x6D, 0x1A, 0xC8, 0x0C, 0xBD, 0xAC, 0x4E, 0xB9, -0x0F, 0xBF, 0xF2, 0x46, 0x00, 0xB5, 0x88, 0xFD, -0xBB, 0x3A, 0x78, 0xD5, 0x4B, 0xCA, 0x2D, 0x62, -0x3F, 0x6E, 0xCE, 0x9E, 0x35, 0x52, 0xF2, 0x86, -0x61, 0x00, 0x19, 0xD6, 0x23, 0xF6, 0xEC, 0xE9, -0xE3, 0x55, 0x2F, 0x28, 0x33, 0x9A, 0xE3, 0x66, -0xF6, 0x24, 0x97, 0x12, 0xCE, 0xC9, 0xEC, 0xCB, -0x97, 0xD2, 0x49, 0x25, 0x15, 0xAA, 0xCF, 0x29, -0x69, 0x42, 0xAA, 0xA5, 0x7C, 0x56, 0x92, 0x94, -0xEE, 0x88, 0xF3, 0x4A, 0x71, 0xB4, 0x4E, 0x29, -0xC6, 0xED, 0xDF, 0x46, 0x3B, 0x8A, 0x35, 0x90, -0x19, 0x99, 0x2E, 0x18, 0x99, 0x1E, 0x37, 0x3A, -0x9C, 0x01, 0x9B, 0xE4, 0x79, 0x73, 0x93, 0x59, -0x69, 0xD9, 0x36, 0x65, 0x99, 0x62, 0x34, 0x1E, -0x56, 0x95, 0xAD, 0x96, 0x75, 0x7B, 0xD6, 0x4F, -0x94, 0x6F, 0x1A, 0xA3, 0x0C, 0x3C, 0xEE, 0x71, -0xE6, 0x51, 0x45, 0x56, 0x6D, 0x22, 0xED, 0x29, -0x29, 0x53, 0xFA, 0x4A, 0x41, 0xE2, 0xFC, 0xBB, -0x3F, 0x77, 0x28, 0x66, 0x7B, 0x58, 0x9D, 0x72, -0x1F, 0x7F, 0xE4, 0x8C, 0x0C, 0xC0, 0x31, 0x9F, -0xCB, 0xB3, 0xF7, 0x72, 0x8F, 0x19, 0xB6, 0x76, -0x8F, 0x61, 0x8B, 0x99, 0xDA, 0xDA, 0x16, 0x99, -0xB7, 0xB0, 0x49, 0x2A, 0x74, 0x2D, 0x0C, 0x9D, -0xD4, 0xAA, 0x92, 0x85, 0x39, 0x40, 0xD2, 0x9B, -0xD7, 0x0C, 0x3C, 0xA7, 0x16, 0x27, 0x1C, 0x6A, -0x5D, 0x91, 0xDF, 0x43, 0x70, 0xDC, 0xA2, 0x01, -0x8C, 0xF5, 0xC1, 0xFE, 0xF1, 0x3F, 0xF3, 0x4F, -0xFE, 0x07, 0xB5, 0x35, 0xC6, 0x31, 0xEC, 0x4A, -0xCE, 0x25, 0x9D, 0x94, 0x19, 0x97, 0xD1, 0xA3, -0x72, 0x4A, 0x5B, 0x55, 0x9E, 0x4D, 0xD1, 0x75, -0x55, 0xBA, 0x88, 0x2D, 0x25, 0x21, 0xDD, 0x29, -0xE7, 0x10, 0xE3, 0xA9, 0x1C, 0x43, 0x8E, 0xDB, -0xBA, 0x94, 0x37, 0x10, 0x6B, 0x21, 0x00, 0x19, -0xD5, 0xDB, 0xF6, 0xED, 0x17, 0xE0, 0xA5, 0x2F, -0x30, 0x33, 0x9A, 0xE3, 0x18, 0xF6, 0x25, 0x67, -0x12, 0xCE, 0xCA, 0x0C, 0xCB, 0xE8, 0xD1, 0xB9, -0x25, 0x2D, 0xAA, 0xCF, 0x26, 0xE8, 0xBA, 0xAA, -0xDD, 0x44, 0x16, 0x92, 0x90, 0xEE, 0x94, 0xF3, -0x88, 0x71, 0xD4, 0x8E, 0x21, 0xC7, 0x6D, 0xDD, -0x4A, 0x1B, 0x88, 0x35, 0x90, 0x19, 0x99, 0x2E, -0x18, 0x99, 0x1E, 0x37, 0x3A, 0x9C, 0x03, 0x30, -0x80, 0x04, 0xDB, 0x99, 0x69, 0x09, 0x8B, 0x7E, -0xCF, 0x8D, 0x99, 0x66, 0x54, 0x6C, 0x12, 0x4A, -0x9D, 0xC7, 0x67, 0x57, 0xAD, 0x3D, 0x25, 0x0A, -0x6A, 0xA9, 0x4F, 0x3B, 0x9C, 0x79, 0x4A, 0x71, -0x62, 0x71, 0xC7, 0x17, 0x69, 0x4B, 0xBF, 0xD4, -0x1F, 0xC0, 0x43, 0x8C, 0x79, 0xAE, 0xB5, 0x84, -0x79, 0x57, 0x7E, 0x9A, 0xC8, 0x57, 0xAD, 0xDD, -0x5B, 0x64, 0xEB, 0x69, 0xD0, 0xD5, 0xD6, 0x50, -0xA7, 0xF3, 0x47, 0x9B, 0x18, 0xD0, 0x33, 0x7C, -0x61, 0x0D, 0x9F, 0x48, 0xEC, 0xC0, 0x03, 0x12, -0xFB, 0x5E, 0xC3, 0x68, 0xCC, 0x2A, 0x34, 0xCC, -0xCB, 0x83, 0xB7, 0xC9, 0x2B, 0x94, 0xEC, 0xEB, -0x1A, 0x5E, 0xAA, 0x8E, 0x9D, 0x03, 0xCE, 0x30, -0xEE, 0x69, 0xE8, 0xC8, 0x71, 0x20, 0x71, 0xA7, -0x13, 0x69, 0x09, 0xBB, 0xD4, 0x03, 0xD9, 0xE4, -0xB8, 0xE2, 0x7D, 0x86, 0xEF, 0x65, 0xDF, 0x8C, -0x2E, 0x4B, 0x8E, 0x27, 0xD8, 0x6E, 0xF6, 0x5D, -0xF8, 0xC2, 0xD6, 0x23, 0xF6, 0xEC, 0xE9, 0xE3, -0x55, 0x2F, 0x28, 0xB5, 0x88, 0xFD, 0xBB, 0x3A, -0x78, 0xD5, 0x4B, 0xCA, 0x02, 0xE4, 0xB8, 0xE2, -0x7D, 0x86, 0xEF, 0x65, 0xDF, 0x8C, 0x0C, 0xE6, -0xB8, 0xE1, 0x1D, 0x3B, 0x68, 0xE2, 0x59, 0xD6, -0x99, 0xA6, 0x65, 0x2D, 0xF2, 0xB2, 0xE5, 0xAA, -0xD0, 0xB1, 0x78, 0x2D, 0x23, 0xA7, 0x41, 0x69, -0x29, 0x86, 0xF3, 0x4C, 0x48, 0x43, 0x49, 0x03, -0x4D, 0x34, 0x9B, 0x08, 0x4D, 0xDE, 0xB0, 0x99, -0xAC, 0x47, 0xED, 0xD9, 0xD3, 0xC6, 0xAA, 0x5E, -0x50, 0x67, 0x35, 0xC6, 0xCD, 0xEC, 0x49, 0x2E, -0x25, 0x9D, 0x93, 0xD9, 0x97, 0x2F, 0xA4, 0x92, -0x4A, 0x2B, 0x55, 0x9E, 0x52, 0xD2, 0x85, 0x55, -0x4A, 0xF8, 0xAD, 0x25, 0x29, 0xDD, 0x11, 0xE6, -0x94, 0xE3, 0x68, 0x9C, 0x53, 0x8D, 0xDB, 0xBE, -0x8C, 0x77, 0x14, 0x04, 0xF1, 0x1C, 0x23, 0xA7, -0x92, 0x5F, 0xB3, 0xAC, 0x66, 0x64, 0xF6, 0x52, -0xA6, 0x49, 0x97, 0xAF, 0x7B, 0xC9, 0x5E, 0xF0, -0x5A, 0x3A, 0xBE, 0xA1, 0x54, 0xD3, 0xD1, 0x73, -0x8A, 0x90, 0xA7, 0x1B, 0x44, 0xE2, 0x94, 0xBC, -0xD2, 0x92, 0x3F, 0x4C, 0x48, 0x13, 0x39, 0x2E, -0x38, 0x9F, 0x61, 0xBB, 0xD9, 0x77, 0xE3, 0x01, -0x97, 0xF4, 0xF7, 0x1B, 0xB6, 0x51, 0xE7, 0xBB, -0x76, 0xD5, 0xB5, 0x74, 0xB7, 0x15, 0xCD, 0x7A, -0x59, 0x15, 0x34, 0x89, 0x02, 0xCD, 0xBA, 0xB9, -0x02, 0x34, 0x47, 0xF3, 0xD1, 0x18, 0x5A, 0xBA, -0x14, 0x8C, 0x2E, 0xD2, 0x16, 0x95, 0x28, 0x12, -0x10, 0x29, 0x46, 0xCC, 0x00, 0x33, 0xC9, 0x71, -0xC4, 0xFB, 0x0D, 0xDE, 0xCB, 0xBF, 0x18, 0x5C, -0x97, 0x1C, 0x4F, 0xB0, 0xDD, 0xEC, 0xBB, 0xF1, -0x83, 0x30, 0x80, 0x0C, 0xF2, 0x5C, 0x71, 0x3E, -0xC3, 0x77, 0xB2, 0xEF, 0xC6, 0x17, 0x25, 0xC7, -0x13, 0xEC, 0x37, 0x7B, 0x2E, 0xFC, 0x60, 0xCC, -0x20, 0x03, 0x3C, 0x97, 0x1C, 0x4F, 0xB0, 0xDD, -0xEC, 0xBB, 0xF1, 0x81, 0x9C, 0xD7, 0x1C, 0x23, -0xA7, 0x6D, 0x1C, 0x4B, 0x3A, 0xD3, 0x34, 0xCC, -0xA5, 0xBE, 0x56, 0x5C, 0xB5, 0x5A, 0x16, 0x2F, -0x05, 0xA4, 0x74, 0xE8, 0x2D, 0x25, 0x30, 0xDE, -0x69, 0x89, 0x08, 0x69, 0x20, 0x69, 0xA6, 0x93, -0x61, 0x09, 0xBB, 0xD6, 0x35, 0x90, 0x19, 0x99, -0x2E, 0x18, 0x99, 0x1E, 0x37, 0x3A, 0x9C, 0x07, -0x8D, 0x36, 0xE6, 0xA6, 0x42, 0x6D, 0x1F, 0xB3, -0xE3, 0x69, 0x99, 0x95, 0xEB, 0x7C, 0x92, 0xB9, -0x71, 0xD9, 0xD6, 0x2A, 0x8F, 0x47, 0x4E, 0x82, -0xAA, 0x53, 0x0E, 0xE6, 0x9E, 0x42, 0x1C, 0x48, -0x1C, 0x69, 0xC4, 0xDA, 0x42, 0x6E, 0xF5, 0x07, -0xF1, 0x08, 0x00, 0xCB, 0x40, 0xF7, 0x1B, 0xBD, -0x67, 0xB4, 0xEC, 0x53, 0x14, 0xE9, 0x74, 0xAB, -0x47, 0x2C, 0x96, 0xB5, 0xBD, 0x22, 0x40, 0xA5, -0xFD, 0xE1, 0x01, 0x12, 0x99, 0xCC, 0x4A, 0x67, -0xFC, 0xC9, 0xB0, 0xA5, 0xF4, 0x62, 0x58, 0x44, -0x84, 0x06, 0x73, 0x5C, 0x6C, 0xDE, 0xC4, 0x92, -0xE2, 0x59, 0xD9, 0x3D, 0x99, 0x72, 0xFA, 0x49, -0x24, 0xA2, 0xB5, 0x59, 0xE5, 0x2D, 0x28, 0x55, -0x54, 0xAF, 0x8A, 0xD2, 0x52, 0x9D, 0xD1, 0x1E, -0x69, 0x4E, 0x36, 0x89, 0xC5, 0x38, 0xDD, 0xBB, -0xE8, 0xC7, 0x71, 0x42, 0x63, 0xA5, 0xC4, 0xEB, -0xEF, 0xFB, 0x83, 0x24, 0x78, 0xA6, 0x4B, 0x86, -0x26, 0x47, 0x8D, 0xCE, 0xA7, 0x01, 0x6B, 0x11, -0xFB, 0x76, 0x74, 0xF1, 0xAA, 0x97, 0x94, 0x5A, -0xC4, 0x7E, 0xDD, 0x9D, 0x3C, 0x6A, 0xA5, 0xE5, -0x0C, 0xC2, 0x00, 0x33, 0xAC, 0x47, 0xED, 0xD9, -0xD3, 0xC6, 0xAA, 0x5E, 0x50, 0x67, 0x35, 0xC6, -0xCD, 0xEC, 0x49, 0x2E, 0x25, 0x9D, 0x93, 0xD9, -0x97, 0x2F, 0xA4, 0x92, 0x4A, 0x2B, 0x55, 0x9E, -0x52, 0xD2, 0x85, 0x55, 0x4A, 0xF8, 0xAD, 0x25, -0x29, 0xDD, 0x11, 0xE6, 0x94, 0xE3, 0x68, 0x9C, -0x53, 0x8D, 0xDB, 0xBE, 0x8C, 0x77, 0x14, 0x6B, -0x20, 0x33, 0x32, 0x5C, 0x31, 0x32, 0x3C, 0x6E, -0x75, 0x38, 0x0C, 0xCD, 0x3E, 0x76, 0x89, 0xBB, -0x97, 0xF4, 0x3B, 0x4D, 0x5D, 0xD6, 0x86, 0xD4, -0x5B, 0xAC, 0x9F, 0xC6, 0x90, 0x2F, 0xDA, 0xA9, -0x59, 0xE9, 0xFC, 0xD1, 0x09, 0x42, 0x8C, 0x0C, -0xDF, 0xBE, 0x9E, 0xCD, 0xC5, 0x1A, 0x67, 0x58, -0x8F, 0xDB, 0xB3, 0xA7, 0x8D, 0x54, 0xBC, 0xA3, -0x8C, 0xFE, 0xD0, 0x76, 0x16, 0xFF, 0x00, 0x76, -0x0A, 0xAD, 0xAD, 0xE9, 0x66, 0xD1, 0x5A, 0x7D, -0x52, 0xCF, 0x4E, 0xD5, 0x6A, 0x4E, 0xAC, 0x8B, -0xD3, 0xA4, 0x4A, 0x14, 0x61, 0x1D, 0xC7, 0x47, -0x76, 0xCD, 0xE2, 0x7D, 0xAA, 0xAF, 0xD9, 0xDA, -0xBB, 0x09, 0x5D, 0xB5, 0xD7, 0xB5, 0xEB, 0x77, -0x54, 0xF5, 0x4D, 0x12, 0x52, 0x43, 0x59, 0x58, -0x9D, 0x1A, 0x2F, 0x4F, 0x9D, 0x08, 0x53, 0x8E, -0xE2, 0xC6, 0x10, 0xF7, 0x80, 0xEC, 0xF5, 0x88, -0xFD, 0xBB, 0x3A, 0x78, 0xD5, 0x4B, 0xCA, 0x2D, -0x62, 0x3F, 0x6E, 0xCE, 0x9E, 0x35, 0x52, 0xF2, -0x8C, 0x67, 0xCA, 0x8D, 0xFB, 0x7B, 0x73, 0xDD, -0x2A, 0x5F, 0x04, 0x5C, 0xA8, 0xDF, 0xB7, 0xB7, -0x3D, 0xD2, 0xA5, 0xF0, 0x40, 0x6C, 0xCD, 0x62, -0x3F, 0x6E, 0xCE, 0x9E, 0x35, 0x52, 0xF2, 0x8B, -0x58, 0x8F, 0xDB, 0xB3, 0xA7, 0x8D, 0x54, 0xBC, -0xA3, 0x33, 0x3B, 0x27, 0xA5, 0x3B, 0x17, 0x95, -0x78, 0x68, 0x54, 0xBB, 0x7A, 0xDD, 0xD5, 0x56, -0xBE, 0xA9, 0x25, 0xA1, 0xAB, 0xAC, 0xA7, 0x43, -0xE7, 0x4C, 0x36, 0x31, 0xA0, 0x7E, 0xE8, 0xC2, -0x1B, 0x7E, 0x81, 0xD9, 0xFC, 0xBB, 0x3F, 0x77, -0x28, 0x06, 0x6D, 0x62, 0x3F, 0x6E, 0xCE, 0x9E, -0x35, 0x52, 0xF2, 0x83, 0x39, 0xAE, 0x36, 0x6F, -0x62, 0x49, 0x71, 0x2C, 0xEC, 0x9E, 0xCC, 0xB9, -0x7D, 0x24, 0x92, 0x51, 0x5A, 0xAC, 0xF2, 0x96, -0x94, 0x2A, 0xAA, 0x57, 0xC5, 0x69, 0x29, 0x4E, -0xE8, 0x8F, 0x34, 0xA7, 0x1B, 0x44, 0xE2, 0x9C, -0x6E, 0xDD, 0xF4, 0x63, 0xB8, 0xA3, 0xC5, 0xF9, -0x76, 0x7E, 0xEE, 0x51, 0xC6, 0x39, 0x2E, 0x56, -0x3A, 0xB0, 0x92, 0x35, 0x69, 0xFE, 0x53, 0xE9, -0xAC, 0x1F, 0xE1, 0x7F, 0xEB, 0xA4, 0xAC, 0xF9, -0xFE, 0x93, 0xE7, 0x2B, 0x3D, 0x2F, 0xFA, 0xD9, -0x00, 0x1B, 0xFC, 0x42, 0x10, 0x0C, 0x9A, 0xD4, -0xBE, 0x39, 0x09, 0xCF, 0xBF, 0x67, 0xD5, 0x28, -0x4A, 0x08, 0x6D, 0xF2, 0xB2, 0xE5, 0xC3, 0x76, -0xC9, 0xB4, 0x8F, 0x47, 0x6B, 0xA0, 0xAA, 0x42, -0x25, 0xE9, 0x48, 0x8C, 0xF3, 0x4C, 0xA0, 0x6A, -0x42, 0x1D, 0xCE, 0x84, 0x61, 0x02, 0x6D, 0xDC, -0x64, 0xE4, 0xA7, 0x5B, 0xAB, 0x57, 0x61, 0x24, -0x31, 0x5A, 0x05, 0x7A, 0xDD, 0xD5, 0xDD, 0x6E, -0xF7, 0xA9, 0xAC, 0xAC, 0x4E, 0x91, 0x2F, 0xA1, -0x52, 0x74, 0x21, 0x4E, 0x1B, 0xCB, 0x18, 0x47, -0xDC, 0x34, 0xCC, 0xF6, 0xB0, 0xC4, 0xD7, 0x70, -0x59, 0xD4, 0x02, 0x99, 0x2E, 0x18, 0x99, 0x1E, -0x37, 0x3A, 0x9C, 0x00, 0xCF, 0x2E, 0x7F, 0xB2, -0xEE, 0xFF, 0x00, 0xFD, 0x38, 0xB9, 0x73, 0xFD, -0x97, 0x77, 0xFF, 0x00, 0xE9, 0xC6, 0xCC, 0x10, -0x0C, 0x67, 0xCB, 0x9F, 0xEC, 0xBB, 0xBF, 0xFF, -0x00, 0x4E, 0x38, 0xC7, 0x25, 0x3A, 0xDD, 0x5A, -0xBB, 0x09, 0x21, 0x8A, 0xD0, 0x2B, 0xD6, 0xEE, -0xAE, 0xEB, 0x77, 0xBD, 0x4D, 0x65, 0x62, 0x74, -0x89, 0x7D, 0x0A, 0x93, 0xA1, 0x0A, 0x70, 0xDE, -0x58, 0xC2, 0x3E, 0xE1, 0xBF, 0xC0, 0xCC, 0xC9, -0x70, 0xC4, 0xC8, 0xF1, 0xB9, 0xD4, 0xE0, 0x33, -0x33, 0xED, 0x9D, 0x6E, 0xB2, 0x9D, 0x84, 0xAE, -0xC5, 0x68, 0x15, 0xD5, 0x78, 0xD4, 0xF5, 0xBB, -0xDE, 0xBA, 0xAE, 0xAD, 0x3A, 0x34, 0xBE, 0x85, -0x49, 0xB1, 0x8D, 0x08, 0x6F, 0x24, 0x23, 0x1F, -0x70, 0x9F, 0x6C, 0xEB, 0x75, 0x94, 0xEC, 0x25, -0x76, 0x2B, 0x40, 0xAE, 0xAB, 0xC6, 0xA7, 0xAD, -0xDE, 0xF5, 0xD5, 0x75, 0x69, 0xD1, 0xA5, 0xF4, -0x2A, 0x4D, 0x8C, 0x68, 0x43, 0x79, 0x21, 0x18, -0xFB, 0x86, 0x99, 0x9E, 0xD6, 0x18, 0x9A, 0xEE, -0x0B, 0x3A, 0x80, 0x53, 0xDA, 0xC3, 0x13, 0x5D, -0xC1, 0x67, 0x50, 0x00, 0xCC, 0xCE, 0x4A, 0x75, -0xBA, 0xB5, 0x76, 0x12, 0x43, 0x15, 0xA0, 0x57, -0xAD, 0xDD, 0x5D, 0xD6, 0xEF, 0x7A, 0x9A, 0xCA, -0xC4, 0xE9, 0x12, 0xFA, 0x15, 0x27, 0x42, 0x14, -0xE1, 0xBC, 0xB1, 0x84, 0x7D, 0xC3, 0xB3, 0xE5, -0xCF, 0xF6, 0x5D, 0xDF, 0xFF, 0x00, 0xA7, 0x0C, -0xD3, 0x25, 0xC3, 0x13, 0x23, 0xC6, 0xE7, 0x53, -0x86, 0x60, 0x18, 0x01, 0x92, 0x9D, 0x6D, 0xC0, -0xF3, 0xDB, 0x76, 0xD7, 0x40, 0xAD, 0x3A, 0x55, -0x60, 0xEA, 0x97, 0xBD, 0x0B, 0x2D, 0x95, 0x01, -0x51, 0x7A, 0x75, 0x25, 0xA7, 0x4A, 0x31, 0xDC, -0x6C, 0x37, 0x6D, 0xDE, 0x3B, 0x3E, 0x5C, 0xFF, -0x00, 0x65, 0xDD, 0xFF, 0x00, 0xFA, 0x70, 0xCC, -0xE9, 0x71, 0x3A, 0xFB, 0xFE, 0xE0, 0xC9, 0x1E, -0x19, 0x80, 0x63, 0x3E, 0x5C, 0xFF, 0x00, 0x65, -0xDD, 0xFF, 0x00, 0xFA, 0x71, 0xC6, 0x39, 0x29, -0xD6, 0xEA, 0xD5, 0xD8, 0x49, 0x0C, 0x56, 0x81, -0x5E, 0xB7, 0x75, 0x77, 0x5B, 0xBD, 0xEA, 0x6B, -0x2B, 0x13, 0xA4, 0x4B, 0xE8, 0x54, 0x9D, 0x08, -0x53, 0x86, 0xF2, 0xC6, 0x11, 0xF7, 0x0D, 0xFE, -0x06, 0x66, 0x4B, 0x86, 0x26, 0x47, 0x8D, 0xCE, -0xA7, 0x00, 0xCC, 0x21, 0x08, 0x00, 0xCC, 0xF6, -0xB0, 0xC4, 0xD7, 0x70, 0x59, 0xD4, 0x02, 0x99, -0x2E, 0x18, 0x99, 0x1E, 0x37, 0x3A, 0x9C, 0x53, -0xDA, 0xC3, 0x13, 0x5D, 0xC1, 0x67, 0x50, 0x0A, -0x64, 0xB8, 0x62, 0x64, 0x78, 0xDC, 0xEA, 0x70, -0x0C, 0xC2, 0x10, 0x80, 0x40, 0x66, 0x64, 0xB8, -0x62, 0x64, 0x78, 0xDC, 0xEA, 0x70, 0xCC, 0x06, -0x66, 0x4B, 0x86, 0x26, 0x47, 0x8D, 0xCE, 0xA7, -0x01, 0x4F, 0x6B, 0x0C, 0x4D, 0x77, 0x05, 0x9D, -0x40, 0x29, 0xED, 0x61, 0x89, 0xAE, 0xE0, 0xB3, -0xA8, 0x05, 0x3D, 0xAC, 0x31, 0x35, 0xDC, 0x16, -0x75, 0x00, 0xA7, 0xB5, 0x86, 0x26, 0xBB, 0x82, -0xCE, 0xA0, 0x01, 0x4C, 0x97, 0x0C, 0x4C, 0x8F, -0x1B, 0x9D, 0x4E, 0x19, 0x86, 0x4D, 0x9A, 0xE3, -0xFB, 0x74, 0xEC, 0x5B, 0x89, 0x67, 0x59, 0x96, -0x99, 0xAB, 0xB0, 0x4A, 0xCA, 0x76, 0xAB, 0x42, -0xBD, 0xDE, 0xB4, 0x92, 0x85, 0x35, 0xA4, 0xA7, -0x9B, 0xCE, 0x31, 0x19, 0x4D, 0x2C, 0x4D, 0x38, -0xD2, 0xEC, 0x29, 0x77, 0xFA, 0xC2, 0x67, 0x2A, -0x37, 0x13, 0xED, 0xCF, 0x74, 0xAE, 0xFC, 0x10, -0x03, 0x33, 0x80, 0xFA, 0xCE, 0xFE, 0x13, 0xFC, -0xB0, 0xCD, 0x32, 0x5C, 0x31, 0x32, 0x3C, 0x6E, -0x75, 0x38, 0x00, 0x79, 0x2D, 0x4C, 0x84, 0xDA, -0x33, 0x13, 0x91, 0x69, 0x99, 0x95, 0xEB, 0x7C, -0x92, 0xB9, 0xA2, 0xF6, 0x75, 0x8A, 0xA3, 0xD1, -0xD3, 0xA0, 0x79, 0xA6, 0x1D, 0xCD, 0x3C, 0x84, -0x38, 0x90, 0x38, 0xD3, 0x89, 0xB4, 0x84, 0xDD, -0xEA, 0x0F, 0xF3, 0x25, 0xC3, 0x13, 0x23, 0xC6, -0xE7, 0x53, 0x80, 0x66, 0x03, 0x33, 0x25, 0xC3, -0x13, 0x23, 0xC6, 0xE7, 0x53, 0x86, 0x60, 0x33, -0x32, 0x5C, 0x31, 0x32, 0x3C, 0x6E, 0x75, 0x38, -0x06, 0x61, 0x08, 0x40, 0x06, 0x67, 0xB5, 0x86, -0x26, 0xBB, 0x82, 0xCE, 0xA0, 0x14, 0xC9, 0x70, -0xC4, 0xC8, 0xF1, 0xB9, 0xD4, 0xE2, 0x9E, 0xD6, -0x18, 0x9A, 0xEE, 0x0B, 0x3A, 0x80, 0x53, 0x25, -0xC3, 0x13, 0x23, 0xC6, 0xE7, 0x53, 0x80, 0x66, -0x10, 0x84, 0x02, 0x03, 0x33, 0x25, 0xC3, 0x13, -0x23, 0xC6, 0xE7, 0x53, 0x86, 0x60, 0x33, 0x32, -0x5C, 0x31, 0x32, 0x3C, 0x6E, 0x75, 0x38, 0x0A, -0x7B, 0x58, 0x62, 0x6B, 0xB8, 0x2C, 0xEA, 0x01, -0x4F, 0x6B, 0x0C, 0x4D, 0x77, 0x05, 0x9D, 0x40, -0x29, 0xED, 0x61, 0x89, 0xAE, 0xE0, 0xB3, 0xA8, -0x05, 0x3D, 0xAC, 0x31, 0x35, 0xDC, 0x16, 0x75, -0x00, 0x06, 0x61, 0x08, 0x40, 0x31, 0x9C, 0xEB, -0x65, 0x86, 0xEE, 0x5F, 0xD7, 0x2C, 0x93, 0xA6, -0x36, 0x66, 0x4D, 0x95, 0xB8, 0xFF, 0x00, 0x82, -0xDD, 0x88, 0x0F, 0xB5, 0x5A, 0xAA, 0x4E, 0xF9, -0xF8, 0x11, 0x21, 0x94, 0x52, 0x12, 0x9E, 0xF3, -0xA3, 0xBB, 0x61, 0x07, 0xB5, 0x35, 0xC6, 0x31, -0xEC, 0x4A, 0xCE, 0x25, 0x9D, 0x94, 0x19, 0x97, -0xD1, 0xA3, 0x72, 0x4A, 0x5B, 0x55, 0x9E, 0x4D, -0xD1, 0x75, 0x55, 0xBA, 0x88, 0x2D, 0x25, 0x21, -0xDD, 0x29, 0xE7, 0x10, 0xE3, 0xA9, 0x1C, 0x43, -0x8E, 0xDB, 0xBA, 0x94, 0x37, 0x10, 0x78, 0xB3, -0x80, 0xFA, 0xCE, 0xFE, 0x13, 0xFC, 0xB0, 0xCD, -0x32, 0x5C, 0x31, 0x32, 0x3C, 0x6E, 0x75, 0x38, -0x0B, 0x57, 0x6F, 0xDB, 0xB4, 0x5F, 0x82, 0x94, -0xBC, 0xC0, 0xCE, 0x6B, 0x8C, 0x63, 0xD8, 0x95, -0x9C, 0x4B, 0x3B, 0x28, 0x33, 0x2F, 0xA3, 0x46, -0xE4, 0x94, 0xB6, 0xAB, 0x3C, 0x9B, 0xA2, 0xEA, -0xAB, 0x75, 0x10, 0x5A, 0x4A, 0x43, 0xBA, 0x53, -0xCE, 0x21, 0xC7, 0x52, 0x38, 0x87, 0x1D, 0xB7, -0x75, 0x28, 0x6E, 0x20, 0xD6, 0x40, 0x66, 0x64, -0xB8, 0x62, 0x64, 0x78, 0xDC, 0xEA, 0x70, 0x16, -0xB1, 0x1F, 0xB7, 0x67, 0x4F, 0x1A, 0xA9, 0x79, -0x45, 0xAC, 0x47, 0xED, 0xD9, 0xD3, 0xC6, 0xAA, -0x5E, 0x50, 0xCC, 0x20, 0x19, 0x36, 0x74, 0x6D, -0x9B, 0xD8, 0x95, 0x9C, 0x4B, 0x45, 0x27, 0xB4, -0xCE, 0x5F, 0x46, 0xE4, 0x94, 0xB6, 0x5B, 0x44, -0xA5, 0xA5, 0x0A, 0xAB, 0x75, 0x10, 0x5A, 0x44, -0x53, 0x7A, 0x23, 0x0D, 0x21, 0xC7, 0x52, 0x38, -0x86, 0x9B, 0xB3, 0x75, 0x28, 0xEE, 0x20, 0xA6, -0xB8, 0xD9, 0xBD, 0x89, 0x25, 0xC4, 0xB3, 0xB2, -0x7B, 0x32, 0xE5, 0xF4, 0x92, 0x49, 0x45, 0x6A, -0xB3, 0xCA, 0x5A, 0x50, 0xAA, 0xA9, 0x5F, 0x15, -0xA4, 0xA5, 0x3B, 0xA2, 0x3C, 0xD2, 0x9C, 0x6D, -0x13, 0x8A, 0x71, 0xBB, 0x77, 0xD1, 0x8E, 0xE2, -0x84, 0xC9, 0xED, 0x61, 0x89, 0xAE, 0xE0, 0xB3, -0xA8, 0x05, 0x32, 0x5C, 0x31, 0x32, 0x3C, 0x6E, -0x75, 0x38, 0x0B, 0x58, 0x8F, 0xDB, 0xB3, 0xA7, -0x8D, 0x54, 0xBC, 0xA2, 0xD6, 0x23, 0xF6, 0xEC, -0xE9, 0xE3, 0x55, 0x2F, 0x28, 0x66, 0x10, 0x01, -0x9D, 0x62, 0x3F, 0x6E, 0xCE, 0x9E, 0x35, 0x52, -0xF2, 0x8F, 0x6A, 0x6B, 0x8C, 0xB4, 0xBA, 0xC5, -0xB8, 0x96, 0x75, 0x99, 0x69, 0x94, 0x6C, 0x12, -0xB2, 0x9D, 0xAA, 0xD0, 0xAF, 0x5A, 0x62, 0x4A, -0x14, 0xD6, 0x92, 0x9E, 0x6F, 0x38, 0xC2, 0x94, -0xD2, 0xC4, 0xD3, 0x8D, 0x2E, 0xC2, 0x97, 0x7F, -0xAC, 0x26, 0x08, 0x00, 0xCC, 0xF6, 0xB0, 0xC4, -0xD7, 0x70, 0x59, 0xD4, 0x02, 0x9E, 0xD6, 0x18, -0x9A, 0xEE, 0x0B, 0x3A, 0x80, 0x53, 0xDA, 0xC3, -0x13, 0x5D, 0xC1, 0x67, 0x50, 0x0A, 0x7B, 0x58, -0x62, 0x6B, 0xB8, 0x2C, 0xEA, 0x00, 0x0C, 0xC2, -0x10, 0x80, 0x63, 0x39, 0xC0, 0x7D, 0x67, 0x7F, -0x09, 0xFE, 0x58, 0x66, 0x99, 0x2E, 0x18, 0x99, -0x1E, 0x37, 0x3A, 0x9C, 0x0C, 0xCE, 0x03, 0xEB, -0x3B, 0xF8, 0x4F, 0xF2, 0xC3, 0x34, 0xC9, 0x70, -0xC4, 0xC8, 0xF1, 0xB9, 0xD4, 0xE0, 0x19, 0x80, -0xCC, 0xC9, 0x70, 0xC4, 0xC8, 0xF1, 0xB9, 0xD4, -0xE1, 0x98, 0x0C, 0xCC, 0x97, 0x0C, 0x4C, 0x8F, -0x1B, 0x9D, 0x4E, 0x03, 0xFF, 0xD9}; -static const int goodJpegImageWidth = 128; -static const int goodJpegImageHeight = 128; - -// https://code.google.com/p/android/issues/detail?id=42382 -// https://code.google.com/p/android/issues/detail?id=9064 -// https://code.google.com/p/skia/issues/detail?id=1649 - -/** - This test will test the ability of the SkImageDecoder to deal with - Jpeg files which have been mangled somehow. We want to display as - much of the jpeg as possible. -*/ -DEF_TEST(Jpeg, reporter) { - size_t len = sizeof(goodJpegImage) / 2; - // I am explicitly not putting the entire image into the - // DecodeMemory. This simulates a network error. - - SkBitmap bm8888; - bool imageDecodeSuccess = SkImageDecoder::DecodeMemory( - static_cast<void *>(goodJpegImage), len, &bm8888); - REPORTER_ASSERT(reporter, imageDecodeSuccess); - REPORTER_ASSERT(reporter, bm8888.width() == goodJpegImageWidth); - REPORTER_ASSERT(reporter, bm8888.height() == goodJpegImageHeight); - REPORTER_ASSERT(reporter, !(bm8888.empty())); - - // Pick a few pixels and verify that their colors match the colors - // we expect (given the original image). - REPORTER_ASSERT(reporter, bm8888.getColor(7, 9) == 0xffffffff); - REPORTER_ASSERT(reporter, bm8888.getColor(28, 3) == 0xff000000); - REPORTER_ASSERT(reporter, bm8888.getColor(27, 34) == 0xffffffff); - REPORTER_ASSERT(reporter, bm8888.getColor(71, 18) == 0xff000000); - -#ifdef SK_BUILD_FOR_IOS // the iOS jpeg decoder fills to gray - REPORTER_ASSERT(reporter, bm8888.getColor(127, 127) == 0xff808080 - || bm8888.getColor(127, 127) == SK_ColorWHITE); -#else - // This is the fill color - REPORTER_ASSERT(reporter, bm8888.getColor(127, 127) == SK_ColorWHITE); -#endif - - #if JPEG_TEST_WRITE_TO_FILE_FOR_DEBUGGING - // Check to see that the resulting bitmap is nice - bool writeSuccess = (!(bm8888.empty())) && SkImageEncoder::EncodeFile( - "HalfOfAJpeg.png", bm8888, SkImageEncoder::kPNG_Type, 100); - SkASSERT(writeSuccess); - #endif -}
diff --git a/src/third_party/skia/tests/KtxTest.cpp b/src/third_party/skia/tests/KtxTest.cpp deleted file mode 100644 index 1a61f3a..0000000 --- a/src/third_party/skia/tests/KtxTest.cpp +++ /dev/null
@@ -1,168 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Resources.h" -#include "SkBitmap.h" -#include "SkData.h" -#include "SkDecodingImageGenerator.h" -#include "SkForceLinking.h" -#include "SkImageDecoder.h" -#include "SkOSFile.h" -#include "SkRandom.h" -#include "SkStream.h" -#include "Test.h" - -__SK_FORCE_IMAGE_DECODER_LINKING; - -/** - * First, make sure that writing an 8-bit RGBA KTX file and then - * reading it produces the same bitmap. - */ -DEF_TEST(KtxReadWrite, reporter) { - - // Random number generator with explicit seed for reproducibility - SkRandom rand(0x1005cbad); - - SkBitmap bm8888; - bm8888.allocN32Pixels(128, 128); - - uint8_t *pixels = reinterpret_cast<uint8_t*>(bm8888.getPixels()); - REPORTER_ASSERT(reporter, pixels); - - if (NULL == pixels) { - return; - } - - uint8_t *row = pixels; - for (int y = 0; y < bm8888.height(); ++y) { - for (int x = 0; x < bm8888.width(); ++x) { - uint8_t a = rand.nextRangeU(0, 255); - uint8_t r = rand.nextRangeU(0, 255); - uint8_t g = rand.nextRangeU(0, 255); - uint8_t b = rand.nextRangeU(0, 255); - - SkPMColor &pixel = *(reinterpret_cast<SkPMColor*>(row + x*sizeof(SkPMColor))); - pixel = SkPreMultiplyARGB(a, r, g, b); - } - row += bm8888.rowBytes(); - } - REPORTER_ASSERT(reporter, !(bm8888.empty())); - - SkAutoDataUnref encodedData(SkImageEncoder::EncodeData(bm8888, SkImageEncoder::kKTX_Type, 0)); - REPORTER_ASSERT(reporter, encodedData); - - SkAutoTUnref<SkMemoryStream> stream(SkNEW_ARGS(SkMemoryStream, (encodedData))); - REPORTER_ASSERT(reporter, stream); - - SkBitmap decodedBitmap; - bool imageDecodeSuccess = SkImageDecoder::DecodeStream(stream, &decodedBitmap); - REPORTER_ASSERT(reporter, imageDecodeSuccess); - - REPORTER_ASSERT(reporter, decodedBitmap.colorType() == bm8888.colorType()); - REPORTER_ASSERT(reporter, decodedBitmap.alphaType() == bm8888.alphaType()); - REPORTER_ASSERT(reporter, decodedBitmap.width() == bm8888.width()); - REPORTER_ASSERT(reporter, decodedBitmap.height() == bm8888.height()); - REPORTER_ASSERT(reporter, !(decodedBitmap.empty())); - - uint8_t *decodedPixels = reinterpret_cast<uint8_t*>(decodedBitmap.getPixels()); - REPORTER_ASSERT(reporter, decodedPixels); - REPORTER_ASSERT(reporter, decodedBitmap.getSize() == bm8888.getSize()); - - if (NULL == decodedPixels) { - return; - } - - REPORTER_ASSERT(reporter, memcmp(decodedPixels, pixels, decodedBitmap.getSize()) == 0); -} - -/** - * Next test is to see whether or not reading an unpremultiplied KTX file accurately - * creates a premultiplied buffer... - */ -DEF_TEST(KtxReadUnpremul, reporter) { - - static const uint8_t kHalfWhiteKTX[] = { - 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, // First twelve bytes is magic - 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A, // KTX identifier string - 0x01, 0x02, 0x03, 0x04, // Then magic endian specifier - 0x01, 0x14, 0x00, 0x00, // uint32_t fGLType; - 0x01, 0x00, 0x00, 0x00, // uint32_t fGLTypeSize; - 0x08, 0x19, 0x00, 0x00, // uint32_t fGLFormat; - 0x58, 0x80, 0x00, 0x00, // uint32_t fGLInternalFormat; - 0x08, 0x19, 0x00, 0x00, // uint32_t fGLBaseInternalFormat; - 0x02, 0x00, 0x00, 0x00, // uint32_t fPixelWidth; - 0x02, 0x00, 0x00, 0x00, // uint32_t fPixelHeight; - 0x00, 0x00, 0x00, 0x00, // uint32_t fPixelDepth; - 0x00, 0x00, 0x00, 0x00, // uint32_t fNumberOfArrayElements; - 0x01, 0x00, 0x00, 0x00, // uint32_t fNumberOfFaces; - 0x01, 0x00, 0x00, 0x00, // uint32_t fNumberOfMipmapLevels; - 0x00, 0x00, 0x00, 0x00, // uint32_t fBytesOfKeyValueData; - 0x10, 0x00, 0x00, 0x00, // image size: 2x2 image of RGBA = 4 * 4 = 16 bytes - 0xFF, 0xFF, 0xFF, 0x80, // Pixel 1 - 0xFF, 0xFF, 0xFF, 0x80, // Pixel 2 - 0xFF, 0xFF, 0xFF, 0x80, // Pixel 3 - 0xFF, 0xFF, 0xFF, 0x80};// Pixel 4 - - SkAutoTUnref<SkMemoryStream> stream( - SkNEW_ARGS(SkMemoryStream, (kHalfWhiteKTX, sizeof(kHalfWhiteKTX)))); - REPORTER_ASSERT(reporter, stream); - - SkBitmap decodedBitmap; - bool imageDecodeSuccess = SkImageDecoder::DecodeStream(stream, &decodedBitmap); - REPORTER_ASSERT(reporter, imageDecodeSuccess); - - REPORTER_ASSERT(reporter, decodedBitmap.colorType() == kN32_SkColorType); - REPORTER_ASSERT(reporter, decodedBitmap.alphaType() == kPremul_SkAlphaType); - REPORTER_ASSERT(reporter, decodedBitmap.width() == 2); - REPORTER_ASSERT(reporter, decodedBitmap.height() == 2); - REPORTER_ASSERT(reporter, !(decodedBitmap.empty())); - - uint8_t *decodedPixels = reinterpret_cast<uint8_t*>(decodedBitmap.getPixels()); - REPORTER_ASSERT(reporter, decodedPixels); - - uint8_t *row = decodedPixels; - for (int j = 0; j < decodedBitmap.height(); ++j) { - for (int i = 0; i < decodedBitmap.width(); ++i) { - SkPMColor pixel = *(reinterpret_cast<SkPMColor*>(row + i*sizeof(SkPMColor))); - REPORTER_ASSERT(reporter, SkPreMultiplyARGB(0x80, 0xFF, 0xFF, 0xFF) == pixel); - } - row += decodedBitmap.rowBytes(); - } -} - -/** - * Finally, make sure that if we get ETC1 data from a PKM file that we can then - * accurately write it out into a KTX file (i.e. transferring the ETC1 data from - * the PKM to the KTX should produce an identical KTX to the one we have on file) - */ -DEF_TEST(KtxReexportPKM, reporter) { - SkString pkmFilename = GetResourcePath("mandrill_128.pkm"); - - // Load PKM file into a bitmap - SkBitmap etcBitmap; - SkAutoTUnref<SkData> fileData(SkData::NewFromFileName(pkmFilename.c_str())); - REPORTER_ASSERT(reporter, fileData); - if (NULL == fileData) { - return; - } - - bool installDiscardablePixelRefSuccess = - SkInstallDiscardablePixelRef( - SkDecodingImageGenerator::Create( - fileData, SkDecodingImageGenerator::Options()), &etcBitmap); - REPORTER_ASSERT(reporter, installDiscardablePixelRefSuccess); - - // Write the bitmap out to a KTX file. - SkData *ktxDataPtr = SkImageEncoder::EncodeData(etcBitmap, SkImageEncoder::kKTX_Type, 0); - SkAutoDataUnref newKtxData(ktxDataPtr); - REPORTER_ASSERT(reporter, ktxDataPtr); - - // See is this data is identical to data in existing ktx file. - SkString ktxFilename = GetResourcePath("mandrill_128.ktx"); - SkAutoDataUnref oldKtxData(SkData::NewFromFileName(ktxFilename.c_str())); - REPORTER_ASSERT(reporter, oldKtxData->equals(newKtxData)); -}
diff --git a/src/third_party/skia/tests/LListTest.cpp b/src/third_party/skia/tests/LListTest.cpp index 8fb0117..e28cad9 100644 --- a/src/third_party/skia/tests/LListTest.cpp +++ b/src/third_party/skia/tests/LListTest.cpp
@@ -16,15 +16,10 @@ } bool operator== (const ListElement& other) { return fID == other.fID; } -#if SK_ENABLE_INST_COUNT - // Make the instance count available publicly. - static int InstanceCount() { return GetInstanceCount(); } -#endif - int fID; private: - SK_DECLARE_INST_COUNT_ROOT(ListElement); + SK_DECLARE_INTERNAL_LLIST_INTERFACE(ListElement); }; @@ -46,7 +41,7 @@ #endif } -static void TestTInternalLList(skiatest::Reporter* reporter) { +static void test_tinternallist(skiatest::Reporter* reporter) { SkTInternalLList<ListElement> list; ListElement elements[4] = { ListElement(0), @@ -96,12 +91,12 @@ check_list(list, reporter, true, 0, false, false, false, false, elements); // test out methods that add to the middle of the list. - list.addAfter(&elements[1], NULL); + list.addAfter(&elements[1], nullptr); check_list(list, reporter, false, 1, false, true, false, false, elements); list.remove(&elements[1]); - list.addBefore(&elements[1], NULL); + list.addBefore(&elements[1], nullptr); check_list(list, reporter, false, 1, false, true, false, false, elements); list.addBefore(&elements[0], &elements[1]); @@ -117,200 +112,211 @@ for (int i = 0; cur; ++i, cur = iter.next()) { REPORTER_ASSERT(reporter, cur->fID == i); } + while (!list.isEmpty()) { + list.remove(list.tail()); + } + + // test concat. + SkTInternalLList<ListElement> listA, listB; + listA.concat(std::move(listB)); + check_list(listA, reporter, true, 0, false, false, false, false, elements); + check_list(listB, reporter, true, 0, false, false, false, false, elements); + + listB.addToTail(&elements[0]); + listA.concat(std::move(listB)); + check_list(listA, reporter, false, 1, true, false, false, false, elements); + check_list(listB, reporter, true, 0, false, false, false, false, elements); + + listB.addToTail(&elements[1]); + listA.concat(std::move(listB)); + check_list(listA, reporter, false, 2, true, true, false, false, elements); + check_list(listB, reporter, true, 0, false, false, false, false, elements); + + listA.concat(std::move(listB)); + check_list(listA, reporter, false, 2, true, true, false, false, elements); + check_list(listB, reporter, true, 0, false, false, false, false, elements); + + listB.addToTail(&elements[2]); + listB.addToTail(&elements[3]); + listA.concat(std::move(listB)); + check_list(listA, reporter, false, 4, true, true, true, true, elements); + check_list(listB, reporter, true, 0, false, false, false, false, elements); + + cur = iter.init(listA, Iter::kHead_IterStart); + for (int i = 0; cur; ++i, cur = iter.next()) { + REPORTER_ASSERT(reporter, cur->fID == i); + } } -static void TestTLList(skiatest::Reporter* reporter) { - typedef SkTLList<ListElement> ElList; - typedef ElList::Iter Iter; +template <unsigned int N> static void test_tllist(skiatest::Reporter* reporter) { + typedef SkTLList<ListElement, N> ElList; + typedef typename ElList::Iter Iter; SkRandom random; - for (int i = 1; i <= 16; i *= 2) { + ElList list1; + ElList list2; + Iter iter1; + Iter iter2; + Iter iter3; + Iter iter4; - ElList list1(i); - ElList list2(i); - Iter iter1; - Iter iter2; - Iter iter3; - Iter iter4; + REPORTER_ASSERT(reporter, list1.isEmpty()); + REPORTER_ASSERT(reporter, nullptr == iter1.init(list1, Iter::kHead_IterStart)); + REPORTER_ASSERT(reporter, nullptr == iter1.init(list1, Iter::kTail_IterStart)); + // Try popping an empty list + list1.popHead(); + list1.popTail(); + REPORTER_ASSERT(reporter, list1.isEmpty()); + REPORTER_ASSERT(reporter, list1 == list2); -#if SK_ENABLE_INST_COUNT - SkASSERT(0 == ListElement::InstanceCount()); -#endif + // Create two identical lists, one by appending to head and the other to the tail. + list1.addToHead(ListElement(1)); + list2.addToTail(ListElement(1)); + iter1.init(list1, Iter::kHead_IterStart); + iter2.init(list1, Iter::kTail_IterStart); + REPORTER_ASSERT(reporter, iter1.get()->fID == iter2.get()->fID); + iter3.init(list2, Iter::kHead_IterStart); + iter4.init(list2, Iter::kTail_IterStart); + REPORTER_ASSERT(reporter, iter3.get()->fID == iter1.get()->fID); + REPORTER_ASSERT(reporter, iter4.get()->fID == iter1.get()->fID); + REPORTER_ASSERT(reporter, list1 == list2); - REPORTER_ASSERT(reporter, list1.isEmpty()); - REPORTER_ASSERT(reporter, NULL == iter1.init(list1, Iter::kHead_IterStart)); - REPORTER_ASSERT(reporter, NULL == iter1.init(list1, Iter::kTail_IterStart)); - // Try popping an empty list - list1.popHead(); - list1.popTail(); - REPORTER_ASSERT(reporter, list1.isEmpty()); - REPORTER_ASSERT(reporter, list1 == list2); + list2.reset(); - // Create two identical lists, one by appending to head and the other to the tail. - list1.addToHead(ListElement(1)); - list2.addToTail(ListElement(1)); -#if SK_ENABLE_INST_COUNT - SkASSERT(2 == ListElement::InstanceCount()); -#endif - iter1.init(list1, Iter::kHead_IterStart); - iter2.init(list1, Iter::kTail_IterStart); - REPORTER_ASSERT(reporter, iter1.get()->fID == iter2.get()->fID); - iter3.init(list2, Iter::kHead_IterStart); - iter4.init(list2, Iter::kTail_IterStart); - REPORTER_ASSERT(reporter, iter3.get()->fID == iter1.get()->fID); - REPORTER_ASSERT(reporter, iter4.get()->fID == iter1.get()->fID); - REPORTER_ASSERT(reporter, list1 == list2); + // use both before/after in-place construction on an empty list + list2.addBefore(list2.headIter(), 1); + REPORTER_ASSERT(reporter, list2 == list1); + list2.reset(); - list2.reset(); + list2.addAfter(list2.tailIter(), 1); + REPORTER_ASSERT(reporter, list2 == list1); - // use both before/after in-place construction on an empty list - SkNEW_INSERT_IN_LLIST_BEFORE(&list2, list2.headIter(), ListElement, (1)); - REPORTER_ASSERT(reporter, list2 == list1); - list2.reset(); + // add an element to the second list, check that iters are still valid + iter3.init(list2, Iter::kHead_IterStart); + iter4.init(list2, Iter::kTail_IterStart); + list2.addToHead(ListElement(2)); - SkNEW_INSERT_IN_LLIST_AFTER(&list2, list2.tailIter(), ListElement, (1)); - REPORTER_ASSERT(reporter, list2 == list1); + REPORTER_ASSERT(reporter, iter3.get()->fID == iter1.get()->fID); + REPORTER_ASSERT(reporter, iter4.get()->fID == iter1.get()->fID); + REPORTER_ASSERT(reporter, 1 == Iter(list2, Iter::kTail_IterStart).get()->fID); + REPORTER_ASSERT(reporter, 2 == Iter(list2, Iter::kHead_IterStart).get()->fID); + REPORTER_ASSERT(reporter, list1 != list2); + list1.addToHead(ListElement(2)); + REPORTER_ASSERT(reporter, list1 == list2); + REPORTER_ASSERT(reporter, !list1.isEmpty()); - // add an element to the second list, check that iters are still valid - iter3.init(list2, Iter::kHead_IterStart); - iter4.init(list2, Iter::kTail_IterStart); - list2.addToHead(ListElement(2)); + list1.reset(); + list2.reset(); + REPORTER_ASSERT(reporter, list1.isEmpty() && list2.isEmpty()); -#if SK_ENABLE_INST_COUNT - SkASSERT(3 == ListElement::InstanceCount()); -#endif + // randomly perform insertions and deletions on a list and perform tests + int count = 0; + for (int j = 0; j < 100; ++j) { + if (list1.isEmpty() || random.nextBiasedBool(3 * SK_Scalar1 / 4)) { + int id = j; + // Choose one of three ways to insert a new element: at the head, at the tail, + // before a random element, after a random element + int numValidMethods = 0 == count ? 2 : 4; + int insertionMethod = random.nextULessThan(numValidMethods); + switch (insertionMethod) { + case 0: + list1.addToHead(ListElement(id)); + break; + case 1: + list1.addToTail(ListElement(id)); + break; + case 2: // fallthru to share code that picks random element. + case 3: { + int n = random.nextULessThan(list1.count()); + Iter iter = list1.headIter(); + // remember the elements before/after the insertion point. + while (n--) { + iter.next(); + } + Iter prev(iter); + Iter next(iter); + next.next(); + prev.prev(); - REPORTER_ASSERT(reporter, iter3.get()->fID == iter1.get()->fID); - REPORTER_ASSERT(reporter, iter4.get()->fID == iter1.get()->fID); - REPORTER_ASSERT(reporter, 1 == Iter(list2, Iter::kTail_IterStart).get()->fID); - REPORTER_ASSERT(reporter, 2 == Iter(list2, Iter::kHead_IterStart).get()->fID); - REPORTER_ASSERT(reporter, list1 != list2); - list1.addToHead(ListElement(2)); - REPORTER_ASSERT(reporter, list1 == list2); -#if SK_ENABLE_INST_COUNT - SkASSERT(4 == ListElement::InstanceCount()); -#endif - REPORTER_ASSERT(reporter, !list1.isEmpty()); + SkASSERT(iter.get()); + // insert either before or after the iterator, then check that the + // surrounding sequence is correct. + if (2 == insertionMethod) { + list1.addBefore(iter, id); + Iter newItem(iter); + newItem.prev(); + REPORTER_ASSERT(reporter, newItem.get()->fID == id); - list1.reset(); - list2.reset(); -#if SK_ENABLE_INST_COUNT - SkASSERT(0 == ListElement::InstanceCount()); -#endif - REPORTER_ASSERT(reporter, list1.isEmpty() && list2.isEmpty()); - - // randomly perform insertions and deletions on a list and perform tests - int count = 0; - for (int j = 0; j < 100; ++j) { - if (list1.isEmpty() || random.nextBiasedBool(3 * SK_Scalar1 / 4)) { - int id = j; - // Choose one of three ways to insert a new element: at the head, at the tail, - // before a random element, after a random element - int numValidMethods = 0 == count ? 2 : 4; - int insertionMethod = random.nextULessThan(numValidMethods); - switch (insertionMethod) { - case 0: - list1.addToHead(ListElement(id)); - break; - case 1: - list1.addToTail(ListElement(id)); - break; - case 2: // fallthru to share code that picks random element. - case 3: { - int n = random.nextULessThan(list1.count()); - Iter iter = list1.headIter(); - // remember the elements before/after the insertion point. - while (n--) { - iter.next(); + if (next.get()) { + REPORTER_ASSERT(reporter, next.prev()->fID == iter.get()->fID); } - Iter prev(iter); - Iter next(iter); - next.next(); - prev.prev(); + if (prev.get()) { + REPORTER_ASSERT(reporter, prev.next()->fID == id); + } + } else { + list1.addAfter(iter, id); + Iter newItem(iter); + newItem.next(); + REPORTER_ASSERT(reporter, newItem.get()->fID == id); - SkASSERT(iter.get()); - // insert either before or after the iterator, then check that the - // surrounding sequence is correct. - if (2 == insertionMethod) { - SkNEW_INSERT_IN_LLIST_BEFORE(&list1, iter, ListElement, (id)); - Iter newItem(iter); - newItem.prev(); - REPORTER_ASSERT(reporter, newItem.get()->fID == id); - - if (next.get()) { - REPORTER_ASSERT(reporter, next.prev()->fID == iter.get()->fID); - } - if (prev.get()) { - REPORTER_ASSERT(reporter, prev.next()->fID == id); - } - } else { - SkNEW_INSERT_IN_LLIST_AFTER(&list1, iter, ListElement, (id)); - Iter newItem(iter); - newItem.next(); - REPORTER_ASSERT(reporter, newItem.get()->fID == id); - - if (next.get()) { - REPORTER_ASSERT(reporter, next.prev()->fID == id); - } - if (prev.get()) { - REPORTER_ASSERT(reporter, prev.next()->fID == iter.get()->fID); - } + if (next.get()) { + REPORTER_ASSERT(reporter, next.prev()->fID == id); + } + if (prev.get()) { + REPORTER_ASSERT(reporter, prev.next()->fID == iter.get()->fID); } } } - ++count; - } else { - // walk to a random place either forward or backwards and remove. - int n = random.nextULessThan(list1.count()); - Iter::IterStart start; - ListElement* (Iter::*incrFunc)(); - - if (random.nextBool()) { - start = Iter::kHead_IterStart; - incrFunc = &Iter::next; - } else { - start = Iter::kTail_IterStart; - incrFunc = &Iter::prev; - } - - // find the element - Iter iter(list1, start); - while (n--) { - REPORTER_ASSERT(reporter, iter.get()); - (iter.*incrFunc)(); - } - REPORTER_ASSERT(reporter, iter.get()); - - // remember the prev and next elements from the element to be removed - Iter prev = iter; - Iter next = iter; - prev.prev(); - next.next(); - list1.remove(iter.get()); - - // make sure the remembered next/prev iters still work - Iter pn = prev; pn.next(); - Iter np = next; np.prev(); - // pn should match next unless the target node was the head, in which case prev - // walked off the list. - REPORTER_ASSERT(reporter, pn.get() == next.get() || NULL == prev.get()); - // Similarly, np should match prev unless next originally walked off the tail. - REPORTER_ASSERT(reporter, np.get() == prev.get() || NULL == next.get()); - --count; } - REPORTER_ASSERT(reporter, count == list1.count()); -#if SK_ENABLE_INST_COUNT - SkASSERT(count == ListElement::InstanceCount()); -#endif + ++count; + } else { + // walk to a random place either forward or backwards and remove. + int n = random.nextULessThan(list1.count()); + typename Iter::IterStart start; + ListElement* (Iter::*incrFunc)(); + + if (random.nextBool()) { + start = Iter::kHead_IterStart; + incrFunc = &Iter::next; + } else { + start = Iter::kTail_IterStart; + incrFunc = &Iter::prev; + } + + // find the element + Iter iter(list1, start); + while (n--) { + REPORTER_ASSERT(reporter, iter.get()); + (iter.*incrFunc)(); + } + REPORTER_ASSERT(reporter, iter.get()); + + // remember the prev and next elements from the element to be removed + Iter prev = iter; + Iter next = iter; + prev.prev(); + next.next(); + list1.remove(iter.get()); + + // make sure the remembered next/prev iters still work + Iter pn = prev; pn.next(); + Iter np = next; np.prev(); + // pn should match next unless the target node was the head, in which case prev + // walked off the list. + REPORTER_ASSERT(reporter, pn.get() == next.get() || nullptr == prev.get()); + // Similarly, np should match prev unless next originally walked off the tail. + REPORTER_ASSERT(reporter, np.get() == prev.get() || nullptr == next.get()); + --count; } - list1.reset(); -#if SK_ENABLE_INST_COUNT - SkASSERT(0 == ListElement::InstanceCount()); -#endif + REPORTER_ASSERT(reporter, count == list1.count()); } } DEF_TEST(LList, reporter) { - TestTInternalLList(reporter); - TestTLList(reporter); + test_tinternallist(reporter); + test_tllist<1>(reporter); + test_tllist<3>(reporter); + test_tllist<8>(reporter); + test_tllist<10>(reporter); + test_tllist<16>(reporter); }
diff --git a/src/third_party/skia/tests/LRUCacheTest.cpp b/src/third_party/skia/tests/LRUCacheTest.cpp new file mode 100644 index 0000000..6a65e4a --- /dev/null +++ b/src/third_party/skia/tests/LRUCacheTest.cpp
@@ -0,0 +1,70 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkLRUCache.h" +#include "Test.h" + +struct Value { + Value(int value, int* counter) + : fValue(value) + , fCounter(counter) { + (*fCounter)++; + } + + ~Value() { + (*fCounter)--; + } + + int fValue; + int* fCounter; +}; + +DEF_TEST(LRUCacheSequential, r) { + int instances = 0; + { + static const int kSize = 100; + SkLRUCache<int, std::unique_ptr<Value>> test(kSize); + for (int i = 1; i < kSize * 2; i++) { + REPORTER_ASSERT(r, !test.find(i)); + test.insert(i, std::unique_ptr<Value>(new Value(i * i, &instances))); + REPORTER_ASSERT(r, test.find(i)); + REPORTER_ASSERT(r, i * i == (*test.find(i))->fValue); + if (i > kSize) { + REPORTER_ASSERT(r, kSize == instances); + REPORTER_ASSERT(r, !test.find(i - kSize)); + } else { + REPORTER_ASSERT(r, i == instances); + } + REPORTER_ASSERT(r, (int) test.count() == instances); + } + } + REPORTER_ASSERT(r, 0 == instances); +} + +DEF_TEST(LRUCacheRandom, r) { + int instances = 0; + { + int seq[] = { 0, 1, 2, 3, 4, 1, 6, 2, 7, 5, 3, 2, 2, 3, 1, 7 }; + int expected[] = { 7, 1, 3, 2, 5 }; + static const int kSize = 5; + SkLRUCache<int, std::unique_ptr<Value>> test(kSize); + for (int i = 0; i < (int) (sizeof(seq) / sizeof(int)); i++) { + int k = seq[i]; + if (!test.find(k)) { + test.insert(k, std::unique_ptr<Value>(new Value(k, &instances))); + } + } + REPORTER_ASSERT(r, kSize == instances); + REPORTER_ASSERT(r, kSize == test.count()); + for (int i = 0; i < kSize; i++) { + int k = expected[i]; + REPORTER_ASSERT(r, test.find(k)); + REPORTER_ASSERT(r, k == (*test.find(k))->fValue); + } + } + REPORTER_ASSERT(r, 0 == instances); +}
diff --git a/src/third_party/skia/tests/LayerDrawLooperTest.cpp b/src/third_party/skia/tests/LayerDrawLooperTest.cpp index bc76a02..f3705b7 100644 --- a/src/third_party/skia/tests/LayerDrawLooperTest.cpp +++ b/src/third_party/skia/tests/LayerDrawLooperTest.cpp
@@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include "SkArenaAlloc.h" #include "SkBitmap.h" #include "SkBitmapDevice.h" #include "SkCanvas.h" @@ -15,8 +16,6 @@ #include "SkRect.h" #include "SkRefCnt.h" #include "SkScalar.h" -#include "SkSmallAllocator.h" -#include "SkXfermode.h" #include "Test.h" static SkBitmap make_bm(int w, int h) { @@ -25,14 +24,15 @@ return bm; } +// TODO: can this be derived from SkBaseDevice? class FakeDevice : public SkBitmapDevice { public: - FakeDevice() : SkBitmapDevice(make_bm(100, 100)) { } + FakeDevice() : INHERITED(make_bm(100, 100), SkSurfaceProps(0, kUnknown_SkPixelGeometry)) { + } - virtual void drawRect(const SkDraw& draw, const SkRect& r, - const SkPaint& paint) SK_OVERRIDE { - fLastMatrix = *draw.fMatrix; - this->INHERITED::drawRect(draw, r, paint); + void drawRect(const SkRect& r, const SkPaint& paint) override { + fLastMatrix = this->ctm(); + this->INHERITED::drawRect(r, paint); } SkMatrix fLastMatrix; @@ -52,19 +52,18 @@ layerInfo.fOffset.set(10.0f, 20.0f); layerInfo.fPaintBits |= SkLayerDrawLooper::kXfermode_Bit; SkPaint* layerPaint = looperBuilder.addLayer(layerInfo); - layerPaint->setXfermodeMode(SkXfermode::kSrc_Mode); + layerPaint->setBlendMode(SkBlendMode::kSrc); FakeDevice device; SkCanvas canvas(&device); SkPaint paint; - SkAutoTUnref<SkLayerDrawLooper> looper(looperBuilder.detachLooper()); - SkSmallAllocator<1, 32> allocator; - void* buffer = allocator.reserveT<SkDrawLooper::Context>(looper->contextSize()); - SkDrawLooper::Context* context = looper->createContext(&canvas, buffer); + auto looper(looperBuilder.detach()); + SkArenaAlloc alloc{48}; + SkDrawLooper::Context* context = looper->makeContext(&canvas, &alloc); // The back layer should come first. REPORTER_ASSERT(reporter, context->next(&canvas, &paint)); - REPORTER_ASSERT(reporter, SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrc_Mode)); + REPORTER_ASSERT(reporter, paint.getBlendMode() == SkBlendMode::kSrc); canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint); REPORTER_ASSERT(reporter, 10.0f == device.fLastMatrix.getTranslateX()); REPORTER_ASSERT(reporter, 20.0f == device.fLastMatrix.getTranslateY()); @@ -72,7 +71,7 @@ // Then the front layer. REPORTER_ASSERT(reporter, context->next(&canvas, &paint)); - REPORTER_ASSERT(reporter, SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode)); + REPORTER_ASSERT(reporter, paint.getBlendMode() == SkBlendMode::kSrcOver); canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint); REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateX()); REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateY()); @@ -92,19 +91,18 @@ layerInfo.fOffset.set(10.0f, 20.0f); layerInfo.fPaintBits |= SkLayerDrawLooper::kXfermode_Bit; SkPaint* layerPaint = looperBuilder.addLayerOnTop(layerInfo); - layerPaint->setXfermodeMode(SkXfermode::kSrc_Mode); + layerPaint->setBlendMode(SkBlendMode::kSrc); FakeDevice device; SkCanvas canvas(&device); SkPaint paint; - SkAutoTUnref<SkLayerDrawLooper> looper(looperBuilder.detachLooper()); - SkSmallAllocator<1, 32> allocator; - void* buffer = allocator.reserveT<SkDrawLooper::Context>(looper->contextSize()); - SkDrawLooper::Context* context = looper->createContext(&canvas, buffer); + auto looper(looperBuilder.detach()); + SkArenaAlloc alloc{48}; + SkDrawLooper::Context* context = looper->makeContext(&canvas, &alloc); // The back layer should come first. REPORTER_ASSERT(reporter, context->next(&canvas, &paint)); - REPORTER_ASSERT(reporter, SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode)); + REPORTER_ASSERT(reporter, paint.getBlendMode() == SkBlendMode::kSrcOver); canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint); REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateX()); REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateY()); @@ -112,7 +110,7 @@ // Then the front layer. REPORTER_ASSERT(reporter, context->next(&canvas, &paint)); - REPORTER_ASSERT(reporter, SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrc_Mode)); + REPORTER_ASSERT(reporter, paint.getBlendMode() == SkBlendMode::kSrc); canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint); REPORTER_ASSERT(reporter, 10.0f == device.fLastMatrix.getTranslateX()); REPORTER_ASSERT(reporter, 20.0f == device.fLastMatrix.getTranslateY()); @@ -132,19 +130,18 @@ layerInfo.fOffset.set(10.0f, 20.0f); layerInfo.fPaintBits |= SkLayerDrawLooper::kXfermode_Bit; SkPaint* layerPaint = looperBuilder.addLayerOnTop(layerInfo); - layerPaint->setXfermodeMode(SkXfermode::kSrc_Mode); + layerPaint->setBlendMode(SkBlendMode::kSrc); FakeDevice device; SkCanvas canvas(&device); SkPaint paint; - SkAutoTUnref<SkLayerDrawLooper> looper(looperBuilder.detachLooper()); - SkSmallAllocator<1, 32> allocator; - void* buffer = allocator.reserveT<SkDrawLooper::Context>(looper->contextSize()); - SkDrawLooper::Context* context = looper->createContext(&canvas, buffer); + sk_sp<SkDrawLooper> looper(looperBuilder.detach()); + SkArenaAlloc alloc{48}; + SkDrawLooper::Context* context = looper->makeContext(&canvas, &alloc); // The back layer should come first. REPORTER_ASSERT(reporter, context->next(&canvas, &paint)); - REPORTER_ASSERT(reporter, SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode)); + REPORTER_ASSERT(reporter, paint.getBlendMode() == SkBlendMode::kSrcOver); canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint); REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateX()); REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateY()); @@ -152,7 +149,7 @@ // Then the front layer. REPORTER_ASSERT(reporter, context->next(&canvas, &paint)); - REPORTER_ASSERT(reporter, SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrc_Mode)); + REPORTER_ASSERT(reporter, paint.getBlendMode() == SkBlendMode::kSrc); canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint); REPORTER_ASSERT(reporter, 10.0f == device.fLastMatrix.getTranslateX()); REPORTER_ASSERT(reporter, 20.0f == device.fLastMatrix.getTranslateY());
diff --git a/src/third_party/skia/tests/LayerRasterizerTest.cpp b/src/third_party/skia/tests/LayerRasterizerTest.cpp index 4b236ac..310aa3b 100644 --- a/src/third_party/skia/tests/LayerRasterizerTest.cpp +++ b/src/third_party/skia/tests/LayerRasterizerTest.cpp
@@ -26,14 +26,14 @@ gCount++; } - ~DummyRasterizer() { + ~DummyRasterizer() override { // Not threadsafe. Only used in one thread. gCount--; } static int GetCount() { return gCount; } - SK_DECLARE_NOT_FLATTENABLE_PROCS(DummyRasterizer) + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(DummyRasterizer) private: static int gCount; @@ -43,11 +43,15 @@ int DummyRasterizer::gCount; +sk_sp<SkFlattenable> DummyRasterizer::CreateProc(SkReadBuffer&) { + return sk_make_sp<DummyRasterizer>(); +} + // Check to make sure that the SkPaint in the layer has its destructor called. DEF_TEST(LayerRasterizer_destructor, reporter) { { SkPaint paint; - paint.setRasterizer(SkNEW(DummyRasterizer))->unref(); + paint.setRasterizer(sk_make_sp<DummyRasterizer>()); REPORTER_ASSERT(reporter, DummyRasterizer::GetCount() == 1); SkLayerRasterizer::Builder builder; @@ -79,7 +83,7 @@ DEF_TEST(LayerRasterizer_copy, reporter) { SkLayerRasterizer::Builder builder; - REPORTER_ASSERT(reporter, NULL == builder.snapshotRasterizer()); + REPORTER_ASSERT(reporter, nullptr == builder.snapshot()); SkPaint paint; // Create a bunch of paints with different flags. for (uint32_t flags = 0x01; flags < SkPaint::kAllFlags; flags <<= 1) { @@ -88,14 +92,14 @@ } // Create a layer rasterizer with all the existing layers. - SkAutoTUnref<SkLayerRasterizer> firstCopy(builder.snapshotRasterizer()); + sk_sp<SkLayerRasterizer> firstCopy(builder.snapshot()); // Add one more layer. paint.setFlags(SkPaint::kAllFlags); builder.addLayer(paint); - SkAutoTUnref<SkLayerRasterizer> oneLarger(builder.snapshotRasterizer()); - SkAutoTUnref<SkLayerRasterizer> detached(builder.detachRasterizer()); + sk_sp<SkLayerRasterizer> oneLarger(builder.snapshot()); + sk_sp<SkLayerRasterizer> detached(builder.detach()); // Check the counts for consistency. const int largerCount = LayerRasterizerTester::CountLayers(*oneLarger.get()); @@ -103,9 +107,9 @@ REPORTER_ASSERT(reporter, largerCount == LayerRasterizerTester::CountLayers(*detached.get())); REPORTER_ASSERT(reporter, smallerCount == largerCount - 1); - const SkLayerRasterizer_Rec* recFirstCopy = NULL; - const SkLayerRasterizer_Rec* recOneLarger = NULL; - const SkLayerRasterizer_Rec* recDetached = NULL; + const SkLayerRasterizer_Rec* recFirstCopy = nullptr; + const SkLayerRasterizer_Rec* recOneLarger = nullptr; + const SkLayerRasterizer_Rec* recDetached = nullptr; const SkDeque& layersFirstCopy = LayerRasterizerTester::GetLayers(*firstCopy.get()); const SkDeque& layersOneLarger = LayerRasterizerTester::GetLayers(*oneLarger.get()); @@ -129,7 +133,7 @@ REPORTER_ASSERT(reporter, equals(*recOneLarger, *recDetached)); if (smallerCount == i) { - REPORTER_ASSERT(reporter, recFirstCopy == NULL); + REPORTER_ASSERT(reporter, recFirstCopy == nullptr); } else { REPORTER_ASSERT(reporter, equals(*recFirstCopy, *recOneLarger)); } @@ -138,5 +142,5 @@ DEF_TEST(LayerRasterizer_detachEmpty, reporter) { SkLayerRasterizer::Builder builder; - REPORTER_ASSERT(reporter, NULL == builder.detachRasterizer()); + REPORTER_ASSERT(reporter, nullptr == builder.detach()); }
diff --git a/src/third_party/skia/tests/MD5Test.cpp b/src/third_party/skia/tests/MD5Test.cpp index efad26d..214a7a8 100644 --- a/src/third_party/skia/tests/MD5Test.cpp +++ b/src/third_party/skia/tests/MD5Test.cpp
@@ -23,7 +23,7 @@ // All at once { SkMD5 context; - context.update(reinterpret_cast<const uint8_t*>(string), len); + context.write(string, len); SkMD5::Digest digest; context.finish(digest); @@ -36,7 +36,7 @@ const uint8_t* data = reinterpret_cast<const uint8_t*>(string); const uint8_t* end = reinterpret_cast<const uint8_t*>(string + len); for (; data < end; ++data) { - context.update(data, 1); + context.write(data, 1); } SkMD5::Digest digest; context.finish(digest);
diff --git a/src/third_party/skia/tests/MallocPixelRefTest.cpp b/src/third_party/skia/tests/MallocPixelRefTest.cpp index e267d6f..957c8b3 100644 --- a/src/third_party/skia/tests/MallocPixelRefTest.cpp +++ b/src/third_party/skia/tests/MallocPixelRefTest.cpp
@@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include "SkAutoMalloc.h" #include "SkData.h" #include "SkMallocPixelRef.h" #include "Test.h" @@ -24,82 +25,91 @@ REPORTER_ASSERT(reporter, true); SkImageInfo info = SkImageInfo::MakeN32Premul(10, 13); { - SkAutoTUnref<SkMallocPixelRef> pr( - SkMallocPixelRef::NewAllocate(info, info.minRowBytes() - 1, NULL)); + sk_sp<SkPixelRef> pr( + SkMallocPixelRef::MakeAllocate(info, info.minRowBytes() - 1)); // rowbytes too small. - REPORTER_ASSERT(reporter, NULL == pr.get()); + REPORTER_ASSERT(reporter, nullptr == pr.get()); } { size_t rowBytes = info.minRowBytes() - 1; size_t size = info.getSafeSize(rowBytes); - SkAutoDataUnref data(SkData::NewUninitialized(size)); - SkAutoTUnref<SkMallocPixelRef> pr( - SkMallocPixelRef::NewWithData(info, rowBytes, NULL, data)); + sk_sp<SkData> data(SkData::MakeUninitialized(size)); + sk_sp<SkPixelRef> pr( + SkMallocPixelRef::MakeWithData(info, rowBytes, data)); // rowbytes too small. - REPORTER_ASSERT(reporter, NULL == pr.get()); + REPORTER_ASSERT(reporter, nullptr == pr.get()); } { size_t rowBytes = info.minRowBytes() + 2; size_t size = info.getSafeSize(rowBytes) - 1; - SkAutoDataUnref data(SkData::NewUninitialized(size)); - SkAutoTUnref<SkMallocPixelRef> pr( - SkMallocPixelRef::NewWithData(info, rowBytes, NULL, data)); + sk_sp<SkData> data(SkData::MakeUninitialized(size)); + sk_sp<SkPixelRef> pr( + SkMallocPixelRef::MakeWithData(info, rowBytes, data)); // data too small. - REPORTER_ASSERT(reporter, NULL == pr.get()); + REPORTER_ASSERT(reporter, nullptr == pr.get()); } size_t rowBytes = info.minRowBytes() + 7; size_t size = info.getSafeSize(rowBytes) + 9; { SkAutoMalloc memory(size); - SkAutoTUnref<SkMallocPixelRef> pr( - SkMallocPixelRef::NewDirect(info, memory.get(), rowBytes, NULL)); - REPORTER_ASSERT(reporter, pr.get() != NULL); + sk_sp<SkPixelRef> pr( + SkMallocPixelRef::MakeDirect(info, memory.get(), rowBytes)); + REPORTER_ASSERT(reporter, pr.get() != nullptr); REPORTER_ASSERT(reporter, memory.get() == pr->pixels()); } { - SkAutoTUnref<SkMallocPixelRef> pr( - SkMallocPixelRef::NewAllocate(info, rowBytes, NULL)); - REPORTER_ASSERT(reporter, pr.get() != NULL); + sk_sp<SkPixelRef> pr( + SkMallocPixelRef::MakeAllocate(info, rowBytes)); + REPORTER_ASSERT(reporter, pr.get() != nullptr); REPORTER_ASSERT(reporter, pr->pixels()); } { void* addr = static_cast<void*>(new uint8_t[size]); - SkAutoTUnref<SkMallocPixelRef> pr( - SkMallocPixelRef::NewWithProc(info, rowBytes, NULL, addr, - delete_uint8_proc, NULL)); - REPORTER_ASSERT(reporter, pr.get() != NULL); + sk_sp<SkPixelRef> pr( + SkMallocPixelRef::MakeWithProc(info, rowBytes, addr, delete_uint8_proc, nullptr)); + REPORTER_ASSERT(reporter, pr.get() != nullptr); REPORTER_ASSERT(reporter, addr == pr->pixels()); } { int x = 0; SkAutoMalloc memory(size); - SkAutoTUnref<SkMallocPixelRef> pr( - SkMallocPixelRef::NewWithProc(info, rowBytes, NULL, - memory.get(), set_to_one_proc, - static_cast<void*>(&x))); - REPORTER_ASSERT(reporter, pr.get() != NULL); + sk_sp<SkPixelRef> pr( + SkMallocPixelRef::MakeWithProc(info, rowBytes, + memory.get(), set_to_one_proc, + static_cast<void*>(&x))); + REPORTER_ASSERT(reporter, pr.get() != nullptr); REPORTER_ASSERT(reporter, memory.get() == pr->pixels()); REPORTER_ASSERT(reporter, 0 == x); - pr.reset(NULL); + pr.reset(nullptr); + // make sure that set_to_one_proc was called. + REPORTER_ASSERT(reporter, 1 == x); + } + { + int x = 0; + SkAutoMalloc memory(size); + sk_sp<SkPixelRef> pr( + SkMallocPixelRef::MakeWithProc(SkImageInfo::MakeN32Premul(-1, -1), rowBytes, + memory.get(), set_to_one_proc, + static_cast<void*>(&x))); + REPORTER_ASSERT(reporter, pr.get() == nullptr); // make sure that set_to_one_proc was called. REPORTER_ASSERT(reporter, 1 == x); } { void* addr = static_cast<void*>(new uint8_t[size]); - REPORTER_ASSERT(reporter, addr != NULL); - SkAutoTUnref<SkMallocPixelRef> pr( - SkMallocPixelRef::NewWithProc(info, rowBytes, NULL, addr, - delete_uint8_proc, NULL)); + REPORTER_ASSERT(reporter, addr != nullptr); + sk_sp<SkPixelRef> pr( + SkMallocPixelRef::MakeWithProc(info, rowBytes, addr, + delete_uint8_proc, nullptr)); REPORTER_ASSERT(reporter, addr == pr->pixels()); } { - SkAutoDataUnref data(SkData::NewUninitialized(size)); + sk_sp<SkData> data(SkData::MakeUninitialized(size)); SkData* dataPtr = data.get(); REPORTER_ASSERT(reporter, dataPtr->unique()); - SkAutoTUnref<SkMallocPixelRef> pr( - SkMallocPixelRef::NewWithData(info, rowBytes, NULL, data.get())); + sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeWithData(info, rowBytes, data); REPORTER_ASSERT(reporter, !(dataPtr->unique())); - data.reset(NULL); + data.reset(nullptr); REPORTER_ASSERT(reporter, dataPtr->unique()); REPORTER_ASSERT(reporter, dataPtr->data() == pr->pixels()); }
diff --git a/src/third_party/skia/tests/MaskCacheTest.cpp b/src/third_party/skia/tests/MaskCacheTest.cpp new file mode 100644 index 0000000..13cd354 --- /dev/null +++ b/src/third_party/skia/tests/MaskCacheTest.cpp
@@ -0,0 +1,106 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCachedData.h" +#include "SkMaskCache.h" +#include "SkResourceCache.h" +#include "Test.h" + +enum LockedState { + kUnlocked, + kLocked, +}; + +enum CachedState { + kNotInCache, + kInCache, +}; + +static void check_data(skiatest::Reporter* reporter, SkCachedData* data, + int refcnt, CachedState cacheState, LockedState lockedState) { + REPORTER_ASSERT(reporter, data->testing_only_getRefCnt() == refcnt); + REPORTER_ASSERT(reporter, data->testing_only_isInCache() == (kInCache == cacheState)); + bool isLocked = (data->data() != nullptr); + REPORTER_ASSERT(reporter, isLocked == (lockedState == kLocked)); +} + +DEF_TEST(RRectMaskCache, reporter) { + SkResourceCache cache(1024); + + SkScalar sigma = 0.8f; + SkRect rect = SkRect::MakeWH(100, 100); + SkRRect rrect; + rrect.setRectXY(rect, 30, 30); + SkBlurStyle style = kNormal_SkBlurStyle; + SkBlurQuality quality = kLow_SkBlurQuality; + SkMask mask; + + SkCachedData* data = SkMaskCache::FindAndRef(sigma, style, quality, rrect, &mask, &cache); + REPORTER_ASSERT(reporter, nullptr == data); + + size_t size = 256; + data = cache.newCachedData(size); + memset(data->writable_data(), 0xff, size); + mask.fBounds.setXYWH(0, 0, 100, 100); + mask.fRowBytes = 100; + mask.fFormat = SkMask::kBW_Format; + SkMaskCache::Add(sigma, style, quality, rrect, mask, data, &cache); + check_data(reporter, data, 2, kInCache, kLocked); + + data->unref(); + check_data(reporter, data, 1, kInCache, kUnlocked); + + sk_bzero(&mask, sizeof(mask)); + data = SkMaskCache::FindAndRef(sigma, style, quality, rrect, &mask, &cache); + REPORTER_ASSERT(reporter, data); + REPORTER_ASSERT(reporter, data->size() == size); + REPORTER_ASSERT(reporter, mask.fBounds.top() == 0 && mask.fBounds.bottom() == 100); + REPORTER_ASSERT(reporter, data->data() == (const void*)mask.fImage); + check_data(reporter, data, 2, kInCache, kLocked); + + cache.purgeAll(); + check_data(reporter, data, 1, kNotInCache, kLocked); + data->unref(); +} + +DEF_TEST(RectsMaskCache, reporter) { + SkResourceCache cache(1024); + + SkScalar sigma = 0.8f; + SkRect rect = SkRect::MakeWH(100, 100); + SkRect rects[2] = {rect}; + SkBlurStyle style = kNormal_SkBlurStyle; + SkBlurQuality quality = kLow_SkBlurQuality; + SkMask mask; + + SkCachedData* data = SkMaskCache::FindAndRef(sigma, style, quality, rects, 1, &mask, &cache); + REPORTER_ASSERT(reporter, nullptr == data); + + size_t size = 256; + data = cache.newCachedData(size); + memset(data->writable_data(), 0xff, size); + mask.fBounds.setXYWH(0, 0, 100, 100); + mask.fRowBytes = 100; + mask.fFormat = SkMask::kBW_Format; + SkMaskCache::Add(sigma, style, quality, rects, 1, mask, data, &cache); + check_data(reporter, data, 2, kInCache, kLocked); + + data->unref(); + check_data(reporter, data, 1, kInCache, kUnlocked); + + sk_bzero(&mask, sizeof(mask)); + data = SkMaskCache::FindAndRef(sigma, style, quality, rects, 1, &mask, &cache); + REPORTER_ASSERT(reporter, data); + REPORTER_ASSERT(reporter, data->size() == size); + REPORTER_ASSERT(reporter, mask.fBounds.top() == 0 && mask.fBounds.bottom() == 100); + REPORTER_ASSERT(reporter, data->data() == (const void*)mask.fImage); + check_data(reporter, data, 2, kInCache, kLocked); + + cache.purgeAll(); + check_data(reporter, data, 1, kNotInCache, kLocked); + data->unref(); +}
diff --git a/src/third_party/skia/tests/MathTest.cpp b/src/third_party/skia/tests/MathTest.cpp index 2053936..0bd3cf5 100644 --- a/src/third_party/skia/tests/MathTest.cpp +++ b/src/third_party/skia/tests/MathTest.cpp
@@ -5,10 +5,15 @@ * found in the LICENSE file. */ +#include "float.h" + #include "SkColorPriv.h" #include "SkEndian.h" +#include "SkFDot6.h" +#include "SkFixed.h" #include "SkFloatBits.h" #include "SkFloatingPoint.h" +#include "SkHalf.h" #include "SkMathPriv.h" #include "SkPoint.h" #include "SkRandom.h" @@ -32,6 +37,38 @@ } } +static void test_quick_div(skiatest::Reporter* reporter) { + /* + The inverse table is generated by turning on SkDebugf in the following test code + */ + SkFixed storage[kInverseTableSize * 2]; + SkFixed* table = storage + kInverseTableSize; + + // SkDebugf("static const int gFDot6INVERSE[] = {"); + for (SkFDot6 i=-kInverseTableSize; i<kInverseTableSize; i++) { + if (i != 0) { + table[i] = SkFDot6Div(SK_FDot6One, i); + REPORTER_ASSERT(reporter, table[i] == gFDot6INVERSE[i + kInverseTableSize]); + } + // SkDebugf("%d, ", table[i]); + } + // SkDebugf("}\n"); + + + for (SkFDot6 a = -1024; a <= 1024; a++) { + for (SkFDot6 b = -1024; b <= 1024; b++) { + if (b != 0) { + SkFixed ourAnswer = QuickSkFDot6Div(a, b); + SkFixed directAnswer = SkFDot6Div(a, b); + REPORTER_ASSERT(reporter, + (directAnswer == 0 && ourAnswer == 0) || + SkFixedDiv(SkAbs32(directAnswer - ourAnswer), SkAbs32(directAnswer)) <= 1 << 10 + ); + } + } + } +} + /////////////////////////////////////////////////////////////////////////////// static float sk_fsel(float pred, float result_ge, float result_lt) { @@ -51,9 +88,10 @@ static void test_floor_value(skiatest::Reporter* reporter, float value) { float fast = fast_floor(value); float std = std_floor(value); - REPORTER_ASSERT(reporter, std == fast); -// SkDebugf("value[%1.9f] std[%g] fast[%g] equal[%d]\n", -// value, std, fast, std == fast); + if (std != fast) { + ERRORF(reporter, "fast_floor(%.9g) == %.9g != %.9g == std_floor(%.9g)", + value, fast, std, value); + } } static void test_floor(skiatest::Reporter* reporter) { @@ -149,7 +187,7 @@ if (r0 != r1 && r0 != r2) { SkDebugf("src:%d dst:%d a:%d result:%d float:%g\n", - src, dst, a, r0, f); + src, dst, a, r0, f); failed += 1; } if (r0 > 255) { @@ -175,11 +213,8 @@ float diff = sk_float_abs(f1 - r1); diff = sk_float_abs(diff - 0.5f); if (diff > (1 / 255.f)) { -#ifdef SK_DEBUG - SkDebugf("src:%d dst:%d a:%d result:%d float:%g\n", - src, dst, a, r0, f1); -#endif - REPORTER_ASSERT(reporter, false); + ERRORF(reporter, "src:%d dst:%d a:%d " + "result:%d float:%g\n", src, dst, a, r0, f1); } } } @@ -207,34 +242,29 @@ /* returns true if a == b as resulting from (int)x. Since it is undefined what to do if the float exceeds 2^32-1, we check for that explicitly. */ -static bool equal_float_native_skia(float x, uint32_t ni, uint32_t si) { - if (!(x == x)) { // NAN - return ((int32_t)si) == SK_MaxS32 || ((int32_t)si) == SK_MinS32; +static bool equal_float_native_skia(float x, int32_t ni, int32_t si) { + // When the float is out of integer range (NaN, above, below), + // the C cast is undefined, but Skia's methods should have clamped. + if (!(x == x)) { // NaN + return si == SK_MaxS32 || si == SK_MinS32; } - // for out of range, C is undefined, but skia always should return NaN32 if (x > SK_MaxS32) { - return ((int32_t)si) == SK_MaxS32; + return si == SK_MaxS32; } - if (x < -SK_MaxS32) { - return ((int32_t)si) == SK_MinS32; + if (x < SK_MinS32) { + return si == SK_MinS32; } return si == ni; } static void assert_float_equal(skiatest::Reporter* reporter, const char op[], - float x, uint32_t ni, uint32_t si) { + float x, int32_t ni, int32_t si) { if (!equal_float_native_skia(x, ni, si)) { ERRORF(reporter, "%s float %g bits %x native %x skia %x\n", op, x, SkFloat2Bits(x), ni, si); } } -static void test_float_cast(skiatest::Reporter* reporter, float x) { - int ix = (int)x; - int iix = SkFloatToIntCast(x); - assert_float_equal(reporter, "cast", x, ix, iix); -} - static void test_float_floor(skiatest::Reporter* reporter, float x) { int ix = (int)floor(x); int iix = SkFloatToIntFloor(x); @@ -255,23 +285,17 @@ } static void test_float_conversions(skiatest::Reporter* reporter, float x) { - test_float_cast(reporter, x); test_float_floor(reporter, x); test_float_round(reporter, x); test_float_ceil(reporter, x); } -static void test_int2float(skiatest::Reporter* reporter, int ival) { - float x0 = (float)ival; - float x1 = SkIntToFloatCast(ival); - REPORTER_ASSERT(reporter, x0 == x1); -} - static void unittest_fastfloat(skiatest::Reporter* reporter) { SkRandom rand; size_t i; static const float gFloats[] = { + 0.f/0.f, -0.f/0.f, 1.f/0.f, -1.f/0.f, 0.f, 1.f, 0.5f, 0.499999f, 0.5000001f, 1.f/3, 0.000000001f, 1000000000.f, // doesn't overflow 0.0000000001f, 10000000000.f // does overflow @@ -287,17 +311,6 @@ float x = nextFloat(rand); test_float_conversions(reporter, x); } - - test_int2float(reporter, 0); - test_int2float(reporter, 1); - test_int2float(reporter, -1); - for (i = 0; i < 100000; i++) { - // for now only test ints that are 24bits or less, since we don't - // round (down) large ints the same as IEEE... - int ival = rand.nextU() & 0xFFFFFF; - test_int2float(reporter, ival); - test_int2float(reporter, -ival); - } } } @@ -326,6 +339,96 @@ REPORTER_ASSERT(reporter, SkScalarIsFinite(0)); } +static void unittest_half(skiatest::Reporter* reporter) { + static const float gFloats[] = { + 0.f, 1.f, 0.5f, 0.499999f, 0.5000001f, 1.f/3, + -0.f, -1.f, -0.5f, -0.499999f, -0.5000001f, -1.f/3 + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gFloats); ++i) { + SkHalf h = SkFloatToHalf(gFloats[i]); + float f = SkHalfToFloat(h); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f, gFloats[i])); + } + + // check some special values + union FloatUnion { + uint32_t fU; + float fF; + }; + + static const FloatUnion largestPositiveHalf = { ((142 << 23) | (1023 << 13)) }; + SkHalf h = SkFloatToHalf(largestPositiveHalf.fF); + float f = SkHalfToFloat(h); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f, largestPositiveHalf.fF)); + + static const FloatUnion largestNegativeHalf = { (1u << 31) | (142u << 23) | (1023u << 13) }; + h = SkFloatToHalf(largestNegativeHalf.fF); + f = SkHalfToFloat(h); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f, largestNegativeHalf.fF)); + + static const FloatUnion smallestPositiveHalf = { 102 << 23 }; + h = SkFloatToHalf(smallestPositiveHalf.fF); + f = SkHalfToFloat(h); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f, smallestPositiveHalf.fF)); + + static const FloatUnion overflowHalf = { ((143 << 23) | (1023 << 13)) }; + h = SkFloatToHalf(overflowHalf.fF); + f = SkHalfToFloat(h); + REPORTER_ASSERT(reporter, !SkScalarIsFinite(f) ); + + static const FloatUnion underflowHalf = { 101 << 23 }; + h = SkFloatToHalf(underflowHalf.fF); + f = SkHalfToFloat(h); + REPORTER_ASSERT(reporter, f == 0.0f ); + + static const FloatUnion inf32 = { 255 << 23 }; + h = SkFloatToHalf(inf32.fF); + f = SkHalfToFloat(h); + REPORTER_ASSERT(reporter, !SkScalarIsFinite(f) ); + + static const FloatUnion nan32 = { 255 << 23 | 1 }; + h = SkFloatToHalf(nan32.fF); + f = SkHalfToFloat(h); + REPORTER_ASSERT(reporter, SkScalarIsNaN(f) ); + +} + +template <typename RSqrtFn> +static void test_rsqrt(skiatest::Reporter* reporter, RSqrtFn rsqrt) { + const float maxRelativeError = 6.50196699e-4f; + + // test close to 0 up to 1 + float input = 0.000001f; + for (int i = 0; i < 1000; ++i) { + float exact = 1.0f/sk_float_sqrt(input); + float estimate = rsqrt(input); + float relativeError = sk_float_abs(exact - estimate)/exact; + REPORTER_ASSERT(reporter, relativeError <= maxRelativeError); + input += 0.001f; + } + + // test 1 to ~100 + input = 1.0f; + for (int i = 0; i < 1000; ++i) { + float exact = 1.0f/sk_float_sqrt(input); + float estimate = rsqrt(input); + float relativeError = sk_float_abs(exact - estimate)/exact; + REPORTER_ASSERT(reporter, relativeError <= maxRelativeError); + input += 0.01f; + } + + // test some big numbers + input = 1000000.0f; + for (int i = 0; i < 100; ++i) { + float exact = 1.0f/sk_float_sqrt(input); + float estimate = rsqrt(input); + float relativeError = sk_float_abs(exact - estimate)/exact; + REPORTER_ASSERT(reporter, relativeError <= maxRelativeError); + input += 754326.f; + } +} + static void test_muldiv255(skiatest::Reporter* reporter) { for (int a = 0; a <= 255; a++) { for (int b = 0; b <= 255; b++) { @@ -460,16 +563,27 @@ REPORTER_ASSERT(reporter, result == SK_Fixed1); result = SkFixedDiv(1, SK_Fixed1); REPORTER_ASSERT(reporter, result == 1); + result = SkFixedDiv(10 - 1, SK_Fixed1 * 3); + REPORTER_ASSERT(reporter, result == 3); + } + + { + REPORTER_ASSERT(reporter, (SkFixedRoundToFixed(-SK_Fixed1 * 10) >> 1) == -SK_Fixed1 * 5); + REPORTER_ASSERT(reporter, (SkFixedFloorToFixed(-SK_Fixed1 * 10) >> 1) == -SK_Fixed1 * 5); + REPORTER_ASSERT(reporter, (SkFixedCeilToFixed(-SK_Fixed1 * 10) >> 1) == -SK_Fixed1 * 5); } unittest_fastfloat(reporter); unittest_isfinite(reporter); + unittest_half(reporter); + test_rsqrt(reporter, sk_float_rsqrt); + test_rsqrt(reporter, sk_float_rsqrt_portable); for (i = 0; i < 10000; i++) { SkFixed numer = rand.nextS(); SkFixed denom = rand.nextS(); SkFixed result = SkFixedDiv(numer, denom); - int64_t check = ((int64_t)numer << 16) / denom; + int64_t check = SkLeftShift((int64_t)numer, 16) / denom; (void)SkCLZ(numer); (void)SkCLZ(denom); @@ -480,6 +594,9 @@ } else if (check < -SK_MaxS32) { check = SK_MinS32; } + if (result != (int32_t)check) { + ERRORF(reporter, "\nFixed Divide: %8x / %8x -> %8x %8x\n", numer, denom, result, check); + } REPORTER_ASSERT(reporter, result == (int32_t)check); } @@ -492,6 +609,7 @@ test_muldivround(reporter); test_clz(reporter); + test_quick_div(reporter); } template <typename T> struct PairRec { @@ -601,3 +719,41 @@ DEF_TEST(divmod_s64, r) { test_divmod<int64_t>(r); } + +static void test_nextsizepow2(skiatest::Reporter* r, size_t test, size_t expectedAns) { + size_t ans = GrNextSizePow2(test); + + REPORTER_ASSERT(r, ans == expectedAns); + //SkDebugf("0x%zx -> 0x%zx (0x%zx)\n", test, ans, expectedAns); +} + +DEF_TEST(GrNextSizePow2, reporter) { + constexpr int kNumSizeTBits = 8 * sizeof(size_t); + + size_t test = 0, expectedAns = 1; + + test_nextsizepow2(reporter, test, expectedAns); + + test = 1; expectedAns = 1; + + for (int i = 1; i < kNumSizeTBits; ++i) { + test_nextsizepow2(reporter, test, expectedAns); + + test++; + expectedAns <<= 1; + + test_nextsizepow2(reporter, test, expectedAns); + + test = expectedAns; + } + + // For the remaining three tests there is no higher power (of 2) + test = 0x1; + test <<= kNumSizeTBits-1; + test_nextsizepow2(reporter, test, test); + + test++; + test_nextsizepow2(reporter, test, test); + + test_nextsizepow2(reporter, SIZE_MAX, SIZE_MAX); +}
diff --git a/src/third_party/skia/tests/Matrix44Test.cpp b/src/third_party/skia/tests/Matrix44Test.cpp index 0bd4a8b..382cd21 100644 --- a/src/third_party/skia/tests/Matrix44Test.cpp +++ b/src/third_party/skia/tests/Matrix44Test.cpp
@@ -78,7 +78,7 @@ static void test_constructor(skiatest::Reporter* reporter) { // Allocate a matrix on the heap SkMatrix44* placeholderMatrix = new SkMatrix44(SkMatrix44::kUninitialized_Constructor); - SkAutoTDelete<SkMatrix44> deleteMe(placeholderMatrix); + std::unique_ptr<SkMatrix44> deleteMe(placeholderMatrix); for (int row = 0; row < 4; ++row) { for (int col = 0; col < 4; ++col) { @@ -104,6 +104,14 @@ REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix); REPORTER_ASSERT(reporter, testMatrix->isIdentity()); REPORTER_ASSERT(reporter, *testMatrix == SkMatrix44::I()); + + // Verify that that constructing from an SkMatrix initializes everything. + SkMatrix44 scaleMatrix(SkMatrix44::kUninitialized_Constructor); + scaleMatrix.setScale(3, 4, 5); + REPORTER_ASSERT(reporter, scaleMatrix.isScale()); + testMatrix = new(&scaleMatrix) SkMatrix44(SkMatrix::I()); + REPORTER_ASSERT(reporter, testMatrix->isIdentity()); + REPORTER_ASSERT(reporter, *testMatrix == SkMatrix44::I()); } static void test_translate(skiatest::Reporter* reporter) { @@ -192,8 +200,8 @@ SkMScalar dstA[4], dstB[4]; for (int i = 0; i < 4; ++i) { - dstA[i] = 123456789; - dstB[i] = 987654321; + dstA[i] = SkDoubleToMScalar(123456789); + dstB[i] = SkDoubleToMScalar(987654321); } mat.map2(src2, 1, dstA); @@ -360,15 +368,15 @@ 0, 0, 0, 1); SkMatrix44 scaleTranslation(SkMatrix44::kUninitialized_Constructor); - scaleTranslation.setScale(10, 100, 1000); + scaleTranslation.setScale(32, 128, 1024); scaleTranslation.preTranslate(2, 3, 4); scaleTranslation.invert(&inverse); inverse.asRowMajord(inverseData); assert16<double>(reporter, inverseData, - 0.1, 0, 0, -2, - 0, 0.01, 0, -3, - 0, 0, 0.001, -4, - 0, 0, 0, 1); + 0.03125, 0, 0, -2, + 0, 0.0078125, 0, -3, + 0, 0, 0.0009765625, -4, + 0, 0, 0, 1); SkMatrix44 rotation(SkMatrix44::kUninitialized_Constructor); rotation.setRotateDegreesAbout(0, 0, 1, 90); @@ -418,6 +426,31 @@ 0, 0, -1, 1}; expected.setRowMajord(expectedInverseAffineAndPerspective); REPORTER_ASSERT(reporter, nearly_equal(expected, inverse)); + + SkMatrix44 tinyScale(SkMatrix44::kIdentity_Constructor); + tinyScale.setDouble(0, 0, 1e-39); + REPORTER_ASSERT(reporter, tinyScale.getType() == SkMatrix44::kScale_Mask); + REPORTER_ASSERT(reporter, !tinyScale.invert(nullptr)); + REPORTER_ASSERT(reporter, !tinyScale.invert(&inverse)); + + SkMatrix44 tinyScaleTranslate(SkMatrix44::kIdentity_Constructor); + tinyScaleTranslate.setDouble(0, 0, 1e-38); + REPORTER_ASSERT(reporter, tinyScaleTranslate.invert(nullptr)); + tinyScaleTranslate.setDouble(0, 3, 10); + REPORTER_ASSERT( + reporter, tinyScaleTranslate.getType() == + (SkMatrix44::kScale_Mask | SkMatrix44::kTranslate_Mask)); + REPORTER_ASSERT(reporter, !tinyScaleTranslate.invert(nullptr)); + REPORTER_ASSERT(reporter, !tinyScaleTranslate.invert(&inverse)); + + SkMatrix44 tinyScalePerspective(SkMatrix44::kIdentity_Constructor); + tinyScalePerspective.setDouble(0, 0, 1e-39); + tinyScalePerspective.setDouble(3, 2, -1); + REPORTER_ASSERT(reporter, (tinyScalePerspective.getType() & + SkMatrix44::kPerspective_Mask) == + SkMatrix44::kPerspective_Mask); + REPORTER_ASSERT(reporter, !tinyScalePerspective.invert(nullptr)); + REPORTER_ASSERT(reporter, !tinyScalePerspective.invert(&inverse)); } static void test_transpose(skiatest::Reporter* reporter) { @@ -451,6 +484,23 @@ } } +static void test_set_3x3(skiatest::Reporter* r) { + static float vals[9] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, }; + + SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); + mat.set3x3RowMajorf(vals); + + REPORTER_ASSERT(r, 1.0f == mat.getFloat(0, 0)); + REPORTER_ASSERT(r, 2.0f == mat.getFloat(0, 1)); + REPORTER_ASSERT(r, 3.0f == mat.getFloat(0, 2)); + REPORTER_ASSERT(r, 4.0f == mat.getFloat(1, 0)); + REPORTER_ASSERT(r, 5.0f == mat.getFloat(1, 1)); + REPORTER_ASSERT(r, 6.0f == mat.getFloat(1, 2)); + REPORTER_ASSERT(r, 7.0f == mat.getFloat(2, 0)); + REPORTER_ASSERT(r, 8.0f == mat.getFloat(2, 1)); + REPORTER_ASSERT(r, 9.0f == mat.getFloat(2, 2)); +} + static void test_set_row_col_major(skiatest::Reporter* reporter) { SkMatrix44 a(SkMatrix44::kUninitialized_Constructor); SkMatrix44 b(SkMatrix44::kUninitialized_Constructor); @@ -524,32 +574,259 @@ static void test_has_perspective(skiatest::Reporter* reporter) { SkMatrix44 transform(SkMatrix44::kIdentity_Constructor); - transform.set(3, 2, -0.1); + transform.setDouble(3, 2, -0.1); REPORTER_ASSERT(reporter, transform.hasPerspective()); transform.reset(); REPORTER_ASSERT(reporter, !transform.hasPerspective()); - transform.set(3, 0, -1.0); + transform.setDouble(3, 0, -1.0); REPORTER_ASSERT(reporter, transform.hasPerspective()); transform.reset(); - transform.set(3, 1, -1.0); + transform.setDouble(3, 1, -1.0); REPORTER_ASSERT(reporter, transform.hasPerspective()); transform.reset(); - transform.set(3, 2, -0.3); + transform.setDouble(3, 2, -0.3); REPORTER_ASSERT(reporter, transform.hasPerspective()); transform.reset(); - transform.set(3, 3, 0.5); + transform.setDouble(3, 3, 0.5); REPORTER_ASSERT(reporter, transform.hasPerspective()); - + transform.reset(); - transform.set(3, 3, 0.0); + transform.setDouble(3, 3, 0.0); REPORTER_ASSERT(reporter, transform.hasPerspective()); } +static bool is_rectilinear (SkVector4& p1, SkVector4& p2, SkVector4& p3, SkVector4& p4) { + return (SkScalarNearlyEqual(p1.fData[0], p2.fData[0]) && + SkScalarNearlyEqual(p2.fData[1], p3.fData[1]) && + SkScalarNearlyEqual(p3.fData[0], p4.fData[0]) && + SkScalarNearlyEqual(p4.fData[1], p1.fData[1])) || + (SkScalarNearlyEqual(p1.fData[1], p2.fData[1]) && + SkScalarNearlyEqual(p2.fData[0], p3.fData[0]) && + SkScalarNearlyEqual(p3.fData[1], p4.fData[1]) && + SkScalarNearlyEqual(p4.fData[0], p1.fData[0])); +} + +static SkVector4 mul_with_persp_divide(const SkMatrix44& transform, const SkVector4& target) { + SkVector4 result = transform * target; + if (result.fData[3] != 0.0f && result.fData[3] != SK_Scalar1) { + float wInverse = SK_Scalar1 / result.fData[3]; + result.set(result.fData[0] * wInverse, + result.fData[1] * wInverse, + result.fData[2] * wInverse, + SK_Scalar1); + } + return result; +} + +static bool empirically_preserves_2d_axis_alignment(skiatest::Reporter* reporter, + const SkMatrix44& transform) { + SkVector4 p1(5.0f, 5.0f, 0.0f); + SkVector4 p2(10.0f, 5.0f, 0.0f); + SkVector4 p3(10.0f, 20.0f, 0.0f); + SkVector4 p4(5.0f, 20.0f, 0.0f); + + REPORTER_ASSERT(reporter, is_rectilinear(p1, p2, p3, p4)); + + p1 = mul_with_persp_divide(transform, p1); + p2 = mul_with_persp_divide(transform, p2); + p3 = mul_with_persp_divide(transform, p3); + p4 = mul_with_persp_divide(transform, p4); + + return is_rectilinear(p1, p2, p3, p4); +} + +static void test(bool expected, skiatest::Reporter* reporter, const SkMatrix44& transform) { + if (expected) { + REPORTER_ASSERT(reporter, empirically_preserves_2d_axis_alignment(reporter, transform)); + REPORTER_ASSERT(reporter, transform.preserves2dAxisAlignment()); + } else { + REPORTER_ASSERT(reporter, !empirically_preserves_2d_axis_alignment(reporter, transform)); + REPORTER_ASSERT(reporter, !transform.preserves2dAxisAlignment()); + } +} + +static void test_preserves_2d_axis_alignment(skiatest::Reporter* reporter) { + SkMatrix44 transform(SkMatrix44::kUninitialized_Constructor); + SkMatrix44 transform2(SkMatrix44::kUninitialized_Constructor); + + static const struct TestCase { + SkMScalar a; // row 1, column 1 + SkMScalar b; // row 1, column 2 + SkMScalar c; // row 2, column 1 + SkMScalar d; // row 2, column 2 + bool expected; + } test_cases[] = { + { 3.f, 0.f, + 0.f, 4.f, true }, // basic case + { 0.f, 4.f, + 3.f, 0.f, true }, // rotate by 90 + { 0.f, 0.f, + 0.f, 4.f, true }, // degenerate x + { 3.f, 0.f, + 0.f, 0.f, true }, // degenerate y + { 0.f, 0.f, + 3.f, 0.f, true }, // degenerate x + rotate by 90 + { 0.f, 4.f, + 0.f, 0.f, true }, // degenerate y + rotate by 90 + { 3.f, 4.f, + 0.f, 0.f, false }, + { 0.f, 0.f, + 3.f, 4.f, false }, + { 0.f, 3.f, + 0.f, 4.f, false }, + { 3.f, 0.f, + 4.f, 0.f, false }, + { 3.f, 4.f, + 5.f, 0.f, false }, + { 3.f, 4.f, + 0.f, 5.f, false }, + { 3.f, 0.f, + 4.f, 5.f, false }, + { 0.f, 3.f, + 4.f, 5.f, false }, + { 2.f, 3.f, + 4.f, 5.f, false }, + }; + + for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) { + const TestCase& value = test_cases[i]; + transform.setIdentity(); + transform.set(0, 0, value.a); + transform.set(0, 1, value.b); + transform.set(1, 0, value.c); + transform.set(1, 1, value.d); + + test(value.expected, reporter, transform); + } + + // Try the same test cases again, but this time make sure that other matrix + // elements (except perspective) have entries, to test that they are ignored. + for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) { + const TestCase& value = test_cases[i]; + transform.setIdentity(); + transform.set(0, 0, value.a); + transform.set(0, 1, value.b); + transform.set(1, 0, value.c); + transform.set(1, 1, value.d); + + transform.set(0, 2, 1.f); + transform.set(0, 3, 2.f); + transform.set(1, 2, 3.f); + transform.set(1, 3, 4.f); + transform.set(2, 0, 5.f); + transform.set(2, 1, 6.f); + transform.set(2, 2, 7.f); + transform.set(2, 3, 8.f); + + test(value.expected, reporter, transform); + } + + // Try the same test cases again, but this time add perspective which is + // always assumed to not-preserve axis alignment. + for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) { + const TestCase& value = test_cases[i]; + transform.setIdentity(); + transform.set(0, 0, value.a); + transform.set(0, 1, value.b); + transform.set(1, 0, value.c); + transform.set(1, 1, value.d); + + transform.set(0, 2, 1.f); + transform.set(0, 3, 2.f); + transform.set(1, 2, 3.f); + transform.set(1, 3, 4.f); + transform.set(2, 0, 5.f); + transform.set(2, 1, 6.f); + transform.set(2, 2, 7.f); + transform.set(2, 3, 8.f); + transform.set(3, 0, 9.f); + transform.set(3, 1, 10.f); + transform.set(3, 2, 11.f); + transform.set(3, 3, 12.f); + + test(false, reporter, transform); + } + + // Try a few more practical situations to check precision + // Reuse TestCase (a, b, c, d) as (x, y, z, degrees) axis to rotate about. + TestCase rotation_tests[] = { + { 0.0, 0.0, 1.0, 90.0, true }, + { 0.0, 0.0, 1.0, 180.0, true }, + { 0.0, 0.0, 1.0, 270.0, true }, + { 0.0, 1.0, 0.0, 90.0, true }, + { 1.0, 0.0, 0.0, 90.0, true }, + { 0.0, 0.0, 1.0, 45.0, false }, + // In 3d these next two are non-preserving, but we're testing in 2d after + // orthographic projection, where they are. + { 0.0, 1.0, 0.0, 45.0, true }, + { 1.0, 0.0, 0.0, 45.0, true }, + }; + + for (size_t i = 0; i < sizeof(rotation_tests)/sizeof(TestCase); ++i) { + const TestCase& value = rotation_tests[i]; + transform.setRotateDegreesAbout(value.a, value.b, value.c, value.d); + test(value.expected, reporter, transform); + } + + static const struct DoubleRotationCase { + SkMScalar x1; + SkMScalar y1; + SkMScalar z1; + SkMScalar degrees1; + SkMScalar x2; + SkMScalar y2; + SkMScalar z2; + SkMScalar degrees2; + bool expected; + } double_rotation_tests[] = { + { 0.0, 0.0, 1.0, 90.0, 0.0, 1.0, 0.0, 90.0, true }, + { 0.0, 0.0, 1.0, 90.0, 1.0, 0.0, 0.0, 90.0, true }, + { 0.0, 1.0, 0.0, 90.0, 0.0, 0.0, 1.0, 90.0, true }, + }; + + for (size_t i = 0; i < sizeof(double_rotation_tests)/sizeof(DoubleRotationCase); ++i) { + const DoubleRotationCase& value = double_rotation_tests[i]; + transform.setRotateDegreesAbout(value.x1, value.y1, value.z1, value.degrees1); + transform2.setRotateDegreesAbout(value.x2, value.y2, value.z2, value.degrees2); + transform.postConcat(transform2); + test(value.expected, reporter, transform); + } + + // Perspective cases. + transform.setIdentity(); + transform.setDouble(3, 2, -0.1); // Perspective depth 10 + transform2.setRotateDegreesAbout(0.0, 1.0, 0.0, 45.0); + transform.preConcat(transform2); + test(false, reporter, transform); + + transform.setIdentity(); + transform.setDouble(3, 2, -0.1); // Perspective depth 10 + transform2.setRotateDegreesAbout(0.0, 0.0, 1.0, 90.0); + transform.preConcat(transform2); + test(true, reporter, transform); +} + +// just want to exercise the various converters for MScalar +static void test_toint(skiatest::Reporter* reporter) { + SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); + mat.setScale(3, 3, 3); + + SkMScalar sum = SkMScalarFloor(mat.get(0, 0)) + + SkMScalarRound(mat.get(1, 0)) + + SkMScalarCeil(mat.get(2, 0)); + int isum = SkMScalarFloorToInt(mat.get(0, 1)) + + SkMScalarRoundToInt(mat.get(1, 2)) + + SkMScalarCeilToInt(mat.get(2, 3)); + REPORTER_ASSERT(reporter, sum >= 0); + REPORTER_ASSERT(reporter, isum >= 0); + REPORTER_ASSERT(reporter, static_cast<SkMScalar>(isum) == SkIntToMScalar(isum)); +} + DEF_TEST(Matrix44, reporter) { SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor); @@ -575,7 +852,7 @@ mat.setScale(3, 3, 3); rot.setRotateDegreesAbout(0, 0, -1, 90); mat.postConcat(rot); - REPORTER_ASSERT(reporter, mat.invert(NULL)); + REPORTER_ASSERT(reporter, mat.invert(nullptr)); mat.invert(&inverse); iden1.setConcat(mat, inverse); REPORTER_ASSERT(reporter, is_identity(iden1)); @@ -584,22 +861,27 @@ // test tiny-valued matrix inverse mat.reset(); - mat.setScale(1.0e-12, 1.0e-12, 1.0e-12); + auto v = SkDoubleToMScalar(1.0e-12); + mat.setScale(v,v,v); rot.setRotateDegreesAbout(0, 0, -1, 90); mat.postConcat(rot); - mat.postTranslate(1.0e-12, 1.0e-12, 1.0e-12); - REPORTER_ASSERT(reporter, mat.invert(NULL)); + mat.postTranslate(v,v,v); + REPORTER_ASSERT(reporter, mat.invert(nullptr)); mat.invert(&inverse); iden1.setConcat(mat, inverse); REPORTER_ASSERT(reporter, is_identity(iden1)); // test mixed-valued matrix inverse mat.reset(); - mat.setScale(1.0e-10, 3.0, 1.0e+10); + mat.setScale(SkDoubleToMScalar(1.0e-2), + SkDoubleToMScalar(3.0), + SkDoubleToMScalar(1.0e+2)); rot.setRotateDegreesAbout(0, 0, -1, 90); mat.postConcat(rot); - mat.postTranslate(1.0e+10, 3.0, 1.0e-10); - REPORTER_ASSERT(reporter, mat.invert(NULL)); + mat.postTranslate(SkDoubleToMScalar(1.0e+2), + SkDoubleToMScalar(3.0), + SkDoubleToMScalar(1.0e-2)); + REPORTER_ASSERT(reporter, mat.invert(nullptr)); mat.invert(&inverse); iden1.setConcat(mat, inverse); REPORTER_ASSERT(reporter, is_identity(iden1)); @@ -607,7 +889,7 @@ // test degenerate matrix mat.reset(); mat.set3x3(1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0); - REPORTER_ASSERT(reporter, !mat.invert(NULL)); + REPORTER_ASSERT(reporter, !mat.invert(nullptr)); // test rol/col Major getters { @@ -651,9 +933,12 @@ test_transpose(reporter); test_get_set_double(reporter); test_set_row_col_major(reporter); + test_set_3x3(reporter); test_translate(reporter); test_scale(reporter); test_map2(reporter); test_3x3_conversion(reporter); test_has_perspective(reporter); + test_preserves_2d_axis_alignment(reporter); + test_toint(reporter); }
diff --git a/src/third_party/skia/tests/MatrixClipCollapseTest.cpp b/src/third_party/skia/tests/MatrixClipCollapseTest.cpp index c994027..de6af36 100644 --- a/src/third_party/skia/tests/MatrixClipCollapseTest.cpp +++ b/src/third_party/skia/tests/MatrixClipCollapseTest.cpp
@@ -192,8 +192,8 @@ } static void add_clip(ClipType clip, MatType mat, SkTDArray<DrawType>* expected) { - if (NULL == expected) { - // expected is NULL if this clip will be fused into later clips + if (nullptr == expected) { + // expected is nullptr if this clip will be fused into later clips return; } @@ -255,8 +255,8 @@ } static void add_mat(MatType mat, SkTDArray<DrawType>* expected) { - if (NULL == expected) { - // expected is NULL if this matrix call will be fused into later ones + if (nullptr == expected) { + // expected is nullptr if this matrix call will be fused into later ones return; } @@ -475,7 +475,7 @@ (*emitMC)(canvas, mat, clip, kSaveLayer_DrawOpType, expected, accumulatedClips+1); *expected->append() = SAVE_LAYER; // TODO: widen testing to exercise saveLayer's parameters - canvas->saveLayer(NULL, NULL); + canvas->saveLayer(nullptr, nullptr); if (needsSaveRestore) { *expected->append() = SAVE; } @@ -513,14 +513,14 @@ (*emitMC)(canvas, mat, clip, kSaveLayer_DrawOpType, expected, accumulatedClips+1); *expected->append() = SAVE_LAYER; // TODO: widen testing to exercise saveLayer's parameters - canvas->saveLayer(NULL, NULL); + canvas->saveLayer(nullptr, nullptr); (*emitMC)(canvas, mat, clip, kSaveLayer_DrawOpType, expected, 1); if (kNone_MatType != mat || kNone_ClipType != clip) { *expected->append() = SAVE; } *expected->append() = SAVE_LAYER; // TODO: widen testing to exercise saveLayer's parameters - canvas->saveLayer(NULL, NULL); + canvas->saveLayer(nullptr, nullptr); if (needsSaveRestore) { *expected->append() = SAVE; } @@ -569,11 +569,11 @@ PFEmitMC emitMC, MatType mat, ClipType clip, PFEmitBody emitBody, DrawOpType draw, SkTDArray<DrawType>* expected) { - (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these get fused into later ops + (*emitMC)(canvas, mat, clip, draw, nullptr, 0); // these get fused into later ops canvas->save(); (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1); canvas->restore(); - (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these will get removed + (*emitMC)(canvas, mat, clip, draw, nullptr, 0); // these will get removed } // Emit: @@ -592,14 +592,14 @@ PFEmitMC emitMC, MatType mat, ClipType clip, PFEmitBody emitBody, DrawOpType draw, SkTDArray<DrawType>* expected) { - (*emitMC)(canvas, mat, clip, draw, NULL, 1); // these will get fused into later ops + (*emitMC)(canvas, mat, clip, draw, nullptr, 1); // these will get fused into later ops canvas->save(); (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1); canvas->restore(); canvas->save(); (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1); canvas->restore(); - (*emitMC)(canvas, mat, clip, draw, NULL, 1); // these will get removed + (*emitMC)(canvas, mat, clip, draw, nullptr, 1); // these will get removed } // Emit: @@ -621,17 +621,17 @@ PFEmitMC emitMC, MatType mat, ClipType clip, PFEmitBody emitBody, DrawOpType draw, SkTDArray<DrawType>* expected) { - (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these will get fused into later ops + (*emitMC)(canvas, mat, clip, draw, nullptr, 0); // these will get fused into later ops canvas->save(); (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1); canvas->restore(); canvas->save(); - (*emitMC)(canvas, mat, clip, draw, NULL, 1); // these will get fused into later ops + (*emitMC)(canvas, mat, clip, draw, nullptr, 1); // these will get fused into later ops canvas->save(); (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 2); canvas->restore(); canvas->restore(); - (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these will get removed + (*emitMC)(canvas, mat, clip, draw, nullptr, 0); // these will get removed } //////////////////////////////////////////////////////////////////////////////
diff --git a/src/third_party/skia/tests/MatrixTest.cpp b/src/third_party/skia/tests/MatrixTest.cpp index fc7ac42..da2ae35 100644 --- a/src/third_party/skia/tests/MatrixTest.cpp +++ b/src/third_party/skia/tests/MatrixTest.cpp
@@ -42,7 +42,7 @@ if (0 == aVal && 0 == bVal && aValI != bValI) { foundZeroSignDiff = true; } else { - REPORTER_ASSERT(reporter, aVal == bVal && aValI == aValI); + REPORTER_ASSERT(reporter, aVal == bVal && aValI == bValI); } } REPORTER_ASSERT(reporter, foundZeroSignDiff); @@ -71,6 +71,45 @@ return nearly_equal(m, identity); } +static void assert9(skiatest::Reporter* reporter, const SkMatrix& m, + SkScalar a, SkScalar b, SkScalar c, + SkScalar d, SkScalar e, SkScalar f, + SkScalar g, SkScalar h, SkScalar i) { + SkScalar buffer[9]; + m.get9(buffer); + REPORTER_ASSERT(reporter, buffer[0] == a); + REPORTER_ASSERT(reporter, buffer[1] == b); + REPORTER_ASSERT(reporter, buffer[2] == c); + REPORTER_ASSERT(reporter, buffer[3] == d); + REPORTER_ASSERT(reporter, buffer[4] == e); + REPORTER_ASSERT(reporter, buffer[5] == f); + REPORTER_ASSERT(reporter, buffer[6] == g); + REPORTER_ASSERT(reporter, buffer[7] == h); + REPORTER_ASSERT(reporter, buffer[8] == i); +} + +static void test_set9(skiatest::Reporter* reporter) { + + SkMatrix m; + m.reset(); + assert9(reporter, m, 1, 0, 0, 0, 1, 0, 0, 0, 1); + + m.setScale(2, 3); + assert9(reporter, m, 2, 0, 0, 0, 3, 0, 0, 0, 1); + + m.postTranslate(4, 5); + assert9(reporter, m, 2, 0, 4, 0, 3, 5, 0, 0, 1); + + SkScalar buffer[9]; + sk_bzero(buffer, sizeof(buffer)); + buffer[SkMatrix::kMScaleX] = 1; + buffer[SkMatrix::kMScaleY] = 1; + buffer[SkMatrix::kMPersp2] = 1; + REPORTER_ASSERT(reporter, !m.isIdentity()); + m.set9(buffer); + REPORTER_ASSERT(reporter, m.isIdentity()); +} + static void test_matrix_recttorect(skiatest::Reporter* reporter) { SkRect src, dst; SkMatrix matrix; @@ -103,7 +142,7 @@ // add 100 in case we have a bug, I don't want to kill my stack in the test static const size_t kBufferSize = SkMatrix::kMaxFlattenSize + 100; char buffer[kBufferSize]; - size_t size1 = m.writeToMemory(NULL); + size_t size1 = m.writeToMemory(nullptr); size_t size2 = m.writeToMemory(buffer); REPORTER_ASSERT(reporter, size1 == size2); REPORTER_ASSERT(reporter, size1 <= SkMatrix::kMaxFlattenSize); @@ -163,18 +202,31 @@ SkMatrix perspX; perspX.reset(); - perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000)); + perspX.setPerspX(SK_Scalar1 / 1000); REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMinScale()); REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxScale()); - // Verify that getMinMaxScales() doesn't update the scales array on failure. - scales[0] = -5; - scales[1] = -5; success = perspX.getMinMaxScales(scales); - REPORTER_ASSERT(reporter, !success && -5 * SK_Scalar1 == scales[0] && -5 * SK_Scalar1 == scales[1]); + REPORTER_ASSERT(reporter, !success); + + // skbug.com/4718 + SkMatrix big; + big.setAll(2.39394089e+36f, 8.85347779e+36f, 9.26526204e+36f, + 3.9159619e+36f, 1.44823453e+37f, 1.51559342e+37f, + 0.f, 0.f, 1.f); + success = big.getMinMaxScales(scales); + REPORTER_ASSERT(reporter, !success); + + // skbug.com/4718 + SkMatrix givingNegativeNearlyZeros; + givingNegativeNearlyZeros.setAll(0.00436534f, 0.114138f, 0.37141f, + 0.00358857f, 0.0936228f, -0.0174198f, + 0.f, 0.f, 1.f); + success = givingNegativeNearlyZeros.getMinMaxScales(scales); + REPORTER_ASSERT(reporter, success && 0 == scales[0]); SkMatrix perspY; perspY.reset(); - perspY.setPerspY(SkScalarToPersp(-SK_Scalar1 / 500)); + perspY.setPerspY(-SK_Scalar1 / 500); REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMinScale()); REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxScale()); scales[0] = -5; @@ -187,8 +239,8 @@ SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)]; for (size_t i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) { mats[i] = baseMats[i]; - bool invertable = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]); - REPORTER_ASSERT(reporter, invertable); + bool invertible = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]); + REPORTER_ASSERT(reporter, invertible); } SkRandom rand; for (int m = 0; m < 1000; ++m) { @@ -231,8 +283,8 @@ mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors)); for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) { SkScalar d = vectors[i].length(); - REPORTER_ASSERT(reporter, SkScalarDiv(d, maxScale) < gVectorScaleTol); - REPORTER_ASSERT(reporter, SkScalarDiv(minScale, d) < gVectorScaleTol); + REPORTER_ASSERT(reporter, d / maxScale < gVectorScaleTol); + REPORTER_ASSERT(reporter, minScale / d < gVectorScaleTol); if (max < d) { max = d; } @@ -240,8 +292,8 @@ min = d; } } - REPORTER_ASSERT(reporter, SkScalarDiv(max, maxScale) >= gCloseScaleTol); - REPORTER_ASSERT(reporter, SkScalarDiv(minScale, min) >= gCloseScaleTol); + REPORTER_ASSERT(reporter, max / maxScale >= gCloseScaleTol); + REPORTER_ASSERT(reporter, minScale / min >= gCloseScaleTol); } } @@ -319,13 +371,13 @@ // perspective x mat.reset(); - mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2)); + mat.setPerspX(SK_Scalar1 / 2); REPORTER_ASSERT(reporter, !mat.isSimilarity()); REPORTER_ASSERT(reporter, !mat.preservesRightAngles()); // perspective y mat.reset(); - mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2)); + mat.setPerspY(SK_Scalar1 / 2); REPORTER_ASSERT(reporter, !mat.isSimilarity()); REPORTER_ASSERT(reporter, !mat.preservesRightAngles()); @@ -458,7 +510,7 @@ REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2)); REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2)); // make sure it doesn't crash if we pass in NULLs - REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, NULL, NULL, NULL)); + REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, nullptr, nullptr, nullptr)); // rotation only mat.setRotate(kRotation0); @@ -625,8 +677,14 @@ const float kRotation1 = -50.f; const float kScale0 = 5000.f; +#if defined(GOOGLE3) + // Stack frame size is limited in GOOGLE3. + const int kTripleCount = 100; + const int kMatrixCount = 100; +#else const int kTripleCount = 1000; const int kMatrixCount = 1000; +#endif SkRandom rand; SkScalar randTriples[3*kTripleCount]; @@ -672,7 +730,7 @@ // doesn't crash with null dst, src, count == 0 { - mats[0].mapHomogeneousPoints(NULL, NULL, 0); + mats[0].mapHomogeneousPoints(nullptr, nullptr, 0); } // uniform scale of point @@ -732,6 +790,34 @@ } +static bool check_decompScale(const SkMatrix& matrix) { + SkSize scale; + SkMatrix remaining; + + if (!matrix.decomposeScale(&scale, &remaining)) { + return false; + } + if (scale.width() <= 0 || scale.height() <= 0) { + return false; + } + remaining.preScale(scale.width(), scale.height()); + return nearly_equal(matrix, remaining); +} + +static void test_decompScale(skiatest::Reporter* reporter) { + SkMatrix m; + + m.reset(); + REPORTER_ASSERT(reporter, check_decompScale(m)); + m.setScale(2, 3); + REPORTER_ASSERT(reporter, check_decompScale(m)); + m.setRotate(35, 0, 0); + REPORTER_ASSERT(reporter, check_decompScale(m)); + + m.setScale(1, 0); + REPORTER_ASSERT(reporter, !check_decompScale(m)); +} + DEF_TEST(Matrix, reporter) { SkMatrix mat, inverse, iden1, iden2; @@ -755,7 +841,7 @@ mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0); mat.postRotate(SkIntToScalar(25)); - REPORTER_ASSERT(reporter, mat.invert(NULL)); + REPORTER_ASSERT(reporter, mat.invert(nullptr)); REPORTER_ASSERT(reporter, mat.invert(&inverse)); iden1.setConcat(mat, inverse); REPORTER_ASSERT(reporter, is_identity(iden1)); @@ -765,10 +851,17 @@ test_flatten(reporter, iden2); mat.setScale(0, SK_Scalar1); - REPORTER_ASSERT(reporter, !mat.invert(NULL)); + REPORTER_ASSERT(reporter, !mat.invert(nullptr)); REPORTER_ASSERT(reporter, !mat.invert(&inverse)); mat.setScale(SK_Scalar1, 0); - REPORTER_ASSERT(reporter, !mat.invert(NULL)); + REPORTER_ASSERT(reporter, !mat.invert(nullptr)); + REPORTER_ASSERT(reporter, !mat.invert(&inverse)); + + // Inverting this matrix results in a non-finite matrix + mat.setAll(0.0f, 1.0f, 2.0f, + 0.0f, 1.0f, -3.40277175e+38f, + 1.00003040f, 1.0f, 0.0f); + REPORTER_ASSERT(reporter, !mat.invert(nullptr)); REPORTER_ASSERT(reporter, !mat.invert(&inverse)); // rectStaysRect test @@ -828,7 +921,7 @@ REPORTER_ASSERT(reporter, affineEqual(TransY)); #undef affineEqual - mat.set(SkMatrix::kMPersp1, SkScalarToPersp(SK_Scalar1 / 2)); + mat.set(SkMatrix::kMPersp1, SK_Scalar1 / 2); REPORTER_ASSERT(reporter, !mat.asAffine(affine)); SkMatrix mat2; @@ -849,6 +942,14 @@ test_matrix_recttorect(reporter); test_matrix_decomposition(reporter); test_matrix_homogeneous(reporter); + test_set9(reporter); + + test_decompScale(reporter); + + mat.setScaleTranslate(2, 3, 1, 4); + mat2.setScale(2, 3); + mat2.postTranslate(1, 4); + REPORTER_ASSERT(reporter, mat == mat2); } DEF_TEST(Matrix_Concat, r) { @@ -863,3 +964,29 @@ REPORTER_ASSERT(r, expected == SkMatrix::Concat(a, b)); } + +// Test that all variants of maprect are correct. +DEF_TEST(Matrix_maprects, r) { + const SkScalar scale = 1000; + + SkMatrix mat; + mat.setScale(2, 3); + mat.postTranslate(1, 4); + + SkRandom rand; + for (int i = 0; i < 10000; ++i) { + SkRect src = SkRect::MakeLTRB(rand.nextSScalar1() * scale, + rand.nextSScalar1() * scale, + rand.nextSScalar1() * scale, + rand.nextSScalar1() * scale); + SkRect dst[3]; + + mat.mapPoints((SkPoint*)&dst[0].fLeft, (SkPoint*)&src.fLeft, 2); + dst[0].sort(); + mat.mapRect(&dst[1], src); + mat.mapRectScaleTranslate(&dst[2], src); + + REPORTER_ASSERT(r, dst[0] == dst[1]); + REPORTER_ASSERT(r, dst[0] == dst[2]); + } +}
diff --git a/src/third_party/skia/tests/MemsetTest.cpp b/src/third_party/skia/tests/MemsetTest.cpp index ee6aaea..b68844c 100644 --- a/src/third_party/skia/tests/MemsetTest.cpp +++ b/src/third_party/skia/tests/MemsetTest.cpp
@@ -5,40 +5,10 @@ * found in the LICENSE file. */ -#include "SkChunkAlloc.h" +#include "SkRandom.h" #include "SkUtils.h" #include "Test.h" -static void test_chunkalloc(skiatest::Reporter* reporter) { - size_t min = 256; - SkChunkAlloc alloc(min); - - REPORTER_ASSERT(reporter, 0 == alloc.totalCapacity()); - REPORTER_ASSERT(reporter, 0 == alloc.totalUsed()); - REPORTER_ASSERT(reporter, 0 == alloc.blockCount()); - REPORTER_ASSERT(reporter, !alloc.contains(NULL)); - REPORTER_ASSERT(reporter, !alloc.contains(reporter)); - - alloc.reset(); - REPORTER_ASSERT(reporter, 0 == alloc.totalCapacity()); - REPORTER_ASSERT(reporter, 0 == alloc.totalUsed()); - REPORTER_ASSERT(reporter, 0 == alloc.blockCount()); - - size_t size = min >> 1; - void* ptr = alloc.allocThrow(size); - REPORTER_ASSERT(reporter, alloc.totalCapacity() >= size); - REPORTER_ASSERT(reporter, alloc.totalUsed() == size); - REPORTER_ASSERT(reporter, alloc.blockCount() > 0); - REPORTER_ASSERT(reporter, alloc.contains(ptr)); - - alloc.reset(); - REPORTER_ASSERT(reporter, !alloc.contains(ptr)); - REPORTER_ASSERT(reporter, 0 == alloc.totalCapacity()); - REPORTER_ASSERT(reporter, 0 == alloc.totalUsed()); -} - -/////////////////////////////////////////////////////////////////////////////// - static void set_zero(void* dst, size_t bytes) { char* ptr = (char*)dst; for (size_t i = 0; i < bytes; ++i) { @@ -54,24 +24,24 @@ #define VALUE16 0x1234 #define VALUE32 0x12345678 -static bool compare16(const uint16_t base[], uint16_t value, int count) { +static void compare16(skiatest::Reporter* r, const uint16_t base[], + uint16_t value, int count) { for (int i = 0; i < count; ++i) { if (base[i] != value) { - SkDebugf("[%d] expected %x found %x\n", i, value, base[i]); - return false; + ERRORF(r, "[%d] expected %x found %x\n", i, value, base[i]); + return; } } - return true; } -static bool compare32(const uint32_t base[], uint32_t value, int count) { +static void compare32(skiatest::Reporter* r, const uint32_t base[], + uint32_t value, int count) { for (int i = 0; i < count; ++i) { if (base[i] != value) { - SkDebugf("[%d] expected %x found %x\n", i, value, base[i]); - return false; + ERRORF(r, "[%d] expected %x found %x\n", i, value, base[i]); + return; } } - return true; } static void test_16(skiatest::Reporter* reporter) { @@ -84,10 +54,9 @@ uint16_t* base = &buffer[PAD + alignment]; sk_memset16(base, VALUE16, count); - REPORTER_ASSERT(reporter, - compare16(buffer, 0, PAD + alignment) && - compare16(base, VALUE16, count) && - compare16(base + count, 0, TOTAL - count - PAD - alignment)); + compare16(reporter, buffer, 0, PAD + alignment); + compare16(reporter, base, VALUE16, count); + compare16(reporter, base + count, 0, TOTAL - count - PAD - alignment); } } } @@ -102,10 +71,9 @@ uint32_t* base = &buffer[PAD + alignment]; sk_memset32(base, VALUE32, count); - REPORTER_ASSERT(reporter, - compare32(buffer, 0, PAD + alignment) && - compare32(base, VALUE32, count) && - compare32(base + count, 0, TOTAL - count - PAD - alignment)); + compare32(reporter, buffer, 0, PAD + alignment); + compare32(reporter, base, VALUE32, count); + compare32(reporter, base + count, 0, TOTAL - count - PAD - alignment); } } } @@ -118,6 +86,4 @@ DEF_TEST(Memset, reporter) { test_16(reporter); test_32(reporter); - - test_chunkalloc(reporter); }
diff --git a/src/third_party/skia/tests/MessageBusTest.cpp b/src/third_party/skia/tests/MessageBusTest.cpp index f7a02b2..163addf 100644 --- a/src/third_party/skia/tests/MessageBusTest.cpp +++ b/src/third_party/skia/tests/MessageBusTest.cpp
@@ -25,7 +25,7 @@ SkMessageBus<TestMessage>::Post(m2); // Make sure we got two. - SkTDArray<TestMessage> messages; + SkTArray<TestMessage> messages; inbox1.poll(&messages); REPORTER_ASSERT(r, 2 == messages.count()); REPORTER_ASSERT(r, 5 == messages[0].x);
diff --git a/src/third_party/skia/tests/MetaDataTest.cpp b/src/third_party/skia/tests/MetaDataTest.cpp index eb7eae9..2567398 100644 --- a/src/third_party/skia/tests/MetaDataTest.cpp +++ b/src/third_party/skia/tests/MetaDataTest.cpp
@@ -7,10 +7,11 @@ #include "SkMetaData.h" #include "Test.h" +#include "SkRefCnt.h" static void test_ptrs(skiatest::Reporter* reporter) { SkRefCnt ref; - REPORTER_ASSERT(reporter, 1 == ref.getRefCnt()); + REPORTER_ASSERT(reporter, ref.unique()); { SkMetaData md0, md1; @@ -19,19 +20,19 @@ md0.setRefCnt(name, &ref); REPORTER_ASSERT(reporter, md0.findRefCnt(name)); REPORTER_ASSERT(reporter, md0.hasRefCnt(name, &ref)); - REPORTER_ASSERT(reporter, 2 == ref.getRefCnt()); + REPORTER_ASSERT(reporter, !ref.unique()); md1 = md0; REPORTER_ASSERT(reporter, md1.findRefCnt(name)); REPORTER_ASSERT(reporter, md1.hasRefCnt(name, &ref)); - REPORTER_ASSERT(reporter, 3 == ref.getRefCnt()); + REPORTER_ASSERT(reporter, !ref.unique()); REPORTER_ASSERT(reporter, md0.removeRefCnt(name)); REPORTER_ASSERT(reporter, !md0.findRefCnt(name)); REPORTER_ASSERT(reporter, !md0.hasRefCnt(name, &ref)); - REPORTER_ASSERT(reporter, 2 == ref.getRefCnt()); + REPORTER_ASSERT(reporter, !ref.unique()); } - REPORTER_ASSERT(reporter, 1 == ref.getRefCnt()); + REPORTER_ASSERT(reporter, ref.unique()); } DEF_TEST(MetaData, reporter) { @@ -83,7 +84,7 @@ int loop = 0; int count; SkMetaData::Type t; - while ((name = iter.next(&t, &count)) != NULL) + while ((name = iter.next(&t, &count)) != nullptr) { int match = 0; for (unsigned i = 0; i < SK_ARRAY_COUNT(gElems); i++)
diff --git a/src/third_party/skia/tests/MiniDataTest.cpp b/src/third_party/skia/tests/MiniDataTest.cpp deleted file mode 100644 index cb656b3..0000000 --- a/src/third_party/skia/tests/MiniDataTest.cpp +++ /dev/null
@@ -1,16 +0,0 @@ -#include "SkMiniData.h" -#include "Test.h" - -DEF_TEST(MiniData, r) { - static const char* s = "abcdefghijklmnopqrstuvwxyz"; - - for (size_t len = 0; len <= 26; len++) { - SkMiniData md(s, len); - REPORTER_ASSERT(r, md.len() == len); - REPORTER_ASSERT(r, 0 == memcmp(md.data(), s, len)); - - SkMiniData copy(md); - REPORTER_ASSERT(r, copy.len() == len); - REPORTER_ASSERT(r, 0 == memcmp(copy.data(), s, len)); - } -}
diff --git a/src/third_party/skia/tests/MipMapTest.cpp b/src/third_party/skia/tests/MipMapTest.cpp index 33f4672..7e0a653 100644 --- a/src/third_party/skia/tests/MipMapTest.cpp +++ b/src/third_party/skia/tests/MipMapTest.cpp
@@ -10,12 +10,8 @@ #include "SkRandom.h" #include "Test.h" -static void make_bitmap(SkBitmap* bm, SkRandom& rand) { - // for now, Build needs a min size of 2, otherwise it will return NULL. - // should fix that to support 1 X N, where N > 1 to return non-null. - int w = 2 + rand.nextU() % 1000; - int h = 2 + rand.nextU() % 1000; - bm->allocN32Pixels(w, h); +static void make_bitmap(SkBitmap* bm, int width, int height) { + bm->allocN32Pixels(width, height); bm->eraseColor(SK_ColorWHITE); } @@ -24,11 +20,16 @@ SkRandom rand; for (int i = 0; i < 500; ++i) { - make_bitmap(&bm, rand); - SkAutoTUnref<SkMipMap> mm(SkMipMap::Build(bm)); + int width = 1 + rand.nextU() % 1000; + int height = 1 + rand.nextU() % 1000; + make_bitmap(&bm, width, height); + sk_sp<SkMipMap> mm(SkMipMap::Build(bm, SkDestinationSurfaceColorMode::kLegacy, nullptr)); - REPORTER_ASSERT(reporter, !mm->extractLevel(SK_Scalar1, NULL)); - REPORTER_ASSERT(reporter, !mm->extractLevel(SK_Scalar1 * 2, NULL)); + REPORTER_ASSERT(reporter, mm->countLevels() == SkMipMap::ComputeLevelCount(width, height)); + REPORTER_ASSERT(reporter, !mm->extractLevel(SkSize::Make(SK_Scalar1, SK_Scalar1), + nullptr)); + REPORTER_ASSERT(reporter, !mm->extractLevel(SkSize::Make(SK_Scalar1 * 2, SK_Scalar1 * 2), + nullptr)); SkMipMap::Level prevLevel; sk_bzero(&prevLevel, sizeof(prevLevel)); @@ -38,18 +39,166 @@ scale = scale * 2 / 3; SkMipMap::Level level; - if (mm->extractLevel(scale, &level)) { - REPORTER_ASSERT(reporter, level.fPixels); - REPORTER_ASSERT(reporter, level.fWidth > 0); - REPORTER_ASSERT(reporter, level.fHeight > 0); - REPORTER_ASSERT(reporter, level.fRowBytes >= level.fWidth * 4); + if (mm->extractLevel(SkSize::Make(scale, scale), &level)) { + REPORTER_ASSERT(reporter, level.fPixmap.addr()); + REPORTER_ASSERT(reporter, level.fPixmap.width() > 0); + REPORTER_ASSERT(reporter, level.fPixmap.height() > 0); + REPORTER_ASSERT(reporter, (int)level.fPixmap.rowBytes() >= level.fPixmap.width() * 4); - if (prevLevel.fPixels) { - REPORTER_ASSERT(reporter, level.fWidth <= prevLevel.fWidth); - REPORTER_ASSERT(reporter, level.fHeight <= prevLevel.fHeight); + if (prevLevel.fPixmap.addr()) { + REPORTER_ASSERT(reporter, level.fPixmap.width() <= prevLevel.fPixmap.width()); + REPORTER_ASSERT(reporter, level.fPixmap.height() <= prevLevel.fPixmap.height()); } prevLevel = level; } } } } + +static void test_mipmap_generation(int width, int height, int expectedMipLevelCount, + skiatest::Reporter* reporter) { + SkBitmap bm; + bm.allocN32Pixels(width, height); + bm.eraseColor(SK_ColorWHITE); + sk_sp<SkMipMap> mm(SkMipMap::Build(bm, SkDestinationSurfaceColorMode::kLegacy, nullptr)); + + const int mipLevelCount = mm->countLevels(); + REPORTER_ASSERT(reporter, mipLevelCount == expectedMipLevelCount); + REPORTER_ASSERT(reporter, mipLevelCount == SkMipMap::ComputeLevelCount(width, height)); + for (int i = 0; i < mipLevelCount; ++i) { + SkMipMap::Level level; + REPORTER_ASSERT(reporter, mm->getLevel(i, &level)); + // Make sure the mipmaps contain valid data and that the sizes are correct + REPORTER_ASSERT(reporter, level.fPixmap.addr()); + SkISize size = SkMipMap::ComputeLevelSize(width, height, i); + REPORTER_ASSERT(reporter, level.fPixmap.width() == size.width()); + REPORTER_ASSERT(reporter, level.fPixmap.height() == size.height()); + + // + 1 because SkMipMap does not include the base mipmap level. + int twoToTheMipLevel = 1 << (i + 1); + int currentWidth = width / twoToTheMipLevel; + int currentHeight = height / twoToTheMipLevel; + REPORTER_ASSERT(reporter, level.fPixmap.width() == currentWidth); + REPORTER_ASSERT(reporter, level.fPixmap.height() == currentHeight); + } +} + +DEF_TEST(MipMap_DirectLevelAccess, reporter) { + // create mipmap with invalid size + { + // SkMipMap current requires the dimensions be greater than 2x2 + SkBitmap bm; + bm.allocN32Pixels(1, 1); + bm.eraseColor(SK_ColorWHITE); + sk_sp<SkMipMap> mm(SkMipMap::Build(bm, SkDestinationSurfaceColorMode::kLegacy, nullptr)); + + REPORTER_ASSERT(reporter, mm == nullptr); + } + + // check small mipmap's count and levels + // There should be 5 mipmap levels generated: + // 16x16, 8x8, 4x4, 2x2, 1x1 + test_mipmap_generation(32, 32, 5, reporter); + + // check large mipmap's count and levels + // There should be 9 mipmap levels generated: + // 500x500, 250x250, 125x125, 62x62, 31x31, 15x15, 7x7, 3x3, 1x1 + test_mipmap_generation(1000, 1000, 9, reporter); +} + +struct LevelCountScenario { + int fWidth; + int fHeight; + int fExpectedLevelCount; +}; + +DEF_TEST(MipMap_ComputeLevelCount, reporter) { + const LevelCountScenario tests[] = { + // Test mipmaps with negative sizes + {-100, 100, 0}, + {100, -100, 0}, + {-100, -100, 0}, + + // Test mipmaps with 0, 1, 2 as dimensions + // (SkMipMap::Build requires a min size of 1) + // + // 0 + {0, 100, 0}, + {100, 0, 0}, + {0, 0, 0}, + // 1 + {1, 100, 6}, + {100, 1, 6}, + {1, 1, 0}, + // 2 + {2, 100, 6}, + {100, 2, 6}, + {2, 2, 1}, + + // Test a handful of boundaries such as 63x63 and 64x64 + {63, 63, 5}, + {64, 64, 6}, + {127, 127, 6}, + {128, 128, 7}, + {255, 255, 7}, + {256, 256, 8}, + + // Test different dimensions, such as 256x64 + {64, 129, 7}, + {255, 32, 7}, + {500, 1000, 9} + }; + + for (auto& currentTest : tests) { + int levelCount = SkMipMap::ComputeLevelCount(currentTest.fWidth, currentTest.fHeight); + REPORTER_ASSERT(reporter, currentTest.fExpectedLevelCount == levelCount); + } +} + +struct LevelSizeScenario { + int fBaseWidth; + int fBaseHeight; + int fLevel; + SkISize fExpectedMipMapLevelSize; +}; + +DEF_TEST(MipMap_ComputeLevelSize, reporter) { + const LevelSizeScenario tests[] = { + // Test mipmaps with negative sizes + {-100, 100, 0, SkISize::Make(0, 0)}, + {100, -100, 0, SkISize::Make(0, 0)}, + {-100, -100, 0, SkISize::Make(0, 0)}, + + // Test mipmaps with 0, 1, 2 as dimensions + // (SkMipMap::Build requires a min size of 1) + // + // 0 + {0, 100, 0, SkISize::Make(0, 0)}, + {100, 0, 0, SkISize::Make(0, 0)}, + {0, 0, 0, SkISize::Make(0, 0)}, + // 1 + + {1, 100, 0, SkISize::Make(1, 50)}, + {100, 1, 0, SkISize::Make(50, 1)}, + {1, 1, 0, SkISize::Make(0, 0)}, + // 2 + {2, 100, 0, SkISize::Make(1, 50)}, + {100, 2, 1, SkISize::Make(25, 1)}, + {2, 2, 0, SkISize::Make(1, 1)}, + + // Test a handful of cases + {63, 63, 2, SkISize::Make(7, 7)}, + {64, 64, 2, SkISize::Make(8, 8)}, + {127, 127, 2, SkISize::Make(15, 15)}, + {64, 129, 3, SkISize::Make(4, 8)}, + {255, 32, 6, SkISize::Make(1, 1)}, + {500, 1000, 1, SkISize::Make(125, 250)}, + }; + + for (auto& currentTest : tests) { + SkISize levelSize = SkMipMap::ComputeLevelSize(currentTest.fBaseWidth, + currentTest.fBaseHeight, + currentTest.fLevel); + REPORTER_ASSERT(reporter, currentTest.fExpectedMipMapLevelSize == levelSize); + } +}
diff --git a/src/third_party/skia/tests/NameAllocatorTest.cpp b/src/third_party/skia/tests/NameAllocatorTest.cpp deleted file mode 100644 index 86efdb2..0000000 --- a/src/third_party/skia/tests/NameAllocatorTest.cpp +++ /dev/null
@@ -1,169 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#if SK_SUPPORT_GPU - -#include "gl/GrGLNameAllocator.h" -#include "Test.h" - -//////////////////////////////////////////////////////////////////////////////// - -class NameLeakTest { - static const GrGLuint kFirstName = 101; - static const GrGLuint kRange = 1013; - -public: - NameLeakTest(skiatest::Reporter* reporter) - : fReporter(reporter), - fAllocator(kFirstName, kFirstName + kRange), - fAllocatedCount(0), - fRandomName(kFirstName + 4 * kRange / 7) { - memset(fAllocatedNames, 0, sizeof(fAllocatedNames)); - } - - bool run() { - if (!this->allocateAllRemaining()) { - return false; - } - - for (GrGLuint freeCount = 1; freeCount <= kRange; ++freeCount) { - if (!this->freeRandomNames(freeCount)) { - return false; - } - if (!this->allocateAllRemaining()) { - return false; - } - } - - return true; - } - -private: - bool isAllocated(GrGLuint name) const { - return fAllocatedNames[name - kFirstName]; - } - - void setAllocated(GrGLuint name, bool allocated) { - fAllocatedNames[name - kFirstName] = allocated; - } - - bool allocateAllRemaining() { - for (; fAllocatedCount < kRange; ++fAllocatedCount) { - GrGLuint name = fAllocator.allocateName(); - if (0 == name) { - ERRORF(fReporter, - "Name allocate failed, but there should still be %u free names", - kRange - fAllocatedCount); - return false; - } - if (name < kFirstName || name >= kFirstName + kRange) { - ERRORF(fReporter, - "Name allocate returned name %u outside its bounds [%u, %u)", - name, kFirstName, kFirstName + kRange); - return false; - } - if (this->isAllocated(name)) { - ERRORF(fReporter, "Name allocate returned name that is already allocated"); - return false; - } - - this->setAllocated(name, true); - } - - // Ensure it returns 0 once all the names are allocated. - GrGLuint name = fAllocator.allocateName(); - if (0 != name) { - ERRORF(fReporter, - "Name allocate did not fail when all names were already in use"); - return false; - } - - // Ensure every unique name is allocated. - for (GrGLuint i = 0; i < kRange; ++i) { - if (!this->isAllocated(kFirstName + i)) { - ERRORF(fReporter, "Not all unique names are allocated after allocateAllRemaining()"); - return false; - } - } - - return true; - } - - bool freeRandomNames(GrGLuint count) { - // The values a and c make up an LCG (pseudo-random generator). These - // values must satisfy the Hull-Dobell Theorem (with m=kRange): - // http://en.wikipedia.org/wiki/Linear_congruential_generator - // We use our own generator to guarantee it hits each unique value - // within kRange exactly once before repeating. - const GrGLuint seed = (count + fRandomName) / 2; - const GrGLuint a = seed * kRange + 1; - const GrGLuint c = (seed * 743) % kRange; - - for (GrGLuint i = 0; i < count; ++i) { - fRandomName = (a * fRandomName + c) % kRange; - const GrGLuint name = kFirstName + fRandomName; - if (!this->isAllocated(name)) { - ERRORF(fReporter, "Test bug: Should not free a not-allocated name at this point (%u)", i); - return false; - } - - fAllocator.free(name); - this->setAllocated(name, false); - --fAllocatedCount; - } - - return true; - } - - skiatest::Reporter* fReporter; - GrGLNameAllocator fAllocator; - bool fAllocatedNames[kRange]; - GrGLuint fAllocatedCount; - GrGLuint fRandomName; -}; - -DEF_GPUTEST(NameAllocator, reporter, factory) { - // Ensure no names are leaked or double-allocated during heavy usage. - { - NameLeakTest nameLeakTest(reporter); - nameLeakTest.run(); - } - - static const GrGLuint range = 32; - GrGLNameAllocator allocator(1, 1 + range); - for (GrGLuint i = 1; i <= range; ++i) { - allocator.allocateName(); - } - REPORTER_ASSERT(reporter, 0 == allocator.allocateName()); - - // Test freeing names out of range. - allocator.free(allocator.firstName() - 1); - allocator.free(allocator.endName()); - REPORTER_ASSERT(reporter, 0 == allocator.allocateName()); - - // Test freeing not-allocated names. - for (GrGLuint i = 1; i <= range/2; i += 2) { - allocator.free(i); - } - for (GrGLuint i = 1; i <= range/2; i += 2) { - // None of these names will be allocated. - allocator.free(i); - } - for (GrGLuint i = 1; i <= range/2; ++i) { - // Every other name will not be be allocated. - allocator.free(i); - } - for (GrGLuint i = 1; i <= range/2; ++i) { - if (0 == allocator.allocateName()) { - ERRORF(reporter, "Name allocate failed when there should be free names"); - break; - } - } - REPORTER_ASSERT(reporter, 0 == allocator.allocateName()); -} - -#endif
diff --git a/src/third_party/skia/tests/OSPathTest.cpp b/src/third_party/skia/tests/OSPathTest.cpp index facc6ad..22deff8 100644 --- a/src/third_party/skia/tests/OSPathTest.cpp +++ b/src/third_party/skia/tests/OSPathTest.cpp
@@ -5,7 +5,7 @@ * found in the LICENSE file. */ -#include "SkOSFile.h" +#include "SkOSPath.h" #include "SkString.h" #include "Test.h" @@ -15,24 +15,24 @@ * and tests using SkOSPath::Basename on the result. * @param reporter Reporter for test conditions. * @param dir String representing the path to a folder. May or may not - * end with SkPATH_SEPARATOR. + * end with SkOSPath::SEPARATOR. * @param filename String representing the basename of a file. Must NOT - * contain SkPATH_SEPARATOR. + * contain SkOSPath::SEPARATOR. */ static void test_dir_with_file(skiatest::Reporter* reporter, SkString dir, SkString filename) { - // If filename contains SkPATH_SEPARATOR, the tests will fail. - SkASSERT(!filename.contains(SkPATH_SEPARATOR)); + // If filename contains SkOSPath::SEPARATOR, the tests will fail. + SkASSERT(!filename.contains(SkOSPath::SEPARATOR)); // Tests for SkOSPath::Join and SkOSPath::Basename - // fullName should be "dir<SkPATH_SEPARATOR>file" + // fullName should be "dir<SkOSPath::SEPARATOR>file" SkString fullName = SkOSPath::Join(dir.c_str(), filename.c_str()); // fullName should be the combined size of dir and file, plus one if // dir did not include the final path separator. size_t expectedSize = dir.size() + filename.size(); - if (!dir.endsWith(SkPATH_SEPARATOR) && !dir.isEmpty()) { + if (!dir.endsWith(SkOSPath::SEPARATOR) && !dir.isEmpty()) { expectedSize++; } REPORTER_ASSERT(reporter, fullName.size() == expectedSize); @@ -46,7 +46,7 @@ // dirname should be the same as dir with any trailing seperators removed. // Except when the the string is just "/". SkString strippedDir = dir; - while (strippedDir.size() > 2 && strippedDir[strippedDir.size() - 1] == SkPATH_SEPARATOR) { + while (strippedDir.size() > 2 && strippedDir[strippedDir.size() - 1] == SkOSPath::SEPARATOR) { strippedDir.remove(strippedDir.size() - 1, 1); } if (!dirname.equals(strippedDir)) { @@ -55,7 +55,7 @@ REPORTER_ASSERT(reporter, dirname.equals(strippedDir)); // basename will not contain a path separator - REPORTER_ASSERT(reporter, !basename.contains(SkPATH_SEPARATOR)); + REPORTER_ASSERT(reporter, !basename.contains(SkOSPath::SEPARATOR)); // Now take the basename of filename, which should be the same as filename. basename = SkOSPath::Basename(filename.c_str()); @@ -68,7 +68,7 @@ test_dir_with_file(reporter, dir, filename); // Now make sure this works with a path separator at the end of dir. - dir.appendUnichar(SkPATH_SEPARATOR); + dir.appendUnichar(SkOSPath::SEPARATOR); test_dir_with_file(reporter, dir, filename); // Test using no filename. @@ -82,16 +82,16 @@ test_dir_with_file(reporter, dir, filename); // Basename of a directory with a path separator at the end is empty. - dir.appendUnichar(SkPATH_SEPARATOR); + dir.appendUnichar(SkOSPath::SEPARATOR); SkString baseOfDir = SkOSPath::Basename(dir.c_str()); REPORTER_ASSERT(reporter, baseOfDir.size() == 0); - // Basename of NULL is an empty string. - SkString empty = SkOSPath::Basename(NULL); + // Basename of nullptr is an empty string. + SkString empty = SkOSPath::Basename(nullptr); REPORTER_ASSERT(reporter, empty.size() == 0); // File in root dir - dir.printf("%c", SkPATH_SEPARATOR); + dir.printf("%c", SkOSPath::SEPARATOR); filename.set("file"); test_dir_with_file(reporter, dir, filename); @@ -99,7 +99,7 @@ filename.reset(); test_dir_with_file(reporter, dir, filename); - // Test that NULL can be used for the directory and filename. - SkString emptyPath = SkOSPath::Join(NULL, NULL); + // Test that nullptr can be used for the directory and filename. + SkString emptyPath = SkOSPath::Join(nullptr, nullptr); REPORTER_ASSERT(reporter, emptyPath.isEmpty()); }
diff --git a/src/third_party/skia/tests/ObjectPoolTest.cpp b/src/third_party/skia/tests/ObjectPoolTest.cpp deleted file mode 100644 index 404448e..0000000 --- a/src/third_party/skia/tests/ObjectPoolTest.cpp +++ /dev/null
@@ -1,68 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkTObjectPool.h" -#include "SkTObjectPool.h" -#include "Test.h" - -class PoolEntry { -public: -private: - SK_DECLARE_INTERNAL_SLIST_INTERFACE(PoolEntry); -}; - -static const int kNumItemsPerBlock = 3; -typedef SkTObjectPool<PoolEntry, kNumItemsPerBlock> ObjectPoolType; - -static bool verifyPool(skiatest::Reporter* reporter, - const ObjectPoolType& pool, - const char* stage, - int available, int blocks) { - if (available != pool.available()) { - ERRORF(reporter, "%s - Pool available is %d not %d", - stage, pool.available(), available); - return false; - } - if (blocks != pool.blocks()) { - ERRORF(reporter, "%s - Pool blocks is %d not %d", - stage, pool.blocks(), blocks); - return false; - } - return true; -} - -static const int kNumToAcquire = kNumItemsPerBlock * 5; -static void testObjectPool(skiatest::Reporter* reporter) { - ObjectPoolType pool; - SkTInternalSList<PoolEntry> used; - verifyPool(reporter, pool, "empty", 0, 0); - for (int index = 0; index < kNumToAcquire; ++index) { - used.push(pool.acquire()); - int blocks = (index / kNumItemsPerBlock) + 1; - int available = (blocks * kNumItemsPerBlock) - (index + 1); - if (!verifyPool(reporter, pool, "acquire", available, blocks)) { - return; - } - } - int available = pool.available(); - int blocks = pool.blocks(); - for (int index = 0; index < kNumToAcquire / 2; ++index) { - pool.release(used.pop()); - ++available; - if (!verifyPool(reporter, pool, "release", available, blocks)) { - return; - } - } - available += used.getCount(); - pool.releaseAll(&used); - REPORTER_ASSERT(reporter, used.isEmpty()); - verifyPool(reporter, pool, "releaseAll", available, blocks); -} - -DEF_TEST(ObjectPool, reporter) { - testObjectPool(reporter); -}
diff --git a/src/third_party/skia/tests/OnFlushCallbackTest.cpp b/src/third_party/skia/tests/OnFlushCallbackTest.cpp new file mode 100644 index 0000000..c7503bc --- /dev/null +++ b/src/third_party/skia/tests/OnFlushCallbackTest.cpp
@@ -0,0 +1,619 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#if SK_SUPPORT_GPU + +#include "GrBackendSemaphore.h" +#include "GrClip.h" +#include "GrContextPriv.h" +#include "GrDefaultGeoProcFactory.h" +#include "GrOnFlushResourceProvider.h" +#include "GrRenderTargetContextPriv.h" +#include "GrResourceProvider.h" +#include "GrQuad.h" +#include "effects/GrSimpleTextureEffect.h" +#include "ops/GrSimpleMeshDrawOpHelper.h" + +namespace { +// This is a simplified mesh drawing op that can be used in the atlas generation test. +// Please see AtlasedRectOp below. +class NonAARectOp : public GrMeshDrawOp { +protected: + using Helper = GrSimpleMeshDrawOpHelper; + +public: + DEFINE_OP_CLASS_ID + const char* name() const override { return "NonAARectOp"; } + + // This creates an instance of a simple non-AA solid color rect-drawing Op + static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkRect& r) { + return Helper::FactoryHelper<NonAARectOp>(std::move(paint), r, nullptr, ClassID()); + } + + // This creates an instance of a simple non-AA textured rect-drawing Op + static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkRect& r, const SkRect& local) { + return Helper::FactoryHelper<NonAARectOp>(std::move(paint), r, &local, ClassID()); + } + + GrColor color() const { return fColor; } + + NonAARectOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkRect& r, + const SkRect* localRect, int32_t classID) + : INHERITED(classID) + , fColor(color) + , fHasLocalRect(SkToBool(localRect)) + , fRect(r) + , fHelper(helperArgs, GrAAType::kNone) { + if (fHasLocalRect) { + fLocalQuad.set(*localRect); + } + // Choose some conservative values for aa bloat and zero area. + this->setBounds(r, HasAABloat::kYes, IsZeroArea::kYes); + } + + FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } + + RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip*) override { + // Set the color to unknown because the subclass may change the color later. + GrProcessorAnalysisColor gpColor; + gpColor.setToUnknown(); + // We ignore the clip so pass this rather than the GrAppliedClip param. + static GrAppliedClip kNoClip; + return fHelper.xpRequiresDstTexture(caps, &kNoClip, GrProcessorAnalysisCoverage::kNone, + &gpColor); + } + +protected: + GrColor fColor; + bool fHasLocalRect; + GrQuad fLocalQuad; + SkRect fRect; + +private: + bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; } + + void onPrepareDraws(Target* target) const override { + using namespace GrDefaultGeoProcFactory; + + // The vertex attrib order is always pos, color, local coords. + static const int kColorOffset = sizeof(SkPoint); + static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor); + + sk_sp<GrGeometryProcessor> gp = + GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type, + Coverage::kSolid_Type, + fHasLocalRect ? LocalCoords::kHasExplicit_Type + : LocalCoords::kUnused_Type, + SkMatrix::I()); + if (!gp) { + SkDebugf("Couldn't create GrGeometryProcessor for GrAtlasedOp\n"); + return; + } + + size_t vertexStride = gp->getVertexStride(); + + SkASSERT(fHasLocalRect + ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr) + : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr)); + + const GrBuffer* indexBuffer; + int firstIndex; + uint16_t* indices = target->makeIndexSpace(6, &indexBuffer, &firstIndex); + if (!indices) { + SkDebugf("Indices could not be allocated for GrAtlasedOp.\n"); + return; + } + + const GrBuffer* vertexBuffer; + int firstVertex; + void* vertices = target->makeVertexSpace(vertexStride, 4, &vertexBuffer, &firstVertex); + if (!vertices) { + SkDebugf("Vertices could not be allocated for GrAtlasedOp.\n"); + return; + } + + // Setup indices + indices[0] = 0; + indices[1] = 1; + indices[2] = 2; + indices[3] = 0; + indices[4] = 2; + indices[5] = 3; + + // Setup positions + SkPoint* position = (SkPoint*) vertices; + position->setRectFan(fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, vertexStride); + + // Setup vertex colors + GrColor* color = (GrColor*)((intptr_t)vertices + kColorOffset); + for (int i = 0; i < 4; ++i) { + *color = fColor; + color = (GrColor*)((intptr_t)color + vertexStride); + } + + // Setup local coords + if (fHasLocalRect) { + SkPoint* coords = (SkPoint*)((intptr_t) vertices + kLocalOffset); + for (int i = 0; i < 4; i++) { + *coords = fLocalQuad.point(i); + coords = (SkPoint*)((intptr_t) coords + vertexStride); + } + } + + GrMesh mesh(GrPrimitiveType::kTriangles); + mesh.setIndexed(indexBuffer, 6, firstIndex, 0, 3); + mesh.setVertexData(vertexBuffer, firstVertex); + + target->draw(gp.get(), fHelper.makePipeline(target), mesh); + } + + Helper fHelper; + + typedef GrMeshDrawOp INHERITED; +}; + +} // anonymous namespace + +#ifdef SK_DEBUG +#include "SkImageEncoder.h" +#include "sk_tool_utils.h" + +static void save_bm(const SkBitmap& bm, const char name[]) { + bool result = sk_tool_utils::EncodeImageToFile(name, bm, SkEncodedImageFormat::kPNG, 100); + SkASSERT(result); +} +#endif + +static constexpr SkRect kEmptyRect = SkRect::MakeEmpty(); + +namespace { + +/* + * Atlased ops just draw themselves as textured rects with the texture pixels being + * pulled out of the atlas. Their color is based on their ID. + */ +class AtlasedRectOp final : public NonAARectOp { +public: + DEFINE_OP_CLASS_ID + + ~AtlasedRectOp() override { + fID = -1; + } + + const char* name() const override { return "AtlasedRectOp"; } + + int id() const { return fID; } + + static std::unique_ptr<AtlasedRectOp> Make(GrPaint&& paint, const SkRect& r, int id) { + GrDrawOp* op = Helper::FactoryHelper<AtlasedRectOp>(std::move(paint), r, id).release(); + return std::unique_ptr<AtlasedRectOp>(static_cast<AtlasedRectOp*>(op)); + } + + // We set the initial color of the NonAARectOp based on the ID. + // Note that we force creation of a NonAARectOp that has local coords in anticipation of + // pulling from the atlas. + AtlasedRectOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkRect& r, int id) + : INHERITED(helperArgs, kColors[id], r, &kEmptyRect, ClassID()) + , fID(id) + , fNext(nullptr) { + SkASSERT(fID < kMaxIDs); + } + + void setColor(GrColor color) { fColor = color; } + void setLocalRect(const SkRect& localRect) { + SkASSERT(fHasLocalRect); // This should've been created to anticipate this + fLocalQuad.set(localRect); + } + + AtlasedRectOp* next() const { return fNext; } + void setNext(AtlasedRectOp* next) { + fNext = next; + } + +private: + + static const int kMaxIDs = 9; + static const SkColor kColors[kMaxIDs]; + + int fID; + // The Atlased ops have an internal singly-linked list of ops that land in the same opList + AtlasedRectOp* fNext; + + typedef NonAARectOp INHERITED; +}; + +} // anonymous namespace + +const GrColor AtlasedRectOp::kColors[kMaxIDs] = { + GrColorPackRGBA(255, 0, 0, 255), + GrColorPackRGBA(0, 255, 0, 255), + GrColorPackRGBA(0, 0, 255, 255), + GrColorPackRGBA(0, 255, 255, 255), + GrColorPackRGBA(255, 0, 255, 255), + GrColorPackRGBA(255, 255, 0, 255), + GrColorPackRGBA(0, 0, 0, 255), + GrColorPackRGBA(128, 128, 128, 255), + GrColorPackRGBA(255, 255, 255, 255) +}; + +static const int kDrawnTileSize = 16; + +/* + * Rather than performing any rect packing, this atlaser just lays out constant-sized + * tiles in an Nx1 row + */ +static const int kAtlasTileSize = 2; + +/* + * This class aggregates the op information required for atlasing + */ +class AtlasObject final : public GrOnFlushCallbackObject { +public: + AtlasObject() : fDone(false) { } + + ~AtlasObject() override { + SkASSERT(fDone); + } + + void markAsDone() { + fDone = true; + } + + // Insert the new op in an internal singly-linked list for 'opListID' + void addOp(uint32_t opListID, AtlasedRectOp* op) { + LinkedListHeader* header = nullptr; + for (int i = 0; i < fOps.count(); ++i) { + if (opListID == fOps[i].fID) { + header = &(fOps[i]); + } + } + + if (!header) { + fOps.push({opListID, nullptr}); + header = &(fOps[fOps.count()-1]); + } + + op->setNext(header->fHead); + header->fHead = op; + } + + // For the time being we need to pre-allocate the atlas. + void setAtlasDest(sk_sp<GrTextureProxy> atlasDest) { + fAtlasDest = atlasDest; + } + + void saveRTC(sk_sp<GrRenderTargetContext> rtc) { + SkASSERT(!fRTC); + fRTC = rtc; + } + +#ifdef SK_DEBUG + void saveAtlasToDisk() { + SkBitmap readBack; + readBack.allocN32Pixels(fRTC->width(), fRTC->height()); + + bool result = fRTC->readPixels(readBack.info(), + readBack.getPixels(), readBack.rowBytes(), 0, 0); + SkASSERT(result); + save_bm(readBack, "atlas-real.png"); + } +#endif + + /* + * This callback back creates the atlas and updates the AtlasedRectOps to read from it + */ + void preFlush(GrOnFlushResourceProvider* resourceProvider, + const uint32_t* opListIDs, int numOpListIDs, + SkTArray<sk_sp<GrRenderTargetContext>>* results) override { + SkASSERT(!results->count()); + + // Until MDB is landed we will most-likely only have one opList. + SkTDArray<LinkedListHeader*> lists; + for (int i = 0; i < numOpListIDs; ++i) { + if (LinkedListHeader* list = this->getList(opListIDs[i])) { + lists.push(list); + } + } + + if (!lists.count()) { + return; // nothing to atlas + } + + // TODO: right now we have to pre-allocate the atlas bc the TextureSamplers need a + // hard GrTexture +#if 0 + GrSurfaceDesc desc; + desc.fFlags = kRenderTarget_GrSurfaceFlag; + desc.fWidth = this->numOps() * kAtlasTileSize; + desc.fHeight = kAtlasTileSize; + desc.fConfig = kRGBA_8888_GrPixelConfig; + + sk_sp<GrRenderTargetContext> rtc = resourceProvider->makeRenderTargetContext(desc, + nullptr, + nullptr); +#else + // At this point all the GrAtlasedOp's should have lined up to read from 'atlasDest' and + // there should either be two writes to clear it or no writes. + SkASSERT(9 == fAtlasDest->getPendingReadCnt_TestOnly()); + SkASSERT(2 == fAtlasDest->getPendingWriteCnt_TestOnly() || + 0 == fAtlasDest->getPendingWriteCnt_TestOnly()); + sk_sp<GrRenderTargetContext> rtc = resourceProvider->makeRenderTargetContext( + fAtlasDest, + nullptr, nullptr); +#endif + + rtc->clear(nullptr, 0xFFFFFFFF, true); // clear the atlas + + int blocksInAtlas = 0; + for (int i = 0; i < lists.count(); ++i) { + for (AtlasedRectOp* op = lists[i]->fHead; op; op = op->next()) { + SkIRect r = SkIRect::MakeXYWH(blocksInAtlas*kAtlasTileSize, 0, + kAtlasTileSize, kAtlasTileSize); + + // For now, we avoid the resource buffer issues and just use clears +#if 1 + rtc->clear(&r, op->color(), false); +#else + GrPaint paint; + paint.setColor4f(GrColor4f::FromGrColor(op->color())); + std::unique_ptr<GrDrawOp> drawOp(NonAARectOp::Make(std::move(paint), + SkRect::Make(r))); + rtc->priv().testingOnly_addDrawOp(std::move(drawOp)); +#endif + blocksInAtlas++; + + // Set the atlased Op's color to white (so we know we're not using it for + // the final draw). + op->setColor(0xFFFFFFFF); + + // Set the atlased Op's localRect to point to where it landed in the atlas + op->setLocalRect(SkRect::Make(r)); + + // TODO: we also need to set the op's GrSuperDeferredSimpleTextureEffect to point + // to the rtc's proxy! + } + + // We've updated all these ops and we certainly don't want to process them again + this->clearOpsFor(lists[i]); + } + + // Hide a ref to the RTC in AtlasData so we can check on it later + this->saveRTC(rtc); + + results->push_back(std::move(rtc)); + } + +private: + typedef struct { + uint32_t fID; + AtlasedRectOp* fHead; + } LinkedListHeader; + + LinkedListHeader* getList(uint32_t opListID) { + for (int i = 0; i < fOps.count(); ++i) { + if (opListID == fOps[i].fID) { + return &(fOps[i]); + } + } + return nullptr; + } + + void clearOpsFor(LinkedListHeader* header) { + // The AtlasedRectOps have yet to execute (and this class doesn't own them) so just + // forget about them in the laziest way possible. + header->fHead = nullptr; + header->fID = 0; // invalid opList ID + } + + // Each opList containing AtlasedRectOps gets its own internal singly-linked list + SkTDArray<LinkedListHeader> fOps; + + // The RTC used to create the atlas + sk_sp<GrRenderTargetContext> fRTC; + + // For the time being we need to pre-allocate the atlas bc the TextureSamplers require + // a GrTexture + sk_sp<GrTextureProxy> fAtlasDest; + + // Set to true when the testing harness expects this object to be no longer used + bool fDone; +}; + +// This creates an off-screen rendertarget whose ops which eventually pull from the atlas. +static sk_sp<GrTextureProxy> make_upstream_image(GrContext* context, AtlasObject* object, int start, + sk_sp<GrTextureProxy> fakeAtlas) { + sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContext( + SkBackingFit::kApprox, + 3*kDrawnTileSize, + kDrawnTileSize, + kRGBA_8888_GrPixelConfig, + nullptr)); + + rtc->clear(nullptr, GrColorPackRGBA(255, 0, 0, 255), true); + + for (int i = 0; i < 3; ++i) { + SkRect r = SkRect::MakeXYWH(i*kDrawnTileSize, 0, kDrawnTileSize, kDrawnTileSize); + + // TODO: here is the blocker for deferring creation of the atlas. The TextureSamplers + // created here currently require a hard GrTexture. + sk_sp<GrFragmentProcessor> fp = GrSimpleTextureEffect::Make(fakeAtlas, + nullptr, SkMatrix::I()); + GrPaint paint; + paint.addColorFragmentProcessor(std::move(fp)); + paint.setPorterDuffXPFactory(SkBlendMode::kSrc); + std::unique_ptr<AtlasedRectOp> op(AtlasedRectOp::Make(std::move(paint), r, start + i)); + + AtlasedRectOp* sparePtr = op.get(); + + uint32_t opListID = rtc->priv().testingOnly_addDrawOp(std::move(op)); + + object->addOp(opListID, sparePtr); + } + + return rtc->asTextureProxyRef(); +} + +// Enable this if you want to debug the final draws w/o having the atlasCallback create the +// atlas +#if 0 +#include "SkGrPriv.h" + +sk_sp<GrTextureProxy> pre_create_atlas(GrContext* context) { + SkBitmap bm; + bm.allocN32Pixels(18, 2, true); + bm.erase(SK_ColorRED, SkIRect::MakeXYWH(0, 0, 2, 2)); + bm.erase(SK_ColorGREEN, SkIRect::MakeXYWH(2, 0, 2, 2)); + bm.erase(SK_ColorBLUE, SkIRect::MakeXYWH(4, 0, 2, 2)); + bm.erase(SK_ColorCYAN, SkIRect::MakeXYWH(6, 0, 2, 2)); + bm.erase(SK_ColorMAGENTA, SkIRect::MakeXYWH(8, 0, 2, 2)); + bm.erase(SK_ColorYELLOW, SkIRect::MakeXYWH(10, 0, 2, 2)); + bm.erase(SK_ColorBLACK, SkIRect::MakeXYWH(12, 0, 2, 2)); + bm.erase(SK_ColorGRAY, SkIRect::MakeXYWH(14, 0, 2, 2)); + bm.erase(SK_ColorWHITE, SkIRect::MakeXYWH(16, 0, 2, 2)); + +#if 1 + save_bm(bm, "atlas-fake.png"); +#endif + + GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bm.info(), *context->caps()); + desc.fFlags |= kRenderTarget_GrSurfaceFlag; + + sk_sp<GrSurfaceProxy> tmp = GrSurfaceProxy::MakeDeferred(*context->caps(), + context->textureProvider(), + desc, SkBudgeted::kYes, + bm.getPixels(), bm.rowBytes()); + + return sk_ref_sp(tmp->asTextureProxy()); +} +#else +// TODO: this is unfortunate and must be removed. We want the atlas to be created later. +sk_sp<GrTextureProxy> pre_create_atlas(GrContext* context) { + GrSurfaceDesc desc; + desc.fFlags = kRenderTarget_GrSurfaceFlag; + desc.fConfig = kSkia8888_GrPixelConfig; + desc.fOrigin = kBottomLeft_GrSurfaceOrigin; + desc.fWidth = 32; + desc.fHeight = 16; + sk_sp<GrSurfaceProxy> atlasDest = GrSurfaceProxy::MakeDeferred( + context->resourceProvider(), + desc, SkBackingFit::kExact, + SkBudgeted::kYes, + GrResourceProvider::kNoPendingIO_Flag); + return sk_ref_sp(atlasDest->asTextureProxy()); +} +#endif + +static void test_color(skiatest::Reporter* reporter, const SkBitmap& bm, int x, SkColor expected) { + SkColor readback = bm.getColor(x, kDrawnTileSize/2); + REPORTER_ASSERT(reporter, expected == readback); + if (expected != readback) { + SkDebugf("Color mismatch: %x %x\n", expected, readback); + } +} + +/* + * For the atlasing test we make a DAG that looks like: + * + * RT1 with ops: 0,1,2 RT2 with ops: 3,4,5 RT3 with ops: 6,7,8 + * \ / + * \ / + * RT4 + * We then flush RT4 and expect only ops 0-5 to be atlased together. + * Each op is just a solid colored rect so both the atlas and the final image should appear as: + * R G B C M Y + * with the atlas having width = 6*kAtlasTileSize and height = kAtlasTileSize. + * + * Note: until MDB lands, the atlas will actually have width= 9*kAtlasTileSize and look like: + * R G B C M Y K Grey White + */ +DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(OnFlushCallbackTest, reporter, ctxInfo) { + static const int kNumProxies = 3; + + GrContext* context = ctxInfo.grContext(); + + if (context->caps()->useDrawInsteadOfClear()) { + // TODO: fix the buffer issues so this can run on all devices + return; + } + + AtlasObject object; + + // For now (until we add a GrSuperDeferredSimpleTextureEffect), we create the final atlas + // proxy ahead of time. + sk_sp<GrTextureProxy> atlasDest = pre_create_atlas(context); + + object.setAtlasDest(atlasDest); + + context->contextPriv().addOnFlushCallbackObject(&object); + + sk_sp<GrTextureProxy> proxies[kNumProxies]; + for (int i = 0; i < kNumProxies; ++i) { + proxies[i] = make_upstream_image(context, &object, i*3, atlasDest); + } + + static const int kFinalWidth = 6*kDrawnTileSize; + static const int kFinalHeight = kDrawnTileSize; + + sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContext( + SkBackingFit::kApprox, + kFinalWidth, + kFinalHeight, + kRGBA_8888_GrPixelConfig, + nullptr)); + + rtc->clear(nullptr, 0xFFFFFFFF, true); + + // Note that this doesn't include the third texture proxy + for (int i = 0; i < kNumProxies-1; ++i) { + SkRect r = SkRect::MakeXYWH(i*3*kDrawnTileSize, 0, 3*kDrawnTileSize, kDrawnTileSize); + + SkMatrix t = SkMatrix::MakeTrans(-i*3*kDrawnTileSize, 0); + + GrPaint paint; + sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(std::move(proxies[i]), + nullptr, t)); + paint.setPorterDuffXPFactory(SkBlendMode::kSrc); + paint.addColorFragmentProcessor(std::move(fp)); + + rtc->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r); + } + + rtc->prepareForExternalIO(0, nullptr); + + SkBitmap readBack; + readBack.allocN32Pixels(kFinalWidth, kFinalHeight); + + SkDEBUGCODE(bool result =) rtc->readPixels(readBack.info(), readBack.getPixels(), + readBack.rowBytes(), 0, 0); + SkASSERT(result); + + context->contextPriv().testingOnly_flushAndRemoveOnFlushCallbackObject(&object); + + object.markAsDone(); + +#if 0 + save_bm(readBack, "atlas-final-image.png"); + data.saveAtlasToDisk(); +#endif + + int x = kDrawnTileSize/2; + test_color(reporter, readBack, x, SK_ColorRED); + x += kDrawnTileSize; + test_color(reporter, readBack, x, SK_ColorGREEN); + x += kDrawnTileSize; + test_color(reporter, readBack, x, SK_ColorBLUE); + x += kDrawnTileSize; + test_color(reporter, readBack, x, SK_ColorCYAN); + x += kDrawnTileSize; + test_color(reporter, readBack, x, SK_ColorMAGENTA); + x += kDrawnTileSize; + test_color(reporter, readBack, x, SK_ColorYELLOW); +} + +#endif
diff --git a/src/third_party/skia/tests/OnceTest.cpp b/src/third_party/skia/tests/OnceTest.cpp index 192abaa..ef8d3d9 100644 --- a/src/third_party/skia/tests/OnceTest.cpp +++ b/src/third_party/skia/tests/OnceTest.cpp
@@ -16,49 +16,25 @@ DEF_TEST(SkOnce_Singlethreaded, r) { int x = 0; - SK_DECLARE_STATIC_ONCE(once); // No matter how many times we do this, x will be 5. - SkOnce(&once, add_five, &x); - SkOnce(&once, add_five, &x); - SkOnce(&once, add_five, &x); - SkOnce(&once, add_five, &x); - SkOnce(&once, add_five, &x); + SkOnce once; + once(add_five, &x); + once(add_five, &x); + once(add_five, &x); + once(add_five, &x); + once(add_five, &x); REPORTER_ASSERT(r, 5 == x); } -static void add_six(int* x) { - *x += 6; -} - -class Racer : public SkRunnable { -public: - SkOnceFlag* once; - int* ptr; - - virtual void run() SK_OVERRIDE { - SkOnce(once, add_six, ptr); - } -}; - DEF_TEST(SkOnce_Multithreaded, r) { - const int kTasks = 16; - - // Make a bunch of tasks that will race to be the first to add six to x. - Racer racers[kTasks]; - SK_DECLARE_STATIC_ONCE(once); int x = 0; - for (int i = 0; i < kTasks; i++) { - racers[i].once = &once; - racers[i].ptr = &x; - } - // Let them race. - SkTaskGroup tg; - for (int i = 0; i < kTasks; i++) { - tg.add(&racers[i]); - } - tg.wait(); + // Run a bunch of tasks to be the first to add six to x. + SkOnce once; + SkTaskGroup().batch(1021, [&](int) { + once([&] { x += 6; }); + }); // Only one should have done the +=. REPORTER_ASSERT(r, 6 == x); @@ -68,9 +44,9 @@ static void inc_gX() { gX++; } DEF_TEST(SkOnce_NoArg, r) { - SK_DECLARE_STATIC_ONCE(once); - SkOnce(&once, inc_gX); - SkOnce(&once, inc_gX); - SkOnce(&once, inc_gX); + SkOnce once; + once(inc_gX); + once(inc_gX); + once(inc_gX); REPORTER_ASSERT(r, 1 == gX); }
diff --git a/src/third_party/skia/tests/OverAlignedTest.cpp b/src/third_party/skia/tests/OverAlignedTest.cpp new file mode 100644 index 0000000..fcc3e16 --- /dev/null +++ b/src/third_party/skia/tests/OverAlignedTest.cpp
@@ -0,0 +1,22 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" +#include "SkRandom.h" + +// Clang seems to think only 32-bit alignment is guaranteed on 32-bit x86 Android. +// See https://reviews.llvm.org/D8357 +// This is why we have disabled -Wover-aligned there (we allocate 8-byte aligned structs in Ganesh). +DEF_TEST(OverAligned, r) { + SkRandom rand; + // Let's test that assertion. We think it really should be providing 8-byte alignment. + for (int i = 0; i < 1000; i++) { + void* p = sk_malloc_throw(rand.nextRangeU(0,100)); + REPORTER_ASSERT(r, SkIsAlign8((uintptr_t)p)); + sk_free(p); + } +}
diff --git a/src/third_party/skia/tests/PDFDeflateWStreamTest.cpp b/src/third_party/skia/tests/PDFDeflateWStreamTest.cpp new file mode 100644 index 0000000..3d734c8 --- /dev/null +++ b/src/third_party/skia/tests/PDFDeflateWStreamTest.cpp
@@ -0,0 +1,169 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#ifdef SK_SUPPORT_PDF + +#include "SkDeflate.h" +#include "SkRandom.h" + +namespace { + +#include "zlib.h" + +// Different zlib implementations use different T. +// We've seen size_t and unsigned. +template <typename T> void* skia_alloc_func(void*, T items, T size) { + return sk_calloc_throw(SkToSizeT(items) * SkToSizeT(size)); +} + +void skia_free_func(void*, void* address) { sk_free(address); } + +/** + * Use the un-deflate compression algorithm to decompress the data in src, + * returning the result. Returns nullptr if an error occurs. + */ +std::unique_ptr<SkStreamAsset> stream_inflate(skiatest::Reporter* reporter, SkStream* src) { + SkDynamicMemoryWStream decompressedDynamicMemoryWStream; + SkWStream* dst = &decompressedDynamicMemoryWStream; + + static const size_t kBufferSize = 1024; + uint8_t inputBuffer[kBufferSize]; + uint8_t outputBuffer[kBufferSize]; + z_stream flateData; + flateData.zalloc = &skia_alloc_func; + flateData.zfree = &skia_free_func; + flateData.opaque = nullptr; + flateData.next_in = nullptr; + flateData.avail_in = 0; + flateData.next_out = outputBuffer; + flateData.avail_out = kBufferSize; + int rc; + rc = inflateInit(&flateData); + if (rc != Z_OK) { + ERRORF(reporter, "Zlib: inflateInit failed"); + return nullptr; + } + uint8_t* input = (uint8_t*)src->getMemoryBase(); + size_t inputLength = src->getLength(); + if (input == nullptr || inputLength == 0) { + input = nullptr; + flateData.next_in = inputBuffer; + flateData.avail_in = 0; + } else { + flateData.next_in = input; + flateData.avail_in = SkToUInt(inputLength); + } + + rc = Z_OK; + while (true) { + if (flateData.avail_out < kBufferSize) { + if (!dst->write(outputBuffer, kBufferSize - flateData.avail_out)) { + rc = Z_BUF_ERROR; + break; + } + flateData.next_out = outputBuffer; + flateData.avail_out = kBufferSize; + } + if (rc != Z_OK) + break; + if (flateData.avail_in == 0) { + if (input != nullptr) + break; + size_t read = src->read(&inputBuffer, kBufferSize); + if (read == 0) + break; + flateData.next_in = inputBuffer; + flateData.avail_in = SkToUInt(read); + } + rc = inflate(&flateData, Z_NO_FLUSH); + } + while (rc == Z_OK) { + rc = inflate(&flateData, Z_FINISH); + if (flateData.avail_out < kBufferSize) { + if (!dst->write(outputBuffer, kBufferSize - flateData.avail_out)) { + ERRORF(reporter, "write failed"); + return nullptr; + } + flateData.next_out = outputBuffer; + flateData.avail_out = kBufferSize; + } + } + + inflateEnd(&flateData); + if (rc != Z_STREAM_END) { + ERRORF(reporter, "Zlib: inflateEnd failed"); + return nullptr; + } + return decompressedDynamicMemoryWStream.detachAsStream(); +} +} // namespace + +DEF_TEST(SkPDF_DeflateWStream, r) { + SkRandom random(123456); + for (int i = 0; i < 50; ++i) { + uint32_t size = random.nextULessThan(10000); + SkAutoTMalloc<uint8_t> buffer(size); + for (uint32_t j = 0; j < size; ++j) { + buffer[j] = random.nextU() & 0xff; + } + + SkDynamicMemoryWStream dynamicMemoryWStream; + { + SkDeflateWStream deflateWStream(&dynamicMemoryWStream); + uint32_t j = 0; + while (j < size) { + uint32_t writeSize = + SkTMin(size - j, random.nextRangeU(1, 400)); + if (!deflateWStream.write(&buffer[j], writeSize)) { + ERRORF(r, "something went wrong."); + return; + } + j += writeSize; + } + REPORTER_ASSERT(r, deflateWStream.bytesWritten() == size); + } + std::unique_ptr<SkStreamAsset> compressed(dynamicMemoryWStream.detachAsStream()); + std::unique_ptr<SkStreamAsset> decompressed(stream_inflate(r, compressed.get())); + + if (!decompressed) { + ERRORF(r, "Decompression failed."); + return; + } + if (decompressed->getLength() != size) { + ERRORF(r, "Decompression failed to get right size [%d]." + " %u != %u", i, (unsigned)(decompressed->getLength()), + (unsigned)size); + SkString s = SkStringPrintf("/tmp/deftst_compressed_%d", i); + SkFILEWStream o(s.c_str()); + o.writeStream(compressed.get(), compressed->getLength()); + compressed->rewind(); + + s = SkStringPrintf("/tmp/deftst_input_%d", i); + SkFILEWStream o2(s.c_str()); + o2.write(&buffer[0], size); + + continue; + } + uint32_t minLength = SkTMin(size, + (uint32_t)(decompressed->getLength())); + for (uint32_t i = 0; i < minLength; ++i) { + uint8_t c; + SkDEBUGCODE(size_t rb =)decompressed->read(&c, sizeof(uint8_t)); + SkASSERT(sizeof(uint8_t) == rb); + if (buffer[i] != c) { + ERRORF(r, "Decompression failed at byte %u.", (unsigned)i); + break; + } + } + } + SkDeflateWStream emptyDeflateWStream(nullptr); + REPORTER_ASSERT(r, !emptyDeflateWStream.writeText("FOO")); +} + +#endif
diff --git a/src/third_party/skia/tests/PDFDocumentTest.cpp b/src/third_party/skia/tests/PDFDocumentTest.cpp new file mode 100644 index 0000000..f01d0f9 --- /dev/null +++ b/src/third_party/skia/tests/PDFDocumentTest.cpp
@@ -0,0 +1,224 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "Test.h" + +#include "Resources.h" +#include "SkCanvas.h" +#include "SkDocument.h" +#include "SkOSFile.h" +#include "SkOSPath.h" +#include "SkStream.h" +#include "SkPixelSerializer.h" + +#include "sk_tool_utils.h" + +static void test_empty(skiatest::Reporter* reporter) { + SkDynamicMemoryWStream stream; + + sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream)); + + doc->close(); + + REPORTER_ASSERT(reporter, stream.bytesWritten() == 0); +} + +static void test_abort(skiatest::Reporter* reporter) { + SkDynamicMemoryWStream stream; + sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream)); + + SkCanvas* canvas = doc->beginPage(100, 100); + canvas->drawColor(SK_ColorRED); + doc->endPage(); + + doc->abort(); + + // Test that only the header is written, not the full document. + REPORTER_ASSERT(reporter, stream.bytesWritten() < 256); +} + +static void test_abortWithFile(skiatest::Reporter* reporter) { + SkString tmpDir = skiatest::GetTmpDir(); + + if (tmpDir.isEmpty()) { + return; // TODO(edisonn): unfortunatelly this pattern is used in other + // tests, but if GetTmpDir() starts returning and empty dir + // allways, then all these tests will be disabled. + } + + SkString path = SkOSPath::Join(tmpDir.c_str(), "aborted.pdf"); + + // Make sure doc's destructor is called to flush. + { + sk_sp<SkDocument> doc(SkDocument::MakePDF(path.c_str())); + + SkCanvas* canvas = doc->beginPage(100, 100); + canvas->drawColor(SK_ColorRED); + doc->endPage(); + + doc->abort(); + } + + FILE* file = fopen(path.c_str(), "r"); + // Test that only the header is written, not the full document. + char buffer[256]; + REPORTER_ASSERT(reporter, fread(buffer, 1, sizeof(buffer), file) < sizeof(buffer)); + fclose(file); +} + +static void test_file(skiatest::Reporter* reporter) { + SkString tmpDir = skiatest::GetTmpDir(); + if (tmpDir.isEmpty()) { + return; // TODO(edisonn): unfortunatelly this pattern is used in other + // tests, but if GetTmpDir() starts returning and empty dir + // allways, then all these tests will be disabled. + } + + SkString path = SkOSPath::Join(tmpDir.c_str(), "file.pdf"); + + sk_sp<SkDocument> doc(SkDocument::MakePDF(path.c_str())); + + SkCanvas* canvas = doc->beginPage(100, 100); + + canvas->drawColor(SK_ColorRED); + doc->endPage(); + doc->close(); + + FILE* file = fopen(path.c_str(), "r"); + REPORTER_ASSERT(reporter, file != nullptr); + char header[100]; + REPORTER_ASSERT(reporter, fread(header, 4, 1, file) != 0); + REPORTER_ASSERT(reporter, strncmp(header, "%PDF", 4) == 0); + fclose(file); +} + +static void test_close(skiatest::Reporter* reporter) { + SkDynamicMemoryWStream stream; + sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream)); + + SkCanvas* canvas = doc->beginPage(100, 100); + canvas->drawColor(SK_ColorRED); + doc->endPage(); + + doc->close(); + + REPORTER_ASSERT(reporter, stream.bytesWritten() != 0); +} + +DEF_TEST(SkPDF_document_tests, reporter) { + REQUIRE_PDF_DOCUMENT(document_tests, reporter); + test_empty(reporter); + test_abort(reporter); + test_abortWithFile(reporter); + test_file(reporter); + test_close(reporter); +} + +namespace { +class JPEGSerializer final : public SkPixelSerializer { + bool onUseEncodedData(const void*, size_t) override { return true; } + SkData* onEncode(const SkPixmap& pixmap) override { + return sk_tool_utils::EncodeImageToData(pixmap, SkEncodedImageFormat::kJPEG, 85).release(); + } +}; +} // namespace + +size_t count_bytes(const SkBitmap& bm, bool useDCT) { + SkDynamicMemoryWStream stream; + sk_sp<SkDocument> doc; + if (useDCT) { + doc = SkDocument::MakePDF(&stream, SK_ScalarDefaultRasterDPI, + SkDocument::PDFMetadata(), + sk_make_sp<JPEGSerializer>(), false); + } else { + doc = SkDocument::MakePDF(&stream); + } + SkCanvas* canvas = doc->beginPage(64, 64); + canvas->drawBitmap(bm, 0, 0); + doc->endPage(); + doc->close(); + return stream.bytesWritten(); +} + +DEF_TEST(SkPDF_document_dct_encoder, r) { + REQUIRE_PDF_DOCUMENT(SkPDF_document_dct_encoder, r); + SkBitmap bm; + if (GetResourceAsBitmap("mandrill_64.png", &bm)) { + // Lossy encoding works better on photographs. + REPORTER_ASSERT(r, count_bytes(bm, true) < count_bytes(bm, false)); + } +} + +DEF_TEST(SkPDF_document_skbug_4734, r) { + REQUIRE_PDF_DOCUMENT(SkPDF_document_skbug_4734, r); + SkDynamicMemoryWStream stream; + sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream)); + SkCanvas* canvas = doc->beginPage(64, 64); + canvas->scale(10000.0f, 10000.0f); + canvas->translate(20.0f, 10.0f); + canvas->rotate(30.0f); + const char text[] = "HELLO"; + canvas->drawString(text, 0, 0, SkPaint()); +} + +static bool contains(const uint8_t* result, size_t size, const char expectation[]) { + size_t len = strlen(expectation); + size_t N = 1 + size - len; + for (size_t i = 0; i < N; ++i) { + if (0 == memcmp(result + i, expectation, len)) { + return true; + } + } + return false; +} + +// verify that the PDFA flag does something. +DEF_TEST(SkPDF_pdfa_document, r) { + REQUIRE_PDF_DOCUMENT(SkPDF_pdfa_document, r); + + SkDocument::PDFMetadata pdfMetadata; + pdfMetadata.fTitle = "test document"; + pdfMetadata.fCreation.fEnabled = true; + pdfMetadata.fCreation.fDateTime = {0, 1999, 12, 5, 31, 23, 59, 59}; + + SkDynamicMemoryWStream buffer; + auto doc = SkDocument::MakePDF(&buffer, SK_ScalarDefaultRasterDPI, + pdfMetadata, nullptr, /* pdfa = */ true); + doc->beginPage(64, 64)->drawColor(SK_ColorRED); + doc->close(); + sk_sp<SkData> data(buffer.detachAsData()); + + static const char* expectations[] = { + "sRGB IEC61966-2.1", + "<dc:title><rdf:Alt><rdf:li xml:lang=\"x-default\">test document", + "<xmp:CreateDate>1999-12-31T23:59:59+00:00</xmp:CreateDate>", + "/Subtype /XML", + "/CreationDate (D:19991231235959+00'00')>>", + }; + for (const char* expectation : expectations) { + if (!contains(data->bytes(), data->size(), expectation)) { + ERRORF(r, "PDFA expectation missing: '%s'.", expectation); + } + } + pdfMetadata.fProducer = "phoney library"; + doc = SkDocument::MakePDF(&buffer, SK_ScalarDefaultRasterDPI, + pdfMetadata, nullptr, /* pdfa = */ true); + doc->beginPage(64, 64)->drawColor(SK_ColorRED); + doc->close(); + data = buffer.detachAsData(); + + static const char* moreExpectations[] = { + "/Producer (phoney library)", + "/ProductionLibrary (Skia/PDF m", + "<!-- <skia:ProductionLibrary>Skia/PDF m", + "<pdf:Producer>phoney library</pdf:Producer>", + }; + for (const char* expectation : moreExpectations) { + if (!contains(data->bytes(), data->size(), expectation)) { + ERRORF(r, "PDFA expectation missing: '%s'.", expectation); + } + } +}
diff --git a/src/third_party/skia/tests/ToUnicodeTest.cpp b/src/third_party/skia/tests/PDFGlyphsToUnicodeTest.cpp similarity index 70% rename from src/third_party/skia/tests/ToUnicodeTest.cpp rename to src/third_party/skia/tests/PDFGlyphsToUnicodeTest.cpp index 919ba11..332520b 100644 --- a/src/third_party/skia/tests/ToUnicodeTest.cpp +++ b/src/third_party/skia/tests/PDFGlyphsToUnicodeTest.cpp
@@ -5,35 +5,38 @@ * found in the LICENSE file. */ -#include "SkData.h" -#include "SkPDFFont.h" -#include "SkPDFTypes.h" -#include "SkStream.h" #include "Test.h" +#ifdef SK_SUPPORT_PDF + +#include "SkBitSet.h" +#include "SkData.h" +#include "SkPDFMakeToUnicodeCmap.h" +#include "SkStream.h" + +static const int kMaximumGlyphCount = SK_MaxU16 + 1; + static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset, const char* buffer, size_t len) { - SkAutoDataUnref data(stream.copyToData()); - if (offset + len > data->size()) { - return false; - } if (len != strlen(buffer)) { return false; } - return memcmp(data->bytes() + offset, buffer, len) == 0; + + const size_t streamSize = stream.bytesWritten(); + + if (offset + len > streamSize) { + return false; + } + + SkAutoTMalloc<char> data(streamSize); + stream.copyTo(data.get()); + return memcmp(data.get() + offset, buffer, len) == 0; } -void append_cmap_sections(const SkTDArray<SkUnichar>& glyphToUnicode, - const SkPDFGlyphSet* subset, - SkDynamicMemoryWStream* cmap, - bool multiByteGlyphs, - uint16_t firstGlypthID, - uint16_t lastGlypthID); - -DEF_TEST(ToUnicode, reporter) { +DEF_TEST(SkPDF_ToUnicode, reporter) { SkTDArray<SkUnichar> glyphToUnicode; SkTDArray<uint16_t> glyphsInSubset; - SkPDFGlyphSet subset; + SkBitSet subset(kMaximumGlyphCount); glyphToUnicode.push(0); // 0 glyphToUnicode.push(0); // 1 @@ -72,8 +75,8 @@ glyphToUnicode.push(0x1013); SkDynamicMemoryWStream buffer; - subset.set(glyphsInSubset.begin(), glyphsInSubset.count()); - append_cmap_sections(glyphToUnicode, &subset, &buffer, true, 0, 0xFFFF); + subset.setAll(glyphsInSubset.begin(), glyphsInSubset.count()); + SkPDFAppendCmapSections(glyphToUnicode, &subset, &buffer, true, 0, 0xFFFF); char expectedResult[] = "4 beginbfchar\n\ @@ -90,12 +93,12 @@ endbfrange\n"; REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult, - buffer.getOffset())); + buffer.bytesWritten())); // Remove characters and ranges. buffer.reset(); - append_cmap_sections(glyphToUnicode, &subset, &buffer, true, 8, 0x00FF); + SkPDFAppendCmapSections(glyphToUnicode, &subset, &buffer, true, 8, 0x00FF); char expectedResultChop1[] = "2 beginbfchar\n\ @@ -108,12 +111,12 @@ endbfrange\n"; REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResultChop1, - buffer.getOffset())); + buffer.bytesWritten())); // Remove characters from range to downdrade it to one char. buffer.reset(); - append_cmap_sections(glyphToUnicode, &subset, &buffer, true, 0x00D, 0x00FE); + SkPDFAppendCmapSections(glyphToUnicode, &subset, &buffer, true, 0x00D, 0x00FE); char expectedResultChop2[] = "2 beginbfchar\n\ @@ -122,34 +125,34 @@ endbfchar\n"; REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResultChop2, - buffer.getOffset())); + buffer.bytesWritten())); buffer.reset(); - append_cmap_sections(glyphToUnicode, NULL, &buffer, false, 0xFC, 0x110); + SkPDFAppendCmapSections(glyphToUnicode, nullptr, &buffer, false, 0xFC, 0x110); char expectedResultSingleBytes[] = "2 beginbfchar\n\ -<0001> <0000>\n\ -<0002> <0000>\n\ +<01> <0000>\n\ +<02> <0000>\n\ endbfchar\n\ 1 beginbfrange\n\ -<0003> <0006> <1010>\n\ +<03> <06> <1010>\n\ endbfrange\n"; REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResultSingleBytes, - buffer.getOffset())); + buffer.bytesWritten())); glyphToUnicode.reset(); glyphsInSubset.reset(); - SkPDFGlyphSet subset2; + SkBitSet subset2(kMaximumGlyphCount); // Test mapping: // I n s t a l // Glyph id 2c 51 56 57 44 4f // Unicode 49 6e 73 74 61 6c - for (size_t i = 0; i < 100; ++i) { + for (SkUnichar i = 0; i < 100; ++i) { glyphToUnicode.push(i + 29); } @@ -161,8 +164,8 @@ glyphsInSubset.push(0x57); SkDynamicMemoryWStream buffer2; - subset2.set(glyphsInSubset.begin(), glyphsInSubset.count()); - append_cmap_sections(glyphToUnicode, &subset2, &buffer2, true, 0, 0xffff); + subset2.setAll(glyphsInSubset.begin(), glyphsInSubset.count()); + SkPDFAppendCmapSections(glyphToUnicode, &subset2, &buffer2, true, 0, 0xffff); char expectedResult2[] = "4 beginbfchar\n\ @@ -176,5 +179,7 @@ endbfrange\n"; REPORTER_ASSERT(reporter, stream_equals(buffer2, 0, expectedResult2, - buffer2.getOffset())); + buffer2.bytesWritten())); } + +#endif
diff --git a/src/third_party/skia/tests/PDFJpegEmbedTest.cpp b/src/third_party/skia/tests/PDFJpegEmbedTest.cpp index c1d0ea8..100c9b3 100644 --- a/src/third_party/skia/tests/PDFJpegEmbedTest.cpp +++ b/src/third_party/skia/tests/PDFJpegEmbedTest.cpp
@@ -5,26 +5,15 @@ * found in the LICENSE file. */ -#include "SkDocument.h" #include "SkCanvas.h" -#include "SkImageGenerator.h" #include "SkData.h" +#include "SkDocument.h" +#include "SkImageGenerator.h" #include "SkStream.h" -#include "SkDecodingImageGenerator.h" #include "Resources.h" #include "Test.h" -// Returned bitmap is lazy. Only lazy bitmaps hold onto the original data. -static SkBitmap bitmap_from_data(SkData* data) { - SkASSERT(data); - SkBitmap bm; - SkInstallDiscardablePixelRef( - SkDecodingImageGenerator::Create( - data, SkDecodingImageGenerator::Options()), &bm); - return bm; -} - static bool is_subset_of(SkData* smaller, SkData* larger) { SkASSERT(smaller && larger); if (smaller->size() > larger->size()) { @@ -41,15 +30,15 @@ } -static SkData* load_resource( +static sk_sp<SkData> load_resource( skiatest::Reporter* r, const char* test, const char* filename) { SkString path(GetResourcePath(filename)); - SkData* data = SkData::NewFromFileName(path.c_str()); - if (!data && r->verbose()) { - SkDebugf("\n%s: Resource '%s' can not be found.\n", - test, filename); + sk_sp<SkData> data(SkData::MakeFromFileName(path.c_str())); + if (!data) { + INFOF(r, "\n%s: Resource '%s' can not be found.\n", + test, filename); } - return data; // May return NULL. + return data; // May return nullptr. } /** @@ -57,45 +46,169 @@ * directly embedded into the PDF (without re-encoding) when that * makes sense. */ -DEF_TEST(PDFJpegEmbedTest, r) { - const char test[] = "PDFJpegEmbedTest"; - SkAutoTUnref<SkData> mandrillData( - load_resource(r, test, "mandrill_512_q075.jpg")); - SkAutoTUnref<SkData> cmykData(load_resource(r, test, "CMYK.jpg")); +DEF_TEST(SkPDF_JpegEmbedTest, r) { + REQUIRE_PDF_DOCUMENT(SkPDF_JpegEmbedTest, r); + const char test[] = "SkPDF_JpegEmbedTest"; + sk_sp<SkData> mandrillData(load_resource(r, test, "mandrill_512_q075.jpg")); + sk_sp<SkData> cmykData(load_resource(r, test, "CMYK.jpg")); if (!mandrillData || !cmykData) { return; } - + //////////////////////////////////////////////////////////////////////////// SkDynamicMemoryWStream pdf; - SkAutoTUnref<SkDocument> document(SkDocument::CreatePDF(&pdf)); + sk_sp<SkDocument> document(SkDocument::MakePDF(&pdf)); SkCanvas* canvas = document->beginPage(642, 1028); canvas->clear(SK_ColorLTGRAY); - SkBitmap bm1(bitmap_from_data(mandrillData)); - canvas->drawBitmap(bm1, 65.0, 0.0, NULL); - SkBitmap bm2(bitmap_from_data(cmykData)); - canvas->drawBitmap(bm2, 0.0, 512.0, NULL); + sk_sp<SkImage> im1(SkImage::MakeFromEncoded(mandrillData)); + canvas->drawImage(im1.get(), 65.0, 0.0, nullptr); + sk_sp<SkImage> im2(SkImage::MakeFromEncoded(cmykData)); + canvas->drawImage(im2.get(), 0.0, 512.0, nullptr); canvas->flush(); document->endPage(); document->close(); - SkAutoTUnref<SkData> pdfData(pdf.copyToData()); + sk_sp<SkData> pdfData = pdf.detachAsData(); SkASSERT(pdfData); - pdf.reset(); - REPORTER_ASSERT(r, is_subset_of(mandrillData, pdfData)); + REPORTER_ASSERT(r, is_subset_of(mandrillData.get(), pdfData.get())); // This JPEG uses a nonstandard colorspace - it can not be // embedded into the PDF directly. - REPORTER_ASSERT(r, !is_subset_of(cmykData, pdfData)); + REPORTER_ASSERT(r, !is_subset_of(cmykData.get(), pdfData.get())); +} - // The following is for debugging purposes only. - const char* outputPath = getenv("SKIA_TESTS_PDF_JPEG_EMBED_OUTPUT_PATH"); - if (outputPath) { - SkFILEWStream output(outputPath); - if (output.isValid()) { - output.write(pdfData->data(), pdfData->size()); +#ifdef SK_SUPPORT_PDF + +#include "SkJpegInfo.h" + +DEF_TEST(SkPDF_JpegIdentification, r) { + static struct { + const char* path; + bool isJfif; + SkJFIFInfo::Type type; + } kTests[] = {{"CMYK.jpg", false, SkJFIFInfo::kGrayscale}, + {"color_wheel.jpg", true, SkJFIFInfo::kYCbCr}, + {"grayscale.jpg", true, SkJFIFInfo::kGrayscale}, + {"mandrill_512_q075.jpg", true, SkJFIFInfo::kYCbCr}, + {"randPixels.jpg", true, SkJFIFInfo::kYCbCr}}; + for (size_t i = 0; i < SK_ARRAY_COUNT(kTests); ++i) { + sk_sp<SkData> data(load_resource(r, "JpegIdentification", kTests[i].path)); + if (!data) { + continue; } + SkJFIFInfo info; + bool isJfif = SkIsJFIF(data.get(), &info); + if (isJfif != kTests[i].isJfif) { + ERRORF(r, "%s failed isJfif test", kTests[i].path); + continue; + } + if (!isJfif) { + continue; // not applicable + } + if (kTests[i].type != info.fType) { + ERRORF(r, "%s failed jfif type test", kTests[i].path); + continue; + } + INFOF(r, "\nJpegIdentification: %s [%d x %d]\n", kTests[i].path, + info.fSize.width(), info.fSize.height()); + } + + // Test several malformed jpegs. + SkJFIFInfo info; + { + static const char goodJpeg[] = + "\377\330\377\340\0\20JFIF\0\1\1\0\0\1\0\1\0\0\377\333\0C\0\10\6\6\7" + "\6\5\10\7\7\7\t\t\10\n\14\24\r\14\13\13\14\31\22\23\17\24\35\32\37" + "\36\35\32\34\34 $.' \",#\34\34(7),01444\37'9=82<.342\377\333\0C\1\t" + "\t\t\14\13\14\30\r\r\0302!\34!222222222222222222222222222222222222" + "22222222222222\377\300\0\21\10\2\0\2\0\3\1\"\0\2\21\1\3\21\001"; + size_t goodJpegLength = 177; + auto data = SkData::MakeWithoutCopy(goodJpeg, goodJpegLength); + REPORTER_ASSERT(r, SkIsJFIF(data.get(), &info)); + REPORTER_ASSERT(r, info.fSize == SkISize::Make(512, 512)); + REPORTER_ASSERT(r, info.fType == SkJFIFInfo::kYCbCr); + + // Not long enough to read first (SOI) segment marker. + data = SkData::MakeWithoutCopy(goodJpeg, 1); + REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info)); + + // Not long enough to read second segment (APP0) marker. + data = SkData::MakeWithoutCopy(goodJpeg, 3); + REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info)); + + // Not long enough to read second segment's length. + data = SkData::MakeWithoutCopy(goodJpeg, 5); + REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info)); + + // APP0 segment is truncated. + data = SkData::MakeWithoutCopy(goodJpeg, 7); + REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info)); + + // Missing SOF segment. + data = SkData::MakeWithoutCopy(goodJpeg, 89); + REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info)); + } + { + // JFIF tag missing. + static const char jpeg[] = + "\377\330\377\340\0\20JFIX\0\1\1\0\0\1\0\1\0\0\377\333\0C\0\10\6\6\7" + "\6\5\10\7\7\7\t\t\10\n\14\24\r\14\13\13\14\31\22\23\17\24\35\32\37" + "\36\35\32\34\34 $.' \",#\34\34(7),01444\37'9=82<.342\377\333\0C\1\t" + "\t\t\14\13\14\30\r\r\0302!\34!222222222222222222222222222222222222" + "22222222222222\377\300\0\21\10\2\0\2\0\3\1\"\0\2\21\1\3\21\001"; + size_t jpegLength = 177; + auto data = SkData::MakeWithoutCopy(jpeg, jpegLength); + REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info)); + } + { + // APP0 segment short (byte 6 changed). + static const char jpeg[] = + "\377\330\377\340\0\5JFIF\0\1\1\0\0\1\0\1\0\0\377\333\0C\0\10\6\6\7" + "\6\5\10\7\7\7\t\t\10\n\14\24\r\14\13\13\14\31\22\23\17\24\35\32\37" + "\36\35\32\34\34 $.' \",#\34\34(7),01444\37'9=82<.342\377\333\0C\1\t" + "\t\t\14\13\14\30\r\r\0302!\34!222222222222222222222222222222222222" + "22222222222222\377\300\0\21\10\2\0\2\0\3\1\"\0\2\21\1\3\21\001"; + size_t jpegLength = 177; + auto data = SkData::MakeWithoutCopy(jpeg, jpegLength); + REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info)); + } + { + // SOF segment short. ('\21' replaced with '\5') + static const char jpeg[] = + "\377\330\377\340\0\20JFIF\0\1\1\0\0\1\0\1\0\0\377\333\0C\0\10\6\6\7" + "\6\5\10\7\7\7\t\t\10\n\14\24\r\14\13\13\14\31\22\23\17\24\35\32\37" + "\36\35\32\34\34 $.' \",#\34\34(7),01444\37'9=82<.342\377\333\0C\1\t" + "\t\t\14\13\14\30\r\r\0302!\34!222222222222222222222222222222222222" + "22222222222222\377\300\0\5\10\2\0\2\0\3\1\"\0\2\21\1\3\21\001"; + size_t jpegLength = 177; + auto data = SkData::MakeWithoutCopy(jpeg, jpegLength); + REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info)); + } + { + // Unsupported 12-bit components. ('\10' replaced with '\14') + static const char jpeg[] = + "\377\330\377\340\0\20JFIF\0\1\1\0\0\1\0\1\0\0\377\333\0C\0\10\6\6\7" + "\6\5\10\7\7\7\t\t\10\n\14\24\r\14\13\13\14\31\22\23\17\24\35\32\37" + "\36\35\32\34\34 $.' \",#\34\34(7),01444\37'9=82<.342\377\333\0C\1\t" + "\t\t\14\13\14\30\r\r\0302!\34!222222222222222222222222222222222222" + "22222222222222\377\300\0\21\14\2\0\2\0\3\1\"\0\2\21\1\3\21\001"; + size_t jpegLength = 177; + auto data = SkData::MakeWithoutCopy(jpeg, jpegLength); + REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info)); + } + { + // Two color channels. ('\3' replaced with '\2') + static const char jpeg[] = + "\377\330\377\340\0\20JFIF\0\1\1\0\0\1\0\1\0\0\377\333\0C\0\10\6\6\7" + "\6\5\10\7\7\7\t\t\10\n\14\24\r\14\13\13\14\31\22\23\17\24\35\32\37" + "\36\35\32\34\34 $.' \",#\34\34(7),01444\37'9=82<.342\377\333\0C\1\t" + "\t\t\14\13\14\30\r\r\0302!\34!222222222222222222222222222222222222" + "22222222222222\377\300\0\21\10\2\0\2\0\2\1\"\0\2\21\1\3\21\001"; + size_t jpegLength = 177; + auto data = SkData::MakeWithoutCopy(jpeg, jpegLength); + REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info)); } } +#endif
diff --git a/src/third_party/skia/tests/PDFMetadataAttributeTest.cpp b/src/third_party/skia/tests/PDFMetadataAttributeTest.cpp new file mode 100644 index 0000000..01351fa --- /dev/null +++ b/src/third_party/skia/tests/PDFMetadataAttributeTest.cpp
@@ -0,0 +1,58 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "SkDocument.h" +#include "SkStream.h" +#include "SkData.h" +#include "Test.h" + +DEF_TEST(SkPDF_Metadata, r) { + REQUIRE_PDF_DOCUMENT(SkPDF_Metadata, r); + SkTime::DateTime now; + SkTime::GetDateTime(&now); + SkDocument::PDFMetadata metadata; + metadata.fTitle = "A1"; + metadata.fAuthor = "A2"; + metadata.fSubject = "A3"; + metadata.fKeywords = "A4"; + metadata.fCreator = "A5"; + metadata.fCreation.fEnabled = true; + metadata.fCreation.fDateTime = now; + metadata.fModified.fEnabled = true; + metadata.fModified.fDateTime = now; + + SkDynamicMemoryWStream pdf; + sk_sp<SkDocument> doc = SkDocument::MakePDF(&pdf, SK_ScalarDefaultRasterDPI, + metadata, nullptr, false); + doc->beginPage(612.0f, 792.0f); + doc->close(); + sk_sp<SkData> data = pdf.detachAsData(); + static const char* expectations[] = { + "/Title (A1)", + "/Author (A2)", + "/Subject (A3)", + "/Keywords (A4)", + "/Creator (A5)", + "/Producer (Skia/PDF ", + "/CreationDate (D:", + "/ModDate (D:" + }; + const uint8_t* bytes = data->bytes(); + for (const char* expectation : expectations) { + size_t len = strlen(expectation); + bool found = false; + size_t N = 1 + data->size() - len; + for (size_t i = 0; i < N; ++i) { + if (0 == memcmp(bytes + i, expectation, len)) { + found = true; + break; + } + } + if (!found) { + ERRORF(r, "expectation missing: '%s'.", expectation); + } + } +}
diff --git a/src/third_party/skia/tests/PDFOpaqueSrcModeToSrcOverTest.cpp b/src/third_party/skia/tests/PDFOpaqueSrcModeToSrcOverTest.cpp new file mode 100644 index 0000000..8b3cfe0 --- /dev/null +++ b/src/third_party/skia/tests/PDFOpaqueSrcModeToSrcOverTest.cpp
@@ -0,0 +1,47 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "SkCanvas.h" +#include "SkDocument.h" +#include "SkStream.h" +#include "Test.h" + +static void run_test(SkWStream* out, SkBlendMode mode, U8CPU alpha) { + sk_sp<SkDocument> pdfDoc(SkDocument::MakePDF(out)); + SkCanvas* c = pdfDoc->beginPage(612.0f, 792.0f); + SkPaint black; + SkPaint background; + background.setColor(SK_ColorWHITE); + background.setAlpha(alpha); + background.setBlendMode(mode); + c->drawRect(SkRect::MakeWH(612.0f, 792.0f), background); + c->drawRect(SkRect::MakeXYWH(36.0f, 36.0f, 9.0f, 9.0f), black); + c->drawRect(SkRect::MakeXYWH(72.0f, 72.0f, 468.0f, 648.0f), background); + c->drawRect(SkRect::MakeXYWH(108.0f, 108.0f, 9.0f, 9.0f), black); + pdfDoc->close(); +} + +// http://crbug.com/473572 +DEF_TEST(SkPDF_OpaqueSrcModeToSrcOver, r) { + REQUIRE_PDF_DOCUMENT(SkPDF_OpaqueSrcModeToSrcOver, r); + SkDynamicMemoryWStream srcMode; + SkDynamicMemoryWStream srcOverMode; + + U8CPU alpha = SK_AlphaOPAQUE; + run_test(&srcMode, SkBlendMode::kSrc, alpha); + run_test(&srcOverMode, SkBlendMode::kSrcOver, alpha); + REPORTER_ASSERT(r, srcMode.bytesWritten() == srcOverMode.bytesWritten()); + // The two PDFs should be equal because they have an opaque alpha. + + srcMode.reset(); + srcOverMode.reset(); + + alpha = 0x80; + run_test(&srcMode, SkBlendMode::kSrc, alpha); + run_test(&srcOverMode, SkBlendMode::kSrcOver, alpha); + REPORTER_ASSERT(r, srcMode.bytesWritten() > srcOverMode.bytesWritten()); + // The two PDFs should not be equal because they have a non-opaque alpha. +}
diff --git a/src/third_party/skia/tests/PDFPrimitivesTest.cpp b/src/third_party/skia/tests/PDFPrimitivesTest.cpp index 05677cd..766bbef 100644 --- a/src/third_party/skia/tests/PDFPrimitivesTest.cpp +++ b/src/third_party/skia/tests/PDFPrimitivesTest.cpp
@@ -5,322 +5,154 @@ * found in the LICENSE file. */ +#include "Test.h" + +#ifdef SK_SUPPORT_PDF + +#include "Resources.h" #include "SkBitmap.h" #include "SkCanvas.h" #include "SkData.h" -#include "SkFlate.h" +#include "SkDocument.h" +#include "SkDeflate.h" #include "SkImageEncoder.h" +#include "SkMakeUnique.h" #include "SkMatrix.h" -#include "SkPDFCatalog.h" +#include "SkPDFCanon.h" #include "SkPDFDevice.h" -#include "SkPDFStream.h" +#include "SkPDFFont.h" #include "SkPDFTypes.h" +#include "SkPDFUtils.h" #include "SkReadBuffer.h" #include "SkScalar.h" +#include "SkSpecialImage.h" #include "SkStream.h" #include "SkTypes.h" -#include "Test.h" +#include "sk_tool_utils.h" -class SkPDFTestDict : public SkPDFDict { -public: - virtual void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, - SkTSet<SkPDFObject*>* newResourceObjects) { - for (int i = 0; i < fResources.count(); i++) { - newResourceObjects->add(fResources[i]); - fResources[i]->ref(); - } - } - - void addResource(SkPDFObject* object) { - fResources.append(1, &object); - } - -private: - SkTDArray<SkPDFObject*> fResources; -}; +#include <cstdlib> +#include <cmath> #define DUMMY_TEXT "DCT compessed stream." -static SkData* encode_to_dct_data(size_t* pixelRefOffset, const SkBitmap& bitmap) { - *pixelRefOffset = 0; - return SkData::NewWithProc(DUMMY_TEXT, sizeof(DUMMY_TEXT) - 1, NULL, NULL); -} - -static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset, - const void* buffer, size_t len) { - SkAutoDataUnref data(stream.copyToData()); - if (offset + len > data->size()) { - return false; - } - return memcmp(data->bytes() + offset, buffer, len) == 0; -} - -static bool stream_contains(const SkDynamicMemoryWStream& stream, - const char* buffer) { - SkAutoDataUnref data(stream.copyToData()); - int len = strlen(buffer); // our buffer does not have EOSs. - - for (int offset = 0 ; offset < (int)data->size() - len; offset++) { - if (memcmp(data->bytes() + offset, buffer, len) == 0) { - return true; - } - } - - return false; -} - -static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj, - const char* expectedData, size_t expectedSize, - bool indirect, bool compression) { - SkPDFDocument::Flags docFlags = (SkPDFDocument::Flags) 0; - if (!compression) { - docFlags = SkTBitOr(docFlags, SkPDFDocument::kFavorSpeedOverSize_Flags); - } - SkPDFCatalog catalog(docFlags); - size_t directSize = obj->getOutputSize(&catalog, false); - REPORTER_ASSERT(reporter, directSize == expectedSize); - +template <typename T> +static SkString emit_to_string(T& obj, SkPDFObjNumMap* catPtr = nullptr) { + SkPDFObjNumMap catalog; SkDynamicMemoryWStream buffer; - obj->emit(&buffer, &catalog, false); - REPORTER_ASSERT(reporter, directSize == buffer.getOffset()); - REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedData, - directSize)); + if (!catPtr) { + catPtr = &catalog; + } + obj.emitObject(&buffer, *catPtr); + SkString tmp(buffer.bytesWritten()); + buffer.copyTo(tmp.writable_str()); + return tmp; +} - if (indirect) { - // Indirect output. - static char header[] = "1 0 obj\n"; - static size_t headerLen = strlen(header); - static char footer[] = "\nendobj\n"; - static size_t footerLen = strlen(footer); +static bool eq(const SkString& str, const char* strPtr, size_t len) { + return len == str.size() && 0 == memcmp(str.c_str(), strPtr, len); +} - catalog.addObject(obj, false); - - size_t indirectSize = obj->getOutputSize(&catalog, true); - REPORTER_ASSERT(reporter, - indirectSize == directSize + headerLen + footerLen); - - buffer.reset(); - obj->emit(&buffer, &catalog, true); - REPORTER_ASSERT(reporter, indirectSize == buffer.getOffset()); - REPORTER_ASSERT(reporter, stream_equals(buffer, 0, header, headerLen)); - REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen, expectedData, - directSize)); - REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen + directSize, - footer, footerLen)); +static void assert_eql(skiatest::Reporter* reporter, + const SkString& skString, + const char* str, + size_t len) { + if (!eq(skString, str, len)) { + REPORT_FAILURE(reporter, "", SkStringPrintf( + "'%*s' != '%s'", len, str, skString.c_str())); } } -static void SimpleCheckObjectOutput(skiatest::Reporter* reporter, - SkPDFObject* obj, - const char* expectedResult) { - CheckObjectOutput(reporter, obj, expectedResult, - strlen(expectedResult), true, false); +static void assert_eq(skiatest::Reporter* reporter, + const SkString& skString, + const char* str) { + assert_eql(reporter, skString, str, strlen(str)); +} + + +template <typename T> +static void assert_emit_eq(skiatest::Reporter* reporter, + T& object, + const char* string) { + SkString result = emit_to_string(object); + assert_eq(reporter, result, string); } static void TestPDFStream(skiatest::Reporter* reporter) { char streamBytes[] = "Test\nFoo\tBar"; - SkAutoTUnref<SkMemoryStream> streamData(new SkMemoryStream( - streamBytes, strlen(streamBytes), true)); - SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData.get())); - SimpleCheckObjectOutput( - reporter, stream.get(), - "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream"); - stream->insert("Attribute", new SkPDFInt(42))->unref(); - SimpleCheckObjectOutput(reporter, stream.get(), - "<</Length 12\n/Attribute 42\n>> stream\n" - "Test\nFoo\tBar\nendstream"); + auto streamData = skstd::make_unique<SkMemoryStream>( + streamBytes, strlen(streamBytes), true); + auto stream = sk_make_sp<SkPDFStream>(std::move(streamData)); + assert_emit_eq(reporter, + *stream, + "<</Length 12>> stream\nTest\nFoo\tBar\nendstream"); + stream->dict()->insertInt("Attribute", 42); + assert_emit_eq(reporter, + *stream, + "<</Length 12\n/Attribute 42>> stream\n" + "Test\nFoo\tBar\nendstream"); - if (SkFlate::HaveFlate()) { + { char streamBytes2[] = "This is a longer string, so that compression " "can do something with it. With shorter strings, " "the short circuit logic cuts in and we end up " "with an uncompressed string."; - SkAutoDataUnref streamData2(SkData::NewWithCopy(streamBytes2, - strlen(streamBytes2))); - SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData2.get())); + auto stream = sk_make_sp<SkPDFStream>( + SkData::MakeWithCopy(streamBytes2, strlen(streamBytes2))); SkDynamicMemoryWStream compressedByteStream; - SkFlate::Deflate(streamData2.get(), &compressedByteStream); - SkAutoDataUnref compressedData(compressedByteStream.copyToData()); + SkDeflateWStream deflateWStream(&compressedByteStream); + deflateWStream.write(streamBytes2, strlen(streamBytes2)); + deflateWStream.finalize(); - // Check first without compression. - SkDynamicMemoryWStream expectedResult1; - expectedResult1.writeText("<</Length 167\n>> stream\n"); - expectedResult1.writeText(streamBytes2); - expectedResult1.writeText("\nendstream"); - SkAutoDataUnref expectedResultData1(expectedResult1.copyToData()); - CheckObjectOutput(reporter, stream.get(), - (const char*) expectedResultData1->data(), - expectedResultData1->size(), true, false); - - // Then again with compression. - SkDynamicMemoryWStream expectedResult2; - expectedResult2.writeText("<</Filter /FlateDecode\n/Length 116\n" - ">> stream\n"); - expectedResult2.write(compressedData->data(), compressedData->size()); - expectedResult2.writeText("\nendstream"); - SkAutoDataUnref expectedResultData2(expectedResult2.copyToData()); - CheckObjectOutput(reporter, stream.get(), - (const char*) expectedResultData2->data(), - expectedResultData2->size(), true, true); + SkDynamicMemoryWStream expected; + expected.writeText("<</Filter /FlateDecode\n/Length 116>> stream\n"); + compressedByteStream.writeToStream(&expected); + compressedByteStream.reset(); + expected.writeText("\nendstream"); + sk_sp<SkData> expectedResultData2(expected.detachAsData()); + SkString result = emit_to_string(*stream); + #ifndef SK_PDF_LESS_COMPRESSION + assert_eql(reporter, + result, + (const char*)expectedResultData2->data(), + expectedResultData2->size()); + #endif } } -static void TestCatalog(skiatest::Reporter* reporter) { - SkPDFCatalog catalog((SkPDFDocument::Flags)0); - SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1)); - SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2)); - SkAutoTUnref<SkPDFInt> int3(new SkPDFInt(3)); - int1.get()->ref(); - SkAutoTUnref<SkPDFInt> int1Again(int1.get()); +static void TestObjectNumberMap(skiatest::Reporter* reporter) { + SkPDFObjNumMap objNumMap; + sk_sp<SkPDFArray> a1(new SkPDFArray); + sk_sp<SkPDFArray> a2(new SkPDFArray); + sk_sp<SkPDFArray> a3(new SkPDFArray); - catalog.addObject(int1.get(), false); - catalog.addObject(int2.get(), false); - catalog.addObject(int3.get(), false); + objNumMap.addObject(a1.get()); + objNumMap.addObject(a2.get()); + objNumMap.addObject(a3.get()); - REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3); - REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3); - REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int3.get()) == 3); - - SkDynamicMemoryWStream buffer; - catalog.emitObjectNumber(&buffer, int1.get()); - catalog.emitObjectNumber(&buffer, int2.get()); - catalog.emitObjectNumber(&buffer, int3.get()); - catalog.emitObjectNumber(&buffer, int1Again.get()); - char expectedResult[] = "1 02 03 01 0"; - REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult, - strlen(expectedResult))); + // The objects should be numbered in the order they are added, + // starting with 1. + REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a1.get()) == 1); + REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a2.get()) == 2); + REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a3.get()) == 3); + // Assert that repeated calls to get the object number return + // consistent result. + REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a1.get()) == 1); } static void TestObjectRef(skiatest::Reporter* reporter) { - SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1)); - SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2)); - SkAutoTUnref<SkPDFObjRef> int2ref(new SkPDFObjRef(int2.get())); + sk_sp<SkPDFArray> a1(new SkPDFArray); + sk_sp<SkPDFArray> a2(new SkPDFArray); + a2->appendObjRef(a1); - SkPDFCatalog catalog((SkPDFDocument::Flags)0); - catalog.addObject(int1.get(), false); - catalog.addObject(int2.get(), false); - REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3); - REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3); + SkPDFObjNumMap catalog; + catalog.addObject(a1.get()); + REPORTER_ASSERT(reporter, catalog.getObjectNumber(a1.get()) == 1); - char expectedResult[] = "2 0 R"; - SkDynamicMemoryWStream buffer; - int2ref->emitObject(&buffer, &catalog, false); - REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult)); - REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult, - buffer.getOffset())); -} - -static void TestSubstitute(skiatest::Reporter* reporter) { - SkAutoTUnref<SkPDFTestDict> proxy(new SkPDFTestDict()); - SkAutoTUnref<SkPDFTestDict> stub(new SkPDFTestDict()); - SkAutoTUnref<SkPDFInt> int33(new SkPDFInt(33)); - SkAutoTUnref<SkPDFDict> stubResource(new SkPDFDict()); - SkAutoTUnref<SkPDFInt> int44(new SkPDFInt(44)); - - stub->insert("Value", int33.get()); - stubResource->insert("InnerValue", int44.get()); - stub->addResource(stubResource.get()); - - SkPDFCatalog catalog((SkPDFDocument::Flags)0); - catalog.addObject(proxy.get(), false); - catalog.setSubstitute(proxy.get(), stub.get()); - - SkDynamicMemoryWStream buffer; - proxy->emit(&buffer, &catalog, false); - catalog.emitSubstituteResources(&buffer, false); - - char objectResult[] = "2 0 obj\n<</Value 33\n>>\nendobj\n"; - REPORTER_ASSERT( - reporter, - catalog.setFileOffset(proxy.get(), 0) == strlen(objectResult)); - - char expectedResult[] = - "<</Value 33\n>>1 0 obj\n<</InnerValue 44\n>>\nendobj\n"; - REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult)); - REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult, - buffer.getOffset())); -} - -// Create a bitmap that would be very eficiently compressed in a ZIP. -static void setup_bitmap(SkBitmap* bitmap, int width, int height) { - bitmap->allocN32Pixels(width, height); - bitmap->eraseColor(SK_ColorWHITE); -} - -static void TestImage(skiatest::Reporter* reporter, const SkBitmap& bitmap, - const char* expected, bool useDCTEncoder) { - SkISize pageSize = SkISize::Make(bitmap.width(), bitmap.height()); - SkAutoTUnref<SkPDFDevice> dev(new SkPDFDevice(pageSize, pageSize, SkMatrix::I())); - - if (useDCTEncoder) { - dev->setDCTEncoder(encode_to_dct_data); - } - - SkCanvas c(dev); - c.drawBitmap(bitmap, 0, 0, NULL); - - SkPDFDocument doc; - doc.appendPage(dev); - - SkDynamicMemoryWStream stream; - doc.emitPDF(&stream); - - REPORTER_ASSERT(reporter, stream_contains(stream, expected)); -} - -static void TestUncompressed(skiatest::Reporter* reporter) { - SkBitmap bitmap; - setup_bitmap(&bitmap, 1, 1); - TestImage(reporter, bitmap, - "/Subtype /Image\n" - "/Width 1\n" - "/Height 1\n" - "/ColorSpace /DeviceRGB\n" - "/BitsPerComponent 8\n" - "/Length 3\n" - ">> stream", - true); -} - -static void TestFlateDecode(skiatest::Reporter* reporter) { - if (!SkFlate::HaveFlate()) { - return; - } - SkBitmap bitmap; - setup_bitmap(&bitmap, 10, 10); - TestImage(reporter, bitmap, - "/Subtype /Image\n" - "/Width 10\n" - "/Height 10\n" - "/ColorSpace /DeviceRGB\n" - "/BitsPerComponent 8\n" - "/Filter /FlateDecode\n" - "/Length 13\n" - ">> stream", - false); -} - -static void TestDCTDecode(skiatest::Reporter* reporter) { - SkBitmap bitmap; - setup_bitmap(&bitmap, 32, 32); - TestImage(reporter, bitmap, - "/Subtype /Image\n" - "/Width 32\n" - "/Height 32\n" - "/ColorSpace /DeviceRGB\n" - "/BitsPerComponent 8\n" - "/Filter /DCTDecode\n" - "/ColorTransform 0\n" - "/Length 21\n" - ">> stream", - true); -} - -static void TestImages(skiatest::Reporter* reporter) { - TestUncompressed(reporter); - TestFlateDecode(reporter); - TestDCTDecode(reporter); + SkString result = emit_to_string(*a2, &catalog); + // If appendObjRef misbehaves, then the result would + // be [[]], not [1 0 R]. + assert_eq(reporter, result, "[1 0 R]"); } // This test used to assert without the fix submitted for @@ -328,155 +160,336 @@ // SKP files might have invalid glyph ids. This test ensures they are ignored, // and there is no assert on input data in Debug mode. static void test_issue1083() { - SkISize pageSize = SkISize::Make(100, 100); - SkAutoTUnref<SkPDFDevice> dev(new SkPDFDevice(pageSize, pageSize, SkMatrix::I())); - - SkCanvas c(dev); + SkDynamicMemoryWStream outStream; + sk_sp<SkDocument> doc(SkDocument::MakePDF(&outStream)); + SkCanvas* canvas = doc->beginPage(100.0f, 100.0f); SkPaint paint; paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); uint16_t glyphID = 65000; - c.drawText(&glyphID, 2, 0, 0, paint); + canvas->drawText(&glyphID, 2, 0, 0, paint); - SkPDFDocument doc; - doc.appendPage(dev); - - SkDynamicMemoryWStream stream; - doc.emitPDF(&stream); + doc->close(); } -DEF_TEST(PDFPrimitives, reporter) { - SkAutoTUnref<SkPDFInt> int42(new SkPDFInt(42)); - SimpleCheckObjectOutput(reporter, int42.get(), "42"); +static void assert_emit_eq_number(skiatest::Reporter* reporter, float number) { + SkPDFUnion pdfUnion = SkPDFUnion::Scalar(number); + SkString result = emit_to_string(pdfUnion); + float value = static_cast<float>(std::atof(result.c_str())); + if (value != number) { + ERRORF(reporter, "%.9g != %s", number, result.c_str()); + } +} - SkAutoTUnref<SkPDFScalar> realHalf(new SkPDFScalar(SK_ScalarHalf)); - SimpleCheckObjectOutput(reporter, realHalf.get(), "0.5"); - SkAutoTUnref<SkPDFScalar> bigScalar(new SkPDFScalar(110999.75f)); -#if !defined(SK_ALLOW_LARGE_PDF_SCALARS) - SimpleCheckObjectOutput(reporter, bigScalar.get(), "111000"); -#else - SimpleCheckObjectOutput(reporter, bigScalar.get(), "110999.75"); +static void TestPDFUnion(skiatest::Reporter* reporter) { + SkPDFUnion boolTrue = SkPDFUnion::Bool(true); + assert_emit_eq(reporter, boolTrue, "true"); - SkAutoTUnref<SkPDFScalar> biggerScalar(new SkPDFScalar(50000000.1)); - SimpleCheckObjectOutput(reporter, biggerScalar.get(), "50000000"); + SkPDFUnion boolFalse = SkPDFUnion::Bool(false); + assert_emit_eq(reporter, boolFalse, "false"); - SkAutoTUnref<SkPDFScalar> smallestScalar(new SkPDFScalar(1.0/65536)); - SimpleCheckObjectOutput(reporter, smallestScalar.get(), "0.00001526"); -#endif + SkPDFUnion int42 = SkPDFUnion::Int(42); + assert_emit_eq(reporter, int42, "42"); - SkAutoTUnref<SkPDFString> stringSimple( - new SkPDFString("test ) string ( foo")); - SimpleCheckObjectOutput(reporter, stringSimple.get(), - "(test \\) string \\( foo)"); - SkAutoTUnref<SkPDFString> stringComplex( - new SkPDFString("\ttest ) string ( foo")); - SimpleCheckObjectOutput(reporter, stringComplex.get(), - "<0974657374202920737472696E67202820666F6F>"); + assert_emit_eq_number(reporter, SK_ScalarHalf); + assert_emit_eq_number(reporter, 110999.75f); // bigScalar + assert_emit_eq_number(reporter, 50000000.1f); // biggerScalar + assert_emit_eq_number(reporter, 1.0f / 65536); // smallScalar - SkAutoTUnref<SkPDFName> name(new SkPDFName("Test name\twith#tab")); - const char expectedResult[] = "/Test#20name#09with#23tab"; - CheckObjectOutput(reporter, name.get(), expectedResult, - strlen(expectedResult), false, false); + SkPDFUnion stringSimple = SkPDFUnion::String("test ) string ( foo"); + assert_emit_eq(reporter, stringSimple, "(test \\) string \\( foo)"); - SkAutoTUnref<SkPDFName> escapedName(new SkPDFName("A#/%()<>[]{}B")); - const char escapedNameExpected[] = "/A#23#2F#25#28#29#3C#3E#5B#5D#7B#7DB"; - CheckObjectOutput(reporter, escapedName.get(), escapedNameExpected, - strlen(escapedNameExpected), false, false); + SkString stringComplexInput("\ttest ) string ( foo"); + SkPDFUnion stringComplex = SkPDFUnion::String(stringComplexInput); + assert_emit_eq(reporter, stringComplex, "(\\011test \\) string \\( foo)"); + + SkString binaryStringInput("\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20"); + SkPDFUnion binaryString = SkPDFUnion::String(binaryStringInput); + assert_emit_eq(reporter, binaryString, "<0102030405060708090A0B0C0D0E0F10>"); + + SkString nameInput("Test name\twith#tab"); + SkPDFUnion name = SkPDFUnion::Name(nameInput); + assert_emit_eq(reporter, name, "/Test#20name#09with#23tab"); + + SkString nameInput2("A#/%()<>[]{}B"); + SkPDFUnion name2 = SkPDFUnion::Name(nameInput2); + assert_emit_eq(reporter, name2, "/A#23#2F#25#28#29#3C#3E#5B#5D#7B#7DB"); + + SkPDFUnion name3 = SkPDFUnion::Name("SimpleNameWithOnlyPrintableASCII"); + assert_emit_eq(reporter, name3, "/SimpleNameWithOnlyPrintableASCII"); // Test that we correctly handle characters with the high-bit set. - const unsigned char highBitCString[] = {0xDE, 0xAD, 'b', 'e', 0xEF, 0}; - SkAutoTUnref<SkPDFName> highBitName( - new SkPDFName((const char*)highBitCString)); - const char highBitExpectedResult[] = "/#DE#ADbe#EF"; - CheckObjectOutput(reporter, highBitName.get(), highBitExpectedResult, - strlen(highBitExpectedResult), false, false); + SkString highBitString("\xDE\xAD" "be\xEF"); + SkPDFUnion highBitName = SkPDFUnion::Name(highBitString); + assert_emit_eq(reporter, highBitName, "/#DE#ADbe#EF"); +} - SkAutoTUnref<SkPDFArray> array(new SkPDFArray); - SimpleCheckObjectOutput(reporter, array.get(), "[]"); - array->append(int42.get()); - SimpleCheckObjectOutput(reporter, array.get(), "[42]"); - array->append(realHalf.get()); - SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5]"); - SkAutoTUnref<SkPDFInt> int0(new SkPDFInt(0)); - array->append(int0.get()); - SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5 0]"); - SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1)); - array->setAt(0, int1.get()); - SimpleCheckObjectOutput(reporter, array.get(), "[1 0.5 0]"); +static void TestPDFArray(skiatest::Reporter* reporter) { + sk_sp<SkPDFArray> array(new SkPDFArray); + assert_emit_eq(reporter, *array, "[]"); - SkAutoTUnref<SkPDFDict> dict(new SkPDFDict); - SimpleCheckObjectOutput(reporter, dict.get(), "<<>>"); - SkAutoTUnref<SkPDFName> n1(new SkPDFName("n1")); - dict->insert(n1.get(), int42.get()); - SimpleCheckObjectOutput(reporter, dict.get(), "<</n1 42\n>>"); - SkAutoTUnref<SkPDFName> n2(new SkPDFName("n2")); - SkAutoTUnref<SkPDFName> n3(new SkPDFName("n3")); - dict->insert(n2.get(), realHalf.get()); - dict->insert(n3.get(), array.get()); - SimpleCheckObjectOutput(reporter, dict.get(), - "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]\n>>"); + array->appendInt(42); + assert_emit_eq(reporter, *array, "[42]"); + array->appendScalar(SK_ScalarHalf); + assert_emit_eq(reporter, *array, "[42 .5]"); + + array->appendInt(0); + assert_emit_eq(reporter, *array, "[42 .5 0]"); + + array->appendBool(true); + assert_emit_eq(reporter, *array, "[42 .5 0 true]"); + + array->appendName("ThisName"); + assert_emit_eq(reporter, *array, "[42 .5 0 true /ThisName]"); + + array->appendName(SkString("AnotherName")); + assert_emit_eq(reporter, *array, "[42 .5 0 true /ThisName /AnotherName]"); + + array->appendString("This String"); + assert_emit_eq(reporter, *array, + "[42 .5 0 true /ThisName /AnotherName (This String)]"); + + array->appendString(SkString("Another String")); + assert_emit_eq(reporter, *array, + "[42 .5 0 true /ThisName /AnotherName (This String) " + "(Another String)]"); + + sk_sp<SkPDFArray> innerArray(new SkPDFArray); + innerArray->appendInt(-1); + array->appendObject(std::move(innerArray)); + assert_emit_eq(reporter, *array, + "[42 .5 0 true /ThisName /AnotherName (This String) " + "(Another String) [-1]]"); + + sk_sp<SkPDFArray> referencedArray(new SkPDFArray); + SkPDFObjNumMap catalog; + catalog.addObject(referencedArray.get()); + REPORTER_ASSERT(reporter, catalog.getObjectNumber( + referencedArray.get()) == 1); + array->appendObjRef(std::move(referencedArray)); + + SkString result = emit_to_string(*array, &catalog); + assert_eq(reporter, result, + "[42 .5 0 true /ThisName /AnotherName (This String) " + "(Another String) [-1] 1 0 R]"); +} + +static void TestPDFDict(skiatest::Reporter* reporter) { + sk_sp<SkPDFDict> dict(new SkPDFDict); + assert_emit_eq(reporter, *dict, "<<>>"); + + dict->insertInt("n1", SkToSizeT(42)); + assert_emit_eq(reporter, *dict, "<</n1 42>>"); + + dict.reset(new SkPDFDict); + assert_emit_eq(reporter, *dict, "<<>>"); + + dict->insertInt("n1", 42); + assert_emit_eq(reporter, *dict, "<</n1 42>>"); + + dict->insertScalar("n2", SK_ScalarHalf); + + SkString n3("n3"); + sk_sp<SkPDFArray> innerArray(new SkPDFArray); + innerArray->appendInt(-100); + dict->insertObject(n3, std::move(innerArray)); + assert_emit_eq(reporter, *dict, "<</n1 42\n/n2 .5\n/n3 [-100]>>"); + + dict.reset(new SkPDFDict); + assert_emit_eq(reporter, *dict, "<<>>"); + + dict->insertInt("n1", 24); + assert_emit_eq(reporter, *dict, "<</n1 24>>"); + + dict->insertInt("n2", SkToSizeT(99)); + assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99>>"); + + dict->insertScalar("n3", SK_ScalarHalf); + assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5>>"); + + dict->insertName("n4", "AName"); + assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName>>"); + + dict->insertName("n5", SkString("AnotherName")); + assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n" + "/n5 /AnotherName>>"); + + dict->insertString("n6", "A String"); + assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n" + "/n5 /AnotherName\n/n6 (A String)>>"); + + dict->insertString("n7", SkString("Another String")); + assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n" + "/n5 /AnotherName\n/n6 (A String)\n/n7 (Another String)>>"); + + dict.reset(new SkPDFDict("DType")); + assert_emit_eq(reporter, *dict, "<</Type /DType>>"); + + sk_sp<SkPDFArray> referencedArray(new SkPDFArray); + SkPDFObjNumMap catalog; + catalog.addObject(referencedArray.get()); + REPORTER_ASSERT(reporter, catalog.getObjectNumber( + referencedArray.get()) == 1); + dict->insertObjRef("n1", std::move(referencedArray)); + SkString result = emit_to_string(*dict, &catalog); + assert_eq(reporter, result, "<</Type /DType\n/n1 1 0 R>>"); +} + +DEF_TEST(SkPDF_Primitives, reporter) { + TestPDFUnion(reporter); + TestPDFArray(reporter); + TestPDFDict(reporter); TestPDFStream(reporter); - - TestCatalog(reporter); - + TestObjectNumberMap(reporter); TestObjectRef(reporter); - - TestSubstitute(reporter); - test_issue1083(); - - TestImages(reporter); } namespace { class DummyImageFilter : public SkImageFilter { public: - DummyImageFilter(bool visited = false) : SkImageFilter(0, NULL), fVisited(visited) {} - virtual ~DummyImageFilter() SK_OVERRIDE {} - virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* offset) const { - fVisited = true; - offset->fX = offset->fY = 0; - *result = src; - return true; + static sk_sp<DummyImageFilter> Make(bool visited = false) { + return sk_sp<DummyImageFilter>(new DummyImageFilter(visited)); } + + SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(DummyImageFilter) -#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING - explicit DummyImageFilter(SkReadBuffer& buffer) : SkImageFilter(0, NULL) { - fVisited = buffer.readBool(); - } -#endif bool visited() const { return fVisited; } +protected: + sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&, + SkIPoint* offset) const override { + fVisited = true; + offset->fX = offset->fY = 0; + return sk_ref_sp<SkSpecialImage>(source); + } + sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override { + return sk_ref_sp(const_cast<DummyImageFilter*>(this)); + } + private: + DummyImageFilter(bool visited) : INHERITED(nullptr, 0, nullptr), fVisited(visited) {} + mutable bool fVisited; + + typedef SkImageFilter INHERITED; }; -SkFlattenable* DummyImageFilter::CreateProc(SkReadBuffer& buffer) { +sk_sp<SkFlattenable> DummyImageFilter::CreateProc(SkReadBuffer& buffer) { SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0); bool visited = buffer.readBool(); - return SkNEW_ARGS(DummyImageFilter, (visited)); + return DummyImageFilter::Make(visited); } +#ifndef SK_IGNORE_TO_STRING +void DummyImageFilter::toString(SkString* str) const { + str->appendf("DummyImageFilter: ("); + str->append(")"); +} +#endif + }; // Check that PDF rendering of image filters successfully falls back to // CPU rasterization. -DEF_TEST(PDFImageFilter, reporter) { - SkISize pageSize = SkISize::Make(100, 100); - SkAutoTUnref<SkPDFDevice> device(new SkPDFDevice(pageSize, pageSize, SkMatrix::I())); - SkCanvas canvas(device.get()); - SkAutoTUnref<DummyImageFilter> filter(new DummyImageFilter()); +DEF_TEST(SkPDF_ImageFilter, reporter) { + REQUIRE_PDF_DOCUMENT(SkPDF_ImageFilter, reporter); + SkDynamicMemoryWStream stream; + sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream)); + SkCanvas* canvas = doc->beginPage(100.0f, 100.0f); + + sk_sp<DummyImageFilter> filter(DummyImageFilter::Make()); // Filter just created; should be unvisited. REPORTER_ASSERT(reporter, !filter->visited()); SkPaint paint; - paint.setImageFilter(filter.get()); - canvas.drawRect(SkRect::MakeWH(100, 100), paint); + paint.setImageFilter(filter); + canvas->drawRect(SkRect::MakeWH(100, 100), paint); + doc->close(); // Filter was used in rendering; should be visited. REPORTER_ASSERT(reporter, filter->visited()); } + +// Check that PDF rendering of image filters successfully falls back to +// CPU rasterization. +DEF_TEST(SkPDF_FontCanEmbedTypeface, reporter) { + SkPDFCanon canon; + + const char resource[] = "fonts/Roboto2-Regular_NoEmbed.ttf"; + sk_sp<SkTypeface> noEmbedTypeface(MakeResourceAsTypeface(resource)); + if (noEmbedTypeface) { + REPORTER_ASSERT(reporter, + !SkPDFFont::CanEmbedTypeface(noEmbedTypeface.get(), &canon)); + } + sk_sp<SkTypeface> portableTypeface( + sk_tool_utils::create_portable_typeface(NULL, SkFontStyle())); + REPORTER_ASSERT(reporter, + SkPDFFont::CanEmbedTypeface(portableTypeface.get(), &canon)); +} + + +// test to see that all finite scalars round trip via scanf(). +static void check_pdf_scalar_serialization( + skiatest::Reporter* reporter, float inputFloat) { + char floatString[SkPDFUtils::kMaximumFloatDecimalLength]; + size_t len = SkPDFUtils::FloatToDecimal(inputFloat, floatString); + if (len >= sizeof(floatString)) { + ERRORF(reporter, "string too long: %u", (unsigned)len); + return; + } + if (floatString[len] != '\0' || strlen(floatString) != len) { + ERRORF(reporter, "terminator misplaced."); + return; // The terminator is needed for sscanf(). + } + if (reporter->verbose()) { + SkDebugf("%15.9g = \"%s\"\n", inputFloat, floatString); + } + float roundTripFloat; + if (1 != sscanf(floatString, "%f", &roundTripFloat)) { + ERRORF(reporter, "unscannable result: %s", floatString); + return; + } + if (std::isfinite(inputFloat) && roundTripFloat != inputFloat) { + ERRORF(reporter, "roundTripFloat (%.9g) != inputFloat (%.9g)", + roundTripFloat, inputFloat); + } +} + +// Test SkPDFUtils::AppendScalar for accuracy. +DEF_TEST(SkPDF_Primitives_Scalar, reporter) { + SkRandom random(0x5EED); + int iterationCount = 512; + while (iterationCount-- > 0) { + union { uint32_t u; float f; }; + u = random.nextU(); + static_assert(sizeof(float) == sizeof(uint32_t), ""); + check_pdf_scalar_serialization(reporter, f); + } + float alwaysCheck[] = { + 0.0f, -0.0f, 1.0f, -1.0f, SK_ScalarPI, 0.1f, FLT_MIN, FLT_MAX, + -FLT_MIN, -FLT_MAX, FLT_MIN / 16.0f, -FLT_MIN / 16.0f, + SK_FloatNaN, SK_FloatInfinity, SK_FloatNegativeInfinity, + -FLT_MIN / 8388608.0 + }; + for (float inputFloat: alwaysCheck) { + check_pdf_scalar_serialization(reporter, inputFloat); + } +} + +// Test SkPDFUtils:: for accuracy. +DEF_TEST(SkPDF_Primitives_Color, reporter) { + char buffer[5]; + for (int i = 0; i < 256; ++i) { + size_t len = SkPDFUtils::ColorToDecimal(i, buffer); + REPORTER_ASSERT(reporter, len == strlen(buffer)); + float f; + REPORTER_ASSERT(reporter, 1 == sscanf(buffer, "%f", &f)); + int roundTrip = (int)(0.5 + f * 255); + REPORTER_ASSERT(reporter, roundTrip == i); + } +} +#endif
diff --git a/src/third_party/skia/tests/PackBitsTest.cpp b/src/third_party/skia/tests/PackBitsTest.cpp index fdbd9bd..ac9a0ae 100644 --- a/src/third_party/skia/tests/PackBitsTest.cpp +++ b/src/third_party/skia/tests/PackBitsTest.cpp
@@ -52,7 +52,7 @@ REPORTER_ASSERT(reporter, match); } - for (size_t size = 1; size <= 512; size += 1) { + for (uint32_t size = 1; size <= 512; size += 1) { for (int n = 100; n; n--) { uint8_t src[600], src2[600]; uint8_t dst[600];
diff --git a/src/third_party/skia/tests/PackedConfigsTextureTest.cpp b/src/third_party/skia/tests/PackedConfigsTextureTest.cpp new file mode 100644 index 0000000..f91fc42 --- /dev/null +++ b/src/third_party/skia/tests/PackedConfigsTextureTest.cpp
@@ -0,0 +1,152 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* + * This is a straightforward test of using packed pixel configs (4444, 565). + * This test will make sure that these RGBA_4444 and RGB_565 are always supported + * as valid texturing configs. + */ + +#include "Test.h" + +#if SK_SUPPORT_GPU +#include "GrContext.h" +#include "GrContextPriv.h" +#include "GrResourceProvider.h" +#include "GrTextureProxy.h" + +static const int DEV_W = 10, DEV_H = 10; +static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H); +static const uint8_t TOL = 0x4; + +static void check_component(skiatest::Reporter* reporter, uint8_t control, uint8_t test) { + uint8_t diff = 0; + if (control >= test) { + diff = control - test; + } else { + diff = test - control; + } + REPORTER_ASSERT(reporter, diff < TOL); +} + +static uint8_t expand_value(uint8_t original, int sigBits) { + SkASSERT(sigBits >= 4); + uint8_t inSigBitShift = 8 - sigBits; + uint8_t duplBitShift = sigBits - inSigBitShift; + return (original << inSigBitShift) + (original >> duplBitShift); +} + +static void check_4444(skiatest::Reporter* reporter, + const SkTDArray<uint16_t>& controlData, + const SkTDArray<uint32_t>& readBuffer) { + for (int j = 0; j < DEV_H; ++j) { + for (int i = 0; i < DEV_W; ++i) { + uint16_t control = controlData[i + j * DEV_H]; + uint32_t test = readBuffer[i + j * DEV_H]; + + // Test alpha component + uint8_t ctrlComp = expand_value(control & 0xF, 4); + uint8_t testComp = GrColorUnpackA(test); + check_component(reporter, ctrlComp, testComp); + + // Test blue component + ctrlComp = expand_value((control >> 4) & 0xF, 4); + testComp = GrColorUnpackB(test); + check_component(reporter, ctrlComp, testComp); + + // Test green component + ctrlComp = expand_value((control >> 8) & 0xF, 4); + testComp = GrColorUnpackG(test); + check_component(reporter, ctrlComp, testComp); + + // Test red component + ctrlComp = expand_value((control >> 12) & 0xF, 4); + testComp = GrColorUnpackR(test); + check_component(reporter, ctrlComp, testComp); + } + } +} + +static void check_565(skiatest::Reporter* reporter, + const SkTDArray<uint16_t>& controlData, + const SkTDArray<GrColor>& readBuffer) { + for (int j = 0; j < DEV_H; ++j) { + for (int i = 0; i < DEV_W; ++i) { + uint16_t control = controlData[i + j * DEV_H]; + GrColor test = readBuffer[i + j * DEV_H]; + // Test blue component (5 bit control) + uint8_t ctrlComp = expand_value(control & 0x1F, 5); + uint8_t testComp = GrColorUnpackB(test); + check_component(reporter, ctrlComp, testComp); + + // Test green component (6 bit control) + ctrlComp = expand_value((control >> 5) & 0x3F, 6); + testComp = GrColorUnpackG(test); + check_component(reporter, ctrlComp, testComp); + + // Test red component (5 bit control) + ctrlComp = expand_value((control >> 11) & 0x1F, 5); + testComp = GrColorUnpackR(test); + check_component(reporter, ctrlComp, testComp); + } + } +} + +static void run_test(skiatest::Reporter* reporter, GrContext* context, + int arraySize, GrPixelConfig config) { + SkTDArray<uint16_t> controlPixelData; + // We will read back into an 8888 buffer since 565/4444 read backs aren't supported + SkTDArray<GrColor> readBuffer; + controlPixelData.setCount(arraySize); + readBuffer.setCount(arraySize); + + for (int i = 0; i < arraySize; i += 2) { + controlPixelData[i] = 0xFF00; + controlPixelData[i + 1] = 0xFA62; + } + + const SkImageInfo dstInfo = SkImageInfo::Make(DEV_W, DEV_H, + kRGBA_8888_SkColorType, kOpaque_SkAlphaType); + + for (auto origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) { + GrSurfaceDesc desc; + desc.fFlags = kNone_GrSurfaceFlags; + desc.fWidth = DEV_W; + desc.fHeight = DEV_H; + desc.fConfig = config; + desc.fOrigin = origin; + + sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), + desc, SkBudgeted::kNo, + controlPixelData.begin(), 0); + SkASSERT(proxy); + + sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext( + std::move(proxy), nullptr); + + SkAssertResult(sContext->readPixels(dstInfo, readBuffer.begin(), 0, 0, 0)); + + if (kRGBA_4444_GrPixelConfig == config) { + check_4444(reporter, controlPixelData, readBuffer); + } else { + SkASSERT(kRGB_565_GrPixelConfig == config); + check_565(reporter, controlPixelData, readBuffer); + } + } +} + +static const int CONTROL_ARRAY_SIZE = DEV_W * DEV_H; + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(RGBA4444TextureTest, reporter, ctxInfo) { + run_test(reporter, ctxInfo.grContext(), CONTROL_ARRAY_SIZE, kRGBA_4444_GrPixelConfig); +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(RGB565TextureTest, reporter, ctxInfo) { + run_test(reporter, ctxInfo.grContext(), CONTROL_ARRAY_SIZE, kRGB_565_GrPixelConfig); +} + +#endif
diff --git a/src/third_party/skia/tests/PaintBreakTextTest.cpp b/src/third_party/skia/tests/PaintBreakTextTest.cpp new file mode 100644 index 0000000..b716c60 --- /dev/null +++ b/src/third_party/skia/tests/PaintBreakTextTest.cpp
@@ -0,0 +1,85 @@ +/* + * Copyright 2011-2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkAutoMalloc.h" +#include "SkPaint.h" +#include "Test.h" + +static void test_monotonic(skiatest::Reporter* reporter, + const SkPaint& paint, + const char* msg) { + const char* text = "sdfkljAKLDFJKEWkldfjlk#$%&sdfs.dsj"; + const size_t length = strlen(text); + const SkScalar width = paint.measureText(text, length); + + SkScalar mm = 0; + size_t nn = 0; + const SkScalar step = SkMaxScalar(width / 10, SK_Scalar1); + for (SkScalar w = 0; w <= width; w += step) { + SkScalar m; + const size_t n = paint.breakText(text, length, w, &m); + + REPORTER_ASSERT_MESSAGE(reporter, n <= length, msg); + REPORTER_ASSERT_MESSAGE(reporter, m <= width, msg); + + if (n == 0) { + REPORTER_ASSERT_MESSAGE(reporter, m == 0, msg); + } else { + // now assert that we're monotonic + if (n == nn) { + REPORTER_ASSERT_MESSAGE(reporter, m == mm, msg); + } else { + REPORTER_ASSERT_MESSAGE(reporter, n > nn, msg); + REPORTER_ASSERT_MESSAGE(reporter, m > mm, msg); + } + } + nn = n; + mm = m; + } +} + +static void test_eq_measure_text(skiatest::Reporter* reporter, + const SkPaint& paint, + const char* msg) { + const char* text = "The ultimate measure of a man is not where he stands in moments of comfort " + "and convenience, but where he stands at times of challenge and controversy."; + const size_t length = strlen(text); + const SkScalar width = paint.measureText(text, length); + + SkScalar mm; + const size_t length2 = paint.breakText(text, length, width, &mm); + REPORTER_ASSERT_MESSAGE(reporter, length2 == length, msg); + REPORTER_ASSERT_MESSAGE(reporter, mm == width, msg); +} + +static void test_long_text(skiatest::Reporter* reporter, + const SkPaint& paint, + const char* msg) { + static const int kSize = 16 * 1024; + SkAutoMalloc block(kSize); + memset(block.get(), 'a', kSize - 1); + char* text = static_cast<char*>(block.get()); + text[kSize - 1] = '\0'; + const SkScalar width = paint.measureText(text, kSize); + + SkScalar mm; + const size_t length = paint.breakText(text, kSize, width, &mm); + REPORTER_ASSERT_MESSAGE(reporter, length == kSize, msg); + REPORTER_ASSERT_MESSAGE(reporter, mm == width, msg); +} + +DEF_TEST(PaintBreakText, reporter) { + SkPaint paint; + test_monotonic(reporter, paint, "default"); + test_eq_measure_text(reporter, paint, "default"); + test_long_text(reporter, paint, "default"); + paint.setTextSize(SkIntToScalar(1 << 17)); + test_monotonic(reporter, paint, "huge text size"); + test_eq_measure_text(reporter, paint, "huge text size"); + paint.setTextSize(0); + test_monotonic(reporter, paint, "zero text size"); +}
diff --git a/src/third_party/skia/tests/PaintImageFilterTest.cpp b/src/third_party/skia/tests/PaintImageFilterTest.cpp new file mode 100644 index 0000000..0365336 --- /dev/null +++ b/src/third_party/skia/tests/PaintImageFilterTest.cpp
@@ -0,0 +1,111 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkPaintImageFilter.h" +#include "SkShader.h" +#include "Test.h" + +static void test_unscaled(skiatest::Reporter* reporter) { + int w = 10, h = 10; + SkRect r = SkRect::MakeWH(SkIntToScalar(w), SkIntToScalar(h)); + + SkBitmap filterResult, paintResult; + + filterResult.allocN32Pixels(w, h); + SkCanvas canvasFilter(filterResult); + canvasFilter.clear(0x00000000); + + paintResult.allocN32Pixels(w, h); + SkCanvas canvasPaint(paintResult); + canvasPaint.clear(0x00000000); + + SkPoint center = SkPoint::Make(SkIntToScalar(5), SkIntToScalar(5)); + SkColor colors[] = {SK_ColorBLUE, SK_ColorRED, SK_ColorGREEN}; + SkScalar pos[] = {0, SK_ScalarHalf, SK_Scalar1}; + SkScalar radius = SkIntToScalar(5); + + SkPaint gradientPaint; + gradientPaint.setShader(SkGradientShader::MakeRadial( + center, radius, colors, pos, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode)); + + // Test using the image filter + { + SkPaint paint; + SkImageFilter::CropRect cr(r); + paint.setImageFilter(SkPaintImageFilter::Make(gradientPaint, &cr)); + canvasFilter.drawRect(r, paint); + } + + // Test using the paint directly + { + canvasPaint.drawRect(r, gradientPaint); + } + + // Assert that both paths yielded the same result + for (int y = 0; y < r.height(); ++y) { + const SkPMColor* filterPtr = filterResult.getAddr32(0, y); + const SkPMColor* paintPtr = paintResult.getAddr32(0, y); + for (int x = 0; x < r.width(); ++x, ++filterPtr, ++paintPtr) { + REPORTER_ASSERT(reporter, *filterPtr == *paintPtr); + } + } +} + +static void test_scaled(skiatest::Reporter* reporter) { + int w = 10, h = 10; + SkRect r = SkRect::MakeWH(SkIntToScalar(w), SkIntToScalar(h)); + + SkBitmap filterResult, paintResult; + + filterResult.allocN32Pixels(w, h); + SkCanvas canvasFilter(filterResult); + canvasFilter.clear(0x00000000); + + paintResult.allocN32Pixels(w, h); + SkCanvas canvasPaint(paintResult); + canvasPaint.clear(0x00000000); + + SkPoint center = SkPoint::Make(SkIntToScalar(5), SkIntToScalar(5)); + SkColor colors[] = {SK_ColorBLUE, SK_ColorRED, SK_ColorGREEN}; + SkScalar pos[] = {0, SK_ScalarHalf, SK_Scalar1}; + SkScalar radius = SkIntToScalar(5); + + SkPaint gradientPaint; + gradientPaint.setShader(SkGradientShader::MakeRadial( + center, radius, colors, pos, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode)); + + // Test using the image filter + { + SkPaint paint; + SkImageFilter::CropRect cr(r); + paint.setImageFilter(SkPaintImageFilter::Make(gradientPaint, &cr)); + canvasFilter.scale(SkIntToScalar(2), SkIntToScalar(2)); + canvasFilter.drawRect(r, paint); + } + + // Test using the paint directly + { + canvasPaint.scale(SkIntToScalar(2), SkIntToScalar(2)); + canvasPaint.drawRect(r, gradientPaint); + } + + // Assert that both paths yielded the same result + for (int y = 0; y < r.height(); ++y) { + const SkPMColor* filterPtr = filterResult.getAddr32(0, y); + const SkPMColor* paintPtr = paintResult.getAddr32(0, y); + for (int x = 0; x < r.width(); ++x, ++filterPtr, ++paintPtr) { + REPORTER_ASSERT(reporter, *filterPtr == *paintPtr); + } + } +} + +DEF_TEST(PaintImageFilter, reporter) { + test_unscaled(reporter); + test_scaled(reporter); +}
diff --git a/src/third_party/skia/tests/PaintTest.cpp b/src/third_party/skia/tests/PaintTest.cpp index 57d0283..7ddcd0d 100644 --- a/src/third_party/skia/tests/PaintTest.cpp +++ b/src/third_party/skia/tests/PaintTest.cpp
@@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include "SkAutoMalloc.h" #include "SkBlurMask.h" #include "SkBlurMaskFilter.h" #include "SkLayerDrawLooper.h" @@ -15,13 +16,12 @@ #include "SkTypeface.h" #include "SkUtils.h" #include "SkWriteBuffer.h" -#include "SkXfermode.h" #include "Test.h" static size_t uni_to_utf8(const SkUnichar src[], void* dst, int count) { char* u8 = (char*)dst; for (int i = 0; i < count; ++i) { - int n = SkUTF8_FromUnichar(src[i], u8); + int n = SkToInt(SkUTF8_FromUnichar(src[i], u8)); u8 += n; } return u8 - (char*)dst; @@ -30,7 +30,7 @@ static size_t uni_to_utf16(const SkUnichar src[], void* dst, int count) { uint16_t* u16 = (uint16_t*)dst; for (int i = 0; i < count; ++i) { - int n = SkUTF16_FromUnichar(src[i], u16); + int n = SkToInt(SkUTF16_FromUnichar(src[i], u16)); u16 += n; } return (char*)u16 - (char*)dst; @@ -80,7 +80,7 @@ SkRandom rand; SkPaint paint; - paint.setTypeface(SkTypeface::RefDefault())->unref(); + paint.setTypeface(SkTypeface::MakeDefault()); SkTypeface* face = paint.getTypeface(); for (int i = 0; i < 1000; ++i) { @@ -116,27 +116,25 @@ } // temparary api for bicubic, just be sure we can set/clear it -DEF_TEST(Paint_filterlevel, reporter) { +DEF_TEST(Paint_filterQuality, reporter) { SkPaint p0, p1; - REPORTER_ASSERT(reporter, - SkPaint::kNone_FilterLevel == p0.getFilterLevel()); + REPORTER_ASSERT(reporter, kNone_SkFilterQuality == p0.getFilterQuality()); - static const SkPaint::FilterLevel gLevels[] = { - SkPaint::kNone_FilterLevel, - SkPaint::kLow_FilterLevel, - SkPaint::kMedium_FilterLevel, - SkPaint::kHigh_FilterLevel + static const SkFilterQuality gQualitys[] = { + kNone_SkFilterQuality, + kLow_SkFilterQuality, + kMedium_SkFilterQuality, + kHigh_SkFilterQuality }; - for (size_t i = 0; i < SK_ARRAY_COUNT(gLevels); ++i) { - p0.setFilterLevel(gLevels[i]); - REPORTER_ASSERT(reporter, gLevels[i] == p0.getFilterLevel()); + for (size_t i = 0; i < SK_ARRAY_COUNT(gQualitys); ++i) { + p0.setFilterQuality(gQualitys[i]); + REPORTER_ASSERT(reporter, gQualitys[i] == p0.getFilterQuality()); p1 = p0; - REPORTER_ASSERT(reporter, gLevels[i] == p1.getFilterLevel()); + REPORTER_ASSERT(reporter, gQualitys[i] == p1.getFilterQuality()); p0.reset(); - REPORTER_ASSERT(reporter, - SkPaint::kNone_FilterLevel == p0.getFilterLevel()); + REPORTER_ASSERT(reporter, kNone_SkFilterQuality == p0.getFilterQuality()); } } @@ -148,51 +146,24 @@ paint.setStrokeWidth(SkIntToScalar(2)); // set a few pointers SkLayerDrawLooper::Builder looperBuilder; - SkLayerDrawLooper* looper = looperBuilder.detachLooper(); - paint.setLooper(looper)->unref(); - SkMaskFilter* mask = SkBlurMaskFilter::Create(kNormal_SkBlurStyle, - SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(1))); - paint.setMaskFilter(mask)->unref(); + paint.setLooper(looperBuilder.detach()); + paint.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, + SkBlurMask::ConvertRadiusToSigma(1))); // copy the paint using the copy constructor and check they are the same SkPaint copiedPaint = paint; REPORTER_ASSERT(reporter, paint == copiedPaint); -#ifdef SK_BUILD_FOR_ANDROID - // the copy constructor should preserve the Generation ID - uint32_t paintGenID = paint.getGenerationID(); - uint32_t copiedPaintGenID = copiedPaint.getGenerationID(); - REPORTER_ASSERT(reporter, paintGenID == copiedPaintGenID); - REPORTER_ASSERT(reporter, paint == copiedPaint); -#endif - // copy the paint using the equal operator and check they are the same copiedPaint = paint; REPORTER_ASSERT(reporter, paint == copiedPaint); -#ifdef SK_BUILD_FOR_ANDROID - // the equals operator should increment the Generation ID - REPORTER_ASSERT(reporter, paint.getGenerationID() == paintGenID); - REPORTER_ASSERT(reporter, copiedPaint.getGenerationID() != copiedPaintGenID); - copiedPaintGenID = copiedPaint.getGenerationID(); // reset to the new value - REPORTER_ASSERT(reporter, paint == copiedPaint); // operator== ignores fGenerationID -#endif - // clean the paint and check they are back to their initial states SkPaint cleanPaint; paint.reset(); copiedPaint.reset(); REPORTER_ASSERT(reporter, cleanPaint == paint); REPORTER_ASSERT(reporter, cleanPaint == copiedPaint); - -#ifdef SK_BUILD_FOR_ANDROID - // the reset function should increment the Generation ID - REPORTER_ASSERT(reporter, paint.getGenerationID() != paintGenID); - REPORTER_ASSERT(reporter, copiedPaint.getGenerationID() != copiedPaintGenID); - // operator== ignores fGenerationID - REPORTER_ASSERT(reporter, cleanPaint == paint); - REPORTER_ASSERT(reporter, cleanPaint == copiedPaint); -#endif } // found and fixed for webkit: mishandling when we hit recursion limit on @@ -221,7 +192,7 @@ SkRect maxR = fillR; SkScalar miter = SkMaxScalar(SK_Scalar1, paint.getStrokeMiter()); SkScalar inset = paint.getStrokeJoin() == SkPaint::kMiter_Join ? - SkScalarMul(paint.getStrokeWidth(), miter) : + paint.getStrokeWidth() * miter : paint.getStrokeWidth(); maxR.inset(-inset, -inset); @@ -230,11 +201,11 @@ } DEF_TEST(Paint_flattening, reporter) { - const SkPaint::FilterLevel levels[] = { - SkPaint::kNone_FilterLevel, - SkPaint::kLow_FilterLevel, - SkPaint::kMedium_FilterLevel, - SkPaint::kHigh_FilterLevel, + const SkFilterQuality levels[] = { + kNone_SkFilterQuality, + kLow_SkFilterQuality, + kMedium_SkFilterQuality, + kHigh_SkFilterQuality, }; const SkPaint::Hinting hinting[] = { SkPaint::kNo_Hinting, @@ -276,7 +247,7 @@ SkPaint paint; paint.setFlags(0x1234); - FOR_SETUP(i, levels, setFilterLevel) + FOR_SETUP(i, levels, setFilterQuality) FOR_SETUP(j, hinting, setHinting) FOR_SETUP(k, align, setTextAlign) FOR_SETUP(l, caps, setStrokeCap) @@ -284,11 +255,12 @@ FOR_SETUP(n, encodings, setTextEncoding) FOR_SETUP(p, styles, setStyle) - SkWriteBuffer writer; + SkBinaryWriteBuffer writer; paint.flatten(writer); - const uint32_t* written = writer.getWriter32()->contiguousArray(); - SkReadBuffer reader(written, writer.bytesWritten()); + SkAutoMalloc buf(writer.bytesWritten()); + writer.writeToMemory(buf.get()); + SkReadBuffer reader(buf.get(), writer.bytesWritten()); SkPaint paint2; paint2.unflatten(reader); @@ -315,29 +287,23 @@ #define ASSERT(expr) REPORTER_ASSERT(r, expr) -DEF_TEST(Paint_FlatteningTraits, r) { +DEF_TEST(Paint_MoreFlattening, r) { SkPaint paint; paint.setColor(0x00AABBCC); paint.setTextScaleX(1.0f); // Default value, ignored. paint.setTextSize(19); - paint.setXfermode(SkXfermode::Create(SkXfermode::kModulate_Mode))->unref(); - paint.setLooper(NULL); // Default value, ignored. + paint.setBlendMode(SkBlendMode::kModulate); + paint.setLooper(nullptr); // Default value, ignored. - SkWriteBuffer writer; - SkPaint::FlatteningTraits::Flatten(writer, paint); + SkBinaryWriteBuffer writer; + paint.flatten(writer); - // BEGIN white box asserts: if the impl changes, these asserts may change - const size_t expectedBytesWritten = sizeof(void*) == 8 ? 32 : 28; - ASSERT(expectedBytesWritten == writer.bytesWritten()); + SkAutoMalloc buf(writer.bytesWritten()); + writer.writeToMemory(buf.get()); + SkReadBuffer reader(buf.get(), writer.bytesWritten()); - const uint32_t* written = writer.getWriter32()->contiguousArray(); - SkASSERT(written != NULL); - ASSERT(*written == ((1<<0) | (1<<1) | (1<<8))); // Dirty bits for our 3. - // END white box asserts - - SkReadBuffer reader(written, writer.bytesWritten()); SkPaint other; - SkPaint::FlatteningTraits::Unflatten(reader, &other); + other.unflatten(reader); ASSERT(reader.offset() == writer.bytesWritten()); // No matter the encoding, these must always hold. @@ -345,10 +311,57 @@ ASSERT(other.getTextScaleX() == paint.getTextScaleX()); ASSERT(other.getTextSize() == paint.getTextSize()); ASSERT(other.getLooper() == paint.getLooper()); + ASSERT(other.getBlendMode() == paint.getBlendMode()); +} - // We have to be a little looser and compare just the modes. Pointers might not be the same. - SkXfermode::Mode otherMode, paintMode; - ASSERT(other.getXfermode()->asMode(&otherMode)); - ASSERT(paint.getXfermode()->asMode(&paintMode)); - ASSERT(otherMode == paintMode); +DEF_TEST(Paint_getHash, r) { + // Try not to inspect the actual hash values in here. + // We might want to change the hash function. + + SkPaint paint; + const uint32_t defaultHash = paint.getHash(); + + // Check that some arbitrary field affects the hash. + paint.setColor(0xFF00FF00); + REPORTER_ASSERT(r, paint.getHash() != defaultHash); + paint.setColor(SK_ColorBLACK); // Reset to default value. + REPORTER_ASSERT(r, paint.getHash() == defaultHash); + + // SkTypeface is the first field we hash, so test it specially. + paint.setTypeface(SkTypeface::MakeDefault()); + REPORTER_ASSERT(r, paint.getHash() != defaultHash); + paint.setTypeface(nullptr); + REPORTER_ASSERT(r, paint.getHash() == defaultHash); + + // This is part of fBitfields, the last field we hash. + paint.setHinting(SkPaint::kSlight_Hinting); + REPORTER_ASSERT(r, paint.getHash() != defaultHash); + paint.setHinting(SkPaint::kNormal_Hinting); + REPORTER_ASSERT(r, paint.getHash() == defaultHash); +} + +#include "SkColorMatrixFilter.h" + +DEF_TEST(Paint_nothingToDraw, r) { + SkPaint paint; + + REPORTER_ASSERT(r, !paint.nothingToDraw()); + paint.setAlpha(0); + REPORTER_ASSERT(r, paint.nothingToDraw()); + + paint.setAlpha(0xFF); + paint.setBlendMode(SkBlendMode::kDst); + REPORTER_ASSERT(r, paint.nothingToDraw()); + + paint.setAlpha(0); + paint.setBlendMode(SkBlendMode::kSrcOver); + + SkColorMatrix cm; + cm.setIdentity(); // does not change alpha + paint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(cm.fMat)); + REPORTER_ASSERT(r, paint.nothingToDraw()); + + cm.postTranslate(0, 0, 0, 1); // wacks alpha + paint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(cm.fMat)); + REPORTER_ASSERT(r, !paint.nothingToDraw()); }
diff --git a/src/third_party/skia/tests/ParametricStageTest.cpp b/src/third_party/skia/tests/ParametricStageTest.cpp new file mode 100644 index 0000000..5b34e16 --- /dev/null +++ b/src/third_party/skia/tests/ParametricStageTest.cpp
@@ -0,0 +1,78 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkColorSpace.h" +#include "SkRasterPipeline.h" +#include "Test.h" + +static void check_error(skiatest::Reporter* r, float limit, SkColorSpaceTransferFn fn) { + float in[256], out[256]; + for (int i = 0; i < 256; i++) { + in [i] = i / 255.0f; + out[i] = 0.0f; // Not likely important. Just being tidy. + } + + const float* ip = in; + float* op = out; + + SkRasterPipeline_<256> p; + p.append(SkRasterPipeline::load_f32, &ip); + p.append(SkRasterPipeline::parametric_r, &fn); + p.append(SkRasterPipeline::parametric_g, &fn); + p.append(SkRasterPipeline::parametric_b, &fn); + p.append(SkRasterPipeline::parametric_a, &fn); + p.append(SkRasterPipeline::store_f32, &op); + + p.run(0,0, 256/4); + + + for (int i = 0; i < 256; i++) { + float want = (in[i] <= fn.fD) ? fn.fC * in[i] + fn.fF + : powf(in[i] * fn.fA + fn.fB, fn.fG) + fn.fE; + float err = fabsf(out[i] - want); + if (err > limit) { + ERRORF(r, "At %d, error was %g (got %g, want %g)", i, err, out[i], want); + } + } +} + +static void check_error(skiatest::Reporter* r, float limit, float gamma) { + SkColorSpaceTransferFn fn = {0,0,0,0,0,0,0}; + fn.fG = gamma; + fn.fA = 1; + check_error(r, limit, fn); +} + +DEF_TEST(Parametric_sRGB, r) { + // Test our good buddy the sRGB transfer function in resplendent 7-parameter glory. + check_error(r, 1/510.0f, { + 2.4f, + 1.0f / 1.055f, + 0.055f / 1.055f, + 1.0f / 12.92f, + 0.04045f, + 0.0f, + 0.0f, + }); +} + +// A nice little spread of simple gammas. +DEF_TEST(Parametric_1dot0, r) { check_error(r, 1/510.0f, 1.0f); } + +DEF_TEST(Parametric_1dot2, r) { check_error(r, 1/510.0f, 1.2f); } +DEF_TEST(Parametric_1dot4, r) { check_error(r, 1/510.0f, 1.4f); } +DEF_TEST(Parametric_1dot8, r) { check_error(r, 1/510.0f, 1.8f); } +DEF_TEST(Parametric_2dot0, r) { check_error(r, 1/510.0f, 2.0f); } +DEF_TEST(Parametric_2dot2, r) { check_error(r, 1/510.0f, 2.2f); } +DEF_TEST(Parametric_2dot4, r) { check_error(r, 1/510.0f, 2.4f); } + +DEF_TEST(Parametric_inv_1dot2, r) { check_error(r, 1/510.0f, 1/1.2f); } +DEF_TEST(Parametric_inv_1dot4, r) { check_error(r, 1/510.0f, 1/1.4f); } +DEF_TEST(Parametric_inv_1dot8, r) { check_error(r, 1/510.0f, 1/1.8f); } +DEF_TEST(Parametric_inv_2dot0, r) { check_error(r, 1/510.0f, 1/2.0f); } +DEF_TEST(Parametric_inv_2dot2, r) { check_error(r, 1/510.0f, 1/2.2f); } +DEF_TEST(Parametric_inv_2dot4, r) { check_error(r, 1/510.0f, 1/2.4f); }
diff --git a/src/third_party/skia/tests/ParsePathTest.cpp b/src/third_party/skia/tests/ParsePathTest.cpp index ff43d14..408f780 100644 --- a/src/third_party/skia/tests/ParsePathTest.cpp +++ b/src/third_party/skia/tests/ParsePathTest.cpp
@@ -31,6 +31,9 @@ const char* fStr; const SkRect fBounds; } gRec[] = { + { "M1,1 l-2.58-2.828-3.82-0.113, 1.9-3.3223-1.08-3.6702, 3.75,0.7744,3.16-2.1551," + "0.42,3.8008,3.02,2.3384-3.48,1.574-1.29,3.601z", + { -5.39999962f, -10.3142f, 5.77000046f, 1.f } }, { "", { 0, 0, 0, 0 } }, { "M0,0L10,10", { 0, 0, SkIntToScalar(10), SkIntToScalar(10) } }, { "M-5.5,-0.5 Q 0 0 6,6.50", @@ -60,3 +63,68 @@ p.addRoundRect(r, 4, 4.5f); test_to_from(reporter, p); } + +DEF_TEST(ParsePath_invalid, r) { + SkPath path; + // This is an invalid SVG string, but the test verifies that we do not + // crash. + bool success = SkParsePath::FromSVGString("M 5", &path); + REPORTER_ASSERT(r, !success); +} + +#include "random_parse_path.h" +#include "SkRandom.h" + +DEF_TEST(ParsePathRandom, r) { + SkRandom rand; + for (int index = 0; index < 1000; ++index) { + SkPath path, path2; + SkString spec; + uint32_t count = rand.nextRangeU(0, 10); + for (uint32_t i = 0; i < count; ++i) { + spec.append(MakeRandomParsePathPiece(&rand)); + } + bool success = SkParsePath::FromSVGString(spec.c_str(), &path); + REPORTER_ASSERT(r, success); + } +} + +DEF_TEST(ParsePathOptionalCommand, r) { + struct { + const char* fStr; + int fVerbs; + int fPoints; + } gTests[] = { + { "", 0, 0 }, + + { "H100 200 ", 3, 3 }, + { "H-100-200", 3, 3 }, + { "H+100+200", 3, 3 }, + { "H.10.20" , 3, 3 }, + { "H-.10-.20", 3, 3 }, + { "H+.10+.20", 3, 3 }, + + { "L100 100 200 200" , 3, 3 }, + { "L-100-100-200-200", 3, 3 }, + { "L+100+100+200+200", 3, 3 }, + { "L.10.10.20.20" , 3, 3 }, + { "L-.10-.10-.20-.20", 3, 3 }, + { "L+.10+.10+.20+.20", 3, 3 }, + + { "C100 100 200 200 300 300 400 400 500 500 600 600" , 3, 7 }, + { "C100-100-200-200-300-300-400-400-500-500-600-600" , 3, 7 }, + { "C100+100+200+200+300+300+400+400+500+500+600+600" , 3, 7 }, + { "C.10.10.20.20.30.30.40.40.50.50.60.60" , 3, 7 }, + { "C-.10-.10-.20-.20-.30-.30-.40-.40-.50-.50-.60-.60", 3, 7 }, + { "C+.10+.10+.20+.20+.30+.30+.40+.40+.50+.50+.60+.60", 3, 7 }, + + { "c-1.49.71-2.12 2.5-1.4 4 .71 1.49 2.5 2.12 4 1.4z", 4, 7 }, + }; + + SkPath path; + for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) { + REPORTER_ASSERT(r, SkParsePath::FromSVGString(gTests[i].fStr, &path)); + REPORTER_ASSERT(r, path.countVerbs() == gTests[i].fVerbs); + REPORTER_ASSERT(r, path.countPoints() == gTests[i].fPoints); + } +}
diff --git a/src/third_party/skia/tests/PathCoverageTest.cpp b/src/third_party/skia/tests/PathCoverageTest.cpp index 0e4a153..2551563 100644 --- a/src/third_party/skia/tests/PathCoverageTest.cpp +++ b/src/third_party/skia/tests/PathCoverageTest.cpp
@@ -5,7 +5,7 @@ * found in the LICENSE file. */ -#include "SkMath.h" +#include "SkMathPriv.h" #include "SkPoint.h" #include "SkScalar.h" #include "Test.h" @@ -60,7 +60,7 @@ if (d < tol) { return 1; } else { - int temp = SkScalarCeilToInt(SkScalarSqrt(SkScalarDiv(d, tol))); + int temp = SkScalarCeilToInt(SkScalarSqrt(d / tol)); uint32_t count = SkMin32(SkNextPow2(temp), MAX_POINTS_PER_CURVE); return count; }
diff --git a/src/third_party/skia/tests/PathMeasureTest.cpp b/src/third_party/skia/tests/PathMeasureTest.cpp index 578f4eb..df66578 100644 --- a/src/third_party/skia/tests/PathMeasureTest.cpp +++ b/src/third_party/skia/tests/PathMeasureTest.cpp
@@ -201,3 +201,19 @@ test_small_segment2(); test_small_segment3(); } + +DEF_TEST(PathMeasureConic, reporter) { + SkPoint stdP, hiP, pts[] = {{0,0}, {100,0}, {100,0}}; + SkPath p; + p.moveTo(0, 0); + p.conicTo(pts[1], pts[2], 1); + SkPathMeasure stdm(p, false); + REPORTER_ASSERT(reporter, stdm.getPosTan(20, &stdP, nullptr)); + p.reset(); + p.moveTo(0, 0); + p.conicTo(pts[1], pts[2], 10); + stdm.setPath(&p, false); + REPORTER_ASSERT(reporter, stdm.getPosTan(20, &hiP, nullptr)); + REPORTER_ASSERT(reporter, 19.5f < stdP.fX && stdP.fX < 20.5f); + REPORTER_ASSERT(reporter, 19.5f < hiP.fX && hiP.fX < 20.5f); +}
diff --git a/src/third_party/skia/tests/PathOpsAngleIdeas.cpp b/src/third_party/skia/tests/PathOpsAngleIdeas.cpp old mode 100755 new mode 100644 index 901cab2..c50260f --- a/src/third_party/skia/tests/PathOpsAngleIdeas.cpp +++ b/src/third_party/skia/tests/PathOpsAngleIdeas.cpp
@@ -6,8 +6,8 @@ */ #include "PathOpsTestCommon.h" #include "SkIntersections.h" +#include "SkOpContour.h" #include "SkOpSegment.h" -#include "SkPathOpsTriangle.h" #include "SkRandom.h" #include "SkTArray.h" #include "SkTSort.h" @@ -18,12 +18,12 @@ class PathOpsAngleTester { public: - static int ConvexHullOverlaps(const SkOpAngle& lh, const SkOpAngle& rh) { - return lh.convexHullOverlaps(rh); + static int ConvexHullOverlaps(SkOpAngle& lh, SkOpAngle& rh) { + return lh.convexHullOverlaps(&rh); } - static int EndsIntersect(const SkOpAngle& lh, const SkOpAngle& rh) { - return lh.endsIntersect(rh); + static int EndsIntersect(SkOpAngle& lh, SkOpAngle& rh) { + return lh.endsIntersect(&rh); } }; @@ -77,7 +77,7 @@ double s = r * SK_ScalarTanPIOver8; double m = r * SK_ScalarRoot2Over2; // construct circle from quads - const SkDQuad circle[8] = {{{{ r, 0}, { r, -s}, { m, -m}}}, + const QuadPts circle[8] = {{{{ r, 0}, { r, -s}, { m, -m}}}, {{{ m, -m}, { s, -r}, { 0, -r}}}, {{{ 0, -r}, {-s, -r}, {-m, -m}}}, {{{-m, -m}, {-r, -s}, {-r, 0}}}, @@ -86,7 +86,9 @@ {{{ 0, r}, { s, r}, { m, m}}}, {{{ m, m}, { r, s}, { r, 0}}}}; for (int octant = 0; octant < 8; ++octant) { - double t = testArc(reporter, quad, circle[octant], octant); + SkDQuad cQuad; + cQuad.debugSet(circle[octant].fPts); + double t = testArc(reporter, quad, cQuad, octant); if (t < 0) { continue; } @@ -332,6 +334,9 @@ rStep /= 2; } while (rStep > FLT_EPSILON); if (bestCCW < 0) { + if (bestR >= maxRadius) { + SkDebugf(""); + } REPORTER_ASSERT(reporter, bestR < maxRadius); return false; } @@ -406,28 +411,28 @@ return ccw == upperRange.ccw; } -class PathOpsSegmentTester { -public: - static void ConstructQuad(SkOpSegment* segment, SkPoint shortQuad[3]) { - segment->debugConstructQuad(shortQuad); - } -}; - -static void makeSegment(const SkDQuad& quad, SkPoint shortQuad[3], SkOpSegment* result) { +static void makeSegment(SkOpContour* contour, const SkDQuad& quad, SkPoint shortQuad[3]) { shortQuad[0] = quad[0].asSkPoint(); shortQuad[1] = quad[1].asSkPoint(); shortQuad[2] = quad[2].asSkPoint(); - PathOpsSegmentTester::ConstructQuad(result, shortQuad); + contour->addQuad(shortQuad); } static void testQuadAngles(skiatest::Reporter* reporter, const SkDQuad& quad1, const SkDQuad& quad2, - int testNo) { + int testNo, SkArenaAlloc* allocator) { SkPoint shortQuads[2][3]; - SkOpSegment seg[2]; - makeSegment(quad1, shortQuads[0], &seg[0]); - makeSegment(quad2, shortQuads[1], &seg[1]); - int realOverlap = PathOpsAngleTester::ConvexHullOverlaps(*seg[0].debugLastAngle(), - *seg[1].debugLastAngle()); + + SkOpContourHead contour; + SkOpGlobalState state(&contour, allocator SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr)); + contour.init(&state, false, false); + makeSegment(&contour, quad1, shortQuads[0]); + makeSegment(&contour, quad1, shortQuads[1]); + SkOpSegment* seg1 = contour.first(); + seg1->debugAddAngle(0, 1); + SkOpSegment* seg2 = seg1->next(); + seg2->debugAddAngle(0, 1); + int realOverlap = PathOpsAngleTester::ConvexHullOverlaps(*seg1->debugLastAngle(), + *seg2->debugLastAngle()); const SkDPoint& origin = quad1[0]; REPORTER_ASSERT(reporter, origin == quad2[0]); double a1s = atan2(origin.fY - quad1[1].fY, quad1[1].fX - origin.fX); @@ -545,25 +550,30 @@ } if (overlap < 0) { SkDEBUGCODE(int realEnds =) - PathOpsAngleTester::EndsIntersect(*seg[0].debugLastAngle(), - *seg[1].debugLastAngle()); + PathOpsAngleTester::EndsIntersect(*seg1->debugLastAngle(), + *seg2->debugLastAngle()); SkASSERT(realEnds == (firstInside ? 1 : 0)); } bruteForce(reporter, quad1, quad2, firstInside); } DEF_TEST(PathOpsAngleOverlapHullsOne, reporter) { + SkSTArenaAlloc<4096> allocator; // gPathOpsAngleIdeasVerbose = true; - const SkDQuad quads[] = { + const QuadPts quads[] = { {{{939.4808349609375, 914.355224609375}, {-357.7921142578125, 590.842529296875}, {736.8936767578125, -350.717529296875}}}, {{{939.4808349609375, 914.355224609375}, {-182.85418701171875, 634.4552001953125}, {-509.62615966796875, 576.1182861328125}}} }; for (int index = 0; index < (int) SK_ARRAY_COUNT(quads); index += 2) { - testQuadAngles(reporter, quads[index], quads[index + 1], 0); + SkDQuad quad0, quad1; + quad0.debugSet(quads[index].fPts); + quad1.debugSet(quads[index + 1].fPts); + testQuadAngles(reporter, quad0, quad1, 0, &allocator); } } DEF_TEST(PathOpsAngleOverlapHulls, reporter) { + SkSTArenaAlloc<4096> allocator; if (!gPathOpsAngleIdeasVerbose) { // takes a while to run -- so exclude it by default return; } @@ -571,23 +581,26 @@ for (int index = 0; index < 100000; ++index) { if (index % 1000 == 999) SkDebugf("."); SkDPoint origin = {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}; - SkDQuad quad1 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, + QuadPts quad1 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}}}; - if (quad1[0] == quad1[2]) { + if (quad1.fPts[0] == quad1.fPts[2]) { continue; } - SkDQuad quad2 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, + QuadPts quad2 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}}}; - if (quad2[0] == quad2[2]) { + if (quad2.fPts[0] == quad2.fPts[2]) { continue; } SkIntersections i; - i.intersect(quad1, quad2); + SkDQuad q1, q2; + q1.debugSet(quad1.fPts); + q2.debugSet(quad2.fPts); + i.intersect(q1, q2); REPORTER_ASSERT(reporter, i.used() >= 1); if (i.used() > 1) { continue; } - testQuadAngles(reporter, quad1, quad2, index); + testQuadAngles(reporter, q1, q2, index, &allocator); } } @@ -601,29 +614,32 @@ SkDEBUGCODE(int smallIndex); for (int index = 0; index < 100000; ++index) { SkDPoint origin = {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}; - SkDQuad quad1 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, + QuadPts quad1 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}}}; - if (quad1[0] == quad1[2]) { + if (quad1.fPts[0] == quad1.fPts[2]) { continue; } - SkDQuad quad2 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, + QuadPts quad2 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}}}; - if (quad2[0] == quad2[2]) { + if (quad2.fPts[0] == quad2.fPts[2]) { continue; } + SkDQuad q1, q2; + q1.debugSet(quad1.fPts); + q2.debugSet(quad2.fPts); SkIntersections i; - i.intersect(quad1, quad2); + i.intersect(q1, q2); REPORTER_ASSERT(reporter, i.used() >= 1); if (i.used() > 1) { continue; } TRange lowerRange, upperRange; - bool result = bruteMinT(reporter, quad1, quad2, &lowerRange, &upperRange); + bool result = bruteMinT(reporter, q1, q2, &lowerRange, &upperRange); REPORTER_ASSERT(reporter, result); double min = SkTMin(upperRange.t1, upperRange.t2); if (smaller > min) { - small[0] = quad1; - small[1] = quad2; + small[0] = q1; + small[1] = q2; SkDEBUGCODE(smallIndex = index); smaller = min; } @@ -635,7 +651,7 @@ DEF_TEST(PathOpsAngleBruteTOne, reporter) { // gPathOpsAngleIdeasVerbose = true; - const SkDQuad quads[] = { + const QuadPts qPts[] = { {{{-770.8492431640625, 948.2369384765625}, {-853.37066650390625, 972.0301513671875}, {-200.62042236328125, -26.7174072265625}}}, {{{-770.8492431640625, 948.2369384765625}, {513.602783203125, 578.8681640625}, {960.641357421875, -813.69757080078125}}}, {{{563.8267822265625, -107.4566650390625}, {-44.67724609375, -136.57452392578125}, {492.3856201171875, -268.79644775390625}}}, @@ -644,6 +660,10 @@ {{{598.857421875, 846.345458984375}, {715.7142333984375, 955.3599853515625}, {-919.9478759765625, 691.611328125}}}, }; TRange lowerRange, upperRange; + SkDQuad quads[SK_ARRAY_COUNT(qPts)]; + for (int index = 0; index < (int) SK_ARRAY_COUNT(qPts); ++index) { + quads[index].debugSet(qPts[index].fPts); + } bruteMinT(reporter, quads[0], quads[1], &lowerRange, &upperRange); bruteMinT(reporter, quads[2], quads[3], &lowerRange, &upperRange); bruteMinT(reporter, quads[4], quads[5], &lowerRange, &upperRange); @@ -664,7 +684,7 @@ the largest length to determine how stable the curve is vis-a-vis the initial tangent. */ -static const SkDQuad extremeTests[][2] = { +static const QuadPts extremeTests[][2] = { { {{{-708.0077926931004,-154.61669472244046}, {-707.9234268635319,-154.30459999551294}, @@ -791,8 +811,11 @@ } double maxR = SK_ScalarMax; for (int index = 0; index < (int) SK_ARRAY_COUNT(extremeTests); ++index) { - const SkDQuad& quad1 = extremeTests[index][0]; - const SkDQuad& quad2 = extremeTests[index][1]; + const QuadPts& qu1 = extremeTests[index][0]; + const QuadPts& qu2 = extremeTests[index][1]; + SkDQuad quad1, quad2; + quad1.debugSet(qu1.fPts); + quad2.debugSet(qu2.fPts); if (gPathOpsAngleIdeasVerbose) { SkDebugf("%s %d\n", __FUNCTION__, index); }
diff --git a/src/third_party/skia/tests/PathOpsAngleTest.cpp b/src/third_party/skia/tests/PathOpsAngleTest.cpp index faf6158..7f4c04a 100644 --- a/src/third_party/skia/tests/PathOpsAngleTest.cpp +++ b/src/third_party/skia/tests/PathOpsAngleTest.cpp
@@ -6,10 +6,9 @@ */ #include "PathOpsTestCommon.h" #include "SkIntersections.h" +#include "SkOpContour.h" #include "SkOpSegment.h" -#include "SkPathOpsTriangle.h" #include "SkRandom.h" -#include "SkTArray.h" #include "SkTSort.h" #include "Test.h" @@ -52,7 +51,7 @@ float p2 = SkDoubleToScalar(line[1].fY * test.fX); int p1Bits = SkFloatAs2sCompliment(p1); int p2Bits = SkFloatAs2sCompliment(p2); - int epsilon = abs(p1Bits - p2Bits); + int epsilon = SkTAbs(p1Bits - p2Bits); if (maxEpsilon < epsilon) { SkDebugf("line={{0, 0}, {%1.7g, %1.7g}} t=%1.7g pt={%1.7g, %1.7g}" " epsilon=%d\n", @@ -82,7 +81,9 @@ SkDPoint qPt2 = line.ptAtT(t3); qPt.fX += qPt2.fY; qPt.fY -= qPt2.fX; - SkDQuad quad = {{line[0], dPt, qPt}}; + QuadPts q = {{line[0], dPt, qPt}}; + SkDQuad quad; + quad.debugSet(q.fPts); // binary search for maximum movement of quad[1] towards test that still has 1 intersection double moveT = 0.5f; double deltaT = moveT / 2; @@ -105,7 +106,7 @@ float p2 = SkDoubleToScalar(line[1].fY * last.fX); int p1Bits = SkFloatAs2sCompliment(p1); int p2Bits = SkFloatAs2sCompliment(p2); - int epsilon = abs(p1Bits - p2Bits); + int epsilon = SkTAbs(p1Bits - p2Bits); if (maxEpsilon < epsilon) { SkDebugf("line={{0, 0}, {%1.7g, %1.7g}} t=%1.7g/%1.7g/%1.7g moveT=%1.7g" " pt={%1.7g, %1.7g} epsilon=%d\n", @@ -191,20 +192,24 @@ class PathOpsAngleTester { public: - static int After(const SkOpAngle& lh, const SkOpAngle& rh) { + static int After(SkOpAngle& lh, SkOpAngle& rh) { return lh.after(&rh); } - static int ConvexHullOverlaps(const SkOpAngle& lh, const SkOpAngle& rh) { - return lh.convexHullOverlaps(rh); + static int AllOnOneSide(SkOpAngle& lh, SkOpAngle& rh) { + return lh.allOnOneSide(&rh); } - static int Orderable(const SkOpAngle& lh, const SkOpAngle& rh) { - return lh.orderable(rh); + static int ConvexHullOverlaps(SkOpAngle& lh, SkOpAngle& rh) { + return lh.convexHullOverlaps(&rh); } - static int EndsIntersect(const SkOpAngle& lh, const SkOpAngle& rh) { - return lh.endsIntersect(rh); + static int Orderable(SkOpAngle& lh, SkOpAngle& rh) { + return lh.orderable(&rh); + } + + static int EndsIntersect(SkOpAngle& lh, SkOpAngle& rh) { + return lh.endsIntersect(&rh); } static void SetNext(SkOpAngle& lh, SkOpAngle& rh) { @@ -214,25 +219,13 @@ class PathOpsSegmentTester { public: - static void ConstructCubic(SkOpSegment* segment, SkPoint shortCubic[4]) { - segment->debugConstructCubic(shortCubic); - } - - static void ConstructLine(SkOpSegment* segment, SkPoint shortLine[2]) { - segment->debugConstructLine(shortLine); - } - - static void ConstructQuad(SkOpSegment* segment, SkPoint shortQuad[3]) { - segment->debugConstructQuad(shortQuad); - } - static void DebugReset(SkOpSegment* segment) { segment->debugReset(); } }; struct CircleData { - const SkDCubic fPts; + const CubicPts fPts; const int fPtCount; SkPoint fShortPts[4]; }; @@ -246,7 +239,10 @@ static const int circleDataSetSize = (int) SK_ARRAY_COUNT(circleDataSet); DEF_TEST(PathOpsAngleCircle, reporter) { - SkOpSegment segment[2]; + SkSTArenaAlloc<4096> allocator; + SkOpContourHead contour; + SkOpGlobalState state(&contour, &allocator SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr)); + contour.init(&state, false, false); for (int index = 0; index < circleDataSetSize; ++index) { CircleData& data = circleDataSet[index]; for (int idx2 = 0; idx2 < data.fPtCount; ++idx2) { @@ -254,21 +250,25 @@ } switch (data.fPtCount) { case 2: - PathOpsSegmentTester::ConstructLine(&segment[index], data.fShortPts); + contour.addLine(data.fShortPts); break; case 3: - PathOpsSegmentTester::ConstructQuad(&segment[index], data.fShortPts); + contour.addQuad(data.fShortPts); break; case 4: - PathOpsSegmentTester::ConstructCubic(&segment[index], data.fShortPts); + contour.addCubic(data.fShortPts); break; } } - PathOpsAngleTester::Orderable(*segment[0].debugLastAngle(), *segment[1].debugLastAngle()); + SkOpSegment* first = contour.first(); + first->debugAddAngle(0, 1); + SkOpSegment* next = first->next(); + next->debugAddAngle(0, 1); + PathOpsAngleTester::Orderable(*first->debugLastAngle(), *next->debugLastAngle()); } struct IntersectData { - const SkDCubic fPts; + const CubicPts fPts; const int fPtCount; double fTStart; double fTEnd; @@ -379,11 +379,39 @@ { {{{5.000,4.000}, {2.000,3.000}}}, 2, 0.5, 0, {} }, // pathops_visualizer.htm:7377 }; // +// from skpi_gino_com_16 +static IntersectData intersectDataSet17[] = { + { /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}} + , 3, 0.74590454, 0.547660352, {} }, + { /*seg=8*/ {{{185, 734}, {252.93103f, 734}, {308, 789.06897f}, {308, 857}}} + , 4, 0.12052623, 0, {} }, + { /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}} + , 3, 0.74590454, 1, {} }, +}; + +static IntersectData intersectDataSet18[] = { + { /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}} + , 3, 0.74590454, 1, {} }, + { /*seg=8*/ {{{185, 734}, {252.93103f, 734}, {308, 789.06897f}, {308, 857}}} + , 4, 0.12052623, 0.217351928, {} }, + { /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}} + , 3, 0.74590454, 0.547660352, {} }, +}; + +static IntersectData intersectDataSet19[] = { + { /*seg=1*/ {{{0, 1}, {3, 5}, {2, 1}, {3, 1}}} + , 4, 0.135148995, 0.134791946, {} }, + { /*seg=3*/ {{{1, 2}, {1, 2.15061641f}, {1, 2.21049166f}, {1.01366711f, 2.21379328f}}} + , 4, 0.956740456, 0.894913214, {} }, + { /*seg=1*/ {{{0, 1}, {3, 5}, {2, 1}, {3, 1}}} + , 4, 0.135148995, 0.551812363, {} }, +}; + #define I(x) intersectDataSet##x static IntersectData* intersectDataSets[] = { I(1), I(2), I(3), I(4), I(5), I(6), I(7), I(8), I(9), I(10), - I(11), I(12), I(13), I(14), I(15), I(16), + I(11), I(12), I(13), I(14), I(15), I(16), I(17), I(18), I(19), }; #undef I @@ -391,56 +419,55 @@ static const int intersectDataSetSizes[] = { I(1), I(2), I(3), I(4), I(5), I(6), I(7), I(8), I(9), I(10), - I(11), I(12), I(13), I(14), I(15), I(16), + I(11), I(12), I(13), I(14), I(15), I(16), I(17), I(18), I(19), }; #undef I static const int intersectDataSetsSize = (int) SK_ARRAY_COUNT(intersectDataSetSizes); +struct FourPoints { + SkPoint pts[4]; +}; + DEF_TEST(PathOpsAngleAfter, reporter) { + SkSTArenaAlloc<4096> allocator; + SkOpContourHead contour; + SkOpGlobalState state(&contour, &allocator SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr)); + contour.init(&state, false, false); for (int index = intersectDataSetsSize - 1; index >= 0; --index) { IntersectData* dataArray = intersectDataSets[index]; const int dataSize = intersectDataSetSizes[index]; - SkOpSegment segment[3]; for (int index2 = 0; index2 < dataSize - 2; ++index2) { - for (int temp = 0; temp < (int) SK_ARRAY_COUNT(segment); ++temp) { - PathOpsSegmentTester::DebugReset(&segment[temp]); - } - for (int index3 = 0; index3 < (int) SK_ARRAY_COUNT(segment); ++index3) { + allocator.reset(); + contour.reset(); + for (int index3 = 0; index3 < 3; ++index3) { IntersectData& data = dataArray[index2 + index3]; - SkPoint temp[4]; + SkPoint* temp = (SkPoint*) allocator.make<FourPoints>(); for (int idx2 = 0; idx2 < data.fPtCount; ++idx2) { temp[idx2] = data.fPts.fPts[idx2].asSkPoint(); } switch (data.fPtCount) { case 2: { - SkDLine seg = SkDLine::SubDivide(temp, data.fTStart, - data.fTStart < data.fTEnd ? 1 : 0); - data.fShortPts[0] = seg[0].asSkPoint(); - data.fShortPts[1] = seg[1].asSkPoint(); - PathOpsSegmentTester::ConstructLine(&segment[index3], data.fShortPts); + contour.addLine(temp); } break; case 3: { - SkDQuad seg = SkDQuad::SubDivide(temp, data.fTStart, data.fTEnd); - data.fShortPts[0] = seg[0].asSkPoint(); - data.fShortPts[1] = seg[1].asSkPoint(); - data.fShortPts[2] = seg[2].asSkPoint(); - PathOpsSegmentTester::ConstructQuad(&segment[index3], data.fShortPts); + contour.addQuad(temp); } break; case 4: { - SkDCubic seg = SkDCubic::SubDivide(temp, data.fTStart, data.fTEnd); - data.fShortPts[0] = seg[0].asSkPoint(); - data.fShortPts[1] = seg[1].asSkPoint(); - data.fShortPts[2] = seg[2].asSkPoint(); - data.fShortPts[3] = seg[3].asSkPoint(); - PathOpsSegmentTester::ConstructCubic(&segment[index3], data.fShortPts); + contour.addCubic(temp); } break; } } - SkOpAngle& angle1 = *const_cast<SkOpAngle*>(segment[0].debugLastAngle()); - SkOpAngle& angle2 = *const_cast<SkOpAngle*>(segment[1].debugLastAngle()); - SkOpAngle& angle3 = *const_cast<SkOpAngle*>(segment[2].debugLastAngle()); + SkOpSegment* seg1 = contour.first(); + seg1->debugAddAngle(dataArray[index2 + 0].fTStart, dataArray[index2 + 0].fTEnd); + SkOpSegment* seg2 = seg1->next(); + seg2->debugAddAngle(dataArray[index2 + 1].fTStart, dataArray[index2 + 1].fTEnd); + SkOpSegment* seg3 = seg2->next(); + seg3->debugAddAngle(dataArray[index2 + 2].fTStart, dataArray[index2 + 2].fTEnd); + SkOpAngle& angle1 = *seg1->debugLastAngle(); + SkOpAngle& angle2 = *seg2->debugLastAngle(); + SkOpAngle& angle3 = *seg3->debugLastAngle(); PathOpsAngleTester::SetNext(angle1, angle3); // These data sets are seeded when the set itself fails, so likely the dataset does not // match the expected result. The tests above return 1 when first added, but @@ -451,35 +478,52 @@ } } -void SkOpSegment::debugConstruct() { - addStartSpan(1); - addEndSpan(1); - debugAddAngle(0, 1); +void SkOpSegment::debugAddAngle(double startT, double endT) { + SkOpPtT* startPtT = startT == 0 ? fHead.ptT() : startT == 1 ? fTail.ptT() + : this->addT(startT); + SkOpPtT* endPtT = endT == 0 ? fHead.ptT() : endT == 1 ? fTail.ptT() + : this->addT(endT); + SkOpAngle* angle = this->globalState()->allocator()->make<SkOpAngle>(); + SkOpSpanBase* startSpan = &fHead; + while (startSpan->ptT() != startPtT) { + startSpan = startSpan->upCast()->next(); + } + SkOpSpanBase* endSpan = &fHead; + while (endSpan->ptT() != endPtT) { + endSpan = endSpan->upCast()->next(); + } + angle->set(startSpan, endSpan); + if (startT < endT) { + startSpan->upCast()->setToAngle(angle); + endSpan->setFromAngle(angle); + } else { + endSpan->upCast()->setToAngle(angle); + startSpan->setFromAngle(angle); + } } -void SkOpSegment::debugAddAngle(int start, int end) { - SkASSERT(start != end); - SkOpAngle& angle = fAngles.push_back(); - angle.set(this, start, end); -} - -void SkOpSegment::debugConstructCubic(SkPoint shortQuad[4]) { - addCubic(shortQuad, false, false); - addT(NULL, shortQuad[0], 0); - addT(NULL, shortQuad[3], 1); - debugConstruct(); -} - -void SkOpSegment::debugConstructLine(SkPoint shortQuad[2]) { - addLine(shortQuad, false, false); - addT(NULL, shortQuad[0], 0); - addT(NULL, shortQuad[1], 1); - debugConstruct(); -} - -void SkOpSegment::debugConstructQuad(SkPoint shortQuad[3]) { - addQuad(shortQuad, false, false); - addT(NULL, shortQuad[0], 0); - addT(NULL, shortQuad[2], 1); - debugConstruct(); +DEF_TEST(PathOpsAngleAllOnOneSide, reporter) { + SkSTArenaAlloc<4096> allocator; + SkOpContourHead contour; + SkOpGlobalState state(&contour, &allocator SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr)); + contour.init(&state, false, false); + SkPoint conicPts[3] = {{494.37100219726562f, 224.66200256347656f}, + {494.37360910682298f, 224.6729026561527f}, + {494.37600708007813f, 224.68400573730469f}}; + SkPoint linePts[2] = {{494.371002f, 224.662003f}, {494.375000f, 224.675995f}}; + for (int i = 10; i >= 0; --i) { + SkPoint modLinePts[2] = { linePts[0], linePts[1] }; + modLinePts[1].fX += i * .1f; + contour.addLine(modLinePts); + contour.addQuad(conicPts); + // contour.addConic(conicPts, 0.999935746f, &allocator); + SkOpSegment* first = contour.first(); + first->debugAddAngle(0, 1); + SkOpSegment* next = first->next(); + next->debugAddAngle(0, 1); + /* int result = */ + PathOpsAngleTester::AllOnOneSide(*first->debugLastAngle(), *next->debugLastAngle()); + // SkDebugf("i=%d result=%d\n", i , result); + // SkDebugf(""); + } }
diff --git a/src/third_party/skia/tests/PathOpsBattles.cpp b/src/third_party/skia/tests/PathOpsBattles.cpp index 15fffd5..32ba96c 100644 --- a/src/third_party/skia/tests/PathOpsBattles.cpp +++ b/src/third_party/skia/tests/PathOpsBattles.cpp
@@ -36,7 +36,7 @@ path2.cubicTo(-15.5552f, 63.4296f, 12.6591f, 64.0704f, 33.9313f, 49.484f); path2.lineTo(46.9383f, 68.4529f); path2.close(); - testPathOp(reporter, path1, path2, kUnion_PathOp, filename); + testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename); } static void issue414409b(skiatest::Reporter* reporter, const char* filename) { @@ -59,8 +59,7 @@ path2.cubicTo(SkBits2Float(0x42383446), SkBits2Float(0x421ac98f), SkBits2Float(0x4242b98a), SkBits2Float(0x420d5308), SkBits2Float(0x424bbb17), SkBits2Float(0x41fdb8ee)); path2.lineTo(SkBits2Float(0x428ce9ef), SkBits2Float(0x422f7dc6)); path2.close(); -// SkOpSegment.cpp:3488: failed assertion "other->fTs[min].fWindSum == oppWinding" - testPathOp(reporter, path1, path2, kUnion_PathOp, filename); + testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename); } static void issue414409c(skiatest::Reporter* reporter, const char* filename) { @@ -85,7 +84,7 @@ path2.lineTo(SkBits2Float(0x3eccef1a), SkBits2Float(0xc2a5ff81)); path2.close(); -testPathOp(reporter, path1, path2, kUnion_PathOp, filename); +testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename); } // fails to draw correctly @@ -401,7 +400,7 @@ SkPath path2(path); testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); } -// ../../third_party/tcmalloc/chromium/src/free_list.h:118] Memory corruption detected. +// ../../third_party/tcmalloc/chromium/src/free_list.h:118] Memory corruption detected. static void battleOp13(skiatest::Reporter* reporter, const char* filename) { SkPath path; @@ -1273,9 +1272,6 @@ // op end success 1 static void battleOp46(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; - } SkPath path; path.setFillType((SkPath::FillType) 1); path.moveTo(SkBits2Float(0x3697ff52), SkBits2Float(0xc26fffff)); @@ -1490,9 +1486,6 @@ // op end success 1 static void battleOp54(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; - } SkPath path; path.setFillType((SkPath::FillType) 1); path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000)); @@ -1632,9 +1625,6 @@ // op end success 1 static void battleOp59(skiatest::Reporter* reporter, const char* filename) { // hung - if (!FLAGS_runFail) { - return; - } SkPath path; path.setFillType((SkPath::FillType) 0); path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000)); @@ -1736,9 +1726,6 @@ // op end success 1 static void battleOp63(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; - } SkPath path; path.setFillType((SkPath::FillType) 1); path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000)); @@ -2314,9 +2301,6 @@ // op end success 1 static void battleOp85(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; - } SkPath path; path.setFillType((SkPath::FillType) 1); path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000)); @@ -3973,9 +3957,6 @@ // op end success 1 static void battleOp148(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; - } SkPath path; path.setFillType((SkPath::FillType) 0); path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000)); @@ -4084,9 +4065,6 @@ // op end success 1 static void battleOp152(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; // draws wrong - } SkPath path; path.setFillType((SkPath::FillType) 0); path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000)); @@ -4229,9 +4207,6 @@ // op end success 1 static void battleOp157(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; // draws wrong - } SkPath path; path.setFillType((SkPath::FillType) 1); path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000)); @@ -4408,9 +4383,6 @@ // op end success 1 static void battleOp163(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; // draws wrong - } SkPath path; path.setFillType((SkPath::FillType) 1); path.moveTo(SkBits2Float(0x3697ff52), SkBits2Float(0xc2700000)); @@ -4951,9 +4923,6 @@ // op end success 1 static void battleOp181(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; // draws wrong - } SkPath path; path.setFillType((SkPath::FillType) 1); path.moveTo(SkBits2Float(0xb7060057), SkBits2Float(0xc2700000)); @@ -5014,9 +4983,6 @@ // op end success 1 static void battleOp183(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; // draws wrong - } SkPath path; path.setFillType((SkPath::FillType) 1); path.moveTo(SkBits2Float(0x36d3ff52), SkBits2Float(0xc2700000)); @@ -5376,9 +5342,6 @@ // op end success 1 static void battleOp195(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; // draws wrong - } SkPath path; path.setFillType((SkPath::FillType) 0); path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000)); @@ -5451,9 +5414,6 @@ // op end success 1 static void battleOp198(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; // draws wrong - } SkPath path; path.setFillType((SkPath::FillType) 1); path.moveTo(SkBits2Float(0x369bbf59), SkBits2Float(0xc2700000)); @@ -6308,9 +6268,6 @@ // op end success 1 static void battleOp230(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; // draws wrong - } SkPath path; path.setFillType((SkPath::FillType) 1); path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000)); @@ -6505,9 +6462,6 @@ // op end success 1 static void battleOp237(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; // draws wrong - } SkPath path; path.setFillType((SkPath::FillType) 0); path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000)); @@ -7066,9 +7020,6 @@ // op end success 1 static void battleOp256(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; // draws wrong - } SkPath path; path.setFillType((SkPath::FillType) 1); path.moveTo(SkBits2Float(0xb69400ae), SkBits2Float(0xc2700000)); @@ -7465,9 +7416,6 @@ // op end success 1 static void battleOp269(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; // draws wrong - } SkPath path; path.setFillType((SkPath::FillType) 0); path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000)); @@ -7585,9 +7533,6 @@ // op end success 1 static void battleOp273(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; // draws wrong - } SkPath path; path.setFillType((SkPath::FillType) 0); path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000)); @@ -7891,9 +7836,6 @@ // op end success 1 static void battleOp283(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; // draws wrong - } SkPath path; path.setFillType((SkPath::FillType) 0); path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000)); @@ -10739,7 +10681,7 @@ testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); } -static void (*firstTest)(skiatest::Reporter* , const char* filename) = battleOp6001; +static void (*firstTest)(skiatest::Reporter* , const char* filename) = battleOp183; static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0; static struct TestDesc tests[] = { @@ -10792,8 +10734,6 @@ TEST(battleOp43), TEST(battleOp44), TEST(battleOp45), - TEST(battleOp46), // draws wrong : dropped an outer cubic incorrectly - // if assembly rewrite was done, the error would be hidden TEST(battleOp47), TEST(battleOp48), TEST(battleOp49), @@ -10802,17 +10742,15 @@ TEST(battleOp51), TEST(battleOp52), TEST(battleOp53), - TEST(battleOp54), // draws wrong TEST(battleOp55), TEST(battleOp56), TEST(battleOp57), TEST(battleOp58), - TEST(battleOp59), // draws wrong + TEST(battleOp59), TEST(battleOp60), TEST(battleOp61), TEST(battleOp62), - TEST(battleOp63), // draws wrong TEST(battleOp64), TEST(battleOp65), TEST(battleOp66), @@ -10836,7 +10774,7 @@ TEST(battleOp82), TEST(battleOp83), TEST(battleOp84), - TEST(battleOp85), // draws wrong + TEST(battleOp85), TEST(battleOp86), TEST(battleOp87), TEST(battleOp88), @@ -10905,24 +10843,20 @@ TEST(battleOp145), TEST(battleOp146), TEST(battleOp147), - TEST(battleOp148), // draws wrong TEST(battleOp149), TEST(battleOp150), TEST(battleOp151), - TEST(battleOp152), TEST(battleOp153), TEST(battleOp154), TEST(battleOp155), TEST(battleOp156), - TEST(battleOp157), TEST(battleOp158), TEST(battleOp159), TEST(battleOp160), TEST(battleOp161), TEST(battleOp162), - TEST(battleOp163), TEST(battleOp164), TEST(battleOp165), TEST(battleOp166), @@ -10942,9 +10876,7 @@ TEST(battleOp179), TEST(battleOp180), - TEST(battleOp181), TEST(battleOp182), - TEST(battleOp183), TEST(battleOp184), TEST(battleOp185), TEST(battleOp186), @@ -10957,10 +10889,8 @@ TEST(battleOp192), TEST(battleOp193), TEST(battleOp194), - TEST(battleOp195), TEST(battleOp196), TEST(battleOp197), - TEST(battleOp198), TEST(battleOp199), TEST(battleOp200), @@ -10995,7 +10925,6 @@ TEST(battleOp227), TEST(battleOp228), TEST(battleOp229), - TEST(battleOp230), TEST(battleOp231), TEST(battleOp232), @@ -11024,7 +10953,6 @@ TEST(battleOp253), TEST(battleOp254), TEST(battleOp255), - TEST(battleOp256), TEST(battleOp257), TEST(battleOp258), TEST(battleOp259), @@ -11038,12 +10966,10 @@ TEST(battleOp266), TEST(battleOp267), TEST(battleOp268), - TEST(battleOp269), TEST(battleOp270), TEST(battleOp271), TEST(battleOp272), - TEST(battleOp273), TEST(battleOp274), TEST(battleOp275), TEST(battleOp276), @@ -11054,7 +10980,6 @@ TEST(battleOp281), TEST(battleOp282), - TEST(battleOp283), TEST(battleOp284), TEST(battleOp285), TEST(battleOp286), @@ -11168,6 +11093,25 @@ TEST(issue414409c), TEST(issue414409b), TEST(issue414409), + + // these draw wrong + TEST(battleOp46), // dropped an outer cubic incorrectly + // if assembly rewrite was done, the error would be hidden + TEST(battleOp54), + TEST(battleOp63), + TEST(battleOp152), + TEST(battleOp157), + TEST(battleOp163), + TEST(battleOp181), + TEST(battleOp183), + TEST(battleOp195), + TEST(battleOp198), + TEST(battleOp230), + TEST(battleOp256), + TEST(battleOp269), + TEST(battleOp273), + TEST(battleOp148), + TEST(battleOp283), }; @@ -11179,5 +11123,5 @@ #if DEBUG_SHOW_TEST_NAME strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH); #endif - RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse); + RunTestSet(reporter, tests, testCount, firstTest, nullptr, stopTest, runReverse); }
diff --git a/src/third_party/skia/tests/PathOpsBoundsTest.cpp b/src/third_party/skia/tests/PathOpsBoundsTest.cpp index 8683051..573a0ce 100644 --- a/src/third_party/skia/tests/PathOpsBoundsTest.cpp +++ b/src/third_party/skia/tests/PathOpsBoundsTest.cpp
@@ -6,6 +6,7 @@ */ #include "PathOpsTestCommon.h" #include "SkPathOpsBounds.h" +#include "SkPathOpsCurve.h" #include "Test.h" static const SkRect sectTests[][2] = { @@ -28,24 +29,6 @@ static const size_t noSectTestsCount = SK_ARRAY_COUNT(noSectTests); -static const SkRect reallyEmpty[] = { - {0, 0, 0, 0}, - {1, 1, 1, 0}, - {1, 1, 0, 1}, - {1, 1, 0, 0}, - {1, 2, 3, SK_ScalarNaN}, -}; - -static const size_t emptyTestsCount = SK_ARRAY_COUNT(reallyEmpty); - -static const SkRect notReallyEmpty[] = { - {0, 0, 1, 0}, - {0, 0, 0, 1}, - {0, 0, 1, 1}, -}; - -static const size_t notEmptyTestsCount = SK_ARRAY_COUNT(notReallyEmpty); - DEF_TEST(PathOpsBounds, reporter) { for (size_t index = 0; index < sectTestsCount; ++index) { const SkPathOpsBounds& bounds1 = static_cast<const SkPathOpsBounds&>(sectTests[index][0]); @@ -74,37 +57,18 @@ ordinal.set(1, 2, 3, 4); bounds.add(ordinal); REPORTER_ASSERT(reporter, bounds == expected); - SkPoint topLeft = {0, 0}; - bounds.setPointBounds(topLeft); - SkPoint botRight = {3, 4}; + bounds.setEmpty(); + SkDPoint botRight = {3, 4}; bounds.add(botRight); REPORTER_ASSERT(reporter, bounds == expected); - for (size_t index = 0; index < emptyTestsCount; ++index) { - const SkPathOpsBounds& bounds = static_cast<const SkPathOpsBounds&>(reallyEmpty[index]); - // SkASSERT(ValidBounds(bounds)); // don't check because test may contain nan - bool empty = bounds.isReallyEmpty(); - REPORTER_ASSERT(reporter, empty); - } - for (size_t index = 0; index < notEmptyTestsCount; ++index) { - const SkPathOpsBounds& bounds = static_cast<const SkPathOpsBounds&>(notReallyEmpty[index]); - SkASSERT(ValidBounds(bounds)); - bool empty = bounds.isReallyEmpty(); - REPORTER_ASSERT(reporter, !empty); - } const SkPoint curvePts[] = {{0, 0}, {1, 2}, {3, 4}, {5, 6}}; - bounds.setLineBounds(curvePts); - expected.set(0, 0, 1, 2); - REPORTER_ASSERT(reporter, bounds == expected); - (bounds.*SetCurveBounds[1])(curvePts); - REPORTER_ASSERT(reporter, bounds == expected); - bounds.setQuadBounds(curvePts); + SkDCurve curve; + curve.fQuad.set(curvePts); + curve.setQuadBounds(curvePts, 1, 0, 1, &bounds); expected.set(0, 0, 3, 4); REPORTER_ASSERT(reporter, bounds == expected); - (bounds.*SetCurveBounds[2])(curvePts); - REPORTER_ASSERT(reporter, bounds == expected); - bounds.setCubicBounds(curvePts); + curve.fCubic.set(curvePts); + curve.setCubicBounds(curvePts, 1, 0, 1, &bounds); expected.set(0, 0, 5, 6); REPORTER_ASSERT(reporter, bounds == expected); - (bounds.*SetCurveBounds[3])(curvePts); - REPORTER_ASSERT(reporter, bounds == expected); }
diff --git a/src/third_party/skia/tests/PathOpsBuildUseTest.cpp b/src/third_party/skia/tests/PathOpsBuildUseTest.cpp new file mode 100644 index 0000000..67e4252 --- /dev/null +++ b/src/third_party/skia/tests/PathOpsBuildUseTest.cpp
@@ -0,0 +1,2435 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "PathOpsExtendedTest.h" +#include "PathOpsTestCommon.h" + +#define TEST(name) { name, #name } + +static void build1_1(skiatest::Reporter* reporter, const char* filename) { + SkOpBuilder builder; + SkPath path; + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x436ae68e), SkBits2Float(0x43adff26)); // 234.901f, 347.993f +path.quadTo(SkBits2Float(0x436ae68e), SkBits2Float(0x43b32ca2), SkBits2Float(0x4363940a), SkBits2Float(0x43b6d5e4)); // 234.901f, 358.349f, 227.578f, 365.671f +path.quadTo(SkBits2Float(0x435c4186), SkBits2Float(0x43ba7f26), SkBits2Float(0x4351e68e), SkBits2Float(0x43ba7f26)); // 220.256f, 372.993f, 209.901f, 372.993f +path.quadTo(SkBits2Float(0x43478b96), SkBits2Float(0x43ba7f26), SkBits2Float(0x43403912), SkBits2Float(0x43b6d5e4)); // 199.545f, 372.993f, 192.223f, 365.671f +path.quadTo(SkBits2Float(0x4338e68e), SkBits2Float(0x43b32ca2), SkBits2Float(0x4338e68e), SkBits2Float(0x43adff26)); // 184.901f, 358.349f, 184.901f, 347.993f +path.quadTo(SkBits2Float(0x4338e68e), SkBits2Float(0x43a8d1aa), SkBits2Float(0x43403912), SkBits2Float(0x43a52868)); // 184.901f, 337.638f, 192.223f, 330.316f +path.quadTo(SkBits2Float(0x43478b96), SkBits2Float(0x43a17f26), SkBits2Float(0x4351e68e), SkBits2Float(0x43a17f26)); // 199.545f, 322.993f, 209.901f, 322.993f +path.quadTo(SkBits2Float(0x435c4186), SkBits2Float(0x43a17f26), SkBits2Float(0x4363940a), SkBits2Float(0x43a52868)); // 220.256f, 322.993f, 227.578f, 330.316f +path.quadTo(SkBits2Float(0x436ae68e), SkBits2Float(0x43a8d1aa), SkBits2Float(0x436ae68e), SkBits2Float(0x43adff26)); // 234.901f, 337.638f, 234.901f, 347.993f +path.close(); + SkPath path0(path); + builder.add(path0, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43ad0aca), SkBits2Float(0x432a0e2c)); // 346.084f, 170.055f +path.quadTo(SkBits2Float(0x43ad0aca), SkBits2Float(0x43346924), SkBits2Float(0x43a96188), SkBits2Float(0x433bbba8)); // 346.084f, 180.411f, 338.762f, 187.733f +path.quadTo(SkBits2Float(0x43a5b846), SkBits2Float(0x43430e2c), SkBits2Float(0x43a08aca), SkBits2Float(0x43430e2c)); // 331.44f, 195.055f, 321.084f, 195.055f +path.quadTo(SkBits2Float(0x439b5d4e), SkBits2Float(0x43430e2c), SkBits2Float(0x4397b40c), SkBits2Float(0x433bbba8)); // 310.729f, 195.055f, 303.407f, 187.733f +path.quadTo(SkBits2Float(0x43940aca), SkBits2Float(0x43346924), SkBits2Float(0x43940aca), SkBits2Float(0x432a0e2c)); // 296.084f, 180.411f, 296.084f, 170.055f +path.quadTo(SkBits2Float(0x43940aca), SkBits2Float(0x431fb334), SkBits2Float(0x4397b40c), SkBits2Float(0x431860b0)); // 296.084f, 159.7f, 303.407f, 152.378f +path.quadTo(SkBits2Float(0x439b5d4e), SkBits2Float(0x43110e2c), SkBits2Float(0x43a08aca), SkBits2Float(0x43110e2c)); // 310.729f, 145.055f, 321.084f, 145.055f +path.quadTo(SkBits2Float(0x43a5b846), SkBits2Float(0x43110e2c), SkBits2Float(0x43a96188), SkBits2Float(0x431860b0)); // 331.44f, 145.055f, 338.762f, 152.378f +path.quadTo(SkBits2Float(0x43ad0aca), SkBits2Float(0x431fb334), SkBits2Float(0x43ad0aca), SkBits2Float(0x432a0e2c)); // 346.084f, 159.7f, 346.084f, 170.055f +path.close(); + SkPath path1(path); + builder.add(path1, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x431f14f9), SkBits2Float(0x433943fd)); // 159.082f, 185.266f +path.quadTo(SkBits2Float(0x431f14f9), SkBits2Float(0x43439ef4), SkBits2Float(0x4317c275), SkBits2Float(0x434af179)); // 159.082f, 195.621f, 151.76f, 202.943f +path.quadTo(SkBits2Float(0x43106ff0), SkBits2Float(0x435243fd), SkBits2Float(0x430614f9), SkBits2Float(0x435243fd)); // 144.437f, 210.266f, 134.082f, 210.266f +path.quadTo(SkBits2Float(0x42f77403), SkBits2Float(0x435243fd), SkBits2Float(0x42e8cefa), SkBits2Float(0x434af179)); // 123.727f, 210.266f, 116.404f, 202.943f +path.quadTo(SkBits2Float(0x42da29f2), SkBits2Float(0x43439ef4), SkBits2Float(0x42da29f2), SkBits2Float(0x433943fd)); // 109.082f, 195.621f, 109.082f, 185.266f +path.quadTo(SkBits2Float(0x42da29f2), SkBits2Float(0x432ee906), SkBits2Float(0x42e8cefa), SkBits2Float(0x43279681)); // 109.082f, 174.91f, 116.404f, 167.588f +path.quadTo(SkBits2Float(0x42f77403), SkBits2Float(0x432043fd), SkBits2Float(0x430614f9), SkBits2Float(0x432043fd)); // 123.727f, 160.266f, 134.082f, 160.266f +path.quadTo(SkBits2Float(0x43106ff0), SkBits2Float(0x432043fd), SkBits2Float(0x4317c275), SkBits2Float(0x43279681)); // 144.437f, 160.266f, 151.76f, 167.588f +path.quadTo(SkBits2Float(0x431f14f9), SkBits2Float(0x432ee906), SkBits2Float(0x431f14f9), SkBits2Float(0x433943fd)); // 159.082f, 174.91f, 159.082f, 185.266f +path.close(); + SkPath path2(path); + builder.add(path2, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x433ad67a), SkBits2Float(0x43abd585)); // 186.838f, 343.668f +path.quadTo(SkBits2Float(0x433ad67a), SkBits2Float(0x43b10301), SkBits2Float(0x433383f6), SkBits2Float(0x43b4ac43)); // 186.838f, 354.023f, 179.515f, 361.346f +path.quadTo(SkBits2Float(0x432c3172), SkBits2Float(0x43b85585), SkBits2Float(0x4321d67a), SkBits2Float(0x43b85585)); // 172.193f, 368.668f, 161.838f, 368.668f +path.quadTo(SkBits2Float(0x43177b82), SkBits2Float(0x43b85585), SkBits2Float(0x431028fe), SkBits2Float(0x43b4ac43)); // 151.482f, 368.668f, 144.16f, 361.346f +path.quadTo(SkBits2Float(0x4308d67a), SkBits2Float(0x43b10301), SkBits2Float(0x4308d67a), SkBits2Float(0x43abd585)); // 136.838f, 354.023f, 136.838f, 343.668f +path.quadTo(SkBits2Float(0x4308d67a), SkBits2Float(0x43a6a809), SkBits2Float(0x431028fe), SkBits2Float(0x43a2fec7)); // 136.838f, 333.313f, 144.16f, 325.99f +path.quadTo(SkBits2Float(0x43177b82), SkBits2Float(0x439f5585), SkBits2Float(0x4321d67a), SkBits2Float(0x439f5585)); // 151.482f, 318.668f, 161.838f, 318.668f +path.quadTo(SkBits2Float(0x432c3172), SkBits2Float(0x439f5585), SkBits2Float(0x433383f6), SkBits2Float(0x43a2fec7)); // 172.193f, 318.668f, 179.515f, 325.99f +path.quadTo(SkBits2Float(0x433ad67a), SkBits2Float(0x43a6a809), SkBits2Float(0x433ad67a), SkBits2Float(0x43abd585)); // 186.838f, 333.313f, 186.838f, 343.668f +path.close(); + SkPath path3(path); + builder.add(path3, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43bff91b), SkBits2Float(0x43973a57)); // 383.946f, 302.456f +path.quadTo(SkBits2Float(0x43bff91b), SkBits2Float(0x439c67d3), SkBits2Float(0x43bc4fd9), SkBits2Float(0x43a01115)); // 383.946f, 312.811f, 376.624f, 320.133f +path.quadTo(SkBits2Float(0x43b8a697), SkBits2Float(0x43a3ba57), SkBits2Float(0x43b3791b), SkBits2Float(0x43a3ba57)); // 369.301f, 327.456f, 358.946f, 327.456f +path.quadTo(SkBits2Float(0x43ae4b9f), SkBits2Float(0x43a3ba57), SkBits2Float(0x43aaa25d), SkBits2Float(0x43a01115)); // 348.591f, 327.456f, 341.268f, 320.133f +path.quadTo(SkBits2Float(0x43a6f91b), SkBits2Float(0x439c67d3), SkBits2Float(0x43a6f91b), SkBits2Float(0x43973a57)); // 333.946f, 312.811f, 333.946f, 302.456f +path.quadTo(SkBits2Float(0x43a6f91b), SkBits2Float(0x43920cdb), SkBits2Float(0x43aaa25d), SkBits2Float(0x438e6399)); // 333.946f, 292.1f, 341.268f, 284.778f +path.quadTo(SkBits2Float(0x43ae4b9f), SkBits2Float(0x438aba57), SkBits2Float(0x43b3791b), SkBits2Float(0x438aba57)); // 348.591f, 277.456f, 358.946f, 277.456f +path.quadTo(SkBits2Float(0x43b8a697), SkBits2Float(0x438aba57), SkBits2Float(0x43bc4fd9), SkBits2Float(0x438e6399)); // 369.301f, 277.456f, 376.624f, 284.778f +path.quadTo(SkBits2Float(0x43bff91b), SkBits2Float(0x43920cdb), SkBits2Float(0x43bff91b), SkBits2Float(0x43973a57)); // 383.946f, 292.1f, 383.946f, 302.456f +path.close(); + SkPath path4(path); + builder.add(path4, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43374c2c), SkBits2Float(0x437e8a30)); // 183.298f, 254.54f +path.quadTo(SkBits2Float(0x43374c2c), SkBits2Float(0x43847294), SkBits2Float(0x432ff9a8), SkBits2Float(0x43881bd6)); // 183.298f, 264.895f, 175.975f, 272.217f +path.quadTo(SkBits2Float(0x4328a724), SkBits2Float(0x438bc518), SkBits2Float(0x431e4c2c), SkBits2Float(0x438bc518)); // 168.653f, 279.54f, 158.298f, 279.54f +path.quadTo(SkBits2Float(0x4313f134), SkBits2Float(0x438bc518), SkBits2Float(0x430c9eb0), SkBits2Float(0x43881bd6)); // 147.942f, 279.54f, 140.62f, 272.217f +path.quadTo(SkBits2Float(0x43054c2c), SkBits2Float(0x43847294), SkBits2Float(0x43054c2c), SkBits2Float(0x437e8a30)); // 133.298f, 264.895f, 133.298f, 254.54f +path.quadTo(SkBits2Float(0x43054c2c), SkBits2Float(0x43742f38), SkBits2Float(0x430c9eb0), SkBits2Float(0x436cdcb4)); // 133.298f, 244.184f, 140.62f, 236.862f +path.quadTo(SkBits2Float(0x4313f134), SkBits2Float(0x43658a30), SkBits2Float(0x431e4c2c), SkBits2Float(0x43658a30)); // 147.942f, 229.54f, 158.298f, 229.54f +path.quadTo(SkBits2Float(0x4328a724), SkBits2Float(0x43658a30), SkBits2Float(0x432ff9a8), SkBits2Float(0x436cdcb4)); // 168.653f, 229.54f, 175.975f, 236.862f +path.quadTo(SkBits2Float(0x43374c2c), SkBits2Float(0x43742f38), SkBits2Float(0x43374c2c), SkBits2Float(0x437e8a30)); // 183.298f, 244.184f, 183.298f, 254.54f +path.close(); + SkPath path5(path); + builder.add(path5, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x430e01e8), SkBits2Float(0x435c8671)); // 142.007f, 220.525f +path.quadTo(SkBits2Float(0x430e01e8), SkBits2Float(0x4366e168), SkBits2Float(0x4306af64), SkBits2Float(0x436e33ed)); // 142.007f, 230.88f, 134.685f, 238.203f +path.quadTo(SkBits2Float(0x42feb9bf), SkBits2Float(0x43758671), SkBits2Float(0x42ea03d0), SkBits2Float(0x43758671)); // 127.363f, 245.525f, 117.007f, 245.525f +path.quadTo(SkBits2Float(0x42d54de1), SkBits2Float(0x43758671), SkBits2Float(0x42c6a8d8), SkBits2Float(0x436e33ed)); // 106.652f, 245.525f, 99.3298f, 238.203f +path.quadTo(SkBits2Float(0x42b803d0), SkBits2Float(0x4366e168), SkBits2Float(0x42b803d0), SkBits2Float(0x435c8671)); // 92.0074f, 230.88f, 92.0074f, 220.525f +path.quadTo(SkBits2Float(0x42b803d0), SkBits2Float(0x43522b7a), SkBits2Float(0x42c6a8d8), SkBits2Float(0x434ad8f5)); // 92.0074f, 210.17f, 99.3298f, 202.847f +path.quadTo(SkBits2Float(0x42d54de1), SkBits2Float(0x43438671), SkBits2Float(0x42ea03d0), SkBits2Float(0x43438671)); // 106.652f, 195.525f, 117.007f, 195.525f +path.quadTo(SkBits2Float(0x42feb9bf), SkBits2Float(0x43438671), SkBits2Float(0x4306af64), SkBits2Float(0x434ad8f5)); // 127.363f, 195.525f, 134.685f, 202.847f +path.quadTo(SkBits2Float(0x430e01e8), SkBits2Float(0x43522b7a), SkBits2Float(0x430e01e8), SkBits2Float(0x435c8671)); // 142.007f, 210.17f, 142.007f, 220.525f +path.close(); + SkPath path6(path); + builder.add(path6, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x438b7062), SkBits2Float(0x42d54bf2)); // 278.878f, 106.648f +path.quadTo(SkBits2Float(0x438b7062), SkBits2Float(0x42ea01e1), SkBits2Float(0x4387c720), SkBits2Float(0x42f8a6ea)); // 278.878f, 117.004f, 271.556f, 124.326f +path.quadTo(SkBits2Float(0x43841dde), SkBits2Float(0x4303a5f9), SkBits2Float(0x437de0c4), SkBits2Float(0x4303a5f9)); // 264.233f, 131.648f, 253.878f, 131.648f +path.quadTo(SkBits2Float(0x437385cc), SkBits2Float(0x4303a5f9), SkBits2Float(0x436c3348), SkBits2Float(0x42f8a6ea)); // 243.523f, 131.648f, 236.2f, 124.326f +path.quadTo(SkBits2Float(0x4364e0c3), SkBits2Float(0x42ea01e1), SkBits2Float(0x4364e0c3), SkBits2Float(0x42d54bf2)); // 228.878f, 117.004f, 228.878f, 106.648f +path.quadTo(SkBits2Float(0x4364e0c3), SkBits2Float(0x42c09603), SkBits2Float(0x436c3348), SkBits2Float(0x42b1f0fa)); // 228.878f, 96.293f, 236.2f, 88.9707f +path.quadTo(SkBits2Float(0x437385cc), SkBits2Float(0x42a34bf2), SkBits2Float(0x437de0c4), SkBits2Float(0x42a34bf2)); // 243.523f, 81.6483f, 253.878f, 81.6483f +path.quadTo(SkBits2Float(0x43841dde), SkBits2Float(0x42a34bf2), SkBits2Float(0x4387c720), SkBits2Float(0x42b1f0fa)); // 264.233f, 81.6483f, 271.556f, 88.9707f +path.quadTo(SkBits2Float(0x438b7062), SkBits2Float(0x42c09603), SkBits2Float(0x438b7062), SkBits2Float(0x42d54bf2)); // 278.878f, 96.293f, 278.878f, 106.648f +path.close(); + SkPath path7(path); + builder.add(path7, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43de3ff6), SkBits2Float(0x43963745)); // 444.5f, 300.432f +path.quadTo(SkBits2Float(0x43de3ff6), SkBits2Float(0x439b64c1), SkBits2Float(0x43da96b4), SkBits2Float(0x439f0e03)); // 444.5f, 310.787f, 437.177f, 318.109f +path.quadTo(SkBits2Float(0x43d6ed72), SkBits2Float(0x43a2b745), SkBits2Float(0x43d1bff6), SkBits2Float(0x43a2b745)); // 429.855f, 325.432f, 419.5f, 325.432f +path.quadTo(SkBits2Float(0x43cc927a), SkBits2Float(0x43a2b745), SkBits2Float(0x43c8e938), SkBits2Float(0x439f0e03)); // 409.144f, 325.432f, 401.822f, 318.109f +path.quadTo(SkBits2Float(0x43c53ff6), SkBits2Float(0x439b64c1), SkBits2Float(0x43c53ff6), SkBits2Float(0x43963745)); // 394.5f, 310.787f, 394.5f, 300.432f +path.quadTo(SkBits2Float(0x43c53ff6), SkBits2Float(0x439109c9), SkBits2Float(0x43c8e938), SkBits2Float(0x438d6087)); // 394.5f, 290.076f, 401.822f, 282.754f +path.quadTo(SkBits2Float(0x43cc927a), SkBits2Float(0x4389b745), SkBits2Float(0x43d1bff6), SkBits2Float(0x4389b745)); // 409.144f, 275.432f, 419.5f, 275.432f +path.quadTo(SkBits2Float(0x43d6ed72), SkBits2Float(0x4389b745), SkBits2Float(0x43da96b4), SkBits2Float(0x438d6087)); // 429.855f, 275.432f, 437.177f, 282.754f +path.quadTo(SkBits2Float(0x43de3ff6), SkBits2Float(0x439109c9), SkBits2Float(0x43de3ff6), SkBits2Float(0x43963745)); // 444.5f, 290.076f, 444.5f, 300.432f +path.close(); + SkPath path8(path); + builder.add(path8, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43aae79c), SkBits2Float(0x438d0cbc)); // 341.809f, 282.099f +path.quadTo(SkBits2Float(0x43aae79c), SkBits2Float(0x43923a38), SkBits2Float(0x43a73e5a), SkBits2Float(0x4395e37a)); // 341.809f, 292.455f, 334.487f, 299.777f +path.quadTo(SkBits2Float(0x43a39518), SkBits2Float(0x43998cbc), SkBits2Float(0x439e679c), SkBits2Float(0x43998cbc)); // 327.165f, 307.099f, 316.809f, 307.099f +path.quadTo(SkBits2Float(0x43993a20), SkBits2Float(0x43998cbc), SkBits2Float(0x439590de), SkBits2Float(0x4395e37a)); // 306.454f, 307.099f, 299.132f, 299.777f +path.quadTo(SkBits2Float(0x4391e79c), SkBits2Float(0x43923a38), SkBits2Float(0x4391e79c), SkBits2Float(0x438d0cbc)); // 291.809f, 292.455f, 291.809f, 282.099f +path.quadTo(SkBits2Float(0x4391e79c), SkBits2Float(0x4387df40), SkBits2Float(0x439590de), SkBits2Float(0x438435fe)); // 291.809f, 271.744f, 299.132f, 264.422f +path.quadTo(SkBits2Float(0x43993a20), SkBits2Float(0x43808cbc), SkBits2Float(0x439e679c), SkBits2Float(0x43808cbc)); // 306.454f, 257.099f, 316.809f, 257.099f +path.quadTo(SkBits2Float(0x43a39518), SkBits2Float(0x43808cbc), SkBits2Float(0x43a73e5a), SkBits2Float(0x438435fe)); // 327.165f, 257.099f, 334.487f, 264.422f +path.quadTo(SkBits2Float(0x43aae79c), SkBits2Float(0x4387df40), SkBits2Float(0x43aae79c), SkBits2Float(0x438d0cbc)); // 341.809f, 271.744f, 341.809f, 282.099f +path.close(); + SkPath path9(path); + builder.add(path9, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4354ce7d), SkBits2Float(0x43842ec9)); // 212.807f, 264.366f +path.quadTo(SkBits2Float(0x4354ce7d), SkBits2Float(0x43895c45), SkBits2Float(0x434d7bf9), SkBits2Float(0x438d0587)); // 212.807f, 274.721f, 205.484f, 282.043f +path.quadTo(SkBits2Float(0x43462974), SkBits2Float(0x4390aec9), SkBits2Float(0x433bce7d), SkBits2Float(0x4390aec9)); // 198.162f, 289.366f, 187.807f, 289.366f +path.quadTo(SkBits2Float(0x43317386), SkBits2Float(0x4390aec9), SkBits2Float(0x432a2101), SkBits2Float(0x438d0587)); // 177.451f, 289.366f, 170.129f, 282.043f +path.quadTo(SkBits2Float(0x4322ce7d), SkBits2Float(0x43895c45), SkBits2Float(0x4322ce7d), SkBits2Float(0x43842ec9)); // 162.807f, 274.721f, 162.807f, 264.366f +path.quadTo(SkBits2Float(0x4322ce7d), SkBits2Float(0x437e029a), SkBits2Float(0x432a2101), SkBits2Float(0x4376b016)); // 162.807f, 254.01f, 170.129f, 246.688f +path.quadTo(SkBits2Float(0x43317386), SkBits2Float(0x436f5d92), SkBits2Float(0x433bce7d), SkBits2Float(0x436f5d92)); // 177.451f, 239.366f, 187.807f, 239.366f +path.quadTo(SkBits2Float(0x43462974), SkBits2Float(0x436f5d92), SkBits2Float(0x434d7bf9), SkBits2Float(0x4376b016)); // 198.162f, 239.366f, 205.484f, 246.688f +path.quadTo(SkBits2Float(0x4354ce7d), SkBits2Float(0x437e029a), SkBits2Float(0x4354ce7d), SkBits2Float(0x43842ec9)); // 212.807f, 254.01f, 212.807f, 264.366f +path.close(); + SkPath path10(path); + builder.add(path10, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43a8c299), SkBits2Float(0x432fce08)); // 337.52f, 175.805f +path.quadTo(SkBits2Float(0x43a8c299), SkBits2Float(0x433a2900), SkBits2Float(0x43a51957), SkBits2Float(0x43417b84)); // 337.52f, 186.16f, 330.198f, 193.482f +path.quadTo(SkBits2Float(0x43a17015), SkBits2Float(0x4348ce08), SkBits2Float(0x439c4299), SkBits2Float(0x4348ce08)); // 322.876f, 200.805f, 312.52f, 200.805f +path.quadTo(SkBits2Float(0x4397151d), SkBits2Float(0x4348ce08), SkBits2Float(0x43936bdb), SkBits2Float(0x43417b84)); // 302.165f, 200.805f, 294.843f, 193.482f +path.quadTo(SkBits2Float(0x438fc299), SkBits2Float(0x433a2900), SkBits2Float(0x438fc299), SkBits2Float(0x432fce08)); // 287.52f, 186.16f, 287.52f, 175.805f +path.quadTo(SkBits2Float(0x438fc299), SkBits2Float(0x43257310), SkBits2Float(0x43936bdb), SkBits2Float(0x431e208c)); // 287.52f, 165.449f, 294.843f, 158.127f +path.quadTo(SkBits2Float(0x4397151d), SkBits2Float(0x4316ce08), SkBits2Float(0x439c4299), SkBits2Float(0x4316ce08)); // 302.165f, 150.805f, 312.52f, 150.805f +path.quadTo(SkBits2Float(0x43a17015), SkBits2Float(0x4316ce08), SkBits2Float(0x43a51957), SkBits2Float(0x431e208c)); // 322.876f, 150.805f, 330.198f, 158.127f +path.quadTo(SkBits2Float(0x43a8c299), SkBits2Float(0x43257310), SkBits2Float(0x43a8c299), SkBits2Float(0x432fce08)); // 337.52f, 165.449f, 337.52f, 175.805f +path.close(); + SkPath path11(path); + builder.add(path11, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43d7486e), SkBits2Float(0x430ebc47)); // 430.566f, 142.735f +path.quadTo(SkBits2Float(0x43d7486e), SkBits2Float(0x4319173e), SkBits2Float(0x43d39f2c), SkBits2Float(0x432069c3)); // 430.566f, 153.091f, 423.244f, 160.413f +path.quadTo(SkBits2Float(0x43cff5ea), SkBits2Float(0x4327bc47), SkBits2Float(0x43cac86e), SkBits2Float(0x4327bc47)); // 415.921f, 167.735f, 405.566f, 167.735f +path.quadTo(SkBits2Float(0x43c59af2), SkBits2Float(0x4327bc47), SkBits2Float(0x43c1f1b0), SkBits2Float(0x432069c3)); // 395.211f, 167.735f, 387.888f, 160.413f +path.quadTo(SkBits2Float(0x43be486e), SkBits2Float(0x4319173e), SkBits2Float(0x43be486e), SkBits2Float(0x430ebc47)); // 380.566f, 153.091f, 380.566f, 142.735f +path.quadTo(SkBits2Float(0x43be486e), SkBits2Float(0x43046150), SkBits2Float(0x43c1f1b0), SkBits2Float(0x42fa1d96)); // 380.566f, 132.38f, 387.888f, 125.058f +path.quadTo(SkBits2Float(0x43c59af2), SkBits2Float(0x42eb788e), SkBits2Float(0x43cac86e), SkBits2Float(0x42eb788e)); // 395.211f, 117.735f, 405.566f, 117.735f +path.quadTo(SkBits2Float(0x43cff5ea), SkBits2Float(0x42eb788e), SkBits2Float(0x43d39f2c), SkBits2Float(0x42fa1d96)); // 415.921f, 117.735f, 423.244f, 125.058f +path.quadTo(SkBits2Float(0x43d7486e), SkBits2Float(0x43046150), SkBits2Float(0x43d7486e), SkBits2Float(0x430ebc47)); // 430.566f, 132.38f, 430.566f, 142.735f +path.close(); + SkPath path12(path); + builder.add(path12, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43484ac4), SkBits2Float(0x43421f09)); // 200.292f, 194.121f +path.quadTo(SkBits2Float(0x43484ac4), SkBits2Float(0x434c7a00), SkBits2Float(0x4340f840), SkBits2Float(0x4353cc85)); // 200.292f, 204.477f, 192.97f, 211.799f +path.quadTo(SkBits2Float(0x4339a5bc), SkBits2Float(0x435b1f09), SkBits2Float(0x432f4ac4), SkBits2Float(0x435b1f09)); // 185.647f, 219.121f, 175.292f, 219.121f +path.quadTo(SkBits2Float(0x4324efcc), SkBits2Float(0x435b1f09), SkBits2Float(0x431d9d48), SkBits2Float(0x4353cc85)); // 164.937f, 219.121f, 157.614f, 211.799f +path.quadTo(SkBits2Float(0x43164ac4), SkBits2Float(0x434c7a00), SkBits2Float(0x43164ac4), SkBits2Float(0x43421f09)); // 150.292f, 204.477f, 150.292f, 194.121f +path.quadTo(SkBits2Float(0x43164ac4), SkBits2Float(0x4337c412), SkBits2Float(0x431d9d48), SkBits2Float(0x4330718d)); // 150.292f, 183.766f, 157.614f, 176.444f +path.quadTo(SkBits2Float(0x4324efcc), SkBits2Float(0x43291f09), SkBits2Float(0x432f4ac4), SkBits2Float(0x43291f09)); // 164.937f, 169.121f, 175.292f, 169.121f +path.quadTo(SkBits2Float(0x4339a5bc), SkBits2Float(0x43291f09), SkBits2Float(0x4340f840), SkBits2Float(0x4330718d)); // 185.647f, 169.121f, 192.97f, 176.444f +path.quadTo(SkBits2Float(0x43484ac4), SkBits2Float(0x4337c412), SkBits2Float(0x43484ac4), SkBits2Float(0x43421f09)); // 200.292f, 183.766f, 200.292f, 194.121f +path.close(); + SkPath path13(path); + builder.add(path13, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4328883e), SkBits2Float(0x42fb0be0)); // 168.532f, 125.523f +path.quadTo(SkBits2Float(0x4328883e), SkBits2Float(0x4307e0e7), SkBits2Float(0x432135ba), SkBits2Float(0x430f336c)); // 168.532f, 135.879f, 161.21f, 143.201f +path.quadTo(SkBits2Float(0x4319e336), SkBits2Float(0x431685f0), SkBits2Float(0x430f883e), SkBits2Float(0x431685f0)); // 153.888f, 150.523f, 143.532f, 150.523f +path.quadTo(SkBits2Float(0x43052d46), SkBits2Float(0x431685f0), SkBits2Float(0x42fbb584), SkBits2Float(0x430f336c)); // 133.177f, 150.523f, 125.855f, 143.201f +path.quadTo(SkBits2Float(0x42ed107c), SkBits2Float(0x4307e0e7), SkBits2Float(0x42ed107c), SkBits2Float(0x42fb0be0)); // 118.532f, 135.879f, 118.532f, 125.523f +path.quadTo(SkBits2Float(0x42ed107c), SkBits2Float(0x42e655f1), SkBits2Float(0x42fbb584), SkBits2Float(0x42d7b0e9)); // 118.532f, 115.168f, 125.855f, 107.846f +path.quadTo(SkBits2Float(0x43052d46), SkBits2Float(0x42c90be1), SkBits2Float(0x430f883e), SkBits2Float(0x42c90be1)); // 133.177f, 100.523f, 143.532f, 100.523f +path.quadTo(SkBits2Float(0x4319e336), SkBits2Float(0x42c90be1), SkBits2Float(0x432135ba), SkBits2Float(0x42d7b0e9)); // 153.888f, 100.523f, 161.21f, 107.846f +path.quadTo(SkBits2Float(0x4328883e), SkBits2Float(0x42e655f1), SkBits2Float(0x4328883e), SkBits2Float(0x42fb0be0)); // 168.532f, 115.168f, 168.532f, 125.523f +path.close(); + SkPath path14(path); + builder.add(path14, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43b2bff8), SkBits2Float(0x439bb140)); // 357.5f, 311.385f +path.quadTo(SkBits2Float(0x43b2bff8), SkBits2Float(0x43a0debc), SkBits2Float(0x43af16b6), SkBits2Float(0x43a487fe)); // 357.5f, 321.74f, 350.177f, 329.062f +path.quadTo(SkBits2Float(0x43ab6d74), SkBits2Float(0x43a83140), SkBits2Float(0x43a63ff8), SkBits2Float(0x43a83140)); // 342.855f, 336.385f, 332.5f, 336.385f +path.quadTo(SkBits2Float(0x43a1127c), SkBits2Float(0x43a83140), SkBits2Float(0x439d693a), SkBits2Float(0x43a487fe)); // 322.144f, 336.385f, 314.822f, 329.062f +path.quadTo(SkBits2Float(0x4399bff8), SkBits2Float(0x43a0debc), SkBits2Float(0x4399bff8), SkBits2Float(0x439bb140)); // 307.5f, 321.74f, 307.5f, 311.385f +path.quadTo(SkBits2Float(0x4399bff8), SkBits2Float(0x439683c4), SkBits2Float(0x439d693a), SkBits2Float(0x4392da82)); // 307.5f, 301.029f, 314.822f, 293.707f +path.quadTo(SkBits2Float(0x43a1127c), SkBits2Float(0x438f3140), SkBits2Float(0x43a63ff8), SkBits2Float(0x438f3140)); // 322.144f, 286.385f, 332.5f, 286.385f +path.quadTo(SkBits2Float(0x43ab6d74), SkBits2Float(0x438f3140), SkBits2Float(0x43af16b6), SkBits2Float(0x4392da82)); // 342.855f, 286.385f, 350.177f, 293.707f +path.quadTo(SkBits2Float(0x43b2bff8), SkBits2Float(0x439683c4), SkBits2Float(0x43b2bff8), SkBits2Float(0x439bb140)); // 357.5f, 301.029f, 357.5f, 311.385f +path.close(); + SkPath path15(path); + builder.add(path15, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x435ae426), SkBits2Float(0x4341f066)); // 218.891f, 193.939f +path.quadTo(SkBits2Float(0x435ae426), SkBits2Float(0x434c4b5e), SkBits2Float(0x435391a2), SkBits2Float(0x43539de2)); // 218.891f, 204.294f, 211.569f, 211.617f +path.quadTo(SkBits2Float(0x434c3f1e), SkBits2Float(0x435af066), SkBits2Float(0x4341e426), SkBits2Float(0x435af066)); // 204.247f, 218.939f, 193.891f, 218.939f +path.quadTo(SkBits2Float(0x4337892e), SkBits2Float(0x435af066), SkBits2Float(0x433036aa), SkBits2Float(0x43539de2)); // 183.536f, 218.939f, 176.214f, 211.617f +path.quadTo(SkBits2Float(0x4328e426), SkBits2Float(0x434c4b5e), SkBits2Float(0x4328e426), SkBits2Float(0x4341f066)); // 168.891f, 204.294f, 168.891f, 193.939f +path.quadTo(SkBits2Float(0x4328e426), SkBits2Float(0x4337956e), SkBits2Float(0x433036aa), SkBits2Float(0x433042ea)); // 168.891f, 183.584f, 176.214f, 176.261f +path.quadTo(SkBits2Float(0x4337892e), SkBits2Float(0x4328f066), SkBits2Float(0x4341e426), SkBits2Float(0x4328f066)); // 183.536f, 168.939f, 193.891f, 168.939f +path.quadTo(SkBits2Float(0x434c3f1e), SkBits2Float(0x4328f066), SkBits2Float(0x435391a2), SkBits2Float(0x433042ea)); // 204.247f, 168.939f, 211.569f, 176.261f +path.quadTo(SkBits2Float(0x435ae426), SkBits2Float(0x4337956e), SkBits2Float(0x435ae426), SkBits2Float(0x4341f066)); // 218.891f, 183.584f, 218.891f, 193.939f +path.close(); + SkPath path16(path); + builder.add(path16, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x439817ba), SkBits2Float(0x42e83ba4)); // 304.185f, 116.116f +path.quadTo(SkBits2Float(0x439817ba), SkBits2Float(0x42fcf193), SkBits2Float(0x43946e78), SkBits2Float(0x4305cb4e)); // 304.185f, 126.472f, 296.863f, 133.794f +path.quadTo(SkBits2Float(0x4390c536), SkBits2Float(0x430d1dd2), SkBits2Float(0x438b97ba), SkBits2Float(0x430d1dd2)); // 289.541f, 141.116f, 279.185f, 141.116f +path.quadTo(SkBits2Float(0x43866a3e), SkBits2Float(0x430d1dd2), SkBits2Float(0x4382c0fc), SkBits2Float(0x4305cb4e)); // 268.83f, 141.116f, 261.508f, 133.794f +path.quadTo(SkBits2Float(0x437e2f74), SkBits2Float(0x42fcf193), SkBits2Float(0x437e2f74), SkBits2Float(0x42e83ba4)); // 254.185f, 126.472f, 254.185f, 116.116f +path.quadTo(SkBits2Float(0x437e2f74), SkBits2Float(0x42d385b5), SkBits2Float(0x4382c0fc), SkBits2Float(0x42c4e0ac)); // 254.185f, 105.761f, 261.508f, 98.4388f +path.quadTo(SkBits2Float(0x43866a3e), SkBits2Float(0x42b63ba4), SkBits2Float(0x438b97ba), SkBits2Float(0x42b63ba4)); // 268.83f, 91.1165f, 279.185f, 91.1165f +path.quadTo(SkBits2Float(0x4390c536), SkBits2Float(0x42b63ba4), SkBits2Float(0x43946e78), SkBits2Float(0x42c4e0ac)); // 289.541f, 91.1165f, 296.863f, 98.4388f +path.quadTo(SkBits2Float(0x439817ba), SkBits2Float(0x42d385b5), SkBits2Float(0x439817ba), SkBits2Float(0x42e83ba4)); // 304.185f, 105.761f, 304.185f, 116.116f +path.close(); + SkPath path17(path); + builder.add(path17, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4350558c), SkBits2Float(0x4382efb0)); // 208.334f, 261.873f +path.quadTo(SkBits2Float(0x4350558c), SkBits2Float(0x43881d2c), SkBits2Float(0x43490308), SkBits2Float(0x438bc66e)); // 208.334f, 272.228f, 201.012f, 279.55f +path.quadTo(SkBits2Float(0x4341b084), SkBits2Float(0x438f6fb0), SkBits2Float(0x4337558c), SkBits2Float(0x438f6fb0)); // 193.69f, 286.873f, 183.334f, 286.873f +path.quadTo(SkBits2Float(0x432cfa94), SkBits2Float(0x438f6fb0), SkBits2Float(0x4325a810), SkBits2Float(0x438bc66e)); // 172.979f, 286.873f, 165.656f, 279.55f +path.quadTo(SkBits2Float(0x431e558c), SkBits2Float(0x43881d2c), SkBits2Float(0x431e558c), SkBits2Float(0x4382efb0)); // 158.334f, 272.228f, 158.334f, 261.873f +path.quadTo(SkBits2Float(0x431e558c), SkBits2Float(0x437b8468), SkBits2Float(0x4325a810), SkBits2Float(0x437431e4)); // 158.334f, 251.517f, 165.656f, 244.195f +path.quadTo(SkBits2Float(0x432cfa94), SkBits2Float(0x436cdf60), SkBits2Float(0x4337558c), SkBits2Float(0x436cdf60)); // 172.979f, 236.873f, 183.334f, 236.873f +path.quadTo(SkBits2Float(0x4341b084), SkBits2Float(0x436cdf60), SkBits2Float(0x43490308), SkBits2Float(0x437431e4)); // 193.69f, 236.873f, 201.012f, 244.195f +path.quadTo(SkBits2Float(0x4350558c), SkBits2Float(0x437b8468), SkBits2Float(0x4350558c), SkBits2Float(0x4382efb0)); // 208.334f, 251.517f, 208.334f, 261.873f +path.close(); + SkPath path18(path); + builder.add(path18, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42a8ec1a), SkBits2Float(0x43a51083)); // 84.4611f, 330.129f +path.quadTo(SkBits2Float(0x42a8ec1a), SkBits2Float(0x43aa3dff), SkBits2Float(0x429a4711), SkBits2Float(0x43ade741)); // 84.4611f, 340.484f, 77.1388f, 347.807f +path.quadTo(SkBits2Float(0x428ba209), SkBits2Float(0x43b19083), SkBits2Float(0x426dd834), SkBits2Float(0x43b19083)); // 69.8165f, 355.129f, 59.4611f, 355.129f +path.quadTo(SkBits2Float(0x42446c56), SkBits2Float(0x43b19083), SkBits2Float(0x42272246), SkBits2Float(0x43ade741)); // 49.1058f, 355.129f, 41.7835f, 347.807f +path.quadTo(SkBits2Float(0x4209d835), SkBits2Float(0x43aa3dff), SkBits2Float(0x4209d835), SkBits2Float(0x43a51083)); // 34.4611f, 340.484f, 34.4611f, 330.129f +path.quadTo(SkBits2Float(0x4209d835), SkBits2Float(0x439fe307), SkBits2Float(0x42272246), SkBits2Float(0x439c39c5)); // 34.4611f, 319.774f, 41.7835f, 312.451f +path.quadTo(SkBits2Float(0x42446c56), SkBits2Float(0x43989083), SkBits2Float(0x426dd834), SkBits2Float(0x43989083)); // 49.1058f, 305.129f, 59.4611f, 305.129f +path.quadTo(SkBits2Float(0x428ba209), SkBits2Float(0x43989083), SkBits2Float(0x429a4711), SkBits2Float(0x439c39c5)); // 69.8165f, 305.129f, 77.1388f, 312.451f +path.quadTo(SkBits2Float(0x42a8ec1a), SkBits2Float(0x439fe307), SkBits2Float(0x42a8ec1a), SkBits2Float(0x43a51083)); // 84.4611f, 319.774f, 84.4611f, 330.129f +path.close(); + SkPath path19(path); + builder.add(path19, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43aca5fa), SkBits2Float(0x438f0f1d)); // 345.297f, 286.118f +path.quadTo(SkBits2Float(0x43aca5fa), SkBits2Float(0x43943c99), SkBits2Float(0x43a8fcb8), SkBits2Float(0x4397e5db)); // 345.297f, 296.473f, 337.974f, 303.796f +path.quadTo(SkBits2Float(0x43a55376), SkBits2Float(0x439b8f1d), SkBits2Float(0x43a025fa), SkBits2Float(0x439b8f1d)); // 330.652f, 311.118f, 320.297f, 311.118f +path.quadTo(SkBits2Float(0x439af87e), SkBits2Float(0x439b8f1d), SkBits2Float(0x43974f3c), SkBits2Float(0x4397e5db)); // 309.941f, 311.118f, 302.619f, 303.796f +path.quadTo(SkBits2Float(0x4393a5fa), SkBits2Float(0x43943c99), SkBits2Float(0x4393a5fa), SkBits2Float(0x438f0f1d)); // 295.297f, 296.473f, 295.297f, 286.118f +path.quadTo(SkBits2Float(0x4393a5fa), SkBits2Float(0x4389e1a1), SkBits2Float(0x43974f3c), SkBits2Float(0x4386385f)); // 295.297f, 275.763f, 302.619f, 268.44f +path.quadTo(SkBits2Float(0x439af87e), SkBits2Float(0x43828f1d), SkBits2Float(0x43a025fa), SkBits2Float(0x43828f1d)); // 309.941f, 261.118f, 320.297f, 261.118f +path.quadTo(SkBits2Float(0x43a55376), SkBits2Float(0x43828f1d), SkBits2Float(0x43a8fcb8), SkBits2Float(0x4386385f)); // 330.652f, 261.118f, 337.974f, 268.44f +path.quadTo(SkBits2Float(0x43aca5fa), SkBits2Float(0x4389e1a1), SkBits2Float(0x43aca5fa), SkBits2Float(0x438f0f1d)); // 345.297f, 275.763f, 345.297f, 286.118f +path.close(); + SkPath path20(path); + builder.add(path20, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43846cfc), SkBits2Float(0x431b61f0)); // 264.851f, 155.383f +path.quadTo(SkBits2Float(0x43846cfc), SkBits2Float(0x4325bce8), SkBits2Float(0x4380c3ba), SkBits2Float(0x432d0f6c)); // 264.851f, 165.738f, 257.529f, 173.06f +path.quadTo(SkBits2Float(0x437a34f0), SkBits2Float(0x433461f0), SkBits2Float(0x436fd9f8), SkBits2Float(0x433461f0)); // 250.207f, 180.383f, 239.851f, 180.383f +path.quadTo(SkBits2Float(0x43657f00), SkBits2Float(0x433461f0), SkBits2Float(0x435e2c7c), SkBits2Float(0x432d0f6c)); // 229.496f, 180.383f, 222.174f, 173.06f +path.quadTo(SkBits2Float(0x4356d9f7), SkBits2Float(0x4325bce8), SkBits2Float(0x4356d9f7), SkBits2Float(0x431b61f0)); // 214.851f, 165.738f, 214.851f, 155.383f +path.quadTo(SkBits2Float(0x4356d9f7), SkBits2Float(0x431106f8), SkBits2Float(0x435e2c7c), SkBits2Float(0x4309b474)); // 214.851f, 145.027f, 222.174f, 137.705f +path.quadTo(SkBits2Float(0x43657f00), SkBits2Float(0x430261f0), SkBits2Float(0x436fd9f8), SkBits2Float(0x430261f0)); // 229.496f, 130.383f, 239.851f, 130.383f +path.quadTo(SkBits2Float(0x437a34f0), SkBits2Float(0x430261f0), SkBits2Float(0x4380c3ba), SkBits2Float(0x4309b474)); // 250.207f, 130.383f, 257.529f, 137.705f +path.quadTo(SkBits2Float(0x43846cfc), SkBits2Float(0x431106f8), SkBits2Float(0x43846cfc), SkBits2Float(0x431b61f0)); // 264.851f, 145.027f, 264.851f, 155.383f +path.close(); + SkPath path21(path); + builder.add(path21, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x439597be), SkBits2Float(0x438dc3e1)); // 299.185f, 283.53f +path.quadTo(SkBits2Float(0x439597be), SkBits2Float(0x4392f15d), SkBits2Float(0x4391ee7c), SkBits2Float(0x43969a9f)); // 299.185f, 293.886f, 291.863f, 301.208f +path.quadTo(SkBits2Float(0x438e453a), SkBits2Float(0x439a43e1), SkBits2Float(0x438917be), SkBits2Float(0x439a43e1)); // 284.541f, 308.53f, 274.185f, 308.53f +path.quadTo(SkBits2Float(0x4383ea42), SkBits2Float(0x439a43e1), SkBits2Float(0x43804100), SkBits2Float(0x43969a9f)); // 263.83f, 308.53f, 256.508f, 301.208f +path.quadTo(SkBits2Float(0x43792f7c), SkBits2Float(0x4392f15d), SkBits2Float(0x43792f7c), SkBits2Float(0x438dc3e1)); // 249.185f, 293.886f, 249.185f, 283.53f +path.quadTo(SkBits2Float(0x43792f7c), SkBits2Float(0x43889665), SkBits2Float(0x43804100), SkBits2Float(0x4384ed23)); // 249.185f, 273.175f, 256.508f, 265.853f +path.quadTo(SkBits2Float(0x4383ea42), SkBits2Float(0x438143e1), SkBits2Float(0x438917be), SkBits2Float(0x438143e1)); // 263.83f, 258.53f, 274.185f, 258.53f +path.quadTo(SkBits2Float(0x438e453a), SkBits2Float(0x438143e1), SkBits2Float(0x4391ee7c), SkBits2Float(0x4384ed23)); // 284.541f, 258.53f, 291.863f, 265.853f +path.quadTo(SkBits2Float(0x439597be), SkBits2Float(0x43889665), SkBits2Float(0x439597be), SkBits2Float(0x438dc3e1)); // 299.185f, 273.175f, 299.185f, 283.53f +path.close(); + SkPath path22(path); + builder.add(path22, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42db7a2b), SkBits2Float(0x43699568)); // 109.739f, 233.584f +path.quadTo(SkBits2Float(0x42db7a2b), SkBits2Float(0x4373f05f), SkBits2Float(0x42ccd522), SkBits2Float(0x437b42e3)); // 109.739f, 243.939f, 102.416f, 251.261f +path.quadTo(SkBits2Float(0x42be301a), SkBits2Float(0x43814ab4), SkBits2Float(0x42a97a2b), SkBits2Float(0x43814ab4)); // 95.0939f, 258.584f, 84.7386f, 258.584f +path.quadTo(SkBits2Float(0x4294c43c), SkBits2Float(0x43814ab4), SkBits2Float(0x42861f34), SkBits2Float(0x437b42e3)); // 74.3833f, 258.584f, 67.0609f, 251.261f +path.quadTo(SkBits2Float(0x426ef456), SkBits2Float(0x4373f05f), SkBits2Float(0x426ef456), SkBits2Float(0x43699568)); // 59.7386f, 243.939f, 59.7386f, 233.584f +path.quadTo(SkBits2Float(0x426ef456), SkBits2Float(0x435f3a71), SkBits2Float(0x42861f34), SkBits2Float(0x4357e7ed)); // 59.7386f, 223.228f, 67.0609f, 215.906f +path.quadTo(SkBits2Float(0x4294c43c), SkBits2Float(0x43509569), SkBits2Float(0x42a97a2b), SkBits2Float(0x43509569)); // 74.3833f, 208.584f, 84.7386f, 208.584f +path.quadTo(SkBits2Float(0x42be301a), SkBits2Float(0x43509569), SkBits2Float(0x42ccd522), SkBits2Float(0x4357e7ed)); // 95.0939f, 208.584f, 102.416f, 215.906f +path.quadTo(SkBits2Float(0x42db7a2b), SkBits2Float(0x435f3a71), SkBits2Float(0x42db7a2b), SkBits2Float(0x43699568)); // 109.739f, 223.228f, 109.739f, 233.584f +path.close(); + SkPath path23(path); + builder.add(path23, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x433bea80), SkBits2Float(0x43861662)); // 187.916f, 268.175f +path.quadTo(SkBits2Float(0x433bea80), SkBits2Float(0x438b43de), SkBits2Float(0x433497fc), SkBits2Float(0x438eed20)); // 187.916f, 278.53f, 180.594f, 285.853f +path.quadTo(SkBits2Float(0x432d4578), SkBits2Float(0x43929662), SkBits2Float(0x4322ea80), SkBits2Float(0x43929662)); // 173.271f, 293.175f, 162.916f, 293.175f +path.quadTo(SkBits2Float(0x43188f88), SkBits2Float(0x43929662), SkBits2Float(0x43113d04), SkBits2Float(0x438eed20)); // 152.561f, 293.175f, 145.238f, 285.853f +path.quadTo(SkBits2Float(0x4309ea80), SkBits2Float(0x438b43de), SkBits2Float(0x4309ea80), SkBits2Float(0x43861662)); // 137.916f, 278.53f, 137.916f, 268.175f +path.quadTo(SkBits2Float(0x4309ea80), SkBits2Float(0x4380e8e6), SkBits2Float(0x43113d04), SkBits2Float(0x437a7f48)); // 137.916f, 257.82f, 145.238f, 250.497f +path.quadTo(SkBits2Float(0x43188f88), SkBits2Float(0x43732cc4), SkBits2Float(0x4322ea80), SkBits2Float(0x43732cc4)); // 152.561f, 243.175f, 162.916f, 243.175f +path.quadTo(SkBits2Float(0x432d4578), SkBits2Float(0x43732cc4), SkBits2Float(0x433497fc), SkBits2Float(0x437a7f48)); // 173.271f, 243.175f, 180.594f, 250.497f +path.quadTo(SkBits2Float(0x433bea80), SkBits2Float(0x4380e8e6), SkBits2Float(0x433bea80), SkBits2Float(0x43861662)); // 187.916f, 257.82f, 187.916f, 268.175f +path.close(); + SkPath path24(path); + builder.add(path24, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4386aaee), SkBits2Float(0x43991356)); // 269.335f, 306.151f +path.quadTo(SkBits2Float(0x4386aaee), SkBits2Float(0x439e40d2), SkBits2Float(0x438301ac), SkBits2Float(0x43a1ea14)); // 269.335f, 316.506f, 262.013f, 323.829f +path.quadTo(SkBits2Float(0x437eb0d4), SkBits2Float(0x43a59356), SkBits2Float(0x437455dc), SkBits2Float(0x43a59356)); // 254.691f, 331.151f, 244.335f, 331.151f +path.quadTo(SkBits2Float(0x4369fae4), SkBits2Float(0x43a59356), SkBits2Float(0x4362a860), SkBits2Float(0x43a1ea14)); // 233.98f, 331.151f, 226.658f, 323.829f +path.quadTo(SkBits2Float(0x435b55dc), SkBits2Float(0x439e40d2), SkBits2Float(0x435b55dc), SkBits2Float(0x43991356)); // 219.335f, 316.506f, 219.335f, 306.151f +path.quadTo(SkBits2Float(0x435b55dc), SkBits2Float(0x4393e5da), SkBits2Float(0x4362a860), SkBits2Float(0x43903c98)); // 219.335f, 295.796f, 226.658f, 288.473f +path.quadTo(SkBits2Float(0x4369fae4), SkBits2Float(0x438c9356), SkBits2Float(0x437455dc), SkBits2Float(0x438c9356)); // 233.98f, 281.151f, 244.335f, 281.151f +path.quadTo(SkBits2Float(0x437eb0d4), SkBits2Float(0x438c9356), SkBits2Float(0x438301ac), SkBits2Float(0x43903c98)); // 254.691f, 281.151f, 262.013f, 288.473f +path.quadTo(SkBits2Float(0x4386aaee), SkBits2Float(0x4393e5da), SkBits2Float(0x4386aaee), SkBits2Float(0x43991356)); // 269.335f, 295.796f, 269.335f, 306.151f +path.close(); + SkPath path25(path); + builder.add(path25, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43b0a7ab), SkBits2Float(0x42d6b25c)); // 353.31f, 107.348f +path.quadTo(SkBits2Float(0x43b0a7ab), SkBits2Float(0x42eb684b), SkBits2Float(0x43acfe69), SkBits2Float(0x42fa0d53)); // 353.31f, 117.704f, 345.988f, 125.026f +path.quadTo(SkBits2Float(0x43a95527), SkBits2Float(0x4304592e), SkBits2Float(0x43a427ab), SkBits2Float(0x4304592e)); // 338.665f, 132.348f, 328.31f, 132.348f +path.quadTo(SkBits2Float(0x439efa2f), SkBits2Float(0x4304592e), SkBits2Float(0x439b50ed), SkBits2Float(0x42fa0d53)); // 317.955f, 132.348f, 310.632f, 125.026f +path.quadTo(SkBits2Float(0x4397a7ab), SkBits2Float(0x42eb684b), SkBits2Float(0x4397a7ab), SkBits2Float(0x42d6b25c)); // 303.31f, 117.704f, 303.31f, 107.348f +path.quadTo(SkBits2Float(0x4397a7ab), SkBits2Float(0x42c1fc6d), SkBits2Float(0x439b50ed), SkBits2Float(0x42b35765)); // 303.31f, 96.993f, 310.632f, 89.6707f +path.quadTo(SkBits2Float(0x439efa2f), SkBits2Float(0x42a4b25d), SkBits2Float(0x43a427ab), SkBits2Float(0x42a4b25d)); // 317.955f, 82.3484f, 328.31f, 82.3484f +path.quadTo(SkBits2Float(0x43a95527), SkBits2Float(0x42a4b25d), SkBits2Float(0x43acfe69), SkBits2Float(0x42b35765)); // 338.665f, 82.3484f, 345.988f, 89.6707f +path.quadTo(SkBits2Float(0x43b0a7ab), SkBits2Float(0x42c1fc6d), SkBits2Float(0x43b0a7ab), SkBits2Float(0x42d6b25c)); // 353.31f, 96.993f, 353.31f, 107.348f +path.close(); + SkPath path26(path); + builder.add(path26, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43d478db), SkBits2Float(0x4301c45a)); // 424.944f, 129.767f +path.quadTo(SkBits2Float(0x43d478db), SkBits2Float(0x430c1f52), SkBits2Float(0x43d0cf99), SkBits2Float(0x431371d6)); // 424.944f, 140.122f, 417.622f, 147.445f +path.quadTo(SkBits2Float(0x43cd2657), SkBits2Float(0x431ac45a), SkBits2Float(0x43c7f8db), SkBits2Float(0x431ac45a)); // 410.3f, 154.767f, 399.944f, 154.767f +path.quadTo(SkBits2Float(0x43c2cb5f), SkBits2Float(0x431ac45a), SkBits2Float(0x43bf221d), SkBits2Float(0x431371d6)); // 389.589f, 154.767f, 382.267f, 147.445f +path.quadTo(SkBits2Float(0x43bb78db), SkBits2Float(0x430c1f52), SkBits2Float(0x43bb78db), SkBits2Float(0x4301c45a)); // 374.944f, 140.122f, 374.944f, 129.767f +path.quadTo(SkBits2Float(0x43bb78db), SkBits2Float(0x42eed2c5), SkBits2Float(0x43bf221d), SkBits2Float(0x42e02dbc)); // 374.944f, 119.412f, 382.267f, 112.089f +path.quadTo(SkBits2Float(0x43c2cb5f), SkBits2Float(0x42d188b4), SkBits2Float(0x43c7f8db), SkBits2Float(0x42d188b4)); // 389.589f, 104.767f, 399.944f, 104.767f +path.quadTo(SkBits2Float(0x43cd2657), SkBits2Float(0x42d188b4), SkBits2Float(0x43d0cf99), SkBits2Float(0x42e02dbc)); // 410.3f, 104.767f, 417.622f, 112.089f +path.quadTo(SkBits2Float(0x43d478db), SkBits2Float(0x42eed2c5), SkBits2Float(0x43d478db), SkBits2Float(0x4301c45a)); // 424.944f, 119.412f, 424.944f, 129.767f +path.close(); + SkPath path27(path); + builder.add(path27, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4370d681), SkBits2Float(0x4375fc44)); // 240.838f, 245.985f +path.quadTo(SkBits2Float(0x4370d681), SkBits2Float(0x43802b9e), SkBits2Float(0x436983fd), SkBits2Float(0x4383d4e0)); // 240.838f, 256.341f, 233.516f, 263.663f +path.quadTo(SkBits2Float(0x43623178), SkBits2Float(0x43877e22), SkBits2Float(0x4357d681), SkBits2Float(0x43877e22)); // 226.193f, 270.985f, 215.838f, 270.985f +path.quadTo(SkBits2Float(0x434d7b8a), SkBits2Float(0x43877e22), SkBits2Float(0x43462905), SkBits2Float(0x4383d4e0)); // 205.483f, 270.985f, 198.16f, 263.663f +path.quadTo(SkBits2Float(0x433ed681), SkBits2Float(0x43802b9e), SkBits2Float(0x433ed681), SkBits2Float(0x4375fc44)); // 190.838f, 256.341f, 190.838f, 245.985f +path.quadTo(SkBits2Float(0x433ed681), SkBits2Float(0x436ba14d), SkBits2Float(0x43462905), SkBits2Float(0x43644ec9)); // 190.838f, 235.63f, 198.16f, 228.308f +path.quadTo(SkBits2Float(0x434d7b8a), SkBits2Float(0x435cfc45), SkBits2Float(0x4357d681), SkBits2Float(0x435cfc45)); // 205.483f, 220.985f, 215.838f, 220.985f +path.quadTo(SkBits2Float(0x43623178), SkBits2Float(0x435cfc45), SkBits2Float(0x436983fd), SkBits2Float(0x43644ec9)); // 226.193f, 220.985f, 233.516f, 228.308f +path.quadTo(SkBits2Float(0x4370d681), SkBits2Float(0x436ba14d), SkBits2Float(0x4370d681), SkBits2Float(0x4375fc44)); // 240.838f, 235.63f, 240.838f, 245.985f +path.close(); + SkPath path28(path); + builder.add(path28, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43883c1d), SkBits2Float(0x438a9227)); // 272.47f, 277.142f +path.quadTo(SkBits2Float(0x43883c1d), SkBits2Float(0x438fbfa3), SkBits2Float(0x438492db), SkBits2Float(0x439368e5)); // 272.47f, 287.497f, 265.147f, 294.819f +path.quadTo(SkBits2Float(0x4380e999), SkBits2Float(0x43971227), SkBits2Float(0x4377783a), SkBits2Float(0x43971227)); // 257.825f, 302.142f, 247.47f, 302.142f +path.quadTo(SkBits2Float(0x436d1d42), SkBits2Float(0x43971227), SkBits2Float(0x4365cabe), SkBits2Float(0x439368e5)); // 237.114f, 302.142f, 229.792f, 294.819f +path.quadTo(SkBits2Float(0x435e783a), SkBits2Float(0x438fbfa3), SkBits2Float(0x435e783a), SkBits2Float(0x438a9227)); // 222.47f, 287.497f, 222.47f, 277.142f +path.quadTo(SkBits2Float(0x435e783a), SkBits2Float(0x438564ab), SkBits2Float(0x4365cabe), SkBits2Float(0x4381bb69)); // 222.47f, 266.786f, 229.792f, 259.464f +path.quadTo(SkBits2Float(0x436d1d42), SkBits2Float(0x437c244e), SkBits2Float(0x4377783a), SkBits2Float(0x437c244e)); // 237.114f, 252.142f, 247.47f, 252.142f +path.quadTo(SkBits2Float(0x4380e999), SkBits2Float(0x437c244e), SkBits2Float(0x438492db), SkBits2Float(0x4381bb69)); // 257.825f, 252.142f, 265.147f, 259.464f +path.quadTo(SkBits2Float(0x43883c1d), SkBits2Float(0x438564ab), SkBits2Float(0x43883c1d), SkBits2Float(0x438a9227)); // 272.47f, 266.786f, 272.47f, 277.142f +path.close(); + SkPath path29(path); + builder.add(path29, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43b5b3a4), SkBits2Float(0x43a2dbb0)); // 363.403f, 325.716f +path.quadTo(SkBits2Float(0x43b5b3a4), SkBits2Float(0x43a8092c), SkBits2Float(0x43b20a62), SkBits2Float(0x43abb26e)); // 363.403f, 336.072f, 356.081f, 343.394f +path.quadTo(SkBits2Float(0x43ae6120), SkBits2Float(0x43af5bb0), SkBits2Float(0x43a933a4), SkBits2Float(0x43af5bb0)); // 348.759f, 350.716f, 338.403f, 350.716f +path.quadTo(SkBits2Float(0x43a40628), SkBits2Float(0x43af5bb0), SkBits2Float(0x43a05ce6), SkBits2Float(0x43abb26e)); // 328.048f, 350.716f, 320.726f, 343.394f +path.quadTo(SkBits2Float(0x439cb3a4), SkBits2Float(0x43a8092c), SkBits2Float(0x439cb3a4), SkBits2Float(0x43a2dbb0)); // 313.403f, 336.072f, 313.403f, 325.716f +path.quadTo(SkBits2Float(0x439cb3a4), SkBits2Float(0x439dae34), SkBits2Float(0x43a05ce6), SkBits2Float(0x439a04f2)); // 313.403f, 315.361f, 320.726f, 308.039f +path.quadTo(SkBits2Float(0x43a40628), SkBits2Float(0x43965bb0), SkBits2Float(0x43a933a4), SkBits2Float(0x43965bb0)); // 328.048f, 300.716f, 338.403f, 300.716f +path.quadTo(SkBits2Float(0x43ae6120), SkBits2Float(0x43965bb0), SkBits2Float(0x43b20a62), SkBits2Float(0x439a04f2)); // 348.759f, 300.716f, 356.081f, 308.039f +path.quadTo(SkBits2Float(0x43b5b3a4), SkBits2Float(0x439dae34), SkBits2Float(0x43b5b3a4), SkBits2Float(0x43a2dbb0)); // 363.403f, 315.361f, 363.403f, 325.716f +path.close(); + SkPath path30(path); + builder.add(path30, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43a81cf4), SkBits2Float(0x431b2abc)); // 336.226f, 155.167f +path.quadTo(SkBits2Float(0x43a81cf4), SkBits2Float(0x432585b4), SkBits2Float(0x43a473b2), SkBits2Float(0x432cd838)); // 336.226f, 165.522f, 328.904f, 172.845f +path.quadTo(SkBits2Float(0x43a0ca70), SkBits2Float(0x43342abc), SkBits2Float(0x439b9cf4), SkBits2Float(0x43342abc)); // 321.582f, 180.167f, 311.226f, 180.167f +path.quadTo(SkBits2Float(0x43966f78), SkBits2Float(0x43342abc), SkBits2Float(0x4392c636), SkBits2Float(0x432cd838)); // 300.871f, 180.167f, 293.549f, 172.845f +path.quadTo(SkBits2Float(0x438f1cf4), SkBits2Float(0x432585b4), SkBits2Float(0x438f1cf4), SkBits2Float(0x431b2abc)); // 286.226f, 165.522f, 286.226f, 155.167f +path.quadTo(SkBits2Float(0x438f1cf4), SkBits2Float(0x4310cfc4), SkBits2Float(0x4392c636), SkBits2Float(0x43097d40)); // 286.226f, 144.812f, 293.549f, 137.489f +path.quadTo(SkBits2Float(0x43966f78), SkBits2Float(0x43022abc), SkBits2Float(0x439b9cf4), SkBits2Float(0x43022abc)); // 300.871f, 130.167f, 311.226f, 130.167f +path.quadTo(SkBits2Float(0x43a0ca70), SkBits2Float(0x43022abc), SkBits2Float(0x43a473b2), SkBits2Float(0x43097d40)); // 321.582f, 130.167f, 328.904f, 137.489f +path.quadTo(SkBits2Float(0x43a81cf4), SkBits2Float(0x4310cfc4), SkBits2Float(0x43a81cf4), SkBits2Float(0x431b2abc)); // 336.226f, 144.812f, 336.226f, 155.167f +path.close(); + SkPath path31(path); + builder.add(path31, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x435e092f), SkBits2Float(0x43698168)); // 222.036f, 233.505f +path.quadTo(SkBits2Float(0x435e092f), SkBits2Float(0x4373dc5f), SkBits2Float(0x4356b6ab), SkBits2Float(0x437b2ee3)); // 222.036f, 243.861f, 214.714f, 251.183f +path.quadTo(SkBits2Float(0x434f6426), SkBits2Float(0x438140b4), SkBits2Float(0x4345092f), SkBits2Float(0x438140b4)); // 207.391f, 258.505f, 197.036f, 258.505f +path.quadTo(SkBits2Float(0x433aae38), SkBits2Float(0x438140b4), SkBits2Float(0x43335bb3), SkBits2Float(0x437b2ee3)); // 186.681f, 258.505f, 179.358f, 251.183f +path.quadTo(SkBits2Float(0x432c092f), SkBits2Float(0x4373dc5f), SkBits2Float(0x432c092f), SkBits2Float(0x43698168)); // 172.036f, 243.861f, 172.036f, 233.505f +path.quadTo(SkBits2Float(0x432c092f), SkBits2Float(0x435f2671), SkBits2Float(0x43335bb3), SkBits2Float(0x4357d3ed)); // 172.036f, 223.15f, 179.358f, 215.828f +path.quadTo(SkBits2Float(0x433aae38), SkBits2Float(0x43508169), SkBits2Float(0x4345092f), SkBits2Float(0x43508169)); // 186.681f, 208.506f, 197.036f, 208.506f +path.quadTo(SkBits2Float(0x434f6426), SkBits2Float(0x43508169), SkBits2Float(0x4356b6ab), SkBits2Float(0x4357d3ed)); // 207.391f, 208.506f, 214.714f, 215.828f +path.quadTo(SkBits2Float(0x435e092f), SkBits2Float(0x435f2671), SkBits2Float(0x435e092f), SkBits2Float(0x43698168)); // 222.036f, 223.15f, 222.036f, 233.505f +path.close(); + SkPath path32(path); + builder.add(path32, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43b29d51), SkBits2Float(0x434f504b)); // 357.229f, 207.314f +path.quadTo(SkBits2Float(0x43b29d51), SkBits2Float(0x4359ab42), SkBits2Float(0x43aef40f), SkBits2Float(0x4360fdc7)); // 357.229f, 217.669f, 349.907f, 224.991f +path.quadTo(SkBits2Float(0x43ab4acd), SkBits2Float(0x4368504b), SkBits2Float(0x43a61d51), SkBits2Float(0x4368504b)); // 342.584f, 232.314f, 332.229f, 232.314f +path.quadTo(SkBits2Float(0x43a0efd5), SkBits2Float(0x4368504b), SkBits2Float(0x439d4693), SkBits2Float(0x4360fdc7)); // 321.874f, 232.314f, 314.551f, 224.991f +path.quadTo(SkBits2Float(0x43999d51), SkBits2Float(0x4359ab42), SkBits2Float(0x43999d51), SkBits2Float(0x434f504b)); // 307.229f, 217.669f, 307.229f, 207.314f +path.quadTo(SkBits2Float(0x43999d51), SkBits2Float(0x4344f554), SkBits2Float(0x439d4693), SkBits2Float(0x433da2cf)); // 307.229f, 196.958f, 314.551f, 189.636f +path.quadTo(SkBits2Float(0x43a0efd5), SkBits2Float(0x4336504b), SkBits2Float(0x43a61d51), SkBits2Float(0x4336504b)); // 321.874f, 182.314f, 332.229f, 182.314f +path.quadTo(SkBits2Float(0x43ab4acd), SkBits2Float(0x4336504b), SkBits2Float(0x43aef40f), SkBits2Float(0x433da2cf)); // 342.584f, 182.314f, 349.907f, 189.636f +path.quadTo(SkBits2Float(0x43b29d51), SkBits2Float(0x4344f554), SkBits2Float(0x43b29d51), SkBits2Float(0x434f504b)); // 357.229f, 196.958f, 357.229f, 207.314f +path.close(); + SkPath path33(path); + builder.add(path33, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x439022b6), SkBits2Float(0x434132a3)); // 288.271f, 193.198f +path.quadTo(SkBits2Float(0x439022b6), SkBits2Float(0x434b8d9a), SkBits2Float(0x438c7974), SkBits2Float(0x4352e01f)); // 288.271f, 203.553f, 280.949f, 210.875f +path.quadTo(SkBits2Float(0x4388d032), SkBits2Float(0x435a32a3), SkBits2Float(0x4383a2b6), SkBits2Float(0x435a32a3)); // 273.627f, 218.198f, 263.271f, 218.198f +path.quadTo(SkBits2Float(0x437cea74), SkBits2Float(0x435a32a3), SkBits2Float(0x437597f0), SkBits2Float(0x4352e01f)); // 252.916f, 218.198f, 245.594f, 210.875f +path.quadTo(SkBits2Float(0x436e456c), SkBits2Float(0x434b8d9a), SkBits2Float(0x436e456c), SkBits2Float(0x434132a3)); // 238.271f, 203.553f, 238.271f, 193.198f +path.quadTo(SkBits2Float(0x436e456c), SkBits2Float(0x4336d7ac), SkBits2Float(0x437597f0), SkBits2Float(0x432f8527)); // 238.271f, 182.842f, 245.594f, 175.52f +path.quadTo(SkBits2Float(0x437cea74), SkBits2Float(0x432832a3), SkBits2Float(0x4383a2b6), SkBits2Float(0x432832a3)); // 252.916f, 168.198f, 263.271f, 168.198f +path.quadTo(SkBits2Float(0x4388d032), SkBits2Float(0x432832a3), SkBits2Float(0x438c7974), SkBits2Float(0x432f8527)); // 273.627f, 168.198f, 280.949f, 175.52f +path.quadTo(SkBits2Float(0x439022b6), SkBits2Float(0x4336d7ac), SkBits2Float(0x439022b6), SkBits2Float(0x434132a3)); // 288.271f, 182.842f, 288.271f, 193.198f +path.close(); + SkPath path34(path); + builder.add(path34, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x434c6e1b), SkBits2Float(0x4386bd38)); // 204.43f, 269.478f +path.quadTo(SkBits2Float(0x434c6e1b), SkBits2Float(0x438beab4), SkBits2Float(0x43451b97), SkBits2Float(0x438f93f6)); // 204.43f, 279.834f, 197.108f, 287.156f +path.quadTo(SkBits2Float(0x433dc912), SkBits2Float(0x43933d38), SkBits2Float(0x43336e1b), SkBits2Float(0x43933d38)); // 189.785f, 294.478f, 179.43f, 294.478f +path.quadTo(SkBits2Float(0x43291324), SkBits2Float(0x43933d38), SkBits2Float(0x4321c09f), SkBits2Float(0x438f93f6)); // 169.075f, 294.478f, 161.752f, 287.156f +path.quadTo(SkBits2Float(0x431a6e1b), SkBits2Float(0x438beab4), SkBits2Float(0x431a6e1b), SkBits2Float(0x4386bd38)); // 154.43f, 279.834f, 154.43f, 269.478f +path.quadTo(SkBits2Float(0x431a6e1b), SkBits2Float(0x43818fbc), SkBits2Float(0x4321c09f), SkBits2Float(0x437bccf4)); // 154.43f, 259.123f, 161.752f, 251.801f +path.quadTo(SkBits2Float(0x43291324), SkBits2Float(0x43747a70), SkBits2Float(0x43336e1b), SkBits2Float(0x43747a70)); // 169.075f, 244.478f, 179.43f, 244.478f +path.quadTo(SkBits2Float(0x433dc912), SkBits2Float(0x43747a70), SkBits2Float(0x43451b97), SkBits2Float(0x437bccf4)); // 189.785f, 244.478f, 197.108f, 251.801f +path.quadTo(SkBits2Float(0x434c6e1b), SkBits2Float(0x43818fbc), SkBits2Float(0x434c6e1b), SkBits2Float(0x4386bd38)); // 204.43f, 259.123f, 204.43f, 269.478f +path.close(); + SkPath path35(path); + builder.add(path35, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43926b36), SkBits2Float(0x43b08773)); // 292.838f, 353.058f +path.quadTo(SkBits2Float(0x43926b36), SkBits2Float(0x43b5b4ef), SkBits2Float(0x438ec1f4), SkBits2Float(0x43b95e31)); // 292.838f, 363.414f, 285.515f, 370.736f +path.quadTo(SkBits2Float(0x438b18b2), SkBits2Float(0x43bd0773), SkBits2Float(0x4385eb36), SkBits2Float(0x43bd0773)); // 278.193f, 378.058f, 267.838f, 378.058f +path.quadTo(SkBits2Float(0x4380bdba), SkBits2Float(0x43bd0773), SkBits2Float(0x437a28f0), SkBits2Float(0x43b95e31)); // 257.482f, 378.058f, 250.16f, 370.736f +path.quadTo(SkBits2Float(0x4372d66c), SkBits2Float(0x43b5b4ef), SkBits2Float(0x4372d66c), SkBits2Float(0x43b08773)); // 242.838f, 363.414f, 242.838f, 353.058f +path.quadTo(SkBits2Float(0x4372d66c), SkBits2Float(0x43ab59f7), SkBits2Float(0x437a28f0), SkBits2Float(0x43a7b0b5)); // 242.838f, 342.703f, 250.16f, 335.381f +path.quadTo(SkBits2Float(0x4380bdba), SkBits2Float(0x43a40773), SkBits2Float(0x4385eb36), SkBits2Float(0x43a40773)); // 257.482f, 328.058f, 267.838f, 328.058f +path.quadTo(SkBits2Float(0x438b18b2), SkBits2Float(0x43a40773), SkBits2Float(0x438ec1f4), SkBits2Float(0x43a7b0b5)); // 278.193f, 328.058f, 285.515f, 335.381f +path.quadTo(SkBits2Float(0x43926b36), SkBits2Float(0x43ab59f7), SkBits2Float(0x43926b36), SkBits2Float(0x43b08773)); // 292.838f, 342.703f, 292.838f, 353.058f +path.close(); + SkPath path36(path); + builder.add(path36, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43ea874d), SkBits2Float(0x4382542c)); // 469.057f, 260.658f +path.quadTo(SkBits2Float(0x43ea874d), SkBits2Float(0x438781a8), SkBits2Float(0x43e6de0b), SkBits2Float(0x438b2aea)); // 469.057f, 271.013f, 461.735f, 278.335f +path.quadTo(SkBits2Float(0x43e334c9), SkBits2Float(0x438ed42c), SkBits2Float(0x43de074d), SkBits2Float(0x438ed42c)); // 454.412f, 285.658f, 444.057f, 285.658f +path.quadTo(SkBits2Float(0x43d8d9d1), SkBits2Float(0x438ed42c), SkBits2Float(0x43d5308f), SkBits2Float(0x438b2aea)); // 433.702f, 285.658f, 426.379f, 278.335f +path.quadTo(SkBits2Float(0x43d1874d), SkBits2Float(0x438781a8), SkBits2Float(0x43d1874d), SkBits2Float(0x4382542c)); // 419.057f, 271.013f, 419.057f, 260.658f +path.quadTo(SkBits2Float(0x43d1874d), SkBits2Float(0x437a4d60), SkBits2Float(0x43d5308f), SkBits2Float(0x4372fadc)); // 419.057f, 250.302f, 426.379f, 242.98f +path.quadTo(SkBits2Float(0x43d8d9d1), SkBits2Float(0x436ba858), SkBits2Float(0x43de074d), SkBits2Float(0x436ba858)); // 433.702f, 235.658f, 444.057f, 235.658f +path.quadTo(SkBits2Float(0x43e334c9), SkBits2Float(0x436ba858), SkBits2Float(0x43e6de0b), SkBits2Float(0x4372fadc)); // 454.412f, 235.658f, 461.735f, 242.98f +path.quadTo(SkBits2Float(0x43ea874d), SkBits2Float(0x437a4d60), SkBits2Float(0x43ea874d), SkBits2Float(0x4382542c)); // 469.057f, 250.302f, 469.057f, 260.658f +path.close(); + SkPath path37(path); + builder.add(path37, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42e7d715), SkBits2Float(0x436cecfc)); // 115.92f, 236.926f +path.quadTo(SkBits2Float(0x42e7d715), SkBits2Float(0x437747f3), SkBits2Float(0x42d9320c), SkBits2Float(0x437e9a77)); // 115.92f, 247.281f, 108.598f, 254.603f +path.quadTo(SkBits2Float(0x42ca8d04), SkBits2Float(0x4382f67e), SkBits2Float(0x42b5d715), SkBits2Float(0x4382f67e)); // 101.275f, 261.926f, 90.9201f, 261.926f +path.quadTo(SkBits2Float(0x42a12126), SkBits2Float(0x4382f67e), SkBits2Float(0x42927c1e), SkBits2Float(0x437e9a77)); // 80.5647f, 261.926f, 73.2424f, 254.603f +path.quadTo(SkBits2Float(0x4283d715), SkBits2Float(0x437747f3), SkBits2Float(0x4283d715), SkBits2Float(0x436cecfc)); // 65.9201f, 247.281f, 65.9201f, 236.926f +path.quadTo(SkBits2Float(0x4283d715), SkBits2Float(0x43629205), SkBits2Float(0x42927c1e), SkBits2Float(0x435b3f81)); // 65.9201f, 226.57f, 73.2424f, 219.248f +path.quadTo(SkBits2Float(0x42a12126), SkBits2Float(0x4353ecfd), SkBits2Float(0x42b5d715), SkBits2Float(0x4353ecfd)); // 80.5647f, 211.926f, 90.9201f, 211.926f +path.quadTo(SkBits2Float(0x42ca8d04), SkBits2Float(0x4353ecfd), SkBits2Float(0x42d9320c), SkBits2Float(0x435b3f81)); // 101.275f, 211.926f, 108.598f, 219.248f +path.quadTo(SkBits2Float(0x42e7d715), SkBits2Float(0x43629205), SkBits2Float(0x42e7d715), SkBits2Float(0x436cecfc)); // 115.92f, 226.57f, 115.92f, 236.926f +path.close(); + SkPath path38(path); + builder.add(path38, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43545413), SkBits2Float(0x4333cfcf)); // 212.328f, 179.812f +path.quadTo(SkBits2Float(0x43545413), SkBits2Float(0x433e2ac6), SkBits2Float(0x434d018f), SkBits2Float(0x43457d4b)); // 212.328f, 190.167f, 205.006f, 197.489f +path.quadTo(SkBits2Float(0x4345af0a), SkBits2Float(0x434ccfcf), SkBits2Float(0x433b5413), SkBits2Float(0x434ccfcf)); // 197.684f, 204.812f, 187.328f, 204.812f +path.quadTo(SkBits2Float(0x4330f91c), SkBits2Float(0x434ccfcf), SkBits2Float(0x4329a697), SkBits2Float(0x43457d4b)); // 176.973f, 204.812f, 169.651f, 197.489f +path.quadTo(SkBits2Float(0x43225413), SkBits2Float(0x433e2ac6), SkBits2Float(0x43225413), SkBits2Float(0x4333cfcf)); // 162.328f, 190.167f, 162.328f, 179.812f +path.quadTo(SkBits2Float(0x43225413), SkBits2Float(0x432974d8), SkBits2Float(0x4329a697), SkBits2Float(0x43222253)); // 162.328f, 169.456f, 169.651f, 162.134f +path.quadTo(SkBits2Float(0x4330f91c), SkBits2Float(0x431acfcf), SkBits2Float(0x433b5413), SkBits2Float(0x431acfcf)); // 176.973f, 154.812f, 187.328f, 154.812f +path.quadTo(SkBits2Float(0x4345af0a), SkBits2Float(0x431acfcf), SkBits2Float(0x434d018f), SkBits2Float(0x43222253)); // 197.684f, 154.812f, 205.006f, 162.134f +path.quadTo(SkBits2Float(0x43545413), SkBits2Float(0x432974d8), SkBits2Float(0x43545413), SkBits2Float(0x4333cfcf)); // 212.328f, 169.456f, 212.328f, 179.812f +path.close(); + SkPath path39(path); + builder.add(path39, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43c9b16c), SkBits2Float(0x41823be6)); // 403.386f, 16.2792f +path.quadTo(SkBits2Float(0x43c9b16c), SkBits2Float(0x41d513a2), SkBits2Float(0x43c6082a), SkBits2Float(0x4207d3e2)); // 403.386f, 26.6346f, 396.064f, 33.9569f +path.quadTo(SkBits2Float(0x43c25ee8), SkBits2Float(0x42251df3), SkBits2Float(0x43bd316c), SkBits2Float(0x42251df3)); // 388.741f, 41.2792f, 378.386f, 41.2792f +path.quadTo(SkBits2Float(0x43b803f0), SkBits2Float(0x42251df3), SkBits2Float(0x43b45aae), SkBits2Float(0x4207d3e2)); // 368.031f, 41.2792f, 360.708f, 33.9569f +path.quadTo(SkBits2Float(0x43b0b16c), SkBits2Float(0x41d513a2), SkBits2Float(0x43b0b16c), SkBits2Float(0x41823be6)); // 353.386f, 26.6346f, 353.386f, 16.2792f +path.quadTo(SkBits2Float(0x43b0b16c), SkBits2Float(0x40bd90a8), SkBits2Float(0x43b45aae), SkBits2Float(0xbfb2ff80)); // 353.386f, 5.92391f, 360.708f, -1.39842f +path.quadTo(SkBits2Float(0x43b803f0), SkBits2Float(0xc10b8834), SkBits2Float(0x43bd316c), SkBits2Float(0xc10b8834)); // 368.031f, -8.72075f, 378.386f, -8.72075f +path.quadTo(SkBits2Float(0x43c25ee8), SkBits2Float(0xc10b8834), SkBits2Float(0x43c6082a), SkBits2Float(0xbfb2ff80)); // 388.741f, -8.72075f, 396.064f, -1.39842f +path.quadTo(SkBits2Float(0x43c9b16c), SkBits2Float(0x40bd90a8), SkBits2Float(0x43c9b16c), SkBits2Float(0x41823be6)); // 403.386f, 5.92391f, 403.386f, 16.2792f +path.close(); + SkPath path40(path); + builder.add(path40, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43747fcb), SkBits2Float(0x43805e9d)); // 244.499f, 256.739f +path.quadTo(SkBits2Float(0x43747fcb), SkBits2Float(0x43858c19), SkBits2Float(0x436d2d47), SkBits2Float(0x4389355b)); // 244.499f, 267.095f, 237.177f, 274.417f +path.quadTo(SkBits2Float(0x4365dac2), SkBits2Float(0x438cde9d), SkBits2Float(0x435b7fcb), SkBits2Float(0x438cde9d)); // 229.855f, 281.739f, 219.499f, 281.739f +path.quadTo(SkBits2Float(0x435124d4), SkBits2Float(0x438cde9d), SkBits2Float(0x4349d24f), SkBits2Float(0x4389355b)); // 209.144f, 281.739f, 201.822f, 274.417f +path.quadTo(SkBits2Float(0x43427fcb), SkBits2Float(0x43858c19), SkBits2Float(0x43427fcb), SkBits2Float(0x43805e9d)); // 194.499f, 267.095f, 194.499f, 256.739f +path.quadTo(SkBits2Float(0x43427fcb), SkBits2Float(0x43766242), SkBits2Float(0x4349d24f), SkBits2Float(0x436f0fbe)); // 194.499f, 246.384f, 201.822f, 239.061f +path.quadTo(SkBits2Float(0x435124d4), SkBits2Float(0x4367bd3a), SkBits2Float(0x435b7fcb), SkBits2Float(0x4367bd3a)); // 209.144f, 231.739f, 219.499f, 231.739f +path.quadTo(SkBits2Float(0x4365dac2), SkBits2Float(0x4367bd3a), SkBits2Float(0x436d2d47), SkBits2Float(0x436f0fbe)); // 229.855f, 231.739f, 237.177f, 239.061f +path.quadTo(SkBits2Float(0x43747fcb), SkBits2Float(0x43766242), SkBits2Float(0x43747fcb), SkBits2Float(0x43805e9d)); // 244.499f, 246.384f, 244.499f, 256.739f +path.close(); + SkPath path41(path); + builder.add(path41, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43910318), SkBits2Float(0x43826a1e)); // 290.024f, 260.829f +path.quadTo(SkBits2Float(0x43910318), SkBits2Float(0x4387979a), SkBits2Float(0x438d59d6), SkBits2Float(0x438b40dc)); // 290.024f, 271.184f, 282.702f, 278.507f +path.quadTo(SkBits2Float(0x4389b094), SkBits2Float(0x438eea1e), SkBits2Float(0x43848318), SkBits2Float(0x438eea1e)); // 275.38f, 285.829f, 265.024f, 285.829f +path.quadTo(SkBits2Float(0x437eab38), SkBits2Float(0x438eea1e), SkBits2Float(0x437758b4), SkBits2Float(0x438b40dc)); // 254.669f, 285.829f, 247.346f, 278.507f +path.quadTo(SkBits2Float(0x43700630), SkBits2Float(0x4387979a), SkBits2Float(0x43700630), SkBits2Float(0x43826a1e)); // 240.024f, 271.184f, 240.024f, 260.829f +path.quadTo(SkBits2Float(0x43700630), SkBits2Float(0x437a7944), SkBits2Float(0x437758b4), SkBits2Float(0x437326c0)); // 240.024f, 250.474f, 247.346f, 243.151f +path.quadTo(SkBits2Float(0x437eab38), SkBits2Float(0x436bd43c), SkBits2Float(0x43848318), SkBits2Float(0x436bd43c)); // 254.669f, 235.829f, 265.024f, 235.829f +path.quadTo(SkBits2Float(0x4389b094), SkBits2Float(0x436bd43c), SkBits2Float(0x438d59d6), SkBits2Float(0x437326c0)); // 275.38f, 235.829f, 282.702f, 243.151f +path.quadTo(SkBits2Float(0x43910318), SkBits2Float(0x437a7944), SkBits2Float(0x43910318), SkBits2Float(0x43826a1e)); // 290.024f, 250.474f, 290.024f, 260.829f +path.close(); + SkPath path42(path); + builder.add(path42, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x41c80000), SkBits2Float(0x436edb04)); // 25, 238.856f +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x437935fb), SkBits2Float(0x418d6bde), SkBits2Float(0x43804440)); // 25, 249.211f, 17.6777f, 256.533f +path.quadTo(SkBits2Float(0x4125af78), SkBits2Float(0x4383ed82), SkBits2Float(0x00000000), SkBits2Float(0x4383ed82)); // 10.3553f, 263.856f, 0, 263.856f +path.quadTo(SkBits2Float(0xc125af78), SkBits2Float(0x4383ed82), SkBits2Float(0xc18d6bde), SkBits2Float(0x43804440)); // -10.3553f, 263.856f, -17.6777f, 256.533f +path.quadTo(SkBits2Float(0xc1c80000), SkBits2Float(0x437935fb), SkBits2Float(0xc1c80000), SkBits2Float(0x436edb04)); // -25, 249.211f, -25, 238.856f +path.quadTo(SkBits2Float(0xc1c80000), SkBits2Float(0x4364800d), SkBits2Float(0xc18d6bde), SkBits2Float(0x435d2d89)); // -25, 228.5f, -17.6777f, 221.178f +path.quadTo(SkBits2Float(0xc125af78), SkBits2Float(0x4355db05), SkBits2Float(0x00000000), SkBits2Float(0x4355db05)); // -10.3553f, 213.856f, 0, 213.856f +path.quadTo(SkBits2Float(0x4125af78), SkBits2Float(0x4355db05), SkBits2Float(0x418d6bde), SkBits2Float(0x435d2d89)); // 10.3553f, 213.856f, 17.6777f, 221.178f +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4364800d), SkBits2Float(0x41c80000), SkBits2Float(0x436edb04)); // 25, 228.5f, 25, 238.856f +path.close(); + SkPath path43(path); + builder.add(path43, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x435d07bd), SkBits2Float(0x4395fbb5)); // 221.03f, 299.966f +path.quadTo(SkBits2Float(0x435d07bd), SkBits2Float(0x439b2931), SkBits2Float(0x4355b539), SkBits2Float(0x439ed273)); // 221.03f, 310.322f, 213.708f, 317.644f +path.quadTo(SkBits2Float(0x434e62b4), SkBits2Float(0x43a27bb5), SkBits2Float(0x434407bd), SkBits2Float(0x43a27bb5)); // 206.386f, 324.966f, 196.03f, 324.966f +path.quadTo(SkBits2Float(0x4339acc6), SkBits2Float(0x43a27bb5), SkBits2Float(0x43325a41), SkBits2Float(0x439ed273)); // 185.675f, 324.966f, 178.353f, 317.644f +path.quadTo(SkBits2Float(0x432b07bd), SkBits2Float(0x439b2931), SkBits2Float(0x432b07bd), SkBits2Float(0x4395fbb5)); // 171.03f, 310.322f, 171.03f, 299.966f +path.quadTo(SkBits2Float(0x432b07bd), SkBits2Float(0x4390ce39), SkBits2Float(0x43325a41), SkBits2Float(0x438d24f7)); // 171.03f, 289.611f, 178.353f, 282.289f +path.quadTo(SkBits2Float(0x4339acc6), SkBits2Float(0x43897bb5), SkBits2Float(0x434407bd), SkBits2Float(0x43897bb5)); // 185.675f, 274.966f, 196.03f, 274.966f +path.quadTo(SkBits2Float(0x434e62b4), SkBits2Float(0x43897bb5), SkBits2Float(0x4355b539), SkBits2Float(0x438d24f7)); // 206.386f, 274.966f, 213.708f, 282.289f +path.quadTo(SkBits2Float(0x435d07bd), SkBits2Float(0x4390ce39), SkBits2Float(0x435d07bd), SkBits2Float(0x4395fbb5)); // 221.03f, 289.611f, 221.03f, 299.966f +path.close(); + SkPath path44(path); + builder.add(path44, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43a3ec29), SkBits2Float(0x434ac5a3)); // 327.845f, 202.772f +path.quadTo(SkBits2Float(0x43a3ec29), SkBits2Float(0x4355209a), SkBits2Float(0x43a042e7), SkBits2Float(0x435c731f)); // 327.845f, 213.127f, 320.523f, 220.45f +path.quadTo(SkBits2Float(0x439c99a5), SkBits2Float(0x4363c5a3), SkBits2Float(0x43976c29), SkBits2Float(0x4363c5a3)); // 313.2f, 227.772f, 302.845f, 227.772f +path.quadTo(SkBits2Float(0x43923ead), SkBits2Float(0x4363c5a3), SkBits2Float(0x438e956b), SkBits2Float(0x435c731f)); // 292.49f, 227.772f, 285.167f, 220.45f +path.quadTo(SkBits2Float(0x438aec29), SkBits2Float(0x4355209a), SkBits2Float(0x438aec29), SkBits2Float(0x434ac5a3)); // 277.845f, 213.127f, 277.845f, 202.772f +path.quadTo(SkBits2Float(0x438aec29), SkBits2Float(0x43406aac), SkBits2Float(0x438e956b), SkBits2Float(0x43391827)); // 277.845f, 192.417f, 285.167f, 185.094f +path.quadTo(SkBits2Float(0x43923ead), SkBits2Float(0x4331c5a3), SkBits2Float(0x43976c29), SkBits2Float(0x4331c5a3)); // 292.49f, 177.772f, 302.845f, 177.772f +path.quadTo(SkBits2Float(0x439c99a5), SkBits2Float(0x4331c5a3), SkBits2Float(0x43a042e7), SkBits2Float(0x43391827)); // 313.2f, 177.772f, 320.523f, 185.094f +path.quadTo(SkBits2Float(0x43a3ec29), SkBits2Float(0x43406aac), SkBits2Float(0x43a3ec29), SkBits2Float(0x434ac5a3)); // 327.845f, 192.417f, 327.845f, 202.772f +path.close(); + SkPath path45(path); + builder.add(path45, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4380585e), SkBits2Float(0x43199f0e)); // 256.69f, 153.621f +path.quadTo(SkBits2Float(0x4380585e), SkBits2Float(0x4323fa06), SkBits2Float(0x43795e38), SkBits2Float(0x432b4c8a)); // 256.69f, 163.977f, 249.368f, 171.299f +path.quadTo(SkBits2Float(0x43720bb4), SkBits2Float(0x43329f0e), SkBits2Float(0x4367b0bc), SkBits2Float(0x43329f0e)); // 242.046f, 178.621f, 231.69f, 178.621f +path.quadTo(SkBits2Float(0x435d55c4), SkBits2Float(0x43329f0e), SkBits2Float(0x43560340), SkBits2Float(0x432b4c8a)); // 221.335f, 178.621f, 214.013f, 171.299f +path.quadTo(SkBits2Float(0x434eb0bc), SkBits2Float(0x4323fa06), SkBits2Float(0x434eb0bc), SkBits2Float(0x43199f0e)); // 206.69f, 163.977f, 206.69f, 153.621f +path.quadTo(SkBits2Float(0x434eb0bc), SkBits2Float(0x430f4416), SkBits2Float(0x43560340), SkBits2Float(0x4307f192)); // 206.69f, 143.266f, 214.013f, 135.944f +path.quadTo(SkBits2Float(0x435d55c4), SkBits2Float(0x43009f0e), SkBits2Float(0x4367b0bc), SkBits2Float(0x43009f0e)); // 221.335f, 128.621f, 231.69f, 128.621f +path.quadTo(SkBits2Float(0x43720bb4), SkBits2Float(0x43009f0e), SkBits2Float(0x43795e38), SkBits2Float(0x4307f192)); // 242.046f, 128.621f, 249.368f, 135.944f +path.quadTo(SkBits2Float(0x4380585e), SkBits2Float(0x430f4416), SkBits2Float(0x4380585e), SkBits2Float(0x43199f0e)); // 256.69f, 143.266f, 256.69f, 153.621f +path.close(); + SkPath path46(path); + builder.add(path46, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43599e4b), SkBits2Float(0x43c5e452)); // 217.618f, 395.784f +path.quadTo(SkBits2Float(0x43599e4b), SkBits2Float(0x43cb11ce), SkBits2Float(0x43524bc7), SkBits2Float(0x43cebb10)); // 217.618f, 406.139f, 210.296f, 413.461f +path.quadTo(SkBits2Float(0x434af942), SkBits2Float(0x43d26452), SkBits2Float(0x43409e4b), SkBits2Float(0x43d26452)); // 202.974f, 420.784f, 192.618f, 420.784f +path.quadTo(SkBits2Float(0x43364354), SkBits2Float(0x43d26452), SkBits2Float(0x432ef0cf), SkBits2Float(0x43cebb10)); // 182.263f, 420.784f, 174.941f, 413.461f +path.quadTo(SkBits2Float(0x43279e4b), SkBits2Float(0x43cb11ce), SkBits2Float(0x43279e4b), SkBits2Float(0x43c5e452)); // 167.618f, 406.139f, 167.618f, 395.784f +path.quadTo(SkBits2Float(0x43279e4b), SkBits2Float(0x43c0b6d6), SkBits2Float(0x432ef0cf), SkBits2Float(0x43bd0d94)); // 167.618f, 385.428f, 174.941f, 378.106f +path.quadTo(SkBits2Float(0x43364354), SkBits2Float(0x43b96452), SkBits2Float(0x43409e4b), SkBits2Float(0x43b96452)); // 182.263f, 370.784f, 192.618f, 370.784f +path.quadTo(SkBits2Float(0x434af942), SkBits2Float(0x43b96452), SkBits2Float(0x43524bc7), SkBits2Float(0x43bd0d94)); // 202.974f, 370.784f, 210.296f, 378.106f +path.quadTo(SkBits2Float(0x43599e4b), SkBits2Float(0x43c0b6d6), SkBits2Float(0x43599e4b), SkBits2Float(0x43c5e452)); // 217.618f, 385.428f, 217.618f, 395.784f +path.close(); + SkPath path47(path); + builder.add(path47, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x435e28dc), SkBits2Float(0x43a32a69)); // 222.16f, 326.331f +path.quadTo(SkBits2Float(0x435e28dc), SkBits2Float(0x43a857e5), SkBits2Float(0x4356d658), SkBits2Float(0x43ac0127)); // 222.16f, 336.687f, 214.837f, 344.009f +path.quadTo(SkBits2Float(0x434f83d4), SkBits2Float(0x43afaa69), SkBits2Float(0x434528dc), SkBits2Float(0x43afaa69)); // 207.515f, 351.331f, 197.16f, 351.331f +path.quadTo(SkBits2Float(0x433acde4), SkBits2Float(0x43afaa69), SkBits2Float(0x43337b60), SkBits2Float(0x43ac0127)); // 186.804f, 351.331f, 179.482f, 344.009f +path.quadTo(SkBits2Float(0x432c28dc), SkBits2Float(0x43a857e5), SkBits2Float(0x432c28dc), SkBits2Float(0x43a32a69)); // 172.16f, 336.687f, 172.16f, 326.331f +path.quadTo(SkBits2Float(0x432c28dc), SkBits2Float(0x439dfced), SkBits2Float(0x43337b60), SkBits2Float(0x439a53ab)); // 172.16f, 315.976f, 179.482f, 308.654f +path.quadTo(SkBits2Float(0x433acde4), SkBits2Float(0x4396aa69), SkBits2Float(0x434528dc), SkBits2Float(0x4396aa69)); // 186.804f, 301.331f, 197.16f, 301.331f +path.quadTo(SkBits2Float(0x434f83d4), SkBits2Float(0x4396aa69), SkBits2Float(0x4356d658), SkBits2Float(0x439a53ab)); // 207.515f, 301.331f, 214.837f, 308.654f +path.quadTo(SkBits2Float(0x435e28dc), SkBits2Float(0x439dfced), SkBits2Float(0x435e28dc), SkBits2Float(0x43a32a69)); // 222.16f, 315.976f, 222.16f, 326.331f +path.close(); + SkPath path48(path); + builder.add(path48, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x433b4ff9), SkBits2Float(0x438034ac)); // 187.312f, 256.411f +path.quadTo(SkBits2Float(0x433b4ff9), SkBits2Float(0x43856228), SkBits2Float(0x4333fd75), SkBits2Float(0x43890b6a)); // 187.312f, 266.767f, 179.99f, 274.089f +path.quadTo(SkBits2Float(0x432caaf0), SkBits2Float(0x438cb4ac), SkBits2Float(0x43224ff9), SkBits2Float(0x438cb4ac)); // 172.668f, 281.411f, 162.312f, 281.411f +path.quadTo(SkBits2Float(0x4317f502), SkBits2Float(0x438cb4ac), SkBits2Float(0x4310a27d), SkBits2Float(0x43890b6a)); // 151.957f, 281.411f, 144.635f, 274.089f +path.quadTo(SkBits2Float(0x43094ff9), SkBits2Float(0x43856228), SkBits2Float(0x43094ff9), SkBits2Float(0x438034ac)); // 137.312f, 266.767f, 137.312f, 256.411f +path.quadTo(SkBits2Float(0x43094ff9), SkBits2Float(0x43760e60), SkBits2Float(0x4310a27d), SkBits2Float(0x436ebbdc)); // 137.312f, 246.056f, 144.635f, 238.734f +path.quadTo(SkBits2Float(0x4317f502), SkBits2Float(0x43676958), SkBits2Float(0x43224ff9), SkBits2Float(0x43676958)); // 151.957f, 231.411f, 162.312f, 231.411f +path.quadTo(SkBits2Float(0x432caaf0), SkBits2Float(0x43676958), SkBits2Float(0x4333fd75), SkBits2Float(0x436ebbdc)); // 172.668f, 231.411f, 179.99f, 238.734f +path.quadTo(SkBits2Float(0x433b4ff9), SkBits2Float(0x43760e60), SkBits2Float(0x433b4ff9), SkBits2Float(0x438034ac)); // 187.312f, 246.056f, 187.312f, 256.411f +path.close(); + SkPath path49(path); + builder.add(path49, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4346c2ee), SkBits2Float(0x435b284b)); // 198.761f, 219.157f +path.quadTo(SkBits2Float(0x4346c2ee), SkBits2Float(0x43658342), SkBits2Float(0x433f706a), SkBits2Float(0x436cd5c7)); // 198.761f, 229.513f, 191.439f, 236.835f +path.quadTo(SkBits2Float(0x43381de6), SkBits2Float(0x4374284b), SkBits2Float(0x432dc2ee), SkBits2Float(0x4374284b)); // 184.117f, 244.157f, 173.761f, 244.157f +path.quadTo(SkBits2Float(0x432367f6), SkBits2Float(0x4374284b), SkBits2Float(0x431c1572), SkBits2Float(0x436cd5c7)); // 163.406f, 244.157f, 156.084f, 236.835f +path.quadTo(SkBits2Float(0x4314c2ee), SkBits2Float(0x43658342), SkBits2Float(0x4314c2ee), SkBits2Float(0x435b284b)); // 148.761f, 229.513f, 148.761f, 219.157f +path.quadTo(SkBits2Float(0x4314c2ee), SkBits2Float(0x4350cd54), SkBits2Float(0x431c1572), SkBits2Float(0x43497acf)); // 148.761f, 208.802f, 156.084f, 201.48f +path.quadTo(SkBits2Float(0x432367f6), SkBits2Float(0x4342284b), SkBits2Float(0x432dc2ee), SkBits2Float(0x4342284b)); // 163.406f, 194.157f, 173.761f, 194.157f +path.quadTo(SkBits2Float(0x43381de6), SkBits2Float(0x4342284b), SkBits2Float(0x433f706a), SkBits2Float(0x43497acf)); // 184.117f, 194.157f, 191.439f, 201.48f +path.quadTo(SkBits2Float(0x4346c2ee), SkBits2Float(0x4350cd54), SkBits2Float(0x4346c2ee), SkBits2Float(0x435b284b)); // 198.761f, 208.802f, 198.761f, 219.157f +path.close(); + SkPath path50(path); + builder.add(path50, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43fb0cf6), SkBits2Float(0x438812a5)); // 502.101f, 272.146f +path.quadTo(SkBits2Float(0x43fb0cf6), SkBits2Float(0x438d4021), SkBits2Float(0x43f763b4), SkBits2Float(0x4390e963)); // 502.101f, 282.501f, 494.779f, 289.823f +path.quadTo(SkBits2Float(0x43f3ba72), SkBits2Float(0x439492a5), SkBits2Float(0x43ee8cf6), SkBits2Float(0x439492a5)); // 487.457f, 297.146f, 477.101f, 297.146f +path.quadTo(SkBits2Float(0x43e95f7a), SkBits2Float(0x439492a5), SkBits2Float(0x43e5b638), SkBits2Float(0x4390e963)); // 466.746f, 297.146f, 459.424f, 289.823f +path.quadTo(SkBits2Float(0x43e20cf6), SkBits2Float(0x438d4021), SkBits2Float(0x43e20cf6), SkBits2Float(0x438812a5)); // 452.101f, 282.501f, 452.101f, 272.146f +path.quadTo(SkBits2Float(0x43e20cf6), SkBits2Float(0x4382e529), SkBits2Float(0x43e5b638), SkBits2Float(0x437e77ce)); // 452.101f, 261.79f, 459.424f, 254.468f +path.quadTo(SkBits2Float(0x43e95f7a), SkBits2Float(0x4377254a), SkBits2Float(0x43ee8cf6), SkBits2Float(0x4377254a)); // 466.746f, 247.146f, 477.101f, 247.146f +path.quadTo(SkBits2Float(0x43f3ba72), SkBits2Float(0x4377254a), SkBits2Float(0x43f763b4), SkBits2Float(0x437e77ce)); // 487.457f, 247.146f, 494.779f, 254.468f +path.quadTo(SkBits2Float(0x43fb0cf6), SkBits2Float(0x4382e529), SkBits2Float(0x43fb0cf6), SkBits2Float(0x438812a5)); // 502.101f, 261.79f, 502.101f, 272.146f +path.close(); + SkPath path51(path); + builder.add(path51, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x438f42d6), SkBits2Float(0x435a09d4)); // 286.522f, 218.038f +path.quadTo(SkBits2Float(0x438f42d6), SkBits2Float(0x436464cc), SkBits2Float(0x438b9994), SkBits2Float(0x436bb750)); // 286.522f, 228.394f, 279.2f, 235.716f +path.quadTo(SkBits2Float(0x4387f052), SkBits2Float(0x437309d4), SkBits2Float(0x4382c2d6), SkBits2Float(0x437309d4)); // 271.878f, 243.038f, 261.522f, 243.038f +path.quadTo(SkBits2Float(0x437b2ab4), SkBits2Float(0x437309d4), SkBits2Float(0x4373d830), SkBits2Float(0x436bb750)); // 251.167f, 243.038f, 243.844f, 235.716f +path.quadTo(SkBits2Float(0x436c85ac), SkBits2Float(0x436464cc), SkBits2Float(0x436c85ac), SkBits2Float(0x435a09d4)); // 236.522f, 228.394f, 236.522f, 218.038f +path.quadTo(SkBits2Float(0x436c85ac), SkBits2Float(0x434faedc), SkBits2Float(0x4373d830), SkBits2Float(0x43485c58)); // 236.522f, 207.683f, 243.844f, 200.361f +path.quadTo(SkBits2Float(0x437b2ab4), SkBits2Float(0x434109d4), SkBits2Float(0x4382c2d6), SkBits2Float(0x434109d4)); // 251.167f, 193.038f, 261.522f, 193.038f +path.quadTo(SkBits2Float(0x4387f052), SkBits2Float(0x434109d4), SkBits2Float(0x438b9994), SkBits2Float(0x43485c58)); // 271.878f, 193.038f, 279.2f, 200.361f +path.quadTo(SkBits2Float(0x438f42d6), SkBits2Float(0x434faedc), SkBits2Float(0x438f42d6), SkBits2Float(0x435a09d4)); // 286.522f, 207.683f, 286.522f, 218.038f +path.close(); + SkPath path52(path); + builder.add(path52, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43ac18fb), SkBits2Float(0x43378440)); // 344.195f, 183.517f +path.quadTo(SkBits2Float(0x43ac18fb), SkBits2Float(0x4341df38), SkBits2Float(0x43a86fb9), SkBits2Float(0x434931bc)); // 344.195f, 193.872f, 336.873f, 201.194f +path.quadTo(SkBits2Float(0x43a4c677), SkBits2Float(0x43508440), SkBits2Float(0x439f98fb), SkBits2Float(0x43508440)); // 329.551f, 208.517f, 319.195f, 208.517f +path.quadTo(SkBits2Float(0x439a6b7f), SkBits2Float(0x43508440), SkBits2Float(0x4396c23d), SkBits2Float(0x434931bc)); // 308.84f, 208.517f, 301.517f, 201.194f +path.quadTo(SkBits2Float(0x439318fb), SkBits2Float(0x4341df38), SkBits2Float(0x439318fb), SkBits2Float(0x43378440)); // 294.195f, 193.872f, 294.195f, 183.517f +path.quadTo(SkBits2Float(0x439318fb), SkBits2Float(0x432d2948), SkBits2Float(0x4396c23d), SkBits2Float(0x4325d6c4)); // 294.195f, 173.161f, 301.517f, 165.839f +path.quadTo(SkBits2Float(0x439a6b7f), SkBits2Float(0x431e8440), SkBits2Float(0x439f98fb), SkBits2Float(0x431e8440)); // 308.84f, 158.517f, 319.195f, 158.517f +path.quadTo(SkBits2Float(0x43a4c677), SkBits2Float(0x431e8440), SkBits2Float(0x43a86fb9), SkBits2Float(0x4325d6c4)); // 329.551f, 158.517f, 336.873f, 165.839f +path.quadTo(SkBits2Float(0x43ac18fb), SkBits2Float(0x432d2948), SkBits2Float(0x43ac18fb), SkBits2Float(0x43378440)); // 344.195f, 173.161f, 344.195f, 183.517f +path.close(); + SkPath path53(path); + builder.add(path53, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42ef12a3), SkBits2Float(0x430c5faa)); // 119.536f, 140.374f +path.quadTo(SkBits2Float(0x42ef12a3), SkBits2Float(0x4316baa2), SkBits2Float(0x42e06d9a), SkBits2Float(0x431e0d26)); // 119.536f, 150.729f, 112.214f, 158.051f +path.quadTo(SkBits2Float(0x42d1c892), SkBits2Float(0x43255faa), SkBits2Float(0x42bd12a3), SkBits2Float(0x43255faa)); // 104.892f, 165.374f, 94.5364f, 165.374f +path.quadTo(SkBits2Float(0x42a85cb4), SkBits2Float(0x43255faa), SkBits2Float(0x4299b7ac), SkBits2Float(0x431e0d26)); // 84.1811f, 165.374f, 76.8587f, 158.051f +path.quadTo(SkBits2Float(0x428b12a3), SkBits2Float(0x4316baa2), SkBits2Float(0x428b12a3), SkBits2Float(0x430c5faa)); // 69.5364f, 150.729f, 69.5364f, 140.374f +path.quadTo(SkBits2Float(0x428b12a3), SkBits2Float(0x430204b2), SkBits2Float(0x4299b7ac), SkBits2Float(0x42f5645c)); // 69.5364f, 130.018f, 76.8587f, 122.696f +path.quadTo(SkBits2Float(0x42a85cb4), SkBits2Float(0x42e6bf54), SkBits2Float(0x42bd12a3), SkBits2Float(0x42e6bf54)); // 84.1811f, 115.374f, 94.5364f, 115.374f +path.quadTo(SkBits2Float(0x42d1c892), SkBits2Float(0x42e6bf54), SkBits2Float(0x42e06d9a), SkBits2Float(0x42f5645c)); // 104.892f, 115.374f, 112.214f, 122.696f +path.quadTo(SkBits2Float(0x42ef12a3), SkBits2Float(0x430204b2), SkBits2Float(0x42ef12a3), SkBits2Float(0x430c5faa)); // 119.536f, 130.018f, 119.536f, 140.374f +path.close(); + SkPath path54(path); + builder.add(path54, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43f569c1), SkBits2Float(0x43463314)); // 490.826f, 198.2f +path.quadTo(SkBits2Float(0x43f569c1), SkBits2Float(0x43508e0c), SkBits2Float(0x43f1c07f), SkBits2Float(0x4357e090)); // 490.826f, 208.555f, 483.504f, 215.877f +path.quadTo(SkBits2Float(0x43ee173d), SkBits2Float(0x435f3314), SkBits2Float(0x43e8e9c1), SkBits2Float(0x435f3314)); // 476.182f, 223.2f, 465.826f, 223.2f +path.quadTo(SkBits2Float(0x43e3bc45), SkBits2Float(0x435f3314), SkBits2Float(0x43e01303), SkBits2Float(0x4357e090)); // 455.471f, 223.2f, 448.149f, 215.877f +path.quadTo(SkBits2Float(0x43dc69c1), SkBits2Float(0x43508e0c), SkBits2Float(0x43dc69c1), SkBits2Float(0x43463314)); // 440.826f, 208.555f, 440.826f, 198.2f +path.quadTo(SkBits2Float(0x43dc69c1), SkBits2Float(0x433bd81c), SkBits2Float(0x43e01303), SkBits2Float(0x43348598)); // 440.826f, 187.844f, 448.149f, 180.522f +path.quadTo(SkBits2Float(0x43e3bc45), SkBits2Float(0x432d3314), SkBits2Float(0x43e8e9c1), SkBits2Float(0x432d3314)); // 455.471f, 173.2f, 465.826f, 173.2f +path.quadTo(SkBits2Float(0x43ee173d), SkBits2Float(0x432d3314), SkBits2Float(0x43f1c07f), SkBits2Float(0x43348598)); // 476.182f, 173.2f, 483.504f, 180.522f +path.quadTo(SkBits2Float(0x43f569c1), SkBits2Float(0x433bd81c), SkBits2Float(0x43f569c1), SkBits2Float(0x43463314)); // 490.826f, 187.844f, 490.826f, 198.2f +path.close(); + SkPath path55(path); + builder.add(path55, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4346ee50), SkBits2Float(0x4386bdd6)); // 198.931f, 269.483f +path.quadTo(SkBits2Float(0x4346ee50), SkBits2Float(0x438beb52), SkBits2Float(0x433f9bcc), SkBits2Float(0x438f9494)); // 198.931f, 279.838f, 191.609f, 287.161f +path.quadTo(SkBits2Float(0x43384948), SkBits2Float(0x43933dd6), SkBits2Float(0x432dee50), SkBits2Float(0x43933dd6)); // 184.286f, 294.483f, 173.931f, 294.483f +path.quadTo(SkBits2Float(0x43239358), SkBits2Float(0x43933dd6), SkBits2Float(0x431c40d4), SkBits2Float(0x438f9494)); // 163.576f, 294.483f, 156.253f, 287.161f +path.quadTo(SkBits2Float(0x4314ee50), SkBits2Float(0x438beb52), SkBits2Float(0x4314ee50), SkBits2Float(0x4386bdd6)); // 148.931f, 279.838f, 148.931f, 269.483f +path.quadTo(SkBits2Float(0x4314ee50), SkBits2Float(0x4381905a), SkBits2Float(0x431c40d4), SkBits2Float(0x437bce30)); // 148.931f, 259.128f, 156.253f, 251.805f +path.quadTo(SkBits2Float(0x43239358), SkBits2Float(0x43747bac), SkBits2Float(0x432dee50), SkBits2Float(0x43747bac)); // 163.576f, 244.483f, 173.931f, 244.483f +path.quadTo(SkBits2Float(0x43384948), SkBits2Float(0x43747bac), SkBits2Float(0x433f9bcc), SkBits2Float(0x437bce30)); // 184.286f, 244.483f, 191.609f, 251.805f +path.quadTo(SkBits2Float(0x4346ee50), SkBits2Float(0x4381905a), SkBits2Float(0x4346ee50), SkBits2Float(0x4386bdd6)); // 198.931f, 259.128f, 198.931f, 269.483f +path.close(); + SkPath path56(path); + builder.add(path56, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4403bd60), SkBits2Float(0x438666a7)); // 526.959f, 268.802f +path.quadTo(SkBits2Float(0x4403bd60), SkBits2Float(0x438b9423), SkBits2Float(0x4401e8bf), SkBits2Float(0x438f3d65)); // 526.959f, 279.157f, 519.637f, 286.48f +path.quadTo(SkBits2Float(0x4400141e), SkBits2Float(0x4392e6a7), SkBits2Float(0x43fafac0), SkBits2Float(0x4392e6a7)); // 512.314f, 293.802f, 501.959f, 293.802f +path.quadTo(SkBits2Float(0x43f5cd44), SkBits2Float(0x4392e6a7), SkBits2Float(0x43f22402), SkBits2Float(0x438f3d65)); // 491.604f, 293.802f, 484.281f, 286.48f +path.quadTo(SkBits2Float(0x43ee7ac1), SkBits2Float(0x438b9423), SkBits2Float(0x43ee7ac1), SkBits2Float(0x438666a7)); // 476.959f, 279.157f, 476.959f, 268.802f +path.quadTo(SkBits2Float(0x43ee7ac1), SkBits2Float(0x4381392b), SkBits2Float(0x43f22402), SkBits2Float(0x437b1fd2)); // 476.959f, 258.447f, 484.281f, 251.124f +path.quadTo(SkBits2Float(0x43f5cd44), SkBits2Float(0x4373cd4e), SkBits2Float(0x43fafac0), SkBits2Float(0x4373cd4e)); // 491.604f, 243.802f, 501.959f, 243.802f +path.quadTo(SkBits2Float(0x4400141e), SkBits2Float(0x4373cd4e), SkBits2Float(0x4401e8bf), SkBits2Float(0x437b1fd2)); // 512.314f, 243.802f, 519.637f, 251.124f +path.quadTo(SkBits2Float(0x4403bd60), SkBits2Float(0x4381392b), SkBits2Float(0x4403bd60), SkBits2Float(0x438666a7)); // 526.959f, 258.447f, 526.959f, 268.802f +path.close(); + SkPath path57(path); + builder.add(path57, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x433c6aff), SkBits2Float(0x439bf9f9)); // 188.418f, 311.953f +path.quadTo(SkBits2Float(0x433c6aff), SkBits2Float(0x43a12775), SkBits2Float(0x4335187b), SkBits2Float(0x43a4d0b7)); // 188.418f, 322.308f, 181.096f, 329.631f +path.quadTo(SkBits2Float(0x432dc5f6), SkBits2Float(0x43a879f9), SkBits2Float(0x43236aff), SkBits2Float(0x43a879f9)); // 173.773f, 336.953f, 163.418f, 336.953f +path.quadTo(SkBits2Float(0x43191008), SkBits2Float(0x43a879f9), SkBits2Float(0x4311bd83), SkBits2Float(0x43a4d0b7)); // 153.063f, 336.953f, 145.74f, 329.631f +path.quadTo(SkBits2Float(0x430a6aff), SkBits2Float(0x43a12775), SkBits2Float(0x430a6aff), SkBits2Float(0x439bf9f9)); // 138.418f, 322.308f, 138.418f, 311.953f +path.quadTo(SkBits2Float(0x430a6aff), SkBits2Float(0x4396cc7d), SkBits2Float(0x4311bd83), SkBits2Float(0x4393233b)); // 138.418f, 301.598f, 145.74f, 294.275f +path.quadTo(SkBits2Float(0x43191008), SkBits2Float(0x438f79f9), SkBits2Float(0x43236aff), SkBits2Float(0x438f79f9)); // 153.063f, 286.953f, 163.418f, 286.953f +path.quadTo(SkBits2Float(0x432dc5f6), SkBits2Float(0x438f79f9), SkBits2Float(0x4335187b), SkBits2Float(0x4393233b)); // 173.773f, 286.953f, 181.096f, 294.275f +path.quadTo(SkBits2Float(0x433c6aff), SkBits2Float(0x4396cc7d), SkBits2Float(0x433c6aff), SkBits2Float(0x439bf9f9)); // 188.418f, 301.598f, 188.418f, 311.953f +path.close(); + SkPath path58(path); + builder.add(path58, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4397b4f8), SkBits2Float(0x43598b8a)); // 303.414f, 217.545f +path.quadTo(SkBits2Float(0x4397b4f8), SkBits2Float(0x4363e682), SkBits2Float(0x43940bb6), SkBits2Float(0x436b3906)); // 303.414f, 227.9f, 296.091f, 235.223f +path.quadTo(SkBits2Float(0x43906274), SkBits2Float(0x43728b8a), SkBits2Float(0x438b34f8), SkBits2Float(0x43728b8a)); // 288.769f, 242.545f, 278.414f, 242.545f +path.quadTo(SkBits2Float(0x4386077c), SkBits2Float(0x43728b8a), SkBits2Float(0x43825e3a), SkBits2Float(0x436b3906)); // 268.058f, 242.545f, 260.736f, 235.223f +path.quadTo(SkBits2Float(0x437d69f0), SkBits2Float(0x4363e682), SkBits2Float(0x437d69f0), SkBits2Float(0x43598b8a)); // 253.414f, 227.9f, 253.414f, 217.545f +path.quadTo(SkBits2Float(0x437d69f0), SkBits2Float(0x434f3092), SkBits2Float(0x43825e3a), SkBits2Float(0x4347de0e)); // 253.414f, 207.19f, 260.736f, 199.867f +path.quadTo(SkBits2Float(0x4386077c), SkBits2Float(0x43408b8a), SkBits2Float(0x438b34f8), SkBits2Float(0x43408b8a)); // 268.058f, 192.545f, 278.414f, 192.545f +path.quadTo(SkBits2Float(0x43906274), SkBits2Float(0x43408b8a), SkBits2Float(0x43940bb6), SkBits2Float(0x4347de0e)); // 288.769f, 192.545f, 296.091f, 199.867f +path.quadTo(SkBits2Float(0x4397b4f8), SkBits2Float(0x434f3092), SkBits2Float(0x4397b4f8), SkBits2Float(0x43598b8a)); // 303.414f, 207.19f, 303.414f, 217.545f +path.close(); + SkPath path59(path); + builder.add(path59, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x430d7c0c), SkBits2Float(0x435ebbfb)); // 141.485f, 222.734f +path.quadTo(SkBits2Float(0x430d7c0c), SkBits2Float(0x436916f2), SkBits2Float(0x43062988), SkBits2Float(0x43706977)); // 141.485f, 233.09f, 134.162f, 240.412f +path.quadTo(SkBits2Float(0x42fdae07), SkBits2Float(0x4377bbfb), SkBits2Float(0x42e8f818), SkBits2Float(0x4377bbfb)); // 126.84f, 247.734f, 116.485f, 247.734f +path.quadTo(SkBits2Float(0x42d44229), SkBits2Float(0x4377bbfb), SkBits2Float(0x42c59d20), SkBits2Float(0x43706977)); // 106.129f, 247.734f, 98.8069f, 240.412f +path.quadTo(SkBits2Float(0x42b6f818), SkBits2Float(0x436916f2), SkBits2Float(0x42b6f818), SkBits2Float(0x435ebbfb)); // 91.4846f, 233.09f, 91.4846f, 222.734f +path.quadTo(SkBits2Float(0x42b6f818), SkBits2Float(0x43546104), SkBits2Float(0x42c59d20), SkBits2Float(0x434d0e7f)); // 91.4846f, 212.379f, 98.8069f, 205.057f +path.quadTo(SkBits2Float(0x42d44229), SkBits2Float(0x4345bbfb), SkBits2Float(0x42e8f818), SkBits2Float(0x4345bbfb)); // 106.129f, 197.734f, 116.485f, 197.734f +path.quadTo(SkBits2Float(0x42fdae07), SkBits2Float(0x4345bbfb), SkBits2Float(0x43062988), SkBits2Float(0x434d0e7f)); // 126.84f, 197.734f, 134.162f, 205.057f +path.quadTo(SkBits2Float(0x430d7c0c), SkBits2Float(0x43546104), SkBits2Float(0x430d7c0c), SkBits2Float(0x435ebbfb)); // 141.485f, 212.379f, 141.485f, 222.734f +path.close(); + SkPath path60(path); + builder.add(path60, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43b7303b), SkBits2Float(0x42e664c0)); // 366.377f, 115.197f +path.quadTo(SkBits2Float(0x43b7303b), SkBits2Float(0x42fb1aaf), SkBits2Float(0x43b386f9), SkBits2Float(0x4304dfdc)); // 366.377f, 125.552f, 359.054f, 132.874f +path.quadTo(SkBits2Float(0x43afddb7), SkBits2Float(0x430c3260), SkBits2Float(0x43aab03b), SkBits2Float(0x430c3260)); // 351.732f, 140.197f, 341.377f, 140.197f +path.quadTo(SkBits2Float(0x43a582bf), SkBits2Float(0x430c3260), SkBits2Float(0x43a1d97d), SkBits2Float(0x4304dfdc)); // 331.021f, 140.197f, 323.699f, 132.874f +path.quadTo(SkBits2Float(0x439e303b), SkBits2Float(0x42fb1aaf), SkBits2Float(0x439e303b), SkBits2Float(0x42e664c0)); // 316.377f, 125.552f, 316.377f, 115.197f +path.quadTo(SkBits2Float(0x439e303b), SkBits2Float(0x42d1aed1), SkBits2Float(0x43a1d97d), SkBits2Float(0x42c309c8)); // 316.377f, 104.841f, 323.699f, 97.5191f +path.quadTo(SkBits2Float(0x43a582bf), SkBits2Float(0x42b464bf), SkBits2Float(0x43aab03b), SkBits2Float(0x42b464bf)); // 331.021f, 90.1968f, 341.377f, 90.1968f +path.quadTo(SkBits2Float(0x43afddb7), SkBits2Float(0x42b464bf), SkBits2Float(0x43b386f9), SkBits2Float(0x42c309c8)); // 351.732f, 90.1968f, 359.054f, 97.5191f +path.quadTo(SkBits2Float(0x43b7303b), SkBits2Float(0x42d1aed1), SkBits2Float(0x43b7303b), SkBits2Float(0x42e664c0)); // 366.377f, 104.841f, 366.377f, 115.197f +path.close(); + SkPath path61(path); + builder.add(path61, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43ded748), SkBits2Float(0x43786398)); // 445.682f, 248.389f +path.quadTo(SkBits2Float(0x43ded748), SkBits2Float(0x43815f48), SkBits2Float(0x43db2e06), SkBits2Float(0x4385088a)); // 445.682f, 258.744f, 438.36f, 266.067f +path.quadTo(SkBits2Float(0x43d784c4), SkBits2Float(0x4388b1cc), SkBits2Float(0x43d25748), SkBits2Float(0x4388b1cc)); // 431.037f, 273.389f, 420.682f, 273.389f +path.quadTo(SkBits2Float(0x43cd29cc), SkBits2Float(0x4388b1cc), SkBits2Float(0x43c9808a), SkBits2Float(0x4385088a)); // 410.327f, 273.389f, 403.004f, 266.067f +path.quadTo(SkBits2Float(0x43c5d748), SkBits2Float(0x43815f48), SkBits2Float(0x43c5d748), SkBits2Float(0x43786398)); // 395.682f, 258.744f, 395.682f, 248.389f +path.quadTo(SkBits2Float(0x43c5d748), SkBits2Float(0x436e08a1), SkBits2Float(0x43c9808a), SkBits2Float(0x4366b61d)); // 395.682f, 238.034f, 403.004f, 230.711f +path.quadTo(SkBits2Float(0x43cd29cc), SkBits2Float(0x435f6399), SkBits2Float(0x43d25748), SkBits2Float(0x435f6399)); // 410.327f, 223.389f, 420.682f, 223.389f +path.quadTo(SkBits2Float(0x43d784c4), SkBits2Float(0x435f6399), SkBits2Float(0x43db2e06), SkBits2Float(0x4366b61d)); // 431.037f, 223.389f, 438.36f, 230.711f +path.quadTo(SkBits2Float(0x43ded748), SkBits2Float(0x436e08a1), SkBits2Float(0x43ded748), SkBits2Float(0x43786398)); // 445.682f, 238.034f, 445.682f, 248.389f +path.close(); + SkPath path62(path); + builder.add(path62, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43bbf04d), SkBits2Float(0x4397997d)); // 375.877f, 303.199f +path.quadTo(SkBits2Float(0x43bbf04d), SkBits2Float(0x439cc6f9), SkBits2Float(0x43b8470b), SkBits2Float(0x43a0703b)); // 375.877f, 313.554f, 368.555f, 320.877f +path.quadTo(SkBits2Float(0x43b49dc9), SkBits2Float(0x43a4197d), SkBits2Float(0x43af704d), SkBits2Float(0x43a4197d)); // 361.233f, 328.199f, 350.877f, 328.199f +path.quadTo(SkBits2Float(0x43aa42d1), SkBits2Float(0x43a4197d), SkBits2Float(0x43a6998f), SkBits2Float(0x43a0703b)); // 340.522f, 328.199f, 333.2f, 320.877f +path.quadTo(SkBits2Float(0x43a2f04d), SkBits2Float(0x439cc6f9), SkBits2Float(0x43a2f04d), SkBits2Float(0x4397997d)); // 325.877f, 313.554f, 325.877f, 303.199f +path.quadTo(SkBits2Float(0x43a2f04d), SkBits2Float(0x43926c01), SkBits2Float(0x43a6998f), SkBits2Float(0x438ec2bf)); // 325.877f, 292.844f, 333.2f, 285.521f +path.quadTo(SkBits2Float(0x43aa42d1), SkBits2Float(0x438b197d), SkBits2Float(0x43af704d), SkBits2Float(0x438b197d)); // 340.522f, 278.199f, 350.877f, 278.199f +path.quadTo(SkBits2Float(0x43b49dc9), SkBits2Float(0x438b197d), SkBits2Float(0x43b8470b), SkBits2Float(0x438ec2bf)); // 361.233f, 278.199f, 368.555f, 285.521f +path.quadTo(SkBits2Float(0x43bbf04d), SkBits2Float(0x43926c01), SkBits2Float(0x43bbf04d), SkBits2Float(0x4397997d)); // 375.877f, 292.844f, 375.877f, 303.199f +path.close(); + SkPath path63(path); + builder.add(path63, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43b62e97), SkBits2Float(0x4313ec95)); // 364.364f, 147.924f +path.quadTo(SkBits2Float(0x43b62e97), SkBits2Float(0x431e478c), SkBits2Float(0x43b28555), SkBits2Float(0x43259a11)); // 364.364f, 158.279f, 357.042f, 165.602f +path.quadTo(SkBits2Float(0x43aedc13), SkBits2Float(0x432cec95), SkBits2Float(0x43a9ae97), SkBits2Float(0x432cec95)); // 349.719f, 172.924f, 339.364f, 172.924f +path.quadTo(SkBits2Float(0x43a4811b), SkBits2Float(0x432cec95), SkBits2Float(0x43a0d7d9), SkBits2Float(0x43259a11)); // 329.009f, 172.924f, 321.686f, 165.602f +path.quadTo(SkBits2Float(0x439d2e97), SkBits2Float(0x431e478c), SkBits2Float(0x439d2e97), SkBits2Float(0x4313ec95)); // 314.364f, 158.279f, 314.364f, 147.924f +path.quadTo(SkBits2Float(0x439d2e97), SkBits2Float(0x4309919e), SkBits2Float(0x43a0d7d9), SkBits2Float(0x43023f19)); // 314.364f, 137.569f, 321.686f, 130.246f +path.quadTo(SkBits2Float(0x43a4811b), SkBits2Float(0x42f5d92a), SkBits2Float(0x43a9ae97), SkBits2Float(0x42f5d92a)); // 329.009f, 122.924f, 339.364f, 122.924f +path.quadTo(SkBits2Float(0x43aedc13), SkBits2Float(0x42f5d92a), SkBits2Float(0x43b28555), SkBits2Float(0x43023f19)); // 349.719f, 122.924f, 357.042f, 130.246f +path.quadTo(SkBits2Float(0x43b62e97), SkBits2Float(0x4309919e), SkBits2Float(0x43b62e97), SkBits2Float(0x4313ec95)); // 364.364f, 137.569f, 364.364f, 147.924f +path.close(); + SkPath path64(path); + builder.add(path64, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43ec86f7), SkBits2Float(0x43b37791)); // 473.054f, 358.934f +path.quadTo(SkBits2Float(0x43ec86f7), SkBits2Float(0x43b8a50d), SkBits2Float(0x43e8ddb5), SkBits2Float(0x43bc4e4f)); // 473.054f, 369.289f, 465.732f, 376.612f +path.quadTo(SkBits2Float(0x43e53473), SkBits2Float(0x43bff791), SkBits2Float(0x43e006f7), SkBits2Float(0x43bff791)); // 458.41f, 383.934f, 448.054f, 383.934f +path.quadTo(SkBits2Float(0x43dad97b), SkBits2Float(0x43bff791), SkBits2Float(0x43d73039), SkBits2Float(0x43bc4e4f)); // 437.699f, 383.934f, 430.377f, 376.612f +path.quadTo(SkBits2Float(0x43d386f7), SkBits2Float(0x43b8a50d), SkBits2Float(0x43d386f7), SkBits2Float(0x43b37791)); // 423.054f, 369.289f, 423.054f, 358.934f +path.quadTo(SkBits2Float(0x43d386f7), SkBits2Float(0x43ae4a15), SkBits2Float(0x43d73039), SkBits2Float(0x43aaa0d3)); // 423.054f, 348.579f, 430.377f, 341.256f +path.quadTo(SkBits2Float(0x43dad97b), SkBits2Float(0x43a6f791), SkBits2Float(0x43e006f7), SkBits2Float(0x43a6f791)); // 437.699f, 333.934f, 448.054f, 333.934f +path.quadTo(SkBits2Float(0x43e53473), SkBits2Float(0x43a6f791), SkBits2Float(0x43e8ddb5), SkBits2Float(0x43aaa0d3)); // 458.41f, 333.934f, 465.732f, 341.256f +path.quadTo(SkBits2Float(0x43ec86f7), SkBits2Float(0x43ae4a15), SkBits2Float(0x43ec86f7), SkBits2Float(0x43b37791)); // 473.054f, 348.579f, 473.054f, 358.934f +path.close(); + SkPath path65(path); + builder.add(path65, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43840826), SkBits2Float(0x43927b24)); // 264.064f, 292.962f +path.quadTo(SkBits2Float(0x43840826), SkBits2Float(0x4397a8a0), SkBits2Float(0x43805ee4), SkBits2Float(0x439b51e2)); // 264.064f, 303.317f, 256.741f, 310.64f +path.quadTo(SkBits2Float(0x43796b44), SkBits2Float(0x439efb24), SkBits2Float(0x436f104c), SkBits2Float(0x439efb24)); // 249.419f, 317.962f, 239.064f, 317.962f +path.quadTo(SkBits2Float(0x4364b554), SkBits2Float(0x439efb24), SkBits2Float(0x435d62d0), SkBits2Float(0x439b51e2)); // 228.708f, 317.962f, 221.386f, 310.64f +path.quadTo(SkBits2Float(0x4356104c), SkBits2Float(0x4397a8a0), SkBits2Float(0x4356104c), SkBits2Float(0x43927b24)); // 214.064f, 303.317f, 214.064f, 292.962f +path.quadTo(SkBits2Float(0x4356104c), SkBits2Float(0x438d4da8), SkBits2Float(0x435d62d0), SkBits2Float(0x4389a466)); // 214.064f, 282.607f, 221.386f, 275.284f +path.quadTo(SkBits2Float(0x4364b554), SkBits2Float(0x4385fb24), SkBits2Float(0x436f104c), SkBits2Float(0x4385fb24)); // 228.708f, 267.962f, 239.064f, 267.962f +path.quadTo(SkBits2Float(0x43796b44), SkBits2Float(0x4385fb24), SkBits2Float(0x43805ee4), SkBits2Float(0x4389a466)); // 249.419f, 267.962f, 256.741f, 275.284f +path.quadTo(SkBits2Float(0x43840826), SkBits2Float(0x438d4da8), SkBits2Float(0x43840826), SkBits2Float(0x43927b24)); // 264.064f, 282.607f, 264.064f, 292.962f +path.close(); + SkPath path66(path); + builder.add(path66, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4387c794), SkBits2Float(0x43567a78)); // 271.559f, 214.478f +path.quadTo(SkBits2Float(0x4387c794), SkBits2Float(0x4360d570), SkBits2Float(0x43841e52), SkBits2Float(0x436827f4)); // 271.559f, 224.834f, 264.237f, 232.156f +path.quadTo(SkBits2Float(0x43807510), SkBits2Float(0x436f7a78), SkBits2Float(0x43768f28), SkBits2Float(0x436f7a78)); // 256.915f, 239.478f, 246.559f, 239.478f +path.quadTo(SkBits2Float(0x436c3430), SkBits2Float(0x436f7a78), SkBits2Float(0x4364e1ac), SkBits2Float(0x436827f4)); // 236.204f, 239.478f, 228.882f, 232.156f +path.quadTo(SkBits2Float(0x435d8f27), SkBits2Float(0x4360d570), SkBits2Float(0x435d8f27), SkBits2Float(0x43567a78)); // 221.559f, 224.834f, 221.559f, 214.478f +path.quadTo(SkBits2Float(0x435d8f27), SkBits2Float(0x434c1f80), SkBits2Float(0x4364e1ac), SkBits2Float(0x4344ccfc)); // 221.559f, 204.123f, 228.882f, 196.801f +path.quadTo(SkBits2Float(0x436c3430), SkBits2Float(0x433d7a78), SkBits2Float(0x43768f28), SkBits2Float(0x433d7a78)); // 236.204f, 189.478f, 246.559f, 189.478f +path.quadTo(SkBits2Float(0x43807510), SkBits2Float(0x433d7a78), SkBits2Float(0x43841e52), SkBits2Float(0x4344ccfc)); // 256.915f, 189.478f, 264.237f, 196.801f +path.quadTo(SkBits2Float(0x4387c794), SkBits2Float(0x434c1f80), SkBits2Float(0x4387c794), SkBits2Float(0x43567a78)); // 271.559f, 204.123f, 271.559f, 214.478f +path.close(); + SkPath path67(path); + builder.add(path67, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43cfc71f), SkBits2Float(0x4314ea3c)); // 415.556f, 148.915f +path.quadTo(SkBits2Float(0x43cfc71f), SkBits2Float(0x431f4534), SkBits2Float(0x43cc1ddd), SkBits2Float(0x432697b8)); // 415.556f, 159.27f, 408.233f, 166.593f +path.quadTo(SkBits2Float(0x43c8749b), SkBits2Float(0x432dea3c), SkBits2Float(0x43c3471f), SkBits2Float(0x432dea3c)); // 400.911f, 173.915f, 390.556f, 173.915f +path.quadTo(SkBits2Float(0x43be19a3), SkBits2Float(0x432dea3c), SkBits2Float(0x43ba7061), SkBits2Float(0x432697b8)); // 380.2f, 173.915f, 372.878f, 166.593f +path.quadTo(SkBits2Float(0x43b6c71f), SkBits2Float(0x431f4534), SkBits2Float(0x43b6c71f), SkBits2Float(0x4314ea3c)); // 365.556f, 159.27f, 365.556f, 148.915f +path.quadTo(SkBits2Float(0x43b6c71f), SkBits2Float(0x430a8f44), SkBits2Float(0x43ba7061), SkBits2Float(0x43033cc0)); // 365.556f, 138.56f, 372.878f, 131.237f +path.quadTo(SkBits2Float(0x43be19a3), SkBits2Float(0x42f7d478), SkBits2Float(0x43c3471f), SkBits2Float(0x42f7d478)); // 380.2f, 123.915f, 390.556f, 123.915f +path.quadTo(SkBits2Float(0x43c8749b), SkBits2Float(0x42f7d478), SkBits2Float(0x43cc1ddd), SkBits2Float(0x43033cc0)); // 400.911f, 123.915f, 408.233f, 131.237f +path.quadTo(SkBits2Float(0x43cfc71f), SkBits2Float(0x430a8f44), SkBits2Float(0x43cfc71f), SkBits2Float(0x4314ea3c)); // 415.556f, 138.56f, 415.556f, 148.915f +path.close(); + SkPath path68(path); + builder.add(path68, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43a48336), SkBits2Float(0x4336f503)); // 329.025f, 182.957f +path.quadTo(SkBits2Float(0x43a48336), SkBits2Float(0x43414ffa), SkBits2Float(0x43a0d9f4), SkBits2Float(0x4348a27f)); // 329.025f, 193.312f, 321.703f, 200.635f +path.quadTo(SkBits2Float(0x439d30b2), SkBits2Float(0x434ff503), SkBits2Float(0x43980336), SkBits2Float(0x434ff503)); // 314.38f, 207.957f, 304.025f, 207.957f +path.quadTo(SkBits2Float(0x4392d5ba), SkBits2Float(0x434ff503), SkBits2Float(0x438f2c78), SkBits2Float(0x4348a27f)); // 293.67f, 207.957f, 286.347f, 200.635f +path.quadTo(SkBits2Float(0x438b8336), SkBits2Float(0x43414ffa), SkBits2Float(0x438b8336), SkBits2Float(0x4336f503)); // 279.025f, 193.312f, 279.025f, 182.957f +path.quadTo(SkBits2Float(0x438b8336), SkBits2Float(0x432c9a0c), SkBits2Float(0x438f2c78), SkBits2Float(0x43254787)); // 279.025f, 172.602f, 286.347f, 165.279f +path.quadTo(SkBits2Float(0x4392d5ba), SkBits2Float(0x431df503), SkBits2Float(0x43980336), SkBits2Float(0x431df503)); // 293.67f, 157.957f, 304.025f, 157.957f +path.quadTo(SkBits2Float(0x439d30b2), SkBits2Float(0x431df503), SkBits2Float(0x43a0d9f4), SkBits2Float(0x43254787)); // 314.38f, 157.957f, 321.703f, 165.279f +path.quadTo(SkBits2Float(0x43a48336), SkBits2Float(0x432c9a0c), SkBits2Float(0x43a48336), SkBits2Float(0x4336f503)); // 329.025f, 172.602f, 329.025f, 182.957f +path.close(); + SkPath path69(path); + builder.add(path69, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x439a774e), SkBits2Float(0x43a57837)); // 308.932f, 330.939f +path.quadTo(SkBits2Float(0x439a774e), SkBits2Float(0x43aaa5b3), SkBits2Float(0x4396ce0c), SkBits2Float(0x43ae4ef5)); // 308.932f, 341.295f, 301.61f, 348.617f +path.quadTo(SkBits2Float(0x439324ca), SkBits2Float(0x43b1f837), SkBits2Float(0x438df74e), SkBits2Float(0x43b1f837)); // 294.287f, 355.939f, 283.932f, 355.939f +path.quadTo(SkBits2Float(0x4388c9d2), SkBits2Float(0x43b1f837), SkBits2Float(0x43852090), SkBits2Float(0x43ae4ef5)); // 273.577f, 355.939f, 266.254f, 348.617f +path.quadTo(SkBits2Float(0x4381774e), SkBits2Float(0x43aaa5b3), SkBits2Float(0x4381774e), SkBits2Float(0x43a57837)); // 258.932f, 341.295f, 258.932f, 330.939f +path.quadTo(SkBits2Float(0x4381774e), SkBits2Float(0x43a04abb), SkBits2Float(0x43852090), SkBits2Float(0x439ca179)); // 258.932f, 320.584f, 266.254f, 313.262f +path.quadTo(SkBits2Float(0x4388c9d2), SkBits2Float(0x4398f837), SkBits2Float(0x438df74e), SkBits2Float(0x4398f837)); // 273.577f, 305.939f, 283.932f, 305.939f +path.quadTo(SkBits2Float(0x439324ca), SkBits2Float(0x4398f837), SkBits2Float(0x4396ce0c), SkBits2Float(0x439ca179)); // 294.287f, 305.939f, 301.61f, 313.262f +path.quadTo(SkBits2Float(0x439a774e), SkBits2Float(0x43a04abb), SkBits2Float(0x439a774e), SkBits2Float(0x43a57837)); // 308.932f, 320.584f, 308.932f, 330.939f +path.close(); + SkPath path70(path); + builder.add(path70, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x439be624), SkBits2Float(0x438cec6f)); // 311.798f, 281.847f +path.quadTo(SkBits2Float(0x439be624), SkBits2Float(0x439219eb), SkBits2Float(0x43983ce2), SkBits2Float(0x4395c32d)); // 311.798f, 292.202f, 304.476f, 299.525f +path.quadTo(SkBits2Float(0x439493a0), SkBits2Float(0x43996c6f), SkBits2Float(0x438f6624), SkBits2Float(0x43996c6f)); // 297.153f, 306.847f, 286.798f, 306.847f +path.quadTo(SkBits2Float(0x438a38a8), SkBits2Float(0x43996c6f), SkBits2Float(0x43868f66), SkBits2Float(0x4395c32d)); // 276.443f, 306.847f, 269.12f, 299.525f +path.quadTo(SkBits2Float(0x4382e624), SkBits2Float(0x439219eb), SkBits2Float(0x4382e624), SkBits2Float(0x438cec6f)); // 261.798f, 292.202f, 261.798f, 281.847f +path.quadTo(SkBits2Float(0x4382e624), SkBits2Float(0x4387bef3), SkBits2Float(0x43868f66), SkBits2Float(0x438415b1)); // 261.798f, 271.492f, 269.12f, 264.169f +path.quadTo(SkBits2Float(0x438a38a8), SkBits2Float(0x43806c6f), SkBits2Float(0x438f6624), SkBits2Float(0x43806c6f)); // 276.443f, 256.847f, 286.798f, 256.847f +path.quadTo(SkBits2Float(0x439493a0), SkBits2Float(0x43806c6f), SkBits2Float(0x43983ce2), SkBits2Float(0x438415b1)); // 297.153f, 256.847f, 304.476f, 264.169f +path.quadTo(SkBits2Float(0x439be624), SkBits2Float(0x4387bef3), SkBits2Float(0x439be624), SkBits2Float(0x438cec6f)); // 311.798f, 271.492f, 311.798f, 281.847f +path.close(); + SkPath path71(path); + builder.add(path71, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43714851), SkBits2Float(0x43aff2c0)); // 241.282f, 351.896f +path.quadTo(SkBits2Float(0x43714851), SkBits2Float(0x43b5203c), SkBits2Float(0x4369f5cd), SkBits2Float(0x43b8c97e)); // 241.282f, 362.252f, 233.96f, 369.574f +path.quadTo(SkBits2Float(0x4362a348), SkBits2Float(0x43bc72c0), SkBits2Float(0x43584851), SkBits2Float(0x43bc72c0)); // 226.638f, 376.896f, 216.282f, 376.896f +path.quadTo(SkBits2Float(0x434ded5a), SkBits2Float(0x43bc72c0), SkBits2Float(0x43469ad5), SkBits2Float(0x43b8c97e)); // 205.927f, 376.896f, 198.605f, 369.574f +path.quadTo(SkBits2Float(0x433f4851), SkBits2Float(0x43b5203c), SkBits2Float(0x433f4851), SkBits2Float(0x43aff2c0)); // 191.282f, 362.252f, 191.282f, 351.896f +path.quadTo(SkBits2Float(0x433f4851), SkBits2Float(0x43aac544), SkBits2Float(0x43469ad5), SkBits2Float(0x43a71c02)); // 191.282f, 341.541f, 198.605f, 334.219f +path.quadTo(SkBits2Float(0x434ded5a), SkBits2Float(0x43a372c0), SkBits2Float(0x43584851), SkBits2Float(0x43a372c0)); // 205.927f, 326.896f, 216.282f, 326.896f +path.quadTo(SkBits2Float(0x4362a348), SkBits2Float(0x43a372c0), SkBits2Float(0x4369f5cd), SkBits2Float(0x43a71c02)); // 226.638f, 326.896f, 233.96f, 334.219f +path.quadTo(SkBits2Float(0x43714851), SkBits2Float(0x43aac544), SkBits2Float(0x43714851), SkBits2Float(0x43aff2c0)); // 241.282f, 341.541f, 241.282f, 351.896f +path.close(); + SkPath path72(path); + builder.add(path72, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43e644d3), SkBits2Float(0x43041b70)); // 460.538f, 132.107f +path.quadTo(SkBits2Float(0x43e644d3), SkBits2Float(0x430e7668), SkBits2Float(0x43e29b91), SkBits2Float(0x4315c8ec)); // 460.538f, 142.463f, 453.215f, 149.785f +path.quadTo(SkBits2Float(0x43def24f), SkBits2Float(0x431d1b70), SkBits2Float(0x43d9c4d3), SkBits2Float(0x431d1b70)); // 445.893f, 157.107f, 435.538f, 157.107f +path.quadTo(SkBits2Float(0x43d49757), SkBits2Float(0x431d1b70), SkBits2Float(0x43d0ee15), SkBits2Float(0x4315c8ec)); // 425.182f, 157.107f, 417.86f, 149.785f +path.quadTo(SkBits2Float(0x43cd44d3), SkBits2Float(0x430e7668), SkBits2Float(0x43cd44d3), SkBits2Float(0x43041b70)); // 410.538f, 142.463f, 410.538f, 132.107f +path.quadTo(SkBits2Float(0x43cd44d3), SkBits2Float(0x42f380f1), SkBits2Float(0x43d0ee15), SkBits2Float(0x42e4dbe8)); // 410.538f, 121.752f, 417.86f, 114.43f +path.quadTo(SkBits2Float(0x43d49757), SkBits2Float(0x42d636e0), SkBits2Float(0x43d9c4d3), SkBits2Float(0x42d636e0)); // 425.182f, 107.107f, 435.538f, 107.107f +path.quadTo(SkBits2Float(0x43def24f), SkBits2Float(0x42d636e0), SkBits2Float(0x43e29b91), SkBits2Float(0x42e4dbe8)); // 445.893f, 107.107f, 453.215f, 114.43f +path.quadTo(SkBits2Float(0x43e644d3), SkBits2Float(0x42f380f1), SkBits2Float(0x43e644d3), SkBits2Float(0x43041b70)); // 460.538f, 121.752f, 460.538f, 132.107f +path.close(); + SkPath path73(path); + builder.add(path73, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43c29be7), SkBits2Float(0x4366bcd5)); // 389.218f, 230.738f +path.quadTo(SkBits2Float(0x43c29be7), SkBits2Float(0x437117cc), SkBits2Float(0x43bef2a5), SkBits2Float(0x43786a51)); // 389.218f, 241.093f, 381.896f, 248.415f +path.quadTo(SkBits2Float(0x43bb4963), SkBits2Float(0x437fbcd5), SkBits2Float(0x43b61be7), SkBits2Float(0x437fbcd5)); // 374.573f, 255.738f, 364.218f, 255.738f +path.quadTo(SkBits2Float(0x43b0ee6b), SkBits2Float(0x437fbcd5), SkBits2Float(0x43ad4529), SkBits2Float(0x43786a51)); // 353.863f, 255.738f, 346.54f, 248.415f +path.quadTo(SkBits2Float(0x43a99be7), SkBits2Float(0x437117cc), SkBits2Float(0x43a99be7), SkBits2Float(0x4366bcd5)); // 339.218f, 241.093f, 339.218f, 230.738f +path.quadTo(SkBits2Float(0x43a99be7), SkBits2Float(0x435c61de), SkBits2Float(0x43ad4529), SkBits2Float(0x43550f59)); // 339.218f, 220.382f, 346.54f, 213.06f +path.quadTo(SkBits2Float(0x43b0ee6b), SkBits2Float(0x434dbcd5), SkBits2Float(0x43b61be7), SkBits2Float(0x434dbcd5)); // 353.863f, 205.738f, 364.218f, 205.738f +path.quadTo(SkBits2Float(0x43bb4963), SkBits2Float(0x434dbcd5), SkBits2Float(0x43bef2a5), SkBits2Float(0x43550f59)); // 374.573f, 205.738f, 381.896f, 213.06f +path.quadTo(SkBits2Float(0x43c29be7), SkBits2Float(0x435c61de), SkBits2Float(0x43c29be7), SkBits2Float(0x4366bcd5)); // 389.218f, 220.382f, 389.218f, 230.738f +path.close(); + SkPath path74(path); + builder.add(path74, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43b8637d), SkBits2Float(0x435f209d)); // 368.777f, 223.127f +path.quadTo(SkBits2Float(0x43b8637d), SkBits2Float(0x43697b94), SkBits2Float(0x43b4ba3b), SkBits2Float(0x4370ce19)); // 368.777f, 233.483f, 361.455f, 240.805f +path.quadTo(SkBits2Float(0x43b110f9), SkBits2Float(0x4378209d), SkBits2Float(0x43abe37d), SkBits2Float(0x4378209d)); // 354.133f, 248.127f, 343.777f, 248.127f +path.quadTo(SkBits2Float(0x43a6b601), SkBits2Float(0x4378209d), SkBits2Float(0x43a30cbf), SkBits2Float(0x4370ce19)); // 333.422f, 248.127f, 326.1f, 240.805f +path.quadTo(SkBits2Float(0x439f637d), SkBits2Float(0x43697b94), SkBits2Float(0x439f637d), SkBits2Float(0x435f209d)); // 318.777f, 233.483f, 318.777f, 223.127f +path.quadTo(SkBits2Float(0x439f637d), SkBits2Float(0x4354c5a6), SkBits2Float(0x43a30cbf), SkBits2Float(0x434d7321)); // 318.777f, 212.772f, 326.1f, 205.45f +path.quadTo(SkBits2Float(0x43a6b601), SkBits2Float(0x4346209d), SkBits2Float(0x43abe37d), SkBits2Float(0x4346209d)); // 333.422f, 198.127f, 343.777f, 198.127f +path.quadTo(SkBits2Float(0x43b110f9), SkBits2Float(0x4346209d), SkBits2Float(0x43b4ba3b), SkBits2Float(0x434d7321)); // 354.133f, 198.127f, 361.455f, 205.45f +path.quadTo(SkBits2Float(0x43b8637d), SkBits2Float(0x4354c5a6), SkBits2Float(0x43b8637d), SkBits2Float(0x435f209d)); // 368.777f, 212.772f, 368.777f, 223.127f +path.close(); + SkPath path75(path); + builder.add(path75, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43a3ec8f), SkBits2Float(0x435dba35)); // 327.848f, 221.727f +path.quadTo(SkBits2Float(0x43a3ec8f), SkBits2Float(0x4368152c), SkBits2Float(0x43a0434d), SkBits2Float(0x436f67b1)); // 327.848f, 232.083f, 320.526f, 239.405f +path.quadTo(SkBits2Float(0x439c9a0b), SkBits2Float(0x4376ba35), SkBits2Float(0x43976c8f), SkBits2Float(0x4376ba35)); // 313.203f, 246.727f, 302.848f, 246.727f +path.quadTo(SkBits2Float(0x43923f13), SkBits2Float(0x4376ba35), SkBits2Float(0x438e95d1), SkBits2Float(0x436f67b1)); // 292.493f, 246.727f, 285.17f, 239.405f +path.quadTo(SkBits2Float(0x438aec8f), SkBits2Float(0x4368152c), SkBits2Float(0x438aec8f), SkBits2Float(0x435dba35)); // 277.848f, 232.083f, 277.848f, 221.727f +path.quadTo(SkBits2Float(0x438aec8f), SkBits2Float(0x43535f3e), SkBits2Float(0x438e95d1), SkBits2Float(0x434c0cb9)); // 277.848f, 211.372f, 285.17f, 204.05f +path.quadTo(SkBits2Float(0x43923f13), SkBits2Float(0x4344ba35), SkBits2Float(0x43976c8f), SkBits2Float(0x4344ba35)); // 292.493f, 196.727f, 302.848f, 196.727f +path.quadTo(SkBits2Float(0x439c9a0b), SkBits2Float(0x4344ba35), SkBits2Float(0x43a0434d), SkBits2Float(0x434c0cb9)); // 313.203f, 196.727f, 320.526f, 204.05f +path.quadTo(SkBits2Float(0x43a3ec8f), SkBits2Float(0x43535f3e), SkBits2Float(0x43a3ec8f), SkBits2Float(0x435dba35)); // 327.848f, 211.372f, 327.848f, 221.727f +path.close(); + SkPath path76(path); + builder.add(path76, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4408ede5), SkBits2Float(0x436af388)); // 547.717f, 234.951f +path.quadTo(SkBits2Float(0x4408ede5), SkBits2Float(0x43754e7f), SkBits2Float(0x44071944), SkBits2Float(0x437ca103)); // 547.717f, 245.307f, 540.395f, 252.629f +path.quadTo(SkBits2Float(0x440544a3), SkBits2Float(0x4381f9c4), SkBits2Float(0x4402ade5), SkBits2Float(0x4381f9c4)); // 533.072f, 259.951f, 522.717f, 259.951f +path.quadTo(SkBits2Float(0x44001727), SkBits2Float(0x4381f9c4), SkBits2Float(0x43fc850c), SkBits2Float(0x437ca103)); // 512.362f, 259.951f, 505.039f, 252.629f +path.quadTo(SkBits2Float(0x43f8dbca), SkBits2Float(0x43754e7f), SkBits2Float(0x43f8dbca), SkBits2Float(0x436af388)); // 497.717f, 245.307f, 497.717f, 234.951f +path.quadTo(SkBits2Float(0x43f8dbca), SkBits2Float(0x43609891), SkBits2Float(0x43fc850c), SkBits2Float(0x4359460d)); // 497.717f, 224.596f, 505.039f, 217.274f +path.quadTo(SkBits2Float(0x44001727), SkBits2Float(0x4351f389), SkBits2Float(0x4402ade5), SkBits2Float(0x4351f389)); // 512.362f, 209.951f, 522.717f, 209.951f +path.quadTo(SkBits2Float(0x440544a3), SkBits2Float(0x4351f389), SkBits2Float(0x44071944), SkBits2Float(0x4359460d)); // 533.072f, 209.951f, 540.395f, 217.274f +path.quadTo(SkBits2Float(0x4408ede5), SkBits2Float(0x43609891), SkBits2Float(0x4408ede5), SkBits2Float(0x436af388)); // 547.717f, 224.596f, 547.717f, 234.951f +path.close(); + SkPath path77(path); + builder.add(path77, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43a06718), SkBits2Float(0x43848d47)); // 320.805f, 265.104f +path.quadTo(SkBits2Float(0x43a06718), SkBits2Float(0x4389bac3), SkBits2Float(0x439cbdd6), SkBits2Float(0x438d6405)); // 320.805f, 275.459f, 313.483f, 282.781f +path.quadTo(SkBits2Float(0x43991494), SkBits2Float(0x43910d47), SkBits2Float(0x4393e718), SkBits2Float(0x43910d47)); // 306.161f, 290.104f, 295.805f, 290.104f +path.quadTo(SkBits2Float(0x438eb99c), SkBits2Float(0x43910d47), SkBits2Float(0x438b105a), SkBits2Float(0x438d6405)); // 285.45f, 290.104f, 278.128f, 282.781f +path.quadTo(SkBits2Float(0x43876718), SkBits2Float(0x4389bac3), SkBits2Float(0x43876718), SkBits2Float(0x43848d47)); // 270.805f, 275.459f, 270.805f, 265.104f +path.quadTo(SkBits2Float(0x43876718), SkBits2Float(0x437ebf96), SkBits2Float(0x438b105a), SkBits2Float(0x43776d12)); // 270.805f, 254.748f, 278.128f, 247.426f +path.quadTo(SkBits2Float(0x438eb99c), SkBits2Float(0x43701a8e), SkBits2Float(0x4393e718), SkBits2Float(0x43701a8e)); // 285.45f, 240.104f, 295.805f, 240.104f +path.quadTo(SkBits2Float(0x43991494), SkBits2Float(0x43701a8e), SkBits2Float(0x439cbdd6), SkBits2Float(0x43776d12)); // 306.161f, 240.104f, 313.483f, 247.426f +path.quadTo(SkBits2Float(0x43a06718), SkBits2Float(0x437ebf96), SkBits2Float(0x43a06718), SkBits2Float(0x43848d47)); // 320.805f, 254.748f, 320.805f, 265.104f +path.close(); + SkPath path78(path); + builder.add(path78, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43a4f721), SkBits2Float(0x439dea2f)); // 329.931f, 315.83f +path.quadTo(SkBits2Float(0x43a4f721), SkBits2Float(0x43a317ab), SkBits2Float(0x43a14ddf), SkBits2Float(0x43a6c0ed)); // 329.931f, 326.185f, 322.608f, 333.507f +path.quadTo(SkBits2Float(0x439da49d), SkBits2Float(0x43aa6a2f), SkBits2Float(0x43987721), SkBits2Float(0x43aa6a2f)); // 315.286f, 340.83f, 304.931f, 340.83f +path.quadTo(SkBits2Float(0x439349a5), SkBits2Float(0x43aa6a2f), SkBits2Float(0x438fa063), SkBits2Float(0x43a6c0ed)); // 294.575f, 340.83f, 287.253f, 333.507f +path.quadTo(SkBits2Float(0x438bf721), SkBits2Float(0x43a317ab), SkBits2Float(0x438bf721), SkBits2Float(0x439dea2f)); // 279.931f, 326.185f, 279.931f, 315.83f +path.quadTo(SkBits2Float(0x438bf721), SkBits2Float(0x4398bcb3), SkBits2Float(0x438fa063), SkBits2Float(0x43951371)); // 279.931f, 305.474f, 287.253f, 298.152f +path.quadTo(SkBits2Float(0x439349a5), SkBits2Float(0x43916a2f), SkBits2Float(0x43987721), SkBits2Float(0x43916a2f)); // 294.575f, 290.83f, 304.931f, 290.83f +path.quadTo(SkBits2Float(0x439da49d), SkBits2Float(0x43916a2f), SkBits2Float(0x43a14ddf), SkBits2Float(0x43951371)); // 315.286f, 290.83f, 322.608f, 298.152f +path.quadTo(SkBits2Float(0x43a4f721), SkBits2Float(0x4398bcb3), SkBits2Float(0x43a4f721), SkBits2Float(0x439dea2f)); // 329.931f, 305.474f, 329.931f, 315.83f +path.close(); + SkPath path79(path); + builder.add(path79, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4343f4a9), SkBits2Float(0x434a2b20)); // 195.956f, 202.168f +path.quadTo(SkBits2Float(0x4343f4a9), SkBits2Float(0x43548618), SkBits2Float(0x433ca225), SkBits2Float(0x435bd89c)); // 195.956f, 212.524f, 188.633f, 219.846f +path.quadTo(SkBits2Float(0x43354fa0), SkBits2Float(0x43632b20), SkBits2Float(0x432af4a9), SkBits2Float(0x43632b20)); // 181.311f, 227.168f, 170.956f, 227.168f +path.quadTo(SkBits2Float(0x432099b2), SkBits2Float(0x43632b20), SkBits2Float(0x4319472d), SkBits2Float(0x435bd89c)); // 160.6f, 227.168f, 153.278f, 219.846f +path.quadTo(SkBits2Float(0x4311f4a9), SkBits2Float(0x43548618), SkBits2Float(0x4311f4a9), SkBits2Float(0x434a2b20)); // 145.956f, 212.524f, 145.956f, 202.168f +path.quadTo(SkBits2Float(0x4311f4a9), SkBits2Float(0x433fd028), SkBits2Float(0x4319472d), SkBits2Float(0x43387da4)); // 145.956f, 191.813f, 153.278f, 184.491f +path.quadTo(SkBits2Float(0x432099b2), SkBits2Float(0x43312b20), SkBits2Float(0x432af4a9), SkBits2Float(0x43312b20)); // 160.6f, 177.168f, 170.956f, 177.168f +path.quadTo(SkBits2Float(0x43354fa0), SkBits2Float(0x43312b20), SkBits2Float(0x433ca225), SkBits2Float(0x43387da4)); // 181.311f, 177.168f, 188.633f, 184.491f +path.quadTo(SkBits2Float(0x4343f4a9), SkBits2Float(0x433fd028), SkBits2Float(0x4343f4a9), SkBits2Float(0x434a2b20)); // 195.956f, 191.813f, 195.956f, 202.168f +path.close(); + SkPath path80(path); + builder.add(path80, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4281a60e), SkBits2Float(0x42f78d6e)); // 64.8243f, 123.776f +path.quadTo(SkBits2Float(0x4281a60e), SkBits2Float(0x430621ae), SkBits2Float(0x4266020c), SkBits2Float(0x430d7433)); // 64.8243f, 134.132f, 57.502f, 141.454f +path.quadTo(SkBits2Float(0x4248b7fa), SkBits2Float(0x4314c6b7), SkBits2Float(0x421f4c1c), SkBits2Float(0x4314c6b7)); // 50.1797f, 148.776f, 39.8243f, 148.776f +path.quadTo(SkBits2Float(0x41ebc07c), SkBits2Float(0x4314c6b7), SkBits2Float(0x41b12c59), SkBits2Float(0x430d7433)); // 29.469f, 148.776f, 22.1467f, 141.454f +path.quadTo(SkBits2Float(0x416d306c), SkBits2Float(0x430621ae), SkBits2Float(0x416d306c), SkBits2Float(0x42f78d6e)); // 14.8243f, 134.132f, 14.8243f, 123.776f +path.quadTo(SkBits2Float(0x416d306c), SkBits2Float(0x42e2d77f), SkBits2Float(0x41b12c59), SkBits2Float(0x42d43276)); // 14.8243f, 113.421f, 22.1467f, 106.099f +path.quadTo(SkBits2Float(0x41ebc07c), SkBits2Float(0x42c58d6e), SkBits2Float(0x421f4c1c), SkBits2Float(0x42c58d6e)); // 29.469f, 98.7762f, 39.8243f, 98.7762f +path.quadTo(SkBits2Float(0x4248b7fa), SkBits2Float(0x42c58d6e), SkBits2Float(0x4266020c), SkBits2Float(0x42d43276)); // 50.1797f, 98.7762f, 57.502f, 106.099f +path.quadTo(SkBits2Float(0x4281a60e), SkBits2Float(0x42e2d77f), SkBits2Float(0x4281a60e), SkBits2Float(0x42f78d6e)); // 64.8243f, 113.421f, 64.8243f, 123.776f +path.close(); + SkPath path81(path); + builder.add(path81, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43740113), SkBits2Float(0x4363dcf0)); // 244.004f, 227.863f +path.quadTo(SkBits2Float(0x43740113), SkBits2Float(0x436e37e8), SkBits2Float(0x436cae8f), SkBits2Float(0x43758a6c)); // 244.004f, 238.218f, 236.682f, 245.541f +path.quadTo(SkBits2Float(0x43655c0a), SkBits2Float(0x437cdcf0), SkBits2Float(0x435b0113), SkBits2Float(0x437cdcf0)); // 229.36f, 252.863f, 219.004f, 252.863f +path.quadTo(SkBits2Float(0x4350a61c), SkBits2Float(0x437cdcf0), SkBits2Float(0x43495397), SkBits2Float(0x43758a6c)); // 208.649f, 252.863f, 201.327f, 245.541f +path.quadTo(SkBits2Float(0x43420113), SkBits2Float(0x436e37e8), SkBits2Float(0x43420113), SkBits2Float(0x4363dcf0)); // 194.004f, 238.218f, 194.004f, 227.863f +path.quadTo(SkBits2Float(0x43420113), SkBits2Float(0x435981f8), SkBits2Float(0x43495397), SkBits2Float(0x43522f74)); // 194.004f, 217.508f, 201.327f, 210.185f +path.quadTo(SkBits2Float(0x4350a61c), SkBits2Float(0x434adcf0), SkBits2Float(0x435b0113), SkBits2Float(0x434adcf0)); // 208.649f, 202.863f, 219.004f, 202.863f +path.quadTo(SkBits2Float(0x43655c0a), SkBits2Float(0x434adcf0), SkBits2Float(0x436cae8f), SkBits2Float(0x43522f74)); // 229.36f, 202.863f, 236.682f, 210.185f +path.quadTo(SkBits2Float(0x43740113), SkBits2Float(0x435981f8), SkBits2Float(0x43740113), SkBits2Float(0x4363dcf0)); // 244.004f, 217.508f, 244.004f, 227.863f +path.close(); + SkPath path82(path); + builder.add(path82, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43bf0464), SkBits2Float(0x431e17ca)); // 382.034f, 158.093f +path.quadTo(SkBits2Float(0x43bf0464), SkBits2Float(0x432872c2), SkBits2Float(0x43bb5b22), SkBits2Float(0x432fc546)); // 382.034f, 168.448f, 374.712f, 175.771f +path.quadTo(SkBits2Float(0x43b7b1e0), SkBits2Float(0x433717ca), SkBits2Float(0x43b28464), SkBits2Float(0x433717ca)); // 367.39f, 183.093f, 357.034f, 183.093f +path.quadTo(SkBits2Float(0x43ad56e8), SkBits2Float(0x433717ca), SkBits2Float(0x43a9ada6), SkBits2Float(0x432fc546)); // 346.679f, 183.093f, 339.357f, 175.771f +path.quadTo(SkBits2Float(0x43a60464), SkBits2Float(0x432872c2), SkBits2Float(0x43a60464), SkBits2Float(0x431e17ca)); // 332.034f, 168.448f, 332.034f, 158.093f +path.quadTo(SkBits2Float(0x43a60464), SkBits2Float(0x4313bcd2), SkBits2Float(0x43a9ada6), SkBits2Float(0x430c6a4e)); // 332.034f, 147.738f, 339.357f, 140.415f +path.quadTo(SkBits2Float(0x43ad56e8), SkBits2Float(0x430517ca), SkBits2Float(0x43b28464), SkBits2Float(0x430517ca)); // 346.679f, 133.093f, 357.034f, 133.093f +path.quadTo(SkBits2Float(0x43b7b1e0), SkBits2Float(0x430517ca), SkBits2Float(0x43bb5b22), SkBits2Float(0x430c6a4e)); // 367.39f, 133.093f, 374.712f, 140.415f +path.quadTo(SkBits2Float(0x43bf0464), SkBits2Float(0x4313bcd2), SkBits2Float(0x43bf0464), SkBits2Float(0x431e17ca)); // 382.034f, 147.738f, 382.034f, 158.093f +path.close(); + SkPath path83(path); + builder.add(path83, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43a72678), SkBits2Float(0x438273e0)); // 334.301f, 260.905f +path.quadTo(SkBits2Float(0x43a72678), SkBits2Float(0x4387a15c), SkBits2Float(0x43a37d36), SkBits2Float(0x438b4a9e)); // 334.301f, 271.261f, 326.978f, 278.583f +path.quadTo(SkBits2Float(0x439fd3f4), SkBits2Float(0x438ef3e0), SkBits2Float(0x439aa678), SkBits2Float(0x438ef3e0)); // 319.656f, 285.905f, 309.301f, 285.905f +path.quadTo(SkBits2Float(0x439578fc), SkBits2Float(0x438ef3e0), SkBits2Float(0x4391cfba), SkBits2Float(0x438b4a9e)); // 298.945f, 285.905f, 291.623f, 278.583f +path.quadTo(SkBits2Float(0x438e2678), SkBits2Float(0x4387a15c), SkBits2Float(0x438e2678), SkBits2Float(0x438273e0)); // 284.301f, 271.261f, 284.301f, 260.905f +path.quadTo(SkBits2Float(0x438e2678), SkBits2Float(0x437a8cc8), SkBits2Float(0x4391cfba), SkBits2Float(0x43733a44)); // 284.301f, 250.55f, 291.623f, 243.228f +path.quadTo(SkBits2Float(0x439578fc), SkBits2Float(0x436be7c0), SkBits2Float(0x439aa678), SkBits2Float(0x436be7c0)); // 298.945f, 235.905f, 309.301f, 235.905f +path.quadTo(SkBits2Float(0x439fd3f4), SkBits2Float(0x436be7c0), SkBits2Float(0x43a37d36), SkBits2Float(0x43733a44)); // 319.656f, 235.905f, 326.978f, 243.228f +path.quadTo(SkBits2Float(0x43a72678), SkBits2Float(0x437a8cc8), SkBits2Float(0x43a72678), SkBits2Float(0x438273e0)); // 334.301f, 250.55f, 334.301f, 260.905f +path.close(); + SkPath path84(path); + builder.add(path84, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4361bf09), SkBits2Float(0x43464372)); // 225.746f, 198.263f +path.quadTo(SkBits2Float(0x4361bf09), SkBits2Float(0x43509e6a), SkBits2Float(0x435a6c85), SkBits2Float(0x4357f0ee)); // 225.746f, 208.619f, 218.424f, 215.941f +path.quadTo(SkBits2Float(0x43531a00), SkBits2Float(0x435f4372), SkBits2Float(0x4348bf09), SkBits2Float(0x435f4372)); // 211.102f, 223.263f, 200.746f, 223.263f +path.quadTo(SkBits2Float(0x433e6412), SkBits2Float(0x435f4372), SkBits2Float(0x4337118d), SkBits2Float(0x4357f0ee)); // 190.391f, 223.263f, 183.069f, 215.941f +path.quadTo(SkBits2Float(0x432fbf09), SkBits2Float(0x43509e6a), SkBits2Float(0x432fbf09), SkBits2Float(0x43464372)); // 175.746f, 208.619f, 175.746f, 198.263f +path.quadTo(SkBits2Float(0x432fbf09), SkBits2Float(0x433be87a), SkBits2Float(0x4337118d), SkBits2Float(0x433495f6)); // 175.746f, 187.908f, 183.069f, 180.586f +path.quadTo(SkBits2Float(0x433e6412), SkBits2Float(0x432d4372), SkBits2Float(0x4348bf09), SkBits2Float(0x432d4372)); // 190.391f, 173.263f, 200.746f, 173.263f +path.quadTo(SkBits2Float(0x43531a00), SkBits2Float(0x432d4372), SkBits2Float(0x435a6c85), SkBits2Float(0x433495f6)); // 211.102f, 173.263f, 218.424f, 180.586f +path.quadTo(SkBits2Float(0x4361bf09), SkBits2Float(0x433be87a), SkBits2Float(0x4361bf09), SkBits2Float(0x43464372)); // 225.746f, 187.908f, 225.746f, 198.263f +path.close(); + SkPath path85(path); + builder.add(path85, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4377220b), SkBits2Float(0x4392653c)); // 247.133f, 292.791f +path.quadTo(SkBits2Float(0x4377220b), SkBits2Float(0x439792b8), SkBits2Float(0x436fcf87), SkBits2Float(0x439b3bfa)); // 247.133f, 303.146f, 239.811f, 310.469f +path.quadTo(SkBits2Float(0x43687d02), SkBits2Float(0x439ee53c), SkBits2Float(0x435e220b), SkBits2Float(0x439ee53c)); // 232.488f, 317.791f, 222.133f, 317.791f +path.quadTo(SkBits2Float(0x4353c714), SkBits2Float(0x439ee53c), SkBits2Float(0x434c748f), SkBits2Float(0x439b3bfa)); // 211.778f, 317.791f, 204.455f, 310.469f +path.quadTo(SkBits2Float(0x4345220b), SkBits2Float(0x439792b8), SkBits2Float(0x4345220b), SkBits2Float(0x4392653c)); // 197.133f, 303.146f, 197.133f, 292.791f +path.quadTo(SkBits2Float(0x4345220b), SkBits2Float(0x438d37c0), SkBits2Float(0x434c748f), SkBits2Float(0x43898e7e)); // 197.133f, 282.436f, 204.455f, 275.113f +path.quadTo(SkBits2Float(0x4353c714), SkBits2Float(0x4385e53c), SkBits2Float(0x435e220b), SkBits2Float(0x4385e53c)); // 211.778f, 267.791f, 222.133f, 267.791f +path.quadTo(SkBits2Float(0x43687d02), SkBits2Float(0x4385e53c), SkBits2Float(0x436fcf87), SkBits2Float(0x43898e7e)); // 232.488f, 267.791f, 239.811f, 275.113f +path.quadTo(SkBits2Float(0x4377220b), SkBits2Float(0x438d37c0), SkBits2Float(0x4377220b), SkBits2Float(0x4392653c)); // 247.133f, 282.436f, 247.133f, 292.791f +path.close(); + SkPath path86(path); + builder.add(path86, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4402a250), SkBits2Float(0x4331ae72)); // 522.536f, 177.681f +path.quadTo(SkBits2Float(0x4402a250), SkBits2Float(0x433c096a), SkBits2Float(0x4400cdaf), SkBits2Float(0x43435bee)); // 522.536f, 188.037f, 515.214f, 195.359f +path.quadTo(SkBits2Float(0x43fdf21c), SkBits2Float(0x434aae72), SkBits2Float(0x43f8c4a0), SkBits2Float(0x434aae72)); // 507.891f, 202.681f, 497.536f, 202.681f +path.quadTo(SkBits2Float(0x43f39724), SkBits2Float(0x434aae72), SkBits2Float(0x43efede2), SkBits2Float(0x43435bee)); // 487.181f, 202.681f, 479.858f, 195.359f +path.quadTo(SkBits2Float(0x43ec44a0), SkBits2Float(0x433c096a), SkBits2Float(0x43ec44a0), SkBits2Float(0x4331ae72)); // 472.536f, 188.037f, 472.536f, 177.681f +path.quadTo(SkBits2Float(0x43ec44a0), SkBits2Float(0x4327537a), SkBits2Float(0x43efede2), SkBits2Float(0x432000f6)); // 472.536f, 167.326f, 479.858f, 160.004f +path.quadTo(SkBits2Float(0x43f39724), SkBits2Float(0x4318ae72), SkBits2Float(0x43f8c4a0), SkBits2Float(0x4318ae72)); // 487.181f, 152.681f, 497.536f, 152.681f +path.quadTo(SkBits2Float(0x43fdf21c), SkBits2Float(0x4318ae72), SkBits2Float(0x4400cdaf), SkBits2Float(0x432000f6)); // 507.891f, 152.681f, 515.214f, 160.004f +path.quadTo(SkBits2Float(0x4402a250), SkBits2Float(0x4327537a), SkBits2Float(0x4402a250), SkBits2Float(0x4331ae72)); // 522.536f, 167.326f, 522.536f, 177.681f +path.close(); + SkPath path87(path); + builder.add(path87, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x439cfd65), SkBits2Float(0x4331c20a)); // 313.98f, 177.758f +path.quadTo(SkBits2Float(0x439cfd65), SkBits2Float(0x433c1d02), SkBits2Float(0x43995423), SkBits2Float(0x43436f86)); // 313.98f, 188.113f, 306.657f, 195.436f +path.quadTo(SkBits2Float(0x4395aae1), SkBits2Float(0x434ac20a), SkBits2Float(0x43907d65), SkBits2Float(0x434ac20a)); // 299.335f, 202.758f, 288.98f, 202.758f +path.quadTo(SkBits2Float(0x438b4fe9), SkBits2Float(0x434ac20a), SkBits2Float(0x4387a6a7), SkBits2Float(0x43436f86)); // 278.624f, 202.758f, 271.302f, 195.436f +path.quadTo(SkBits2Float(0x4383fd65), SkBits2Float(0x433c1d02), SkBits2Float(0x4383fd65), SkBits2Float(0x4331c20a)); // 263.98f, 188.113f, 263.98f, 177.758f +path.quadTo(SkBits2Float(0x4383fd65), SkBits2Float(0x43276712), SkBits2Float(0x4387a6a7), SkBits2Float(0x4320148e)); // 263.98f, 167.403f, 271.302f, 160.08f +path.quadTo(SkBits2Float(0x438b4fe9), SkBits2Float(0x4318c20a), SkBits2Float(0x43907d65), SkBits2Float(0x4318c20a)); // 278.624f, 152.758f, 288.98f, 152.758f +path.quadTo(SkBits2Float(0x4395aae1), SkBits2Float(0x4318c20a), SkBits2Float(0x43995423), SkBits2Float(0x4320148e)); // 299.335f, 152.758f, 306.657f, 160.08f +path.quadTo(SkBits2Float(0x439cfd65), SkBits2Float(0x43276712), SkBits2Float(0x439cfd65), SkBits2Float(0x4331c20a)); // 313.98f, 167.403f, 313.98f, 177.758f +path.close(); + SkPath path88(path); + builder.add(path88, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43c6cc0f), SkBits2Float(0x430c1343)); // 397.594f, 140.075f +path.quadTo(SkBits2Float(0x43c6cc0f), SkBits2Float(0x43166e3a), SkBits2Float(0x43c322cd), SkBits2Float(0x431dc0bf)); // 397.594f, 150.431f, 390.272f, 157.753f +path.quadTo(SkBits2Float(0x43bf798b), SkBits2Float(0x43251343), SkBits2Float(0x43ba4c0f), SkBits2Float(0x43251343)); // 382.95f, 165.075f, 372.594f, 165.075f +path.quadTo(SkBits2Float(0x43b51e93), SkBits2Float(0x43251343), SkBits2Float(0x43b17551), SkBits2Float(0x431dc0bf)); // 362.239f, 165.075f, 354.917f, 157.753f +path.quadTo(SkBits2Float(0x43adcc0f), SkBits2Float(0x43166e3a), SkBits2Float(0x43adcc0f), SkBits2Float(0x430c1343)); // 347.594f, 150.431f, 347.594f, 140.075f +path.quadTo(SkBits2Float(0x43adcc0f), SkBits2Float(0x4301b84c), SkBits2Float(0x43b17551), SkBits2Float(0x42f4cb8e)); // 347.594f, 129.72f, 354.917f, 122.398f +path.quadTo(SkBits2Float(0x43b51e93), SkBits2Float(0x42e62686), SkBits2Float(0x43ba4c0f), SkBits2Float(0x42e62686)); // 362.239f, 115.075f, 372.594f, 115.075f +path.quadTo(SkBits2Float(0x43bf798b), SkBits2Float(0x42e62686), SkBits2Float(0x43c322cd), SkBits2Float(0x42f4cb8e)); // 382.95f, 115.075f, 390.272f, 122.398f +path.quadTo(SkBits2Float(0x43c6cc0f), SkBits2Float(0x4301b84c), SkBits2Float(0x43c6cc0f), SkBits2Float(0x430c1343)); // 397.594f, 129.72f, 397.594f, 140.075f +path.close(); + SkPath path89(path); + builder.add(path89, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43453a6b), SkBits2Float(0x438ed8e3)); // 197.228f, 285.694f +path.quadTo(SkBits2Float(0x43453a6b), SkBits2Float(0x4394065f), SkBits2Float(0x433de7e7), SkBits2Float(0x4397afa1)); // 197.228f, 296.05f, 189.906f, 303.372f +path.quadTo(SkBits2Float(0x43369562), SkBits2Float(0x439b58e3), SkBits2Float(0x432c3a6b), SkBits2Float(0x439b58e3)); // 182.584f, 310.694f, 172.228f, 310.694f +path.quadTo(SkBits2Float(0x4321df74), SkBits2Float(0x439b58e3), SkBits2Float(0x431a8cef), SkBits2Float(0x4397afa1)); // 161.873f, 310.694f, 154.551f, 303.372f +path.quadTo(SkBits2Float(0x43133a6b), SkBits2Float(0x4394065f), SkBits2Float(0x43133a6b), SkBits2Float(0x438ed8e3)); // 147.228f, 296.05f, 147.228f, 285.694f +path.quadTo(SkBits2Float(0x43133a6b), SkBits2Float(0x4389ab67), SkBits2Float(0x431a8cef), SkBits2Float(0x43860225)); // 147.228f, 275.339f, 154.551f, 268.017f +path.quadTo(SkBits2Float(0x4321df74), SkBits2Float(0x438258e3), SkBits2Float(0x432c3a6b), SkBits2Float(0x438258e3)); // 161.873f, 260.694f, 172.228f, 260.694f +path.quadTo(SkBits2Float(0x43369562), SkBits2Float(0x438258e3), SkBits2Float(0x433de7e7), SkBits2Float(0x43860225)); // 182.584f, 260.694f, 189.906f, 268.017f +path.quadTo(SkBits2Float(0x43453a6b), SkBits2Float(0x4389ab67), SkBits2Float(0x43453a6b), SkBits2Float(0x438ed8e3)); // 197.228f, 275.339f, 197.228f, 285.694f +path.close(); + SkPath path90(path); + builder.add(path90, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43a452ad), SkBits2Float(0x434c38a8)); // 328.646f, 204.221f +path.quadTo(SkBits2Float(0x43a452ad), SkBits2Float(0x435693a0), SkBits2Float(0x43a0a96b), SkBits2Float(0x435de624)); // 328.646f, 214.577f, 321.324f, 221.899f +path.quadTo(SkBits2Float(0x439d0029), SkBits2Float(0x436538a8), SkBits2Float(0x4397d2ad), SkBits2Float(0x436538a8)); // 314.001f, 229.221f, 303.646f, 229.221f +path.quadTo(SkBits2Float(0x4392a531), SkBits2Float(0x436538a8), SkBits2Float(0x438efbef), SkBits2Float(0x435de624)); // 293.291f, 229.221f, 285.968f, 221.899f +path.quadTo(SkBits2Float(0x438b52ad), SkBits2Float(0x435693a0), SkBits2Float(0x438b52ad), SkBits2Float(0x434c38a8)); // 278.646f, 214.577f, 278.646f, 204.221f +path.quadTo(SkBits2Float(0x438b52ad), SkBits2Float(0x4341ddb0), SkBits2Float(0x438efbef), SkBits2Float(0x433a8b2c)); // 278.646f, 193.866f, 285.968f, 186.544f +path.quadTo(SkBits2Float(0x4392a531), SkBits2Float(0x433338a8), SkBits2Float(0x4397d2ad), SkBits2Float(0x433338a8)); // 293.291f, 179.221f, 303.646f, 179.221f +path.quadTo(SkBits2Float(0x439d0029), SkBits2Float(0x433338a8), SkBits2Float(0x43a0a96b), SkBits2Float(0x433a8b2c)); // 314.001f, 179.221f, 321.324f, 186.544f +path.quadTo(SkBits2Float(0x43a452ad), SkBits2Float(0x4341ddb0), SkBits2Float(0x43a452ad), SkBits2Float(0x434c38a8)); // 328.646f, 193.866f, 328.646f, 204.221f +path.close(); + SkPath path91(path); + builder.add(path91, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x44002d54), SkBits2Float(0x4339e9e8)); // 512.708f, 185.914f +path.quadTo(SkBits2Float(0x44002d54), SkBits2Float(0x434444e0), SkBits2Float(0x43fcb166), SkBits2Float(0x434b9764)); // 512.708f, 196.269f, 505.386f, 203.591f +path.quadTo(SkBits2Float(0x43f90824), SkBits2Float(0x4352e9e8), SkBits2Float(0x43f3daa8), SkBits2Float(0x4352e9e8)); // 498.064f, 210.914f, 487.708f, 210.914f +path.quadTo(SkBits2Float(0x43eead2c), SkBits2Float(0x4352e9e8), SkBits2Float(0x43eb03ea), SkBits2Float(0x434b9764)); // 477.353f, 210.914f, 470.031f, 203.591f +path.quadTo(SkBits2Float(0x43e75aa7), SkBits2Float(0x434444e0), SkBits2Float(0x43e75aa7), SkBits2Float(0x4339e9e8)); // 462.708f, 196.269f, 462.708f, 185.914f +path.quadTo(SkBits2Float(0x43e75aa7), SkBits2Float(0x432f8ef0), SkBits2Float(0x43eb03ea), SkBits2Float(0x43283c6c)); // 462.708f, 175.558f, 470.031f, 168.236f +path.quadTo(SkBits2Float(0x43eead2c), SkBits2Float(0x4320e9e8), SkBits2Float(0x43f3daa8), SkBits2Float(0x4320e9e8)); // 477.353f, 160.914f, 487.708f, 160.914f +path.quadTo(SkBits2Float(0x43f90824), SkBits2Float(0x4320e9e8), SkBits2Float(0x43fcb166), SkBits2Float(0x43283c6c)); // 498.064f, 160.914f, 505.386f, 168.236f +path.quadTo(SkBits2Float(0x44002d54), SkBits2Float(0x432f8ef0), SkBits2Float(0x44002d54), SkBits2Float(0x4339e9e8)); // 512.708f, 175.558f, 512.708f, 185.914f +path.close(); + SkPath path92(path); + builder.add(path92, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4385d99d), SkBits2Float(0x4302a8cd)); // 267.7f, 130.659f +path.quadTo(SkBits2Float(0x4385d99d), SkBits2Float(0x430d03c4), SkBits2Float(0x4382305b), SkBits2Float(0x43145649)); // 267.7f, 141.015f, 260.378f, 148.337f +path.quadTo(SkBits2Float(0x437d0e32), SkBits2Float(0x431ba8cd), SkBits2Float(0x4372b33a), SkBits2Float(0x431ba8cd)); // 253.055f, 155.659f, 242.7f, 155.659f +path.quadTo(SkBits2Float(0x43685842), SkBits2Float(0x431ba8cd), SkBits2Float(0x436105be), SkBits2Float(0x43145649)); // 232.345f, 155.659f, 225.022f, 148.337f +path.quadTo(SkBits2Float(0x4359b33a), SkBits2Float(0x430d03c4), SkBits2Float(0x4359b33a), SkBits2Float(0x4302a8cd)); // 217.7f, 141.015f, 217.7f, 130.659f +path.quadTo(SkBits2Float(0x4359b33a), SkBits2Float(0x42f09bab), SkBits2Float(0x436105be), SkBits2Float(0x42e1f6a2)); // 217.7f, 120.304f, 225.022f, 112.982f +path.quadTo(SkBits2Float(0x43685842), SkBits2Float(0x42d3519a), SkBits2Float(0x4372b33a), SkBits2Float(0x42d3519a)); // 232.345f, 105.659f, 242.7f, 105.659f +path.quadTo(SkBits2Float(0x437d0e32), SkBits2Float(0x42d3519a), SkBits2Float(0x4382305b), SkBits2Float(0x42e1f6a2)); // 253.055f, 105.659f, 260.378f, 112.982f +path.quadTo(SkBits2Float(0x4385d99d), SkBits2Float(0x42f09bab), SkBits2Float(0x4385d99d), SkBits2Float(0x4302a8cd)); // 267.7f, 120.304f, 267.7f, 130.659f +path.close(); + SkPath path93(path); + builder.add(path93, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42f92820), SkBits2Float(0x435b1722)); // 124.578f, 219.09f +path.quadTo(SkBits2Float(0x42f92820), SkBits2Float(0x4365721a), SkBits2Float(0x42ea8318), SkBits2Float(0x436cc49e)); // 124.578f, 229.446f, 117.256f, 236.768f +path.quadTo(SkBits2Float(0x42dbde0f), SkBits2Float(0x43741722), SkBits2Float(0x42c72820), SkBits2Float(0x43741722)); // 109.934f, 244.09f, 99.5784f, 244.09f +path.quadTo(SkBits2Float(0x42b27231), SkBits2Float(0x43741722), SkBits2Float(0x42a3cd28), SkBits2Float(0x436cc49e)); // 89.223f, 244.09f, 81.9007f, 236.768f +path.quadTo(SkBits2Float(0x42952820), SkBits2Float(0x4365721a), SkBits2Float(0x42952820), SkBits2Float(0x435b1722)); // 74.5784f, 229.446f, 74.5784f, 219.09f +path.quadTo(SkBits2Float(0x42952820), SkBits2Float(0x4350bc2a), SkBits2Float(0x42a3cd28), SkBits2Float(0x434969a6)); // 74.5784f, 208.735f, 81.9007f, 201.413f +path.quadTo(SkBits2Float(0x42b27231), SkBits2Float(0x43421722), SkBits2Float(0x42c72820), SkBits2Float(0x43421722)); // 89.223f, 194.09f, 99.5784f, 194.09f +path.quadTo(SkBits2Float(0x42dbde0f), SkBits2Float(0x43421722), SkBits2Float(0x42ea8318), SkBits2Float(0x434969a6)); // 109.934f, 194.09f, 117.256f, 201.413f +path.quadTo(SkBits2Float(0x42f92820), SkBits2Float(0x4350bc2a), SkBits2Float(0x42f92820), SkBits2Float(0x435b1722)); // 124.578f, 208.735f, 124.578f, 219.09f +path.close(); + SkPath path94(path); + builder.add(path94, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43b70d86), SkBits2Float(0x435cf621)); // 366.106f, 220.961f +path.quadTo(SkBits2Float(0x43b70d86), SkBits2Float(0x43675118), SkBits2Float(0x43b36444), SkBits2Float(0x436ea39d)); // 366.106f, 231.317f, 358.783f, 238.639f +path.quadTo(SkBits2Float(0x43afbb02), SkBits2Float(0x4375f621), SkBits2Float(0x43aa8d86), SkBits2Float(0x4375f621)); // 351.461f, 245.961f, 341.106f, 245.961f +path.quadTo(SkBits2Float(0x43a5600a), SkBits2Float(0x4375f621), SkBits2Float(0x43a1b6c8), SkBits2Float(0x436ea39d)); // 330.75f, 245.961f, 323.428f, 238.639f +path.quadTo(SkBits2Float(0x439e0d86), SkBits2Float(0x43675118), SkBits2Float(0x439e0d86), SkBits2Float(0x435cf621)); // 316.106f, 231.317f, 316.106f, 220.961f +path.quadTo(SkBits2Float(0x439e0d86), SkBits2Float(0x43529b2a), SkBits2Float(0x43a1b6c8), SkBits2Float(0x434b48a5)); // 316.106f, 210.606f, 323.428f, 203.284f +path.quadTo(SkBits2Float(0x43a5600a), SkBits2Float(0x4343f621), SkBits2Float(0x43aa8d86), SkBits2Float(0x4343f621)); // 330.75f, 195.961f, 341.106f, 195.961f +path.quadTo(SkBits2Float(0x43afbb02), SkBits2Float(0x4343f621), SkBits2Float(0x43b36444), SkBits2Float(0x434b48a5)); // 351.461f, 195.961f, 358.783f, 203.284f +path.quadTo(SkBits2Float(0x43b70d86), SkBits2Float(0x43529b2a), SkBits2Float(0x43b70d86), SkBits2Float(0x435cf621)); // 366.106f, 210.606f, 366.106f, 220.961f +path.close(); + SkPath path95(path); + builder.add(path95, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x436d80e1), SkBits2Float(0x43885efa)); // 237.503f, 272.742f +path.quadTo(SkBits2Float(0x436d80e1), SkBits2Float(0x438d8c76), SkBits2Float(0x43662e5d), SkBits2Float(0x439135b8)); // 237.503f, 283.097f, 230.181f, 290.42f +path.quadTo(SkBits2Float(0x435edbd8), SkBits2Float(0x4394defa), SkBits2Float(0x435480e1), SkBits2Float(0x4394defa)); // 222.859f, 297.742f, 212.503f, 297.742f +path.quadTo(SkBits2Float(0x434a25ea), SkBits2Float(0x4394defa), SkBits2Float(0x4342d365), SkBits2Float(0x439135b8)); // 202.148f, 297.742f, 194.826f, 290.42f +path.quadTo(SkBits2Float(0x433b80e1), SkBits2Float(0x438d8c76), SkBits2Float(0x433b80e1), SkBits2Float(0x43885efa)); // 187.503f, 283.097f, 187.503f, 272.742f +path.quadTo(SkBits2Float(0x433b80e1), SkBits2Float(0x4383317e), SkBits2Float(0x4342d365), SkBits2Float(0x437f1078)); // 187.503f, 262.387f, 194.826f, 255.064f +path.quadTo(SkBits2Float(0x434a25ea), SkBits2Float(0x4377bdf4), SkBits2Float(0x435480e1), SkBits2Float(0x4377bdf4)); // 202.148f, 247.742f, 212.503f, 247.742f +path.quadTo(SkBits2Float(0x435edbd8), SkBits2Float(0x4377bdf4), SkBits2Float(0x43662e5d), SkBits2Float(0x437f1078)); // 222.859f, 247.742f, 230.181f, 255.064f +path.quadTo(SkBits2Float(0x436d80e1), SkBits2Float(0x4383317e), SkBits2Float(0x436d80e1), SkBits2Float(0x43885efa)); // 237.503f, 262.387f, 237.503f, 272.742f +path.close(); + SkPath path96(path); + builder.add(path96, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43e54635), SkBits2Float(0x43794ba0)); // 458.548f, 249.295f +path.quadTo(SkBits2Float(0x43e54635), SkBits2Float(0x4381d34c), SkBits2Float(0x43e19cf3), SkBits2Float(0x43857c8e)); // 458.548f, 259.651f, 451.226f, 266.973f +path.quadTo(SkBits2Float(0x43ddf3b1), SkBits2Float(0x438925d0), SkBits2Float(0x43d8c635), SkBits2Float(0x438925d0)); // 443.904f, 274.295f, 433.548f, 274.295f +path.quadTo(SkBits2Float(0x43d398b9), SkBits2Float(0x438925d0), SkBits2Float(0x43cfef77), SkBits2Float(0x43857c8e)); // 423.193f, 274.295f, 415.871f, 266.973f +path.quadTo(SkBits2Float(0x43cc4635), SkBits2Float(0x4381d34c), SkBits2Float(0x43cc4635), SkBits2Float(0x43794ba0)); // 408.548f, 259.651f, 408.548f, 249.295f +path.quadTo(SkBits2Float(0x43cc4635), SkBits2Float(0x436ef0a8), SkBits2Float(0x43cfef77), SkBits2Float(0x43679e24)); // 408.548f, 238.94f, 415.871f, 231.618f +path.quadTo(SkBits2Float(0x43d398b9), SkBits2Float(0x43604ba0), SkBits2Float(0x43d8c635), SkBits2Float(0x43604ba0)); // 423.193f, 224.295f, 433.548f, 224.295f +path.quadTo(SkBits2Float(0x43ddf3b1), SkBits2Float(0x43604ba0), SkBits2Float(0x43e19cf3), SkBits2Float(0x43679e24)); // 443.904f, 224.295f, 451.226f, 231.618f +path.quadTo(SkBits2Float(0x43e54635), SkBits2Float(0x436ef0a8), SkBits2Float(0x43e54635), SkBits2Float(0x43794ba0)); // 458.548f, 238.94f, 458.548f, 249.295f +path.close(); + SkPath path97(path); + builder.add(path97, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42596ac0), SkBits2Float(0x42f09a24)); // 54.3542f, 120.301f +path.quadTo(SkBits2Float(0x42596ac0), SkBits2Float(0x4302a809), SkBits2Float(0x423c20af), SkBits2Float(0x4309fa8e)); // 54.3542f, 130.656f, 47.0319f, 137.979f +path.quadTo(SkBits2Float(0x421ed69e), SkBits2Float(0x43114d12), SkBits2Float(0x41ead580), SkBits2Float(0x43114d12)); // 39.7096f, 145.301f, 29.3542f, 145.301f +path.quadTo(SkBits2Float(0x4197fdc4), SkBits2Float(0x43114d12), SkBits2Float(0x413ad344), SkBits2Float(0x4309fa8e)); // 18.9989f, 145.301f, 11.6766f, 137.979f +path.quadTo(SkBits2Float(0x408b5604), SkBits2Float(0x4302a809), SkBits2Float(0x408b5604), SkBits2Float(0x42f09a24)); // 4.35425f, 130.656f, 4.35425f, 120.301f +path.quadTo(SkBits2Float(0x408b5604), SkBits2Float(0x42dbe435), SkBits2Float(0x413ad344), SkBits2Float(0x42cd3f2d)); // 4.35425f, 109.946f, 11.6766f, 102.623f +path.quadTo(SkBits2Float(0x4197fdc4), SkBits2Float(0x42be9a25), SkBits2Float(0x41ead580), SkBits2Float(0x42be9a25)); // 18.9989f, 95.3011f, 29.3542f, 95.3011f +path.quadTo(SkBits2Float(0x421ed69e), SkBits2Float(0x42be9a25), SkBits2Float(0x423c20af), SkBits2Float(0x42cd3f2d)); // 39.7096f, 95.3011f, 47.0319f, 102.623f +path.quadTo(SkBits2Float(0x42596ac0), SkBits2Float(0x42dbe435), SkBits2Float(0x42596ac0), SkBits2Float(0x42f09a24)); // 54.3542f, 109.946f, 54.3542f, 120.301f +path.close(); + SkPath path98(path); + builder.add(path98, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43ab48a8), SkBits2Float(0x438703b7)); // 342.568f, 270.029f +path.quadTo(SkBits2Float(0x43ab48a8), SkBits2Float(0x438c3133), SkBits2Float(0x43a79f66), SkBits2Float(0x438fda75)); // 342.568f, 280.384f, 335.245f, 287.707f +path.quadTo(SkBits2Float(0x43a3f624), SkBits2Float(0x439383b7), SkBits2Float(0x439ec8a8), SkBits2Float(0x439383b7)); // 327.923f, 295.029f, 317.568f, 295.029f +path.quadTo(SkBits2Float(0x43999b2c), SkBits2Float(0x439383b7), SkBits2Float(0x4395f1ea), SkBits2Float(0x438fda75)); // 307.212f, 295.029f, 299.89f, 287.707f +path.quadTo(SkBits2Float(0x439248a8), SkBits2Float(0x438c3133), SkBits2Float(0x439248a8), SkBits2Float(0x438703b7)); // 292.568f, 280.384f, 292.568f, 270.029f +path.quadTo(SkBits2Float(0x439248a8), SkBits2Float(0x4381d63b), SkBits2Float(0x4395f1ea), SkBits2Float(0x437c59f2)); // 292.568f, 259.674f, 299.89f, 252.351f +path.quadTo(SkBits2Float(0x43999b2c), SkBits2Float(0x4375076e), SkBits2Float(0x439ec8a8), SkBits2Float(0x4375076e)); // 307.212f, 245.029f, 317.568f, 245.029f +path.quadTo(SkBits2Float(0x43a3f624), SkBits2Float(0x4375076e), SkBits2Float(0x43a79f66), SkBits2Float(0x437c59f2)); // 327.923f, 245.029f, 335.245f, 252.351f +path.quadTo(SkBits2Float(0x43ab48a8), SkBits2Float(0x4381d63b), SkBits2Float(0x43ab48a8), SkBits2Float(0x438703b7)); // 342.568f, 259.674f, 342.568f, 270.029f +path.close(); + SkPath path99(path); + builder.add(path99, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43b3d091), SkBits2Float(0x4394a1fa)); // 359.629f, 297.265f +path.quadTo(SkBits2Float(0x43b3d091), SkBits2Float(0x4399cf76), SkBits2Float(0x43b0274f), SkBits2Float(0x439d78b8)); // 359.629f, 307.621f, 352.307f, 314.943f +path.quadTo(SkBits2Float(0x43ac7e0d), SkBits2Float(0x43a121fa), SkBits2Float(0x43a75091), SkBits2Float(0x43a121fa)); // 344.985f, 322.265f, 334.629f, 322.265f +path.quadTo(SkBits2Float(0x43a22315), SkBits2Float(0x43a121fa), SkBits2Float(0x439e79d3), SkBits2Float(0x439d78b8)); // 324.274f, 322.265f, 316.952f, 314.943f +path.quadTo(SkBits2Float(0x439ad091), SkBits2Float(0x4399cf76), SkBits2Float(0x439ad091), SkBits2Float(0x4394a1fa)); // 309.629f, 307.621f, 309.629f, 297.265f +path.quadTo(SkBits2Float(0x439ad091), SkBits2Float(0x438f747e), SkBits2Float(0x439e79d3), SkBits2Float(0x438bcb3c)); // 309.629f, 286.91f, 316.952f, 279.588f +path.quadTo(SkBits2Float(0x43a22315), SkBits2Float(0x438821fa), SkBits2Float(0x43a75091), SkBits2Float(0x438821fa)); // 324.274f, 272.265f, 334.629f, 272.265f +path.quadTo(SkBits2Float(0x43ac7e0d), SkBits2Float(0x438821fa), SkBits2Float(0x43b0274f), SkBits2Float(0x438bcb3c)); // 344.985f, 272.265f, 352.307f, 279.588f +path.quadTo(SkBits2Float(0x43b3d091), SkBits2Float(0x438f747e), SkBits2Float(0x43b3d091), SkBits2Float(0x4394a1fa)); // 359.629f, 286.91f, 359.629f, 297.265f +path.close(); + SkPath path100(path); + builder.add(path100, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42a153aa), SkBits2Float(0x4387c2b8)); // 80.6634f, 271.521f +path.quadTo(SkBits2Float(0x42a153aa), SkBits2Float(0x438cf034), SkBits2Float(0x4292aea2), SkBits2Float(0x43909976)); // 80.6634f, 281.877f, 73.3411f, 289.199f +path.quadTo(SkBits2Float(0x42840999), SkBits2Float(0x439442b8), SkBits2Float(0x425ea754), SkBits2Float(0x439442b8)); // 66.0187f, 296.521f, 55.6634f, 296.521f +path.quadTo(SkBits2Float(0x42353b76), SkBits2Float(0x439442b8), SkBits2Float(0x4217f165), SkBits2Float(0x43909976)); // 45.3081f, 296.521f, 37.9857f, 289.199f +path.quadTo(SkBits2Float(0x41f54ea8), SkBits2Float(0x438cf034), SkBits2Float(0x41f54ea8), SkBits2Float(0x4387c2b8)); // 30.6634f, 281.877f, 30.6634f, 271.521f +path.quadTo(SkBits2Float(0x41f54ea8), SkBits2Float(0x4382953c), SkBits2Float(0x4217f165), SkBits2Float(0x437dd7f4)); // 30.6634f, 261.166f, 37.9857f, 253.844f +path.quadTo(SkBits2Float(0x42353b76), SkBits2Float(0x43768570), SkBits2Float(0x425ea754), SkBits2Float(0x43768570)); // 45.3081f, 246.521f, 55.6634f, 246.521f +path.quadTo(SkBits2Float(0x42840999), SkBits2Float(0x43768570), SkBits2Float(0x4292aea2), SkBits2Float(0x437dd7f4)); // 66.0187f, 246.521f, 73.3411f, 253.844f +path.quadTo(SkBits2Float(0x42a153aa), SkBits2Float(0x4382953c), SkBits2Float(0x42a153aa), SkBits2Float(0x4387c2b8)); // 80.6634f, 261.166f, 80.6634f, 271.521f +path.close(); + SkPath path101(path); + builder.add(path101, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x436da8c1), SkBits2Float(0x434950e1)); // 237.659f, 201.316f +path.quadTo(SkBits2Float(0x436da8c1), SkBits2Float(0x4353abd8), SkBits2Float(0x4366563d), SkBits2Float(0x435afe5d)); // 237.659f, 211.671f, 230.337f, 218.994f +path.quadTo(SkBits2Float(0x435f03b8), SkBits2Float(0x436250e1), SkBits2Float(0x4354a8c1), SkBits2Float(0x436250e1)); // 223.015f, 226.316f, 212.659f, 226.316f +path.quadTo(SkBits2Float(0x434a4dca), SkBits2Float(0x436250e1), SkBits2Float(0x4342fb45), SkBits2Float(0x435afe5d)); // 202.304f, 226.316f, 194.982f, 218.994f +path.quadTo(SkBits2Float(0x433ba8c1), SkBits2Float(0x4353abd8), SkBits2Float(0x433ba8c1), SkBits2Float(0x434950e1)); // 187.659f, 211.671f, 187.659f, 201.316f +path.quadTo(SkBits2Float(0x433ba8c1), SkBits2Float(0x433ef5ea), SkBits2Float(0x4342fb45), SkBits2Float(0x4337a365)); // 187.659f, 190.961f, 194.982f, 183.638f +path.quadTo(SkBits2Float(0x434a4dca), SkBits2Float(0x433050e1), SkBits2Float(0x4354a8c1), SkBits2Float(0x433050e1)); // 202.304f, 176.316f, 212.659f, 176.316f +path.quadTo(SkBits2Float(0x435f03b8), SkBits2Float(0x433050e1), SkBits2Float(0x4366563d), SkBits2Float(0x4337a365)); // 223.015f, 176.316f, 230.337f, 183.638f +path.quadTo(SkBits2Float(0x436da8c1), SkBits2Float(0x433ef5ea), SkBits2Float(0x436da8c1), SkBits2Float(0x434950e1)); // 237.659f, 190.961f, 237.659f, 201.316f +path.close(); + SkPath path102(path); + builder.add(path102, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43ba530a), SkBits2Float(0x4376baae)); // 372.649f, 246.729f +path.quadTo(SkBits2Float(0x43ba530a), SkBits2Float(0x43808ad3), SkBits2Float(0x43b6a9c8), SkBits2Float(0x43843415)); // 372.649f, 257.085f, 365.326f, 264.407f +path.quadTo(SkBits2Float(0x43b30086), SkBits2Float(0x4387dd57), SkBits2Float(0x43add30a), SkBits2Float(0x4387dd57)); // 358.004f, 271.729f, 347.649f, 271.729f +path.quadTo(SkBits2Float(0x43a8a58e), SkBits2Float(0x4387dd57), SkBits2Float(0x43a4fc4c), SkBits2Float(0x43843415)); // 337.293f, 271.729f, 329.971f, 264.407f +path.quadTo(SkBits2Float(0x43a1530a), SkBits2Float(0x43808ad3), SkBits2Float(0x43a1530a), SkBits2Float(0x4376baae)); // 322.649f, 257.085f, 322.649f, 246.729f +path.quadTo(SkBits2Float(0x43a1530a), SkBits2Float(0x436c5fb6), SkBits2Float(0x43a4fc4c), SkBits2Float(0x43650d32)); // 322.649f, 236.374f, 329.971f, 229.052f +path.quadTo(SkBits2Float(0x43a8a58e), SkBits2Float(0x435dbaae), SkBits2Float(0x43add30a), SkBits2Float(0x435dbaae)); // 337.293f, 221.729f, 347.649f, 221.729f +path.quadTo(SkBits2Float(0x43b30086), SkBits2Float(0x435dbaae), SkBits2Float(0x43b6a9c8), SkBits2Float(0x43650d32)); // 358.004f, 221.729f, 365.326f, 229.052f +path.quadTo(SkBits2Float(0x43ba530a), SkBits2Float(0x436c5fb6), SkBits2Float(0x43ba530a), SkBits2Float(0x4376baae)); // 372.649f, 236.374f, 372.649f, 246.729f +path.close(); + SkPath path103(path); + builder.add(path103, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4399286a), SkBits2Float(0x435887a5)); // 306.316f, 216.53f +path.quadTo(SkBits2Float(0x4399286a), SkBits2Float(0x4362e29c), SkBits2Float(0x43957f28), SkBits2Float(0x436a3521)); // 306.316f, 226.885f, 298.993f, 234.208f +path.quadTo(SkBits2Float(0x4391d5e6), SkBits2Float(0x437187a5), SkBits2Float(0x438ca86a), SkBits2Float(0x437187a5)); // 291.671f, 241.53f, 281.316f, 241.53f +path.quadTo(SkBits2Float(0x43877aee), SkBits2Float(0x437187a5), SkBits2Float(0x4383d1ac), SkBits2Float(0x436a3521)); // 270.96f, 241.53f, 263.638f, 234.208f +path.quadTo(SkBits2Float(0x4380286a), SkBits2Float(0x4362e29c), SkBits2Float(0x4380286a), SkBits2Float(0x435887a5)); // 256.316f, 226.885f, 256.316f, 216.53f +path.quadTo(SkBits2Float(0x4380286a), SkBits2Float(0x434e2cae), SkBits2Float(0x4383d1ac), SkBits2Float(0x4346da29)); // 256.316f, 206.175f, 263.638f, 198.852f +path.quadTo(SkBits2Float(0x43877aee), SkBits2Float(0x433f87a5), SkBits2Float(0x438ca86a), SkBits2Float(0x433f87a5)); // 270.96f, 191.53f, 281.316f, 191.53f +path.quadTo(SkBits2Float(0x4391d5e6), SkBits2Float(0x433f87a5), SkBits2Float(0x43957f28), SkBits2Float(0x4346da29)); // 291.671f, 191.53f, 298.993f, 198.852f +path.quadTo(SkBits2Float(0x4399286a), SkBits2Float(0x434e2cae), SkBits2Float(0x4399286a), SkBits2Float(0x435887a5)); // 306.316f, 206.175f, 306.316f, 216.53f +path.close(); + SkPath path104(path); + builder.add(path104, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43a08755), SkBits2Float(0x43120238)); // 321.057f, 146.009f +path.quadTo(SkBits2Float(0x43a08755), SkBits2Float(0x431c5d30), SkBits2Float(0x439cde13), SkBits2Float(0x4323afb4)); // 321.057f, 156.364f, 313.735f, 163.686f +path.quadTo(SkBits2Float(0x439934d1), SkBits2Float(0x432b0238), SkBits2Float(0x43940755), SkBits2Float(0x432b0238)); // 306.413f, 171.009f, 296.057f, 171.009f +path.quadTo(SkBits2Float(0x438ed9d9), SkBits2Float(0x432b0238), SkBits2Float(0x438b3097), SkBits2Float(0x4323afb4)); // 285.702f, 171.009f, 278.38f, 163.686f +path.quadTo(SkBits2Float(0x43878755), SkBits2Float(0x431c5d30), SkBits2Float(0x43878755), SkBits2Float(0x43120238)); // 271.057f, 156.364f, 271.057f, 146.009f +path.quadTo(SkBits2Float(0x43878755), SkBits2Float(0x4307a740), SkBits2Float(0x438b3097), SkBits2Float(0x430054bc)); // 271.057f, 135.653f, 278.38f, 128.331f +path.quadTo(SkBits2Float(0x438ed9d9), SkBits2Float(0x42f20470), SkBits2Float(0x43940755), SkBits2Float(0x42f20470)); // 285.702f, 121.009f, 296.057f, 121.009f +path.quadTo(SkBits2Float(0x439934d1), SkBits2Float(0x42f20470), SkBits2Float(0x439cde13), SkBits2Float(0x430054bc)); // 306.413f, 121.009f, 313.735f, 128.331f +path.quadTo(SkBits2Float(0x43a08755), SkBits2Float(0x4307a740), SkBits2Float(0x43a08755), SkBits2Float(0x43120238)); // 321.057f, 135.653f, 321.057f, 146.009f +path.close(); + SkPath path105(path); + builder.add(path105, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43e31764), SkBits2Float(0x438e240b)); // 454.183f, 284.282f +path.quadTo(SkBits2Float(0x43e31764), SkBits2Float(0x43935187), SkBits2Float(0x43df6e22), SkBits2Float(0x4396fac9)); // 454.183f, 294.637f, 446.86f, 301.959f +path.quadTo(SkBits2Float(0x43dbc4e0), SkBits2Float(0x439aa40b), SkBits2Float(0x43d69764), SkBits2Float(0x439aa40b)); // 439.538f, 309.282f, 429.183f, 309.282f +path.quadTo(SkBits2Float(0x43d169e8), SkBits2Float(0x439aa40b), SkBits2Float(0x43cdc0a6), SkBits2Float(0x4396fac9)); // 418.827f, 309.282f, 411.505f, 301.959f +path.quadTo(SkBits2Float(0x43ca1764), SkBits2Float(0x43935187), SkBits2Float(0x43ca1764), SkBits2Float(0x438e240b)); // 404.183f, 294.637f, 404.183f, 284.282f +path.quadTo(SkBits2Float(0x43ca1764), SkBits2Float(0x4388f68f), SkBits2Float(0x43cdc0a6), SkBits2Float(0x43854d4d)); // 404.183f, 273.926f, 411.505f, 266.604f +path.quadTo(SkBits2Float(0x43d169e8), SkBits2Float(0x4381a40b), SkBits2Float(0x43d69764), SkBits2Float(0x4381a40b)); // 418.827f, 259.282f, 429.183f, 259.282f +path.quadTo(SkBits2Float(0x43dbc4e0), SkBits2Float(0x4381a40b), SkBits2Float(0x43df6e22), SkBits2Float(0x43854d4d)); // 439.538f, 259.282f, 446.86f, 266.604f +path.quadTo(SkBits2Float(0x43e31764), SkBits2Float(0x4388f68f), SkBits2Float(0x43e31764), SkBits2Float(0x438e240b)); // 454.183f, 273.926f, 454.183f, 284.282f +path.close(); + SkPath path106(path); + builder.add(path106, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43430483), SkBits2Float(0x4345764d)); // 195.018f, 197.462f +path.quadTo(SkBits2Float(0x43430483), SkBits2Float(0x434fd144), SkBits2Float(0x433bb1ff), SkBits2Float(0x435723c9)); // 195.018f, 207.817f, 187.695f, 215.14f +path.quadTo(SkBits2Float(0x43345f7a), SkBits2Float(0x435e764d), SkBits2Float(0x432a0483), SkBits2Float(0x435e764d)); // 180.373f, 222.462f, 170.018f, 222.462f +path.quadTo(SkBits2Float(0x431fa98c), SkBits2Float(0x435e764d), SkBits2Float(0x43185707), SkBits2Float(0x435723c9)); // 159.662f, 222.462f, 152.34f, 215.14f +path.quadTo(SkBits2Float(0x43110483), SkBits2Float(0x434fd144), SkBits2Float(0x43110483), SkBits2Float(0x4345764d)); // 145.018f, 207.817f, 145.018f, 197.462f +path.quadTo(SkBits2Float(0x43110483), SkBits2Float(0x433b1b56), SkBits2Float(0x43185707), SkBits2Float(0x4333c8d1)); // 145.018f, 187.107f, 152.34f, 179.784f +path.quadTo(SkBits2Float(0x431fa98c), SkBits2Float(0x432c764d), SkBits2Float(0x432a0483), SkBits2Float(0x432c764d)); // 159.662f, 172.462f, 170.018f, 172.462f +path.quadTo(SkBits2Float(0x43345f7a), SkBits2Float(0x432c764d), SkBits2Float(0x433bb1ff), SkBits2Float(0x4333c8d1)); // 180.373f, 172.462f, 187.695f, 179.784f +path.quadTo(SkBits2Float(0x43430483), SkBits2Float(0x433b1b56), SkBits2Float(0x43430483), SkBits2Float(0x4345764d)); // 195.018f, 187.107f, 195.018f, 197.462f +path.close(); + SkPath path107(path); + builder.add(path107, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x439a55b2), SkBits2Float(0x4370a1cc)); // 308.669f, 240.632f +path.quadTo(SkBits2Float(0x439a55b2), SkBits2Float(0x437afcc3), SkBits2Float(0x4396ac70), SkBits2Float(0x438127a4)); // 308.669f, 250.987f, 301.347f, 258.31f +path.quadTo(SkBits2Float(0x4393032e), SkBits2Float(0x4384d0e6), SkBits2Float(0x438dd5b2), SkBits2Float(0x4384d0e6)); // 294.025f, 265.632f, 283.669f, 265.632f +path.quadTo(SkBits2Float(0x4388a836), SkBits2Float(0x4384d0e6), SkBits2Float(0x4384fef4), SkBits2Float(0x438127a4)); // 273.314f, 265.632f, 265.992f, 258.31f +path.quadTo(SkBits2Float(0x438155b2), SkBits2Float(0x437afcc3), SkBits2Float(0x438155b2), SkBits2Float(0x4370a1cc)); // 258.669f, 250.987f, 258.669f, 240.632f +path.quadTo(SkBits2Float(0x438155b2), SkBits2Float(0x436646d5), SkBits2Float(0x4384fef4), SkBits2Float(0x435ef451)); // 258.669f, 230.277f, 265.992f, 222.954f +path.quadTo(SkBits2Float(0x4388a836), SkBits2Float(0x4357a1cd), SkBits2Float(0x438dd5b2), SkBits2Float(0x4357a1cd)); // 273.314f, 215.632f, 283.669f, 215.632f +path.quadTo(SkBits2Float(0x4393032e), SkBits2Float(0x4357a1cd), SkBits2Float(0x4396ac70), SkBits2Float(0x435ef451)); // 294.025f, 215.632f, 301.347f, 222.954f +path.quadTo(SkBits2Float(0x439a55b2), SkBits2Float(0x436646d5), SkBits2Float(0x439a55b2), SkBits2Float(0x4370a1cc)); // 308.669f, 230.277f, 308.669f, 240.632f +path.close(); + SkPath path108(path); + builder.add(path108, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42fcf02d), SkBits2Float(0x4327ee31)); // 126.469f, 167.93f +path.quadTo(SkBits2Float(0x42fcf02d), SkBits2Float(0x43324928), SkBits2Float(0x42ee4b24), SkBits2Float(0x43399bad)); // 126.469f, 178.286f, 119.147f, 185.608f +path.quadTo(SkBits2Float(0x42dfa61c), SkBits2Float(0x4340ee31), SkBits2Float(0x42caf02d), SkBits2Float(0x4340ee31)); // 111.824f, 192.93f, 101.469f, 192.93f +path.quadTo(SkBits2Float(0x42b63a3e), SkBits2Float(0x4340ee31), SkBits2Float(0x42a79536), SkBits2Float(0x43399bad)); // 91.1138f, 192.93f, 83.7914f, 185.608f +path.quadTo(SkBits2Float(0x4298f02d), SkBits2Float(0x43324928), SkBits2Float(0x4298f02d), SkBits2Float(0x4327ee31)); // 76.4691f, 178.286f, 76.4691f, 167.93f +path.quadTo(SkBits2Float(0x4298f02d), SkBits2Float(0x431d933a), SkBits2Float(0x42a79536), SkBits2Float(0x431640b5)); // 76.4691f, 157.575f, 83.7914f, 150.253f +path.quadTo(SkBits2Float(0x42b63a3e), SkBits2Float(0x430eee31), SkBits2Float(0x42caf02d), SkBits2Float(0x430eee31)); // 91.1138f, 142.93f, 101.469f, 142.93f +path.quadTo(SkBits2Float(0x42dfa61c), SkBits2Float(0x430eee31), SkBits2Float(0x42ee4b24), SkBits2Float(0x431640b5)); // 111.824f, 142.93f, 119.147f, 150.253f +path.quadTo(SkBits2Float(0x42fcf02d), SkBits2Float(0x431d933a), SkBits2Float(0x42fcf02d), SkBits2Float(0x4327ee31)); // 126.469f, 157.575f, 126.469f, 167.93f +path.close(); + SkPath path109(path); + builder.add(path109, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4408a32e), SkBits2Float(0x438aadd7)); // 546.55f, 277.358f +path.quadTo(SkBits2Float(0x4408a32e), SkBits2Float(0x438fdb53), SkBits2Float(0x4406ce8d), SkBits2Float(0x43938495)); // 546.55f, 287.713f, 539.227f, 295.036f +path.quadTo(SkBits2Float(0x4404f9ec), SkBits2Float(0x43972dd7), SkBits2Float(0x4402632e), SkBits2Float(0x43972dd7)); // 531.905f, 302.358f, 521.55f, 302.358f +path.quadTo(SkBits2Float(0x43ff98e0), SkBits2Float(0x43972dd7), SkBits2Float(0x43fbef9e), SkBits2Float(0x43938495)); // 511.194f, 302.358f, 503.872f, 295.036f +path.quadTo(SkBits2Float(0x43f8465c), SkBits2Float(0x438fdb53), SkBits2Float(0x43f8465c), SkBits2Float(0x438aadd7)); // 496.55f, 287.713f, 496.55f, 277.358f +path.quadTo(SkBits2Float(0x43f8465c), SkBits2Float(0x4385805b), SkBits2Float(0x43fbef9e), SkBits2Float(0x4381d719)); // 496.55f, 267.003f, 503.872f, 259.68f +path.quadTo(SkBits2Float(0x43ff98e0), SkBits2Float(0x437c5bae), SkBits2Float(0x4402632e), SkBits2Float(0x437c5bae)); // 511.194f, 252.358f, 521.55f, 252.358f +path.quadTo(SkBits2Float(0x4404f9ec), SkBits2Float(0x437c5bae), SkBits2Float(0x4406ce8d), SkBits2Float(0x4381d719)); // 531.905f, 252.358f, 539.227f, 259.68f +path.quadTo(SkBits2Float(0x4408a32e), SkBits2Float(0x4385805b), SkBits2Float(0x4408a32e), SkBits2Float(0x438aadd7)); // 546.55f, 267.003f, 546.55f, 277.358f +path.close(); + SkPath path110(path); + builder.add(path110, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43ae6436), SkBits2Float(0x4399c25d)); // 348.783f, 307.518f +path.quadTo(SkBits2Float(0x43ae6436), SkBits2Float(0x439eefd9), SkBits2Float(0x43aabaf4), SkBits2Float(0x43a2991b)); // 348.783f, 317.874f, 341.461f, 325.196f +path.quadTo(SkBits2Float(0x43a711b2), SkBits2Float(0x43a6425d), SkBits2Float(0x43a1e436), SkBits2Float(0x43a6425d)); // 334.138f, 332.518f, 323.783f, 332.518f +path.quadTo(SkBits2Float(0x439cb6ba), SkBits2Float(0x43a6425d), SkBits2Float(0x43990d78), SkBits2Float(0x43a2991b)); // 313.428f, 332.518f, 306.105f, 325.196f +path.quadTo(SkBits2Float(0x43956436), SkBits2Float(0x439eefd9), SkBits2Float(0x43956436), SkBits2Float(0x4399c25d)); // 298.783f, 317.874f, 298.783f, 307.518f +path.quadTo(SkBits2Float(0x43956436), SkBits2Float(0x439494e1), SkBits2Float(0x43990d78), SkBits2Float(0x4390eb9f)); // 298.783f, 297.163f, 306.105f, 289.841f +path.quadTo(SkBits2Float(0x439cb6ba), SkBits2Float(0x438d425d), SkBits2Float(0x43a1e436), SkBits2Float(0x438d425d)); // 313.428f, 282.518f, 323.783f, 282.518f +path.quadTo(SkBits2Float(0x43a711b2), SkBits2Float(0x438d425d), SkBits2Float(0x43aabaf4), SkBits2Float(0x4390eb9f)); // 334.138f, 282.518f, 341.461f, 289.841f +path.quadTo(SkBits2Float(0x43ae6436), SkBits2Float(0x439494e1), SkBits2Float(0x43ae6436), SkBits2Float(0x4399c25d)); // 348.783f, 297.163f, 348.783f, 307.518f +path.close(); + SkPath path111(path); + builder.add(path111, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x437ef735), SkBits2Float(0x43a4d766)); // 254.966f, 329.683f +path.quadTo(SkBits2Float(0x437ef735), SkBits2Float(0x43aa04e2), SkBits2Float(0x4377a4b1), SkBits2Float(0x43adae24)); // 254.966f, 340.038f, 247.643f, 347.36f +path.quadTo(SkBits2Float(0x4370522c), SkBits2Float(0x43b15766), SkBits2Float(0x4365f735), SkBits2Float(0x43b15766)); // 240.321f, 354.683f, 229.966f, 354.683f +path.quadTo(SkBits2Float(0x435b9c3e), SkBits2Float(0x43b15766), SkBits2Float(0x435449b9), SkBits2Float(0x43adae24)); // 219.61f, 354.683f, 212.288f, 347.36f +path.quadTo(SkBits2Float(0x434cf735), SkBits2Float(0x43aa04e2), SkBits2Float(0x434cf735), SkBits2Float(0x43a4d766)); // 204.966f, 340.038f, 204.966f, 329.683f +path.quadTo(SkBits2Float(0x434cf735), SkBits2Float(0x439fa9ea), SkBits2Float(0x435449b9), SkBits2Float(0x439c00a8)); // 204.966f, 319.327f, 212.288f, 312.005f +path.quadTo(SkBits2Float(0x435b9c3e), SkBits2Float(0x43985766), SkBits2Float(0x4365f735), SkBits2Float(0x43985766)); // 219.61f, 304.683f, 229.966f, 304.683f +path.quadTo(SkBits2Float(0x4370522c), SkBits2Float(0x43985766), SkBits2Float(0x4377a4b1), SkBits2Float(0x439c00a8)); // 240.321f, 304.683f, 247.643f, 312.005f +path.quadTo(SkBits2Float(0x437ef735), SkBits2Float(0x439fa9ea), SkBits2Float(0x437ef735), SkBits2Float(0x43a4d766)); // 254.966f, 319.327f, 254.966f, 329.683f +path.close(); + SkPath path112(path); + builder.add(path112, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4323b1dd), SkBits2Float(0x433e57b5)); // 163.695f, 190.343f +path.quadTo(SkBits2Float(0x4323b1dd), SkBits2Float(0x4348b2ac), SkBits2Float(0x431c5f59), SkBits2Float(0x43500531)); // 163.695f, 200.698f, 156.372f, 208.02f +path.quadTo(SkBits2Float(0x43150cd4), SkBits2Float(0x435757b5), SkBits2Float(0x430ab1dd), SkBits2Float(0x435757b5)); // 149.05f, 215.343f, 138.695f, 215.343f +path.quadTo(SkBits2Float(0x430056e6), SkBits2Float(0x435757b5), SkBits2Float(0x42f208c2), SkBits2Float(0x43500531)); // 128.339f, 215.343f, 121.017f, 208.02f +path.quadTo(SkBits2Float(0x42e363ba), SkBits2Float(0x4348b2ac), SkBits2Float(0x42e363ba), SkBits2Float(0x433e57b5)); // 113.695f, 200.698f, 113.695f, 190.343f +path.quadTo(SkBits2Float(0x42e363ba), SkBits2Float(0x4333fcbe), SkBits2Float(0x42f208c2), SkBits2Float(0x432caa39)); // 113.695f, 179.987f, 121.017f, 172.665f +path.quadTo(SkBits2Float(0x430056e6), SkBits2Float(0x432557b5), SkBits2Float(0x430ab1dd), SkBits2Float(0x432557b5)); // 128.339f, 165.343f, 138.695f, 165.343f +path.quadTo(SkBits2Float(0x43150cd4), SkBits2Float(0x432557b5), SkBits2Float(0x431c5f59), SkBits2Float(0x432caa39)); // 149.05f, 165.343f, 156.372f, 172.665f +path.quadTo(SkBits2Float(0x4323b1dd), SkBits2Float(0x4333fcbe), SkBits2Float(0x4323b1dd), SkBits2Float(0x433e57b5)); // 163.695f, 179.987f, 163.695f, 190.343f +path.close(); + SkPath path113(path); + builder.add(path113, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x436e6f43), SkBits2Float(0x435d1aaa)); // 238.435f, 221.104f +path.quadTo(SkBits2Float(0x436e6f43), SkBits2Float(0x436775a2), SkBits2Float(0x43671cbf), SkBits2Float(0x436ec826)); // 238.435f, 231.46f, 231.112f, 238.782f +path.quadTo(SkBits2Float(0x435fca3a), SkBits2Float(0x43761aaa), SkBits2Float(0x43556f43), SkBits2Float(0x43761aaa)); // 223.79f, 246.104f, 213.435f, 246.104f +path.quadTo(SkBits2Float(0x434b144c), SkBits2Float(0x43761aaa), SkBits2Float(0x4343c1c7), SkBits2Float(0x436ec826)); // 203.079f, 246.104f, 195.757f, 238.782f +path.quadTo(SkBits2Float(0x433c6f43), SkBits2Float(0x436775a2), SkBits2Float(0x433c6f43), SkBits2Float(0x435d1aaa)); // 188.435f, 231.46f, 188.435f, 221.104f +path.quadTo(SkBits2Float(0x433c6f43), SkBits2Float(0x4352bfb2), SkBits2Float(0x4343c1c7), SkBits2Float(0x434b6d2e)); // 188.435f, 210.749f, 195.757f, 203.426f +path.quadTo(SkBits2Float(0x434b144c), SkBits2Float(0x43441aaa), SkBits2Float(0x43556f43), SkBits2Float(0x43441aaa)); // 203.079f, 196.104f, 213.435f, 196.104f +path.quadTo(SkBits2Float(0x435fca3a), SkBits2Float(0x43441aaa), SkBits2Float(0x43671cbf), SkBits2Float(0x434b6d2e)); // 223.79f, 196.104f, 231.112f, 203.426f +path.quadTo(SkBits2Float(0x436e6f43), SkBits2Float(0x4352bfb2), SkBits2Float(0x436e6f43), SkBits2Float(0x435d1aaa)); // 238.435f, 210.749f, 238.435f, 221.104f +path.close(); + SkPath path114(path); + builder.add(path114, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43699f20), SkBits2Float(0x43b74967)); // 233.622f, 366.573f +path.quadTo(SkBits2Float(0x43699f20), SkBits2Float(0x43bc76e3), SkBits2Float(0x43624c9c), SkBits2Float(0x43c02025)); // 233.622f, 376.929f, 226.299f, 384.251f +path.quadTo(SkBits2Float(0x435afa18), SkBits2Float(0x43c3c967), SkBits2Float(0x43509f20), SkBits2Float(0x43c3c967)); // 218.977f, 391.573f, 208.622f, 391.573f +path.quadTo(SkBits2Float(0x43464428), SkBits2Float(0x43c3c967), SkBits2Float(0x433ef1a4), SkBits2Float(0x43c02025)); // 198.266f, 391.573f, 190.944f, 384.251f +path.quadTo(SkBits2Float(0x43379f20), SkBits2Float(0x43bc76e3), SkBits2Float(0x43379f20), SkBits2Float(0x43b74967)); // 183.622f, 376.929f, 183.622f, 366.573f +path.quadTo(SkBits2Float(0x43379f20), SkBits2Float(0x43b21beb), SkBits2Float(0x433ef1a4), SkBits2Float(0x43ae72a9)); // 183.622f, 356.218f, 190.944f, 348.896f +path.quadTo(SkBits2Float(0x43464428), SkBits2Float(0x43aac967), SkBits2Float(0x43509f20), SkBits2Float(0x43aac967)); // 198.266f, 341.573f, 208.622f, 341.573f +path.quadTo(SkBits2Float(0x435afa18), SkBits2Float(0x43aac967), SkBits2Float(0x43624c9c), SkBits2Float(0x43ae72a9)); // 218.977f, 341.573f, 226.299f, 348.896f +path.quadTo(SkBits2Float(0x43699f20), SkBits2Float(0x43b21beb), SkBits2Float(0x43699f20), SkBits2Float(0x43b74967)); // 233.622f, 356.218f, 233.622f, 366.573f +path.close(); + SkPath path115(path); + builder.add(path115, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4372b5ce), SkBits2Float(0x434919ea)); // 242.71f, 201.101f +path.quadTo(SkBits2Float(0x4372b5ce), SkBits2Float(0x435374e2), SkBits2Float(0x436b634a), SkBits2Float(0x435ac766)); // 242.71f, 211.457f, 235.388f, 218.779f +path.quadTo(SkBits2Float(0x436410c6), SkBits2Float(0x436219ea), SkBits2Float(0x4359b5ce), SkBits2Float(0x436219ea)); // 228.066f, 226.101f, 217.71f, 226.101f +path.quadTo(SkBits2Float(0x434f5ad6), SkBits2Float(0x436219ea), SkBits2Float(0x43480852), SkBits2Float(0x435ac766)); // 207.355f, 226.101f, 200.033f, 218.779f +path.quadTo(SkBits2Float(0x4340b5ce), SkBits2Float(0x435374e2), SkBits2Float(0x4340b5ce), SkBits2Float(0x434919ea)); // 192.71f, 211.457f, 192.71f, 201.101f +path.quadTo(SkBits2Float(0x4340b5ce), SkBits2Float(0x433ebef2), SkBits2Float(0x43480852), SkBits2Float(0x43376c6e)); // 192.71f, 190.746f, 200.033f, 183.424f +path.quadTo(SkBits2Float(0x434f5ad6), SkBits2Float(0x433019ea), SkBits2Float(0x4359b5ce), SkBits2Float(0x433019ea)); // 207.355f, 176.101f, 217.71f, 176.101f +path.quadTo(SkBits2Float(0x436410c6), SkBits2Float(0x433019ea), SkBits2Float(0x436b634a), SkBits2Float(0x43376c6e)); // 228.066f, 176.101f, 235.388f, 183.424f +path.quadTo(SkBits2Float(0x4372b5ce), SkBits2Float(0x433ebef2), SkBits2Float(0x4372b5ce), SkBits2Float(0x434919ea)); // 242.71f, 190.746f, 242.71f, 201.101f +path.close(); + SkPath path116(path); + builder.add(path116, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43212033), SkBits2Float(0x433c0771)); // 161.126f, 188.029f +path.quadTo(SkBits2Float(0x43212033), SkBits2Float(0x43466268), SkBits2Float(0x4319cdaf), SkBits2Float(0x434db4ed)); // 161.126f, 198.384f, 153.803f, 205.707f +path.quadTo(SkBits2Float(0x43127b2a), SkBits2Float(0x43550771), SkBits2Float(0x43082033), SkBits2Float(0x43550771)); // 146.481f, 213.029f, 136.126f, 213.029f +path.quadTo(SkBits2Float(0x42fb8a77), SkBits2Float(0x43550771), SkBits2Float(0x42ece56e), SkBits2Float(0x434db4ed)); // 125.77f, 213.029f, 118.448f, 205.707f +path.quadTo(SkBits2Float(0x42de4066), SkBits2Float(0x43466268), SkBits2Float(0x42de4066), SkBits2Float(0x433c0771)); // 111.126f, 198.384f, 111.126f, 188.029f +path.quadTo(SkBits2Float(0x42de4066), SkBits2Float(0x4331ac7a), SkBits2Float(0x42ece56e), SkBits2Float(0x432a59f5)); // 111.126f, 177.674f, 118.448f, 170.351f +path.quadTo(SkBits2Float(0x42fb8a77), SkBits2Float(0x43230771), SkBits2Float(0x43082033), SkBits2Float(0x43230771)); // 125.77f, 163.029f, 136.126f, 163.029f +path.quadTo(SkBits2Float(0x43127b2a), SkBits2Float(0x43230771), SkBits2Float(0x4319cdaf), SkBits2Float(0x432a59f5)); // 146.481f, 163.029f, 153.803f, 170.351f +path.quadTo(SkBits2Float(0x43212033), SkBits2Float(0x4331ac7a), SkBits2Float(0x43212033), SkBits2Float(0x433c0771)); // 161.126f, 177.674f, 161.126f, 188.029f +path.close(); + SkPath path117(path); + builder.add(path117, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x434d1191), SkBits2Float(0x431c5cdf)); // 205.069f, 156.363f +path.quadTo(SkBits2Float(0x434d1191), SkBits2Float(0x4326b7d6), SkBits2Float(0x4345bf0d), SkBits2Float(0x432e0a5b)); // 205.069f, 166.718f, 197.746f, 174.04f +path.quadTo(SkBits2Float(0x433e6c88), SkBits2Float(0x43355cdf), SkBits2Float(0x43341191), SkBits2Float(0x43355cdf)); // 190.424f, 181.363f, 180.069f, 181.363f +path.quadTo(SkBits2Float(0x4329b69a), SkBits2Float(0x43355cdf), SkBits2Float(0x43226415), SkBits2Float(0x432e0a5b)); // 169.713f, 181.363f, 162.391f, 174.04f +path.quadTo(SkBits2Float(0x431b1191), SkBits2Float(0x4326b7d6), SkBits2Float(0x431b1191), SkBits2Float(0x431c5cdf)); // 155.069f, 166.718f, 155.069f, 156.363f +path.quadTo(SkBits2Float(0x431b1191), SkBits2Float(0x431201e8), SkBits2Float(0x43226415), SkBits2Float(0x430aaf63)); // 155.069f, 146.007f, 162.391f, 138.685f +path.quadTo(SkBits2Float(0x4329b69a), SkBits2Float(0x43035cdf), SkBits2Float(0x43341191), SkBits2Float(0x43035cdf)); // 169.713f, 131.363f, 180.069f, 131.363f +path.quadTo(SkBits2Float(0x433e6c88), SkBits2Float(0x43035cdf), SkBits2Float(0x4345bf0d), SkBits2Float(0x430aaf63)); // 190.424f, 131.363f, 197.746f, 138.685f +path.quadTo(SkBits2Float(0x434d1191), SkBits2Float(0x431201e8), SkBits2Float(0x434d1191), SkBits2Float(0x431c5cdf)); // 205.069f, 146.007f, 205.069f, 156.363f +path.close(); + SkPath path118(path); + builder.add(path118, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43f7761c), SkBits2Float(0x43ba1964)); // 494.923f, 372.198f +path.quadTo(SkBits2Float(0x43f7761c), SkBits2Float(0x43bf46e0), SkBits2Float(0x43f3ccda), SkBits2Float(0x43c2f022)); // 494.923f, 382.554f, 487.6f, 389.876f +path.quadTo(SkBits2Float(0x43f02398), SkBits2Float(0x43c69964), SkBits2Float(0x43eaf61c), SkBits2Float(0x43c69964)); // 480.278f, 397.198f, 469.923f, 397.198f +path.quadTo(SkBits2Float(0x43e5c8a0), SkBits2Float(0x43c69964), SkBits2Float(0x43e21f5e), SkBits2Float(0x43c2f022)); // 459.567f, 397.198f, 452.245f, 389.876f +path.quadTo(SkBits2Float(0x43de761c), SkBits2Float(0x43bf46e0), SkBits2Float(0x43de761c), SkBits2Float(0x43ba1964)); // 444.923f, 382.554f, 444.923f, 372.198f +path.quadTo(SkBits2Float(0x43de761c), SkBits2Float(0x43b4ebe8), SkBits2Float(0x43e21f5e), SkBits2Float(0x43b142a6)); // 444.923f, 361.843f, 452.245f, 354.521f +path.quadTo(SkBits2Float(0x43e5c8a0), SkBits2Float(0x43ad9964), SkBits2Float(0x43eaf61c), SkBits2Float(0x43ad9964)); // 459.567f, 347.198f, 469.923f, 347.198f +path.quadTo(SkBits2Float(0x43f02398), SkBits2Float(0x43ad9964), SkBits2Float(0x43f3ccda), SkBits2Float(0x43b142a6)); // 480.278f, 347.198f, 487.6f, 354.521f +path.quadTo(SkBits2Float(0x43f7761c), SkBits2Float(0x43b4ebe8), SkBits2Float(0x43f7761c), SkBits2Float(0x43ba1964)); // 494.923f, 361.843f, 494.923f, 372.198f +path.close(); + SkPath path119(path); + builder.add(path119, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4387b4f4), SkBits2Float(0x4382767e)); // 271.414f, 260.926f +path.quadTo(SkBits2Float(0x4387b4f4), SkBits2Float(0x4387a3fa), SkBits2Float(0x43840bb2), SkBits2Float(0x438b4d3c)); // 271.414f, 271.281f, 264.091f, 278.603f +path.quadTo(SkBits2Float(0x43806270), SkBits2Float(0x438ef67e), SkBits2Float(0x437669e8), SkBits2Float(0x438ef67e)); // 256.769f, 285.926f, 246.414f, 285.926f +path.quadTo(SkBits2Float(0x436c0ef1), SkBits2Float(0x438ef67e), SkBits2Float(0x4364bc6d), SkBits2Float(0x438b4d3c)); // 236.058f, 285.926f, 228.736f, 278.603f +path.quadTo(SkBits2Float(0x435d69e9), SkBits2Float(0x4387a3fa), SkBits2Float(0x435d69e9), SkBits2Float(0x4382767e)); // 221.414f, 271.281f, 221.414f, 260.926f +path.quadTo(SkBits2Float(0x435d69e9), SkBits2Float(0x437a9204), SkBits2Float(0x4364bc6d), SkBits2Float(0x43733f80)); // 221.414f, 250.57f, 228.736f, 243.248f +path.quadTo(SkBits2Float(0x436c0ef1), SkBits2Float(0x436becfc), SkBits2Float(0x437669e8), SkBits2Float(0x436becfc)); // 236.058f, 235.926f, 246.414f, 235.926f +path.quadTo(SkBits2Float(0x43806270), SkBits2Float(0x436becfc), SkBits2Float(0x43840bb2), SkBits2Float(0x43733f80)); // 256.769f, 235.926f, 264.091f, 243.248f +path.quadTo(SkBits2Float(0x4387b4f4), SkBits2Float(0x437a9204), SkBits2Float(0x4387b4f4), SkBits2Float(0x4382767e)); // 271.414f, 250.57f, 271.414f, 260.926f +path.close(); + SkPath path120(path); + builder.add(path120, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43f176b1), SkBits2Float(0x437ef3b8)); // 482.927f, 254.952f +path.quadTo(SkBits2Float(0x43f176b1), SkBits2Float(0x4384a758), SkBits2Float(0x43edcd6f), SkBits2Float(0x4388509a)); // 482.927f, 265.307f, 475.605f, 272.63f +path.quadTo(SkBits2Float(0x43ea242d), SkBits2Float(0x438bf9dc), SkBits2Float(0x43e4f6b1), SkBits2Float(0x438bf9dc)); // 468.283f, 279.952f, 457.927f, 279.952f +path.quadTo(SkBits2Float(0x43dfc935), SkBits2Float(0x438bf9dc), SkBits2Float(0x43dc1ff3), SkBits2Float(0x4388509a)); // 447.572f, 279.952f, 440.25f, 272.63f +path.quadTo(SkBits2Float(0x43d876b1), SkBits2Float(0x4384a758), SkBits2Float(0x43d876b1), SkBits2Float(0x437ef3b8)); // 432.927f, 265.307f, 432.927f, 254.952f +path.quadTo(SkBits2Float(0x43d876b1), SkBits2Float(0x437498c0), SkBits2Float(0x43dc1ff3), SkBits2Float(0x436d463c)); // 432.927f, 244.597f, 440.25f, 237.274f +path.quadTo(SkBits2Float(0x43dfc935), SkBits2Float(0x4365f3b8), SkBits2Float(0x43e4f6b1), SkBits2Float(0x4365f3b8)); // 447.572f, 229.952f, 457.927f, 229.952f +path.quadTo(SkBits2Float(0x43ea242d), SkBits2Float(0x4365f3b8), SkBits2Float(0x43edcd6f), SkBits2Float(0x436d463c)); // 468.283f, 229.952f, 475.605f, 237.274f +path.quadTo(SkBits2Float(0x43f176b1), SkBits2Float(0x437498c0), SkBits2Float(0x43f176b1), SkBits2Float(0x437ef3b8)); // 482.927f, 244.597f, 482.927f, 254.952f +path.close(); + SkPath path121(path); + builder.add(path121, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x437bd45b), SkBits2Float(0x4361cf52)); // 251.83f, 225.81f +path.quadTo(SkBits2Float(0x437bd45b), SkBits2Float(0x436c2a4a), SkBits2Float(0x437481d7), SkBits2Float(0x43737cce)); // 251.83f, 236.165f, 244.507f, 243.488f +path.quadTo(SkBits2Float(0x436d2f52), SkBits2Float(0x437acf52), SkBits2Float(0x4362d45b), SkBits2Float(0x437acf52)); // 237.185f, 250.81f, 226.83f, 250.81f +path.quadTo(SkBits2Float(0x43587964), SkBits2Float(0x437acf52), SkBits2Float(0x435126df), SkBits2Float(0x43737cce)); // 216.474f, 250.81f, 209.152f, 243.488f +path.quadTo(SkBits2Float(0x4349d45b), SkBits2Float(0x436c2a4a), SkBits2Float(0x4349d45b), SkBits2Float(0x4361cf52)); // 201.83f, 236.165f, 201.83f, 225.81f +path.quadTo(SkBits2Float(0x4349d45b), SkBits2Float(0x4357745a), SkBits2Float(0x435126df), SkBits2Float(0x435021d6)); // 201.83f, 215.454f, 209.152f, 208.132f +path.quadTo(SkBits2Float(0x43587964), SkBits2Float(0x4348cf52), SkBits2Float(0x4362d45b), SkBits2Float(0x4348cf52)); // 216.474f, 200.81f, 226.83f, 200.81f +path.quadTo(SkBits2Float(0x436d2f52), SkBits2Float(0x4348cf52), SkBits2Float(0x437481d7), SkBits2Float(0x435021d6)); // 237.185f, 200.81f, 244.507f, 208.132f +path.quadTo(SkBits2Float(0x437bd45b), SkBits2Float(0x4357745a), SkBits2Float(0x437bd45b), SkBits2Float(0x4361cf52)); // 251.83f, 215.454f, 251.83f, 225.81f +path.close(); + SkPath path122(path); + builder.add(path122, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43bb783c), SkBits2Float(0x43d20000)); // 374.939f, 420 +path.quadTo(SkBits2Float(0x43bb783c), SkBits2Float(0x43d72d7c), SkBits2Float(0x43b7cefa), SkBits2Float(0x43dad6be)); // 374.939f, 430.355f, 367.617f, 437.678f +path.quadTo(SkBits2Float(0x43b425b8), SkBits2Float(0x43de8000), SkBits2Float(0x43aef83c), SkBits2Float(0x43de8000)); // 360.295f, 445, 349.939f, 445 +path.quadTo(SkBits2Float(0x43a9cac0), SkBits2Float(0x43de8000), SkBits2Float(0x43a6217e), SkBits2Float(0x43dad6be)); // 339.584f, 445, 332.262f, 437.678f +path.quadTo(SkBits2Float(0x43a2783c), SkBits2Float(0x43d72d7c), SkBits2Float(0x43a2783c), SkBits2Float(0x43d20000)); // 324.939f, 430.355f, 324.939f, 420 +path.quadTo(SkBits2Float(0x43a2783c), SkBits2Float(0x43ccd284), SkBits2Float(0x43a6217e), SkBits2Float(0x43c92942)); // 324.939f, 409.645f, 332.262f, 402.322f +path.quadTo(SkBits2Float(0x43a9cac0), SkBits2Float(0x43c58000), SkBits2Float(0x43aef83c), SkBits2Float(0x43c58000)); // 339.584f, 395, 349.939f, 395 +path.quadTo(SkBits2Float(0x43b425b8), SkBits2Float(0x43c58000), SkBits2Float(0x43b7cefa), SkBits2Float(0x43c92942)); // 360.295f, 395, 367.617f, 402.322f +path.quadTo(SkBits2Float(0x43bb783c), SkBits2Float(0x43ccd284), SkBits2Float(0x43bb783c), SkBits2Float(0x43d20000)); // 374.939f, 409.645f, 374.939f, 420 +path.close(); + SkPath path123(path); + builder.add(path123, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43ca2436), SkBits2Float(0x435899a3)); // 404.283f, 216.6f +path.quadTo(SkBits2Float(0x43ca2436), SkBits2Float(0x4362f49a), SkBits2Float(0x43c67af4), SkBits2Float(0x436a471f)); // 404.283f, 226.955f, 396.961f, 234.278f +path.quadTo(SkBits2Float(0x43c2d1b2), SkBits2Float(0x437199a3), SkBits2Float(0x43bda436), SkBits2Float(0x437199a3)); // 389.638f, 241.6f, 379.283f, 241.6f +path.quadTo(SkBits2Float(0x43b876ba), SkBits2Float(0x437199a3), SkBits2Float(0x43b4cd78), SkBits2Float(0x436a471f)); // 368.928f, 241.6f, 361.605f, 234.278f +path.quadTo(SkBits2Float(0x43b12436), SkBits2Float(0x4362f49a), SkBits2Float(0x43b12436), SkBits2Float(0x435899a3)); // 354.283f, 226.955f, 354.283f, 216.6f +path.quadTo(SkBits2Float(0x43b12436), SkBits2Float(0x434e3eac), SkBits2Float(0x43b4cd78), SkBits2Float(0x4346ec27)); // 354.283f, 206.245f, 361.605f, 198.922f +path.quadTo(SkBits2Float(0x43b876ba), SkBits2Float(0x433f99a3), SkBits2Float(0x43bda436), SkBits2Float(0x433f99a3)); // 368.928f, 191.6f, 379.283f, 191.6f +path.quadTo(SkBits2Float(0x43c2d1b2), SkBits2Float(0x433f99a3), SkBits2Float(0x43c67af4), SkBits2Float(0x4346ec27)); // 389.638f, 191.6f, 396.961f, 198.922f +path.quadTo(SkBits2Float(0x43ca2436), SkBits2Float(0x434e3eac), SkBits2Float(0x43ca2436), SkBits2Float(0x435899a3)); // 404.283f, 206.245f, 404.283f, 216.6f +path.close(); + SkPath path124(path); + builder.add(path124, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x440faed8), SkBits2Float(0x43474968)); // 574.732f, 199.287f +path.quadTo(SkBits2Float(0x440faed8), SkBits2Float(0x4351a460), SkBits2Float(0x440dda37), SkBits2Float(0x4358f6e4)); // 574.732f, 209.642f, 567.41f, 216.964f +path.quadTo(SkBits2Float(0x440c0596), SkBits2Float(0x43604968), SkBits2Float(0x44096ed8), SkBits2Float(0x43604968)); // 560.087f, 224.287f, 549.732f, 224.287f +path.quadTo(SkBits2Float(0x4406d81a), SkBits2Float(0x43604968), SkBits2Float(0x44050379), SkBits2Float(0x4358f6e4)); // 539.377f, 224.287f, 532.054f, 216.964f +path.quadTo(SkBits2Float(0x44032ed8), SkBits2Float(0x4351a460), SkBits2Float(0x44032ed8), SkBits2Float(0x43474968)); // 524.732f, 209.642f, 524.732f, 199.287f +path.quadTo(SkBits2Float(0x44032ed8), SkBits2Float(0x433cee70), SkBits2Float(0x44050379), SkBits2Float(0x43359bec)); // 524.732f, 188.931f, 532.054f, 181.609f +path.quadTo(SkBits2Float(0x4406d81a), SkBits2Float(0x432e4968), SkBits2Float(0x44096ed8), SkBits2Float(0x432e4968)); // 539.377f, 174.287f, 549.732f, 174.287f +path.quadTo(SkBits2Float(0x440c0596), SkBits2Float(0x432e4968), SkBits2Float(0x440dda37), SkBits2Float(0x43359bec)); // 560.087f, 174.287f, 567.41f, 181.609f +path.quadTo(SkBits2Float(0x440faed8), SkBits2Float(0x433cee70), SkBits2Float(0x440faed8), SkBits2Float(0x43474968)); // 574.732f, 188.931f, 574.732f, 199.287f +path.close(); + SkPath path125(path); + builder.add(path125, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x437055b3), SkBits2Float(0x438ae764)); // 240.335f, 277.808f +path.quadTo(SkBits2Float(0x437055b3), SkBits2Float(0x439014e0), SkBits2Float(0x4369032f), SkBits2Float(0x4393be22)); // 240.335f, 288.163f, 233.012f, 295.485f +path.quadTo(SkBits2Float(0x4361b0aa), SkBits2Float(0x43976764), SkBits2Float(0x435755b3), SkBits2Float(0x43976764)); // 225.69f, 302.808f, 215.335f, 302.808f +path.quadTo(SkBits2Float(0x434cfabc), SkBits2Float(0x43976764), SkBits2Float(0x4345a837), SkBits2Float(0x4393be22)); // 204.979f, 302.808f, 197.657f, 295.485f +path.quadTo(SkBits2Float(0x433e55b3), SkBits2Float(0x439014e0), SkBits2Float(0x433e55b3), SkBits2Float(0x438ae764)); // 190.335f, 288.163f, 190.335f, 277.808f +path.quadTo(SkBits2Float(0x433e55b3), SkBits2Float(0x4385b9e8), SkBits2Float(0x4345a837), SkBits2Float(0x438210a6)); // 190.335f, 267.452f, 197.657f, 260.13f +path.quadTo(SkBits2Float(0x434cfabc), SkBits2Float(0x437ccec8), SkBits2Float(0x435755b3), SkBits2Float(0x437ccec8)); // 204.979f, 252.808f, 215.335f, 252.808f +path.quadTo(SkBits2Float(0x4361b0aa), SkBits2Float(0x437ccec8), SkBits2Float(0x4369032f), SkBits2Float(0x438210a6)); // 225.69f, 252.808f, 233.012f, 260.13f +path.quadTo(SkBits2Float(0x437055b3), SkBits2Float(0x4385b9e8), SkBits2Float(0x437055b3), SkBits2Float(0x438ae764)); // 240.335f, 267.452f, 240.335f, 277.808f +path.close(); + SkPath path126(path); + builder.add(path126, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43a201c9), SkBits2Float(0x43546e88)); // 324.014f, 212.432f +path.quadTo(SkBits2Float(0x43a201c9), SkBits2Float(0x435ec980), SkBits2Float(0x439e5887), SkBits2Float(0x43661c04)); // 324.014f, 222.787f, 316.692f, 230.109f +path.quadTo(SkBits2Float(0x439aaf45), SkBits2Float(0x436d6e88), SkBits2Float(0x439581c9), SkBits2Float(0x436d6e88)); // 309.369f, 237.432f, 299.014f, 237.432f +path.quadTo(SkBits2Float(0x4390544d), SkBits2Float(0x436d6e88), SkBits2Float(0x438cab0b), SkBits2Float(0x43661c04)); // 288.659f, 237.432f, 281.336f, 230.109f +path.quadTo(SkBits2Float(0x438901c9), SkBits2Float(0x435ec980), SkBits2Float(0x438901c9), SkBits2Float(0x43546e88)); // 274.014f, 222.787f, 274.014f, 212.432f +path.quadTo(SkBits2Float(0x438901c9), SkBits2Float(0x434a1390), SkBits2Float(0x438cab0b), SkBits2Float(0x4342c10c)); // 274.014f, 202.076f, 281.336f, 194.754f +path.quadTo(SkBits2Float(0x4390544d), SkBits2Float(0x433b6e88), SkBits2Float(0x439581c9), SkBits2Float(0x433b6e88)); // 288.659f, 187.432f, 299.014f, 187.432f +path.quadTo(SkBits2Float(0x439aaf45), SkBits2Float(0x433b6e88), SkBits2Float(0x439e5887), SkBits2Float(0x4342c10c)); // 309.369f, 187.432f, 316.692f, 194.754f +path.quadTo(SkBits2Float(0x43a201c9), SkBits2Float(0x434a1390), SkBits2Float(0x43a201c9), SkBits2Float(0x43546e88)); // 324.014f, 202.076f, 324.014f, 212.432f +path.close(); + SkPath path127(path); + builder.add(path127, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42d10265), SkBits2Float(0x434da766)); // 104.505f, 205.654f +path.quadTo(SkBits2Float(0x42d10265), SkBits2Float(0x4358025e), SkBits2Float(0x42c25d5c), SkBits2Float(0x435f54e2)); // 104.505f, 216.009f, 97.1823f, 223.332f +path.quadTo(SkBits2Float(0x42b3b854), SkBits2Float(0x4366a766), SkBits2Float(0x429f0265), SkBits2Float(0x4366a766)); // 89.86f, 230.654f, 79.5047f, 230.654f +path.quadTo(SkBits2Float(0x428a4c76), SkBits2Float(0x4366a766), SkBits2Float(0x42774edb), SkBits2Float(0x435f54e2)); // 69.1493f, 230.654f, 61.827f, 223.332f +path.quadTo(SkBits2Float(0x425a04ca), SkBits2Float(0x4358025e), SkBits2Float(0x425a04ca), SkBits2Float(0x434da766)); // 54.5047f, 216.009f, 54.5047f, 205.654f +path.quadTo(SkBits2Float(0x425a04ca), SkBits2Float(0x43434c6e), SkBits2Float(0x42774edb), SkBits2Float(0x433bf9ea)); // 54.5047f, 195.299f, 61.827f, 187.976f +path.quadTo(SkBits2Float(0x428a4c76), SkBits2Float(0x4334a766), SkBits2Float(0x429f0265), SkBits2Float(0x4334a766)); // 69.1493f, 180.654f, 79.5047f, 180.654f +path.quadTo(SkBits2Float(0x42b3b854), SkBits2Float(0x4334a766), SkBits2Float(0x42c25d5c), SkBits2Float(0x433bf9ea)); // 89.86f, 180.654f, 97.1823f, 187.976f +path.quadTo(SkBits2Float(0x42d10265), SkBits2Float(0x43434c6e), SkBits2Float(0x42d10265), SkBits2Float(0x434da766)); // 104.505f, 195.299f, 104.505f, 205.654f +path.close(); + SkPath path128(path); + builder.add(path128, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4396e5f4), SkBits2Float(0x432540f2)); // 301.797f, 165.254f +path.quadTo(SkBits2Float(0x4396e5f4), SkBits2Float(0x432f9bea), SkBits2Float(0x43933cb2), SkBits2Float(0x4336ee6e)); // 301.797f, 175.609f, 294.474f, 182.931f +path.quadTo(SkBits2Float(0x438f9370), SkBits2Float(0x433e40f2), SkBits2Float(0x438a65f4), SkBits2Float(0x433e40f2)); // 287.152f, 190.254f, 276.797f, 190.254f +path.quadTo(SkBits2Float(0x43853878), SkBits2Float(0x433e40f2), SkBits2Float(0x43818f36), SkBits2Float(0x4336ee6e)); // 266.441f, 190.254f, 259.119f, 182.931f +path.quadTo(SkBits2Float(0x437bcbe8), SkBits2Float(0x432f9bea), SkBits2Float(0x437bcbe8), SkBits2Float(0x432540f2)); // 251.797f, 175.609f, 251.797f, 165.254f +path.quadTo(SkBits2Float(0x437bcbe8), SkBits2Float(0x431ae5fa), SkBits2Float(0x43818f36), SkBits2Float(0x43139376)); // 251.797f, 154.898f, 259.119f, 147.576f +path.quadTo(SkBits2Float(0x43853878), SkBits2Float(0x430c40f2), SkBits2Float(0x438a65f4), SkBits2Float(0x430c40f2)); // 266.441f, 140.254f, 276.797f, 140.254f +path.quadTo(SkBits2Float(0x438f9370), SkBits2Float(0x430c40f2), SkBits2Float(0x43933cb2), SkBits2Float(0x43139376)); // 287.152f, 140.254f, 294.474f, 147.576f +path.quadTo(SkBits2Float(0x4396e5f4), SkBits2Float(0x431ae5fa), SkBits2Float(0x4396e5f4), SkBits2Float(0x432540f2)); // 301.797f, 154.898f, 301.797f, 165.254f +path.close(); + SkPath path129(path); + builder.add(path129, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43759ab0), SkBits2Float(0x43329c4b)); // 245.604f, 178.611f +path.quadTo(SkBits2Float(0x43759ab0), SkBits2Float(0x433cf742), SkBits2Float(0x436e482c), SkBits2Float(0x434449c7)); // 245.604f, 188.966f, 238.282f, 196.288f +path.quadTo(SkBits2Float(0x4366f5a8), SkBits2Float(0x434b9c4b), SkBits2Float(0x435c9ab0), SkBits2Float(0x434b9c4b)); // 230.96f, 203.611f, 220.604f, 203.611f +path.quadTo(SkBits2Float(0x43523fb8), SkBits2Float(0x434b9c4b), SkBits2Float(0x434aed34), SkBits2Float(0x434449c7)); // 210.249f, 203.611f, 202.927f, 196.288f +path.quadTo(SkBits2Float(0x43439ab0), SkBits2Float(0x433cf742), SkBits2Float(0x43439ab0), SkBits2Float(0x43329c4b)); // 195.604f, 188.966f, 195.604f, 178.611f +path.quadTo(SkBits2Float(0x43439ab0), SkBits2Float(0x43284154), SkBits2Float(0x434aed34), SkBits2Float(0x4320eecf)); // 195.604f, 168.255f, 202.927f, 160.933f +path.quadTo(SkBits2Float(0x43523fb8), SkBits2Float(0x43199c4b), SkBits2Float(0x435c9ab0), SkBits2Float(0x43199c4b)); // 210.249f, 153.611f, 220.604f, 153.611f +path.quadTo(SkBits2Float(0x4366f5a8), SkBits2Float(0x43199c4b), SkBits2Float(0x436e482c), SkBits2Float(0x4320eecf)); // 230.96f, 153.611f, 238.282f, 160.933f +path.quadTo(SkBits2Float(0x43759ab0), SkBits2Float(0x43284154), SkBits2Float(0x43759ab0), SkBits2Float(0x43329c4b)); // 245.604f, 168.255f, 245.604f, 178.611f +path.close(); + SkPath path130(path); + builder.add(path130, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43a6e3cc), SkBits2Float(0x43954ede)); // 333.78f, 298.616f +path.quadTo(SkBits2Float(0x43a6e3cc), SkBits2Float(0x439a7c5a), SkBits2Float(0x43a33a8a), SkBits2Float(0x439e259c)); // 333.78f, 308.971f, 326.457f, 316.294f +path.quadTo(SkBits2Float(0x439f9148), SkBits2Float(0x43a1cede), SkBits2Float(0x439a63cc), SkBits2Float(0x43a1cede)); // 319.135f, 323.616f, 308.78f, 323.616f +path.quadTo(SkBits2Float(0x43953650), SkBits2Float(0x43a1cede), SkBits2Float(0x43918d0e), SkBits2Float(0x439e259c)); // 298.424f, 323.616f, 291.102f, 316.294f +path.quadTo(SkBits2Float(0x438de3cc), SkBits2Float(0x439a7c5a), SkBits2Float(0x438de3cc), SkBits2Float(0x43954ede)); // 283.78f, 308.971f, 283.78f, 298.616f +path.quadTo(SkBits2Float(0x438de3cc), SkBits2Float(0x43902162), SkBits2Float(0x43918d0e), SkBits2Float(0x438c7820)); // 283.78f, 288.261f, 291.102f, 280.938f +path.quadTo(SkBits2Float(0x43953650), SkBits2Float(0x4388cede), SkBits2Float(0x439a63cc), SkBits2Float(0x4388cede)); // 298.424f, 273.616f, 308.78f, 273.616f +path.quadTo(SkBits2Float(0x439f9148), SkBits2Float(0x4388cede), SkBits2Float(0x43a33a8a), SkBits2Float(0x438c7820)); // 319.135f, 273.616f, 326.457f, 280.938f +path.quadTo(SkBits2Float(0x43a6e3cc), SkBits2Float(0x43902162), SkBits2Float(0x43a6e3cc), SkBits2Float(0x43954ede)); // 333.78f, 288.261f, 333.78f, 298.616f +path.close(); + SkPath path131(path); + builder.add(path131, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x428e400e), SkBits2Float(0x43a02b61)); // 71.1251f, 320.339f +path.quadTo(SkBits2Float(0x428e400e), SkBits2Float(0x43a558dd), SkBits2Float(0x427f360c), SkBits2Float(0x43a9021f)); // 71.1251f, 330.694f, 63.8028f, 338.017f +path.quadTo(SkBits2Float(0x4261ebfa), SkBits2Float(0x43acab61), SkBits2Float(0x4238801c), SkBits2Float(0x43acab61)); // 56.4804f, 345.339f, 46.1251f, 345.339f +path.quadTo(SkBits2Float(0x420f143e), SkBits2Float(0x43acab61), SkBits2Float(0x41e39459), SkBits2Float(0x43a9021f)); // 35.7698f, 345.339f, 28.4474f, 338.017f +path.quadTo(SkBits2Float(0x41a90036), SkBits2Float(0x43a558dd), SkBits2Float(0x41a90036), SkBits2Float(0x43a02b61)); // 21.1251f, 330.694f, 21.1251f, 320.339f +path.quadTo(SkBits2Float(0x41a90036), SkBits2Float(0x439afde5), SkBits2Float(0x41e39459), SkBits2Float(0x439754a3)); // 21.1251f, 309.984f, 28.4474f, 302.661f +path.quadTo(SkBits2Float(0x420f143e), SkBits2Float(0x4393ab61), SkBits2Float(0x4238801c), SkBits2Float(0x4393ab61)); // 35.7698f, 295.339f, 46.1251f, 295.339f +path.quadTo(SkBits2Float(0x4261ebfa), SkBits2Float(0x4393ab61), SkBits2Float(0x427f360c), SkBits2Float(0x439754a3)); // 56.4804f, 295.339f, 63.8028f, 302.661f +path.quadTo(SkBits2Float(0x428e400e), SkBits2Float(0x439afde5), SkBits2Float(0x428e400e), SkBits2Float(0x43a02b61)); // 71.1251f, 309.984f, 71.1251f, 320.339f +path.close(); + SkPath path132(path); + builder.add(path132, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4323b27c), SkBits2Float(0x4379f49c)); // 163.697f, 249.956f +path.quadTo(SkBits2Float(0x4323b27c), SkBits2Float(0x438227ca), SkBits2Float(0x431c5ff8), SkBits2Float(0x4385d10c)); // 163.697f, 260.311f, 156.375f, 267.633f +path.quadTo(SkBits2Float(0x43150d74), SkBits2Float(0x43897a4e), SkBits2Float(0x430ab27c), SkBits2Float(0x43897a4e)); // 149.053f, 274.956f, 138.697f, 274.956f +path.quadTo(SkBits2Float(0x43005784), SkBits2Float(0x43897a4e), SkBits2Float(0x42f20a00), SkBits2Float(0x4385d10c)); // 128.342f, 274.956f, 121.02f, 267.633f +path.quadTo(SkBits2Float(0x42e364f8), SkBits2Float(0x438227ca), SkBits2Float(0x42e364f8), SkBits2Float(0x4379f49c)); // 113.697f, 260.311f, 113.697f, 249.956f +path.quadTo(SkBits2Float(0x42e364f8), SkBits2Float(0x436f99a4), SkBits2Float(0x42f20a00), SkBits2Float(0x43684720)); // 113.697f, 239.6f, 121.02f, 232.278f +path.quadTo(SkBits2Float(0x43005784), SkBits2Float(0x4360f49c), SkBits2Float(0x430ab27c), SkBits2Float(0x4360f49c)); // 128.342f, 224.956f, 138.697f, 224.956f +path.quadTo(SkBits2Float(0x43150d74), SkBits2Float(0x4360f49c), SkBits2Float(0x431c5ff8), SkBits2Float(0x43684720)); // 149.053f, 224.956f, 156.375f, 232.278f +path.quadTo(SkBits2Float(0x4323b27c), SkBits2Float(0x436f99a4), SkBits2Float(0x4323b27c), SkBits2Float(0x4379f49c)); // 163.697f, 239.6f, 163.697f, 249.956f +path.close(); + SkPath path133(path); + builder.add(path133, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43629af6), SkBits2Float(0x434de1f1)); // 226.605f, 205.883f +path.quadTo(SkBits2Float(0x43629af6), SkBits2Float(0x43583ce8), SkBits2Float(0x435b4872), SkBits2Float(0x435f8f6d)); // 226.605f, 216.238f, 219.283f, 223.56f +path.quadTo(SkBits2Float(0x4353f5ee), SkBits2Float(0x4366e1f1), SkBits2Float(0x43499af6), SkBits2Float(0x4366e1f1)); // 211.961f, 230.883f, 201.605f, 230.883f +path.quadTo(SkBits2Float(0x433f3ffe), SkBits2Float(0x4366e1f1), SkBits2Float(0x4337ed7a), SkBits2Float(0x435f8f6d)); // 191.25f, 230.883f, 183.928f, 223.56f +path.quadTo(SkBits2Float(0x43309af6), SkBits2Float(0x43583ce8), SkBits2Float(0x43309af6), SkBits2Float(0x434de1f1)); // 176.605f, 216.238f, 176.605f, 205.883f +path.quadTo(SkBits2Float(0x43309af6), SkBits2Float(0x434386fa), SkBits2Float(0x4337ed7a), SkBits2Float(0x433c3475)); // 176.605f, 195.527f, 183.928f, 188.205f +path.quadTo(SkBits2Float(0x433f3ffe), SkBits2Float(0x4334e1f1), SkBits2Float(0x43499af6), SkBits2Float(0x4334e1f1)); // 191.25f, 180.883f, 201.605f, 180.883f +path.quadTo(SkBits2Float(0x4353f5ee), SkBits2Float(0x4334e1f1), SkBits2Float(0x435b4872), SkBits2Float(0x433c3475)); // 211.961f, 180.883f, 219.283f, 188.205f +path.quadTo(SkBits2Float(0x43629af6), SkBits2Float(0x434386fa), SkBits2Float(0x43629af6), SkBits2Float(0x434de1f1)); // 226.605f, 195.527f, 226.605f, 205.883f +path.close(); + SkPath path134(path); + builder.add(path134, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43969933), SkBits2Float(0x00000000)); // 301.197f, 0 +path.quadTo(SkBits2Float(0x43969933), SkBits2Float(0x4125af78), SkBits2Float(0x4392eff1), SkBits2Float(0x418d6bde)); // 301.197f, 10.3553f, 293.875f, 17.6777f +path.quadTo(SkBits2Float(0x438f46af), SkBits2Float(0x41c80000), SkBits2Float(0x438a1933), SkBits2Float(0x41c80000)); // 286.552f, 25, 276.197f, 25 +path.quadTo(SkBits2Float(0x4384ebb7), SkBits2Float(0x41c80000), SkBits2Float(0x43814275), SkBits2Float(0x418d6bde)); // 265.842f, 25, 258.519f, 17.6777f +path.quadTo(SkBits2Float(0x437b3266), SkBits2Float(0x4125af78), SkBits2Float(0x437b3266), SkBits2Float(0x00000000)); // 251.197f, 10.3553f, 251.197f, 0 +path.quadTo(SkBits2Float(0x437b3266), SkBits2Float(0xc125af78), SkBits2Float(0x43814275), SkBits2Float(0xc18d6bde)); // 251.197f, -10.3553f, 258.519f, -17.6777f +path.quadTo(SkBits2Float(0x4384ebb7), SkBits2Float(0xc1c80000), SkBits2Float(0x438a1933), SkBits2Float(0xc1c80000)); // 265.842f, -25, 276.197f, -25 +path.quadTo(SkBits2Float(0x438f46af), SkBits2Float(0xc1c80000), SkBits2Float(0x4392eff1), SkBits2Float(0xc18d6bde)); // 286.552f, -25, 293.875f, -17.6777f +path.quadTo(SkBits2Float(0x43969933), SkBits2Float(0xc125af78), SkBits2Float(0x43969933), SkBits2Float(0x00000000)); // 301.197f, -10.3553f, 301.197f, 0 +path.close(); + SkPath path135(path); + builder.add(path135, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43310d09), SkBits2Float(0x43ab2014)); // 177.051f, 342.251f +path.quadTo(SkBits2Float(0x43310d09), SkBits2Float(0x43b04d90), SkBits2Float(0x4329ba85), SkBits2Float(0x43b3f6d2)); // 177.051f, 352.606f, 169.729f, 359.928f +path.quadTo(SkBits2Float(0x43226800), SkBits2Float(0x43b7a014), SkBits2Float(0x43180d09), SkBits2Float(0x43b7a014)); // 162.406f, 367.251f, 152.051f, 367.251f +path.quadTo(SkBits2Float(0x430db212), SkBits2Float(0x43b7a014), SkBits2Float(0x43065f8d), SkBits2Float(0x43b3f6d2)); // 141.696f, 367.251f, 134.373f, 359.928f +path.quadTo(SkBits2Float(0x42fe1a12), SkBits2Float(0x43b04d90), SkBits2Float(0x42fe1a12), SkBits2Float(0x43ab2014)); // 127.051f, 352.606f, 127.051f, 342.251f +path.quadTo(SkBits2Float(0x42fe1a12), SkBits2Float(0x43a5f298), SkBits2Float(0x43065f8d), SkBits2Float(0x43a24956)); // 127.051f, 331.895f, 134.373f, 324.573f +path.quadTo(SkBits2Float(0x430db212), SkBits2Float(0x439ea014), SkBits2Float(0x43180d09), SkBits2Float(0x439ea014)); // 141.696f, 317.251f, 152.051f, 317.251f +path.quadTo(SkBits2Float(0x43226800), SkBits2Float(0x439ea014), SkBits2Float(0x4329ba85), SkBits2Float(0x43a24956)); // 162.406f, 317.251f, 169.729f, 324.573f +path.quadTo(SkBits2Float(0x43310d09), SkBits2Float(0x43a5f298), SkBits2Float(0x43310d09), SkBits2Float(0x43ab2014)); // 177.051f, 331.895f, 177.051f, 342.251f +path.close(); + SkPath path136(path); + builder.add(path136, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43c19844), SkBits2Float(0x43818fc9)); // 387.19f, 259.123f +path.quadTo(SkBits2Float(0x43c19844), SkBits2Float(0x4386bd45), SkBits2Float(0x43bdef02), SkBits2Float(0x438a6687)); // 387.19f, 269.479f, 379.867f, 276.801f +path.quadTo(SkBits2Float(0x43ba45c0), SkBits2Float(0x438e0fc9), SkBits2Float(0x43b51844), SkBits2Float(0x438e0fc9)); // 372.545f, 284.123f, 362.19f, 284.123f +path.quadTo(SkBits2Float(0x43afeac8), SkBits2Float(0x438e0fc9), SkBits2Float(0x43ac4186), SkBits2Float(0x438a6687)); // 351.834f, 284.123f, 344.512f, 276.801f +path.quadTo(SkBits2Float(0x43a89844), SkBits2Float(0x4386bd45), SkBits2Float(0x43a89844), SkBits2Float(0x43818fc9)); // 337.19f, 269.479f, 337.19f, 259.123f +path.quadTo(SkBits2Float(0x43a89844), SkBits2Float(0x4378c49a), SkBits2Float(0x43ac4186), SkBits2Float(0x43717216)); // 337.19f, 248.768f, 344.512f, 241.446f +path.quadTo(SkBits2Float(0x43afeac8), SkBits2Float(0x436a1f92), SkBits2Float(0x43b51844), SkBits2Float(0x436a1f92)); // 351.834f, 234.123f, 362.19f, 234.123f +path.quadTo(SkBits2Float(0x43ba45c0), SkBits2Float(0x436a1f92), SkBits2Float(0x43bdef02), SkBits2Float(0x43717216)); // 372.545f, 234.123f, 379.867f, 241.446f +path.quadTo(SkBits2Float(0x43c19844), SkBits2Float(0x4378c49a), SkBits2Float(0x43c19844), SkBits2Float(0x43818fc9)); // 387.19f, 248.768f, 387.19f, 259.123f +path.close(); + SkPath path137(path); + builder.add(path137, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43565ef5), SkBits2Float(0x42de6e20)); // 214.371f, 111.215f +path.quadTo(SkBits2Float(0x43565ef5), SkBits2Float(0x42f3240f), SkBits2Float(0x434f0c71), SkBits2Float(0x4300e48c)); // 214.371f, 121.57f, 207.049f, 128.893f +path.quadTo(SkBits2Float(0x4347b9ec), SkBits2Float(0x43083710), SkBits2Float(0x433d5ef5), SkBits2Float(0x43083710)); // 199.726f, 136.215f, 189.371f, 136.215f +path.quadTo(SkBits2Float(0x433303fe), SkBits2Float(0x43083710), SkBits2Float(0x432bb179), SkBits2Float(0x4300e48c)); // 179.016f, 136.215f, 171.693f, 128.893f +path.quadTo(SkBits2Float(0x43245ef5), SkBits2Float(0x42f3240f), SkBits2Float(0x43245ef5), SkBits2Float(0x42de6e20)); // 164.371f, 121.57f, 164.371f, 111.215f +path.quadTo(SkBits2Float(0x43245ef5), SkBits2Float(0x42c9b831), SkBits2Float(0x432bb179), SkBits2Float(0x42bb1329)); // 164.371f, 100.86f, 171.693f, 93.5374f +path.quadTo(SkBits2Float(0x433303fe), SkBits2Float(0x42ac6e21), SkBits2Float(0x433d5ef5), SkBits2Float(0x42ac6e21)); // 179.016f, 86.2151f, 189.371f, 86.2151f +path.quadTo(SkBits2Float(0x4347b9ec), SkBits2Float(0x42ac6e21), SkBits2Float(0x434f0c71), SkBits2Float(0x42bb1329)); // 199.726f, 86.2151f, 207.049f, 93.5374f +path.quadTo(SkBits2Float(0x43565ef5), SkBits2Float(0x42c9b831), SkBits2Float(0x43565ef5), SkBits2Float(0x42de6e20)); // 214.371f, 100.86f, 214.371f, 111.215f +path.close(); + SkPath path138(path); + builder.add(path138, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43e8a73d), SkBits2Float(0x439f6574)); // 465.307f, 318.793f +path.quadTo(SkBits2Float(0x43e8a73d), SkBits2Float(0x43a492f0), SkBits2Float(0x43e4fdfb), SkBits2Float(0x43a83c32)); // 465.307f, 329.148f, 457.984f, 336.47f +path.quadTo(SkBits2Float(0x43e154b9), SkBits2Float(0x43abe574), SkBits2Float(0x43dc273d), SkBits2Float(0x43abe574)); // 450.662f, 343.793f, 440.307f, 343.793f +path.quadTo(SkBits2Float(0x43d6f9c1), SkBits2Float(0x43abe574), SkBits2Float(0x43d3507f), SkBits2Float(0x43a83c32)); // 429.951f, 343.793f, 422.629f, 336.47f +path.quadTo(SkBits2Float(0x43cfa73d), SkBits2Float(0x43a492f0), SkBits2Float(0x43cfa73d), SkBits2Float(0x439f6574)); // 415.307f, 329.148f, 415.307f, 318.793f +path.quadTo(SkBits2Float(0x43cfa73d), SkBits2Float(0x439a37f8), SkBits2Float(0x43d3507f), SkBits2Float(0x43968eb6)); // 415.307f, 308.437f, 422.629f, 301.115f +path.quadTo(SkBits2Float(0x43d6f9c1), SkBits2Float(0x4392e574), SkBits2Float(0x43dc273d), SkBits2Float(0x4392e574)); // 429.951f, 293.793f, 440.307f, 293.793f +path.quadTo(SkBits2Float(0x43e154b9), SkBits2Float(0x4392e574), SkBits2Float(0x43e4fdfb), SkBits2Float(0x43968eb6)); // 450.662f, 293.793f, 457.984f, 301.115f +path.quadTo(SkBits2Float(0x43e8a73d), SkBits2Float(0x439a37f8), SkBits2Float(0x43e8a73d), SkBits2Float(0x439f6574)); // 465.307f, 308.437f, 465.307f, 318.793f +path.close(); + SkPath path139(path); + builder.add(path139, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43abcc05), SkBits2Float(0x42d3af00)); // 343.594f, 105.842f +path.quadTo(SkBits2Float(0x43abcc05), SkBits2Float(0x42e864ef), SkBits2Float(0x43a822c3), SkBits2Float(0x42f709f8)); // 343.594f, 116.197f, 336.272f, 123.519f +path.quadTo(SkBits2Float(0x43a47981), SkBits2Float(0x4302d780), SkBits2Float(0x439f4c05), SkBits2Float(0x4302d780)); // 328.949f, 130.842f, 318.594f, 130.842f +path.quadTo(SkBits2Float(0x439a1e89), SkBits2Float(0x4302d780), SkBits2Float(0x43967547), SkBits2Float(0x42f709f8)); // 308.239f, 130.842f, 300.916f, 123.519f +path.quadTo(SkBits2Float(0x4392cc05), SkBits2Float(0x42e864ef), SkBits2Float(0x4392cc05), SkBits2Float(0x42d3af00)); // 293.594f, 116.197f, 293.594f, 105.842f +path.quadTo(SkBits2Float(0x4392cc05), SkBits2Float(0x42bef911), SkBits2Float(0x43967547), SkBits2Float(0x42b05408)); // 293.594f, 95.4865f, 300.916f, 88.1641f +path.quadTo(SkBits2Float(0x439a1e89), SkBits2Float(0x42a1aeff), SkBits2Float(0x439f4c05), SkBits2Float(0x42a1aeff)); // 308.239f, 80.8418f, 318.594f, 80.8418f +path.quadTo(SkBits2Float(0x43a47981), SkBits2Float(0x42a1aeff), SkBits2Float(0x43a822c3), SkBits2Float(0x42b05408)); // 328.949f, 80.8418f, 336.272f, 88.1641f +path.quadTo(SkBits2Float(0x43abcc05), SkBits2Float(0x42bef911), SkBits2Float(0x43abcc05), SkBits2Float(0x42d3af00)); // 343.594f, 95.4865f, 343.594f, 105.842f +path.close(); + SkPath path140(path); + builder.add(path140, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x438e8ffa), SkBits2Float(0x438bdd2b)); // 285.125f, 279.728f +path.quadTo(SkBits2Float(0x438e8ffa), SkBits2Float(0x43910aa7), SkBits2Float(0x438ae6b8), SkBits2Float(0x4394b3e9)); // 285.125f, 290.083f, 277.802f, 297.406f +path.quadTo(SkBits2Float(0x43873d76), SkBits2Float(0x43985d2b), SkBits2Float(0x43820ffa), SkBits2Float(0x43985d2b)); // 270.48f, 304.728f, 260.125f, 304.728f +path.quadTo(SkBits2Float(0x4379c4fc), SkBits2Float(0x43985d2b), SkBits2Float(0x43727278), SkBits2Float(0x4394b3e9)); // 249.769f, 304.728f, 242.447f, 297.406f +path.quadTo(SkBits2Float(0x436b1ff4), SkBits2Float(0x43910aa7), SkBits2Float(0x436b1ff4), SkBits2Float(0x438bdd2b)); // 235.125f, 290.083f, 235.125f, 279.728f +path.quadTo(SkBits2Float(0x436b1ff4), SkBits2Float(0x4386afaf), SkBits2Float(0x43727278), SkBits2Float(0x4383066d)); // 235.125f, 269.373f, 242.447f, 262.05f +path.quadTo(SkBits2Float(0x4379c4fc), SkBits2Float(0x437eba56), SkBits2Float(0x43820ffa), SkBits2Float(0x437eba56)); // 249.769f, 254.728f, 260.125f, 254.728f +path.quadTo(SkBits2Float(0x43873d76), SkBits2Float(0x437eba56), SkBits2Float(0x438ae6b8), SkBits2Float(0x4383066d)); // 270.48f, 254.728f, 277.802f, 262.05f +path.quadTo(SkBits2Float(0x438e8ffa), SkBits2Float(0x4386afaf), SkBits2Float(0x438e8ffa), SkBits2Float(0x438bdd2b)); // 285.125f, 269.373f, 285.125f, 279.728f +path.close(); + SkPath path141(path); + builder.add(path141, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x441d8000), SkBits2Float(0x435e8072)); // 630, 222.502f +path.quadTo(SkBits2Float(0x441d8000), SkBits2Float(0x4368db6a), SkBits2Float(0x441bab5f), SkBits2Float(0x43702dee)); // 630, 232.857f, 622.678f, 240.179f +path.quadTo(SkBits2Float(0x4419d6be), SkBits2Float(0x43778072), SkBits2Float(0x44174000), SkBits2Float(0x43778072)); // 615.355f, 247.502f, 605, 247.502f +path.quadTo(SkBits2Float(0x4414a942), SkBits2Float(0x43778072), SkBits2Float(0x4412d4a1), SkBits2Float(0x43702dee)); // 594.645f, 247.502f, 587.322f, 240.179f +path.quadTo(SkBits2Float(0x44110000), SkBits2Float(0x4368db6a), SkBits2Float(0x44110000), SkBits2Float(0x435e8072)); // 580, 232.857f, 580, 222.502f +path.quadTo(SkBits2Float(0x44110000), SkBits2Float(0x4354257a), SkBits2Float(0x4412d4a1), SkBits2Float(0x434cd2f6)); // 580, 212.146f, 587.322f, 204.824f +path.quadTo(SkBits2Float(0x4414a942), SkBits2Float(0x43458072), SkBits2Float(0x44174000), SkBits2Float(0x43458072)); // 594.645f, 197.502f, 605, 197.502f +path.quadTo(SkBits2Float(0x4419d6be), SkBits2Float(0x43458072), SkBits2Float(0x441bab5f), SkBits2Float(0x434cd2f6)); // 615.355f, 197.502f, 622.678f, 204.824f +path.quadTo(SkBits2Float(0x441d8000), SkBits2Float(0x4354257a), SkBits2Float(0x441d8000), SkBits2Float(0x435e8072)); // 630, 212.146f, 630, 222.502f +path.close(); + SkPath path142(path); + builder.add(path142, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43cab007), SkBits2Float(0x4370ffc2)); // 405.375f, 240.999f +path.quadTo(SkBits2Float(0x43cab007), SkBits2Float(0x437b5aba), SkBits2Float(0x43c706c5), SkBits2Float(0x4381569f)); // 405.375f, 251.354f, 398.053f, 258.677f +path.quadTo(SkBits2Float(0x43c35d83), SkBits2Float(0x4384ffe1), SkBits2Float(0x43be3007), SkBits2Float(0x4384ffe1)); // 390.731f, 265.999f, 380.375f, 265.999f +path.quadTo(SkBits2Float(0x43b9028b), SkBits2Float(0x4384ffe1), SkBits2Float(0x43b55949), SkBits2Float(0x4381569f)); // 370.02f, 265.999f, 362.698f, 258.677f +path.quadTo(SkBits2Float(0x43b1b007), SkBits2Float(0x437b5aba), SkBits2Float(0x43b1b007), SkBits2Float(0x4370ffc2)); // 355.375f, 251.354f, 355.375f, 240.999f +path.quadTo(SkBits2Float(0x43b1b007), SkBits2Float(0x4366a4ca), SkBits2Float(0x43b55949), SkBits2Float(0x435f5246)); // 355.375f, 230.644f, 362.698f, 223.321f +path.quadTo(SkBits2Float(0x43b9028b), SkBits2Float(0x4357ffc2), SkBits2Float(0x43be3007), SkBits2Float(0x4357ffc2)); // 370.02f, 215.999f, 380.375f, 215.999f +path.quadTo(SkBits2Float(0x43c35d83), SkBits2Float(0x4357ffc2), SkBits2Float(0x43c706c5), SkBits2Float(0x435f5246)); // 390.731f, 215.999f, 398.053f, 223.321f +path.quadTo(SkBits2Float(0x43cab007), SkBits2Float(0x4366a4ca), SkBits2Float(0x43cab007), SkBits2Float(0x4370ffc2)); // 405.375f, 230.644f, 405.375f, 240.999f +path.close(); + SkPath path143(path); + builder.add(path143, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43ae2f7d), SkBits2Float(0x43587ea0)); // 348.371f, 216.495f +path.quadTo(SkBits2Float(0x43ae2f7d), SkBits2Float(0x4362d998), SkBits2Float(0x43aa863b), SkBits2Float(0x436a2c1c)); // 348.371f, 226.85f, 341.049f, 234.172f +path.quadTo(SkBits2Float(0x43a6dcf9), SkBits2Float(0x43717ea0), SkBits2Float(0x43a1af7d), SkBits2Float(0x43717ea0)); // 333.726f, 241.495f, 323.371f, 241.495f +path.quadTo(SkBits2Float(0x439c8201), SkBits2Float(0x43717ea0), SkBits2Float(0x4398d8bf), SkBits2Float(0x436a2c1c)); // 313.016f, 241.495f, 305.693f, 234.172f +path.quadTo(SkBits2Float(0x43952f7d), SkBits2Float(0x4362d998), SkBits2Float(0x43952f7d), SkBits2Float(0x43587ea0)); // 298.371f, 226.85f, 298.371f, 216.495f +path.quadTo(SkBits2Float(0x43952f7d), SkBits2Float(0x434e23a8), SkBits2Float(0x4398d8bf), SkBits2Float(0x4346d124)); // 298.371f, 206.139f, 305.693f, 198.817f +path.quadTo(SkBits2Float(0x439c8201), SkBits2Float(0x433f7ea0), SkBits2Float(0x43a1af7d), SkBits2Float(0x433f7ea0)); // 313.016f, 191.495f, 323.371f, 191.495f +path.quadTo(SkBits2Float(0x43a6dcf9), SkBits2Float(0x433f7ea0), SkBits2Float(0x43aa863b), SkBits2Float(0x4346d124)); // 333.726f, 191.495f, 341.049f, 198.817f +path.quadTo(SkBits2Float(0x43ae2f7d), SkBits2Float(0x434e23a8), SkBits2Float(0x43ae2f7d), SkBits2Float(0x43587ea0)); // 348.371f, 206.139f, 348.371f, 216.495f +path.close(); + SkPath path144(path); + builder.add(path144, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43add1a9), SkBits2Float(0x436897ac)); // 347.638f, 232.592f +path.quadTo(SkBits2Float(0x43add1a9), SkBits2Float(0x4372f2a3), SkBits2Float(0x43aa2867), SkBits2Float(0x437a4527)); // 347.638f, 242.948f, 340.316f, 250.27f +path.quadTo(SkBits2Float(0x43a67f25), SkBits2Float(0x4380cbd6), SkBits2Float(0x43a151a9), SkBits2Float(0x4380cbd6)); // 332.993f, 257.592f, 322.638f, 257.592f +path.quadTo(SkBits2Float(0x439c242d), SkBits2Float(0x4380cbd6), SkBits2Float(0x43987aeb), SkBits2Float(0x437a4527)); // 312.283f, 257.592f, 304.96f, 250.27f +path.quadTo(SkBits2Float(0x4394d1a9), SkBits2Float(0x4372f2a3), SkBits2Float(0x4394d1a9), SkBits2Float(0x436897ac)); // 297.638f, 242.948f, 297.638f, 232.592f +path.quadTo(SkBits2Float(0x4394d1a9), SkBits2Float(0x435e3cb5), SkBits2Float(0x43987aeb), SkBits2Float(0x4356ea31)); // 297.638f, 222.237f, 304.96f, 214.915f +path.quadTo(SkBits2Float(0x439c242d), SkBits2Float(0x434f97ad), SkBits2Float(0x43a151a9), SkBits2Float(0x434f97ad)); // 312.283f, 207.592f, 322.638f, 207.592f +path.quadTo(SkBits2Float(0x43a67f25), SkBits2Float(0x434f97ad), SkBits2Float(0x43aa2867), SkBits2Float(0x4356ea31)); // 332.993f, 207.592f, 340.316f, 214.915f +path.quadTo(SkBits2Float(0x43add1a9), SkBits2Float(0x435e3cb5), SkBits2Float(0x43add1a9), SkBits2Float(0x436897ac)); // 347.638f, 222.237f, 347.638f, 232.592f +path.close(); + SkPath path145(path); + builder.add(path145, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43f37c5c), SkBits2Float(0x438cffd4)); // 486.972f, 281.999f +path.quadTo(SkBits2Float(0x43f37c5c), SkBits2Float(0x43922d50), SkBits2Float(0x43efd31a), SkBits2Float(0x4395d692)); // 486.972f, 292.354f, 479.649f, 299.676f +path.quadTo(SkBits2Float(0x43ec29d8), SkBits2Float(0x43997fd4), SkBits2Float(0x43e6fc5c), SkBits2Float(0x43997fd4)); // 472.327f, 306.999f, 461.972f, 306.999f +path.quadTo(SkBits2Float(0x43e1cee0), SkBits2Float(0x43997fd4), SkBits2Float(0x43de259e), SkBits2Float(0x4395d692)); // 451.616f, 306.999f, 444.294f, 299.676f +path.quadTo(SkBits2Float(0x43da7c5c), SkBits2Float(0x43922d50), SkBits2Float(0x43da7c5c), SkBits2Float(0x438cffd4)); // 436.972f, 292.354f, 436.972f, 281.999f +path.quadTo(SkBits2Float(0x43da7c5c), SkBits2Float(0x4387d258), SkBits2Float(0x43de259e), SkBits2Float(0x43842916)); // 436.972f, 271.643f, 444.294f, 264.321f +path.quadTo(SkBits2Float(0x43e1cee0), SkBits2Float(0x43807fd4), SkBits2Float(0x43e6fc5c), SkBits2Float(0x43807fd4)); // 451.616f, 256.999f, 461.972f, 256.999f +path.quadTo(SkBits2Float(0x43ec29d8), SkBits2Float(0x43807fd4), SkBits2Float(0x43efd31a), SkBits2Float(0x43842916)); // 472.327f, 256.999f, 479.649f, 264.321f +path.quadTo(SkBits2Float(0x43f37c5c), SkBits2Float(0x4387d258), SkBits2Float(0x43f37c5c), SkBits2Float(0x438cffd4)); // 486.972f, 271.643f, 486.972f, 281.999f +path.close(); + SkPath path146(path); + builder.add(path146, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x434938a6), SkBits2Float(0x439b6682)); // 201.221f, 310.801f +path.quadTo(SkBits2Float(0x434938a6), SkBits2Float(0x43a093fe), SkBits2Float(0x4341e622), SkBits2Float(0x43a43d40)); // 201.221f, 321.156f, 193.899f, 328.479f +path.quadTo(SkBits2Float(0x433a939e), SkBits2Float(0x43a7e682), SkBits2Float(0x433038a6), SkBits2Float(0x43a7e682)); // 186.577f, 335.801f, 176.221f, 335.801f +path.quadTo(SkBits2Float(0x4325ddae), SkBits2Float(0x43a7e682), SkBits2Float(0x431e8b2a), SkBits2Float(0x43a43d40)); // 165.866f, 335.801f, 158.544f, 328.479f +path.quadTo(SkBits2Float(0x431738a6), SkBits2Float(0x43a093fe), SkBits2Float(0x431738a6), SkBits2Float(0x439b6682)); // 151.221f, 321.156f, 151.221f, 310.801f +path.quadTo(SkBits2Float(0x431738a6), SkBits2Float(0x43963906), SkBits2Float(0x431e8b2a), SkBits2Float(0x43928fc4)); // 151.221f, 300.445f, 158.544f, 293.123f +path.quadTo(SkBits2Float(0x4325ddae), SkBits2Float(0x438ee682), SkBits2Float(0x433038a6), SkBits2Float(0x438ee682)); // 165.866f, 285.801f, 176.221f, 285.801f +path.quadTo(SkBits2Float(0x433a939e), SkBits2Float(0x438ee682), SkBits2Float(0x4341e622), SkBits2Float(0x43928fc4)); // 186.577f, 285.801f, 193.899f, 293.123f +path.quadTo(SkBits2Float(0x434938a6), SkBits2Float(0x43963906), SkBits2Float(0x434938a6), SkBits2Float(0x439b6682)); // 201.221f, 300.445f, 201.221f, 310.801f +path.close(); + SkPath path147(path); + builder.add(path147, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43957ad9), SkBits2Float(0x4340d6a9)); // 298.96f, 192.839f +path.quadTo(SkBits2Float(0x43957ad9), SkBits2Float(0x434b31a0), SkBits2Float(0x4391d197), SkBits2Float(0x43528425)); // 298.96f, 203.194f, 291.637f, 210.516f +path.quadTo(SkBits2Float(0x438e2855), SkBits2Float(0x4359d6a9), SkBits2Float(0x4388fad9), SkBits2Float(0x4359d6a9)); // 284.315f, 217.839f, 273.96f, 217.839f +path.quadTo(SkBits2Float(0x4383cd5d), SkBits2Float(0x4359d6a9), SkBits2Float(0x4380241b), SkBits2Float(0x43528425)); // 263.604f, 217.839f, 256.282f, 210.516f +path.quadTo(SkBits2Float(0x4378f5b2), SkBits2Float(0x434b31a0), SkBits2Float(0x4378f5b2), SkBits2Float(0x4340d6a9)); // 248.96f, 203.194f, 248.96f, 192.839f +path.quadTo(SkBits2Float(0x4378f5b2), SkBits2Float(0x43367bb2), SkBits2Float(0x4380241b), SkBits2Float(0x432f292d)); // 248.96f, 182.483f, 256.282f, 175.161f +path.quadTo(SkBits2Float(0x4383cd5d), SkBits2Float(0x4327d6a9), SkBits2Float(0x4388fad9), SkBits2Float(0x4327d6a9)); // 263.604f, 167.839f, 273.96f, 167.839f +path.quadTo(SkBits2Float(0x438e2855), SkBits2Float(0x4327d6a9), SkBits2Float(0x4391d197), SkBits2Float(0x432f292d)); // 284.315f, 167.839f, 291.637f, 175.161f +path.quadTo(SkBits2Float(0x43957ad9), SkBits2Float(0x43367bb2), SkBits2Float(0x43957ad9), SkBits2Float(0x4340d6a9)); // 298.96f, 182.483f, 298.96f, 192.839f +path.close(); + SkPath path148(path); + builder.add(path148, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4359cdc3), SkBits2Float(0x431623e6)); // 217.804f, 150.14f +path.quadTo(SkBits2Float(0x4359cdc3), SkBits2Float(0x43207ede), SkBits2Float(0x43527b3f), SkBits2Float(0x4327d162)); // 217.804f, 160.496f, 210.481f, 167.818f +path.quadTo(SkBits2Float(0x434b28ba), SkBits2Float(0x432f23e6), SkBits2Float(0x4340cdc3), SkBits2Float(0x432f23e6)); // 203.159f, 175.14f, 192.804f, 175.14f +path.quadTo(SkBits2Float(0x433672cc), SkBits2Float(0x432f23e6), SkBits2Float(0x432f2047), SkBits2Float(0x4327d162)); // 182.448f, 175.14f, 175.126f, 167.818f +path.quadTo(SkBits2Float(0x4327cdc3), SkBits2Float(0x43207ede), SkBits2Float(0x4327cdc3), SkBits2Float(0x431623e6)); // 167.804f, 160.496f, 167.804f, 150.14f +path.quadTo(SkBits2Float(0x4327cdc3), SkBits2Float(0x430bc8ee), SkBits2Float(0x432f2047), SkBits2Float(0x4304766a)); // 167.804f, 139.785f, 175.126f, 132.463f +path.quadTo(SkBits2Float(0x433672cc), SkBits2Float(0x42fa47cc), SkBits2Float(0x4340cdc3), SkBits2Float(0x42fa47cc)); // 182.448f, 125.14f, 192.804f, 125.14f +path.quadTo(SkBits2Float(0x434b28ba), SkBits2Float(0x42fa47cc), SkBits2Float(0x43527b3f), SkBits2Float(0x4304766a)); // 203.159f, 125.14f, 210.481f, 132.463f +path.quadTo(SkBits2Float(0x4359cdc3), SkBits2Float(0x430bc8ee), SkBits2Float(0x4359cdc3), SkBits2Float(0x431623e6)); // 217.804f, 139.785f, 217.804f, 150.14f +path.close(); + SkPath path149(path); + builder.add(path149, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4395d72b), SkBits2Float(0x43c5524a)); // 299.681f, 394.643f +path.quadTo(SkBits2Float(0x4395d72b), SkBits2Float(0x43ca7fc6), SkBits2Float(0x43922de9), SkBits2Float(0x43ce2908)); // 299.681f, 404.998f, 292.359f, 412.321f +path.quadTo(SkBits2Float(0x438e84a7), SkBits2Float(0x43d1d24a), SkBits2Float(0x4389572b), SkBits2Float(0x43d1d24a)); // 285.036f, 419.643f, 274.681f, 419.643f +path.quadTo(SkBits2Float(0x438429af), SkBits2Float(0x43d1d24a), SkBits2Float(0x4380806d), SkBits2Float(0x43ce2908)); // 264.326f, 419.643f, 257.003f, 412.321f +path.quadTo(SkBits2Float(0x4379ae56), SkBits2Float(0x43ca7fc6), SkBits2Float(0x4379ae56), SkBits2Float(0x43c5524a)); // 249.681f, 404.998f, 249.681f, 394.643f +path.quadTo(SkBits2Float(0x4379ae56), SkBits2Float(0x43c024ce), SkBits2Float(0x4380806d), SkBits2Float(0x43bc7b8c)); // 249.681f, 384.288f, 257.003f, 376.965f +path.quadTo(SkBits2Float(0x438429af), SkBits2Float(0x43b8d24a), SkBits2Float(0x4389572b), SkBits2Float(0x43b8d24a)); // 264.326f, 369.643f, 274.681f, 369.643f +path.quadTo(SkBits2Float(0x438e84a7), SkBits2Float(0x43b8d24a), SkBits2Float(0x43922de9), SkBits2Float(0x43bc7b8c)); // 285.036f, 369.643f, 292.359f, 376.965f +path.quadTo(SkBits2Float(0x4395d72b), SkBits2Float(0x43c024ce), SkBits2Float(0x4395d72b), SkBits2Float(0x43c5524a)); // 299.681f, 384.288f, 299.681f, 394.643f +path.close(); + SkPath path150(path); + builder.add(path150, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43884f5e), SkBits2Float(0x436c6388)); // 272.62f, 236.389f +path.quadTo(SkBits2Float(0x43884f5e), SkBits2Float(0x4376be7f), SkBits2Float(0x4384a61c), SkBits2Float(0x437e1103)); // 272.62f, 246.744f, 265.298f, 254.066f +path.quadTo(SkBits2Float(0x4380fcda), SkBits2Float(0x4382b1c4), SkBits2Float(0x43779ebc), SkBits2Float(0x4382b1c4)); // 257.975f, 261.389f, 247.62f, 261.389f +path.quadTo(SkBits2Float(0x436d43c5), SkBits2Float(0x4382b1c4), SkBits2Float(0x4365f141), SkBits2Float(0x437e1103)); // 237.265f, 261.389f, 229.942f, 254.066f +path.quadTo(SkBits2Float(0x435e9ebd), SkBits2Float(0x4376be7f), SkBits2Float(0x435e9ebd), SkBits2Float(0x436c6388)); // 222.62f, 246.744f, 222.62f, 236.389f +path.quadTo(SkBits2Float(0x435e9ebd), SkBits2Float(0x43620891), SkBits2Float(0x4365f141), SkBits2Float(0x435ab60d)); // 222.62f, 226.033f, 229.942f, 218.711f +path.quadTo(SkBits2Float(0x436d43c5), SkBits2Float(0x43536389), SkBits2Float(0x43779ebc), SkBits2Float(0x43536389)); // 237.265f, 211.389f, 247.62f, 211.389f +path.quadTo(SkBits2Float(0x4380fcda), SkBits2Float(0x43536389), SkBits2Float(0x4384a61c), SkBits2Float(0x435ab60d)); // 257.975f, 211.389f, 265.298f, 218.711f +path.quadTo(SkBits2Float(0x43884f5e), SkBits2Float(0x43620891), SkBits2Float(0x43884f5e), SkBits2Float(0x436c6388)); // 272.62f, 226.033f, 272.62f, 236.389f +path.close(); + SkPath path151(path); + builder.add(path151, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43ac3331), SkBits2Float(0x4330c484)); // 344.4f, 176.768f +path.quadTo(SkBits2Float(0x43ac3331), SkBits2Float(0x433b1f7c), SkBits2Float(0x43a889ef), SkBits2Float(0x43427200)); // 344.4f, 187.123f, 337.078f, 194.445f +path.quadTo(SkBits2Float(0x43a4e0ad), SkBits2Float(0x4349c484), SkBits2Float(0x439fb331), SkBits2Float(0x4349c484)); // 329.755f, 201.768f, 319.4f, 201.768f +path.quadTo(SkBits2Float(0x439a85b5), SkBits2Float(0x4349c484), SkBits2Float(0x4396dc73), SkBits2Float(0x43427200)); // 309.045f, 201.768f, 301.722f, 194.445f +path.quadTo(SkBits2Float(0x43933331), SkBits2Float(0x433b1f7c), SkBits2Float(0x43933331), SkBits2Float(0x4330c484)); // 294.4f, 187.123f, 294.4f, 176.768f +path.quadTo(SkBits2Float(0x43933331), SkBits2Float(0x4326698c), SkBits2Float(0x4396dc73), SkBits2Float(0x431f1708)); // 294.4f, 166.412f, 301.722f, 159.09f +path.quadTo(SkBits2Float(0x439a85b5), SkBits2Float(0x4317c484), SkBits2Float(0x439fb331), SkBits2Float(0x4317c484)); // 309.045f, 151.768f, 319.4f, 151.768f +path.quadTo(SkBits2Float(0x43a4e0ad), SkBits2Float(0x4317c484), SkBits2Float(0x43a889ef), SkBits2Float(0x431f1708)); // 329.755f, 151.768f, 337.078f, 159.09f +path.quadTo(SkBits2Float(0x43ac3331), SkBits2Float(0x4326698c), SkBits2Float(0x43ac3331), SkBits2Float(0x4330c484)); // 344.4f, 166.412f, 344.4f, 176.768f +path.close(); + SkPath path152(path); + builder.add(path152, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x435659f0), SkBits2Float(0x438a1820)); // 214.351f, 276.188f +path.quadTo(SkBits2Float(0x435659f0), SkBits2Float(0x438f459c), SkBits2Float(0x434f076c), SkBits2Float(0x4392eede)); // 214.351f, 286.544f, 207.029f, 293.866f +path.quadTo(SkBits2Float(0x4347b4e8), SkBits2Float(0x43969820), SkBits2Float(0x433d59f0), SkBits2Float(0x43969820)); // 199.707f, 301.188f, 189.351f, 301.188f +path.quadTo(SkBits2Float(0x4332fef8), SkBits2Float(0x43969820), SkBits2Float(0x432bac74), SkBits2Float(0x4392eede)); // 178.996f, 301.188f, 171.674f, 293.866f +path.quadTo(SkBits2Float(0x432459f0), SkBits2Float(0x438f459c), SkBits2Float(0x432459f0), SkBits2Float(0x438a1820)); // 164.351f, 286.544f, 164.351f, 276.188f +path.quadTo(SkBits2Float(0x432459f0), SkBits2Float(0x4384eaa4), SkBits2Float(0x432bac74), SkBits2Float(0x43814162)); // 164.351f, 265.833f, 171.674f, 258.511f +path.quadTo(SkBits2Float(0x4332fef8), SkBits2Float(0x437b3040), SkBits2Float(0x433d59f0), SkBits2Float(0x437b3040)); // 178.996f, 251.188f, 189.351f, 251.188f +path.quadTo(SkBits2Float(0x4347b4e8), SkBits2Float(0x437b3040), SkBits2Float(0x434f076c), SkBits2Float(0x43814162)); // 199.707f, 251.188f, 207.029f, 258.511f +path.quadTo(SkBits2Float(0x435659f0), SkBits2Float(0x4384eaa4), SkBits2Float(0x435659f0), SkBits2Float(0x438a1820)); // 214.351f, 265.833f, 214.351f, 276.188f +path.close(); + SkPath path153(path); + builder.add(path153, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x437aa20e), SkBits2Float(0x434e714e)); // 250.633f, 206.443f +path.quadTo(SkBits2Float(0x437aa20e), SkBits2Float(0x4358cc46), SkBits2Float(0x43734f8a), SkBits2Float(0x43601eca)); // 250.633f, 216.798f, 243.311f, 224.12f +path.quadTo(SkBits2Float(0x436bfd06), SkBits2Float(0x4367714e), SkBits2Float(0x4361a20e), SkBits2Float(0x4367714e)); // 235.988f, 231.443f, 225.633f, 231.443f +path.quadTo(SkBits2Float(0x43574716), SkBits2Float(0x4367714e), SkBits2Float(0x434ff492), SkBits2Float(0x43601eca)); // 215.278f, 231.443f, 207.955f, 224.12f +path.quadTo(SkBits2Float(0x4348a20e), SkBits2Float(0x4358cc46), SkBits2Float(0x4348a20e), SkBits2Float(0x434e714e)); // 200.633f, 216.798f, 200.633f, 206.443f +path.quadTo(SkBits2Float(0x4348a20e), SkBits2Float(0x43441656), SkBits2Float(0x434ff492), SkBits2Float(0x433cc3d2)); // 200.633f, 196.087f, 207.955f, 188.765f +path.quadTo(SkBits2Float(0x43574716), SkBits2Float(0x4335714e), SkBits2Float(0x4361a20e), SkBits2Float(0x4335714e)); // 215.278f, 181.443f, 225.633f, 181.443f +path.quadTo(SkBits2Float(0x436bfd06), SkBits2Float(0x4335714e), SkBits2Float(0x43734f8a), SkBits2Float(0x433cc3d2)); // 235.988f, 181.443f, 243.311f, 188.765f +path.quadTo(SkBits2Float(0x437aa20e), SkBits2Float(0x43441656), SkBits2Float(0x437aa20e), SkBits2Float(0x434e714e)); // 250.633f, 196.087f, 250.633f, 206.443f +path.close(); + SkPath path154(path); + builder.add(path154, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43ed22e3), SkBits2Float(0x439da644)); // 474.273f, 315.299f +path.quadTo(SkBits2Float(0x43ed22e3), SkBits2Float(0x43a2d3c0), SkBits2Float(0x43e979a1), SkBits2Float(0x43a67d02)); // 474.273f, 325.654f, 466.95f, 332.977f +path.quadTo(SkBits2Float(0x43e5d05f), SkBits2Float(0x43aa2644), SkBits2Float(0x43e0a2e3), SkBits2Float(0x43aa2644)); // 459.628f, 340.299f, 449.273f, 340.299f +path.quadTo(SkBits2Float(0x43db7567), SkBits2Float(0x43aa2644), SkBits2Float(0x43d7cc25), SkBits2Float(0x43a67d02)); // 438.917f, 340.299f, 431.595f, 332.977f +path.quadTo(SkBits2Float(0x43d422e3), SkBits2Float(0x43a2d3c0), SkBits2Float(0x43d422e3), SkBits2Float(0x439da644)); // 424.273f, 325.654f, 424.273f, 315.299f +path.quadTo(SkBits2Float(0x43d422e3), SkBits2Float(0x439878c8), SkBits2Float(0x43d7cc25), SkBits2Float(0x4394cf86)); // 424.273f, 304.944f, 431.595f, 297.621f +path.quadTo(SkBits2Float(0x43db7567), SkBits2Float(0x43912644), SkBits2Float(0x43e0a2e3), SkBits2Float(0x43912644)); // 438.917f, 290.299f, 449.273f, 290.299f +path.quadTo(SkBits2Float(0x43e5d05f), SkBits2Float(0x43912644), SkBits2Float(0x43e979a1), SkBits2Float(0x4394cf86)); // 459.628f, 290.299f, 466.95f, 297.621f +path.quadTo(SkBits2Float(0x43ed22e3), SkBits2Float(0x439878c8), SkBits2Float(0x43ed22e3), SkBits2Float(0x439da644)); // 474.273f, 304.944f, 474.273f, 315.299f +path.close(); + SkPath path155(path); + builder.add(path155, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43cc4428), SkBits2Float(0x43797560)); // 408.532f, 249.458f +path.quadTo(SkBits2Float(0x43cc4428), SkBits2Float(0x4381e82c), SkBits2Float(0x43c89ae6), SkBits2Float(0x4385916e)); // 408.532f, 259.814f, 401.21f, 267.136f +path.quadTo(SkBits2Float(0x43c4f1a4), SkBits2Float(0x43893ab0), SkBits2Float(0x43bfc428), SkBits2Float(0x43893ab0)); // 393.888f, 274.458f, 383.532f, 274.458f +path.quadTo(SkBits2Float(0x43ba96ac), SkBits2Float(0x43893ab0), SkBits2Float(0x43b6ed6a), SkBits2Float(0x4385916e)); // 373.177f, 274.458f, 365.855f, 267.136f +path.quadTo(SkBits2Float(0x43b34428), SkBits2Float(0x4381e82c), SkBits2Float(0x43b34428), SkBits2Float(0x43797560)); // 358.532f, 259.814f, 358.532f, 249.458f +path.quadTo(SkBits2Float(0x43b34428), SkBits2Float(0x436f1a69), SkBits2Float(0x43b6ed6a), SkBits2Float(0x4367c7e5)); // 358.532f, 239.103f, 365.855f, 231.781f +path.quadTo(SkBits2Float(0x43ba96ac), SkBits2Float(0x43607561), SkBits2Float(0x43bfc428), SkBits2Float(0x43607561)); // 373.177f, 224.459f, 383.532f, 224.459f +path.quadTo(SkBits2Float(0x43c4f1a4), SkBits2Float(0x43607561), SkBits2Float(0x43c89ae6), SkBits2Float(0x4367c7e5)); // 393.888f, 224.459f, 401.21f, 231.781f +path.quadTo(SkBits2Float(0x43cc4428), SkBits2Float(0x436f1a69), SkBits2Float(0x43cc4428), SkBits2Float(0x43797560)); // 408.532f, 239.103f, 408.532f, 249.458f +path.close(); + SkPath path156(path); + builder.add(path156, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42e47b81), SkBits2Float(0x43b97683)); // 114.241f, 370.926f +path.quadTo(SkBits2Float(0x42e47b81), SkBits2Float(0x43bea3ff), SkBits2Float(0x42d5d678), SkBits2Float(0x43c24d41)); // 114.241f, 381.281f, 106.919f, 388.604f +path.quadTo(SkBits2Float(0x42c73170), SkBits2Float(0x43c5f683), SkBits2Float(0x42b27b81), SkBits2Float(0x43c5f683)); // 99.5966f, 395.926f, 89.2412f, 395.926f +path.quadTo(SkBits2Float(0x429dc592), SkBits2Float(0x43c5f683), SkBits2Float(0x428f208a), SkBits2Float(0x43c24d41)); // 78.8859f, 395.926f, 71.5636f, 388.604f +path.quadTo(SkBits2Float(0x42807b81), SkBits2Float(0x43bea3ff), SkBits2Float(0x42807b81), SkBits2Float(0x43b97683)); // 64.2412f, 381.281f, 64.2412f, 370.926f +path.quadTo(SkBits2Float(0x42807b81), SkBits2Float(0x43b44907), SkBits2Float(0x428f208a), SkBits2Float(0x43b09fc5)); // 64.2412f, 360.571f, 71.5636f, 353.248f +path.quadTo(SkBits2Float(0x429dc592), SkBits2Float(0x43acf683), SkBits2Float(0x42b27b81), SkBits2Float(0x43acf683)); // 78.8859f, 345.926f, 89.2412f, 345.926f +path.quadTo(SkBits2Float(0x42c73170), SkBits2Float(0x43acf683), SkBits2Float(0x42d5d678), SkBits2Float(0x43b09fc5)); // 99.5966f, 345.926f, 106.919f, 353.248f +path.quadTo(SkBits2Float(0x42e47b81), SkBits2Float(0x43b44907), SkBits2Float(0x42e47b81), SkBits2Float(0x43b97683)); // 114.241f, 360.571f, 114.241f, 370.926f +path.close(); + SkPath path157(path); + builder.add(path157, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4384239e), SkBits2Float(0x42a36f30)); // 264.278f, 81.7172f +path.quadTo(SkBits2Float(0x4384239e), SkBits2Float(0x42b8251f), SkBits2Float(0x43807a5c), SkBits2Float(0x42c6ca28)); // 264.278f, 92.0725f, 256.956f, 99.3948f +path.quadTo(SkBits2Float(0x4379a234), SkBits2Float(0x42d56f30), SkBits2Float(0x436f473c), SkBits2Float(0x42d56f30)); // 249.634f, 106.717f, 239.278f, 106.717f +path.quadTo(SkBits2Float(0x4364ec44), SkBits2Float(0x42d56f30), SkBits2Float(0x435d99c0), SkBits2Float(0x42c6ca28)); // 228.923f, 106.717f, 221.601f, 99.3948f +path.quadTo(SkBits2Float(0x4356473c), SkBits2Float(0x42b8251f), SkBits2Float(0x4356473c), SkBits2Float(0x42a36f30)); // 214.278f, 92.0725f, 214.278f, 81.7172f +path.quadTo(SkBits2Float(0x4356473c), SkBits2Float(0x428eb941), SkBits2Float(0x435d99c0), SkBits2Float(0x42801438)); // 214.278f, 71.3618f, 221.601f, 64.0395f +path.quadTo(SkBits2Float(0x4364ec44), SkBits2Float(0x4262de60), SkBits2Float(0x436f473c), SkBits2Float(0x4262de60)); // 228.923f, 56.7172f, 239.278f, 56.7172f +path.quadTo(SkBits2Float(0x4379a234), SkBits2Float(0x4262de60), SkBits2Float(0x43807a5c), SkBits2Float(0x42801438)); // 249.634f, 56.7172f, 256.956f, 64.0395f +path.quadTo(SkBits2Float(0x4384239e), SkBits2Float(0x428eb941), SkBits2Float(0x4384239e), SkBits2Float(0x42a36f30)); // 264.278f, 71.3618f, 264.278f, 81.7172f +path.close(); + SkPath path158(path); + builder.add(path158, (SkPathOp) 2); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x435be541), SkBits2Float(0x43baaaf6)); // 219.896f, 373.336f +path.quadTo(SkBits2Float(0x435be541), SkBits2Float(0x43bfd872), SkBits2Float(0x435492bd), SkBits2Float(0x43c381b4)); // 219.896f, 383.691f, 212.573f, 391.013f +path.quadTo(SkBits2Float(0x434d4038), SkBits2Float(0x43c72af6), SkBits2Float(0x4342e541), SkBits2Float(0x43c72af6)); // 205.251f, 398.336f, 194.896f, 398.336f +path.quadTo(SkBits2Float(0x43388a4a), SkBits2Float(0x43c72af6), SkBits2Float(0x433137c5), SkBits2Float(0x43c381b4)); // 184.54f, 398.336f, 177.218f, 391.013f +path.quadTo(SkBits2Float(0x4329e541), SkBits2Float(0x43bfd872), SkBits2Float(0x4329e541), SkBits2Float(0x43baaaf6)); // 169.896f, 383.691f, 169.896f, 373.336f +path.quadTo(SkBits2Float(0x4329e541), SkBits2Float(0x43b57d7a), SkBits2Float(0x433137c5), SkBits2Float(0x43b1d438)); // 169.896f, 362.98f, 177.218f, 355.658f +path.quadTo(SkBits2Float(0x43388a4a), SkBits2Float(0x43ae2af6), SkBits2Float(0x4342e541), SkBits2Float(0x43ae2af6)); // 184.54f, 348.336f, 194.896f, 348.336f +path.quadTo(SkBits2Float(0x434d4038), SkBits2Float(0x43ae2af6), SkBits2Float(0x435492bd), SkBits2Float(0x43b1d438)); // 205.251f, 348.336f, 212.573f, 355.658f +path.quadTo(SkBits2Float(0x435be541), SkBits2Float(0x43b57d7a), SkBits2Float(0x435be541), SkBits2Float(0x43baaaf6)); // 219.896f, 362.98f, 219.896f, 373.336f +path.close(); + SkPath path159(path); + builder.add(path159, (SkPathOp) 2); + + builder.resolve(&path); +} + +static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0; +static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0; + +static struct TestDesc tests[] = { + TEST(build1_1), +}; + + +static const size_t testCount = SK_ARRAY_COUNT(tests); + +static bool runReverse = false; + +DEF_TEST(PathOpsBuildUse, reporter) { +#if DEBUG_SHOW_TEST_NAME + strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH); +#endif + RunTestSet(reporter, tests, testCount, firstTest, nullptr, stopTest, runReverse); +}
diff --git a/src/third_party/skia/tests/PathOpsBuilderConicTest.cpp b/src/third_party/skia/tests/PathOpsBuilderConicTest.cpp new file mode 100644 index 0000000..e6a6447 --- /dev/null +++ b/src/third_party/skia/tests/PathOpsBuilderConicTest.cpp
@@ -0,0 +1,641 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "PathOpsExtendedTest.h" +#include "SkRandom.h" +#include "SkRegion.h" +#include "SubsetPath.h" + +#define DEBUG_SIMPLIFY_FAILS 0 + +struct OvalSet { + SkRect fBounds; + int fColumns; + int fRows; + int fRotations; + SkScalar fXSpacing; + SkScalar fYSpacing; +}; + +static void testOvalSet(const OvalSet& set, const SkPath& oval, SkOpBuilder* builder, + SkRegion* region, SkPath* result) { + for (int x = 0; x < set.fColumns; ++x) { + for (int y = 0; y < set.fRows; ++y) { + for (SkScalar r = 0; r < 360; r += 360.f / set.fRotations) { + SkPath rotated; + SkMatrix matrix; + matrix.reset(); + matrix.postRotate(r, 0, 0); + matrix.postTranslate(x * set.fXSpacing, y * set.fYSpacing); + oval.transform(matrix, &rotated); + if (builder) { + builder->add(rotated, kUnion_SkPathOp); + } else if (!region) { + Op(*result, rotated, kUnion_SkPathOp, result); + } else { + SkRegion rgnB, openClip; + openClip.setRect(-16000, -16000, 16000, 16000); + rgnB.setPath(rotated, openClip); + region->op(rgnB, SkRegion::kUnion_Op); + } + } + } + } + if (builder) { + builder->resolve(result); + } else if (region) { + region->getBoundaryPath(result); + } +} + +static void testOne(skiatest::Reporter* reporter, const OvalSet& set) { + SkPath oval, regionResult, builderResult, opResult; + oval.setFillType(SkPath::kWinding_FillType); + oval.addOval(set.fBounds); + SkOpBuilder builder; + SkRegion region; + testOvalSet(set, oval, nullptr, ®ion, ®ionResult); + testOvalSet(set, oval, &builder, nullptr, &builderResult); + testOvalSet(set, oval, nullptr, nullptr, &opResult); + SkBitmap bitmap; + comparePaths(reporter, __FUNCTION__ , regionResult, builderResult, bitmap); + comparePaths(reporter, __FUNCTION__ , regionResult, opResult, bitmap); +} + +struct OvalSetOneOff { + int fCol; + int fRow; + int fRot; + int fTrial; +} oneOffs[] = { + { 2, 2, 9, 73 }, + { 1, 2, 7, 93 } +}; + +static void setupOne(skiatest::Reporter* reporter, int col, int row, int rot, int trial) { + const int scale = 10; + SkRandom r; + r.setSeed(col * 100000000 + row * 10000000 + rot * 1000000 + trial); + SkScalar xOffset = r.nextRangeScalar(1, 40) * scale; + SkScalar yOffset = r.nextRangeScalar(1, 100) * scale; + OvalSet set = {{0, 0, 0, 0}, col, row, rot, xOffset, yOffset}; + set.fBounds.setXYWH(5, 5, + r.nextRangeScalar(5, 50) * scale, r.nextRangeScalar(50, 90) * scale); + testOne(reporter, set); +} + +#include "SkCommandLineFlags.h" + +DEFINE_int32(processOffset, 0, "Offset the test by this value. This permits multiple processes " + "to exercise the same test in parallel with different test values."); +DEFINE_int32(processCount, 1, "Test iteration count. This permits multiple " + "processes " + "to exercise the same test in parallel with different test values."); +DEFINE_int32(trialRuns, 100, "Run this many tests (defaults to 100)."); + +DEF_TEST(SixtyOvals, reporter) { + bool skipOneOffs = false; + int trialRuns = FLAGS_trialRuns / FLAGS_processCount; + for (int col = 1; col <= 2; ++col) { + for (int row = 1; row <= 3; ++row) { + for (int rot = 2; rot <= 9; ++rot) { + for (int trial = FLAGS_processOffset * trialRuns; --trialRuns >= 0; ++trial) { + if (skipOneOffs) { + for (const OvalSetOneOff& oneOff : oneOffs) { + if (col == oneOff.fCol && row == oneOff.fRow && rot == oneOff.fRot + && trial == oneOff.fTrial) { + goto skipTest; + } + } + } + setupOne(reporter, col, row, rot, trial); + skipTest: + ; + } + } + } + } +} + +DEF_TEST(SixtyOvalsOneOff, reporter) { + for (const OvalSetOneOff& oneOff : oneOffs) { + setupOne(reporter, oneOff.fCol, oneOff.fRow, oneOff.fRot, oneOff.fTrial); + } +} + +#if DEBUG_SIMPLIFY_FAILS +static bool simplify_fails(skiatest::Reporter* reporter, const SkPath& path) { + SkPath dummy; + bool failed = !Simplify(path, &dummy); + if (!failed) { + SkBitmap bitmap; + failed = !!comparePaths(reporter, __FUNCTION__, path, dummy, bitmap); + } + return failed; +} + +static SkPath subset_simplify_fail(skiatest::Reporter* reporter, const SkPath& path) { + SubsetContours subsetContours(path); + bool failed = simplify_fails(reporter, path); + SkASSERT(failed); + SkPath lastFailed = path; + SkPath minimal; + while (subsetContours.subset(failed, &minimal)) { + failed = simplify_fails(reporter, minimal); + SkDebugf(" %s\n", failed ? "failed" : ""); + if (failed) { + lastFailed = minimal; + } + } + failed = simplify_fails(reporter, lastFailed); + SkASSERT(failed); + SubsetVerbs subsetVerbs(lastFailed); + while (subsetVerbs.subset(failed, &minimal)) { + failed = simplify_fails(reporter, minimal); + SkDebugf(" %s\n", failed ? "failed" : ""); + if (failed) { + lastFailed = minimal; + } + } + return lastFailed; +} +#endif + +DEF_TEST(SixtyOvals_2_2_9_73, reporter) { + SkPath path; + path.moveTo(SkBits2Float(0x434d53ca), SkBits2Float(0x43ad6ab0)); // 205.327f, 346.833f +path.conicTo(SkBits2Float(0x434d53ca), SkBits2Float(0x40a00000), SkBits2Float(0x42d253ca), SkBits2Float(0x40a00000), SkBits2Float(0x3f3504f3)); // 205.327f, 5, 105.164f, 5, 0.707107f +path.conicTo(SkBits2Float(0x40a00000), SkBits2Float(0x40a00000), SkBits2Float(0x40a00000), SkBits2Float(0x43ad6ab0), SkBits2Float(0x3f3504f3)); // 5, 5, 5, 346.833f, 0.707107f +path.conicTo(SkBits2Float(0x40a00000), SkBits2Float(0x442c2ab0), SkBits2Float(0x42d253ca), SkBits2Float(0x442c2ab0), SkBits2Float(0x3f3504f3)); // 5, 688.667f, 105.164f, 688.667f, 0.707107f +path.conicTo(SkBits2Float(0x434d53ca), SkBits2Float(0x442c2ab0), SkBits2Float(0x434d53ca), SkBits2Float(0x43ad6ab0), SkBits2Float(0x3f3504f3)); // 205.327f, 688.667f, 205.327f, 346.833f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0xc2834d04), SkBits2Float(0x43c6d5fb)); // -65.6504f, 397.672f +path.conicTo(SkBits2Float(0x431a136e), SkBits2Float(0x4307cfe3), SkBits2Float(0x429ab133), SkBits2Float(0x428edb31), SkBits2Float(0x3f3504f3)); // 154.076f, 135.812f, 77.3461f, 71.4281f, 0.707107f +path.conicTo(SkBits2Float(0x3f1dc4d0), SkBits2Float(0x40e169c2), SkBits2Float(0xc35b1c2c), SkBits2Float(0x438673b0), SkBits2Float(0x3f3504f3)); // 0.616284f, 7.04416f, -219.11f, 268.904f, 0.707107f +path.conicTo(SkBits2Float(0xc3db6b0e), SkBits2Float(0x4404b0dc), SkBits2Float(0xc3b50da4), SkBits2Float(0x4414c96f), SkBits2Float(0x3f3504f3)); // -438.836f, 530.763f, -362.107f, 595.147f, 0.707107f +path.conicTo(SkBits2Float(0xc38eb03a), SkBits2Float(0x4424e202), SkBits2Float(0xc2834d04), SkBits2Float(0x43c6d5fb), SkBits2Float(0x3f3504f3)); // -285.377f, 659.531f, -65.6504f, 397.672f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0xc398f46d), SkBits2Float(0x438337ac)); // -305.91f, 262.435f +path.conicTo(SkBits2Float(0x41f5d870), SkBits2Float(0x434b137f), SkBits2Float(0x41556629), SkBits2Float(0x42d0de52), SkBits2Float(0x3f3504f3)); // 30.7307f, 203.076f, 13.3374f, 104.434f, 0.707107f +path.conicTo(SkBits2Float(0xc081c918), SkBits2Float(0x40b95a5c), SkBits2Float(0xc3aa5918), SkBits2Float(0x42824d58), SkBits2Float(0x3f3504f3)); // -4.0558f, 5.79228f, -340.696f, 65.1511f, 0.707107f +path.conicTo(SkBits2Float(0xc4295587), SkBits2Float(0x42f9050a), SkBits2Float(0xc424fc5c), SkBits2Float(0x435f26db), SkBits2Float(0x3f3504f3)); // -677.336f, 124.51f, -659.943f, 223.152f, 0.707107f +path.conicTo(SkBits2Float(0xc420a331), SkBits2Float(0x43a0e598), SkBits2Float(0xc398f46d), SkBits2Float(0x438337ac), SkBits2Float(0x3f3504f3)); // -642.55f, 321.794f, -305.91f, 262.435f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0xc3c983e0), SkBits2Float(0x408cdc40)); // -403.03f, 4.40189f +path.conicTo(SkBits2Float(0xc2d5fcd2), SkBits2Float(0x432f5193), SkBits2Float(0xc263a5d9), SkBits2Float(0x42b12617), SkBits2Float(0x3f3504f3)); // -106.994f, 175.319f, -56.912f, 88.5744f, 0.707107f +path.conicTo(SkBits2Float(0xc0da9066), SkBits2Float(0x3fea4196), SkBits2Float(0xc3976eed), SkBits2Float(0xc329162e), SkBits2Float(0x3f3504f3)); // -6.83013f, 1.83013f, -302.867f, -169.087f, 0.707107f +path.conicTo(SkBits2Float(0xc415b9cc), SkBits2Float(0xc3aa006f), SkBits2Float(0xc4223f09), SkBits2Float(0xc37d4256), SkBits2Float(0x3f3504f3)); // -598.903f, -340.003f, -648.985f, -253.259f, 0.707107f +path.conicTo(SkBits2Float(0xc42ec446), SkBits2Float(0xc32683cf), SkBits2Float(0xc3c983e0), SkBits2Float(0x408cdc40), SkBits2Float(0x3f3504f3)); // -699.067f, -166.515f, -403.03f, 4.40189f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0xc39bc8c8), SkBits2Float(0xc37fb0d7)); // -311.569f, -255.691f +path.conicTo(SkBits2Float(0xc342a797), SkBits2Float(0x42830e25), SkBits2Float(0xc2c9102e), SkBits2Float(0x41fa2834), SkBits2Float(0x3f3504f3)); // -194.655f, 65.5276f, -100.532f, 31.2696f, 0.707107f +path.conicTo(SkBits2Float(0xc0cd12f5), SkBits2Float(0xc03f4152), SkBits2Float(0xc2f6a523), SkBits2Float(0xc3a21a77), SkBits2Float(0x3f3504f3)); // -6.40856f, -2.98836f, -123.323f, -324.207f, 0.707107f +path.conicTo(SkBits2Float(0xc3703c8a), SkBits2Float(0xc4215b37), SkBits2Float(0xc3a72e05), SkBits2Float(0xc418cab4), SkBits2Float(0x3f3504f3)); // -240.236f, -645.425f, -334.36f, -611.167f, 0.707107f +path.conicTo(SkBits2Float(0xc3d63dc5), SkBits2Float(0xc4103a31), SkBits2Float(0xc39bc8c8), SkBits2Float(0xc37fb0d7), SkBits2Float(0x3f3504f3)); // -428.483f, -576.909f, -311.569f, -255.691f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0xc294a419), SkBits2Float(0xc3c6124c)); // -74.3205f, -396.143f +path.conicTo(SkBits2Float(0xc33f3c05), SkBits2Float(0xc295d95d), SkBits2Float(0xc2c2390a), SkBits2Float(0xc222aa8c), SkBits2Float(0x3f3504f3)); // -191.234f, -74.9245f, -97.1114f, -40.6665f, 0.707107f +path.conicTo(SkBits2Float(0xc03f4154), SkBits2Float(0xc0cd12f4), SkBits2Float(0x42e3d9e6), SkBits2Float(0xc3a3d041), SkBits2Float(0x3f3504f3)); // -2.98836f, -6.40856f, 113.926f, -327.627f, 0.707107f +path.conicTo(SkBits2Float(0x4366d6ec), SkBits2Float(0xc422361b), SkBits2Float(0x4308b76c), SkBits2Float(0xc42ac69e), SkBits2Float(0x3f3504f3)); // 230.84f, -648.845f, 136.716f, -683.103f, 0.707107f +path.conicTo(SkBits2Float(0x422a5fb0), SkBits2Float(0xc4335721), SkBits2Float(0xc294a419), SkBits2Float(0xc3c6124c), SkBits2Float(0x3f3504f3)); // 42.5934f, -717.361f, -74.3205f, -396.143f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x4345b3f8), SkBits2Float(0xc3af9e21)); // 197.703f, -351.235f +path.conicTo(SkBits2Float(0xc2c4aac2), SkBits2Float(0xc3345194), SkBits2Float(0xc24101bb), SkBits2Float(0xc2bb2617), SkBits2Float(0x3f3504f3)); // -98.3335f, -180.319f, -48.2517f, -93.5744f, 0.707107f +path.conicTo(SkBits2Float(0x3fea41a0), SkBits2Float(0xc0da9066), SkBits2Float(0x4394eeee), SkBits2Float(0xc331bf31), SkBits2Float(0x3f3504f3)); // 1.83013f, -6.83013f, 297.867f, -177.747f, 0.707107f +path.conicTo(SkBits2Float(0x441479cd), SkBits2Float(0xc3ae54f0), SkBits2Float(0x4407f490), SkBits2Float(0xc3d9b434), SkBits2Float(0x3f3504f3)); // 593.903f, -348.664f, 543.821f, -435.408f, 0.707107f +path.conicTo(SkBits2Float(0x43f6dea8), SkBits2Float(0xc40289bc), SkBits2Float(0x4345b3f8), SkBits2Float(0xc3af9e21), SkBits2Float(0x3f3504f3)); // 493.74f, -522.152f, 197.703f, -351.235f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x43bc9c08), SkBits2Float(0xc30dfb1e)); // 377.219f, -141.981f +path.conicTo(SkBits2Float(0x422250a2), SkBits2Float(0xc34956f5), SkBits2Float(0x41b97bee), SkBits2Float(0xc2cd653e), SkBits2Float(0x3f3504f3)); // 40.5787f, -201.34f, 23.1855f, -102.698f, 0.707107f +path.conicTo(SkBits2Float(0x40b95a5b), SkBits2Float(0xc081c919), SkBits2Float(0x43ab375e), SkBits2Float(0x425d363a), SkBits2Float(0x3f3504f3)); // 5.79228f, -4.0558f, 342.433f, 55.303f, 0.707107f +path.conicTo(SkBits2Float(0x4429c4a9), SkBits2Float(0x42e552cb), SkBits2Float(0x442e1dd4), SkBits2Float(0x4180287c), SkBits2Float(0x3f3504f3)); // 679.073f, 114.662f, 696.466f, 16.0198f, 0.707107f +path.conicTo(SkBits2Float(0x443276ff), SkBits2Float(0xc2a53e8d), SkBits2Float(0x43bc9c08), SkBits2Float(0xc30dfb1e), SkBits2Float(0x3f3504f3)); // 713.859f, -82.6222f, 377.219f, -141.981f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x43be1d75), SkBits2Float(0x4305b53c)); // 380.23f, 133.708f +path.conicTo(SkBits2Float(0x432080f6), SkBits2Float(0xc30026d3), SkBits2Float(0x42a78c44), SkBits2Float(0xc27f121c), SkBits2Float(0x3f3504f3)); // 160.504f, -128.152f, 83.774f, -63.7677f, 0.707107f +path.conicTo(SkBits2Float(0x40e169c3), SkBits2Float(0x3f1dc4b8), SkBits2Float(0x4362c542), SkBits2Float(0x43833cea), SkBits2Float(0x3f3504f3)); // 7.04416f, 0.616283f, 226.771f, 262.476f, 0.707107f +path.conicTo(SkBits2Float(0x43df3f9c), SkBits2Float(0x44031579), SkBits2Float(0x4402ce83), SkBits2Float(0x43e5f9cc), SkBits2Float(0x3f3504f3)); // 446.497f, 524.336f, 523.227f, 459.952f, 0.707107f +path.conicTo(SkBits2Float(0x4415fd38), SkBits2Float(0x43c5c8a6), SkBits2Float(0x43be1d75), SkBits2Float(0x4305b53c), SkBits2Float(0x3f3504f3)); // 599.957f, 395.568f, 380.23f, 133.708f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x434d53ca), SkBits2Float(0x44487cfb)); // 205.327f, 801.953f +path.conicTo(SkBits2Float(0x434d53ca), SkBits2Float(0x43e60f46), SkBits2Float(0x42d253ca), SkBits2Float(0x43e60f46), SkBits2Float(0x3f3504f3)); // 205.327f, 460.119f, 105.164f, 460.119f, 0.707107f +path.conicTo(SkBits2Float(0x40a00000), SkBits2Float(0x43e60f46), SkBits2Float(0x40a00000), SkBits2Float(0x44487cfb), SkBits2Float(0x3f3504f3)); // 5, 460.119f, 5, 801.953f, 0.707107f +path.conicTo(SkBits2Float(0x40a00000), SkBits2Float(0x448ef92a), SkBits2Float(0x42d253ca), SkBits2Float(0x448ef92a), SkBits2Float(0x3f3504f3)); // 5, 1143.79f, 105.164f, 1143.79f, 0.707107f +path.conicTo(SkBits2Float(0x434d53ca), SkBits2Float(0x448ef92a), SkBits2Float(0x434d53ca), SkBits2Float(0x44487cfb), SkBits2Float(0x3f3504f3)); // 205.327f, 1143.79f, 205.327f, 801.953f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0xc2834d04), SkBits2Float(0x445532a0)); // -65.6504f, 852.791f +path.conicTo(SkBits2Float(0x431a136e), SkBits2Float(0x4413bb9c), SkBits2Float(0x429ab133), SkBits2Float(0x4403a309), SkBits2Float(0x3f3504f3)); // 154.076f, 590.931f, 77.3461f, 526.547f, 0.707107f +path.conicTo(SkBits2Float(0x3f1dc4d0), SkBits2Float(0x43e714ed), SkBits2Float(0xc35b1c2c), SkBits2Float(0x4435017b), SkBits2Float(0x3f3504f3)); // 0.616284f, 462.163f, -219.11f, 724.023f, 0.707107f +path.conicTo(SkBits2Float(0xc3db6b0e), SkBits2Float(0x4476787f), SkBits2Float(0xc3b50da4), SkBits2Float(0x44834889), SkBits2Float(0x3f3504f3)); // -438.836f, 985.883f, -362.107f, 1050.27f, 0.707107f +path.conicTo(SkBits2Float(0xc38eb03a), SkBits2Float(0x448b54d2), SkBits2Float(0xc2834d04), SkBits2Float(0x445532a0), SkBits2Float(0x3f3504f3)); // -285.377f, 1114.65f, -65.6504f, 852.791f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0xc398f46d), SkBits2Float(0x44336379)); // -305.91f, 717.554f +path.conicTo(SkBits2Float(0x41f5d870), SkBits2Float(0x44248c83), SkBits2Float(0x41556629), SkBits2Float(0x440be36d), SkBits2Float(0x3f3504f3)); // 30.7307f, 658.195f, 13.3374f, 559.554f, 0.707107f +path.conicTo(SkBits2Float(0xc081c918), SkBits2Float(0x43e674af), SkBits2Float(0xc3aa5918), SkBits2Float(0x4402114e), SkBits2Float(0x3f3504f3)); // -4.0558f, 460.912f, -340.696f, 520.27f, 0.707107f +path.conicTo(SkBits2Float(0xc4295587), SkBits2Float(0x4410e844), SkBits2Float(0xc424fc5c), SkBits2Float(0x4429915a), SkBits2Float(0x3f3504f3)); // -677.336f, 579.629f, -659.943f, 678.271f, 0.707107f +path.conicTo(SkBits2Float(0xc420a331), SkBits2Float(0x44423a6f), SkBits2Float(0xc398f46d), SkBits2Float(0x44336379), SkBits2Float(0x3f3504f3)); // -642.55f, 776.913f, -305.91f, 717.554f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0xc3c983e0), SkBits2Float(0x43e5c2b7)); // -403.03f, 459.521f +path.conicTo(SkBits2Float(0xc2d5fcd2), SkBits2Float(0x441d9c08), SkBits2Float(0xc263a5d9), SkBits2Float(0x4407ec66), SkBits2Float(0x3f3504f3)); // -106.994f, 630.438f, -56.912f, 543.694f, 0.707107f +path.conicTo(SkBits2Float(0xc0da9066), SkBits2Float(0x43e47988), SkBits2Float(0xc3976eed), SkBits2Float(0x438f042f), SkBits2Float(0x3f3504f3)); // -6.83013f, 456.949f, -302.867f, 286.033f, 0.707107f +path.conicTo(SkBits2Float(0xc415b9cc), SkBits2Float(0x42e63b5c), SkBits2Float(0xc4223f09), SkBits2Float(0x4349dc36), SkBits2Float(0x3f3504f3)); // -598.903f, 115.116f, -648.985f, 201.86f, 0.707107f +path.conicTo(SkBits2Float(0xc42ec446), SkBits2Float(0x43904d5e), SkBits2Float(0xc3c983e0), SkBits2Float(0x43e5c2b7), SkBits2Float(0x3f3504f3)); // -699.067f, 288.604f, -403.03f, 459.521f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0xc39bc8c8), SkBits2Float(0x43476db5)); // -311.569f, 199.429f +path.conicTo(SkBits2Float(0xc342a797), SkBits2Float(0x44022968), SkBits2Float(0xc2c9102e), SkBits2Float(0x43f331c9), SkBits2Float(0x3f3504f3)); // -194.655f, 520.647f, -100.532f, 486.389f, 0.707107f +path.conicTo(SkBits2Float(0xc0cd12f5), SkBits2Float(0x43e210c3), SkBits2Float(0xc2f6a523), SkBits2Float(0x4302e99e), SkBits2Float(0x3f3504f3)); // -6.40856f, 452.131f, -123.323f, 130.913f, 0.707107f +path.conicTo(SkBits2Float(0xc3703c8a), SkBits2Float(0xc33e4e50), SkBits2Float(0xc3a72e05), SkBits2Float(0xc31c0c44), SkBits2Float(0x3f3504f3)); // -240.236f, -190.306f, -334.36f, -156.048f, 0.707107f +path.conicTo(SkBits2Float(0xc3d63dc5), SkBits2Float(0xc2f39470), SkBits2Float(0xc39bc8c8), SkBits2Float(0x43476db5), SkBits2Float(0x3f3504f3)); // -428.483f, -121.79f, -311.569f, 199.429f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0xc294a419), SkBits2Float(0x426be7d0)); // -74.3205f, 58.9764f +path.conicTo(SkBits2Float(0xc33f3c05), SkBits2Float(0x43be18ef), SkBits2Float(0xc2c2390a), SkBits2Float(0x43cf39f4), SkBits2Float(0x3f3504f3)); // -191.234f, 380.195f, -97.1114f, 414.453f, 0.707107f +path.conicTo(SkBits2Float(0xc03f4154), SkBits2Float(0x43e05afa), SkBits2Float(0x42e3d9e6), SkBits2Float(0x42fefc14), SkBits2Float(0x3f3504f3)); // -2.98836f, 448.711f, 113.926f, 127.492f, 0.707107f +path.conicTo(SkBits2Float(0x4366d6ec), SkBits2Float(0xc341b9e0), SkBits2Float(0x4308b76c), SkBits2Float(0xc363fbec), SkBits2Float(0x3f3504f3)); // 230.84f, -193.726f, 136.716f, -227.984f, 0.707107f +path.conicTo(SkBits2Float(0x422a5fb0), SkBits2Float(0xc3831efc), SkBits2Float(0xc294a419), SkBits2Float(0x426be7d0), SkBits2Float(0x3f3504f3)); // 42.5934f, -262.242f, -74.3205f, 58.9764f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x4345b3f8), SkBits2Float(0x42cfc494)); // 197.703f, 103.884f +path.conicTo(SkBits2Float(0xc2c4aac2), SkBits2Float(0x4389667c), SkBits2Float(0xc24101bb), SkBits2Float(0x43b4c5c0), SkBits2Float(0x3f3504f3)); // -98.3335f, 274.801f, -48.2517f, 361.545f, 0.707107f +path.conicTo(SkBits2Float(0x3fea41a0), SkBits2Float(0x43e02504), SkBits2Float(0x4394eeee), SkBits2Float(0x438aafae), SkBits2Float(0x3f3504f3)); // 1.83013f, 448.289f, 297.867f, 277.372f, 0.707107f +path.conicTo(SkBits2Float(0x441479cd), SkBits2Float(0x42d4e958), SkBits2Float(0x4407f490), SkBits2Float(0x419db120), SkBits2Float(0x3f3504f3)); // 593.903f, 106.456f, 543.821f, 19.7115f, 0.707107f +path.conicTo(SkBits2Float(0x43f6dea8), SkBits2Float(0xc28610c8), SkBits2Float(0x4345b3f8), SkBits2Float(0x42cfc494), SkBits2Float(0x3f3504f3)); // 493.74f, -67.0328f, 197.703f, 103.884f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x43bc9c08), SkBits2Float(0x439c91b7)); // 377.219f, 313.138f +path.conicTo(SkBits2Float(0x422250a2), SkBits2Float(0x437dc797), SkBits2Float(0x41b97bee), SkBits2Float(0x43b035f6), SkBits2Float(0x3f3504f3)); // 40.5787f, 253.78f, 23.1855f, 352.422f, 0.707107f +path.conicTo(SkBits2Float(0x40b95a5b), SkBits2Float(0x43e18822), SkBits2Float(0x43ab375e), SkBits2Float(0x43ff360d), SkBits2Float(0x3f3504f3)); // 5.79228f, 451.064f, 342.433f, 510.422f, 0.707107f +path.conicTo(SkBits2Float(0x4429c4a9), SkBits2Float(0x440e71fc), SkBits2Float(0x442e1dd4), SkBits2Float(0x43eb91ce), SkBits2Float(0x3f3504f3)); // 679.073f, 569.781f, 696.466f, 471.139f, 0.707107f +path.conicTo(SkBits2Float(0x443276ff), SkBits2Float(0x43ba3fa3), SkBits2Float(0x43bc9c08), SkBits2Float(0x439c91b7), SkBits2Float(0x3f3504f3)); // 713.859f, 372.497f, 377.219f, 313.138f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x43be1d75), SkBits2Float(0x441334f2)); // 380.23f, 588.827f +path.conicTo(SkBits2Float(0x432080f6), SkBits2Float(0x43a37bdc), SkBits2Float(0x42a78c44), SkBits2Float(0x43c3ad02), SkBits2Float(0x3f3504f3)); // 160.504f, 326.968f, 83.774f, 391.352f, 0.707107f +path.conicTo(SkBits2Float(0x40e169c3), SkBits2Float(0x43e3de28), SkBits2Float(0x4362c542), SkBits2Float(0x44336618), SkBits2Float(0x3f3504f3)); // 7.04416f, 455.736f, 226.771f, 717.595f, 0.707107f +path.conicTo(SkBits2Float(0x43df3f9c), SkBits2Float(0x4474dd1c), SkBits2Float(0x4402ce83), SkBits2Float(0x4464c489), SkBits2Float(0x3f3504f3)); // 446.497f, 979.455f, 523.227f, 915.071f, 0.707107f +path.conicTo(SkBits2Float(0x4415fd38), SkBits2Float(0x4454abf6), SkBits2Float(0x43be1d75), SkBits2Float(0x441334f2), SkBits2Float(0x3f3504f3)); // 599.957f, 850.687f, 380.23f, 588.827f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x43bb9978), SkBits2Float(0x43ad6ab0)); // 375.199f, 346.833f +path.conicTo(SkBits2Float(0x43bb9978), SkBits2Float(0x40a00000), SkBits2Float(0x43898486), SkBits2Float(0x40a00000), SkBits2Float(0x3f3504f3)); // 375.199f, 5, 275.035f, 5, 0.707107f +path.conicTo(SkBits2Float(0x432edf26), SkBits2Float(0x40a00000), SkBits2Float(0x432edf26), SkBits2Float(0x43ad6ab0), SkBits2Float(0x3f3504f3)); // 174.872f, 5, 174.872f, 346.833f, 0.707107f +path.conicTo(SkBits2Float(0x432edf26), SkBits2Float(0x442c2ab0), SkBits2Float(0x43898486), SkBits2Float(0x442c2ab0), SkBits2Float(0x3f3504f3)); // 174.872f, 688.667f, 275.035f, 688.667f, 0.707107f +path.conicTo(SkBits2Float(0x43bb9978), SkBits2Float(0x442c2ab0), SkBits2Float(0x43bb9978), SkBits2Float(0x43ad6ab0), SkBits2Float(0x3f3504f3)); // 375.199f, 688.667f, 375.199f, 346.833f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x42d07148), SkBits2Float(0x43c6d5fb)); // 104.221f, 397.672f +path.conicTo(SkBits2Float(0x43a1f94a), SkBits2Float(0x4307cfe3), SkBits2Float(0x437737c0), SkBits2Float(0x428edb31), SkBits2Float(0x3f3504f3)); // 323.948f, 135.812f, 247.218f, 71.4281f, 0.707107f +path.conicTo(SkBits2Float(0x432a7ceb), SkBits2Float(0x40e169c2), SkBits2Float(0xc244f418), SkBits2Float(0x438673b0), SkBits2Float(0x3f3504f3)); // 170.488f, 7.04416f, -49.2384f, 268.904f, 0.707107f +path.conicTo(SkBits2Float(0xc3867b7b), SkBits2Float(0x4404b0dc), SkBits2Float(0xc3403c22), SkBits2Float(0x4414c96f), SkBits2Float(0x3f3504f3)); // -268.965f, 530.763f, -192.235f, 595.147f, 0.707107f +path.conicTo(SkBits2Float(0xc2e7029c), SkBits2Float(0x4424e202), SkBits2Float(0x42d07148), SkBits2Float(0x43c6d5fb), SkBits2Float(0x3f3504f3)); // -115.505f, 659.531f, 104.221f, 397.672f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0xc30809b4), SkBits2Float(0x438337ac)); // -136.038f, 262.435f +path.conicTo(SkBits2Float(0x43489a34), SkBits2Float(0x434b137f), SkBits2Float(0x43373589), SkBits2Float(0x42d0de52), SkBits2Float(0x3f3504f3)); // 200.602f, 203.076f, 183.209f, 104.434f, 0.707107f +path.conicTo(SkBits2Float(0x4325d0dd), SkBits2Float(0x40b95a5c), SkBits2Float(0xc32ad30a), SkBits2Float(0x42824d58), SkBits2Float(0x3f3504f3)); // 165.816f, 5.79228f, -170.824f, 65.1511f, 0.707107f +path.conicTo(SkBits2Float(0xc3fdbb7b), SkBits2Float(0x42f9050a), SkBits2Float(0xc3f50925), SkBits2Float(0x435f26db), SkBits2Float(0x3f3504f3)); // -507.465f, 124.51f, -490.071f, 223.152f, 0.707107f +path.conicTo(SkBits2Float(0xc3ec56cf), SkBits2Float(0x43a0e598), SkBits2Float(0xc30809b4), SkBits2Float(0x438337ac), SkBits2Float(0x3f3504f3)); // -472.678f, 321.794f, -136.038f, 262.435f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0xc369289a), SkBits2Float(0x408cdc40)); // -233.159f, 4.40189f +path.conicTo(SkBits2Float(0x427b82f4), SkBits2Float(0x432f5193), SkBits2Float(0x42e1eb60), SkBits2Float(0x42b12617), SkBits2Float(0x3f3504f3)); // 62.8779f, 175.319f, 112.96f, 88.5744f, 0.707107f +path.conicTo(SkBits2Float(0x43230aa3), SkBits2Float(0x3fea4196), SkBits2Float(0xc304feb4), SkBits2Float(0xc329162e), SkBits2Float(0x3f3504f3)); // 163.042f, 1.83013f, -132.995f, -169.087f, 0.707107f +path.conicTo(SkBits2Float(0xc3d68405), SkBits2Float(0xc3aa006f), SkBits2Float(0xc3ef8e7f), SkBits2Float(0xc37d4256), SkBits2Float(0x3f3504f3)); // -429.031f, -340.003f, -479.113f, -253.259f, 0.707107f +path.conicTo(SkBits2Float(0xc4044c7c), SkBits2Float(0xc32683cf), SkBits2Float(0xc369289a), SkBits2Float(0x408cdc40), SkBits2Float(0x3f3504f3)); // -529.195f, -166.515f, -233.159f, 4.40189f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0xc30db26a), SkBits2Float(0xc37fb0d7)); // -141.697f, -255.691f +path.conicTo(SkBits2Float(0xc1c64388), SkBits2Float(0x42830e25), SkBits2Float(0x428aae1e), SkBits2Float(0x41fa2834), SkBits2Float(0x3f3504f3)); // -24.783f, 65.5276f, 69.3401f, 31.2696f, 0.707107f +path.conicTo(SkBits2Float(0x4323768e), SkBits2Float(0xc03f4152), SkBits2Float(0x423a3252), SkBits2Float(0xc3a21a77), SkBits2Float(0x3f3504f3)); // 163.463f, -2.98836f, 46.5491f, -324.207f, 0.707107f +path.conicTo(SkBits2Float(0xc28cbac8), SkBits2Float(0xc4215b37), SkBits2Float(0xc3247ce4), SkBits2Float(0xc418cab4), SkBits2Float(0x3f3504f3)); // -70.3648f, -645.425f, -164.488f, -611.167f, 0.707107f +path.conicTo(SkBits2Float(0xc3814e32), SkBits2Float(0xc4103a31), SkBits2Float(0xc30db26a), SkBits2Float(0xc37fb0d7), SkBits2Float(0x3f3504f3)); // -258.611f, -576.909f, -141.697f, -255.691f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x42bf1a33), SkBits2Float(0xc3c6124c)); // 95.5512f, -396.143f +path.conicTo(SkBits2Float(0xc1aae6f8), SkBits2Float(0xc295d95d), SkBits2Float(0x42918542), SkBits2Float(0xc222aa8c), SkBits2Float(0x3f3504f3)); // -21.3628f, -74.9245f, 72.7603f, -40.6665f, 0.707107f +path.conicTo(SkBits2Float(0x4326e221), SkBits2Float(0xc0cd12f4), SkBits2Float(0x438de60c), SkBits2Float(0xc3a3d041), SkBits2Float(0x3f3504f3)); // 166.883f, -6.40856f, 283.797f, -327.627f, 0.707107f +path.conicTo(SkBits2Float(0x43c85b09), SkBits2Float(0xc422361b), SkBits2Float(0x43994b49), SkBits2Float(0xc42ac69e), SkBits2Float(0x3f3504f3)); // 400.711f, -648.845f, 306.588f, -683.103f, 0.707107f +path.conicTo(SkBits2Float(0x43547712), SkBits2Float(0xc4335721), SkBits2Float(0x42bf1a33), SkBits2Float(0xc3c6124c), SkBits2Float(0x3f3504f3)); // 212.465f, -717.361f, 95.5512f, -396.143f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x43b7c98f), SkBits2Float(0xc3af9e21)); // 367.575f, -351.235f +path.conicTo(SkBits2Float(0x428f138a), SkBits2Float(0xc3345194), SkBits2Float(0x42f33d6e), SkBits2Float(0xc2bb2617), SkBits2Float(0x3f3504f3)); // 71.5382f, -180.319f, 121.62f, -93.5744f, 0.707107f +path.conicTo(SkBits2Float(0x432bb3a9), SkBits2Float(0xc0da9066), SkBits2Float(0x43e9de81), SkBits2Float(0xc331bf31), SkBits2Float(0x3f3504f3)); // 171.702f, -6.83013f, 467.738f, -177.747f, 0.707107f +path.conicTo(SkBits2Float(0x443ef196), SkBits2Float(0xc3ae54f0), SkBits2Float(0x44326c5a), SkBits2Float(0xc3d9b434), SkBits2Float(0x3f3504f3)); // 763.775f, -348.664f, 713.693f, -435.408f, 0.707107f +path.conicTo(SkBits2Float(0x4425e71e), SkBits2Float(0xc40289bc), SkBits2Float(0x43b7c98f), SkBits2Float(0xc3af9e21), SkBits2Float(0x3f3504f3)); // 663.611f, -522.152f, 367.575f, -351.235f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x4408c5ce), SkBits2Float(0xc30dfb1e)); // 547.091f, -141.981f +path.conicTo(SkBits2Float(0x4352734e), SkBits2Float(0xc34956f5), SkBits2Float(0x43410ea4), SkBits2Float(0xc2cd653e), SkBits2Float(0x3f3504f3)); // 210.45f, -201.34f, 193.057f, -102.698f, 0.707107f +path.conicTo(SkBits2Float(0x432fa9f9), SkBits2Float(0xc081c919), SkBits2Float(0x44001378), SkBits2Float(0x425d363a), SkBits2Float(0x3f3504f3)); // 175.664f, -4.0558f, 512.304f, 55.303f, 0.707107f +path.conicTo(SkBits2Float(0x44543c72), SkBits2Float(0x42e552cb), SkBits2Float(0x4458959e), SkBits2Float(0x4180287c), SkBits2Float(0x3f3504f3)); // 848.944f, 114.662f, 866.338f, 16.0198f, 0.707107f +path.conicTo(SkBits2Float(0x445ceec8), SkBits2Float(0xc2a53e8d), SkBits2Float(0x4408c5ce), SkBits2Float(0xc30dfb1e), SkBits2Float(0x3f3504f3)); // 883.731f, -82.6222f, 547.091f, -141.981f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x44098684), SkBits2Float(0x4305b53c)); // 550.102f, 133.708f +path.conicTo(SkBits2Float(0x43a5300e), SkBits2Float(0xc30026d3), SkBits2Float(0x437da548), SkBits2Float(0xc27f121c), SkBits2Float(0x3f3504f3)); // 330.375f, -128.152f, 253.646f, -63.7677f, 0.707107f +path.conicTo(SkBits2Float(0x4330ea74), SkBits2Float(0x3f1dc4b8), SkBits2Float(0x43c65234), SkBits2Float(0x43833cea), SkBits2Float(0x3f3504f3)); // 176.916f, 0.616283f, 396.642f, 262.476f, 0.707107f +path.conicTo(SkBits2Float(0x441a1798), SkBits2Float(0x44031579), SkBits2Float(0x442d464c), SkBits2Float(0x43e5f9cc), SkBits2Float(0x3f3504f3)); // 616.369f, 524.336f, 693.098f, 459.952f, 0.707107f +path.conicTo(SkBits2Float(0x44407502), SkBits2Float(0x43c5c8a6), SkBits2Float(0x44098684), SkBits2Float(0x4305b53c), SkBits2Float(0x3f3504f3)); // 769.828f, 395.568f, 550.102f, 133.708f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x43bb9978), SkBits2Float(0x44487cfb)); // 375.199f, 801.953f +path.conicTo(SkBits2Float(0x43bb9978), SkBits2Float(0x43e60f46), SkBits2Float(0x43898486), SkBits2Float(0x43e60f46), SkBits2Float(0x3f3504f3)); // 375.199f, 460.119f, 275.035f, 460.119f, 0.707107f +path.conicTo(SkBits2Float(0x432edf26), SkBits2Float(0x43e60f46), SkBits2Float(0x432edf26), SkBits2Float(0x44487cfb), SkBits2Float(0x3f3504f3)); // 174.872f, 460.119f, 174.872f, 801.953f, 0.707107f +path.conicTo(SkBits2Float(0x432edf26), SkBits2Float(0x448ef92a), SkBits2Float(0x43898486), SkBits2Float(0x448ef92a), SkBits2Float(0x3f3504f3)); // 174.872f, 1143.79f, 275.035f, 1143.79f, 0.707107f +path.conicTo(SkBits2Float(0x43bb9978), SkBits2Float(0x448ef92a), SkBits2Float(0x43bb9978), SkBits2Float(0x44487cfb), SkBits2Float(0x3f3504f3)); // 375.199f, 1143.79f, 375.199f, 801.953f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x42d07148), SkBits2Float(0x445532a0)); // 104.221f, 852.791f +path.conicTo(SkBits2Float(0x43a1f94a), SkBits2Float(0x4413bb9c), SkBits2Float(0x437737c0), SkBits2Float(0x4403a309), SkBits2Float(0x3f3504f3)); // 323.948f, 590.931f, 247.218f, 526.547f, 0.707107f +path.conicTo(SkBits2Float(0x432a7ceb), SkBits2Float(0x43e714ed), SkBits2Float(0xc244f418), SkBits2Float(0x4435017b), SkBits2Float(0x3f3504f3)); // 170.488f, 462.163f, -49.2384f, 724.023f, 0.707107f +path.conicTo(SkBits2Float(0xc3867b7b), SkBits2Float(0x4476787f), SkBits2Float(0xc3403c22), SkBits2Float(0x44834889), SkBits2Float(0x3f3504f3)); // -268.965f, 985.883f, -192.235f, 1050.27f, 0.707107f +path.conicTo(SkBits2Float(0xc2e7029c), SkBits2Float(0x448b54d2), SkBits2Float(0x42d07148), SkBits2Float(0x445532a0), SkBits2Float(0x3f3504f3)); // -115.505f, 1114.65f, 104.221f, 852.791f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0xc30809b4), SkBits2Float(0x44336379)); // -136.038f, 717.554f +path.conicTo(SkBits2Float(0x43489a34), SkBits2Float(0x44248c83), SkBits2Float(0x43373589), SkBits2Float(0x440be36d), SkBits2Float(0x3f3504f3)); // 200.602f, 658.195f, 183.209f, 559.554f, 0.707107f +path.conicTo(SkBits2Float(0x4325d0dd), SkBits2Float(0x43e674af), SkBits2Float(0xc32ad30a), SkBits2Float(0x4402114e), SkBits2Float(0x3f3504f3)); // 165.816f, 460.912f, -170.824f, 520.27f, 0.707107f +path.conicTo(SkBits2Float(0xc3fdbb7b), SkBits2Float(0x4410e844), SkBits2Float(0xc3f50925), SkBits2Float(0x4429915a), SkBits2Float(0x3f3504f3)); // -507.465f, 579.629f, -490.071f, 678.271f, 0.707107f +path.conicTo(SkBits2Float(0xc3ec56cf), SkBits2Float(0x44423a6f), SkBits2Float(0xc30809b4), SkBits2Float(0x44336379), SkBits2Float(0x3f3504f3)); // -472.678f, 776.913f, -136.038f, 717.554f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0xc369289a), SkBits2Float(0x43e5c2b7)); // -233.159f, 459.521f +path.conicTo(SkBits2Float(0x427b82f4), SkBits2Float(0x441d9c08), SkBits2Float(0x42e1eb60), SkBits2Float(0x4407ec66), SkBits2Float(0x3f3504f3)); // 62.8779f, 630.438f, 112.96f, 543.694f, 0.707107f +path.conicTo(SkBits2Float(0x43230aa3), SkBits2Float(0x43e47988), SkBits2Float(0xc304feb4), SkBits2Float(0x438f042f), SkBits2Float(0x3f3504f3)); // 163.042f, 456.949f, -132.995f, 286.033f, 0.707107f +path.conicTo(SkBits2Float(0xc3d68405), SkBits2Float(0x42e63b5c), SkBits2Float(0xc3ef8e7f), SkBits2Float(0x4349dc36), SkBits2Float(0x3f3504f3)); // -429.031f, 115.116f, -479.113f, 201.86f, 0.707107f +path.conicTo(SkBits2Float(0xc4044c7c), SkBits2Float(0x43904d5e), SkBits2Float(0xc369289a), SkBits2Float(0x43e5c2b7), SkBits2Float(0x3f3504f3)); // -529.195f, 288.604f, -233.159f, 459.521f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0xc30db26a), SkBits2Float(0x43476db5)); // -141.697f, 199.429f +path.conicTo(SkBits2Float(0xc1c64388), SkBits2Float(0x44022968), SkBits2Float(0x428aae1e), SkBits2Float(0x43f331c9), SkBits2Float(0x3f3504f3)); // -24.783f, 520.647f, 69.3401f, 486.389f, 0.707107f +path.conicTo(SkBits2Float(0x4323768e), SkBits2Float(0x43e210c3), SkBits2Float(0x423a3252), SkBits2Float(0x4302e99e), SkBits2Float(0x3f3504f3)); // 163.463f, 452.131f, 46.5491f, 130.913f, 0.707107f +path.conicTo(SkBits2Float(0xc28cbac8), SkBits2Float(0xc33e4e50), SkBits2Float(0xc3247ce4), SkBits2Float(0xc31c0c44), SkBits2Float(0x3f3504f3)); // -70.3648f, -190.306f, -164.488f, -156.048f, 0.707107f +path.conicTo(SkBits2Float(0xc3814e32), SkBits2Float(0xc2f39470), SkBits2Float(0xc30db26a), SkBits2Float(0x43476db5), SkBits2Float(0x3f3504f3)); // -258.611f, -121.79f, -141.697f, 199.429f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x42bf1a33), SkBits2Float(0x426be7d0)); // 95.5512f, 58.9764f +path.conicTo(SkBits2Float(0xc1aae6f8), SkBits2Float(0x43be18ef), SkBits2Float(0x42918542), SkBits2Float(0x43cf39f4), SkBits2Float(0x3f3504f3)); // -21.3628f, 380.195f, 72.7603f, 414.453f, 0.707107f +path.conicTo(SkBits2Float(0x4326e221), SkBits2Float(0x43e05afa), SkBits2Float(0x438de60c), SkBits2Float(0x42fefc14), SkBits2Float(0x3f3504f3)); // 166.883f, 448.711f, 283.797f, 127.492f, 0.707107f +path.conicTo(SkBits2Float(0x43c85b09), SkBits2Float(0xc341b9e0), SkBits2Float(0x43994b49), SkBits2Float(0xc363fbec), SkBits2Float(0x3f3504f3)); // 400.711f, -193.726f, 306.588f, -227.984f, 0.707107f +path.conicTo(SkBits2Float(0x43547712), SkBits2Float(0xc3831efc), SkBits2Float(0x42bf1a33), SkBits2Float(0x426be7d0), SkBits2Float(0x3f3504f3)); // 212.465f, -262.242f, 95.5512f, 58.9764f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x43b7c98f), SkBits2Float(0x42cfc494)); // 367.575f, 103.884f +path.conicTo(SkBits2Float(0x428f138a), SkBits2Float(0x4389667c), SkBits2Float(0x42f33d6e), SkBits2Float(0x43b4c5c0), SkBits2Float(0x3f3504f3)); // 71.5382f, 274.801f, 121.62f, 361.545f, 0.707107f +path.conicTo(SkBits2Float(0x432bb3a9), SkBits2Float(0x43e02504), SkBits2Float(0x43e9de81), SkBits2Float(0x438aafae), SkBits2Float(0x3f3504f3)); // 171.702f, 448.289f, 467.738f, 277.372f, 0.707107f +path.conicTo(SkBits2Float(0x443ef196), SkBits2Float(0x42d4e958), SkBits2Float(0x44326c5a), SkBits2Float(0x419db120), SkBits2Float(0x3f3504f3)); // 763.775f, 106.456f, 713.693f, 19.7115f, 0.707107f +path.conicTo(SkBits2Float(0x4425e71e), SkBits2Float(0xc28610c8), SkBits2Float(0x43b7c98f), SkBits2Float(0x42cfc494), SkBits2Float(0x3f3504f3)); // 663.611f, -67.0328f, 367.575f, 103.884f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x4408c5ce), SkBits2Float(0x439c91b7)); // 547.091f, 313.138f +path.conicTo(SkBits2Float(0x4352734e), SkBits2Float(0x437dc797), SkBits2Float(0x43410ea4), SkBits2Float(0x43b035f6), SkBits2Float(0x3f3504f3)); // 210.45f, 253.78f, 193.057f, 352.422f, 0.707107f +path.conicTo(SkBits2Float(0x432fa9f9), SkBits2Float(0x43e18822), SkBits2Float(0x44001378), SkBits2Float(0x43ff360d), SkBits2Float(0x3f3504f3)); // 175.664f, 451.064f, 512.304f, 510.422f, 0.707107f +path.conicTo(SkBits2Float(0x44543c72), SkBits2Float(0x440e71fc), SkBits2Float(0x4458959e), SkBits2Float(0x43eb91ce), SkBits2Float(0x3f3504f3)); // 848.944f, 569.781f, 866.338f, 471.139f, 0.707107f +path.conicTo(SkBits2Float(0x445ceec8), SkBits2Float(0x43ba3fa3), SkBits2Float(0x4408c5ce), SkBits2Float(0x439c91b7), SkBits2Float(0x3f3504f3)); // 883.731f, 372.497f, 547.091f, 313.138f, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x44098684), SkBits2Float(0x441334f2)); // 550.102f, 588.827f +path.conicTo(SkBits2Float(0x43a5300e), SkBits2Float(0x43a37bdc), SkBits2Float(0x437da548), SkBits2Float(0x43c3ad02), SkBits2Float(0x3f3504f3)); // 330.375f, 326.968f, 253.646f, 391.352f, 0.707107f +path.conicTo(SkBits2Float(0x4330ea74), SkBits2Float(0x43e3de28), SkBits2Float(0x43c65234), SkBits2Float(0x44336618), SkBits2Float(0x3f3504f3)); // 176.916f, 455.736f, 396.642f, 717.595f, 0.707107f +path.conicTo(SkBits2Float(0x441a1798), SkBits2Float(0x4474dd1c), SkBits2Float(0x442d464c), SkBits2Float(0x4464c489), SkBits2Float(0x3f3504f3)); // 616.369f, 979.455f, 693.098f, 915.071f, 0.707107f +path.conicTo(SkBits2Float(0x44407502), SkBits2Float(0x4454abf6), SkBits2Float(0x44098684), SkBits2Float(0x441334f2), SkBits2Float(0x3f3504f3)); // 769.828f, 850.687f, 550.102f, 588.827f, 0.707107f +path.close(); +SkPath lastFailed = path; +#if DEBUG_SIMPLIFY_FAILS + for (;;) { + SkPath failed = subset_simplify_fail(reporter, lastFailed); + if (failed == lastFailed) { + break; + } + lastFailed = failed; + } +#endif + testSimplify(reporter, lastFailed, __FUNCTION__); +} + +DEF_TEST(SixtyOvals_2_2_9_73_reduced, reporter) { + SkPath path; +path.moveTo(377.219f, -141.981f); +path.conicTo(40.5787f, -201.34f, 23.1855f, -102.698f, 0.707107f); +path.lineTo(377.219f, -141.981f); +path.close(); +path.moveTo(306.588f, -227.984f); +path.conicTo(212.465f, -262.242f, 95.5512f, 58.9764f, 0.707107f); +path.lineTo(306.588f, -227.984f); +path.close(); +testSimplify(reporter, path, __FUNCTION__); +} + +DEF_TEST(SixtyOvalsA, reporter) { +SkPath path; +path.setFillType(SkPath::kEvenOdd_FillType); +path.moveTo(11.1722f, -8.10398f); +path.conicTo(22.9143f, -10.3787f, 23.7764f, -7.72542f, 1.00863f); +path.conicTo(24.6671f, -4.98406f, 13.8147f, 0.0166066f, 0.973016f); +path.conicTo(24.6378f, 5.07425f, 23.7764f, 7.72542f, 1.00888f); +path.conicTo(22.8777f, 10.4915f, 11.1648f, 8.13034f, 0.960143f); +path.conicTo(16.9503f, 18.5866f, 14.6946f, 20.2254f, 1.00881f); +path.conicTo(12.4417f, 21.8623f, 4.29722f, 13.1468f, 1.0092f); +path.conicTo(2.92708f, 25, 0, 25, 0.955692f); +path.conicTo(-2.79361f, 25, -4.258f, 13.1048f, 1.00818f); +path.conicTo(-4.27813f, 13.1264f, -4.29822f, 13.1479f, 1.03158f); +path.conicTo(-12.44f, 21.8635f, -14.6946f, 20.2254f, 1.00811f); +path.conicTo(-16.9933f, 18.5554f, -11.1722f, 8.10398f, 0.989875f); +path.conicTo(-22.9143f, 10.3787f, -23.7764f, 7.72542f, 1.00863f); +path.conicTo(-24.6671f, 4.98406f, -13.8147f, -0.0166066f, 0.973016f); +path.conicTo(-24.6378f, -5.07425f, -23.7764f, -7.72542f, 1.00888f); +path.conicTo(-22.8777f, -10.4915f, -11.1648f, -8.13034f, 0.960143f); +path.conicTo(-16.9503f, -18.5866f, -14.6946f, -20.2254f, 1.00881f); +path.conicTo(-12.4417f, -21.8623f, -4.29722f, -13.1468f, 1.0092f); +path.conicTo(-2.92708f, -25, 0, -25, 0.955692f); +path.conicTo(2.79361f, -25, 4.258f, -13.1048f, 1.00818f); +path.conicTo(4.27813f, -13.1264f, 4.29822f, -13.1479f, 1.03158f); +path.conicTo(12.44f, -21.8635f, 14.6946f, -20.2254f, 1.00811f); +path.conicTo(16.9933f, -18.5554f, 11.1722f, -8.10398f, 0.989875f); +path.close(); +SkPath one(path); +path.reset(); +path.setFillType(SkPath::kWinding_FillType); +path.moveTo(-1.54509f, -4.75528f); +path.conicTo(22.2313f, -12.4807f, 23.7764f, -7.72543f, 0.707107f); +path.conicTo(25.3215f, -2.97014f, 1.54509f, 4.75528f, 0.707107f); +path.conicTo(-22.2313f, 12.4807f, -23.7764f, 7.72543f, 0.707107f); +path.conicTo(-25.3215f, 2.97014f, -1.54509f, -4.75528f, 0.707107f); +path.close(); +SkPath two(path); +SkPath result; +Op(one, two, kUnion_SkPathOp, &result); +} + +DEF_TEST(SixtyOvalsAX, reporter) { +SkPath path; +path.setFillType(SkPath::kEvenOdd_FillType); +path.moveTo(SkBits2Float(0x4132c174), SkBits2Float(0xc101a9e5)); // 11.1722f, -8.10398f +path.conicTo(SkBits2Float(0x41b7508a), SkBits2Float(0xc1260efe), SkBits2Float(0x41be3618), SkBits2Float(0xc0f736ad), SkBits2Float(0x3f811abd)); // 22.9143f, -10.3787f, 23.7764f, -7.72542f, 1.00863f +path.conicTo(SkBits2Float(0x41c5564b), SkBits2Float(0xc09f7d6d), SkBits2Float(0x415d0934), SkBits2Float(0x3c880a93), SkBits2Float(0x3f79179a)); // 24.6671f, -4.98406f, 13.8147f, 0.0166066f, 0.973016f +path.conicTo(SkBits2Float(0x41c51a48), SkBits2Float(0x40a2603c), SkBits2Float(0x41be3618), SkBits2Float(0x40f736ac), SkBits2Float(0x3f8122f3)); // 24.6378f, 5.07425f, 23.7764f, 7.72542f, 1.00888f +path.conicTo(SkBits2Float(0x41b7056f), SkBits2Float(0x4127dd49), SkBits2Float(0x4132a328), SkBits2Float(0x410215e1), SkBits2Float(0x3f75cbec)); // 22.8777f, 10.4915f, 11.1648f, 8.13034f, 0.960143f +path.conicTo(SkBits2Float(0x41879a3b), SkBits2Float(0x4194b151), SkBits2Float(0x416b1d34), SkBits2Float(0x41a1cdac), SkBits2Float(0x3f8120d4)); // 16.9503f, 18.5866f, 14.6946f, 20.2254f, 1.00881f +path.conicTo(SkBits2Float(0x41471107), SkBits2Float(0x41aee601), SkBits2Float(0x408982d1), SkBits2Float(0x41525939), SkBits2Float(0x3f812d7f)); // 12.4417f, 21.8623f, 4.29722f, 13.1468f, 1.0092f +path.conicTo(SkBits2Float(0x403b5543), SkBits2Float(0x41c80000), SkBits2Float(0x00000000), SkBits2Float(0x41c80000), SkBits2Float(0x3f74a837)); // 2.92708f, 25, 0, 25, 0.955692f +path.conicTo(SkBits2Float(0xc032ca93), SkBits2Float(0x41c80000), SkBits2Float(0xc088418e), SkBits2Float(0x4151ad32), SkBits2Float(0x3f810c2d)); // -2.79361f, 25, -4.258f, 13.1048f, 1.00818f +path.conicTo(SkBits2Float(0xc088e66c), SkBits2Float(0x4152058a), SkBits2Float(0xc0898afc), SkBits2Float(0x41525d9e), SkBits2Float(0x3f840adb)); // -4.27813f, 13.1264f, -4.29822f, 13.1479f, 1.03158f +path.conicTo(SkBits2Float(0xc1470a56), SkBits2Float(0x41aee870), SkBits2Float(0xc16b1d36), SkBits2Float(0x41a1cdac), SkBits2Float(0x3f81099f)); // -12.44f, 21.8635f, -14.6946f, 20.2254f, 1.00811f +path.conicTo(SkBits2Float(0xc187f23a), SkBits2Float(0x41947162), SkBits2Float(0xc132c174), SkBits2Float(0x4101a9e5), SkBits2Float(0x3f7d6873)); // -16.9933f, 18.5554f, -11.1722f, 8.10398f, 0.989875f +path.conicTo(SkBits2Float(0xc1b7508a), SkBits2Float(0x41260efe), SkBits2Float(0xc1be3618), SkBits2Float(0x40f736ad), SkBits2Float(0x3f811abd)); // -22.9143f, 10.3787f, -23.7764f, 7.72542f, 1.00863f +path.conicTo(SkBits2Float(0xc1c5564b), SkBits2Float(0x409f7d6d), SkBits2Float(0xc15d0934), SkBits2Float(0xbc880a93), SkBits2Float(0x3f79179a)); // -24.6671f, 4.98406f, -13.8147f, -0.0166066f, 0.973016f +path.conicTo(SkBits2Float(0xc1c51a48), SkBits2Float(0xc0a2603c), SkBits2Float(0xc1be3618), SkBits2Float(0xc0f736ac), SkBits2Float(0x3f8122f3)); // -24.6378f, -5.07425f, -23.7764f, -7.72542f, 1.00888f +path.conicTo(SkBits2Float(0xc1b7056f), SkBits2Float(0xc127dd49), SkBits2Float(0xc132a328), SkBits2Float(0xc10215e1), SkBits2Float(0x3f75cbec)); // -22.8777f, -10.4915f, -11.1648f, -8.13034f, 0.960143f +path.conicTo(SkBits2Float(0xc1879a3b), SkBits2Float(0xc194b151), SkBits2Float(0xc16b1d34), SkBits2Float(0xc1a1cdac), SkBits2Float(0x3f8120d4)); // -16.9503f, -18.5866f, -14.6946f, -20.2254f, 1.00881f +path.conicTo(SkBits2Float(0xc1471107), SkBits2Float(0xc1aee601), SkBits2Float(0xc08982d1), SkBits2Float(0xc1525939), SkBits2Float(0x3f812d7f)); // -12.4417f, -21.8623f, -4.29722f, -13.1468f, 1.0092f +path.conicTo(SkBits2Float(0xc03b5543), SkBits2Float(0xc1c80000), SkBits2Float(0x00000000), SkBits2Float(0xc1c80000), SkBits2Float(0x3f74a837)); // -2.92708f, -25, 0, -25, 0.955692f +path.conicTo(SkBits2Float(0x4032ca93), SkBits2Float(0xc1c80000), SkBits2Float(0x4088418e), SkBits2Float(0xc151ad32), SkBits2Float(0x3f810c2d)); // 2.79361f, -25, 4.258f, -13.1048f, 1.00818f +path.conicTo(SkBits2Float(0x4088e66c), SkBits2Float(0xc152058a), SkBits2Float(0x40898afc), SkBits2Float(0xc1525d9e), SkBits2Float(0x3f840adb)); // 4.27813f, -13.1264f, 4.29822f, -13.1479f, 1.03158f +path.conicTo(SkBits2Float(0x41470a56), SkBits2Float(0xc1aee870), SkBits2Float(0x416b1d36), SkBits2Float(0xc1a1cdac), SkBits2Float(0x3f81099f)); // 12.44f, -21.8635f, 14.6946f, -20.2254f, 1.00811f +path.conicTo(SkBits2Float(0x4187f23a), SkBits2Float(0xc1947162), SkBits2Float(0x4132c174), SkBits2Float(0xc101a9e5), SkBits2Float(0x3f7d6873)); // 16.9933f, -18.5554f, 11.1722f, -8.10398f, 0.989875f +path.close(); +path.close(); +SkPath one(path); +path.reset(); +path.setFillType(SkPath::kWinding_FillType); +path.moveTo(SkBits2Float(0xbfc5c55c), SkBits2Float(0xc0982b46)); // -1.54509f, -4.75528f +path.conicTo(SkBits2Float(0x41b1d9c2), SkBits2Float(0xc147b0fc), SkBits2Float(0x41be3618), SkBits2Float(0xc0f736b3), SkBits2Float(0x3f3504f3)); // 22.2313f, -12.4807f, 23.7764f, -7.72543f, 0.707107f +path.conicTo(SkBits2Float(0x41ca926e), SkBits2Float(0xc03e16da), SkBits2Float(0x3fc5c55c), SkBits2Float(0x40982b46), SkBits2Float(0x3f3504f3)); // 25.3215f, -2.97014f, 1.54509f, 4.75528f, 0.707107f +path.conicTo(SkBits2Float(0xc1b1d9c2), SkBits2Float(0x4147b0fc), SkBits2Float(0xc1be3618), SkBits2Float(0x40f736b3), SkBits2Float(0x3f3504f3)); // -22.2313f, 12.4807f, -23.7764f, 7.72543f, 0.707107f +path.conicTo(SkBits2Float(0xc1ca926e), SkBits2Float(0x403e16da), SkBits2Float(0xbfc5c55c), SkBits2Float(0xc0982b46), SkBits2Float(0x3f3504f3)); // -25.3215f, 2.97014f, -1.54509f, -4.75528f, 0.707107f +path.close(); +SkPath two(path); +SkPath result; +Op(one, two, kUnion_SkPathOp, &result); +} + +const char ovalsAsQuads[] = "M 146.4187316894531 136.5" +" Q 146.4187316894531 139.8508911132812 146.4066772460938 143.19775390625" +" Q 146.3946533203125 146.5446166992188 146.3705749511719 149.8793640136719" +" Q 146.3465270996094 153.214111328125 146.3104858398438 156.5287170410156" +" Q 146.2744750976562 159.8433227539062 146.2265930175781 163.1298217773438" +" Q 146.1786804199219 166.4163208007812 146.1190490722656 169.6668090820312" +" Q 146.0593872070312 172.9172973632812 145.9881286621094 176.1238708496094" +" Q 145.9168701171875 179.3304443359375 145.8341674804688 182.4854736328125" +" Q 145.75146484375 185.6404418945312 145.6575317382812 188.7362670898438" +" Q 145.5635681152344 191.8320922851562 145.4586181640625 194.8612365722656" +" Q 145.3536682128906 197.8904113769531 145.2379455566406 200.8456420898438" +" Q 145.1222229003906 203.8008422851562 144.9960021972656 206.6750183105469" +" Q 144.8698120117188 209.5491638183594 144.7334289550781 212.3353271484375" +" Q 144.5970458984375 215.1214599609375 144.4508056640625 217.8129272460938" +" Q 144.3045654296875 220.50439453125 144.1488342285156 223.0946655273438" +" Q 143.9931030273438 225.6849365234375 143.8282470703125 228.1677856445312" +" Q 143.6633911132812 230.650634765625 143.4898071289062 233.0200805664062" +" Q 143.3162231445312 235.3894958496094 143.1343078613281 237.6398315429688" +" Q 142.9524230957031 239.89013671875 142.7626647949219 242.0159301757812" +" Q 142.5729064941406 244.1417236328125 142.375732421875 246.1378173828125" +" Q 142.1785583496094 248.1339111328125 141.9744262695312 249.99560546875" +" Q 141.7703247070312 251.8572387695312 141.5597534179688 253.5799255371094" +" Q 141.3492126464844 255.3026428222656 141.1326904296875 256.8822326660156" +" Q 140.9161987304688 258.4617919921875 140.6942749023438 259.8945007324219" +" Q 140.4723510742188 261.3271789550781 140.2455749511719 262.6095275878906" +" Q 140.0187683105469 263.8918762207031 139.7876281738281 265.020751953125" +" Q 139.5564880371094 266.1496276855469 139.3215637207031 267.1223449707031" +" Q 139.086669921875 268.0950622558594 138.8485412597656 268.9092712402344" +" Q 138.6104125976562 269.7234497070312 138.36962890625 270.3771667480469" +" Q 138.1288757324219 271.0308837890625 137.8860473632812 271.5225830078125" +" Q 137.6432189941406 272.0142822265625 137.3988952636719 272.3427124023438" +" Q 137.1546020507812 272.6711730957031 136.9093933105469 272.8355712890625" +" Q 136.6642150878906 272.9999694824219 136.4187316894531 273" +" Q 136.1732482910156 272.9999694824219 135.9280700683594 272.8355712890625" +" Q 135.682861328125 272.6711730957031 135.4385681152344 272.3427124023438" +" Q 135.1942443847656 272.0142822265625 134.951416015625 271.5225830078125" +" Q 134.7085876464844 271.0308837890625 134.4678344726562 270.3771667480469" +" Q 134.22705078125 269.7234497070312 133.9889221191406 268.9092407226562" +" Q 133.7507934570312 268.0950622558594 133.5158996582031 267.122314453125" +" Q 133.2809753417969 266.1495971679688 133.0498352050781 265.020751953125" +" Q 132.8186950683594 263.8918762207031 132.5918884277344 262.6095275878906" +" Q 132.3651123046875 261.3271789550781 132.1431884765625 259.8945007324219" +" Q 131.9212646484375 258.4617919921875 131.7047729492188 256.8822326660156" +" Q 131.4882507324219 255.3026428222656 131.2777099609375 253.5799560546875" +" Q 131.067138671875 251.8572387695312 130.863037109375 249.99560546875" +" Q 130.6589050292969 248.1339111328125 130.4617309570312 246.1378173828125" +" Q 130.2645568847656 244.1417236328125 130.0747985839844 242.0159301757812" +" Q 129.8850402832031 239.89013671875 129.7031555175781 237.6398315429688" +" Q 129.521240234375 235.3894958496094 129.34765625 233.0200805664062" +" Q 129.174072265625 230.650634765625 129.0092163085938 228.1677856445312" +" Q 128.8443603515625 225.6849365234375 128.6886291503906 223.0946655273438" +" Q 128.5328979492188 220.50439453125 128.3866577148438 217.8129272460938" +" Q 128.2404174804688 215.1214599609375 128.1040344238281 212.3353271484375" +" Q 127.9676513671875 209.5491333007812 127.8414306640625 206.6749877929688" +" Q 127.7152404785156 203.8008422851562 127.5995178222656 200.8456420898438" +" Q 127.4837951660156 197.8904113769531 127.3788452148438 194.8612365722656" +" Q 127.2738647460938 191.8320922851562 127.179931640625 188.7362670898438" +" Q 127.0859985351562 185.6404418945312 127.0032958984375 182.4854431152344" +" Q 126.9205932617188 179.3304443359375 126.8493347167969 176.1238708496094" +" Q 126.778076171875 172.9172973632812 126.7184143066406 169.6668090820312" +" Q 126.6587829589844 166.4163208007812 126.6108703613281 163.1298217773438" +" Q 126.56298828125 159.8433227539062 126.5269775390625 156.5287170410156" +" Q 126.4909362792969 153.214111328125 126.4668884277344 149.8793640136719" +" Q 126.4428100585938 146.5446166992188 126.4307861328125 143.19775390625" +" Q 126.4187316894531 139.8508911132812 126.4187316894531 136.5" +" Q 126.4187316894531 133.1491088867188 126.4307861328125 129.8022613525391" +" Q 126.4428100585938 126.4554138183594 126.4668884277344 123.1206665039062" +" Q 126.4909362792969 119.7859039306641 126.5269775390625 116.4712829589844" +" Q 126.56298828125 113.1566619873047 126.6108703613281 109.8701629638672" +" Q 126.6587829589844 106.5836639404297 126.7184143066406 103.3331909179688" +" Q 126.778076171875 100.0827331542969 126.8493347167969 96.87612915039062" +" Q 126.9205932617188 93.66952514648438 127.0032958984375 90.5145263671875" +" Q 127.0859985351562 87.35951232910156 127.179931640625 84.26370239257812" +" Q 127.2738647460938 81.16787719726562 127.3788452148438 78.13871765136719" +" Q 127.4837951660156 75.10955810546875 127.5995178222656 72.15434265136719" +" Q 127.7152404785156 69.19912719726562 127.8414306640625 66.32498168945312" +" Q 127.9676513671875 63.45082092285156 128.1040344238281 60.66465759277344" +" Q 128.2404174804688 57.87849426269531 128.3866577148438 55.18704223632812" +" Q 128.5328979492188 52.49559020996094 128.6886291503906 49.90530395507812" +" Q 128.8443603515625 47.31504821777344 129.0092163085938 44.83219909667969" +" Q 129.174072265625 42.349365234375 129.34765625 39.97991943359375" +" Q 129.521240234375 37.61048889160156 129.7031555175781 35.36016845703125" +" Q 129.8850402832031 33.10984802246094 130.0747985839844 30.98406982421875" +" Q 130.2645568847656 28.85829162597656 130.4617309570312 26.86216735839844" +" Q 130.6589050292969 24.86604309082031 130.863037109375 23.00439453125" +" Q 131.067138671875 21.14274597167969 131.2777099609375 19.4200439453125" +" Q 131.4882507324219 17.69734191894531 131.7047729492188 16.11775207519531" +" Q 131.9212646484375 14.53814697265625 132.1431884765625 13.10545349121094" +" Q 132.3651123046875 11.67277526855469 132.5918884277344 10.39044189453125" +" Q 132.8186950683594 9.108108520507812 133.0498352050781 7.979232788085938" +" Q 133.2809753417969 6.850357055664062 133.5158996582031 5.877639770507812" +" Q 133.7507934570312 4.904937744140625 133.9889221191406 4.090728759765625" +" Q 134.22705078125 3.276535034179688 134.4678344726562 2.622810363769531" +" Q 134.7085876464844 1.969085693359375 134.951416015625 1.477409362792969" +" Q 135.1942443847656 0.9857254028320312 135.4385681152344 0.65728759765625" +" Q 135.682861328125 0.3288421630859375 135.9280700683594 0.1644210815429688" +" Q 136.1732482910156 0 136.4187316894531 0" +" Q 136.6642150878906 0 136.9093933105469 0.1644210815429688" +" Q 137.1546020507812 0.3288421630859375 137.3988952636719 0.65728759765625" +" Q 137.6432189941406 0.9857254028320312 137.8860473632812 1.477409362792969" +" Q 138.1288757324219 1.969085693359375 138.36962890625 2.622810363769531" +" Q 138.6104125976562 3.276535034179688 138.8485412597656 4.090728759765625" +" Q 139.086669921875 4.904937744140625 139.3215637207031 5.877639770507812" +" Q 139.5564880371094 6.850357055664062 139.7876281738281 7.979232788085938" +" Q 140.0187683105469 9.108108520507812 140.2455749511719 10.39044189453125" +" Q 140.4723510742188 11.67277526855469 140.6942749023438 13.10545349121094" +" Q 140.9161987304688 14.53814697265625 141.1326904296875 16.11775207519531" +" Q 141.3492126464844 17.69734191894531 141.5597534179688 19.4200439453125" +" Q 141.7703247070312 21.14274597167969 141.9744262695312 23.00439453125" +" Q 142.1785583496094 24.86604309082031 142.375732421875 26.86216735839844" +" Q 142.5729064941406 28.85829162597656 142.7626647949219 30.98406982421875" +" Q 142.9524230957031 33.10984802246094 143.1343078613281 35.36016845703125" +" Q 143.3162231445312 37.61048889160156 143.4898071289062 39.97991943359375" +" Q 143.6633911132812 42.349365234375 143.8282470703125 44.83219909667969" +" Q 143.9931030273438 47.31504821777344 144.1488342285156 49.90531921386719" +" Q 144.3045654296875 52.49559020996094 144.4508056640625 55.18704223632812" +" Q 144.5970458984375 57.87849426269531 144.7334289550781 60.66465759277344" +" Q 144.8698120117188 63.45082092285156 144.9960021972656 66.32498168945312" +" Q 145.1222229003906 69.19912719726562 145.2379455566406 72.15434265136719" +" Q 145.3536682128906 75.10955810546875 145.4586181640625 78.13871765136719" +" Q 145.5635681152344 81.16787719726562 145.6575317382812 84.26370239257812" +" Q 145.75146484375 87.35951232910156 145.8341674804688 90.5145263671875" +" Q 145.9168701171875 93.66952514648438 145.9881286621094 96.87614440917969" +" Q 146.0593872070312 100.0827331542969 146.1190490722656 103.3332061767578" +" Q 146.1786804199219 106.5836639404297 146.2265930175781 109.8701629638672" +" Q 146.2744750976562 113.1566619873047 146.3104858398438 116.4712829589844" +" Q 146.3465270996094 119.785888671875 146.3705749511719 123.1206665039062" +" Q 146.3946533203125 126.4554138183594 146.4066772460938 129.8022613525391" +" Q 146.4187316894531 133.1491088867188 146.4187316894531 136.5 Z"; + +#include "SkParsePath.h" + +DEF_TEST(PathOpsOvalsAsQuads, reporter) { + return; // don't execute this for now + SkPath path; + SkParsePath::FromSVGString(ovalsAsQuads, &path); + Simplify(path, &path); +} + +DEF_TEST(PathOps64OvalsAsQuads, reporter) { + return; // don't execute this for now + SkPath path, result; + SkOpBuilder builder; + SkParsePath::FromSVGString(ovalsAsQuads, &path); + OvalSet set = {{0, 0, 0, 0}, 2, 3, 9, 100, 100}; + testOvalSet(set, path, &builder, nullptr, &result); +}
diff --git a/src/third_party/skia/tests/PathOpsBuilderTest.cpp b/src/third_party/skia/tests/PathOpsBuilderTest.cpp new file mode 100644 index 0000000..ffeff5f --- /dev/null +++ b/src/third_party/skia/tests/PathOpsBuilderTest.cpp
@@ -0,0 +1,363 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "PathOpsExtendedTest.h" +#include "PathOpsTestCommon.h" +#include "SkBitmap.h" +#include "Test.h" + +DEF_TEST(PathOpsBuilder, reporter) { + SkOpBuilder builder; + SkPath result; + REPORTER_ASSERT(reporter, builder.resolve(&result)); + REPORTER_ASSERT(reporter, result.isEmpty()); + + builder.add(result, kDifference_SkPathOp); + REPORTER_ASSERT(reporter, builder.resolve(&result)); + REPORTER_ASSERT(reporter, result.isEmpty()); + + builder.add(result, kUnion_SkPathOp); + REPORTER_ASSERT(reporter, builder.resolve(&result)); + REPORTER_ASSERT(reporter, result.isEmpty()); + + SkPath rectPath; + rectPath.setFillType(SkPath::kEvenOdd_FillType); + rectPath.addRect(0, 1, 2, 3, SkPath::kCW_Direction); + builder.add(rectPath, kUnion_SkPathOp); + REPORTER_ASSERT(reporter, builder.resolve(&result)); + bool closed; + SkPath::Direction dir; + REPORTER_ASSERT(reporter, result.isRect(nullptr, &closed, &dir)); + REPORTER_ASSERT(reporter, closed); + REPORTER_ASSERT(reporter, dir == SkPath::kCCW_Direction); + int pixelDiff = comparePaths(reporter, __FUNCTION__, rectPath, result); + REPORTER_ASSERT(reporter, pixelDiff == 0); + + rectPath.reset(); + rectPath.setFillType(SkPath::kEvenOdd_FillType); + rectPath.addRect(0, 1, 2, 3, SkPath::kCCW_Direction); + builder.add(rectPath, kUnion_SkPathOp); + REPORTER_ASSERT(reporter, builder.resolve(&result)); + REPORTER_ASSERT(reporter, result.isRect(nullptr, &closed, &dir)); + REPORTER_ASSERT(reporter, closed); + REPORTER_ASSERT(reporter, dir == SkPath::kCCW_Direction); + REPORTER_ASSERT(reporter, rectPath == result); + + builder.add(rectPath, kDifference_SkPathOp); + REPORTER_ASSERT(reporter, builder.resolve(&result)); + REPORTER_ASSERT(reporter, result.isEmpty()); + + SkPath rect2, rect3; + rect2.addRect(2, 1, 4, 3, SkPath::kCW_Direction); + rect3.addRect(4, 1, 5, 3, SkPath::kCCW_Direction); + builder.add(rectPath, kUnion_SkPathOp); + builder.add(rect2, kUnion_SkPathOp); + builder.add(rect3, kUnion_SkPathOp); + REPORTER_ASSERT(reporter, builder.resolve(&result)); + REPORTER_ASSERT(reporter, result.isRect(nullptr, &closed, &dir)); + REPORTER_ASSERT(reporter, closed); + SkRect expected; + expected.set(0, 1, 5, 3); + REPORTER_ASSERT(reporter, result.getBounds() == expected); + + SkPath circle1, circle2, circle3; + circle1.addCircle(5, 6, 4, SkPath::kCW_Direction); + circle2.addCircle(7, 4, 8, SkPath::kCCW_Direction); + circle3.addCircle(6, 5, 6, SkPath::kCW_Direction); + SkPath opCompare; + Op(circle1, circle2, kUnion_SkPathOp, &opCompare); + Op(opCompare, circle3, kDifference_SkPathOp, &opCompare); + builder.add(circle1, kUnion_SkPathOp); + builder.add(circle2, kUnion_SkPathOp); + builder.add(circle3, kDifference_SkPathOp); + REPORTER_ASSERT(reporter, builder.resolve(&result)); + pixelDiff = comparePaths(reporter, __FUNCTION__, opCompare, result); + REPORTER_ASSERT(reporter, pixelDiff == 0); +} + +DEF_TEST(BuilderIssue3838, reporter) { + SkPath path; + path.moveTo(200, 170); + path.lineTo(220, 170); + path.lineTo(220, 230); + path.lineTo(240, 230); + path.lineTo(240, 210); + path.lineTo(180, 210); + path.lineTo(180, 190); + path.lineTo(260, 190); + path.lineTo(260, 250); + path.lineTo(200, 250); + path.lineTo(200, 170); + path.close(); + SkPath path2; + SkOpBuilder builder; + builder.add(path, kUnion_SkPathOp); + builder.resolve(&path2); + int pixelDiff = comparePaths(reporter, __FUNCTION__, path, path2); + REPORTER_ASSERT(reporter, pixelDiff == 0); +} + +DEF_TEST(BuilderIssue3838_2, reporter) { + SkPath path; + path.addCircle(100, 100, 50); + + SkOpBuilder builder; + builder.add(path, kUnion_SkPathOp); + builder.add(path, kUnion_SkPathOp); + + SkPath result; + builder.resolve(&result); + int pixelDiff = comparePaths(reporter, __FUNCTION__, path, result); + REPORTER_ASSERT(reporter, pixelDiff == 0); +} + +DEF_TEST(BuilderIssue3838_3, reporter) { + SkPath path; + path.moveTo(40, 10); + path.lineTo(60, 10); + path.lineTo(60, 30); + path.lineTo(40, 30); + path.lineTo(40, 10); + path.moveTo(41, 11); + path.lineTo(41, 29); + path.lineTo(59, 29); + path.lineTo(59, 11); + path.lineTo(41, 11); + + SkOpBuilder builder; + builder.add(path, kUnion_SkPathOp); + SkPath result; + builder.resolve(&result); + int pixelDiff = comparePaths(reporter, __FUNCTION__, path, result); + REPORTER_ASSERT(reporter, pixelDiff == 0); +} + +DEF_TEST(BuilderIssue502792_2, reporter) { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.addRect(0, 0, 1, 1, SkPath::kCW_Direction); + path.addRect(2, 2, 3, 3, SkPath::kCW_Direction); + pathB.setFillType(SkPath::kEvenOdd_FillType); + pathB.addRect(3, 3, 4, 4, SkPath::kCW_Direction); + pathB.addRect(3, 3, 4, 4, SkPath::kCW_Direction); + SkOpBuilder builder; + builder.add(path, kUnion_SkPathOp); + builder.add(pathB, kDifference_SkPathOp); + SkPath result; + builder.resolve(&result); +} + +DEF_TEST(Fuzz846, reporter) { +/* +<clipPath id="clip-circle"> + <circle id="circle" cx="60" cy="60" r="50" /> +</clipPath> +<clipPath id="clip-rect"> + <clipPath id="clip-rect"> + <clipPath id="clip-rect"> + <clipPath id="clip-rect"> + <rect x="10" y="30" width="0" height="60" /> + <rect x="10" y="30" width="0" height="60" /> + <rect x="10" y="30" width="100" height="60" /> + <rect x="10" y="30" width="32668" /> + <rect x="10" y="30" width="100" height="18446744073709551615" /> + <rect x="10" y="255" width="100" height="60" /> + <rect width="100" height="60" /> + <rect x="10" y="30" width="100" height="60" /> + <rect x="10" y="30" width="100" height="4294967236" /> + <rect x="10" y="30" width="100" height="60" /> + </clipPath> + <rect x="10" y="30" width="0" height="60" /> + <rect x="10" y="30" width="0" height="0.18093252719929986369568203" /> + <rect x="10" y="30" width="100" height="60" /> + <rect x="10" y="30" width="32668" height="60" /> + <rect x="10" y="30" width="100" height="18446744073709551615" /> + <rect x="10" y="255" width="100" height="60" /> + <rect x="2147483649" y="30" width="100" height="60" /> + <rect x="10" y="30" width="100" height="60" /> + <rect x="10" y="30" width="100" height="60" /> + <rect x="10" y="30" width="100" height="60" /> + </clipPath> + <rect x="10" y="30" width="0" height="60" /> + <rect x="10" y="30" width="0" height="60" /> + <rect x="10" y="30" width="100" height="60" /> + <rect x="10" y="30" width="32668" height="60" /> + <rect x="10" y="30" width="100" height="18446744073709551615" /> + <rect x="10" y="255" width="100" height="60" /> + <rect x="2147483649" y="30" width="100" height="60" /> + <rect x="10" y="30" width="100" height="60" /> + <rect x="10" y="2879753595" width="100" height="60" /> + <rect x="10" y="30" width="100" height="60" /> + </clipPath> + <rect x="10" y="30" width="100" height="60" /> + <rect x="10" y="30" width="0" height="60" /> + <rect x="10" y="30" width="100" height="60" /> + <rect x="10" y="30" width="32668" height="60" /> + <rect x="10" y="30" width="100" height="18446744073709551615" /> + <rect x="10" y="255" width="100" height="60" /> + <rect x="2147483649" y="30" width="100" height="60" /> + <rect x="10" y="30" width="100" height="60" /> + <rect x="10" y="30" width="100" height="4294967236" /> + <rect x="10" y="30" width="100" height="4294967236" /> + <rect x="10" y="30" width="100" height="4294967236" /> + <rect x="10" y="30" width="100" height="4294967236" /> + <rect x="10" y="30" width="100" height="60" /> + <rect x="757798030" y="30" width="100" height="60" /> +*/ + SkPath clipCircle, clipRect; + SkPath inner; + clipCircle.addCircle(60, 60, 50); // <circle id="circle" cx="60" cy="60" r="50" /> + + inner.addRect(10, 30, 10+0, 30+60); // <rect x="10" y="30" width="0" height="60" /> + inner.addRect(10, 30, 10+0, 30+60); // <rect x="10" y="30" width="0" height="60" /> + inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" /> + inner.addRect(10, 30, 10+32668, 30+0); // <rect x="10" y="30" width="32668" /> + inner.addRect(10, 30, 10+100, 30+18446744073709551615.f); // <rect x="10" y="30" width="100" height="18446744073709551615" /> + inner.addRect(10, 255, 10+100, 255+60); // <rect x="10" y="255" width="100" height="60" /> + inner.addRect(0, 0, 0+100, 0+60); // <rect width="100" height="60" /> + inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" /> + inner.addRect(10, 30, 10+100, 30+4294967236.f); // <rect x="10" y="30" width="100" height="4294967236" /> + inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" /> + clipRect.addPath(inner); + inner.reset(); + inner.addRect(10, 30, 10+0, 30+60); // <rect x="10" y="30" width="0" height="60" /> + inner.addRect(10, 30, 10+0, 30+0.18093252719929986369568203f); // <rect x="10" y="30" width="0" height="0.18093252719929986369568203" /> + inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" /> + inner.addRect(10, 30, 10+32668, 30+60); // <rect x="10" y="30" width="32668" height="60" /> + inner.addRect(10, 30, 10+100, 30+18446744073709551615.f); // <rect x="10" y="30" width="100" height="18446744073709551615" /> + inner.addRect(10, 255, 10+100, 255+60); // <rect x="10" y="255" width="100" height="60" /> + inner.addRect(2147483649.f, 30, 2147483649.f+100, 30+60); // <rect x="2147483649" y="30" width="100" height="60" /> + inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" /> + inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" /> + inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" /> + clipRect.addPath(inner); + inner.reset(); + inner.addRect(10, 30, 10+0, 30+60); // <rect x="10" y="30" width="0" height="60" /> + inner.addRect(10, 30, 10+0, 30+60); // <rect x="10" y="30" width="0" height="60" /> + inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" /> + inner.addRect(10, 30, 10+32668, 30+60); // <rect x="10" y="30" width="32668" height="60" /> + inner.addRect(10, 30, 10+100, 30+18446744073709551615.f); // <rect x="10" y="30" width="100" height="18446744073709551615" /> + inner.addRect(10, 255, 10+100, 255+60); // <rect x="10" y="255" width="100" height="60" /> + inner.addRect(2147483649.f, 30, 2147483649.f+100, 30+60); // <rect x="2147483649" y="30" width="100" height="60" /> + inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" /> + inner.addRect(10, 2879753595.f, 10+100, 30+2879753595.f); // <rect x="10" y="2879753595" width="100" height="60" /> + inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" /> + clipRect.addPath(inner); + inner.reset(); + inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" /> + inner.addRect(10, 30, 10+0, 30+60); // <rect x="10" y="30" width="0" height="60" /> + inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" /> + inner.addRect(10, 30, 10+32668, 30+60); // <rect x="10" y="30" width="32668" height="60" /> + inner.addRect(10, 30, 10+100, 30+18446744073709551615.f); // <rect x="10" y="30" width="100" height="18446744073709551615" /> + inner.addRect(10, 255, 10+100, 255+60); // <rect x="10" y="255" width="100" height="60" /> + inner.addRect(2147483649.f, 30, 2147483649.f+100, 30+60); // <rect x="2147483649" y="30" width="100" height="60" /> + inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" /> + inner.addRect(10, 30, 10+100, 30+4294967236.f); // <rect x="10" y="30" width="100" height="4294967236" /> + inner.addRect(10, 30, 10+100, 30+4294967236.f); // <rect x="10" y="30" width="100" height="4294967236" /> + inner.addRect(10, 30, 10+100, 30+4294967236.f); // <rect x="10" y="30" width="100" height="4294967236" /> + inner.addRect(10, 30, 10+100, 30+4294967236.f); // <rect x="10" y="30" width="100" height="4294967236" /> + inner.addRect(10, 30, 10+100, 30+60); // <rect x="10" y="30" width="100" height="60" /> + inner.addRect(757798030.f, 30, 757798030.f+100, 30+60); // <rect x="757798030" y="30" width="100" height="60" /> + clipRect.addPath(inner); + + SkOpBuilder builder; + builder.add(clipCircle, kUnion_SkPathOp); + builder.add(clipRect, kDifference_SkPathOp); + SkPath result; + builder.resolve(&result); +} + +DEF_TEST(Issue569540, reporter) { + SkPath path1; + path1.moveTo(5, -225); + path1.lineTo(-225, 7425); + path1.lineTo(7425, 7425); + path1.lineTo(7425, -225); + path1.lineTo(-225, -225); + path1.lineTo(5, -225); + path1.close(); + + SkPath path2; + path2.moveTo(5940, 2790); + path2.lineTo(5940, 2160); + path2.lineTo(5970, 1980); + path2.lineTo(5688, 773669888); + path2.lineTo(5688, 2160); + path2.lineTo(5688, 2430); + path2.lineTo(5400, 4590); + path2.lineTo(5220, 4590); + path2.lineTo(5220, 4920); + path2.cubicTo(5182.22900390625f, 4948.328125f, 5160, 4992.78662109375f, 5160, 5040.00048828125f); + path2.lineTo(5940, 2790); + path2.close(); + + SkOpBuilder builder; + builder.add(path1, kUnion_SkPathOp); + builder.add(path2, kUnion_SkPathOp); + SkPath result; + builder.resolve(&result); +} + +DEF_TEST(SkOpBuilderFuzz665, reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); +path.moveTo(SkBits2Float(0xcc4264a7), SkBits2Float(0x4bb12e50)); // -5.0959e+07f, 2.32235e+07f +path.lineTo(SkBits2Float(0xcc4264b0), SkBits2Float(0x4bb12e48)); // -5.0959e+07f, 2.32234e+07f +path.lineTo(SkBits2Float(0xcc4264a7), SkBits2Float(0x4bb12e50)); // -5.0959e+07f, 2.32235e+07f +path.close(); + SkPath path1(path); + path.reset(); + path.setFillType(SkPath::kWinding_FillType); +path.moveTo(SkBits2Float(0x43213333), SkBits2Float(0x43080000)); // 161.2f, 136 +path.lineTo(SkBits2Float(0x43038000), SkBits2Float(0x43080000)); // 131.5f, 136 +path.cubicTo(SkBits2Float(0x43038000), SkBits2Float(0x42f00000), SkBits2Float(0x42f16666), SkBits2Float(0x42d53333), SkBits2Float(0x42d3cccd), SkBits2Float(0x42cd6666)); // 131.5f, 120, 120.7f, 106.6f, 105.9f, 102.7f +path.lineTo(SkBits2Float(0x42e33333), SkBits2Float(0x42940000)); // 113.6f, 74 + SkPath path2(path); + SkOpBuilder builder; + builder.add(path1, kUnion_SkPathOp); + builder.add(path2, kUnion_SkPathOp); + SkPath result; + builder.resolve(&result); +} + +DEF_TEST(SkOpBuilder618991, reporter) { + SkPath path0; + path0.moveTo(140, 40); + path0.lineTo(200, 210); + path0.lineTo(40, 100); + path0.lineTo(2.22223e+07f, 2.22222e+14f); + path0.lineTo(2.22223e+07f, 2.22222e+14f); + + SkPath path1; + path1.moveTo(160, 60); + path1.lineTo(220, 230); + path1.lineTo(60, 120); + path1.lineTo(2.22223e+07f, 2.22222e+14f); + path1.lineTo(2.22223e+07f, 2.22222e+14f); + + SkOpBuilder builder; + builder.add(path0, SkPathOp::kUnion_SkPathOp); + builder.add(path1, SkPathOp::kUnion_SkPathOp); + builder.resolve(&path0); +} + +DEF_TEST(SkOpBuilderKFuzz1, reporter) { + SkPath path; +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0x39008001), SkBits2Float(0xd31fbc1d)); // 0.000122547f, -6.86056e+11f +path.conicTo(SkBits2Float(0x246a205a), SkBits2Float(0x0080d3fb), SkBits2Float(0xce000001), SkBits2Float(0x04d31fbc), SkBits2Float(0x57a82c00)); // 5.07681e-17f, 1.1831e-38f, -5.36871e+08f, 4.9635e-36f, 3.69814e+14f + SkPath path0(path); + path.reset(); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.cubicTo(SkBits2Float(0x80d3f924), SkBits2Float(0xcecece4f), SkBits2Float(0xcececece), SkBits2Float(0xcececece), SkBits2Float(0x9a9a9ace), SkBits2Float(0x9a9a9a9a)); // -1.94667e-38f, -1.73481e+09f, -1.73483e+09f, -1.73483e+09f, -6.3943e-23f, -6.39427e-23f +path.moveTo(SkBits2Float(0x9a9a019a), SkBits2Float(0xa59a9a9a)); // -6.36955e-23f, -2.68195e-16f + SkPath path1(path); +SkOpBuilder builder; + builder.add(path0, SkPathOp::kUnion_SkPathOp); + builder.add(path1, SkPathOp::kUnion_SkPathOp); + builder.resolve(&path); +}
diff --git a/src/third_party/skia/tests/PathOpsChalkboardTest.cpp b/src/third_party/skia/tests/PathOpsChalkboardTest.cpp new file mode 100644 index 0000000..8c74e7f --- /dev/null +++ b/src/third_party/skia/tests/PathOpsChalkboardTest.cpp
@@ -0,0 +1,182 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "PathOpsExtendedTest.h" +#include "PathOpsThreadedCommon.h" +#include "SkRandom.h" + +#define TEST(name) { name, #name } + +static void chalkboard(skiatest::Reporter* reporter, uint64_t testlines) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +uint64_t i = 0; +path.moveTo(SkBits2Float(0x4470eed9), SkBits2Float(0x439c1ac1)); // 963.732f, 312.209f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4470dde3), SkBits2Float(0x439c63d8)); // 963.467f, 312.78f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470dbd7), SkBits2Float(0x439c3e57), SkBits2Float(0x4470c893), SkBits2Float(0x439c69fd), SkBits2Float(0x4470cfcf), SkBits2Float(0x439c297a)); // 963.435f, 312.487f, 963.134f, 312.828f, 963.247f, 312.324f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470c46b), SkBits2Float(0x439c8149), SkBits2Float(0x4470b137), SkBits2Float(0x439c2938), SkBits2Float(0x4470b5f4), SkBits2Float(0x439ca99b)); // 963.069f, 313.01f, 962.769f, 312.322f, 962.843f, 313.325f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470e842), SkBits2Float(0x439c8335), SkBits2Float(0x447125a2), SkBits2Float(0x439cce78), SkBits2Float(0x44715a2d), SkBits2Float(0x439c61ed)); // 963.629f, 313.025f, 964.588f, 313.613f, 965.409f, 312.765f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x447150d5), SkBits2Float(0x439c945c)); // 965.263f, 313.159f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4471546b), SkBits2Float(0x439c87f2), SkBits2Float(0x4471579e), SkBits2Float(0x439c8085), SkBits2Float(0x44715a8f), SkBits2Float(0x439c7c4c)); // 965.319f, 313.062f, 965.369f, 313.004f, 965.415f, 312.971f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715cbc), SkBits2Float(0x439c79dd)); // 965.449f, 312.952f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715dd3), SkBits2Float(0x439c7918)); // 965.466f, 312.946f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715e56), SkBits2Float(0x439c78d6)); // 965.474f, 312.944f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715e77), SkBits2Float(0x439c78b5)); // 965.476f, 312.943f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715e77), SkBits2Float(0x439c78b5)); // 965.476f, 312.943f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715e87), SkBits2Float(0x439c78b5)); // 965.477f, 312.943f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4471a50e), SkBits2Float(0x439d05c3), SkBits2Float(0x4470fe77), SkBits2Float(0x439bb894), SkBits2Float(0x44710f9e), SkBits2Float(0x439bdb03)); // 966.579f, 314.045f, 963.976f, 311.442f, 964.244f, 311.711f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44710fae), SkBits2Float(0x439bdb24)); // 964.245f, 311.712f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44710fbe), SkBits2Float(0x439bdba7)); // 964.246f, 311.716f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44710fce), SkBits2Float(0x439be397)); // 964.247f, 311.778f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44710eb7), SkBits2Float(0x439bedf5), SkBits2Float(0x44710978), SkBits2Float(0x439bf74d), SkBits2Float(0x447105e2), SkBits2Float(0x439c0064)); // 964.23f, 311.859f, 964.148f, 311.932f, 964.092f, 312.003f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470fe86), SkBits2Float(0x439c1270), SkBits2Float(0x4470fd4f), SkBits2Float(0x439c2250), SkBits2Float(0x44712fde), SkBits2Float(0x439c33d9)); // 963.977f, 312.144f, 963.958f, 312.268f, 964.748f, 312.405f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4470fc48), SkBits2Float(0x439c3271)); // 963.942f, 312.394f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470ee13), SkBits2Float(0x439c4c2b), SkBits2Float(0x4471476b), SkBits2Float(0x439c5c0b), SkBits2Float(0x44711177), SkBits2Float(0x439c7a40)); // 963.72f, 312.595f, 965.116f, 312.719f, 964.273f, 312.955f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44712685), SkBits2Float(0x439c7648)); // 964.602f, 312.924f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x447126a6), SkBits2Float(0x439c7d31), SkBits2Float(0x44711d2d), SkBits2Float(0x439c8085), SkBits2Float(0x44711d1d), SkBits2Float(0x439c8790)); // 964.604f, 312.978f, 964.456f, 313.004f, 964.455f, 313.059f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44712675), SkBits2Float(0x439c843c)); // 964.601f, 313.033f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44713bd5), SkBits2Float(0x439c94e0), SkBits2Float(0x44713956), SkBits2Float(0x439ca065), SkBits2Float(0x44712b63), SkBits2Float(0x439cb357)); // 964.935f, 313.163f, 964.896f, 313.253f, 964.678f, 313.401f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44711af0), SkBits2Float(0x439cb5a5), SkBits2Float(0x44712459), SkBits2Float(0x439cab47), SkBits2Float(0x44711fad), SkBits2Float(0x439ca607)); // 964.421f, 313.419f, 964.568f, 313.338f, 964.495f, 313.297f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44710f1a), SkBits2Float(0x439caf3e), SkBits2Float(0x4471325d), SkBits2Float(0x439cbb26), SkBits2Float(0x4471326e), SkBits2Float(0x439cc93a)); // 964.236f, 313.369f, 964.787f, 313.462f, 964.788f, 313.572f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44712428), SkBits2Float(0x439cd501), SkBits2Float(0x44711ad0), SkBits2Float(0x439cca82), SkBits2Float(0x447113b6), SkBits2Float(0x439cc95b)); // 964.565f, 313.664f, 964.419f, 313.582f, 964.308f, 313.573f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44712b95), SkBits2Float(0x439cf20f), SkBits2Float(0x4470f550), SkBits2Float(0x439d0790), SkBits2Float(0x4471426e), SkBits2Float(0x439d21ce)); // 964.681f, 313.891f, 963.833f, 314.059f, 965.038f, 314.264f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44715072), SkBits2Float(0x439d241c), SkBits2Float(0x44715c6a), SkBits2Float(0x439d15a5), SkBits2Float(0x44716364), SkBits2Float(0x439d24c0)); // 965.257f, 314.282f, 965.444f, 314.169f, 965.553f, 314.287f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44717b22), SkBits2Float(0x439d0791), SkBits2Float(0x44715cbc), SkBits2Float(0x439cf231), SkBits2Float(0x4471475c), SkBits2Float(0x439cda20)); // 965.924f, 314.059f, 965.449f, 313.892f, 965.115f, 313.704f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4471477d), SkBits2Float(0x439ce12a)); // 965.117f, 313.759f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470fc4a), SkBits2Float(0x439cd14b), SkBits2Float(0x44715810), SkBits2Float(0x439cd0e8), SkBits2Float(0x4471372b), SkBits2Float(0x439cb272)); // 963.942f, 313.635f, 965.376f, 313.632f, 964.862f, 313.394f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x447155b2), SkBits2Float(0x439cb91a), SkBits2Float(0x44715581), SkBits2Float(0x439cc72e), SkBits2Float(0x447165f4), SkBits2Float(0x439ccbeb)); // 965.339f, 313.446f, 965.336f, 313.556f, 965.593f, 313.593f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44719e77), SkBits2Float(0x439ca2b4), SkBits2Float(0x44713979), SkBits2Float(0x439c993b), SkBits2Float(0x4471821d), SkBits2Float(0x439c7b47)); // 966.476f, 313.271f, 964.898f, 313.197f, 966.033f, 312.963f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4471847b), SkBits2Float(0x439c7dd6)); // 966.07f, 312.983f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44718b96), SkBits2Float(0x439c77b1), SkBits2Float(0x44717d81), SkBits2Float(0x439c6ebb), SkBits2Float(0x44717667), SkBits2Float(0x439c66ab)); // 966.181f, 312.935f, 965.961f, 312.865f, 965.85f, 312.802f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44716cff), SkBits2Float(0x439c6a41), SkBits2Float(0x44716842), SkBits2Float(0x439c7315), SkBits2Float(0x44716159), SkBits2Float(0x439c793a)); // 965.703f, 312.83f, 965.629f, 312.899f, 965.521f, 312.947f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44715a0d), SkBits2Float(0x439c712a), SkBits2Float(0x44713938), SkBits2Float(0x439c6f3e), SkBits2Float(0x44712b34), SkBits2Float(0x439c6d73)); // 965.407f, 312.884f, 964.894f, 312.869f, 964.675f, 312.855f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44714c19), SkBits2Float(0x439c614a), SkBits2Float(0x44711af2), SkBits2Float(0x439c61ee), SkBits2Float(0x44712b34), SkBits2Float(0x439c518c)); // 965.189f, 312.76f, 964.421f, 312.765f, 964.675f, 312.637f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x447149ab), SkBits2Float(0x439c499c), SkBits2Float(0x4471474d), SkBits2Float(0x439c5c0b), SkBits2Float(0x447157d0), SkBits2Float(0x439c6065)); // 965.151f, 312.575f, 965.114f, 312.719f, 965.372f, 312.753f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x447142b1), SkBits2Float(0x439c4fa0)); // 965.042f, 312.622f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44714053), SkBits2Float(0x439c3f1d), SkBits2Float(0x44716396), SkBits2Float(0x439c3c6d), SkBits2Float(0x447173f9), SkBits2Float(0x439c3292)); // 965.005f, 312.493f, 965.556f, 312.472f, 965.812f, 312.395f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44715c7c), SkBits2Float(0x439c2628), SkBits2Float(0x44716397), SkBits2Float(0x439c3c4c), SkBits2Float(0x447142b1), SkBits2Float(0x439c3398)); // 965.445f, 312.298f, 965.556f, 312.471f, 965.042f, 312.403f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44715572), SkBits2Float(0x439c2919), SkBits2Float(0x44715bd8), SkBits2Float(0x439c10a6), SkBits2Float(0x447159bb), SkBits2Float(0x439bf68a)); // 965.335f, 312.321f, 965.435f, 312.13f, 965.402f, 311.926f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715698), SkBits2Float(0x439be2f4)); // 965.353f, 311.773f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x447153f8), SkBits2Float(0x439bd95a)); // 965.312f, 311.698f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4471526f), SkBits2Float(0x439bd49e)); // 965.288f, 311.661f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4471524e), SkBits2Float(0x439bd45c)); // 965.286f, 311.659f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4471523e), SkBits2Float(0x439bd41a)); // 965.285f, 311.657f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44717148), SkBits2Float(0x439c124f), SkBits2Float(0x44715ae2), SkBits2Float(0x439be562), SkBits2Float(0x447161cb), SkBits2Float(0x439bf335)); // 965.77f, 312.143f, 965.42f, 311.792f, 965.528f, 311.9f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x447161bb), SkBits2Float(0x439bf356)); // 965.527f, 311.901f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x447161bb), SkBits2Float(0x439bf356)); // 965.527f, 311.901f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44716169), SkBits2Float(0x439bf3b8)); // 965.522f, 311.904f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x447160c5), SkBits2Float(0x439bf47d)); // 965.512f, 311.91f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715f7d), SkBits2Float(0x439bf627)); // 965.492f, 311.923f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x447158f6), SkBits2Float(0x439bfeba), SkBits2Float(0x447152e1), SkBits2Float(0x439c0ac3), SkBits2Float(0x44714e15), SkBits2Float(0x439c1919)); // 965.39f, 311.99f, 965.295f, 312.084f, 965.22f, 312.196f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4471548c), SkBits2Float(0x439c10c7), SkBits2Float(0x447151bb), SkBits2Float(0x439bd7f2), SkBits2Float(0x44715927), SkBits2Float(0x439be271)); // 965.321f, 312.131f, 965.277f, 311.687f, 965.393f, 311.769f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x447156b8), SkBits2Float(0x439bd41b), SkBits2Float(0x44714c19), SkBits2Float(0x439bf356), SkBits2Float(0x44714b13), SkBits2Float(0x439c222f)); // 965.355f, 311.657f, 965.189f, 311.901f, 965.173f, 312.267f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44713dd4), SkBits2Float(0x439c4aa2), SkBits2Float(0x44712ea9), SkBits2Float(0x439c2be9), SkBits2Float(0x44712344), SkBits2Float(0x439c0085)); // 964.966f, 312.583f, 964.729f, 312.343f, 964.551f, 312.004f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44712605), SkBits2Float(0x439c2fa0)); // 964.594f, 312.372f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44711af3), SkBits2Float(0x439c7e9a), SkBits2Float(0x44710de4), SkBits2Float(0x439bf41b), SkBits2Float(0x4470fb65), SkBits2Float(0x439c20c7)); // 964.421f, 312.989f, 964.217f, 311.907f, 963.928f, 312.256f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4470fbb7), SkBits2Float(0x439c220f)); // 963.933f, 312.266f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470f5e4), SkBits2Float(0x439c2bc9), SkBits2Float(0x4470ef5d), SkBits2Float(0x439c9e59), SkBits2Float(0x4470e50f), SkBits2Float(0x439c85e6)); // 963.842f, 312.342f, 963.74f, 313.237f, 963.579f, 313.046f +if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470e8f6), SkBits2Float(0x439c4e35), SkBits2Float(0x4470ee98), SkBits2Float(0x439c5333), SkBits2Float(0x4470eed9), SkBits2Float(0x439c1ac1)); // 963.64f, 312.611f, 963.728f, 312.65f, 963.732f, 312.209f +SkASSERT(64 == i); +path.close(); + +testSimplify(reporter, path, "chalkboard"); +} + +static void testChalkboard(PathOpsThreadState* data) { + uint64_t testlines = ((uint64_t) data->fB << 32) | (unsigned int) data->fA; + chalkboard(data->fReporter, testlines); +} + +static void chalkboard_threaded(skiatest::Reporter* reporter, const char* filename) { +#if DEBUG_UNDER_DEVELOPMENT + return; +#endif + initializeTests(reporter, "chalkboard"); + PathOpsThreadedTestRunner testRunner(reporter); + SkRandom r; + for (int samples = 0; samples <= 64; ++samples) { + int testCount; + int bitCount = samples < 32 ? samples : 64 - samples; + int index1 = 63; + int index2 = 62; + switch (bitCount) { + case 0: + testCount = 1; + break; + case 1: + testCount = 64; + break; + case 2: + testCount = reporter->allowExtendedTest() ? 63 * 62 / 2 : 100; + break; + default: + testCount = reporter->allowExtendedTest() ? 10000 : 100; + break; + } + for (int test = 0; test < testCount; ++test) { + uint64_t testlines; + switch (bitCount) { + case 0: + testlines = 0; + break; + case 1: + testlines = 1LL << test; + break; + case 2: + if (reporter->allowExtendedTest()) { + SkASSERT(index1 >= 1); + SkASSERT(index2 >= 0); + testlines = 1LL << index1; + testlines |= 1LL << index2; + if (--index2 < 0) { + --index1; + index2 = index1 - 1; + } + break; + } + default: + testlines = 0; + for (int i = 0; i < bitCount; ++i) { + int bit; + do { + bit = r.nextRangeU(0, 63); + } while (testlines & (1LL << bit)); + testlines |= 1LL << bit; + } + } + if (samples >= 32) { + testlines ^= 0xFFFFFFFFFFFFFFFFLL; + } + *testRunner.fRunnables.append() = + new PathOpsThreadedRunnable(&testChalkboard, + (int) (unsigned) (testlines & 0xFFFFFFFF), + (int) (unsigned) (testlines >> 32), + 0, 0, &testRunner); + } + } + testRunner.render(); +} + +static void chalkboard_1(skiatest::Reporter* reporter, const char* filename) { + uint64_t testlines = 0xFFFFFFFFFFFFFFFFLL; + chalkboard(reporter, testlines); +} + +static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0; +static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0; +static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0; + +static TestDesc tests[] = { + TEST(chalkboard_1), + TEST(chalkboard_threaded), +}; + +static const size_t testCount = SK_ARRAY_COUNT(tests); +static bool runReverse = false; + +DEF_TEST(PathOpsChalkboard, reporter) { + RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse); +}
diff --git a/src/third_party/skia/tests/PathOpsConicIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsConicIntersectionTest.cpp new file mode 100644 index 0000000..1de1583 --- /dev/null +++ b/src/third_party/skia/tests/PathOpsConicIntersectionTest.cpp
@@ -0,0 +1,355 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "PathOpsTestCommon.h" +#include "SkGeometry.h" +#include "SkIntersections.h" +#include "Test.h" + +/* +manually compute the intersection of a pair of circles and see if the conic intersection matches + given two circles + construct a line connecting their centers + + */ + +static const ConicPts testSet[] = { + {{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f}, + {{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f}, + + {{{{5.1114602088928223, 628.77813720703125}, + {10.834027290344238, 988.964111328125}, + {163.40835571289062, 988.964111328125}}}, 0.72944212f}, + {{{{163.40835571289062, 988.964111328125}, + {5, 988.964111328125}, + {5, 614.7423095703125}}}, 0.707106769f}, + + {{{{11.17222976684570312, -8.103978157043457031}, + {22.91432571411132812, -10.37866020202636719}, + {23.7764129638671875, -7.725424289703369141}}}, 1.00862849f}, + {{{{-1.545085430145263672, -4.755282402038574219}, + {22.23132705688476562, -12.48070907592773438}, + {23.7764129638671875, -7.725427150726318359}}}, 0.707106769f}, + + {{{{-4,1}, {-4,5}, {0,5}}}, 0.707106769f}, + {{{{-3,4}, {-3,1}, {0,1}}}, 0.707106769f}, + + {{{{0, 0}, {0, 1}, {1, 1}}}, 0.5f}, + {{{{1, 0}, {0, 0}, {0, 1}}}, 0.5f}, + +}; + +const int testSetCount = (int) SK_ARRAY_COUNT(testSet); + +static void chopCompare(const SkConic chopped[2], const SkDConic dChopped[2]) { + SkASSERT(roughly_equal(chopped[0].fW, dChopped[0].fWeight)); + SkASSERT(roughly_equal(chopped[1].fW, dChopped[1].fWeight)); + for (int cIndex = 0; cIndex < 2; ++cIndex) { + for (int pIndex = 0; pIndex < 3; ++pIndex) { + SkDPoint up; + up.set(chopped[cIndex].fPts[pIndex]); + SkASSERT(dChopped[cIndex].fPts[pIndex].approximatelyEqual(up)); + } + } +#if DEBUG_VISUALIZE_CONICS + dChopped[0].dump(); + dChopped[1].dump(); +#endif +} + +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkImageEncoder.h" +#include "SkPathOpsRect.h" +#include "SkPaint.h" +#include "SkString.h" + +#define DEBUG_VISUALIZE_CONICS 0 + +#if DEBUG_VISUALIZE_CONICS +static void writePng(const SkConic& c, const SkConic ch[2], const char* name) { + const int scale = 10; + SkConic conic, chopped[2]; + for (int index = 0; index < 3; ++index) { + conic.fPts[index].fX = c.fPts[index].fX * scale; + conic.fPts[index].fY = c.fPts[index].fY * scale; + for (int chIndex = 0; chIndex < 2; ++chIndex) { + chopped[chIndex].fPts[index].fX = ch[chIndex].fPts[index].fX * scale; + chopped[chIndex].fPts[index].fY = ch[chIndex].fPts[index].fY * scale; + } + } + conic.fW = c.fW; + chopped[0].fW = ch[0].fW; + chopped[1].fW = ch[1].fW; + SkBitmap bitmap; + SkRect bounds; + conic.computeTightBounds(&bounds); + bounds.outset(10, 10); + bitmap.tryAllocPixels(SkImageInfo::MakeN32Premul( + SkScalarRoundToInt(bounds.width()), SkScalarRoundToInt(bounds.height()))); + SkCanvas canvas(bitmap); + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + canvas.translate(-bounds.fLeft, -bounds.fTop); + canvas.drawColor(SK_ColorWHITE); + SkPath path; + path.moveTo(conic.fPts[0]); + path.conicTo(conic.fPts[1], conic.fPts[2], conic.fW); + paint.setARGB(0x80, 0xFF, 0, 0); + canvas.drawPath(path, paint); + path.reset(); + path.moveTo(chopped[0].fPts[0]); + path.conicTo(chopped[0].fPts[1], chopped[0].fPts[2], chopped[0].fW); + path.moveTo(chopped[1].fPts[0]); + path.conicTo(chopped[1].fPts[1], chopped[1].fPts[2], chopped[1].fW); + paint.setARGB(0x80, 0, 0, 0xFF); + canvas.drawPath(path, paint); + SkString filename("c:\\Users\\caryclark\\Documents\\"); + filename.appendf("%s.png", name); + sk_tool_utils::EncodeImageToFile(filename.c_str(), bitmap, + SkEncodedImageFormat::kPNG, 100); +} + +static void writeDPng(const SkDConic& dC, const char* name) { + const int scale = 5; + SkDConic dConic = {{{ {dC.fPts[0].fX * scale, dC.fPts[0].fY * scale }, + {dC.fPts[1].fX * scale, dC.fPts[1].fY * scale }, + {dC.fPts[2].fX * scale, dC.fPts[2].fY * scale }}}, dC.fWeight }; + SkBitmap bitmap; + SkDRect bounds; + bounds.setBounds(dConic); + bounds.fLeft -= 10; + bounds.fTop -= 10; + bounds.fRight += 10; + bounds.fBottom += 10; + bitmap.tryAllocPixels(SkImageInfo::MakeN32Premul( + SkScalarRoundToInt(SkDoubleToScalar(bounds.width())), + SkScalarRoundToInt(SkDoubleToScalar(bounds.height())))); + SkCanvas canvas(bitmap); + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + canvas.translate(SkDoubleToScalar(-bounds.fLeft), SkDoubleToScalar(-bounds.fTop)); + canvas.drawColor(SK_ColorWHITE); + SkPath path; + path.moveTo(dConic.fPts[0].asSkPoint()); + path.conicTo(dConic.fPts[1].asSkPoint(), dConic.fPts[2].asSkPoint(), dConic.fWeight); + paint.setARGB(0x80, 0xFF, 0, 0); + canvas.drawPath(path, paint); + path.reset(); + const int chops = 2; + for (int tIndex = 0; tIndex < chops; ++tIndex) { + SkDConic chopped = dConic.subDivide(tIndex / (double) chops, + (tIndex + 1) / (double) chops); + path.moveTo(chopped.fPts[0].asSkPoint()); + path.conicTo(chopped.fPts[1].asSkPoint(), chopped.fPts[2].asSkPoint(), chopped.fWeight); + } + paint.setARGB(0x80, 0, 0, 0xFF); + canvas.drawPath(path, paint); + SkString filename("c:\\Users\\caryclark\\Documents\\"); + filename.appendf("%s.png", name); + sk_tool_utils::EncodeImageToFile(filename.c_str(), bitmap, + SkEncodedImageFormat::kPNG, 100); +} +#endif + +static void chopBothWays(const SkDConic& dConic, double t, const char* name) { + SkConic conic; + for (int index = 0; index < 3; ++index) { + conic.fPts[index] = dConic.fPts[index].asSkPoint(); + } + conic.fW = dConic.fWeight; + SkConic chopped[2]; + SkDConic dChopped[2]; + if (!conic.chopAt(SkDoubleToScalar(t), chopped)) { + return; + } + dChopped[0] = dConic.subDivide(0, t); + dChopped[1] = dConic.subDivide(t, 1); +#if DEBUG_VISUALIZE_CONICS + dConic.dump(); +#endif + chopCompare(chopped, dChopped); +#if DEBUG_VISUALIZE_CONICS + writePng(conic, chopped, name); +#endif +} + +#if DEBUG_VISUALIZE_CONICS +const SkDConic frame0[] = { +{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f}, +{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f}, +}; + +const SkDConic frame1[] = { +{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f}, +{{{{306.58801299999999, -227.983994}, {212.46499600000001, -262.24200400000001}, {95.551200899999998, 58.976398500000002}}}, 0.707107008f}, +{{{{377.21899400000001, -141.98100299999999}, {237.77799285476553, -166.56830755921084}, {134.08399674208422, -155.06258330544892}}}, 0.788580656f}, +{{{{134.08399674208422, -155.06258330544892}, {30.390000629402859, -143.55685905168704}, {23.185499199999999, -102.697998}}}, 0.923879623f}, +}; + +const SkDConic frame2[] = { +{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f}, +{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f}, +{{{{205.78973252799028, -158.12538713371103}, {143.97848953841861, -74.076645245042371}, {95.551200899999998, 58.976398500000002}}}, 0.923879623f}, +{{{{377.21899400000001, -141.98100299999999}, {237.77799285476553, -166.56830755921084}, {134.08399674208422, -155.06258330544892}}}, 0.788580656f}, +}; + +const SkDConic frame3[] = { +{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f}, +{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f}, +{{{{205.78973252799028, -158.12538713371103}, {143.97848953841861, -74.076645245042371}, {95.551200899999998, 58.976398500000002}}}, 0.923879623f}, +{{{{252.08225670812539, -156.90491625851064}, {185.93099479842493, -160.81544543232982}, {134.08399674208422, -155.06258330544892}}}, 0.835816324f}, +}; + +const SkDConic frame4[] = { +{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f}, +{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f}, +{{{{205.78973252799028, -158.12538713371103}, {174.88411103320448, -116.10101618937664}, {145.19509369736275, -56.857102571363754}}}, 0.871667147f}, +{{{{252.08225670812539, -156.90491625851064}, {185.93099479842493, -160.81544543232982}, {134.08399674208422, -155.06258330544892}}}, 0.835816324f}, +}; + +const SkDConic frame5[] = { +{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f}, +{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f}, +{{{{205.78973252799028, -158.12538713371103}, {174.88411103320448, -116.10101618937664}, {145.19509369736275, -56.857102571363754}}}, 0.871667147f}, +{{{{252.08225670812539, -156.90491625851064}, {219.70109133058406, -158.81912754088933}, {190.17095392508796, -158.38373974664466}}}, 0.858306944f}, +}; + +const SkDConic frame6[] = { +{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f}, +{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f}, +{{{{205.78973252799028, -158.12538713371103}, {190.33692178059735, -137.11320166154385}, {174.87004877564593, -111.2132534799228}}}, 0.858117759f}, +{{{{252.08225670812539, -156.90491625851064}, {219.70109133058406, -158.81912754088933}, {190.17095392508796, -158.38373974664466}}}, 0.858306944f}, +}; + +const SkDConic* frames[] = { + frame0, frame1, frame2, frame3, frame4, frame5, frame6 +}; + +const int frameSizes[] = { (int) SK_ARRAY_COUNT(frame0), (int) SK_ARRAY_COUNT(frame1), + (int) SK_ARRAY_COUNT(frame2), (int) SK_ARRAY_COUNT(frame3), + (int) SK_ARRAY_COUNT(frame4), (int) SK_ARRAY_COUNT(frame5), + (int) SK_ARRAY_COUNT(frame6), +}; + +static void writeFrames() { + const int scale = 5; + + for (int index = 0; index < (int) SK_ARRAY_COUNT(frameSizes); ++index) { + SkDRect bounds; + bool boundsSet = false; + int frameSize = frameSizes[index]; + for (int fIndex = 0; fIndex < frameSize; ++fIndex) { + const SkDConic& dC = frames[index][fIndex]; + SkDConic dConic = {{{ {dC.fPts[0].fX * scale, dC.fPts[0].fY * scale }, + {dC.fPts[1].fX * scale, dC.fPts[1].fY * scale }, + {dC.fPts[2].fX * scale, dC.fPts[2].fY * scale }}}, dC.fWeight }; + SkDRect dBounds; + dBounds.setBounds(dConic); + if (!boundsSet) { + bounds = dBounds; + boundsSet = true; + } else { + bounds.add((SkDPoint&) dBounds.fLeft); + bounds.add((SkDPoint&) dBounds.fRight); + } + } + bounds.fLeft -= 10; + bounds.fTop -= 10; + bounds.fRight += 10; + bounds.fBottom += 10; + SkBitmap bitmap; + bitmap.tryAllocPixels(SkImageInfo::MakeN32Premul( + SkScalarRoundToInt(SkDoubleToScalar(bounds.width())), + SkScalarRoundToInt(SkDoubleToScalar(bounds.height())))); + SkCanvas canvas(bitmap); + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + canvas.translate(SkDoubleToScalar(-bounds.fLeft), SkDoubleToScalar(-bounds.fTop)); + canvas.drawColor(SK_ColorWHITE); + for (int fIndex = 0; fIndex < frameSize; ++fIndex) { + const SkDConic& dC = frames[index][fIndex]; + SkDConic dConic = {{{ {dC.fPts[0].fX * scale, dC.fPts[0].fY * scale }, + {dC.fPts[1].fX * scale, dC.fPts[1].fY * scale }, + {dC.fPts[2].fX * scale, dC.fPts[2].fY * scale }}}, dC.fWeight }; + SkPath path; + path.moveTo(dConic.fPts[0].asSkPoint()); + path.conicTo(dConic.fPts[1].asSkPoint(), dConic.fPts[2].asSkPoint(), dConic.fWeight); + if (fIndex < 2) { + paint.setARGB(0x80, 0xFF, 0, 0); + } else { + paint.setARGB(0x80, 0, 0, 0xFF); + } + canvas.drawPath(path, paint); + } + SkString filename("c:\\Users\\caryclark\\Documents\\"); + filename.appendf("f%d.png", index); + sk_tool_utils::EncodeImageToFile(filename.c_str(), bitmap, SkEncodedImageFormat::kPNG, 100); + } +} +#endif + +static void oneOff(skiatest::Reporter* reporter, const ConicPts& conic1, const ConicPts& conic2, + bool coin) { +#if DEBUG_VISUALIZE_CONICS + writeFrames(); +#endif + SkDConic c1, c2; + c1.debugSet(conic1.fPts.fPts, conic1.fWeight); + c2.debugSet(conic2.fPts.fPts, conic2.fWeight); + chopBothWays(c1, 0.5, "c1"); + chopBothWays(c2, 0.5, "c2"); +#if DEBUG_VISUALIZE_CONICS + writeDPng(c1, "d1"); + writeDPng(c2, "d2"); +#endif + SkASSERT(ValidConic(c1)); + SkASSERT(ValidConic(c2)); + SkIntersections intersections; + intersections.intersect(c1, c2); + if (coin && intersections.used() != 2) { + SkDebugf(""); + } + REPORTER_ASSERT(reporter, !coin || intersections.used() == 2); + double tt1, tt2; + SkDPoint xy1, xy2; + for (int pt3 = 0; pt3 < intersections.used(); ++pt3) { + tt1 = intersections[0][pt3]; + xy1 = c1.ptAtT(tt1); + tt2 = intersections[1][pt3]; + xy2 = c2.ptAtT(tt2); + const SkDPoint& iPt = intersections.pt(pt3); + REPORTER_ASSERT(reporter, xy1.approximatelyEqual(iPt)); + REPORTER_ASSERT(reporter, xy2.approximatelyEqual(iPt)); + REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2)); + } + reporter->bumpTestCount(); +} + +static void oneOff(skiatest::Reporter* reporter, int outer, int inner) { + const ConicPts& c1 = testSet[outer]; + const ConicPts& c2 = testSet[inner]; + oneOff(reporter, c1, c2, false); +} + +static void oneOffTests(skiatest::Reporter* reporter) { + for (int outer = 0; outer < testSetCount - 1; ++outer) { + for (int inner = outer + 1; inner < testSetCount; ++inner) { + oneOff(reporter, outer, inner); + } + } +} + +DEF_TEST(PathOpsConicIntersectionOneOff, reporter) { + oneOff(reporter, 0, 1); +} + +DEF_TEST(PathOpsConicIntersection, reporter) { + oneOffTests(reporter); +}
diff --git a/src/third_party/skia/tests/PathOpsConicLineIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsConicLineIntersectionTest.cpp new file mode 100644 index 0000000..c3d4a2a --- /dev/null +++ b/src/third_party/skia/tests/PathOpsConicLineIntersectionTest.cpp
@@ -0,0 +1,150 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "PathOpsExtendedTest.h" +#include "PathOpsTestCommon.h" +#include "SkGeometry.h" +#include "SkIntersections.h" +#include "SkPathOpsConic.h" +#include "SkPathOpsLine.h" +#include "SkReduceOrder.h" +#include "Test.h" + +static struct lineConic { + ConicPts conic; + SkDLine line; + int result; + SkDPoint expected[2]; +} lineConicTests[] = { + { + {{{{30.6499996,25.6499996}, {30.6499996,20.6499996}, {25.6499996,20.6499996}}}, 0.707107008f}, + {{{25.6499996,20.6499996}, {45.6500015,20.6499996}}}, + 1, + {{25.6499996,20.6499996}, {0,0}} + }, +}; + +static size_t lineConicTests_count = SK_ARRAY_COUNT(lineConicTests); + +static int doIntersect(SkIntersections& intersections, const SkDConic& conic, const SkDLine& line, + bool& flipped) { + int result; + flipped = false; + if (line[0].fX == line[1].fX) { + double top = line[0].fY; + double bottom = line[1].fY; + flipped = top > bottom; + if (flipped) { + SkTSwap<double>(top, bottom); + } + result = intersections.vertical(conic, top, bottom, line[0].fX, flipped); + } else if (line[0].fY == line[1].fY) { + double left = line[0].fX; + double right = line[1].fX; + flipped = left > right; + if (flipped) { + SkTSwap<double>(left, right); + } + result = intersections.horizontal(conic, left, right, line[0].fY, flipped); + } else { + intersections.intersect(conic, line); + result = intersections.used(); + } + return result; +} + +static struct oneLineConic { + ConicPts conic; + SkDLine line; +} oneOffs[] = { + {{{{{30.6499996,25.6499996}, {30.6499996,20.6499996}, {25.6499996,20.6499996}}}, 0.707107008f}, + {{{25.6499996,20.6499996}, {45.6500015,20.6499996}}}} +}; + +static size_t oneOffs_count = SK_ARRAY_COUNT(oneOffs); + +static void testOneOffs(skiatest::Reporter* reporter) { + bool flipped = false; + for (size_t index = 0; index < oneOffs_count; ++index) { + const ConicPts& c = oneOffs[index].conic; + SkDConic conic; + conic.debugSet(c.fPts.fPts, c.fWeight); + SkASSERT(ValidConic(conic)); + const SkDLine& line = oneOffs[index].line; + SkASSERT(ValidLine(line)); + SkIntersections intersections; + int result = doIntersect(intersections, conic, line, flipped); + for (int inner = 0; inner < result; ++inner) { + double conicT = intersections[0][inner]; + SkDPoint conicXY = conic.ptAtT(conicT); + double lineT = intersections[1][inner]; + SkDPoint lineXY = line.ptAtT(lineT); + if (!conicXY.approximatelyEqual(lineXY)) { + conicXY.approximatelyEqual(lineXY); + SkDebugf(""); + } + REPORTER_ASSERT(reporter, conicXY.approximatelyEqual(lineXY)); + } + } +} + +DEF_TEST(PathOpsConicLineIntersectionOneOff, reporter) { + testOneOffs(reporter); +} + +DEF_TEST(PathOpsConicLineIntersection, reporter) { + for (size_t index = 0; index < lineConicTests_count; ++index) { + int iIndex = static_cast<int>(index); + const ConicPts& c = lineConicTests[index].conic; + SkDConic conic; + conic.debugSet(c.fPts.fPts, c.fWeight); + SkASSERT(ValidConic(conic)); + const SkDLine& line = lineConicTests[index].line; + SkASSERT(ValidLine(line)); + SkReduceOrder reducer; + SkPoint pts[3] = { conic.fPts.fPts[0].asSkPoint(), conic.fPts.fPts[1].asSkPoint(), + conic.fPts.fPts[2].asSkPoint() }; + SkPoint reduced[3]; + SkConic floatConic; + floatConic.set(pts, conic.fWeight); + SkPath::Verb order1 = SkReduceOrder::Conic(floatConic, reduced); + if (order1 != SkPath::kConic_Verb) { + SkDebugf("%s [%d] conic verb=%d\n", __FUNCTION__, iIndex, order1); + REPORTER_ASSERT(reporter, 0); + } + int order2 = reducer.reduce(line); + if (order2 < 2) { + SkDebugf("%s [%d] line order=%d\n", __FUNCTION__, iIndex, order2); + REPORTER_ASSERT(reporter, 0); + } + SkIntersections intersections; + bool flipped = false; + int result = doIntersect(intersections, conic, line, flipped); + REPORTER_ASSERT(reporter, result == lineConicTests[index].result); + if (intersections.used() <= 0) { + continue; + } + for (int pt = 0; pt < result; ++pt) { + double tt1 = intersections[0][pt]; + REPORTER_ASSERT(reporter, tt1 >= 0 && tt1 <= 1); + SkDPoint t1 = conic.ptAtT(tt1); + double tt2 = intersections[1][pt]; + REPORTER_ASSERT(reporter, tt2 >= 0 && tt2 <= 1); + SkDPoint t2 = line.ptAtT(tt2); + if (!t1.approximatelyEqual(t2)) { + SkDebugf("%s [%d,%d] x!= t1=%1.9g (%1.9g,%1.9g) t2=%1.9g (%1.9g,%1.9g)\n", + __FUNCTION__, iIndex, pt, tt1, t1.fX, t1.fY, tt2, t2.fX, t2.fY); + REPORTER_ASSERT(reporter, 0); + } + if (!t1.approximatelyEqual(lineConicTests[index].expected[0]) + && (lineConicTests[index].result == 1 + || !t1.approximatelyEqual(lineConicTests[index].expected[1]))) { + SkDebugf("%s t1=(%1.9g,%1.9g)\n", __FUNCTION__, t1.fX, t1.fY); + REPORTER_ASSERT(reporter, 0); + } + } + } +}
diff --git a/src/third_party/skia/tests/PathOpsConicQuadIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsConicQuadIntersectionTest.cpp new file mode 100644 index 0000000..99411dc --- /dev/null +++ b/src/third_party/skia/tests/PathOpsConicQuadIntersectionTest.cpp
@@ -0,0 +1,73 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "PathOpsTestCommon.h" +#include "SkIntersections.h" +#include "SkPathOpsConic.h" +#include "SkPathOpsQuad.h" +#include "SkReduceOrder.h" +#include "Test.h" + +static struct conicQuad { + ConicPts conic; + QuadPts quad; +} conicQuadTests[] = { + {{{{{494.348663,224.583771}, {494.365143,224.633194}, {494.376404,224.684067}}}, 0.998645842f}, + {{{494.30481,224.474213}, {494.334961,224.538284}, {494.355774,224.605927}}}}, + + {{{{{494.348663,224.583771}, {494.365143,224.633194}, {494.376404,224.684067}}}, 0.998645842f}, + {{{494.355774f, 224.605927f}, {494.363708f, 224.631714f}, {494.370148f, 224.657471f}}}}, +}; + +static const int conicQuadTests_count = (int) SK_ARRAY_COUNT(conicQuadTests); + +static void conicQuadIntersection(skiatest::Reporter* reporter, int index) { + const ConicPts& c = conicQuadTests[index].conic; + SkDConic conic; + conic.debugSet(c.fPts.fPts, c.fWeight); + SkASSERT(ValidConic(conic)); + const QuadPts& q = conicQuadTests[index].quad; + SkDQuad quad; + quad.debugSet(q.fPts); + SkASSERT(ValidQuad(quad)); + SkReduceOrder reduce1; + SkReduceOrder reduce2; + int order1 = reduce2.reduce(conic.fPts); + int order2 = reduce1.reduce(quad); + if (order2 != 3) { + SkDebugf("[%d] conic order=%d\n", index, order1); + REPORTER_ASSERT(reporter, 0); + } + if (order1 != 3) { + SkDebugf("[%d] quad order=%d\n", index, order2); + REPORTER_ASSERT(reporter, 0); + } + SkIntersections i; + int roots = i.intersect(conic, quad); + for (int pt = 0; pt < roots; ++pt) { + double tt1 = i[0][pt]; + SkDPoint xy1 = conic.ptAtT(tt1); + double tt2 = i[1][pt]; + SkDPoint xy2 = quad.ptAtT(tt2); + if (!xy1.approximatelyEqual(xy2)) { + SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n", + __FUNCTION__, index, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY); + } + REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2)); + } + reporter->bumpTestCount(); +} + +DEF_TEST(PathOpsConicQuadIntersection, reporter) { + for (int index = 0; index < conicQuadTests_count; ++index) { + conicQuadIntersection(reporter, index); + reporter->bumpTestCount(); + } +} + +DEF_TEST(PathOpsConicQuadIntersectionOneOff, reporter) { + conicQuadIntersection(reporter, 1); +}
diff --git a/src/third_party/skia/tests/PathOpsCubicConicIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsCubicConicIntersectionTest.cpp new file mode 100644 index 0000000..d792463 --- /dev/null +++ b/src/third_party/skia/tests/PathOpsCubicConicIntersectionTest.cpp
@@ -0,0 +1,77 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "PathOpsTestCommon.h" +#include "SkIntersections.h" +#include "SkPathOpsConic.h" +#include "SkPathOpsCubic.h" +#include "SkReduceOrder.h" +#include "Test.h" + +static struct cubicConic { + CubicPts cubic; + ConicPts conic; +} cubicConicTests[] = { + {{{{188.60000610351562, 2041.5999755859375}, {188.60000610351562, 2065.39990234375}, + {208, 2084.800048828125}, {231.80000305175781, 2084.800048828125}}}, + {{{{231.80000305175781, 2084.800048828125}, {188.60000610351562, 2084.800048828125}, + {188.60000610351562, 2041.5999755859375}}}, 0.707107008f}}, + + {{{{231.80000305175781, 2084.800048828125}, {255.60000610351562, 2084.800048828125}, + {275, 2065.39990234375}, {275, 2041.5999755859375}}}, + {{{{275, 2041.5999755859375}, {275, 2084.800048828125}, + {231.80000305175781, 2084.800048828125}}}, 0.707107008f}}, +}; + +static const int cubicConicTests_count = (int) SK_ARRAY_COUNT(cubicConicTests); + +static void cubicConicIntersection(skiatest::Reporter* reporter, int index) { + const CubicPts& cu = cubicConicTests[index].cubic; + SkDCubic cubic; + cubic.debugSet(cu.fPts); + SkASSERT(ValidCubic(cubic)); + const ConicPts& co = cubicConicTests[index].conic; + SkDConic conic; + conic.debugSet(co.fPts.fPts, co.fWeight); + SkASSERT(ValidConic(conic)); + SkReduceOrder reduce1; + SkReduceOrder reduce2; + int order1 = reduce1.reduce(cubic, SkReduceOrder::kNo_Quadratics); + int order2 = reduce2.reduce(conic.fPts); + if (order1 != 4) { + SkDebugf("[%d] cubic order=%d\n", index, order1); + REPORTER_ASSERT(reporter, 0); + } + if (order2 != 3) { + SkDebugf("[%d] conic order=%d\n", index, order2); + REPORTER_ASSERT(reporter, 0); + } + SkIntersections i; + int roots = i.intersect(cubic, conic); + for (int pt = 0; pt < roots; ++pt) { + double tt1 = i[0][pt]; + SkDPoint xy1 = cubic.ptAtT(tt1); + double tt2 = i[1][pt]; + SkDPoint xy2 = conic.ptAtT(tt2); + if (!xy1.approximatelyEqual(xy2)) { + SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n", + __FUNCTION__, index, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY); + } + REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2)); + } + reporter->bumpTestCount(); +} + +DEF_TEST(PathOpsCubicConicIntersection, reporter) { + for (int index = 0; index < cubicConicTests_count; ++index) { + cubicConicIntersection(reporter, index); + reporter->bumpTestCount(); + } +} + +DEF_TEST(PathOpsCubicConicIntersectionOneOff, reporter) { + cubicConicIntersection(reporter, 1); +}
diff --git a/src/third_party/skia/tests/PathOpsCubicIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsCubicIntersectionTest.cpp index 443e1ae..6eab71d 100644 --- a/src/third_party/skia/tests/PathOpsCubicIntersectionTest.cpp +++ b/src/third_party/skia/tests/PathOpsCubicIntersectionTest.cpp
@@ -6,21 +6,27 @@ */ #include "PathOpsCubicIntersectionTestData.h" #include "PathOpsTestCommon.h" +#include "SkGeometry.h" #include "SkIntersections.h" #include "SkPathOpsRect.h" #include "SkReduceOrder.h" #include "Test.h" +#include <stdlib.h> + const int firstCubicIntersectionTest = 9; static void standardTestCases(skiatest::Reporter* reporter) { for (size_t index = firstCubicIntersectionTest; index < tests_count; ++index) { int iIndex = static_cast<int>(index); - const SkDCubic& cubic1 = tests[index][0]; - const SkDCubic& cubic2 = tests[index][1]; + const CubicPts& cubic1 = tests[index][0]; + const CubicPts& cubic2 = tests[index][1]; + SkDCubic c1, c2; + c1.debugSet(cubic1.fPts); + c2.debugSet(cubic2.fPts); SkReduceOrder reduce1, reduce2; - int order1 = reduce1.reduce(cubic1, SkReduceOrder::kNo_Quadratics); - int order2 = reduce2.reduce(cubic2, SkReduceOrder::kNo_Quadratics); + int order1 = reduce1.reduce(c1, SkReduceOrder::kNo_Quadratics); + int order2 = reduce2.reduce(c2, SkReduceOrder::kNo_Quadratics); const bool showSkipped = false; if (order1 < 4) { if (showSkipped) { @@ -35,7 +41,7 @@ continue; } SkIntersections tIntersections; - tIntersections.intersect(cubic1, cubic2); + tIntersections.intersect(c1, c2); if (!tIntersections.used()) { if (showSkipped) { SkDebugf("%s [%d] no intersection\n", __FUNCTION__, iIndex); @@ -50,9 +56,9 @@ } for (int pt = 0; pt < tIntersections.used(); ++pt) { double tt1 = tIntersections[0][pt]; - SkDPoint xy1 = cubic1.ptAtT(tt1); + SkDPoint xy1 = c1.ptAtT(tt1); double tt2 = tIntersections[1][pt]; - SkDPoint xy2 = cubic2.ptAtT(tt2); + SkDPoint xy2 = c2.ptAtT(tt2); if (!xy1.approximatelyEqual(xy2)) { SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n", __FUNCTION__, (int)index, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY); @@ -63,7 +69,7 @@ } } -static const SkDCubic testSet[] = { +static const CubicPts testSet[] = { // FIXME: uncommenting these two will cause this to fail // this results in two curves very nearly but not exactly coincident #if 0 @@ -161,7 +167,79 @@ const int testSetCount = (int) SK_ARRAY_COUNT(testSet); -static const SkDCubic newTestSet[] = { +static const CubicPts newTestSet[] = { +{ { { 130.0427549999999997, 11417.41309999999976 },{ 130.2331240000000037, 11418.3192999999992 },{ 131.0370790000000056, 11419 },{ 132, 11419 } } }, +{ { { 132, 11419 },{ 130.8954319999999996, 11419 },{ 130, 11418.10449999999946 },{ 130, 11417 } } }, + +{{{1,3}, {-1.0564518,1.79032254}, {1.45265341,0.229448318}, {1.45381773,0.22913377}}}, +{{{1.45381773,0.22913377}, {1.45425761,0.229014933}, {1.0967741,0.451612949}, {0,1}}}, + +{{{1.64551306f, 3.57876182f}, {0.298127174f, 3.70454836f}, {-0.809808373f, 6.39524937f}, {-3.66666651f, 13.333334f}}}, +{{{1, 2}, {1, 2}, {-3.66666651f, 13.333334f}, {5, 6}}}, + +{{{0.0660428554,1.65340209}, {-0.251940489,1.43560803}, {-0.782382965,-0.196299091}, {3.33333325,-0.666666627}}}, +{{{1,3}, {-1.22353387,1.09411383}, {0.319867611,0.12996155}, {0.886705518,0.107543148}}}, + +{{{-0.13654758,2.10514426}, {-0.585797966,1.89349782}, {-0.807703257,-0.192306399}, {6,-1}}}, +{{{1,4}, {-2.25000453,1.42241001}, {1.1314013,0.0505309105}, {1.87140274,0.0363764353}}}, + +{{{1.3127951622009277, 2.0637707710266113}, {1.8210518360137939, 1.9148571491241455}, {1.6106204986572266, -0.68700540065765381}, {8.5, -2.5}}}, +{{{3, 4}, {0.33333325386047363, 1.3333332538604736}, {3.6666667461395264, -0.66666674613952637}, {3.6666665077209473, -0.66666656732559204}}}, + +{{{980.026001,1481.276}, {980.026001,1481.276}, {980.02594,1481.27576}, {980.025879,1481.27527}}}, +{{{980.025879,1481.27527}, {980.025452,1481.27222}, {980.023743,1481.26038}, {980.02179,1481.24072}}}, + +{{{1.80943513,3.07782435}, {1.66686702,2.16806936}, {1.68301272,0}, {3,0}}}, +{{{0,1}, {0,3}, {3,2}, {5,2}}}, + +{{{3.4386673,2.66977954}, {4.06668949,2.17046738}, {4.78887367,1.59629118}, {6,2}}}, +{{{1.71985495,3.49467373}, {2.11620402,2.7201426}, {2.91897964,1.15138781}, {6,3}}}, + +{{{0,1}, {0.392703831,1.78540766}, {0.219947904,2.05676103}, {0.218561709,2.05630541}}}, +{{{0.218561709,2.05630541}, {0.216418028,2.05560064}, {0.624105453,1.40486407}, {4.16666651,1.00000012}}}, + +{{{0, 1}, {3, 5}, {2, 1}, {3, 1}}}, +{{{1.01366711f, 2.21379328f}, {1.09074128f, 2.23241305f}, {1.60246587f, 0.451849401f}, {5, 3}}}, + +{{{0, 1}, {0.541499972f, 3.16599989f}, {1.08299994f, 2.69299984f}, {2.10083938f, 1.80391729f}}}, +{{{0.806384504f, 2.85426903f}, {1.52740121f, 1.99355423f}, {2.81689167f, 0.454222918f}, {5, 1}}}, + +{{{0, 1}, {1.90192389f, 2.90192389f}, {2.59807634f, 2.79422879f}, {3.1076951f, 2.71539044f}}}, +{{{2, 3}, {2.36602545f, 3.36602545f}, {2.330127f, 3.06217766f}, {2.28460979f, 2.67691422f}}}, + +{{{0, 1}, {1.90192389f, 2.90192389f}, {2.59807634f, 2.79422879f}, {3.1076951f, 2.71539044f}}}, +{{{2.28460979f, 2.67691422f}, {2.20577145f, 2.00961876f}, {2.09807634f, 1.09807622f}, {4, 3}}}, + +{{{0, 1}, {0.8211091160774231, 2.0948121547698975}, {0.91805583238601685, 2.515404224395752}, {0.91621249914169312, 2.5146586894989014}}}, +{{{0.91621249914169312, 2.5146586894989014}, {0.91132104396820068, 2.5126807689666748}, {0.21079301834106445, -0.45617169141769409}, {10.5, -1.6666665077209473}}}, + +{{{42.6237564,68.9841232}, {32.449646,81.963089}, {14.7713947,103.565269}, {12.6310005,105.247002}}}, +{{{37.2640038,95.3540039}, {37.2640038,95.3540039}, {11.3710003,83.7339935}, {-25.0779991,124.912003}}}, + +{{{0,1}, {4,5}, {6,0}, {1,0}}}, +{{{0,6}, {0,1}, {1,0}, {5,4}}}, + +{{{0,1}, {4,6}, {5,1}, {6,2}}}, +{{{1,5}, {2,6}, {1,0}, {6,4}}}, + +{{{322, 896.04803466796875}, {314.09201049804687, 833.4376220703125}, {260.24713134765625, 785}, {195, 785}}}, +{{{195, 785}, {265.14016723632812, 785}, {322, 842.30755615234375}, {322, 913}}}, + +{{{1, 4}, {4, 5}, {3, 2}, {6, 3}}}, +{{{2, 3}, {3, 6}, {4, 1}, {5, 4}}}, + +{{{67, 913}, {67, 917.388916015625}, {67.224380493164063, 921.72576904296875}, {67.662384033203125, 926}}}, +{{{194, 1041}, {123.85984039306641, 1041}, {67, 983.69244384765625}, {67, 913}}}, + +{{{1,4}, {1,5}, {6,0}, {5,1}}}, +{{{0,6}, {1,5}, {4,1}, {5,1}}}, + +{{{0,1}, {4,5}, {6,0}, {1,0}}}, +{{{0,6}, {0,1}, {1,0}, {5,4}}}, + +{{{0,1}, {4,6}, {2,0}, {2,0}}}, +{{{0,2}, {0,2}, {1,0}, {6,4}}}, + {{{980.9000244140625, 1474.3280029296875}, {980.9000244140625, 1474.3280029296875}, {978.89300537109375, 1471.95703125}, {981.791015625, 1469.487060546875}}}, {{{981.791015625, 1469.487060546875}, {981.791015625, 1469.4859619140625}, {983.3580322265625, 1472.72900390625}, {980.9000244140625, 1474.3280029296875}}}, @@ -306,11 +384,13 @@ }; const int newTestSetCount = (int) SK_ARRAY_COUNT(newTestSet); - -static void oneOff(skiatest::Reporter* reporter, const SkDCubic& cubic1, const SkDCubic& cubic2, +static void oneOff(skiatest::Reporter* reporter, const CubicPts& cubic1, const CubicPts& cubic2, bool coin) { - SkASSERT(ValidCubic(cubic1)); - SkASSERT(ValidCubic(cubic2)); + SkDCubic c1, c2; + c1.debugSet(cubic1.fPts); + c2.debugSet(cubic2.fPts); + SkASSERT(ValidCubic(c1)); + SkASSERT(ValidCubic(c2)); #if ONE_OFF_DEBUG SkDebugf("computed quadratics given\n"); SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", @@ -320,36 +400,27 @@ cubic2[0].fX, cubic2[0].fY, cubic2[1].fX, cubic2[1].fY, cubic2[2].fX, cubic2[2].fY, cubic2[3].fX, cubic2[3].fY); #endif - SkTArray<SkDQuad, true> quads1; - CubicToQuads(cubic1, cubic1.calcPrecision(), quads1); -#if ONE_OFF_DEBUG - SkDebugf("computed quadratics set 1\n"); - for (int index = 0; index < quads1.count(); ++index) { - const SkDQuad& q = quads1[index]; - SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].fX, q[0].fY, - q[1].fX, q[1].fY, q[2].fX, q[2].fY); - } -#endif - SkTArray<SkDQuad, true> quads2; - CubicToQuads(cubic2, cubic2.calcPrecision(), quads2); -#if ONE_OFF_DEBUG - SkDebugf("computed quadratics set 2\n"); - for (int index = 0; index < quads2.count(); ++index) { - const SkDQuad& q = quads2[index]; - SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].fX, q[0].fY, - q[1].fX, q[1].fY, q[2].fX, q[2].fY); - } -#endif SkIntersections intersections; - intersections.intersect(cubic1, cubic2); - REPORTER_ASSERT(reporter, !coin || intersections.used() == 2); + intersections.intersect(c1, c2); +#if DEBUG_T_SECT_DUMP == 3 + SkDebugf("</div>\n\n"); + SkDebugf("<script type=\"text/javascript\">\n\n"); + SkDebugf("var testDivs = [\n"); + for (int index = 1; index <= gDumpTSectNum; ++index) { + SkDebugf("sect%d,\n", index); + } +#endif + if (coin && intersections.used() < 2) { + SkDebugf(""); + } + REPORTER_ASSERT(reporter, !coin || intersections.used() >= 2); double tt1, tt2; SkDPoint xy1, xy2; for (int pt3 = 0; pt3 < intersections.used(); ++pt3) { tt1 = intersections[0][pt3]; - xy1 = cubic1.ptAtT(tt1); + xy1 = c1.ptAtT(tt1); tt2 = intersections[1][pt3]; - xy2 = cubic2.ptAtT(tt2); + xy2 = c2.ptAtT(tt2); const SkDPoint& iPt = intersections.pt(pt3); #if ONE_OFF_DEBUG SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n", @@ -364,14 +435,20 @@ } static void oneOff(skiatest::Reporter* reporter, int outer, int inner) { - const SkDCubic& cubic1 = testSet[outer]; - const SkDCubic& cubic2 = testSet[inner]; + const CubicPts& cubic1 = testSet[outer]; + const CubicPts& cubic2 = testSet[inner]; oneOff(reporter, cubic1, cubic2, false); } static void newOneOff(skiatest::Reporter* reporter, int outer, int inner) { - const SkDCubic& cubic1 = newTestSet[outer]; - const SkDCubic& cubic2 = newTestSet[inner]; + const CubicPts& cubic1 = newTestSet[outer]; + const CubicPts& cubic2 = newTestSet[inner]; + oneOff(reporter, cubic1, cubic2, false); +} + +static void testsOneOff(skiatest::Reporter* reporter, int index) { + const CubicPts& cubic1 = tests[index][0]; + const CubicPts& cubic2 = tests[index][1]; oneOff(reporter, cubic1, cubic2, false); } @@ -397,12 +474,12 @@ unsigned seed = 0; #endif for (int test = 0; test < tests; ++test) { - SkDCubic cubic1, cubic2; + CubicPts cubic1, cubic2; for (int i = 0; i < 4; ++i) { - cubic1[i].fX = static_cast<double>(SK_RAND(seed)) / RAND_MAX * 100; - cubic1[i].fY = static_cast<double>(SK_RAND(seed)) / RAND_MAX * 100; - cubic2[i].fX = static_cast<double>(SK_RAND(seed)) / RAND_MAX * 100; - cubic2[i].fY = static_cast<double>(SK_RAND(seed)) / RAND_MAX * 100; + cubic1.fPts[i].fX = static_cast<double>(SK_RAND(seed)) / RAND_MAX * 100; + cubic1.fPts[i].fY = static_cast<double>(SK_RAND(seed)) / RAND_MAX * 100; + cubic2.fPts[i].fX = static_cast<double>(SK_RAND(seed)) / RAND_MAX * 100; + cubic2.fPts[i].fY = static_cast<double>(SK_RAND(seed)) / RAND_MAX * 100; } #if DEBUG_CRASH char str[1024]; @@ -415,15 +492,18 @@ cubic2[3].fX, cubic2[3].fY); #endif SkDRect rect1, rect2; - rect1.setBounds(cubic1); - rect2.setBounds(cubic2); + SkDCubic c1, c2; + c1.debugSet(cubic1.fPts); + c2.debugSet(cubic2.fPts); + rect1.setBounds(c1); + rect2.setBounds(c2); bool boundsIntersect = rect1.fLeft <= rect2.fRight && rect2.fLeft <= rect2.fRight && rect1.fTop <= rect2.fBottom && rect2.fTop <= rect1.fBottom; if (test == -1) { SkDebugf("ready...\n"); } SkIntersections intersections2; - int newIntersects = intersections2.intersect(cubic1, cubic2); + int newIntersects = intersections2.intersect(c1, c2); if (!boundsIntersect && newIntersects) { #if DEBUG_CRASH SkDebugf("%s %d unexpected intersection boundsIntersect=%d " @@ -434,9 +514,9 @@ } for (int pt = 0; pt < intersections2.used(); ++pt) { double tt1 = intersections2[0][pt]; - SkDPoint xy1 = cubic1.ptAtT(tt1); + SkDPoint xy1 = c1.ptAtT(tt1); double tt2 = intersections2[1][pt]; - SkDPoint xy2 = cubic2.ptAtT(tt2); + SkDPoint xy2 = c2.ptAtT(tt2); REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2)); } reporter->bumpTestCount(); @@ -445,17 +525,20 @@ static void intersectionFinder(int index0, int index1, double t1Seed, double t2Seed, double t1Step, double t2Step) { - const SkDCubic& cubic1 = newTestSet[index0]; - const SkDCubic& cubic2 = newTestSet[index1]; + const CubicPts& cubic1 = newTestSet[index0]; + const CubicPts& cubic2 = newTestSet[index1]; SkDPoint t1[3], t2[3]; bool toggle = true; + SkDCubic c1, c2; + c1.debugSet(cubic1.fPts); + c2.debugSet(cubic2.fPts); do { - t1[0] = cubic1.ptAtT(t1Seed - t1Step); - t1[1] = cubic1.ptAtT(t1Seed); - t1[2] = cubic1.ptAtT(t1Seed + t1Step); - t2[0] = cubic2.ptAtT(t2Seed - t2Step); - t2[1] = cubic2.ptAtT(t2Seed); - t2[2] = cubic2.ptAtT(t2Seed + t2Step); + t1[0] = c1.ptAtT(t1Seed - t1Step); + t1[1] = c1.ptAtT(t1Seed); + t1[2] = c1.ptAtT(t1Seed + t1Step); + t2[0] = c2.ptAtT(t2Seed - t2Step); + t2[1] = c2.ptAtT(t2Seed); + t2[2] = c2.ptAtT(t2Seed + t2Step); double dist[3][3]; dist[1][1] = t1[1].distance(t2[1]); int best_i = 1, best_j = 1; @@ -496,38 +579,38 @@ double t22 = t2Seed + t2Step * 2; SkDPoint test; while (!approximately_zero(t1Step)) { - test = cubic1.ptAtT(t10); + test = c1.ptAtT(t10); t10 += t1[1].approximatelyEqual(test) ? -t1Step : t1Step; t1Step /= 2; } t1Step = 0.1; while (!approximately_zero(t1Step)) { - test = cubic1.ptAtT(t12); + test = c1.ptAtT(t12); t12 -= t1[1].approximatelyEqual(test) ? -t1Step : t1Step; t1Step /= 2; } while (!approximately_zero(t2Step)) { - test = cubic2.ptAtT(t20); + test = c2.ptAtT(t20); t20 += t2[1].approximatelyEqual(test) ? -t2Step : t2Step; t2Step /= 2; } t2Step = 0.1; while (!approximately_zero(t2Step)) { - test = cubic2.ptAtT(t22); + test = c2.ptAtT(t22); t22 -= t2[1].approximatelyEqual(test) ? -t2Step : t2Step; t2Step /= 2; } #if ONE_OFF_DEBUG SkDebugf("%s t1=(%1.9g<%1.9g<%1.9g) t2=(%1.9g<%1.9g<%1.9g)\n", __FUNCTION__, t10, t1Seed, t12, t20, t2Seed, t22); - SkDPoint p10 = cubic1.ptAtT(t10); - SkDPoint p1Seed = cubic1.ptAtT(t1Seed); - SkDPoint p12 = cubic1.ptAtT(t12); + SkDPoint p10 = c1.ptAtT(t10); + SkDPoint p1Seed = c1.ptAtT(t1Seed); + SkDPoint p12 = c1.ptAtT(t12); SkDebugf("%s p1=(%1.9g,%1.9g)<(%1.9g,%1.9g)<(%1.9g,%1.9g)\n", __FUNCTION__, p10.fX, p10.fY, p1Seed.fX, p1Seed.fY, p12.fX, p12.fY); - SkDPoint p20 = cubic2.ptAtT(t20); - SkDPoint p2Seed = cubic2.ptAtT(t2Seed); - SkDPoint p22 = cubic2.ptAtT(t22); + SkDPoint p20 = c2.ptAtT(t20); + SkDPoint p2Seed = c2.ptAtT(t2Seed); + SkDPoint p22 = c2.ptAtT(t22); SkDebugf("%s p2=(%1.9g,%1.9g)<(%1.9g,%1.9g)<(%1.9g,%1.9g)\n", __FUNCTION__, p20.fX, p20.fY, p2Seed.fX, p2Seed.fY, p22.fX, p22.fY); #endif @@ -543,7 +626,7 @@ intersectionFinder(0, 1, 0.865213351, 0.865208087, t1Step, t2Step); } -static const SkDCubic selfSet[] = { +static const CubicPts selfSet[] = { {{{2, 3}, {0, 4}, {3, 2}, {5, 3}}}, {{{3, 6}, {2, 3}, {4, 0}, {3, 2}}}, {{{0, 2}, {2, 3}, {5, 1}, {3, 2}}}, @@ -557,38 +640,32 @@ int selfSetCount = (int) SK_ARRAY_COUNT(selfSet); static void selfOneOff(skiatest::Reporter* reporter, int index) { - const SkDCubic& cubic = selfSet[index]; -#if ONE_OFF_DEBUG - int idx2; - double max[3]; - int ts = cubic.findMaxCurvature(max); - for (idx2 = 0; idx2 < ts; ++idx2) { - SkDebugf("%s max[%d]=%1.9g (%1.9g, %1.9g)\n", __FUNCTION__, idx2, - max[idx2], cubic.ptAtT(max[idx2]).fX, cubic.ptAtT(max[idx2]).fY); + const CubicPts& cubic = selfSet[index]; + SkPoint c[4]; + for (int i = 0; i < 4; ++i) { + c[i] = cubic.fPts[i].asSkPoint(); } - SkTArray<double, true> ts1; - SkTArray<SkDQuad, true> quads1; - cubic.toQuadraticTs(cubic.calcPrecision(), &ts1); - for (idx2 = 0; idx2 < ts1.count(); ++idx2) { - SkDebugf("%s t[%d]=%1.9g\n", __FUNCTION__, idx2, ts1[idx2]); + SkScalar loopT[3]; + SkCubicType cubicType = SkClassifyCubic(c); + int breaks = SkDCubic::ComplexBreak(c, loopT); + SkASSERT(breaks < 2); + if (breaks && cubicType == SkCubicType::kLoop) { + SkIntersections i; + SkPoint twoCubics[7]; + SkChopCubicAt(c, twoCubics, loopT[0]); + SkDCubic chopped[2]; + chopped[0].set(&twoCubics[0]); + chopped[1].set(&twoCubics[3]); + int result = i.intersect(chopped[0], chopped[1]); + REPORTER_ASSERT(reporter, result == 2); + REPORTER_ASSERT(reporter, i.used() == 2); + for (int index = 0; index < result; ++index) { + SkDPoint pt1 = chopped[0].ptAtT(i[0][index]); + SkDPoint pt2 = chopped[1].ptAtT(i[1][index]); + REPORTER_ASSERT(reporter, pt1.approximatelyEqual(pt2)); + reporter->bumpTestCount(); + } } - CubicToQuads(cubic, cubic.calcPrecision(), quads1); - for (idx2 = 0; idx2 < quads1.count(); ++idx2) { - const SkDQuad& q = quads1[idx2]; - SkDebugf(" {{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}},\n", - q[0].fX, q[0].fY, q[1].fX, q[1].fY, q[2].fX, q[2].fY); - } - SkDebugf("\n"); -#endif - SkIntersections i; - int result = i.intersect(cubic); - REPORTER_ASSERT(reporter, result == 1); - REPORTER_ASSERT(reporter, i.used() == 1); - REPORTER_ASSERT(reporter, !approximately_equal(i[0][0], i[1][0])); - SkDPoint pt1 = cubic.ptAtT(i[0][0]); - SkDPoint pt2 = cubic.ptAtT(i[1][0]); - REPORTER_ASSERT(reporter, pt1.approximatelyEqual(pt2)); - reporter->bumpTestCount(); } static void cubicIntersectionSelfTest(skiatest::Reporter* reporter) { @@ -598,7 +675,12 @@ } } -static const SkDCubic coinSet[] = { +static const CubicPts coinSet[] = { + {{{72.350448608398438, 27.966041564941406}, {72.58441162109375, 27.861515045166016}, + {72.818222045898437, 27.756658554077148}, {73.394996643066406, 27.49799919128418}}}, + {{{73.394996643066406, 27.49799919128418}, {72.818222045898437, 27.756658554077148}, + {72.58441162109375, 27.861515045166016}, {72.350448608398438, 27.966041564941406}}}, + {{{297.04998779296875, 43.928997039794922}, {297.04998779296875, 43.928997039794922}, {300.69699096679688, 45.391998291015625}, {306.92498779296875, 43.08599853515625}}}, {{{297.04998779296875, 43.928997039794922}, {297.04998779296875, 43.928997039794922}, @@ -610,16 +692,13 @@ {{{317, 711}, {322.52285766601562, 711}, {327, 715.4771728515625}, {327, 721}}}, {{{324.07107543945312, 713.928955078125}, {324.4051513671875, 714.26300048828125}, {324.71566772460937, 714.62060546875}, {325, 714.9990234375}}}, - - {{{2, 3}, {0, 4}, {3, 2}, {5, 3}}}, - {{{2, 3}, {0, 4}, {3, 2}, {5, 3}}}, }; static int coinSetCount = (int) SK_ARRAY_COUNT(coinSet); static void coinOneOff(skiatest::Reporter* reporter, int index) { - const SkDCubic& cubic1 = coinSet[index]; - const SkDCubic& cubic2 = coinSet[index + 1]; + const CubicPts& cubic1 = coinSet[index]; + const CubicPts& cubic2 = coinSet[index + 1]; oneOff(reporter, cubic1, cubic2, true); } @@ -638,6 +717,10 @@ newOneOff(reporter, 0, 1); } +DEF_TEST(PathOpsCubicIntersectionTestsOneOff, reporter) { + testsOneOff(reporter, 10); +} + DEF_TEST(PathOpsCubicSelfOneOff, reporter) { selfOneOff(reporter, 0); }
diff --git a/src/third_party/skia/tests/PathOpsCubicIntersectionTestData.cpp b/src/third_party/skia/tests/PathOpsCubicIntersectionTestData.cpp index 31056d2..58dbfde 100644 --- a/src/third_party/skia/tests/PathOpsCubicIntersectionTestData.cpp +++ b/src/third_party/skia/tests/PathOpsCubicIntersectionTestData.cpp
@@ -14,7 +14,7 @@ static const double N = -FLT_EPSILON / 2; static const double M = -FLT_EPSILON / 3; -const SkDCubic pointDegenerates[] = { +const CubicPts pointDegenerates[] = { {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}, {{{1, 1}, {1, 1}, {1, 1}, {1, 1}}}, {{{1 + FLT_EPSILON_HALF, 1}, {1, 1 + FLT_EPSILON_HALF}, {1, 1}, {1, 1}}}, @@ -45,16 +45,16 @@ const size_t pointDegenerates_count = SK_ARRAY_COUNT(pointDegenerates); -const SkDCubic notPointDegenerates[] = { - {{{1 + FLT_EPSILON * 2, 1}, {1, FLT_EPSILON * 2}, {1, 1}, {1, 1}}}, - {{{1 + FLT_EPSILON * 2, 1}, {1 - FLT_EPSILON * 2, 1}, {1, 1}, {1, 1}}} +const CubicPts notPointDegenerates[] = { + {{{1 + FLT_EPSILON * 8, 1}, {1, FLT_EPSILON * 8}, {1, 1}, {1, 1}}}, + {{{1 + FLT_EPSILON * 8, 1}, {1 - FLT_EPSILON * 8, 1}, {1, 1}, {1, 1}}} }; const size_t notPointDegenerates_count = SK_ARRAY_COUNT(notPointDegenerates); // from http://www.truetex.com/bezint.htm -const SkDCubic tests[][2] = { +const CubicPts tests[][2] = { { // intersects in one place (data gives bezier clip fits {{{0, 45}, {6.0094158284751593, 51.610357411322688}, @@ -109,7 +109,7 @@ const size_t tests_count = SK_ARRAY_COUNT(tests); -const SkDCubic lines[] = { +const CubicPts lines[] = { {{{0, 0}, {0, 0}, {0, 0}, {1, 0}}}, // 0: horizontal {{{1, 0}, {0, 0}, {0, 0}, {0, 0}}}, {{{1, 0}, {2, 0}, {3, 0}, {4, 0}}}, @@ -145,7 +145,7 @@ const size_t lines_count = SK_ARRAY_COUNT(lines); // 'not a line' tries to fool the line detection code -const SkDCubic notLines[] = { +const CubicPts notLines[] = { {{{0, 0}, {0, 0}, {0, 1}, {1, 0}}}, {{{0, 0}, {0, 1}, {0, 0}, {1, 0}}}, {{{0, 0}, {0, 1}, {1, 0}, {0, 0}}}, @@ -156,10 +156,10 @@ const size_t notLines_count = SK_ARRAY_COUNT(notLines); -static const double E = FLT_EPSILON * 2; -static const double F = FLT_EPSILON * 3; +static const double E = FLT_EPSILON * 8; +static const double F = FLT_EPSILON * 8; -const SkDCubic modEpsilonLines[] = { +const CubicPts modEpsilonLines[] = { {{{0, E}, {0, 0}, {0, 0}, {1, 0}}}, // horizontal {{{0, 0}, {0, E}, {1, 0}, {0, 0}}}, {{{0, 0}, {1, 0}, {0, E}, {0, 0}}}, @@ -187,8 +187,8 @@ {{{1, 1}, {2, 2}, {2, 2+E}, {1, 1}}}, {{{1, 1}, {1, 1+E}, {3, 3}, {3, 3}}}, // first-middle middle-last coincident {{{1, 1}, {2+E, 2}, {3, 3}, {4, 4}}}, // no coincident - {{{1, 1}, {3, 3}, {2, 2}, {4, 4+F}}}, // INVESTIGATE: why the epsilon is bigger - {{{1, 1+F}, {2, 2}, {4, 4}, {3, 3}}}, // INVESTIGATE: why the epsilon is bigger + {{{1, 1}, {3, 3}, {2, 2}, {4, 4+F+F}}}, // INVESTIGATE: why the epsilon is bigger + {{{1, 1+F+F}, {2, 2}, {4, 4}, {3, 3}}}, // INVESTIGATE: why the epsilon is bigger {{{1, 1}, {3, 3}, {4, 4+E}, {2, 2}}}, {{{1, 1}, {4, 4}, {2, 2}, {3, 3+E}}}, {{{1, 1}, {4, 4}, {3, 3}, {2+E, 2}}}, @@ -202,7 +202,7 @@ const size_t modEpsilonLines_count = SK_ARRAY_COUNT(modEpsilonLines); -const SkDCubic lessEpsilonLines[] = { +const CubicPts lessEpsilonLines[] = { {{{0, D}, {0, 0}, {0, 0}, {1, 0}}}, // horizontal {{{1, 0}, {0, 0}, {0, 0}, {0, D}}}, {{{1, D}, {2, 0}, {3, 0}, {4, 0}}}, @@ -238,7 +238,7 @@ const size_t lessEpsilonLines_count = SK_ARRAY_COUNT(lessEpsilonLines); -const SkDCubic negEpsilonLines[] = { +const CubicPts negEpsilonLines[] = { {{{0, N}, {0, 0}, {0, 0}, {1, 0}}}, // horizontal {{{1, 0}, {0, 0}, {0, 0}, {0, N}}}, {{{1, N}, {2, 0}, {3, 0}, {4, 0}}},
diff --git a/src/third_party/skia/tests/PathOpsCubicIntersectionTestData.h b/src/third_party/skia/tests/PathOpsCubicIntersectionTestData.h index e37d571..4c9894c 100644 --- a/src/third_party/skia/tests/PathOpsCubicIntersectionTestData.h +++ b/src/third_party/skia/tests/PathOpsCubicIntersectionTestData.h
@@ -4,18 +4,18 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ -#include "SkPathOpsCubic.h" +#include "PathOpsTestCommon.h" -extern const SkDCubic pointDegenerates[]; -extern const SkDCubic notPointDegenerates[]; -extern const SkDCubic tests[][2]; -extern SkDCubic hexTests[][2]; +extern const CubicPts pointDegenerates[]; +extern const CubicPts notPointDegenerates[]; +extern const CubicPts tests[][2]; +extern CubicPts hexTests[][2]; -extern const SkDCubic lines[]; -extern const SkDCubic notLines[]; -extern const SkDCubic modEpsilonLines[]; -extern const SkDCubic lessEpsilonLines[]; -extern const SkDCubic negEpsilonLines[]; +extern const CubicPts lines[]; +extern const CubicPts notLines[]; +extern const CubicPts modEpsilonLines[]; +extern const CubicPts lessEpsilonLines[]; +extern const CubicPts negEpsilonLines[]; extern const size_t pointDegenerates_count; extern const size_t notPointDegenerates_count;
diff --git a/src/third_party/skia/tests/PathOpsCubicLineIntersectionIdeas.cpp b/src/third_party/skia/tests/PathOpsCubicLineIntersectionIdeas.cpp index 6aec8b1..1555475 100644 --- a/src/third_party/skia/tests/PathOpsCubicLineIntersectionIdeas.cpp +++ b/src/third_party/skia/tests/PathOpsCubicLineIntersectionIdeas.cpp
@@ -16,7 +16,7 @@ static bool gPathOpsCubicLineIntersectionIdeasVerbose = false; static struct CubicLineFailures { - SkDCubic c; + CubicPts c; double t; SkDPoint p; } cubicLineFailures[] = { @@ -145,13 +145,15 @@ double largestR2 = 0; for (int index = 0; index < 1000000000; ++index) { SkDPoint origin = {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}; - SkDCubic cubic = {{origin, + CubicPts cuPts = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)} }}; // construct a line at a known intersection double t = ran.nextRangeF(0, 1); + SkDCubic cubic; + cubic.debugSet(cuPts.fPts); SkDPoint pt = cubic.ptAtT(t); // skip answers with no intersections (although note the bug!) or two, or more // see if the line / cubic has a fun range of roots @@ -194,13 +196,13 @@ if (realRoots == 3) { smallest = SkTMin(smallest, allRoots[2]); } - SK_ALWAYSBREAK(smallest < 0); - SK_ALWAYSBREAK(smallest >= -1); + SkASSERT_RELEASE(smallest < 0); + SkASSERT_RELEASE(smallest >= -1); largeBits = 0; } else { frexp(largest, &largeBits); - SK_ALWAYSBREAK(largeBits >= 0); - SK_ALWAYSBREAK(largeBits < 256); + SkASSERT_RELEASE(largeBits >= 0); + SkASSERT_RELEASE(largeBits < 256); } double step = 1e-6; if (largeBits > 21) { @@ -222,7 +224,7 @@ break; } step *= 1.5; - SK_ALWAYSBREAK(step < 1); + SkASSERT_RELEASE(step < 1); } while (true); worstStep[largeBits] = SkTMax(worstStep[largeBits], diff); #if 0 @@ -248,7 +250,9 @@ } static double testOneFailure(const CubicLineFailures& failure) { - const SkDCubic& cubic = failure.c; + const CubicPts& c = failure.c; + SkDCubic cubic; + cubic.debugSet(c.fPts); const SkDPoint& pt = failure.p; double A, B, C, D; SkDCubic::Coefficients(&cubic[0].fY, &A, &B, &C, &D); @@ -256,11 +260,11 @@ double allRoots[3] = {0}, validRoots[3] = {0}; int realRoots = SkDCubic::RootsReal(A, B, C, D, allRoots); int valid = SkDQuad::AddValidTs(allRoots, realRoots, validRoots); - SK_ALWAYSBREAK(valid == 1); - SK_ALWAYSBREAK(realRoots != 1); + SkASSERT_RELEASE(valid == 1); + SkASSERT_RELEASE(realRoots != 1); double t = validRoots[0]; SkDPoint calcPt = cubic.ptAtT(t); - SK_ALWAYSBREAK(!calcPt.approximatelyEqual(pt)); + SkASSERT_RELEASE(!calcPt.approximatelyEqual(pt)); int iters = 0; double newT = binary_search(cubic, 0.1, pt, t, &iters); return newT; @@ -271,7 +275,7 @@ for (int index = 0; index < cubicLineFailuresCount; ++index) { const CubicLineFailures& failure = cubicLineFailures[index]; double newT = testOneFailure(failure); - SK_ALWAYSBREAK(newT >= 0); + SkASSERT_RELEASE(newT >= 0); } } @@ -279,5 +283,5 @@ return; // disable for now const CubicLineFailures& failure = cubicLineFailures[1]; double newT = testOneFailure(failure); - SK_ALWAYSBREAK(newT >= 0); + SkASSERT_RELEASE(newT >= 0); }
diff --git a/src/third_party/skia/tests/PathOpsCubicLineIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsCubicLineIntersectionTest.cpp index 234a538..a6ae5e6 100644 --- a/src/third_party/skia/tests/PathOpsCubicLineIntersectionTest.cpp +++ b/src/third_party/skia/tests/PathOpsCubicLineIntersectionTest.cpp
@@ -12,7 +12,7 @@ #include "Test.h" struct lineCubic { - SkDCubic cubic; + CubicPts cubic; SkDLine line; }; @@ -25,7 +25,9 @@ static const size_t failLineCubicTests_count = SK_ARRAY_COUNT(failLineCubicTests); static void testFail(skiatest::Reporter* reporter, int iIndex) { - const SkDCubic& cubic = failLineCubicTests[iIndex].cubic; + const CubicPts& cuPts = failLineCubicTests[iIndex].cubic; + SkDCubic cubic; + cubic.debugSet(cuPts.fPts); SkASSERT(ValidCubic(cubic)); const SkDLine& line = failLineCubicTests[iIndex].line; SkASSERT(ValidLine(line)); @@ -49,6 +51,12 @@ } static lineCubic lineCubicTests[] = { + {{{{0, 6}, {1.0851458311080933, 4.3722810745239258}, {1.5815209150314331, 3.038947582244873}, {1.9683018922805786, 1.9999997615814209}}}, + {{{3,2}, {1,2}}}}, + + {{{{0.468027353,4}, {1.06734705,1.33333337}, {1.36700678,0}, {3,0}}}, + {{{2,1}, {0,1}}}}, + {{{{-634.60540771484375, -481.262939453125}, {266.2696533203125, -752.70867919921875}, {-751.8370361328125, -317.37921142578125}, {-969.7427978515625, 824.7255859375}}}, {{{-287.9506133720805678, -557.1376476615772617}, @@ -117,7 +125,9 @@ } static void testOne(skiatest::Reporter* reporter, int iIndex) { - const SkDCubic& cubic = lineCubicTests[iIndex].cubic; + const CubicPts& cuPts = lineCubicTests[iIndex].cubic; + SkDCubic cubic; + cubic.debugSet(cuPts.fPts); SkASSERT(ValidCubic(cubic)); const SkDLine& line = lineCubicTests[iIndex].line; SkASSERT(ValidLine(line)); @@ -151,8 +161,6 @@ double cubicT = i[0][0]; SkDPoint prev = cubic.ptAtT(cubicT * 2 - 1); SkDPoint sect = cubic.ptAtT(cubicT); - double left[3] = { line.isLeft(prev), line.isLeft(sect), line.isLeft(cubic[3]) }; - SkDebugf("cubic=(%1.9g, %1.9g, %1.9g)\n", left[0], left[1], left[2]); SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", prev.fX, prev.fY, sect.fX, sect.fY); SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", sect.fX, sect.fY, cubic[3].fX, cubic[3].fY); SkDPoint prevL = line.ptAtT(i[1][0] - 0.0000007); @@ -184,7 +192,9 @@ DEF_TEST(PathOpsCubicLineIntersectionOneOff, reporter) { int iIndex = 0; testOne(reporter, iIndex); - const SkDCubic& cubic = lineCubicTests[iIndex].cubic; + const CubicPts& cuPts = lineCubicTests[iIndex].cubic; + SkDCubic cubic; + cubic.debugSet(cuPts.fPts); const SkDLine& line = lineCubicTests[iIndex].line; SkIntersections i; i.intersect(cubic, line);
diff --git a/src/third_party/skia/tests/PathOpsCubicQuadIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsCubicQuadIntersectionTest.cpp index 967dfc7..87c7e13 100644 --- a/src/third_party/skia/tests/PathOpsCubicQuadIntersectionTest.cpp +++ b/src/third_party/skia/tests/PathOpsCubicQuadIntersectionTest.cpp
@@ -13,55 +13,53 @@ #include "Test.h" static struct quadCubic { - SkDCubic cubic; - SkDQuad quad; - int answerCount; - SkDPoint answers[2]; + CubicPts cubic; + QuadPts quad; } quadCubicTests[] = { -#if 0 // FIXME : this should not fail (root problem behind skpcarrot_is24 ) - {{{{1020.08099,672.161987}, {1020.08002,630.73999}, {986.502014,597.161987}, {945.080994,597.161987}}}, - {{{1020,672}, {1020,640.93396}, {998.03302,618.96698}}}, 1, - {{1019.421, 662.449}}}, -#endif + {{{{945.08099365234375, 747.1619873046875}, {982.5679931640625, 747.1619873046875}, {1013.6290283203125, 719.656005859375}, {1019.1910400390625, 683.72601318359375}}}, + {{{945, 747}, {976.0660400390625, 747}, {998.03302001953125, 725.03302001953125}}}}, {{{{778, 14089}, {778, 14091.208984375}, {776.20916748046875, 14093}, {774, 14093}}}, - {{{778, 14089}, {777.99957275390625, 14090.65625}, {776.82843017578125, 14091.828125}}}, 2, - {{778, 14089}, {776.82855609581270,14091.828250841330}}}, + {{{778, 14089}, {777.99957275390625, 14090.65625}, {776.82843017578125, 14091.828125}}}}, + + {{{{1020.08099,672.161987}, {1020.08002,630.73999}, {986.502014,597.161987}, {945.080994,597.161987}}}, + {{{1020,672}, {1020,640.93396}, {998.03302,618.96698}}}}, + + {{{{778, 14089}, {778, 14091.208984375}, {776.20916748046875, 14093}, {774, 14093}}}, + {{{778, 14089}, {777.99957275390625, 14090.65625}, {776.82843017578125, 14091.828125}}}}, {{{{1110, 817}, {1110.55225f, 817}, {1111, 817.447693f}, {1111, 818}}}, - {{{1110.70715f, 817.292908f}, {1110.41406f, 817.000122f}, {1110, 817}}}, 2, - {{1110, 817}, {1110.70715f, 817.292908f}}}, + {{{1110.70715f, 817.292908f}, {1110.41406f, 817.000122f}, {1110, 817}}}}, {{{{1110, 817}, {1110.55225f, 817}, {1111, 817.447693f}, {1111, 818}}}, - {{{1111, 818}, {1110.99988f, 817.585876f}, {1110.70715f, 817.292908f}}}, 2, - {{1110.70715f, 817.292908f}, {1111, 818}}}, + {{{1111, 818}, {1110.99988f, 817.585876f}, {1110.70715f, 817.292908f}}}}, {{{{55, 207}, {52.238574981689453, 207}, {50, 204.76142883300781}, {50, 202}}}, {{{55, 207}, {52.929431915283203, 206.99949645996094}, - {51.464466094970703, 205.53553771972656}}}, 2, - {{55, 207}, {51.464466094970703, 205.53553771972656}}}, + {51.464466094970703, 205.53553771972656}}}}, {{{{49, 47}, {49, 74.614250183105469}, {26.614250183105469, 97}, {-1, 97}}}, {{{-8.659739592076221e-015, 96.991401672363281}, {20.065492630004883, 96.645187377929688}, - {34.355339050292969, 82.355339050292969}}}, 2, - {{34.355339050292969,82.355339050292969}, {34.28654835573549, 82.424006509351585}}}, + {34.355339050292969, 82.355339050292969}}}}, {{{{10,234}, {10,229.58172607421875}, {13.581720352172852,226}, {18,226}}}, - {{{18,226}, {14.686291694641113,226}, {12.342399597167969,228.3424072265625}}}, 1, - {{18,226}, {0,0}}}, + {{{18,226}, {14.686291694641113,226}, {12.342399597167969,228.3424072265625}}}}, {{{{10,234}, {10,229.58172607421875}, {13.581720352172852,226}, {18,226}}}, - {{{12.342399597167969,228.3424072265625}, {10,230.68629455566406}, {10,234}}}, 1, - {{10,234}, {0,0}}}, + {{{12.342399597167969,228.3424072265625}, {10,230.68629455566406}, {10,234}}}}, }; static const int quadCubicTests_count = (int) SK_ARRAY_COUNT(quadCubicTests); static void cubicQuadIntersection(skiatest::Reporter* reporter, int index) { int iIndex = static_cast<int>(index); - const SkDCubic& cubic = quadCubicTests[index].cubic; + const CubicPts& c = quadCubicTests[index].cubic; + SkDCubic cubic; + cubic.debugSet(c.fPts); SkASSERT(ValidCubic(cubic)); - const SkDQuad& quad = quadCubicTests[index].quad; + const QuadPts& q = quadCubicTests[index].quad; + SkDQuad quad; + quad.debugSet(q.fPts); SkASSERT(ValidQuad(quad)); SkReduceOrder reduce1; SkReduceOrder reduce2; @@ -77,7 +75,6 @@ } SkIntersections i; int roots = i.intersect(cubic, quad); - SkASSERT(roots == quadCubicTests[index].answerCount); for (int pt = 0; pt < roots; ++pt) { double tt1 = i[0][pt]; SkDPoint xy1 = cubic.ptAtT(tt1); @@ -88,15 +85,6 @@ __FUNCTION__, iIndex, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY); } REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2)); - bool found = false; - for (int idx2 = 0; idx2 < quadCubicTests[index].answerCount; ++idx2) { - found |= quadCubicTests[index].answers[idx2].approximatelyEqual(xy1); - } - if (!found) { - SkDebugf("%s [%d,%d] xy1=(%g,%g) != \n", - __FUNCTION__, iIndex, pt, xy1.fX, xy1.fY); - } - REPORTER_ASSERT(reporter, found); } reporter->bumpTestCount(); } @@ -111,195 +99,3 @@ DEF_TEST(PathOpsCubicQuadIntersectionOneOff, reporter) { cubicQuadIntersection(reporter, 0); } - -static bool gPathOpCubicQuadSlopVerbose = false; -static const int kCubicToQuadSubdivisionDepth = 8; // slots reserved for cubic to quads subdivision - -// determine that slop required after quad/quad finds a candidate intersection -// use the cross of the tangents plus the distance from 1 or 0 as knobs -DEF_TEST(PathOpsCubicQuadSlop, reporter) { - // create a random non-selfintersecting cubic - // break it into quadratics - // offset the quadratic, measuring the slop required to find the intersection - if (!gPathOpCubicQuadSlopVerbose) { // takes a while to run -- so exclude it by default - return; - } - int results[101]; - sk_bzero(results, sizeof(results)); - double minCross[101]; - sk_bzero(minCross, sizeof(minCross)); - double maxCross[101]; - sk_bzero(maxCross, sizeof(maxCross)); - double sumCross[101]; - sk_bzero(sumCross, sizeof(sumCross)); - int foundOne = 0; - int slopCount = 1; - SkRandom ran; - for (int index = 0; index < 10000000; ++index) { - if (index % 1000 == 999) SkDebugf("."); - SkDCubic cubic = {{ - {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, - {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, - {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, - {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)} - }}; - SkIntersections i; - if (i.intersect(cubic)) { - continue; - } - SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts; - cubic.toQuadraticTs(cubic.calcPrecision(), &ts); - double tStart = 0; - int tsCount = ts.count(); - for (int i1 = 0; i1 <= tsCount; ++i1) { - const double tEnd = i1 < tsCount ? ts[i1] : 1; - SkDCubic part = cubic.subDivide(tStart, tEnd); - SkDQuad quad = part.toQuad(); - SkReduceOrder reducer; - int order = reducer.reduce(quad); - if (order != 3) { - continue; - } - for (int i2 = 0; i2 < 100; ++i2) { - SkDPoint endDisplacement = {ran.nextRangeF(-100, 100), ran.nextRangeF(-100, 100)}; - SkDQuad nearby = {{ - {quad[0].fX + endDisplacement.fX, quad[0].fY + endDisplacement.fY}, - {quad[1].fX + ran.nextRangeF(-100, 100), quad[1].fY + ran.nextRangeF(-100, 100)}, - {quad[2].fX - endDisplacement.fX, quad[2].fY - endDisplacement.fY} - }}; - order = reducer.reduce(nearby); - if (order != 3) { - continue; - } - SkIntersections locals; - locals.allowNear(false); - locals.intersect(quad, nearby); - if (locals.used() != 1) { - continue; - } - // brute force find actual intersection - SkDLine cubicLine = {{ {0, 0}, {cubic[0].fX, cubic[0].fY } }}; - SkIntersections liner; - int i3; - int found = -1; - int foundErr = true; - for (i3 = 1; i3 <= 1000; ++i3) { - cubicLine[0] = cubicLine[1]; - cubicLine[1] = cubic.ptAtT(i3 / 1000.); - liner.reset(); - liner.allowNear(false); - liner.intersect(nearby, cubicLine); - if (liner.used() == 0) { - continue; - } - if (liner.used() > 1) { - foundErr = true; - break; - } - if (found > 0) { - foundErr = true; - break; - } - foundErr = false; - found = i3; - } - if (foundErr) { - continue; - } - SkDVector dist = liner.pt(0) - locals.pt(0); - SkDVector qV = nearby.dxdyAtT(locals[0][0]); - double cubicT = (found - 1 + liner[1][0]) / 1000.; - SkDVector cV = cubic.dxdyAtT(cubicT); - double qxc = qV.crossCheck(cV); - double qvLen = qV.length(); - double cvLen = cV.length(); - double maxLen = SkTMax(qvLen, cvLen); - qxc /= maxLen; - double quadT = tStart + (tEnd - tStart) * locals[0][0]; - double diffT = fabs(cubicT - quadT); - int diffIdx = (int) (diffT * 100); - results[diffIdx]++; - double absQxc = fabs(qxc); - if (sumCross[diffIdx] == 0) { - minCross[diffIdx] = maxCross[diffIdx] = sumCross[diffIdx] = absQxc; - } else { - minCross[diffIdx] = SkTMin(minCross[diffIdx], absQxc); - maxCross[diffIdx] = SkTMax(maxCross[diffIdx], absQxc); - sumCross[diffIdx] += absQxc; - } - if (diffIdx >= 20) { -#if 01 - SkDebugf("cubic={{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}" - " quad={{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}" - " {{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}" - " qT=%1.9g cT=%1.9g dist=%1.9g cross=%1.9g\n", - cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY, - cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY, - nearby[0].fX, nearby[0].fY, nearby[1].fX, nearby[1].fY, - nearby[2].fX, nearby[2].fY, - liner.pt(0).fX, liner.pt(0).fY, - locals.pt(0).fX, locals.pt(0).fY, quadT, cubicT, dist.length(), qxc); -#else - SkDebugf("qT=%1.9g cT=%1.9g dist=%1.9g cross=%1.9g\n", - quadT, cubicT, dist.length(), qxc); - SkDebugf("<div id=\"slop%d\">\n", ++slopCount); - SkDebugf("{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n" - "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n" - "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n", - cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY, - cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY, - nearby[0].fX, nearby[0].fY, nearby[1].fX, nearby[1].fY, - nearby[2].fX, nearby[2].fY, - liner.pt(0).fX, liner.pt(0).fY, - locals.pt(0).fX, locals.pt(0).fY); - SkDebugf("</div>\n\n"); -#endif - } - ++foundOne; - } - tStart = tEnd; - } - if (++foundOne >= 100000) { - break; - } - } -#if 01 - SkDebugf("slopCount=%d\n", slopCount); - int max = 100; - while (results[max] == 0) { - --max; - } - for (int i = 0; i <= max; ++i) { - if (i > 0 && i % 10 == 0) { - SkDebugf("\n"); - } - SkDebugf("%d ", results[i]); - } - SkDebugf("min\n"); - for (int i = 0; i <= max; ++i) { - if (i > 0 && i % 10 == 0) { - SkDebugf("\n"); - } - SkDebugf("%1.9g ", minCross[i]); - } - SkDebugf("max\n"); - for (int i = 0; i <= max; ++i) { - if (i > 0 && i % 10 == 0) { - SkDebugf("\n"); - } - SkDebugf("%1.9g ", maxCross[i]); - } - SkDebugf("avg\n"); - for (int i = 0; i <= max; ++i) { - if (i > 0 && i % 10 == 0) { - SkDebugf("\n"); - } - SkDebugf("%1.9g ", sumCross[i] / results[i]); - } -#else - for (int i = 1; i < slopCount; ++i) { - SkDebugf(" slop%d,\n", i); - } -#endif - SkDebugf("\n"); -}
diff --git a/src/third_party/skia/tests/PathOpsCubicReduceOrderTest.cpp b/src/third_party/skia/tests/PathOpsCubicReduceOrderTest.cpp index dc779b8..89a263c 100644 --- a/src/third_party/skia/tests/PathOpsCubicReduceOrderTest.cpp +++ b/src/third_party/skia/tests/PathOpsCubicReduceOrderTest.cpp
@@ -105,7 +105,9 @@ ? firstTestIndex : SK_MaxS32; #endif for (index = firstPointDegeneratesTest; index < pointDegenerates_count; ++index) { - const SkDCubic& cubic = pointDegenerates[index]; + const CubicPts& c = pointDegenerates[index]; + SkDCubic cubic; + cubic.debugSet(c.fPts); SkASSERT(ValidCubic(cubic)); order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); if (order != 1) { @@ -114,7 +116,9 @@ } } for (index = firstNotPointDegeneratesTest; index < notPointDegenerates_count; ++index) { - const SkDCubic& cubic = notPointDegenerates[index]; + const CubicPts& c = notPointDegenerates[index]; + SkDCubic cubic; + cubic.debugSet(c.fPts); SkASSERT(ValidCubic(cubic)); order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); if (order == 1) { @@ -124,7 +128,9 @@ } } for (index = firstLinesTest; index < lines_count; ++index) { - const SkDCubic& cubic = lines[index]; + const CubicPts& c = lines[index]; + SkDCubic cubic; + cubic.debugSet(c.fPts); SkASSERT(ValidCubic(cubic)); order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); if (order != 2) { @@ -133,7 +139,9 @@ } } for (index = firstNotLinesTest; index < notLines_count; ++index) { - const SkDCubic& cubic = notLines[index]; + const CubicPts& c = notLines[index]; + SkDCubic cubic; + cubic.debugSet(c.fPts); SkASSERT(ValidCubic(cubic)); order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); if (order == 2) { @@ -142,7 +150,9 @@ } } for (index = firstModEpsilonTest; index < modEpsilonLines_count; ++index) { - const SkDCubic& cubic = modEpsilonLines[index]; + const CubicPts& c = modEpsilonLines[index]; + SkDCubic cubic; + cubic.debugSet(c.fPts); SkASSERT(ValidCubic(cubic)); order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); if (order == 2) { @@ -151,7 +161,9 @@ } } for (index = firstLessEpsilonTest; index < lessEpsilonLines_count; ++index) { - const SkDCubic& cubic = lessEpsilonLines[index]; + const CubicPts& c = lessEpsilonLines[index]; + SkDCubic cubic; + cubic.debugSet(c.fPts); SkASSERT(ValidCubic(cubic)); order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); if (order != 2) { @@ -161,7 +173,9 @@ } } for (index = firstNegEpsilonTest; index < negEpsilonLines_count; ++index) { - const SkDCubic& cubic = negEpsilonLines[index]; + const CubicPts& c = negEpsilonLines[index]; + SkDCubic cubic; + cubic.debugSet(c.fPts); SkASSERT(ValidCubic(cubic)); order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); if (order != 2) { @@ -170,9 +184,11 @@ } } for (index = firstQuadraticPointTest; index < quadraticPoints_count; ++index) { - const SkDQuad& quad = quadraticPoints[index]; + const QuadPts& q = quadraticPoints[index]; + SkDQuad quad; + quad.debugSet(q.fPts); SkASSERT(ValidQuad(quad)); - SkDCubic cubic = quad.toCubic(); + SkDCubic cubic = quad.debugToCubic(); order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); if (order != 1) { SkDebugf("[%d] point quad order=%d\n", static_cast<int>(index), order); @@ -180,9 +196,11 @@ } } for (index = firstQuadraticLineTest; index < quadraticLines_count; ++index) { - const SkDQuad& quad = quadraticLines[index]; + const QuadPts& q = quadraticLines[index]; + SkDQuad quad; + quad.debugSet(q.fPts); SkASSERT(ValidQuad(quad)); - SkDCubic cubic = quad.toCubic(); + SkDCubic cubic = quad.debugToCubic(); order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); if (order != 2) { SkDebugf("[%d] line quad order=%d\n", static_cast<int>(index), order); @@ -190,9 +208,11 @@ } } for (index = firstQuadraticModLineTest; index < quadraticModEpsilonLines_count; ++index) { - const SkDQuad& quad = quadraticModEpsilonLines[index]; + const QuadPts& q = quadraticModEpsilonLines[index]; + SkDQuad quad; + quad.debugSet(q.fPts); SkASSERT(ValidQuad(quad)); - SkDCubic cubic = quad.toCubic(); + SkDCubic cubic = quad.debugToCubic(); order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics); if (order != 3) { SkDebugf("[%d] line mod quad order=%d\n", static_cast<int>(index), order);
diff --git a/src/third_party/skia/tests/PathOpsCubicToQuadsTest.cpp b/src/third_party/skia/tests/PathOpsCubicToQuadsTest.cpp deleted file mode 100644 index ab22a83..0000000 --- a/src/third_party/skia/tests/PathOpsCubicToQuadsTest.cpp +++ /dev/null
@@ -1,198 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#include "PathOpsCubicIntersectionTestData.h" -#include "PathOpsQuadIntersectionTestData.h" -#include "PathOpsTestCommon.h" -#include "SkGeometry.h" -#include "SkIntersections.h" -#include "SkPathOpsRect.h" -#include "SkReduceOrder.h" -#include "Test.h" - -static void test(skiatest::Reporter* reporter, const SkDCubic* cubics, const char* name, - int firstTest, size_t testCount) { - for (size_t index = firstTest; index < testCount; ++index) { - const SkDCubic& cubic = cubics[index]; - SkASSERT(ValidCubic(cubic)); - double precision = cubic.calcPrecision(); - SkTArray<SkDQuad, true> quads; - CubicToQuads(cubic, precision, quads); - if (quads.count() != 1 && quads.count() != 2) { - SkDebugf("%s [%d] cubic to quadratics failed count=%d\n", name, static_cast<int>(index), - quads.count()); - } - REPORTER_ASSERT(reporter, quads.count() == 1); - } -} - -static void test(skiatest::Reporter* reporter, const SkDQuad* quadTests, const char* name, - int firstTest, size_t testCount) { - for (size_t index = firstTest; index < testCount; ++index) { - const SkDQuad& quad = quadTests[index]; - SkASSERT(ValidQuad(quad)); - SkDCubic cubic = quad.toCubic(); - double precision = cubic.calcPrecision(); - SkTArray<SkDQuad, true> quads; - CubicToQuads(cubic, precision, quads); - if (quads.count() != 1 && quads.count() != 2) { - SkDebugf("%s [%d] cubic to quadratics failed count=%d\n", name, static_cast<int>(index), - quads.count()); - } - REPORTER_ASSERT(reporter, quads.count() <= 2); - } -} - -static void testC(skiatest::Reporter* reporter, const SkDCubic* cubics, const char* name, - int firstTest, size_t testCount) { - // test if computed line end points are valid - for (size_t index = firstTest; index < testCount; ++index) { - const SkDCubic& cubic = cubics[index]; - SkASSERT(ValidCubic(cubic)); - double precision = cubic.calcPrecision(); - SkTArray<SkDQuad, true> quads; - CubicToQuads(cubic, precision, quads); - if (!AlmostEqualUlps(cubic[0].fX, quads[0][0].fX) - || !AlmostEqualUlps(cubic[0].fY, quads[0][0].fY)) { - SkDebugf("[%d] unmatched start\n", static_cast<int>(index)); - REPORTER_ASSERT(reporter, 0); - } - int last = quads.count() - 1; - if (!AlmostEqualUlps(cubic[3].fX, quads[last][2].fX) - || !AlmostEqualUlps(cubic[3].fY, quads[last][2].fY)) { - SkDebugf("[%d] unmatched end\n", static_cast<int>(index)); - REPORTER_ASSERT(reporter, 0); - } - } -} - -static void testC(skiatest::Reporter* reporter, const SkDCubic(* cubics)[2], const char* name, - int firstTest, size_t testCount) { - for (size_t index = firstTest; index < testCount; ++index) { - for (int idx2 = 0; idx2 < 2; ++idx2) { - const SkDCubic& cubic = cubics[index][idx2]; - SkASSERT(ValidCubic(cubic)); - double precision = cubic.calcPrecision(); - SkTArray<SkDQuad, true> quads; - CubicToQuads(cubic, precision, quads); - if (!AlmostEqualUlps(cubic[0].fX, quads[0][0].fX) - || !AlmostEqualUlps(cubic[0].fY, quads[0][0].fY)) { - SkDebugf("[%d][%d] unmatched start\n", static_cast<int>(index), idx2); - REPORTER_ASSERT(reporter, 0); - } - int last = quads.count() - 1; - if (!AlmostEqualUlps(cubic[3].fX, quads[last][2].fX) - || !AlmostEqualUlps(cubic[3].fY, quads[last][2].fY)) { - SkDebugf("[%d][%d] unmatched end\n", static_cast<int>(index), idx2); - REPORTER_ASSERT(reporter, 0); - } - } - } -} - -DEF_TEST(CubicToQuads, reporter) { - enum { - RunAll, - RunPointDegenerates, - RunNotPointDegenerates, - RunLines, - RunNotLines, - RunModEpsilonLines, - RunLessEpsilonLines, - RunNegEpsilonLines, - RunQuadraticLines, - RunQuadraticModLines, - RunComputedLines, - RunComputedTests, - RunNone - } run = RunAll; - int firstTestIndex = 0; -#if 0 - run = RunComputedLines; - firstTestIndex = 18; -#endif - int firstPointDegeneratesTest = run == RunAll ? 0 : run == RunPointDegenerates - ? firstTestIndex : SK_MaxS32; - int firstNotPointDegeneratesTest = run == RunAll ? 0 : run == RunNotPointDegenerates - ? firstTestIndex : SK_MaxS32; - int firstLinesTest = run == RunAll ? 0 : run == RunLines ? firstTestIndex : SK_MaxS32; - int firstNotLinesTest = run == RunAll ? 0 : run == RunNotLines ? firstTestIndex : SK_MaxS32; - int firstModEpsilonTest = run == RunAll ? 0 : run == RunModEpsilonLines - ? firstTestIndex : SK_MaxS32; - int firstLessEpsilonTest = run == RunAll ? 0 : run == RunLessEpsilonLines - ? firstTestIndex : SK_MaxS32; - int firstNegEpsilonTest = run == RunAll ? 0 : run == RunNegEpsilonLines - ? firstTestIndex : SK_MaxS32; - int firstQuadraticLineTest = run == RunAll ? 0 : run == RunQuadraticLines - ? firstTestIndex : SK_MaxS32; - int firstQuadraticModLineTest = run == RunAll ? 0 : run == RunQuadraticModLines - ? firstTestIndex : SK_MaxS32; - int firstComputedLinesTest = run == RunAll ? 0 : run == RunComputedLines - ? firstTestIndex : SK_MaxS32; - int firstComputedCubicsTest = run == RunAll ? 0 : run == RunComputedTests - ? firstTestIndex : SK_MaxS32; - - test(reporter, pointDegenerates, "pointDegenerates", firstPointDegeneratesTest, - pointDegenerates_count); - testC(reporter, notPointDegenerates, "notPointDegenerates", firstNotPointDegeneratesTest, - notPointDegenerates_count); - test(reporter, lines, "lines", firstLinesTest, lines_count); - testC(reporter, notLines, "notLines", firstNotLinesTest, notLines_count); - testC(reporter, modEpsilonLines, "modEpsilonLines", firstModEpsilonTest, modEpsilonLines_count); - test(reporter, lessEpsilonLines, "lessEpsilonLines", firstLessEpsilonTest, - lessEpsilonLines_count); - test(reporter, negEpsilonLines, "negEpsilonLines", firstNegEpsilonTest, negEpsilonLines_count); - test(reporter, quadraticLines, "quadraticLines", firstQuadraticLineTest, quadraticLines_count); - test(reporter, quadraticModEpsilonLines, "quadraticModEpsilonLines", firstQuadraticModLineTest, - quadraticModEpsilonLines_count); - testC(reporter, lines, "computed lines", firstComputedLinesTest, lines_count); - testC(reporter, tests, "computed tests", firstComputedCubicsTest, tests_count); -} - -static SkDCubic locals[] = { - {{{0, 1}, {1.9274705288631189e-19, 1.0000000000000002}, - {0.0017190297609673323, 0.99828097023903239}, - {0.0053709083094631276, 0.99505672974365911}}}, - {{{14.5975863, 41.632436}, {16.3518929, 26.2639684}, {18.5165519, 7.68775139}, - {8.03767257, 89.1628526}}}, - {{{69.7292014, 38.6877352}, {24.7648688, 23.1501713}, {84.9283191, 90.2588441}, - {80.392774, 61.3533852}}}, - {{{60.776536520932126, 71.249307306133829}, {87.107894191103014, 22.377669868235323}, - {1.4974754310666936, 68.069569937917208}, {45.261946574441133, 17.536076632112298}}}, -}; - -static size_t localsCount = SK_ARRAY_COUNT(locals); - -#define DEBUG_CRASH 0 -#define TEST_AVERAGE_END_POINTS 0 // must take const off to test -extern const bool AVERAGE_END_POINTS; - -static void oneOff(skiatest::Reporter* reporter, size_t x) { - const SkDCubic& cubic = locals[x]; - SkASSERT(ValidCubic(cubic)); - const SkPoint skcubic[4] = { - {static_cast<float>(cubic[0].fX), static_cast<float>(cubic[0].fY)}, - {static_cast<float>(cubic[1].fX), static_cast<float>(cubic[1].fY)}, - {static_cast<float>(cubic[2].fX), static_cast<float>(cubic[2].fY)}, - {static_cast<float>(cubic[3].fX), static_cast<float>(cubic[3].fY)}}; - SkScalar skinflect[2]; - int skin = SkFindCubicInflections(skcubic, skinflect); - if (false) SkDebugf("%s %d %1.9g\n", __FUNCTION__, skin, skinflect[0]); - SkTArray<SkDQuad, true> quads; - double precision = cubic.calcPrecision(); - CubicToQuads(cubic, precision, quads); - if (false) SkDebugf("%s quads=%d\n", __FUNCTION__, quads.count()); -} - -DEF_TEST(CubicsToQuadratics_OneOff_Loop, reporter) { - for (size_t x = 0; x < localsCount; ++x) { - oneOff(reporter, x); - } -} - -DEF_TEST(CubicsToQuadratics_OneOff_Single, reporter) { - oneOff(reporter, 0); -}
diff --git a/src/third_party/skia/tests/PathOpsDCubicTest.cpp b/src/third_party/skia/tests/PathOpsDCubicTest.cpp index 422236d..b8c08df 100644 --- a/src/third_party/skia/tests/PathOpsDCubicTest.cpp +++ b/src/third_party/skia/tests/PathOpsDCubicTest.cpp
@@ -8,22 +8,18 @@ #include "SkPathOpsCubic.h" #include "Test.h" -static const SkDCubic tests[] = { - {{{2, 0}, {3, 1}, {2, 2}, {1, 1}}}, - {{{3, 1}, {2, 2}, {1, 1}, {2, 0}}}, - {{{3, 0}, {2, 1}, {3, 2}, {1, 1}}}, +static const CubicPts hullTests[] = { +{{{2.6250000819563866, 2.3750000223517418}, {2.833333432674408, 2.3333333432674408}, {3.1111112236976624, 2.3333333134651184}, {3.4074075222015381, 2.3333332538604736}}}, }; -static const size_t tests_count = SK_ARRAY_COUNT(tests); +static const size_t hullTests_count = SK_ARRAY_COUNT(hullTests); -DEF_TEST(PathOpsDCubic, reporter) { - for (size_t index = 0; index < tests_count; ++index) { - const SkDCubic& cubic = tests[index]; - SkASSERT(ValidCubic(cubic)); - bool result = cubic.clockwise(); - if (!result) { - SkDebugf("%s [%d] expected clockwise\n", __FUNCTION__, index); - REPORTER_ASSERT(reporter, 0); - } +DEF_TEST(PathOpsCubicHull, reporter) { + for (size_t index = 0; index < hullTests_count; ++index) { + const CubicPts& c = hullTests[index]; + SkDCubic cubic; + cubic.debugSet(c.fPts); + char order[4]; + cubic.convexHull(order); } }
diff --git a/src/third_party/skia/tests/PathOpsDLineTest.cpp b/src/third_party/skia/tests/PathOpsDLineTest.cpp index dd86dd3..1e16002 100644 --- a/src/third_party/skia/tests/PathOpsDLineTest.cpp +++ b/src/third_party/skia/tests/PathOpsDLineTest.cpp
@@ -17,15 +17,6 @@ {{{3, 2}, {1, 1}}}, }; -static const SkDPoint left[] = { - {2, 1}, - {1, 0}, - {1, 1}, - {1, 2}, - {2, 0}, - {2, 1} -}; - static const size_t tests_count = SK_ARRAY_COUNT(tests); DEF_TEST(PathOpsLineUtilities, reporter) { @@ -36,17 +27,6 @@ SkPoint pts[2] = {line[0].asSkPoint(), line[1].asSkPoint()}; line2.set(pts); REPORTER_ASSERT(reporter, line[0] == line2[0] && line[1] == line2[1]); - const SkDPoint& pt = left[index]; - SkASSERT(ValidPoint(pt)); - double result = line.isLeft(pt); - if ((result <= 0 && index >= 1) || (result < 0 && index == 0)) { - SkDebugf("%s [%d] expected left\n", __FUNCTION__, index); - REPORTER_ASSERT(reporter, 0); - } - line2 = line.subDivide(1, 0); - REPORTER_ASSERT(reporter, line[0] == line2[1] && line[1] == line2[0]); - line2 = SkDLine::SubDivide(pts, 1, 0); - REPORTER_ASSERT(reporter, line[0] == line2[1] && line[1] == line2[0]); SkDPoint mid = line.ptAtT(.5); REPORTER_ASSERT(reporter, approximately_equal((line[0].fX + line[1].fX) / 2, mid.fX)); REPORTER_ASSERT(reporter, approximately_equal((line[0].fY + line[1].fY) / 2, mid.fY));
diff --git a/src/third_party/skia/tests/PathOpsDPointTest.cpp b/src/third_party/skia/tests/PathOpsDPointTest.cpp index 186d691..e197d5d 100644 --- a/src/third_party/skia/tests/PathOpsDPointTest.cpp +++ b/src/third_party/skia/tests/PathOpsDPointTest.cpp
@@ -38,7 +38,6 @@ REPORTER_ASSERT(reporter, p == pt); REPORTER_ASSERT(reporter, p.approximatelyEqual(sPt)); REPORTER_ASSERT(reporter, p.roughlyEqual(pt)); - REPORTER_ASSERT(reporter, p.moreRoughlyEqual(pt)); p.fX = p.fY = 0; REPORTER_ASSERT(reporter, p.fX == 0 && p.fY == 0); REPORTER_ASSERT(reporter, p.approximatelyZero());
diff --git a/src/third_party/skia/tests/PathOpsDQuadTest.cpp b/src/third_party/skia/tests/PathOpsDQuadTest.cpp deleted file mode 100644 index bd29ff1..0000000 --- a/src/third_party/skia/tests/PathOpsDQuadTest.cpp +++ /dev/null
@@ -1,63 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#include "PathOpsTestCommon.h" -#include "SkPath.h" -#include "SkPathOpsQuad.h" -#include "SkRRect.h" -#include "Test.h" - -static const SkDQuad tests[] = { - {{{1, 1}, {2, 1}, {0, 2}}}, - {{{0, 0}, {1, 1}, {3, 1}}}, - {{{2, 0}, {1, 1}, {2, 2}}}, - {{{4, 0}, {0, 1}, {4, 2}}}, - {{{0, 0}, {0, 1}, {1, 1}}}, -}; - -static const SkDPoint inPoint[]= { - {1, 1.2}, - {1, 0.8}, - {1.8, 1}, - {1.5, 1}, - {0.4999, 0.5}, // was 0.5, 0.5; points on the hull are considered outside -}; - -static const SkDPoint outPoint[]= { - {1, 1.6}, - {1, 1.5}, - {2.2, 1}, - {1.5, 1.5}, - {1.1, 0.5}, -}; - -static const size_t tests_count = SK_ARRAY_COUNT(tests); - -DEF_TEST(PathOpsDQuad, reporter) { - for (size_t index = 0; index < tests_count; ++index) { - const SkDQuad& quad = tests[index]; - SkASSERT(ValidQuad(quad)); - bool result = quad.pointInHull(inPoint[index]); - if (!result) { - SkDebugf("%s [%d] expected in hull\n", __FUNCTION__, index); - REPORTER_ASSERT(reporter, 0); - } - result = quad.pointInHull(outPoint[index]); - if (result) { - SkDebugf("%s [%d] expected outside hull\n", __FUNCTION__, index); - REPORTER_ASSERT(reporter, 0); - } - } -} - -DEF_TEST(PathOpsRRect, reporter) { - SkPath path; - SkRRect rRect; - SkRect rect = {135, 143, 250, 177}; - SkVector radii[4] = {{8, 8}, {8, 8}, {0, 0}, {0, 0}}; - rRect.setRectRadii(rect, radii); - path.addRRect(rRect); -}
diff --git a/src/third_party/skia/tests/PathOpsDRectTest.cpp b/src/third_party/skia/tests/PathOpsDRectTest.cpp index 874e82b..fa52e78 100644 --- a/src/third_party/skia/tests/PathOpsDRectTest.cpp +++ b/src/third_party/skia/tests/PathOpsDRectTest.cpp
@@ -11,16 +11,7 @@ #include "SkPathOpsRect.h" #include "Test.h" -static const SkDLine lineTests[] = { - {{{2, 1}, {2, 1}}}, - {{{2, 1}, {1, 1}}}, - {{{2, 1}, {2, 2}}}, - {{{1, 1}, {2, 2}}}, - {{{3, 0}, {2, 1}}}, - {{{3, 2}, {1, 1}}}, -}; - -static const SkDQuad quadTests[] = { +static const QuadPts quadTests[] = { {{{1, 1}, {2, 1}, {0, 2}}}, {{{0, 0}, {1, 1}, {3, 1}}}, {{{2, 0}, {1, 1}, {2, 2}}}, @@ -28,50 +19,39 @@ {{{0, 0}, {0, 1}, {1, 1}}}, }; -static const SkDCubic cubicTests[] = { +static const CubicPts cubicTests[] = { {{{2, 0}, {3, 1}, {2, 2}, {1, 1}}}, {{{3, 1}, {2, 2}, {1, 1}, {2, 0}}}, {{{3, 0}, {2, 1}, {3, 2}, {1, 1}}}, }; -static const size_t lineTests_count = SK_ARRAY_COUNT(lineTests); static const size_t quadTests_count = SK_ARRAY_COUNT(quadTests); static const size_t cubicTests_count = SK_ARRAY_COUNT(cubicTests); +static void setRawBounds(const SkDQuad& quad, SkDRect* rect) { + rect->set(quad[0]); + rect->add(quad[1]); + rect->add(quad[2]); +} + +static void setRawBounds(const SkDCubic& cubic, SkDRect* rect) { + rect->set(cubic[0]); + rect->add(cubic[1]); + rect->add(cubic[2]); + rect->add(cubic[3]); +} + DEF_TEST(PathOpsDRect, reporter) { size_t index; SkDRect rect, rect2; - for (index = 0; index < lineTests_count; ++index) { - const SkDLine& line = lineTests[index]; - SkASSERT(ValidLine(line)); - rect.setBounds(line); - REPORTER_ASSERT(reporter, rect.fLeft == SkTMin(line[0].fX, line[1].fX)); - REPORTER_ASSERT(reporter, rect.fTop == SkTMin(line[0].fY, line[1].fY)); - REPORTER_ASSERT(reporter, rect.fRight == SkTMax(line[0].fX, line[1].fX)); - REPORTER_ASSERT(reporter, rect.fBottom == SkTMax(line[0].fY, line[1].fY)); - rect2.set(line[0]); - rect2.add(line[1]); - REPORTER_ASSERT(reporter, rect2.fLeft == SkTMin(line[0].fX, line[1].fX)); - REPORTER_ASSERT(reporter, rect2.fTop == SkTMin(line[0].fY, line[1].fY)); - REPORTER_ASSERT(reporter, rect2.fRight == SkTMax(line[0].fX, line[1].fX)); - REPORTER_ASSERT(reporter, rect2.fBottom == SkTMax(line[0].fY, line[1].fY)); - REPORTER_ASSERT(reporter, rect.contains(line[0])); - REPORTER_ASSERT(reporter, rect.intersects(&rect2)); - } for (index = 0; index < quadTests_count; ++index) { - const SkDQuad& quad = quadTests[index]; + const QuadPts& q = quadTests[index]; + SkDQuad quad; + quad.debugSet(q.fPts); SkASSERT(ValidQuad(quad)); - rect.setRawBounds(quad); - REPORTER_ASSERT(reporter, rect.fLeft == SkTMin(quad[0].fX, - SkTMin(quad[1].fX, quad[2].fX))); - REPORTER_ASSERT(reporter, rect.fTop == SkTMin(quad[0].fY, - SkTMin(quad[1].fY, quad[2].fY))); - REPORTER_ASSERT(reporter, rect.fRight == SkTMax(quad[0].fX, - SkTMax(quad[1].fX, quad[2].fX))); - REPORTER_ASSERT(reporter, rect.fBottom == SkTMax(quad[0].fY, - SkTMax(quad[1].fY, quad[2].fY))); + setRawBounds(quad, &rect); rect2.setBounds(quad); - REPORTER_ASSERT(reporter, rect.intersects(&rect2)); + REPORTER_ASSERT(reporter, rect.intersects(rect2)); // FIXME: add a recursive box subdivision method to verify that tight bounds is correct SkDPoint leftTop = {rect2.fLeft, rect2.fTop}; REPORTER_ASSERT(reporter, rect.contains(leftTop)); @@ -79,19 +59,13 @@ REPORTER_ASSERT(reporter, rect.contains(rightBottom)); } for (index = 0; index < cubicTests_count; ++index) { - const SkDCubic& cubic = cubicTests[index]; + const CubicPts& c = cubicTests[index]; + SkDCubic cubic; + cubic.debugSet(c.fPts); SkASSERT(ValidCubic(cubic)); - rect.setRawBounds(cubic); - REPORTER_ASSERT(reporter, rect.fLeft == SkTMin(cubic[0].fX, - SkTMin(cubic[1].fX, SkTMin(cubic[2].fX, cubic[3].fX)))); - REPORTER_ASSERT(reporter, rect.fTop == SkTMin(cubic[0].fY, - SkTMin(cubic[1].fY, SkTMin(cubic[2].fY, cubic[3].fY)))); - REPORTER_ASSERT(reporter, rect.fRight == SkTMax(cubic[0].fX, - SkTMax(cubic[1].fX, SkTMax(cubic[2].fX, cubic[3].fX)))); - REPORTER_ASSERT(reporter, rect.fBottom == SkTMax(cubic[0].fY, - SkTMax(cubic[1].fY, SkTMax(cubic[2].fY, cubic[3].fY)))); + setRawBounds(cubic, &rect); rect2.setBounds(cubic); - REPORTER_ASSERT(reporter, rect.intersects(&rect2)); + REPORTER_ASSERT(reporter, rect.intersects(rect2)); // FIXME: add a recursive box subdivision method to verify that tight bounds is correct SkDPoint leftTop = {rect2.fLeft, rect2.fTop}; REPORTER_ASSERT(reporter, rect.contains(leftTop));
diff --git a/src/third_party/skia/tests/PathOpsDTriangleTest.cpp b/src/third_party/skia/tests/PathOpsDTriangleTest.cpp deleted file mode 100644 index b5e2d41..0000000 --- a/src/third_party/skia/tests/PathOpsDTriangleTest.cpp +++ /dev/null
@@ -1,70 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#include "PathOpsTestCommon.h" -#include "SkPathOpsTriangle.h" -#include "Test.h" - -static const SkDTriangle tests[] = { - {{{2, 0}, {3, 1}, {2, 2}}}, - {{{3, 1}, {2, 2}, {1, 1}}}, - {{{3, 0}, {2, 1}, {3, 2}}}, -}; - -static const SkDPoint inPoint[] = { - {2.5, 1}, - {2, 1.5}, - {2.5, 1}, -}; - -static const SkDPoint outPoint[] = { - {3, 0}, - {2.5, 2}, - {2.5, 2}, -}; - -static const size_t tests_count = SK_ARRAY_COUNT(tests); - -DEF_TEST(PathOpsTriangleUtilities, reporter) { - for (size_t index = 0; index < tests_count; ++index) { - const SkDTriangle& triangle = tests[index]; - SkASSERT(ValidTriangle(triangle)); - bool result = triangle.contains(inPoint[index]); - if (!result) { - SkDebugf("%s [%d] expected point in triangle\n", __FUNCTION__, index); - REPORTER_ASSERT(reporter, 0); - } - result = triangle.contains(outPoint[index]); - if (result) { - SkDebugf("%s [%d] expected point outside triangle\n", __FUNCTION__, index); - REPORTER_ASSERT(reporter, 0); - } - } -} - -static const SkDTriangle oneOff[] = { - {{{271.03291625750461, 5.0402503630087025e-05}, {275.21652430019037, 3.6997300650817753}, - {279.25839233398438, 7.7416000366210938}}}, - - {{{271.03291625750461, 5.0402503617874572e-05}, {275.21652430019037, 3.6997300650817877}, - {279.25839233398438, 7.7416000366210938}}} -}; - -static const size_t oneOff_count = SK_ARRAY_COUNT(oneOff); - -DEF_TEST(PathOpsTriangleOneOff, reporter) { - for (size_t index = 0; index < oneOff_count; ++index) { - const SkDTriangle& triangle = oneOff[index]; - SkASSERT(ValidTriangle(triangle)); - for (int inner = 0; inner < 3; ++inner) { - bool result = triangle.contains(triangle.fPts[inner]); - if (result) { - SkDebugf("%s [%d][%d] point on triangle is not in\n", __FUNCTION__, index, inner); - REPORTER_ASSERT(reporter, 0); - } - } - } -}
diff --git a/src/third_party/skia/tests/PathOpsDVectorTest.cpp b/src/third_party/skia/tests/PathOpsDVectorTest.cpp index ab291b2..583a868 100644 --- a/src/third_party/skia/tests/PathOpsDVectorTest.cpp +++ b/src/third_party/skia/tests/PathOpsDVectorTest.cpp
@@ -28,8 +28,6 @@ SkASSERT(ValidVector(v2)); v1 += v2; REPORTER_ASSERT(reporter, v1.fX == 0 && v1.fY == 0); - SkDPoint p = tests[index + 1] + v2; - REPORTER_ASSERT(reporter, p == tests[index]); v2 -= v2; REPORTER_ASSERT(reporter, v2.fX == 0 && v2.fY == 0); v1 = tests[index + 1] - tests[index];
diff --git a/src/third_party/skia/tests/PathOpsDebug.cpp b/src/third_party/skia/tests/PathOpsDebug.cpp old mode 100755 new mode 100644 index 8ac38aa..49f730a --- a/src/third_party/skia/tests/PathOpsDebug.cpp +++ b/src/third_party/skia/tests/PathOpsDebug.cpp
@@ -1,5 +1,15 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "PathOpsTSectDebug.h" +#include "SkOpCoincidence.h" #include "SkOpContour.h" #include "SkIntersectionHelper.h" +#include "SkMutex.h" #include "SkOpSegment.h" #include "SkString.h" @@ -19,137 +29,10 @@ } } - -#if DEBUG_SHOW_TEST_NAME - -static void output_scalar(SkScalar num) { - if (num == (int) num) { - SkDebugf("%d", (int) num); - } else { - SkString str; - str.printf("%1.9g", num); - int width = (int) str.size(); - const char* cStr = str.c_str(); - while (cStr[width - 1] == '0') { - --width; - } - str.resize(width); - SkDebugf("%sf", str.c_str()); - } +inline void DebugDumpHexFloat(float x) { + SkDebugf("SkBits2Float(0x%08x)", SkFloat2Bits(x)); } -static void output_points(const SkPoint* pts, int count) { - for (int index = 0; index < count; ++index) { - output_scalar(pts[index].fX); - SkDebugf(", "); - output_scalar(pts[index].fY); - if (index + 1 < count) { - SkDebugf(", "); - } - } - SkDebugf(");\n"); -} - -static void showPathContours(SkPath::RawIter& iter, const char* pathName) { - uint8_t verb; - SkPoint pts[4]; - while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { - switch (verb) { - case SkPath::kMove_Verb: - SkDebugf(" %s.moveTo(", pathName); - output_points(&pts[0], 1); - continue; - case SkPath::kLine_Verb: - SkDebugf(" %s.lineTo(", pathName); - output_points(&pts[1], 1); - break; - case SkPath::kQuad_Verb: - SkDebugf(" %s.quadTo(", pathName); - output_points(&pts[1], 2); - break; - case SkPath::kCubic_Verb: - SkDebugf(" %s.cubicTo(", pathName); - output_points(&pts[1], 3); - break; - case SkPath::kClose_Verb: - SkDebugf(" %s.close();\n", pathName); - break; - default: - SkDEBUGFAIL("bad verb"); - return; - } - } -} - -static const char* gFillTypeStr[] = { - "kWinding_FillType", - "kEvenOdd_FillType", - "kInverseWinding_FillType", - "kInverseEvenOdd_FillType" -}; - -void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) { - SkPath::RawIter iter(path); -#define SUPPORT_RECT_CONTOUR_DETECTION 0 -#if SUPPORT_RECT_CONTOUR_DETECTION - int rectCount = path.isRectContours() ? path.rectContours(NULL, NULL) : 0; - if (rectCount > 0) { - SkTDArray<SkRect> rects; - SkTDArray<SkPath::Direction> directions; - rects.setCount(rectCount); - directions.setCount(rectCount); - path.rectContours(rects.begin(), directions.begin()); - for (int contour = 0; contour < rectCount; ++contour) { - const SkRect& rect = rects[contour]; - SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop, - rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction - ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction"); - } - return; - } -#endif - SkPath::FillType fillType = path.getFillType(); - SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType); - if (includeDeclaration) { - SkDebugf(" SkPath %s;\n", name); - } - SkDebugf(" %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]); - iter.setPath(path); - showPathContours(iter, name); -} - -static void show_function_header(const char* functionName) { - SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName); - if (strcmp("skphealth_com76", functionName) == 0) { - SkDebugf("found it\n"); - } -} - -static const char* gOpStrs[] = { - "kDifference_PathOp", - "kIntersect_PathOp", - "kUnion_PathOp", - "kXor_PathOp", - "kReverseDifference_PathOp", -}; - -static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) { - SkDebugf(" testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]); - SkDebugf("}\n"); -} - -SK_DECLARE_STATIC_MUTEX(gTestMutex); - -void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp, - const char* testName) { - SkAutoMutexAcquire ac(gTestMutex); - show_function_header(testName); - ShowOnePath(a, "path", true); - ShowOnePath(b, "pathB", true); - show_op(shapeOp, "path", "pathB"); -} -#endif - // if not defined by PathOpsDebug.cpp ... #if !defined SK_DEBUG && FORCE_RELEASE bool SkPathOpsDebug::ValidWind(int wind) { @@ -165,236 +48,105 @@ } #endif -void SkOpAngle::dump() const { - dumpOne(true); +static void DumpID(int id) { + SkDebugf("} "); + if (id >= 0) { + SkDebugf("id=%d", id); + } SkDebugf("\n"); } -void SkOpAngle::dumpOne(bool functionHeader) const { -// fSegment->debugValidate(); - const SkOpSpan& mSpan = fSegment->span(SkMin32(fStart, fEnd)); - if (functionHeader) { - SkDebugf("%s ", __FUNCTION__); - } - SkDebugf("[%d", fSegment->debugID()); - SkDebugf("/%d", debugID()); - SkDebugf("] next="); - if (fNext) { - SkDebugf("%d", fNext->fSegment->debugID()); - SkDebugf("/%d", fNext->debugID()); - } else { - SkDebugf("?"); - } - SkDebugf(" sect=%d/%d ", fSectorStart, fSectorEnd); - SkDebugf(" s=%1.9g [%d] e=%1.9g [%d]", fSegment->span(fStart).fT, fStart, - fSegment->span(fEnd).fT, fEnd); - SkDebugf(" sgn=%d windVal=%d", sign(), mSpan.fWindValue); - - SkDebugf(" windSum="); - SkPathOpsDebug::WindingPrintf(mSpan.fWindSum); - if (mSpan.fOppValue != 0 || mSpan.fOppSum != SK_MinS32) { - SkDebugf(" oppVal=%d", mSpan.fOppValue); - SkDebugf(" oppSum="); - SkPathOpsDebug::WindingPrintf(mSpan.fOppSum); - } - if (mSpan.fDone) { - SkDebugf(" done"); - } - if (unorderable()) { - SkDebugf(" unorderable"); - } - if (small()) { - SkDebugf(" small"); - } - if (mSpan.fTiny) { - SkDebugf(" tiny"); - } - if (fSegment->operand()) { - SkDebugf(" operand"); - } - if (fStop) { - SkDebugf(" stop"); - } +void SkDConic::dump() const { + dumpInner(); + SkDebugf("},\n"); } -void SkOpAngle::dumpTo(const SkOpSegment* segment, const SkOpAngle* to) const { - const SkOpAngle* first = this; - const SkOpAngle* next = this; - const char* indent = ""; - do { - SkDebugf("%s", indent); - next->dumpOne(false); - if (segment == next->fSegment) { - if (this == fNext) { - SkDebugf(" << from"); - } - if (to == fNext) { - SkDebugf(" << to"); - } - } - SkDebugf("\n"); - indent = " "; - next = next->fNext; - } while (next && next != first); +void SkDConic::dumpID(int id) const { + dumpInner(); + DumpID(id); } -void SkOpAngle::dumpLoop() const { - const SkOpAngle* first = this; - const SkOpAngle* next = this; - do { - next->dumpOne(false); - SkDebugf("\n"); - next = next->fNext; - } while (next && next != first); -} - -void SkOpAngle::dumpPartials() const { - const SkOpAngle* first = this; - const SkOpAngle* next = this; - do { - next->fCurvePart.dumpNumber(); - next = next->fNext; - } while (next && next != first); -} - -void SkOpAngleSet::dump() const { - // FIXME: unimplemented -/* This requires access to the internal SkChunkAlloc data - Defer implementing this until it is needed for debugging -*/ - SkASSERT(0); -} - -void SkOpContour::dump() const { - int segmentCount = fSegments.count(); - SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID()); - for (int test = 0; test < segmentCount; ++test) { - SkDebugf(" [%d] ((SkOpSegment*) 0x%p) [%d]\n", test, &fSegments[test], - fSegments[test].debugID()); - } -} - -void SkOpContour::dumpAngles() const { - int segmentCount = fSegments.count(); - SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID()); - for (int test = 0; test < segmentCount; ++test) { - SkDebugf(" [%d] ", test); - fSegments[test].dumpAngles(); - } -} - -void SkOpContour::dumpCoincidence(const SkCoincidence& coin) const { - int thisIndex = coin.fSegments[0]; - const SkOpSegment& s1 = fSegments[thisIndex]; - int otherIndex = coin.fSegments[1]; - const SkOpSegment& s2 = coin.fOther->fSegments[otherIndex]; - SkDebugf("((SkOpSegment*) 0x%p) [%d] ((SkOpSegment*) 0x%p) [%d]\n", &s1, s1.debugID(), - &s2, s2.debugID()); - for (int index = 0; index < 2; ++index) { - SkDebugf(" {%1.9gf, %1.9gf}", coin.fPts[0][index].fX, coin.fPts[0][index].fY); - if (coin.fNearly[index]) { - SkDebugf(" {%1.9gf, %1.9gf}", coin.fPts[1][index].fX, coin.fPts[1][index].fY); - } - SkDebugf(" seg1t=%1.9g seg2t=%1.9g\n", coin.fTs[0][index], coin.fTs[1][index]); - } -} - -void SkOpContour::dumpCoincidences() const { - int count = fCoincidences.count(); - if (count > 0) { - SkDebugf("fCoincidences count=%d\n", count); - for (int test = 0; test < count; ++test) { - dumpCoincidence(fCoincidences[test]); - } - } - count = fPartialCoincidences.count(); - if (count == 0) { - return; - } - SkDebugf("fPartialCoincidences count=%d\n", count); - for (int test = 0; test < count; ++test) { - dumpCoincidence(fPartialCoincidences[test]); - } -} - -void SkOpContour::dumpPt(int index) const { - int segmentCount = fSegments.count(); - for (int test = 0; test < segmentCount; ++test) { - const SkOpSegment& segment = fSegments[test]; - if (segment.debugID() == index) { - fSegments[test].dumpPts(); - } - } -} - -void SkOpContour::dumpPts() const { - int segmentCount = fSegments.count(); - SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID()); - for (int test = 0; test < segmentCount; ++test) { - SkDebugf(" [%d] ", test); - fSegments[test].dumpPts(); - } -} - -void SkOpContour::dumpSpan(int index) const { - int segmentCount = fSegments.count(); - for (int test = 0; test < segmentCount; ++test) { - const SkOpSegment& segment = fSegments[test]; - if (segment.debugID() == index) { - fSegments[test].dumpSpans(); - } - } -} - -void SkOpContour::dumpSpans() const { - int segmentCount = fSegments.count(); - SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID()); - for (int test = 0; test < segmentCount; ++test) { - SkDebugf(" [%d] ", test); - fSegments[test].dumpSpans(); - } +void SkDConic::dumpInner() const { + SkDebugf("{"); + fPts.dumpInner(); + SkDebugf("}}, %1.9gf", fWeight); } void SkDCubic::dump() const { - SkDebugf("{{"); - int index = 0; - do { - fPts[index].dump(); - SkDebugf(", "); - } while (++index < 3); - fPts[index].dump(); - SkDebugf("}}\n"); + this->dumpInner(); + SkDebugf("}},\n"); } -void SkDCubic::dumpNumber() const { +void SkDCubic::dumpID(int id) const { + this->dumpInner(); + SkDebugf("}"); + DumpID(id); +} + +static inline bool double_is_NaN(double x) { return x != x; } + +void SkDCubic::dumpInner() const { SkDebugf("{{"); int index = 0; - bool dumpedOne = false; do { - if (!(fPts[index].fX == fPts[index].fX && fPts[index].fY == fPts[index].fY)) { - continue; - } - if (dumpedOne) { + if (index != 0) { + if (double_is_NaN(fPts[index].fX) && double_is_NaN(fPts[index].fY)) { + return; + } SkDebugf(", "); } fPts[index].dump(); - dumpedOne = true; } while (++index < 3); - if (fPts[index].fX == fPts[index].fX && fPts[index].fY == fPts[index].fY) { - if (dumpedOne) { - SkDebugf(", "); - } - fPts[index].dump(); + if (double_is_NaN(fPts[index].fX) && double_is_NaN(fPts[index].fY)) { + return; } - SkDebugf("}}\n"); + SkDebugf(", "); + fPts[index].dump(); +} + +void SkDCurve::dump() const { + dumpID(-1); +} + +void SkDCurve::dumpID(int id) const { +#ifndef SK_RELEASE + switch(fVerb) { + case SkPath::kLine_Verb: + fLine.dumpID(id); + break; + case SkPath::kQuad_Verb: + fQuad.dumpID(id); + break; + case SkPath::kConic_Verb: + fConic.dumpID(id); + break; + case SkPath::kCubic_Verb: + fCubic.dumpID(id); + break; + default: + SkASSERT(0); + } +#else + fCubic.dumpID(id); +#endif } void SkDLine::dump() const { + this->dumpInner(); + SkDebugf("}},\n"); +} + +void SkDLine::dumpID(int id) const { + this->dumpInner(); + SkDebugf("}"); + DumpID(id); +} + +void SkDLine::dumpInner() const { SkDebugf("{{"); fPts[0].dump(); SkDebugf(", "); fPts[1].dump(); - SkDebugf("}}\n"); } void SkDPoint::dump() const { @@ -413,8 +165,26 @@ SkDebugf("}"); } +void SkDPoint::DumpHex(const SkPoint& pt) { + SkDebugf("{"); + DebugDumpHexFloat(pt.fX); + SkDebugf(", "); + DebugDumpHexFloat(pt.fY); + SkDebugf("}"); +} -void SkDQuad::dumpComma(const char* comma) const { +void SkDQuad::dump() const { + dumpInner(); + SkDebugf("}},\n"); +} + +void SkDQuad::dumpID(int id) const { + dumpInner(); + SkDebugf("}"); + DumpID(id); +} + +void SkDQuad::dumpInner() const { SkDebugf("{{"); int index = 0; do { @@ -422,428 +192,600 @@ SkDebugf(", "); } while (++index < 2); fPts[index].dump(); - SkDebugf("}}%s\n", comma ? comma : ""); } -void SkDQuad::dump() const { - dumpComma(""); -} - -void SkIntersectionHelper::dump() const { - SkDPoint::Dump(pts()[0]); - SkDPoint::Dump(pts()[1]); - if (verb() >= SkPath::kQuad_Verb) { - SkDPoint::Dump(pts()[2]); - } - if (verb() >= SkPath::kCubic_Verb) { - SkDPoint::Dump(pts()[3]); - } -} - -const SkTDArray<SkOpSpan>& SkOpSegment::debugSpans() const { - return fTs; -} - -void SkOpSegment::dumpAngles() const { - SkDebugf("((SkOpSegment*) 0x%p) [%d]\n", this, debugID()); - const SkOpAngle* fromAngle = NULL; - const SkOpAngle* toAngle = NULL; - for (int index = 0; index < count(); ++index) { - const SkOpAngle* fAngle = fTs[index].fFromAngle; - const SkOpAngle* tAngle = fTs[index].fToAngle; - if (fromAngle == fAngle && toAngle == tAngle) { - continue; +void SkIntersections::dump() const { + SkDebugf("used=%d of %d", fUsed, fMax); + for (int index = 0; index < fUsed; ++index) { + SkDebugf(" t=(%s%1.9g,%s%1.9g) pt=(%1.9g,%1.9g)", + fIsCoincident[0] & (1 << index) ? "*" : "", fT[0][index], + fIsCoincident[1] & (1 << index) ? "*" : "", fT[1][index], + fPt[index].fX, fPt[index].fY); + if (index < 2 && fNearlySame[index]) { + SkDebugf(" pt2=(%1.9g,%1.9g)",fPt2[index].fX, fPt2[index].fY); } - if (fAngle) { - SkDebugf(" [%d] from=%d ", index, fAngle->debugID()); - fAngle->dumpTo(this, tAngle); - } - if (tAngle) { - SkDebugf(" [%d] to=%d ", index, tAngle->debugID()); - tAngle->dumpTo(this, fAngle); - } - fromAngle = fAngle; - toAngle = tAngle; - } -} - -void SkOpSegment::dumpContour(int firstID, int lastID) const { - if (debugID() < 0) { - return; - } - const SkOpSegment* test = this - (debugID() - 1); - test += (firstID - 1); - const SkOpSegment* last = test + (lastID - firstID); - while (test <= last) { - test->dumpSpans(); - ++test; - } -} - -void SkOpSegment::dumpPts() const { - int last = SkPathOpsVerbToPoints(fVerb); - SkDebugf("((SkOpSegment*) 0x%p) [%d] {{", this, debugID()); - int index = 0; - do { - SkDPoint::Dump(fPts[index]); - SkDebugf(", "); - } while (++index < last); - SkDPoint::Dump(fPts[index]); - SkDebugf("}}\n"); -} - -void SkOpSegment::dumpDPts() const { - int count = SkPathOpsVerbToPoints(fVerb); - SkDebugf("((SkOpSegment*) 0x%p) [%d] {{", this, debugID()); - int index = 0; - do { - SkDPoint dPt = {fPts[index].fX, fPts[index].fY}; - dPt.dump(); - if (index != count) { - SkDebugf(", "); - } - } while (++index <= count); - SkDebugf("}}\n"); -} - -void SkOpSegment::dumpSpans() const { - int count = this->count(); - SkDebugf("((SkOpSegment*) 0x%p) [%d]\n", this, debugID()); - for (int index = 0; index < count; ++index) { - const SkOpSpan& span = this->span(index); - SkDebugf(" [%d] ", index); - span.dumpOne(); - } -} - -void SkPathOpsDebug::DumpCoincidence(const SkTArray<SkOpContour, true>& contours) { - int count = contours.count(); - for (int index = 0; index < count; ++index) { - contours[index].dumpCoincidences(); - } -} - -void SkPathOpsDebug::DumpCoincidence(const SkTArray<SkOpContour* , true>& contours) { - int count = contours.count(); - for (int index = 0; index < count; ++index) { - contours[index]->dumpCoincidences(); - } -} - -void SkPathOpsDebug::DumpContours(const SkTArray<SkOpContour, true>& contours) { - int count = contours.count(); - for (int index = 0; index < count; ++index) { - contours[index].dump(); - } -} - -void SkPathOpsDebug::DumpContours(const SkTArray<SkOpContour* , true>& contours) { - int count = contours.count(); - for (int index = 0; index < count; ++index) { - contours[index]->dump(); - } -} - -void SkPathOpsDebug::DumpContourAngles(const SkTArray<SkOpContour, true>& contours) { - int count = contours.count(); - for (int index = 0; index < count; ++index) { - contours[index].dumpAngles(); - } -} - -void SkPathOpsDebug::DumpContourAngles(const SkTArray<SkOpContour* , true>& contours) { - int count = contours.count(); - for (int index = 0; index < count; ++index) { - contours[index]->dumpAngles(); - } -} - -void SkPathOpsDebug::DumpContourPts(const SkTArray<SkOpContour, true>& contours) { - int count = contours.count(); - for (int index = 0; index < count; ++index) { - contours[index].dumpPts(); - } -} - -void SkPathOpsDebug::DumpContourPts(const SkTArray<SkOpContour* , true>& contours) { - int count = contours.count(); - for (int index = 0; index < count; ++index) { - contours[index]->dumpPts(); - } -} - -void SkPathOpsDebug::DumpContourPt(const SkTArray<SkOpContour, true>& contours, int segmentID) { - int count = contours.count(); - for (int index = 0; index < count; ++index) { - contours[index].dumpPt(segmentID); - } -} - -void SkPathOpsDebug::DumpContourPt(const SkTArray<SkOpContour* , true>& contours, int segmentID) { - int count = contours.count(); - for (int index = 0; index < count; ++index) { - contours[index]->dumpPt(segmentID); - } -} - -void SkPathOpsDebug::DumpContourSpans(const SkTArray<SkOpContour, true>& contours) { - int count = contours.count(); - for (int index = 0; index < count; ++index) { - contours[index].dumpSpans(); - } -} - -void SkPathOpsDebug::DumpContourSpans(const SkTArray<SkOpContour* , true>& contours) { - int count = contours.count(); - for (int index = 0; index < count; ++index) { - contours[index]->dumpSpans(); - } -} - -void SkPathOpsDebug::DumpContourSpan(const SkTArray<SkOpContour, true>& contours, int segmentID) { - int count = contours.count(); - for (int index = 0; index < count; ++index) { - contours[index].dumpSpan(segmentID); - } -} - -void SkPathOpsDebug::DumpContourSpan(const SkTArray<SkOpContour* , true>& contours, int segmentID) { - int count = contours.count(); - for (int index = 0; index < count; ++index) { - contours[index]->dumpSpan(segmentID); - } -} - -void SkPathOpsDebug::DumpSpans(const SkTDArray<SkOpSpan *>& spans) { - int count = spans.count(); - for (int index = 0; index < count; ++index) { - const SkOpSpan* span = spans[index]; - const SkOpSpan& oSpan = span->fOther->span(span->fOtherIndex); - const SkOpSegment* segment = oSpan.fOther; - SkDebugf("((SkOpSegment*) 0x%p) [%d] ", segment, segment->debugID()); - SkDebugf("spanIndex:%d ", oSpan.fOtherIndex); - span->dumpOne(); - } -} - -// this does not require that other T index is initialized or correct -const SkOpSegment* SkOpSpan::debugToSegment(ptrdiff_t* spanIndex) const { - if (!fOther) { - return NULL; - } - int oppCount = fOther->count(); - for (int index = 0; index < oppCount; ++index) { - const SkOpSpan& otherSpan = fOther->span(index); - double otherTestT = otherSpan.fT; - if (otherTestT < fOtherT) { - continue; - } - SkASSERT(otherTestT == fOtherT); - const SkOpSegment* candidate = otherSpan.fOther; - const SkOpSpan* first = candidate->debugSpans().begin(); - const SkOpSpan* last = candidate->debugSpans().end() - 1; - if (first <= this && this <= last) { - if (spanIndex) { - *spanIndex = this - first; - } - return candidate; - } - } - SkASSERT(0); - return NULL; -} - -void SkOpSpan::dumpOne() const { - SkDebugf("t="); - DebugDumpDouble(fT); - SkDebugf(" pt="); - SkDPoint::Dump(fPt); - if (fOther) { - SkDebugf(" other.fID=%d", fOther->debugID()); - SkDebugf(" [%d] otherT=", fOtherIndex); - DebugDumpDouble(fOtherT); - } else { - SkDebugf(" other.fID=? [?] otherT=?"); - } - if (fWindSum != SK_MinS32) { - SkDebugf(" windSum=%d", fWindSum); - } - if (fOppSum != SK_MinS32 && (SkPathOpsDebug::ValidWind(fOppSum) || fOppValue != 0)) { - SkDebugf(" oppSum=%d", fOppSum); - } - SkDebugf(" windValue=%d", fWindValue); - if (SkPathOpsDebug::ValidWind(fOppSum) || fOppValue != 0) { - SkDebugf(" oppValue=%d", fOppValue); - } - if (fFromAngle && fFromAngle->debugID()) { - SkDebugf(" from=%d", fFromAngle->debugID()); - } - if (fToAngle && fToAngle->debugID()) { - SkDebugf(" to=%d", fToAngle->debugID()); - } - if (fChased) { - SkDebugf(" chased"); - } - if (fCoincident) { - SkDebugf(" coincident"); - } - if (fDone) { - SkDebugf(" done"); - } - if (fLoop) { - SkDebugf(" loop"); - } - if (fMultiple) { - SkDebugf(" multiple"); - } - if (fNear) { - SkDebugf(" near"); - } - if (fSmall) { - SkDebugf(" small"); - } - if (fTiny) { - SkDebugf(" tiny"); } SkDebugf("\n"); } -void SkOpSpan::dump() const { - ptrdiff_t spanIndex; - const SkOpSegment* segment = debugToSegment(&spanIndex); - if (segment) { - SkDebugf("((SkOpSegment*) 0x%p) [%d]\n", segment, segment->debugID()); - SkDebugf(" [%d] ", spanIndex); - } else { - SkDebugf("((SkOpSegment*) ?) [?]\n"); - SkDebugf(" [?] "); +const SkOpAngle* SkPathOpsDebug::DebugAngleAngle(const SkOpAngle* angle, int id) { + return angle->debugAngle(id); +} + +SkOpContour* SkPathOpsDebug::DebugAngleContour(SkOpAngle* angle, int id) { + return angle->debugContour(id); +} + +const SkOpPtT* SkPathOpsDebug::DebugAnglePtT(const SkOpAngle* angle, int id) { + return angle->debugPtT(id); +} + +const SkOpSegment* SkPathOpsDebug::DebugAngleSegment(const SkOpAngle* angle, int id) { + return angle->debugSegment(id); +} + +const SkOpSpanBase* SkPathOpsDebug::DebugAngleSpan(const SkOpAngle* angle, int id) { + return angle->debugSpan(id); +} + +const SkOpAngle* SkPathOpsDebug::DebugContourAngle(SkOpContour* contour, int id) { + return contour->debugAngle(id); +} + +SkOpContour* SkPathOpsDebug::DebugContourContour(SkOpContour* contour, int id) { + return contour->debugContour(id); +} + +const SkOpPtT* SkPathOpsDebug::DebugContourPtT(SkOpContour* contour, int id) { + return contour->debugPtT(id); +} + +const SkOpSegment* SkPathOpsDebug::DebugContourSegment(SkOpContour* contour, int id) { + return contour->debugSegment(id); +} + +const SkOpSpanBase* SkPathOpsDebug::DebugContourSpan(SkOpContour* contour, int id) { + return contour->debugSpan(id); +} + +const SkOpAngle* SkPathOpsDebug::DebugCoincidenceAngle(SkOpCoincidence* coin, int id) { + return coin->debugAngle(id); +} + +SkOpContour* SkPathOpsDebug::DebugCoincidenceContour(SkOpCoincidence* coin, int id) { + return coin->debugContour(id); +} + +const SkOpPtT* SkPathOpsDebug::DebugCoincidencePtT(SkOpCoincidence* coin, int id) { + return coin->debugPtT(id); +} + +const SkOpSegment* SkPathOpsDebug::DebugCoincidenceSegment(SkOpCoincidence* coin, int id) { + return coin->debugSegment(id); +} + +const SkOpSpanBase* SkPathOpsDebug::DebugCoincidenceSpan(SkOpCoincidence* coin, int id) { + return coin->debugSpan(id); +} + +const SkOpAngle* SkPathOpsDebug::DebugPtTAngle(const SkOpPtT* ptT, int id) { + return ptT->debugAngle(id); +} + +SkOpContour* SkPathOpsDebug::DebugPtTContour(SkOpPtT* ptT, int id) { + return ptT->debugContour(id); +} + +const SkOpPtT* SkPathOpsDebug::DebugPtTPtT(const SkOpPtT* ptT, int id) { + return ptT->debugPtT(id); +} + +const SkOpSegment* SkPathOpsDebug::DebugPtTSegment(const SkOpPtT* ptT, int id) { + return ptT->debugSegment(id); +} + +const SkOpSpanBase* SkPathOpsDebug::DebugPtTSpan(const SkOpPtT* ptT, int id) { + return ptT->debugSpan(id); +} + +const SkOpAngle* SkPathOpsDebug::DebugSegmentAngle(const SkOpSegment* span, int id) { + return span->debugAngle(id); +} + +SkOpContour* SkPathOpsDebug::DebugSegmentContour(SkOpSegment* span, int id) { + return span->debugContour(id); +} + +const SkOpPtT* SkPathOpsDebug::DebugSegmentPtT(const SkOpSegment* span, int id) { + return span->debugPtT(id); +} + +const SkOpSegment* SkPathOpsDebug::DebugSegmentSegment(const SkOpSegment* span, int id) { + return span->debugSegment(id); +} + +const SkOpSpanBase* SkPathOpsDebug::DebugSegmentSpan(const SkOpSegment* span, int id) { + return span->debugSpan(id); +} + +const SkOpAngle* SkPathOpsDebug::DebugSpanAngle(const SkOpSpanBase* span, int id) { + return span->debugAngle(id); +} + +SkOpContour* SkPathOpsDebug::DebugSpanContour(SkOpSpanBase* span, int id) { + return span->debugContour(id); +} + +const SkOpPtT* SkPathOpsDebug::DebugSpanPtT(const SkOpSpanBase* span, int id) { + return span->debugPtT(id); +} + +const SkOpSegment* SkPathOpsDebug::DebugSpanSegment(const SkOpSpanBase* span, int id) { + return span->debugSegment(id); +} + +const SkOpSpanBase* SkPathOpsDebug::DebugSpanSpan(const SkOpSpanBase* span, int id) { + return span->debugSpan(id); +} + +#if DEBUG_COIN +void SkPathOpsDebug::DumpCoinDict() { + gCoinSumChangedDict.dump("unused coin algorithm", false); + gCoinSumVisitedDict.dump("visited coin function", true); +} + +void SkPathOpsDebug::CoinDict::dump(const char* str, bool visitCheck) const { + int count = fDict.count(); + for (int index = 0; index < count; ++index) { + const auto& entry = fDict[index]; + if (visitCheck || entry.fGlitchType == kUninitialized_Glitch) { + SkDebugf("%s %s : line %d iteration %d", str, entry.fFunctionName, + entry.fLineNumber, entry.fIteration); + DumpGlitchType(entry.fGlitchType); + SkDebugf("\n"); + } } - dumpOne(); +} +#endif + +void SkOpContour::dumpContours() const { + SkOpContour* contour = this->globalState()->contourHead(); + do { + contour->dump(); + } while ((contour = contour->next())); } -void Dump(const SkTArray<class SkOpContour, true>& contours) { - SkPathOpsDebug::DumpContours(contours); +void SkOpContour::dumpContoursAll() const { + SkOpContour* contour = this->globalState()->contourHead(); + do { + contour->dumpAll(); + } while ((contour = contour->next())); } -void Dump(const SkTArray<class SkOpContour* , true>& contours) { - SkPathOpsDebug::DumpContours(contours); +void SkOpContour::dumpContoursAngles() const { + SkOpContour* contour = this->globalState()->contourHead(); + do { + contour->dumpAngles(); + } while ((contour = contour->next())); } -void Dump(const SkTArray<class SkOpContour, true>* contours) { - SkPathOpsDebug::DumpContours(*contours); +void SkOpContour::dumpContoursPts() const { + SkOpContour* contour = this->globalState()->contourHead(); + do { + contour->dumpPts(); + } while ((contour = contour->next())); } -void Dump(const SkTArray<class SkOpContour* , true>* contours) { - SkPathOpsDebug::DumpContours(*contours); +void SkOpContour::dumpContoursPt(int segmentID) const { + SkOpContour* contour = this->globalState()->contourHead(); + do { + contour->dumpPt(segmentID); + } while ((contour = contour->next())); } -void Dump(const SkTDArray<SkOpSpan *>& chase) { - SkPathOpsDebug::DumpSpans(chase); +void SkOpContour::dumpContoursSegment(int segmentID) const { + SkOpContour* contour = this->globalState()->contourHead(); + do { + contour->dumpSegment(segmentID); + } while ((contour = contour->next())); } -void Dump(const SkTDArray<SkOpSpan *>* chase) { - SkPathOpsDebug::DumpSpans(*chase); +void SkOpContour::dumpContoursSpan(int spanID) const { + SkOpContour* contour = this->globalState()->contourHead(); + do { + contour->dumpSpan(spanID); + } while ((contour = contour->next())); } -void DumpAngles(const SkTArray<class SkOpContour, true>& contours) { - SkPathOpsDebug::DumpContourAngles(contours); +void SkOpContour::dumpContoursSpans() const { + SkOpContour* contour = this->globalState()->contourHead(); + do { + contour->dumpSpans(); + } while ((contour = contour->next())); } -void DumpAngles(const SkTArray<class SkOpContour* , true>& contours) { - SkPathOpsDebug::DumpContourAngles(contours); +template <typename TCurve, typename OppCurve> +const SkTSpan<TCurve, OppCurve>* DebugSpan(const SkTSect<TCurve, OppCurve>* sect, int id) { + return sect->debugSpan(id); } -void DumpAngles(const SkTArray<class SkOpContour, true>* contours) { - SkPathOpsDebug::DumpContourAngles(*contours); +void DontCallDebugSpan(int id); +void DontCallDebugSpan(int id) { // exists to instantiate the templates + SkDQuad quad; + SkDConic conic; + SkDCubic cubic; + SkTSect<SkDQuad, SkDQuad> q1q2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDQuad, SkDConic> q1k2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDQuad, SkDCubic> q1c2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDQuad> k1q2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDConic> k1k2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDCubic> k1c2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDQuad> c1q2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDConic> c1k2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDCubic> c1c2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + DebugSpan(&q1q2, id); + DebugSpan(&q1k2, id); + DebugSpan(&q1c2, id); + DebugSpan(&k1q2, id); + DebugSpan(&k1k2, id); + DebugSpan(&k1c2, id); + DebugSpan(&c1q2, id); + DebugSpan(&c1k2, id); + DebugSpan(&c1c2, id); } -void DumpAngles(const SkTArray<class SkOpContour* , true>* contours) { - SkPathOpsDebug::DumpContourAngles(*contours); +template <typename TCurve, typename OppCurve> +const SkTSpan<TCurve, OppCurve>* DebugT(const SkTSect<TCurve, OppCurve>* sect, double t) { + return sect->debugT(t); } -void DumpCoin(const SkTArray<class SkOpContour, true>& contours) { - SkPathOpsDebug::DumpCoincidence(contours); +void DontCallDebugT(double t); +void DontCallDebugT(double t) { // exists to instantiate the templates + SkDQuad quad; + SkDConic conic; + SkDCubic cubic; + SkTSect<SkDQuad, SkDQuad> q1q2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDQuad, SkDConic> q1k2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDQuad, SkDCubic> q1c2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDQuad> k1q2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDConic> k1k2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDCubic> k1c2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDQuad> c1q2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDConic> c1k2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDCubic> c1c2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + DebugT(&q1q2, t); + DebugT(&q1k2, t); + DebugT(&q1c2, t); + DebugT(&k1q2, t); + DebugT(&k1k2, t); + DebugT(&k1c2, t); + DebugT(&c1q2, t); + DebugT(&c1k2, t); + DebugT(&c1c2, t); } -void DumpCoin(const SkTArray<class SkOpContour* , true>& contours) { - SkPathOpsDebug::DumpCoincidence(contours); +template <typename TCurve, typename OppCurve> +void Dump(const SkTSect<TCurve, OppCurve>* sect) { + sect->dump(); } -void DumpCoin(const SkTArray<class SkOpContour, true>* contours) { - SkPathOpsDebug::DumpCoincidence(*contours); +void DontCallDumpTSect(); +void DontCallDumpTSect() { // exists to instantiate the templates + SkDQuad quad; + SkDConic conic; + SkDCubic cubic; + SkTSect<SkDQuad, SkDQuad> q1q2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDQuad, SkDConic> q1k2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDQuad, SkDCubic> q1c2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDQuad> k1q2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDConic> k1k2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDCubic> k1c2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDQuad> c1q2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDConic> c1k2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDCubic> c1c2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + Dump(&q1q2); + Dump(&q1k2); + Dump(&q1c2); + Dump(&k1q2); + Dump(&k1k2); + Dump(&k1c2); + Dump(&c1q2); + Dump(&c1k2); + Dump(&c1c2); } -void DumpCoin(const SkTArray<class SkOpContour* , true>* contours) { - SkPathOpsDebug::DumpCoincidence(*contours); +template <typename TCurve, typename OppCurve> +void DumpBoth(SkTSect<TCurve, OppCurve>* sect1, SkTSect<OppCurve, TCurve>* sect2) { + sect1->dumpBoth(sect2); } -void DumpSpans(const SkTArray<class SkOpContour, true>& contours) { - SkPathOpsDebug::DumpContourSpans(contours); +void DontCallDumpBoth(); +void DontCallDumpBoth() { // exists to instantiate the templates + SkDQuad quad; + SkDConic conic; + SkDCubic cubic; + SkTSect<SkDQuad, SkDQuad> q1q2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDQuad, SkDConic> q1k2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDQuad, SkDCubic> q1c2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDQuad> k1q2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDConic> k1k2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDCubic> k1c2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDQuad> c1q2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDConic> c1k2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDCubic> c1c2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + DumpBoth(&q1q2, &q1q2); + DumpBoth(&q1k2, &k1q2); + DumpBoth(&q1c2, &c1q2); + DumpBoth(&k1q2, &q1k2); + DumpBoth(&k1k2, &k1k2); + DumpBoth(&k1c2, &c1k2); + DumpBoth(&c1q2, &q1c2); + DumpBoth(&c1k2, &k1c2); + DumpBoth(&c1c2, &c1c2); } -void DumpSpans(const SkTArray<class SkOpContour* , true>& contours) { - SkPathOpsDebug::DumpContourSpans(contours); +template <typename TCurve, typename OppCurve> +void DumpBounded(SkTSect<TCurve, OppCurve>* sect1, int id) { + sect1->dumpBounded(id); } -void DumpSpans(const SkTArray<class SkOpContour, true>* contours) { - SkPathOpsDebug::DumpContourSpans(*contours); +void DontCallDumpBounded(); +void DontCallDumpBounded() { + SkDQuad quad; + SkDConic conic; + SkDCubic cubic; + SkTSect<SkDQuad, SkDQuad> q1q2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDQuad, SkDConic> q1k2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDQuad, SkDCubic> q1c2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDQuad> k1q2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDConic> k1k2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDCubic> k1c2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDQuad> c1q2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDConic> c1k2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDCubic> c1c2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + DumpBounded(&q1q2, 0); + DumpBounded(&q1k2, 0); + DumpBounded(&q1c2, 0); + DumpBounded(&k1q2, 0); + DumpBounded(&k1k2, 0); + DumpBounded(&k1c2, 0); + DumpBounded(&c1q2, 0); + DumpBounded(&c1k2, 0); + DumpBounded(&c1c2, 0); } -void DumpSpans(const SkTArray<class SkOpContour* , true>* contours) { - SkPathOpsDebug::DumpContourSpans(*contours); +template <typename TCurve, typename OppCurve> +void DumpBounds(SkTSect<TCurve, OppCurve>* sect1) { + sect1->dumpBounds(); } -void DumpSpan(const SkTArray<class SkOpContour, true>& contours, int segmentID) { - SkPathOpsDebug::DumpContourSpan(contours, segmentID); +void DontCallDumpBounds(); +void DontCallDumpBounds() { + SkDQuad quad; + SkDConic conic; + SkDCubic cubic; + SkTSect<SkDQuad, SkDQuad> q1q2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDQuad, SkDConic> q1k2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDQuad, SkDCubic> q1c2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDQuad> k1q2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDConic> k1k2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDCubic> k1c2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDQuad> c1q2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDConic> c1k2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDCubic> c1c2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + DumpBounds(&q1q2); + DumpBounds(&q1k2); + DumpBounds(&q1c2); + DumpBounds(&k1q2); + DumpBounds(&k1k2); + DumpBounds(&k1c2); + DumpBounds(&c1q2); + DumpBounds(&c1k2); + DumpBounds(&c1c2); } -void DumpSpan(const SkTArray<class SkOpContour* , true>& contours, int segmentID) { - SkPathOpsDebug::DumpContourSpan(contours, segmentID); +template <typename TCurve, typename OppCurve> +void DumpCoin(SkTSect<TCurve, OppCurve>* sect1) { + sect1->dumpCoin(); } -void DumpSpan(const SkTArray<class SkOpContour, true>* contours, int segmentID) { - SkPathOpsDebug::DumpContourSpan(*contours, segmentID); +void DontCallDumpCoin(); +void DontCallDumpCoin() { // exists to instantiate the templates + SkDQuad quad; + SkDConic conic; + SkDCubic cubic; + SkTSect<SkDQuad, SkDQuad> q1q2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDQuad, SkDConic> q1k2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDQuad, SkDCubic> q1c2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDQuad> k1q2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDConic> k1k2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDCubic> k1c2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDQuad> c1q2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDConic> c1k2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDCubic> c1c2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + DumpCoin(&q1q2); + DumpCoin(&q1k2); + DumpCoin(&q1c2); + DumpCoin(&k1q2); + DumpCoin(&k1k2); + DumpCoin(&k1c2); + DumpCoin(&c1q2); + DumpCoin(&c1k2); + DumpCoin(&c1c2); } -void DumpSpan(const SkTArray<class SkOpContour* , true>* contours, int segmentID) { - SkPathOpsDebug::DumpContourSpan(*contours, segmentID); +template <typename TCurve, typename OppCurve> +void DumpCoinCurves(SkTSect<TCurve, OppCurve>* sect1) { + sect1->dumpCoinCurves(); } -void DumpPts(const SkTArray<class SkOpContour, true>& contours) { - SkPathOpsDebug::DumpContourPts(contours); +void DontCallDumpCoinCurves(); +void DontCallDumpCoinCurves() { // exists to instantiate the templates + SkDQuad quad; + SkDConic conic; + SkDCubic cubic; + SkTSect<SkDQuad, SkDQuad> q1q2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDQuad, SkDConic> q1k2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDQuad, SkDCubic> q1c2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDQuad> k1q2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDConic> k1k2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDCubic> k1c2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDQuad> c1q2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDConic> c1k2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDCubic> c1c2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + DumpCoinCurves(&q1q2); + DumpCoinCurves(&q1k2); + DumpCoinCurves(&q1c2); + DumpCoinCurves(&k1q2); + DumpCoinCurves(&k1k2); + DumpCoinCurves(&k1c2); + DumpCoinCurves(&c1q2); + DumpCoinCurves(&c1k2); + DumpCoinCurves(&c1c2); } -void DumpPts(const SkTArray<class SkOpContour* , true>& contours) { - SkPathOpsDebug::DumpContourPts(contours); +template <typename TCurve, typename OppCurve> +void DumpCurves(const SkTSect<TCurve, OppCurve>* sect) { + sect->dumpCurves(); } -void DumpPts(const SkTArray<class SkOpContour, true>* contours) { - SkPathOpsDebug::DumpContourPts(*contours); +void DontCallDumpCurves(); +void DontCallDumpCurves() { // exists to instantiate the templates + SkDQuad quad; + SkDConic conic; + SkDCubic cubic; + SkTSect<SkDQuad, SkDQuad> q1q2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDQuad, SkDConic> q1k2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDQuad, SkDCubic> q1c2(quad SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDQuad> k1q2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDConic> k1k2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDConic, SkDCubic> k1c2(conic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDQuad> c1q2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDConic> c1k2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + SkTSect<SkDCubic, SkDCubic> c1c2(cubic SkDEBUGPARAMS(nullptr) PATH_OPS_DEBUG_T_SECT_PARAMS(1)); + DumpCurves(&q1q2); + DumpCurves(&q1k2); + DumpCurves(&q1c2); + DumpCurves(&k1q2); + DumpCurves(&k1k2); + DumpCurves(&k1c2); + DumpCurves(&c1q2); + DumpCurves(&c1k2); + DumpCurves(&c1c2); } -void DumpPts(const SkTArray<class SkOpContour* , true>* contours) { - SkPathOpsDebug::DumpContourPts(*contours); +template <typename TCurve, typename OppCurve> +void Dump(const SkTSpan<TCurve, OppCurve>* span) { + span->dump(); } -void DumpPt(const SkTArray<class SkOpContour, true>& contours, int segmentID) { - SkPathOpsDebug::DumpContourPt(contours, segmentID); +void DontCallDumpTSpan(); +void DontCallDumpTSpan() { // exists to instantiate the templates + SkTSpan<SkDQuad, SkDQuad> q1q2; q1q2.debugInit(); + SkTSpan<SkDQuad, SkDConic> q1k2; q1k2.debugInit(); + SkTSpan<SkDQuad, SkDCubic> q1c2; q1c2.debugInit(); + SkTSpan<SkDConic, SkDQuad> k1q2; k1q2.debugInit(); + SkTSpan<SkDConic, SkDConic> k1k2; k1k2.debugInit(); + SkTSpan<SkDConic, SkDCubic> k1c2; k1c2.debugInit(); + SkTSpan<SkDCubic, SkDQuad> c1q2; c1q2.debugInit(); + SkTSpan<SkDCubic, SkDConic> c1k2; c1k2.debugInit(); + SkTSpan<SkDCubic, SkDCubic> c1c2; c1c2.debugInit(); + Dump(&q1q2); + Dump(&q1k2); + Dump(&q1c2); + Dump(&k1q2); + Dump(&k1k2); + Dump(&k1c2); + Dump(&c1q2); + Dump(&c1k2); + Dump(&c1c2); } -void DumpPt(const SkTArray<class SkOpContour* , true>& contours, int segmentID) { - SkPathOpsDebug::DumpContourPt(contours, segmentID); +template <typename TCurve, typename OppCurve> +void DumpAll(const SkTSpan<TCurve, OppCurve>* span) { + span->dumpAll(); } -void DumpPt(const SkTArray<class SkOpContour, true>* contours, int segmentID) { - SkPathOpsDebug::DumpContourPt(*contours, segmentID); +void DontCallDumpSpanAll(); +void DontCallDumpSpanAll() { // exists to instantiate the templates + SkTSpan<SkDQuad, SkDQuad> q1q2; q1q2.debugInit(); + SkTSpan<SkDQuad, SkDConic> q1k2; q1k2.debugInit(); + SkTSpan<SkDQuad, SkDCubic> q1c2; q1c2.debugInit(); + SkTSpan<SkDConic, SkDQuad> k1q2; k1q2.debugInit(); + SkTSpan<SkDConic, SkDConic> k1k2; k1k2.debugInit(); + SkTSpan<SkDConic, SkDCubic> k1c2; k1c2.debugInit(); + SkTSpan<SkDCubic, SkDQuad> c1q2; c1q2.debugInit(); + SkTSpan<SkDCubic, SkDConic> c1k2; c1k2.debugInit(); + SkTSpan<SkDCubic, SkDCubic> c1c2; c1c2.debugInit(); + DumpAll(&q1q2); + DumpAll(&q1k2); + DumpAll(&q1c2); + DumpAll(&k1q2); + DumpAll(&k1k2); + DumpAll(&k1c2); + DumpAll(&c1q2); + DumpAll(&c1k2); + DumpAll(&c1c2); } -void DumpPt(const SkTArray<class SkOpContour* , true>* contours, int segmentID) { - SkPathOpsDebug::DumpContourPt(*contours, segmentID); +template <typename TCurve, typename OppCurve> +void DumpBounded(const SkTSpan<TCurve, OppCurve>* span) { + span->dumpBounded(0); +} + +void DontCallDumpSpanBounded(); +void DontCallDumpSpanBounded() { // exists to instantiate the templates + SkTSpan<SkDQuad, SkDQuad> q1q2; q1q2.debugInit(); + SkTSpan<SkDQuad, SkDConic> q1k2; q1k2.debugInit(); + SkTSpan<SkDQuad, SkDCubic> q1c2; q1c2.debugInit(); + SkTSpan<SkDConic, SkDQuad> k1q2; k1q2.debugInit(); + SkTSpan<SkDConic, SkDConic> k1k2; k1k2.debugInit(); + SkTSpan<SkDConic, SkDCubic> k1c2; k1c2.debugInit(); + SkTSpan<SkDCubic, SkDQuad> c1q2; c1q2.debugInit(); + SkTSpan<SkDCubic, SkDConic> c1k2; c1k2.debugInit(); + SkTSpan<SkDCubic, SkDCubic> c1c2; c1c2.debugInit(); + DumpBounded(&q1q2); + DumpBounded(&q1k2); + DumpBounded(&q1c2); + DumpBounded(&k1q2); + DumpBounded(&k1k2); + DumpBounded(&k1c2); + DumpBounded(&c1q2); + DumpBounded(&c1k2); + DumpBounded(&c1c2); +} + +template <typename TCurve, typename OppCurve> +void DumpCoin(const SkTSpan<TCurve, OppCurve>* span) { + span->dumpCoin(); +} + +void DontCallDumpSpanCoin(); +void DontCallDumpSpanCoin() { // exists to instantiate the templates + SkTSpan<SkDQuad, SkDQuad> q1q2; q1q2.debugInit(); + SkTSpan<SkDQuad, SkDConic> q1k2; q1k2.debugInit(); + SkTSpan<SkDQuad, SkDCubic> q1c2; q1c2.debugInit(); + SkTSpan<SkDConic, SkDQuad> k1q2; k1q2.debugInit(); + SkTSpan<SkDConic, SkDConic> k1k2; k1k2.debugInit(); + SkTSpan<SkDConic, SkDCubic> k1c2; k1c2.debugInit(); + SkTSpan<SkDCubic, SkDQuad> c1q2; c1q2.debugInit(); + SkTSpan<SkDCubic, SkDConic> c1k2; c1k2.debugInit(); + SkTSpan<SkDCubic, SkDCubic> c1c2; c1c2.debugInit(); + DumpCoin(&q1q2); + DumpCoin(&q1k2); + DumpCoin(&q1c2); + DumpCoin(&k1q2); + DumpCoin(&k1k2); + DumpCoin(&k1c2); + DumpCoin(&c1q2); + DumpCoin(&c1k2); + DumpCoin(&c1c2); } static void dumpTestCase(const SkDQuad& quad1, const SkDQuad& quad2, int testNo) { - SkDebugf("<div id=\"quad%d\">\n", testNo); - quad1.dumpComma(","); + SkDebugf("\n<div id=\"quad%d\">\n", testNo); + quad1.dumpInner(); + SkDebugf("}}, "); quad2.dump(); SkDebugf("</div>\n\n"); } @@ -873,3 +815,667 @@ SkDLine line = {{quad.ptAtT(t), quad[0]}}; line.dump(); } + +const SkOpAngle* SkOpAngle::debugAngle(int id) const { + return this->segment()->debugAngle(id); +} + +const SkOpCoincidence* SkOpAngle::debugCoincidence() const { + return this->segment()->debugCoincidence(); +} + +SkOpContour* SkOpAngle::debugContour(int id) const { + return this->segment()->debugContour(id); +} + +const SkOpPtT* SkOpAngle::debugPtT(int id) const { + return this->segment()->debugPtT(id); +} + +const SkOpSegment* SkOpAngle::debugSegment(int id) const { + return this->segment()->debugSegment(id); +} + +int SkOpAngle::debugSign() const { + SkASSERT(fStart->t() != fEnd->t()); + return fStart->t() < fEnd->t() ? -1 : 1; +} + +const SkOpSpanBase* SkOpAngle::debugSpan(int id) const { + return this->segment()->debugSpan(id); +} + +void SkOpAngle::dump() const { + dumpOne(true); + SkDebugf("\n"); +} + +void SkOpAngle::dumpOne(bool functionHeader) const { +// fSegment->debugValidate(); + const SkOpSegment* segment = this->segment(); + const SkOpSpan& mSpan = *fStart->starter(fEnd); + if (functionHeader) { + SkDebugf("%s ", __FUNCTION__); + } + SkDebugf("[%d", segment->debugID()); + SkDebugf("/%d", debugID()); + SkDebugf("] next="); + if (fNext) { + SkDebugf("%d", fNext->fStart->segment()->debugID()); + SkDebugf("/%d", fNext->debugID()); + } else { + SkDebugf("?"); + } + SkDebugf(" sect=%d/%d ", fSectorStart, fSectorEnd); + SkDebugf(" s=%1.9g [%d] e=%1.9g [%d]", fStart->t(), fStart->debugID(), + fEnd->t(), fEnd->debugID()); + SkDebugf(" sgn=%d windVal=%d", this->debugSign(), mSpan.windValue()); + + SkDebugf(" windSum="); + SkPathOpsDebug::WindingPrintf(mSpan.windSum()); + if (mSpan.oppValue() != 0 || mSpan.oppSum() != SK_MinS32) { + SkDebugf(" oppVal=%d", mSpan.oppValue()); + SkDebugf(" oppSum="); + SkPathOpsDebug::WindingPrintf(mSpan.oppSum()); + } + if (mSpan.done()) { + SkDebugf(" done"); + } + if (unorderable()) { + SkDebugf(" unorderable"); + } + if (segment->operand()) { + SkDebugf(" operand"); + } +} + +void SkOpAngle::dumpTo(const SkOpSegment* segment, const SkOpAngle* to) const { + const SkOpAngle* first = this; + const SkOpAngle* next = this; + const char* indent = ""; + do { + SkDebugf("%s", indent); + next->dumpOne(false); + if (segment == next->fStart->segment()) { + if (this == fNext) { + SkDebugf(" << from"); + } + if (to == fNext) { + SkDebugf(" << to"); + } + } + SkDebugf("\n"); + indent = " "; + next = next->fNext; + } while (next && next != first); +} + +void SkOpAngle::dumpCurves() const { + const SkOpAngle* first = this; + const SkOpAngle* next = this; + do { + next->fPart.fCurve.dumpID(next->segment()->debugID()); + next = next->fNext; + } while (next && next != first); +} + +void SkOpAngle::dumpLoop() const { + const SkOpAngle* first = this; + const SkOpAngle* next = this; + do { + next->dumpOne(false); + SkDebugf("\n"); + next = next->fNext; + } while (next && next != first); +} + +void SkOpAngle::dumpTest() const { + const SkOpAngle* first = this; + const SkOpAngle* next = this; + do { + SkDebugf("{ "); + SkOpSegment* segment = next->segment(); + segment->dumpPts(); + SkDebugf(", %d, %1.9g, %1.9g, {} },\n", SkPathOpsVerbToPoints(segment->verb()) + 1, + next->start()->t(), next->end()->t()); + next = next->fNext; + } while (next && next != first); +} + +bool SkOpPtT::debugMatchID(int id) const { + int limit = this->debugLoopLimit(false); + int loop = 0; + const SkOpPtT* ptT = this; + do { + if (ptT->debugID() == id) { + return true; + } + } while ((!limit || ++loop <= limit) && (ptT = ptT->next()) && ptT != this); + return false; +} + +const SkOpAngle* SkOpPtT::debugAngle(int id) const { + return this->span()->debugAngle(id); +} + +SkOpContour* SkOpPtT::debugContour(int id) const { + return this->span()->debugContour(id); +} + +const SkOpCoincidence* SkOpPtT::debugCoincidence() const { + return this->span()->debugCoincidence(); +} + +const SkOpPtT* SkOpPtT::debugPtT(int id) const { + return this->span()->debugPtT(id); +} + +const SkOpSegment* SkOpPtT::debugSegment(int id) const { + return this->span()->debugSegment(id); +} + +const SkOpSpanBase* SkOpPtT::debugSpan(int id) const { + return this->span()->debugSpan(id); +} + +void SkOpPtT::dump() const { + SkDebugf("seg=%d span=%d ptT=%d", + this->segment()->debugID(), this->span()->debugID(), this->debugID()); + this->dumpBase(); + SkDebugf("\n"); +} + +void SkOpPtT::dumpAll() const { + contour()->indentDump(); + const SkOpPtT* next = this; + int limit = debugLoopLimit(true); + int loop = 0; + do { + SkDebugf("%.*s", contour()->debugIndent(), " "); + SkDebugf("seg=%d span=%d ptT=%d", + next->segment()->debugID(), next->span()->debugID(), next->debugID()); + next->dumpBase(); + SkDebugf("\n"); + if (limit && ++loop >= limit) { + SkDebugf("*** abort loop ***\n"); + break; + } + } while ((next = next->fNext) && next != this); + contour()->outdentDump(); +} + +void SkOpPtT::dumpBase() const { + SkDebugf(" t=%1.9g pt=(%1.9g,%1.9g)%s%s%s", this->fT, this->fPt.fX, this->fPt.fY, + this->fCoincident ? " coin" : "", + this->fDuplicatePt ? " dup" : "", this->fDeleted ? " deleted" : ""); +} + +const SkOpAngle* SkOpSpanBase::debugAngle(int id) const { + return this->segment()->debugAngle(id); +} + +const SkOpCoincidence* SkOpSpanBase::debugCoincidence() const { + return this->segment()->debugCoincidence(); +} + +SkOpContour* SkOpSpanBase::debugContour(int id) const { + return this->segment()->debugContour(id); +} + +const SkOpPtT* SkOpSpanBase::debugPtT(int id) const { + return this->segment()->debugPtT(id); +} + +const SkOpSegment* SkOpSpanBase::debugSegment(int id) const { + return this->segment()->debugSegment(id); +} + +const SkOpSpanBase* SkOpSpanBase::debugSpan(int id) const { + return this->segment()->debugSpan(id); +} + +void SkOpSpanBase::dump() const { + this->dumpHead(); + this->fPtT.dump(); +} + +void SkOpSpanBase::dumpHead() const { + SkDebugf("%.*s", contour()->debugIndent(), " "); + SkDebugf("seg=%d span=%d", this->segment()->debugID(), this->debugID()); + this->dumpBase(); + SkDebugf("\n"); +} + +void SkOpSpanBase::dumpAll() const { + this->dumpHead(); + this->fPtT.dumpAll(); +} + +void SkOpSpanBase::dumpBase() const { + if (this->fAligned) { + SkDebugf(" aligned"); + } + if (this->fChased) { + SkDebugf(" chased"); + } +#ifdef SK_DEBUG + if (this->fDebugDeleted) { + SkDebugf(" deleted"); + } +#endif + if (!this->final()) { + this->upCast()->dumpSpan(); + } + const SkOpSpanBase* coin = this->coinEnd(); + if (this != coin) { + SkDebugf(" coinEnd seg/span=%d/%d", coin->segment()->debugID(), coin->debugID()); + } else if (this->final() || !this->upCast()->isCoincident()) { + const SkOpPtT* oPt = this->ptT()->next(); + SkDebugf(" seg/span=%d/%d", oPt->segment()->debugID(), oPt->span()->debugID()); + } + SkDebugf(" adds=%d", fSpanAdds); +} + +void SkOpSpanBase::dumpCoin() const { + const SkOpSpan* span = this->upCastable(); + if (!span) { + return; + } + if (!span->isCoincident()) { + return; + } + span->dumpCoin(); +} + +void SkOpSpan::dumpCoin() const { + const SkOpSpan* coincident = fCoincident; + bool ok = debugCoinLoopCheck(); + this->dump(); + int loop = 0; + do { + coincident->dump(); + if (!ok && ++loop > 10) { + SkDebugf("*** abort loop ***\n"); + break; + } + } while ((coincident = coincident->fCoincident) != this); +} + +bool SkOpSpan::dumpSpan() const { + SkOpSpan* coin = fCoincident; + if (this != coin) { + SkDebugf(" coinStart seg/span=%d/%d", coin->segment()->debugID(), coin->debugID()); + } + SkDebugf(" windVal=%d", this->windValue()); + SkDebugf(" windSum="); + SkPathOpsDebug::WindingPrintf(this->windSum()); + if (this->oppValue() != 0 || this->oppSum() != SK_MinS32) { + SkDebugf(" oppVal=%d", this->oppValue()); + SkDebugf(" oppSum="); + SkPathOpsDebug::WindingPrintf(this->oppSum()); + } + if (this->done()) { + SkDebugf(" done"); + } + return this != coin; +} + +const SkOpAngle* SkOpSegment::debugAngle(int id) const { + return this->contour()->debugAngle(id); +} + + +const SkOpCoincidence* SkOpSegment::debugCoincidence() const { + return this->contour()->debugCoincidence(); +} + +SkOpContour* SkOpSegment::debugContour(int id) const { + return this->contour()->debugContour(id); +} + +const SkOpPtT* SkOpSegment::debugPtT(int id) const { + return this->contour()->debugPtT(id); +} + +const SkOpSegment* SkOpSegment::debugSegment(int id) const { + return this->contour()->debugSegment(id); +} + +const SkOpSpanBase* SkOpSegment::debugSpan(int id) const { + return this->contour()->debugSpan(id); +} + +void SkOpSegment::dump() const { + SkDebugf("%.*s", contour()->debugIndent(), " "); + this->dumpPts(); + const SkOpSpanBase* span = &fHead; + contour()->indentDump(); + do { + SkDebugf("%.*s span=%d ", contour()->debugIndent(), " ", span->debugID()); + span->ptT()->dumpBase(); + span->dumpBase(); + SkDebugf("\n"); + } while (!span->final() && (span = span->upCast()->next())); + contour()->outdentDump(); +} + +void SkOpSegment::dumpAll() const { + SkDebugf("%.*s", contour()->debugIndent(), " "); + this->dumpPts(); + const SkOpSpanBase* span = &fHead; + contour()->indentDump(); + do { + span->dumpAll(); + } while (!span->final() && (span = span->upCast()->next())); + contour()->outdentDump(); +} + +void SkOpSegment::dumpAngles() const { + SkDebugf("seg=%d\n", debugID()); + const SkOpSpanBase* span = &fHead; + do { + const SkOpAngle* fAngle = span->fromAngle(); + const SkOpAngle* tAngle = span->final() ? nullptr : span->upCast()->toAngle(); + if (fAngle) { + SkDebugf(" span=%d from=%d ", span->debugID(), fAngle->debugID()); + fAngle->dumpTo(this, tAngle); + } + if (tAngle) { + SkDebugf(" span=%d to=%d ", span->debugID(), tAngle->debugID()); + tAngle->dumpTo(this, fAngle); + } + } while (!span->final() && (span = span->upCast()->next())); +} + +void SkOpSegment::dumpCoin() const { + const SkOpSpan* span = &fHead; + do { + span->dumpCoin(); + } while ((span = span->next()->upCastable())); +} + +void SkOpSegment::dumpPtsInner(const char* prefix) const { + int last = SkPathOpsVerbToPoints(fVerb); + SkDebugf("%s=%d {{", prefix, this->debugID()); + if (fVerb == SkPath::kConic_Verb) { + SkDebugf("{"); + } + int index = 0; + do { + SkDPoint::Dump(fPts[index]); + SkDebugf(", "); + } while (++index < last); + SkDPoint::Dump(fPts[index]); + SkDebugf("}}"); + if (fVerb == SkPath::kConic_Verb) { + SkDebugf(", %1.9gf}", fWeight); + } +} + +void SkOpSegment::dumpPts(const char* prefix) const { + dumpPtsInner(prefix); + SkDebugf("\n"); +} + +void SkCoincidentSpans::dump() const { + SkDebugf("- seg=%d span=%d ptT=%d ", fCoinPtTStart->segment()->debugID(), + fCoinPtTStart->span()->debugID(), fCoinPtTStart->debugID()); + fCoinPtTStart->dumpBase(); + SkDebugf(" span=%d ptT=%d ", fCoinPtTEnd->span()->debugID(), fCoinPtTEnd->debugID()); + fCoinPtTEnd->dumpBase(); + if (fCoinPtTStart->segment()->operand()) { + SkDebugf(" operand"); + } + if (fCoinPtTStart->segment()->isXor()) { + SkDebugf(" xor"); + } + SkDebugf("\n"); + SkDebugf("+ seg=%d span=%d ptT=%d ", fOppPtTStart->segment()->debugID(), + fOppPtTStart->span()->debugID(), fOppPtTStart->debugID()); + fOppPtTStart->dumpBase(); + SkDebugf(" span=%d ptT=%d ", fOppPtTEnd->span()->debugID(), fOppPtTEnd->debugID()); + fOppPtTEnd->dumpBase(); + if (fOppPtTStart->segment()->operand()) { + SkDebugf(" operand"); + } + if (fOppPtTStart->segment()->isXor()) { + SkDebugf(" xor"); + } + SkDebugf("\n"); +} + +void SkOpCoincidence::dump() const { + SkCoincidentSpans* span = fHead; + while (span) { + span->dump(); + span = span->next(); + } + if (!fTop || fHead == fTop) { + return; + } + SkDebugf("top:\n"); + span = fTop; + int count = 0; + while (span) { + span->dump(); + span = span->next(); + SkCoincidentSpans* check = fTop; + ++count; + for (int index = 0; index < count; ++index) { + if (span == check) { + SkDebugf("(loops to #%d)\n", index); + return; + } + check = check->next(); + } + } +} + +void SkOpContour::dump() const { + SkDebugf("contour=%d count=%d op=%d xor=%d\n", this->debugID(), fCount, fOperand, fXor); + if (!fCount) { + return; + } + const SkOpSegment* segment = &fHead; + SkDEBUGCODE(fDebugIndent = 0); + this->indentDump(); + do { + segment->dump(); + } while ((segment = segment->next())); + this->outdentDump(); +} + +void SkOpContour::dumpAll() const { + SkDebugf("contour=%d count=%d op=%d xor=%d\n", this->debugID(), fCount, fOperand, fXor); + if (!fCount) { + return; + } + const SkOpSegment* segment = &fHead; + SkDEBUGCODE(fDebugIndent = 0); + this->indentDump(); + do { + segment->dumpAll(); + } while ((segment = segment->next())); + this->outdentDump(); +} + + +void SkOpContour::dumpAngles() const { + SkDebugf("contour=%d\n", this->debugID()); + const SkOpSegment* segment = &fHead; + do { + SkDebugf(" seg=%d ", segment->debugID()); + segment->dumpAngles(); + } while ((segment = segment->next())); +} + +void SkOpContour::dumpPt(int index) const { + const SkOpSegment* segment = &fHead; + do { + if (segment->debugID() == index) { + segment->dumpPts(); + } + } while ((segment = segment->next())); +} + +void SkOpContour::dumpPts(const char* prefix) const { + SkDebugf("contour=%d\n", this->debugID()); + const SkOpSegment* segment = &fHead; + do { + SkDebugf(" %s=%d ", prefix, segment->debugID()); + segment->dumpPts(prefix); + } while ((segment = segment->next())); +} + +void SkOpContour::dumpPtsX(const char* prefix) const { + if (!this->fCount) { + SkDebugf("<empty>\n"); + return; + } + const SkOpSegment* segment = &fHead; + do { + segment->dumpPts(prefix); + } while ((segment = segment->next())); +} + +void SkOpContour::dumpSegment(int index) const { + debugSegment(index)->dump(); +} + +void SkOpContour::dumpSegments(const char* prefix, SkPathOp op) const { + bool firstOp = false; + const SkOpContour* c = this; + do { + if (!firstOp && c->operand() && op >= 0) { +#if DEBUG_ACTIVE_OP + SkDebugf("op %s\n", SkPathOpsDebug::kPathOpStr[op]); +#endif + firstOp = true; + } + c->dumpPtsX(prefix); + } while ((c = c->next())); +} + +void SkOpContour::dumpSpan(int index) const { + debugSpan(index)->dump(); +} + +void SkOpContour::dumpSpans() const { + SkDebugf("contour=%d\n", this->debugID()); + const SkOpSegment* segment = &fHead; + do { + SkDebugf(" seg=%d ", segment->debugID()); + segment->dump(); + } while ((segment = segment->next())); +} + +void SkOpCurve::dump() const { + int count = SkPathOpsVerbToPoints(SkDEBUGRELEASE(fVerb, SkPath::kCubic_Verb)); + SkDebugf("{{"); + int index; + for (index = 0; index <= count - 1; ++index) { + SkDebugf("{%1.9gf,%1.9gf}, ", fPts[index].fX, fPts[index].fY); + } + SkDebugf("{%1.9gf,%1.9gf}}}\n", fPts[index].fX, fPts[index].fY); +} + +#ifdef SK_DEBUG +const SkOpAngle* SkOpGlobalState::debugAngle(int id) const { + const SkOpContour* contour = fContourHead; + do { + const SkOpSegment* segment = contour->first(); + while (segment) { + const SkOpSpan* span = segment->head(); + do { + SkOpAngle* angle = span->fromAngle(); + if (angle && angle->debugID() == id) { + return angle; + } + angle = span->toAngle(); + if (angle && angle->debugID() == id) { + return angle; + } + } while ((span = span->next()->upCastable())); + const SkOpSpanBase* tail = segment->tail(); + SkOpAngle* angle = tail->fromAngle(); + if (angle && angle->debugID() == id) { + return angle; + } + segment = segment->next(); + } + } while ((contour = contour->next())); + return nullptr; +} + +SkOpContour* SkOpGlobalState::debugContour(int id) const { + SkOpContour* contour = fContourHead; + do { + if (contour->debugID() == id) { + return contour; + } + } while ((contour = contour->next())); + return nullptr; +} + +const SkOpPtT* SkOpGlobalState::debugPtT(int id) const { + const SkOpContour* contour = fContourHead; + do { + const SkOpSegment* segment = contour->first(); + while (segment) { + const SkOpSpan* span = segment->head(); + do { + const SkOpPtT* ptT = span->ptT(); + if (ptT->debugMatchID(id)) { + return ptT; + } + } while ((span = span->next()->upCastable())); + const SkOpSpanBase* tail = segment->tail(); + const SkOpPtT* ptT = tail->ptT(); + if (ptT->debugMatchID(id)) { + return ptT; + } + segment = segment->next(); + } + } while ((contour = contour->next())); + return nullptr; +} + +const SkOpSegment* SkOpGlobalState::debugSegment(int id) const { + const SkOpContour* contour = fContourHead; + do { + const SkOpSegment* segment = contour->first(); + while (segment) { + if (segment->debugID() == id) { + return segment; + } + segment = segment->next(); + } + } while ((contour = contour->next())); + return nullptr; +} + +const SkOpSpanBase* SkOpGlobalState::debugSpan(int id) const { + const SkOpContour* contour = fContourHead; + do { + const SkOpSegment* segment = contour->first(); + while (segment) { + const SkOpSpan* span = segment->head(); + do { + if (span->debugID() == id) { + return span; + } + } while ((span = span->next()->upCastable())); + const SkOpSpanBase* tail = segment->tail(); + if (tail->debugID() == id) { + return tail; + } + segment = segment->next(); + } + } while ((contour = contour->next())); + return nullptr; +} +#endif + +#if DEBUG_T_SECT_DUMP > 1 +int gDumpTSectNum; +#endif
diff --git a/src/third_party/skia/tests/PathOpsExtendedTest.cpp b/src/third_party/skia/tests/PathOpsExtendedTest.cpp index d808ed7..c668963 100644 --- a/src/third_party/skia/tests/PathOpsExtendedTest.cpp +++ b/src/third_party/skia/tests/PathOpsExtendedTest.cpp
@@ -9,21 +9,36 @@ #include "PathOpsThreadedCommon.h" #include "SkBitmap.h" #include "SkCanvas.h" -#include "SkForceLinking.h" #include "SkMatrix.h" +#include "SkMutex.h" #include "SkPaint.h" -#include "SkRTConf.h" +#include "SkRegion.h" #include "SkStream.h" -#include "SkTaskGroup.h" -#include "SkThread.h" + +#include <stdlib.h> #ifdef SK_BUILD_FOR_MAC #include <sys/sysctl.h> #endif -__SK_FORCE_IMAGE_DECODER_LINKING; +// std::to_string isn't implemented on android +#include <sstream> -DEFINE_bool2(runFail, f, false, "run tests known to fail."); +template <typename T> +std::string std_to_string(T value) +{ + std::ostringstream os ; + os << value ; + return os.str() ; +} + +bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result + SkDEBUGPARAMS(bool skipAssert) + SkDEBUGPARAMS(const char* testName)); + +bool SimplifyDebug(const SkPath& one, SkPath* result + SkDEBUGPARAMS(bool skipAssert) + SkDEBUGPARAMS(const char* testName)); static const char marker[] = "</div>\n" @@ -33,11 +48,11 @@ "var testDivs = [\n"; static const char* opStrs[] = { - "kDifference_PathOp", - "kIntersect_PathOp", - "kUnion_PathOp", - "kXor_PathOp", - "kReverseDifference_PathOp", + "kDifference_SkPathOp", + "kIntersect_SkPathOp", + "kUnion_SkPathOp", + "kXOR_PathOp", + "kReverseDifference_SkPathOp", }; static const char* opSuffixes[] = { @@ -45,11 +60,25 @@ "i", "u", "o", + "r", }; -static bool gShowPath = false; -static bool gComparePathsAssert = true; -static bool gPathStrAssert = true; +enum class ExpectSuccess { + kNo, + kYes, + kFlaky +}; + +enum class SkipAssert { + kNo, + kYes +}; + +enum class ExpectMatch { + kNo, + kYes, + kFlaky +}; #if DEBUG_SHOW_TEST_NAME static void showPathData(const SkPath& path) { @@ -82,6 +111,13 @@ lastPt = pts[2]; lastPtSet = true; break; + case SkPath::kConic_Verb: + SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}, //weight=%1.9g\n", + pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, + iter.conicWeight()); + lastPt = pts[2]; + lastPtSet = true; + break; case SkPath::kCubic_Verb: SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, @@ -110,19 +146,19 @@ void showOp(const SkPathOp op) { switch (op) { - case kDifference_PathOp: + case kDifference_SkPathOp: SkDebugf("op difference\n"); break; - case kIntersect_PathOp: + case kIntersect_SkPathOp: SkDebugf("op intersect\n"); break; - case kUnion_PathOp: + case kUnion_SkPathOp: SkDebugf("op union\n"); break; - case kXOR_PathOp: + case kXOR_SkPathOp: SkDebugf("op xor\n"); break; - case kReverseDifference_PathOp: + case kReverseDifference_SkPathOp: SkDebugf("op reverse difference\n"); break; default: @@ -154,7 +190,7 @@ state->fSerialNo[8] = '\0'; SkDebugf("%s\n", state->fSerialNo); if (strcmp(state->fSerialNo, state->fKey) == 0) { - SkDebugf("%s\n", state->fPathStr); + SkDebugf("%s\n", state->fPathStr.c_str()); } #endif } @@ -177,6 +213,15 @@ SkScalar vScale = (bitHeight - 2) / largerHeight; scale.reset(); scale.preScale(hScale, vScale); + larger.fLeft *= hScale; + larger.fRight *= hScale; + larger.fTop *= vScale; + larger.fBottom *= vScale; + SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft + : 16000 < larger.fRight ? 16000 - larger.fRight : 0; + SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop + : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0; + scale.postTranslate(dx, dy); } static int pathsDrawTheSame(SkBitmap& bits, const SkPath& scaledOne, const SkPath& scaledTwo, @@ -273,7 +318,7 @@ return true; } -static int comparePaths(skiatest::Reporter* reporter, const char* filename, const SkPath& one, +int comparePaths(skiatest::Reporter* reporter, const char* filename, const SkPath& one, const SkPath& two, SkBitmap& bitmap) { int errors2x2; SkPath scaledOne, scaledTwo; @@ -282,138 +327,122 @@ return 0; } const int MAX_ERRORS = 9; - REPORTER_ASSERT(reporter, errors2x2 <= MAX_ERRORS || !gComparePathsAssert); return errors2x2 > MAX_ERRORS ? errors2x2 : 0; } -const int gTestFirst = 4; -static int gTestNo = gTestFirst; static SkTDArray<SkPathOp> gTestOp; static void showPathOpPath(const char* testName, const SkPath& one, const SkPath& two, const SkPath& a, const SkPath& b, const SkPath& scaledOne, const SkPath& scaledTwo, const SkPathOp shapeOp, const SkMatrix& scale) { SkASSERT((unsigned) shapeOp < SK_ARRAY_COUNT(opStrs)); - SkString defaultTestName; if (!testName) { - defaultTestName.printf("xOp%d%s", gTestNo, opSuffixes[shapeOp]); - testName = defaultTestName.c_str(); + testName = "xOp"; } - SkDebugf("static void %s(skiatest::Reporter* reporter, const char* filename) {\n", testName); + SkDebugf("static void %s_%s(skiatest::Reporter* reporter, const char* filename) {\n", + testName, opSuffixes[shapeOp]); *gTestOp.append() = shapeOp; - ++gTestNo; SkDebugf(" SkPath path, pathB;\n"); -#if DEBUG_SHOW_TEST_NAME SkPathOpsDebug::ShowOnePath(a, "path", false); SkPathOpsDebug::ShowOnePath(b, "pathB", false); -#endif SkDebugf(" testPathOp(reporter, path, pathB, %s, filename);\n", opStrs[shapeOp]); SkDebugf("}\n"); drawAsciiPaths(scaledOne, scaledTwo, true); } -void ShowTestArray() { - for (int x = gTestFirst; x < gTestNo; ++x) { - SkDebugf(" TEST(xOp%d%s),\n", x, opSuffixes[gTestOp[x - gTestFirst]]); - } -} - SK_DECLARE_STATIC_MUTEX(compareDebugOut3); -SK_DECLARE_STATIC_MUTEX(compareDebugOut4); + static int comparePaths(skiatest::Reporter* reporter, const char* testName, const SkPath& one, const SkPath& scaledOne, const SkPath& two, const SkPath& scaledTwo, SkBitmap& bitmap, - const SkPath& a, const SkPath& b, const SkPathOp shapeOp, const SkMatrix& scale) { + const SkPath& a, const SkPath& b, const SkPathOp shapeOp, const SkMatrix& scale, + ExpectMatch expectMatch) { int errors2x2; + const int MAX_ERRORS = 8; (void) pathsDrawTheSame(bitmap, scaledOne, scaledTwo, errors2x2); - if (errors2x2 == 0) { - if (gShowPath) { - showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale); + if (ExpectMatch::kNo == expectMatch) { + if (errors2x2 < MAX_ERRORS) { + REPORTER_ASSERT(reporter, 0); } return 0; } - const int MAX_ERRORS = 8; - if (errors2x2 > MAX_ERRORS && gComparePathsAssert) { - SkAutoMutexAcquire autoM(compareDebugOut3); - SkDebugf("\n*** this test fails ***\n"); - showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale); - REPORTER_ASSERT(reporter, 0); - } else if (gShowPath || errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) { - SkAutoMutexAcquire autoM(compareDebugOut4); - showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale); + if (errors2x2 == 0) { + return 0; } - return errors2x2 > MAX_ERRORS ? errors2x2 : 0; + if (ExpectMatch::kYes == expectMatch && errors2x2 >= MAX_ERRORS) { + SkAutoMutexAcquire autoM(compareDebugOut3); + showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale); + SkDebugf("\n/*"); + REPORTER_ASSERT(reporter, 0); + SkDebugf(" */\n"); + } + return errors2x2 >= MAX_ERRORS ? errors2x2 : 0; } // Default values for when reporter->verbose() is false. static int testNumber = 55; static const char* testName = "pathOpTest"; -static void writeTestName(const char* nameSuffix, SkMemoryWStream& outFile) { - outFile.writeText(testName); - outFile.writeDecAsText(testNumber); +static void appendTestName(const char* nameSuffix, std::string& out) { + out += testName; + out += std_to_string(testNumber); ++testNumber; if (nameSuffix) { - outFile.writeText(nameSuffix); + out.append(nameSuffix); } } -static void outputToStream(const char* pathStr, const char* pathPrefix, const char* nameSuffix, - const char* testFunction, bool twoPaths, SkMemoryWStream& outFile) { +static void appendTest(const char* pathStr, const char* pathPrefix, const char* nameSuffix, + const char* testFunction, bool twoPaths, std::string& out) { #if 0 - outFile.writeText("<div id=\""); - writeTestName(nameSuffix, outFile); - outFile.writeText("\">\n"); + out.append("\n<div id=\""); + appendTestName(nameSuffix, out); + out.append("\">\n"); if (pathPrefix) { - outFile.writeText(pathPrefix); + out.append(pathPrefix); } - outFile.writeText(pathStr); - outFile.writeText("</div>\n\n"); + out.append(pathStr); + out.append("</div>\n\n"); - outFile.writeText(marker); - outFile.writeText(" "); - writeTestName(nameSuffix, outFile); - outFile.writeText(",\n\n\n"); + out.append(marker); + out.append(" "); + appendTestName(nameSuffix, out); + out.append(",\n\n\n"); #endif - outFile.writeText("static void "); - writeTestName(nameSuffix, outFile); - outFile.writeText("(skiatest::Reporter* reporter) {\n SkPath path"); + out.append("static void "); + appendTestName(nameSuffix, out); + out.append("(skiatest::Reporter* reporter) {\n SkPath path"); if (twoPaths) { - outFile.writeText(", pathB"); + out.append(", pathB"); } - outFile.writeText(";\n"); + out.append(";\n"); if (pathPrefix) { - outFile.writeText(pathPrefix); + out.append(pathPrefix); } - outFile.writeText(pathStr); - outFile.writeText(" "); - outFile.writeText(testFunction); - outFile.writeText("\n}\n\n"); + out += pathStr; + out += " "; + out += testFunction; #if 0 - outFile.writeText("static void (*firstTest)() = "); - writeTestName(nameSuffix, outFile); - outFile.writeText(";\n\n"); + out.append("static void (*firstTest)() = "); + appendTestName(nameSuffix, out); + out.append(";\n\n"); - outFile.writeText("static struct {\n"); - outFile.writeText(" void (*fun)();\n"); - outFile.writeText(" const char* str;\n"); - outFile.writeText("} tests[] = {\n"); - outFile.writeText(" TEST("); - writeTestName(nameSuffix, outFile); - outFile.writeText("),\n"); + out.append("static struct {\n"); + out.append(" void (*fun)();\n"); + out.append(" const char* str;\n"); + out.append("} tests[] = {\n"); + out.append(" TEST("); + appendTestName(nameSuffix, out); + out.append("),\n"); #endif - outFile.flush(); } SK_DECLARE_STATIC_MUTEX(simplifyDebugOut); + bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state, const char* pathStr) { SkPath::FillType fillType = useXor ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType; path.setFillType(fillType); -#if DEBUG_SHOW_TEST_NAME - if (gShowPath) { - SkPathOpsDebug::ShowOnePath(path, "path", false); - } -#endif + state.fReporter->bumpTestCount(); if (!Simplify(path, &out)) { SkDebugf("%s did not expect failure\n", __FUNCTION__); REPORTER_ASSERT(state.fReporter, 0); @@ -422,44 +451,73 @@ if (!state.fReporter->verbose()) { return true; } - int result = comparePaths(state.fReporter, NULL, path, out, *state.fBitmap); - if (result && gPathStrAssert) { + int result = comparePaths(state.fReporter, nullptr, path, out, *state.fBitmap); + if (result) { SkAutoMutexAcquire autoM(simplifyDebugOut); - char temp[8192]; - sk_bzero(temp, sizeof(temp)); - SkMemoryWStream stream(temp, sizeof(temp)); - const char* pathPrefix = NULL; - const char* nameSuffix = NULL; + std::string str; + const char* pathPrefix = nullptr; + const char* nameSuffix = nullptr; if (fillType == SkPath::kEvenOdd_FillType) { pathPrefix = " path.setFillType(SkPath::kEvenOdd_FillType);\n"; nameSuffix = "x"; } const char testFunction[] = "testSimplify(reporter, path);"; - outputToStream(pathStr, pathPrefix, nameSuffix, testFunction, false, stream); - SkDebugf(temp); + appendTest(pathStr, pathPrefix, nameSuffix, testFunction, false, str); + SkDebugf("%s", str.c_str()); REPORTER_ASSERT(state.fReporter, 0); } state.fReporter->bumpTestCount(); return result == 0; } -bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) { -#if DEBUG_SHOW_TEST_NAME +static bool inner_simplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename, + ExpectSuccess expectSuccess, SkipAssert skipAssert, ExpectMatch expectMatch) { +#if 0 && DEBUG_SHOW_TEST_NAME showPathData(path); #endif SkPath out; - if (!Simplify(path, &out)) { - SkDebugf("%s did not expect failure\n", __FUNCTION__); - REPORTER_ASSERT(reporter, 0); + if (!SimplifyDebug(path, &out SkDEBUGPARAMS(SkipAssert::kYes == skipAssert) + SkDEBUGPARAMS(testName))) { + if (ExpectSuccess::kYes == expectSuccess) { + SkDebugf("%s did not expect %s failure\n", __FUNCTION__, filename); + REPORTER_ASSERT(reporter, 0); + } return false; + } else { + if (ExpectSuccess::kNo == expectSuccess) { + SkDebugf("%s %s unexpected success\n", __FUNCTION__, filename); + REPORTER_ASSERT(reporter, 0); + } } SkBitmap bitmap; - int result = comparePaths(reporter, filename, path, out, bitmap); - if (result && gPathStrAssert) { + int errors = comparePaths(reporter, filename, path, out, bitmap); + if (ExpectMatch::kNo == expectMatch) { + if (!errors) { + SkDebugf("%s failing test %s now succeeds\n", __FUNCTION__, filename); + REPORTER_ASSERT(reporter, 0); + return false; + } + } else if (ExpectMatch::kYes == expectMatch && errors) { REPORTER_ASSERT(reporter, 0); } reporter->bumpTestCount(); - return result == 0; + return errors == 0; +} + +bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) { + return inner_simplify(reporter, path, filename, ExpectSuccess::kYes, SkipAssert::kNo, + ExpectMatch::kYes); +} + +bool testSimplifyFuzz(skiatest::Reporter* reporter, const SkPath& path, const char* filename) { + return inner_simplify(reporter, path, filename, ExpectSuccess::kFlaky, SkipAssert::kYes, + ExpectMatch::kFlaky); +} + +bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path, const char* filename, + bool checkFail) { + return inner_simplify(reporter, path, filename, checkFail ? + ExpectSuccess::kYes : ExpectSuccess::kNo, SkipAssert::kNo, ExpectMatch::kNo); } #if DEBUG_SHOW_TEST_NAME @@ -472,17 +530,26 @@ #endif static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, - const SkPathOp shapeOp, const char* testName, bool threaded) { -#if DEBUG_SHOW_TEST_NAME + const SkPathOp shapeOp, const char* testName, ExpectSuccess expectSuccess, + SkipAssert skipAssert, ExpectMatch expectMatch) { +#if 0 && DEBUG_SHOW_TEST_NAME showName(a, b, shapeOp); #endif SkPath out; - if (!Op(a, b, shapeOp, &out) ) { - SkDebugf("%s did not expect failure\n", __FUNCTION__); - REPORTER_ASSERT(reporter, 0); + if (!OpDebug(a, b, shapeOp, &out SkDEBUGPARAMS(SkipAssert::kYes == skipAssert) + SkDEBUGPARAMS(testName))) { + if (ExpectSuccess::kYes == expectSuccess) { + SkDebugf("%s %s did not expect failure\n", __FUNCTION__, testName); + REPORTER_ASSERT(reporter, 0); + } return false; + } else { + if (ExpectSuccess::kNo == expectSuccess) { + SkDebugf("%s %s unexpected success\n", __FUNCTION__, testName); + REPORTER_ASSERT(reporter, 0); + } } - if (threaded && !reporter->verbose()) { + if (!reporter->verbose()) { return true; } SkPath pathOut, scaledPathOut; @@ -510,45 +577,49 @@ scaledOut.addPath(out, scale); scaledOut.setFillType(out.getFillType()); int result = comparePaths(reporter, testName, pathOut, scaledPathOut, out, scaledOut, bitmap, - a, b, shapeOp, scale); - if (result && gPathStrAssert) { - REPORTER_ASSERT(reporter, 0); - } + a, b, shapeOp, scale, expectMatch); reporter->bumpTestCount(); return result == 0; } bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, - const SkPathOp shapeOp, const char* testName) { - return innerPathOp(reporter, a, b, shapeOp, testName, false); + const SkPathOp shapeOp, const char* testName) { + return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kYes, SkipAssert::kNo, + ExpectMatch::kYes); } -bool testPathFailOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, +bool testPathOpCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, + const SkPathOp shapeOp, const char* testName, bool checkFail) { + return innerPathOp(reporter, a, b, shapeOp, testName, checkFail ? + ExpectSuccess::kYes : ExpectSuccess::kNo, SkipAssert::kNo, ExpectMatch::kNo); +} + +bool testPathOpFuzz(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, + const SkPathOp shapeOp, const char* testName) { + return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kFlaky, SkipAssert::kYes, + ExpectMatch::kFlaky); +} + +bool testPathOpFail(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, const SkPathOp shapeOp, const char* testName) { #if DEBUG_SHOW_TEST_NAME showName(a, b, shapeOp); #endif - SkPath out; + SkPath orig; + orig.lineTo(54, 43); + SkPath out = orig; if (Op(a, b, shapeOp, &out) ) { SkDebugf("%s test is expected to fail\n", __FUNCTION__); REPORTER_ASSERT(reporter, 0); return false; } + SkASSERT(out == orig); return true; } -bool testThreadedPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, - const SkPathOp shapeOp, const char* testName) { - return innerPathOp(reporter, a, b, shapeOp, testName, true); -} - SK_DECLARE_STATIC_MUTEX(gMutex); void initializeTests(skiatest::Reporter* reporter, const char* test) { -#if 0 // doesn't work yet - SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); - SK_CONF_SET("images.png.suppressDecoderWarnings", true); -#endif if (reporter->verbose()) { SkAutoMutexAcquire lock(gMutex); testName = test; @@ -559,7 +630,7 @@ inData.setCount((int) inFile.getLength()); size_t inLen = inData.count(); inFile.read(inData.begin(), inLen); - inFile.setPath(NULL); + inFile.close(); char* insert = strstr(inData.begin(), marker); if (insert) { insert += sizeof(marker) - 1; @@ -570,28 +641,27 @@ } } -void outputProgress(char* ramStr, const char* pathStr, SkPath::FillType pathFillType) { +void PathOpsThreadState::outputProgress(const char* pathStr, SkPath::FillType pathFillType) { const char testFunction[] = "testSimplify(path);"; - const char* pathPrefix = NULL; - const char* nameSuffix = NULL; + const char* pathPrefix = nullptr; + const char* nameSuffix = nullptr; if (pathFillType == SkPath::kEvenOdd_FillType) { pathPrefix = " path.setFillType(SkPath::kEvenOdd_FillType);\n"; nameSuffix = "x"; } - SkMemoryWStream rRamStream(ramStr, PATH_STR_SIZE); - outputToStream(pathStr, pathPrefix, nameSuffix, testFunction, false, rRamStream); + appendTest(pathStr, pathPrefix, nameSuffix, testFunction, false, fPathStr); } -void outputProgress(char* ramStr, const char* pathStr, SkPathOp op) { +void PathOpsThreadState::outputProgress(const char* pathStr, SkPathOp op) { const char testFunction[] = "testOp(path);"; SkASSERT((size_t) op < SK_ARRAY_COUNT(opSuffixes)); const char* nameSuffix = opSuffixes[op]; - SkMemoryWStream rRamStream(ramStr, PATH_STR_SIZE); - outputToStream(pathStr, NULL, nameSuffix, testFunction, true, rRamStream); + appendTest(pathStr, nullptr, nameSuffix, testFunction, true, fPathStr); } void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count, void (*firstTest)(skiatest::Reporter* , const char* filename), + void (*skipTest)(skiatest::Reporter* , const char* filename), void (*stopTest)(skiatest::Reporter* , const char* filename), bool reverse) { size_t index; if (firstTest) { @@ -600,8 +670,7 @@ --index; } #if DEBUG_SHOW_TEST_NAME - SkDebugf("<div id=\"%s\">\n", tests[index].str); - SkDebugf(" %s [%s]\n", __FUNCTION__, tests[index].str); + SkDebugf("\n<div id=\"%s\">\n", tests[index].str); #endif (*tests[index].fun)(reporter, tests[index].str); if (tests[index].fun == stopTest) { @@ -610,21 +679,45 @@ } index = reverse ? count - 1 : 0; size_t last = reverse ? 0 : count - 1; + bool foundSkip = !skipTest; do { - if (tests[index].fun != firstTest) { + if (tests[index].fun == skipTest) { + foundSkip = true; + } + if (foundSkip && tests[index].fun != firstTest) { #if DEBUG_SHOW_TEST_NAME - SkDebugf("<div id=\"%s\">\n", tests[index].str); - SkDebugf(" %s [%s]\n", __FUNCTION__, tests[index].str); + SkDebugf("\n<div id=\"%s\">\n", tests[index].str); #endif - (*tests[index].fun)(reporter, tests[index].str); + (*tests[index].fun)(reporter, tests[index].str); } - if (tests[index].fun == stopTest) { - SkDebugf("lastTest\n"); - break; - } - if (index == last) { + if (tests[index].fun == stopTest || index == last) { break; } index += reverse ? -1 : 1; } while (true); +#if DEBUG_SHOW_TEST_NAME + SkDebugf( + "\n" + "</div>\n" + "\n" + "<script type=\"text/javascript\">\n" + "\n" + "var testDivs = [\n" + ); + index = reverse ? count - 1 : 0; + last = reverse ? 0 : count - 1; + foundSkip = !skipTest; + do { + if (tests[index].fun == skipTest) { + foundSkip = true; + } + if (foundSkip && tests[index].fun != firstTest) { + SkDebugf(" %s,\n", tests[index].str); + } + if (tests[index].fun == stopTest || index == last) { + break; + } + index += reverse ? -1 : 1; + } while (true); +#endif }
diff --git a/src/third_party/skia/tests/PathOpsExtendedTest.h b/src/third_party/skia/tests/PathOpsExtendedTest.h index 49ac804..9f8b0ae 100644 --- a/src/third_party/skia/tests/PathOpsExtendedTest.h +++ b/src/third_party/skia/tests/PathOpsExtendedTest.h
@@ -8,16 +8,11 @@ #define PathOpsExtendedTest_DEFINED #include "SkBitmap.h" -#include "SkCommandLineFlags.h" #include "SkPath.h" #include "SkPathOpsTypes.h" #include "SkStream.h" -#include "SkThread.h" -#include "SkThreadUtils.h" #include "Test.h" -DECLARE_bool(runFail); - struct PathOpsThreadState; struct TestDesc { @@ -26,27 +21,39 @@ }; //extern int comparePaths(const SkPath& one, const SkPath& two); -extern int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap); +extern int comparePaths(skiatest::Reporter* reporter, const char* filename, + const SkPath& one, const SkPath& two, SkBitmap& bitmap); + +inline int comparePaths(skiatest::Reporter* reporter, const char* filename, + const SkPath& one, const SkPath& two) { + SkBitmap bitmap; + return comparePaths(reporter, filename, one, two, bitmap); +} + extern bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths); extern void showOp(const SkPathOp op); extern bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, - const SkPathOp , const char* testName); -extern bool testPathFailOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, - const SkPathOp , const char* testName); -extern bool testThreadedPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, - const SkPathOp , const char* testName); + const SkPathOp , const char* testName); +extern bool testPathOpCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, + const SkPathOp , const char* testName, bool checkFail); +extern bool testPathOpFail(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, + const SkPathOp, const char* testName); +extern bool testPathOpFuzz(skiatest::Reporter* reporter, const SkPath& a, + const SkPath& b, const SkPathOp , const char* testName); extern bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state, const char* pathStr); extern bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename); +extern bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path, + const char* filename, bool checkFail); +extern bool testSimplifyFuzz(skiatest::Reporter* reporter, const SkPath& path, + const char* filename); void initializeTests(skiatest::Reporter* reporter, const char* testName); -void outputProgress(char* ramStr, const char* pathStr, SkPath::FillType ); -void outputProgress(char* ramStr, const char* pathStr, SkPathOp op); void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count, void (*firstTest)(skiatest::Reporter* , const char* filename), + void (*skipTest)(skiatest::Reporter* , const char* filename), void (*stopTest)(skiatest::Reporter* , const char* filename), bool reverse); -void ShowTestArray(); void ShowTestName(PathOpsThreadState* data, int a, int b, int c, int d); void ShowFunctionHeader(const char* name); void ShowPath(const SkPath& path, const char* pathName);
diff --git a/src/third_party/skia/tests/PathOpsFuzz763Test.cpp b/src/third_party/skia/tests/PathOpsFuzz763Test.cpp new file mode 100644 index 0000000..90d5723 --- /dev/null +++ b/src/third_party/skia/tests/PathOpsFuzz763Test.cpp
@@ -0,0 +1,2444 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "PathOpsExtendedTest.h" +#include "PathOpsTestCommon.h" + +#define TEST(name) { name, #name } + +static void fuzz763_3084(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.close(); +path.moveTo(SkBits2Float(0x421d76c6), SkBits2Float(0x414d1957)); +path.quadTo(SkBits2Float(0x4229fd05), SkBits2Float(0x413bbdcc), SkBits2Float(0x4235e9b0), SkBits2Float(0x4152e45d)); +path.quadTo(SkBits2Float(0x4241d65c), SkBits2Float(0x416a0aee), SkBits2Float(0x42462d3e), SkBits2Float(0x418e11f4)); +path.quadTo(SkBits2Float(0x424a8421), SkBits2Float(0x41a71e71), SkBits2Float(0x4244ba7d), SkBits2Float(0x41bef7c6)); +path.quadTo(SkBits2Float(0x423ef0da), SkBits2Float(0x41d6d11e), SkBits2Float(0x42326a9b), SkBits2Float(0x41df7ee4)); +path.quadTo(SkBits2Float(0x42273b3e), SkBits2Float(0x41e73f0f), SkBits2Float(0x421c865e), SkBits2Float(0x41ded7e1)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41f0534a), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); +path.quadTo(SkBits2Float(0x4209f7d0), SkBits2Float(0x41c80000), SkBits2Float(0x420f2625), SkBits2Float(0x41cbccd7)); +path.quadTo(SkBits2Float(0x420ba850), SkBits2Float(0x41c340da), SkBits2Float(0x4209b422), SkBits2Float(0x41b7f99d)); +path.quadTo(SkBits2Float(0x42055d40), SkBits2Float(0x419eed20), SkBits2Float(0x420b26e4), SkBits2Float(0x418713c8)); +path.quadTo(SkBits2Float(0x4210f088), SkBits2Float(0x415e74e2), SkBits2Float(0x421d76c6), SkBits2Float(0x414d1957)); +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42240000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_1823(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x405eb92c), SkBits2Float(0x422355aa), SkBits2Float(0x3eee625a), SkBits2Float(0x4223f3e8)); +path.quadTo(SkBits2Float(0x3f238500), SkBits2Float(0x4224bba7), SkBits2Float(0x3f4dcc60), SkBits2Float(0x4225921a)); +path.quadTo(SkBits2Float(0x4036c5c0), SkBits2Float(0x422ffa87), SkBits2Float(0x401de138), SkBits2Float(0x423d244e)); +path.quadTo(SkBits2Float(0x4004fcb0), SkBits2Float(0x424a4e17), SkBits2Float(0xbf0628a0), SkBits2Float(0x42528342)); +path.quadTo(SkBits2Float(0xc04810f8), SkBits2Float(0x425ab86c), SkBits2Float(0xc0cd56bc), SkBits2Float(0x42592a22)); +path.quadTo(SkBits2Float(0xc11b5280), SkBits2Float(0x42579bda), SkBits2Float(0xc13c272a), SkBits2Float(0x424d336e)); +path.quadTo(SkBits2Float(0xc15cfbd4), SkBits2Float(0x4242cb00), SkBits2Float(0xc156c2ae), SkBits2Float(0x4235a138)); +path.quadTo(SkBits2Float(0xc150898c), SkBits2Float(0x42287770), SkBits2Float(0xc126e7d8), SkBits2Float(0x42204246)); +path.quadTo(SkBits2Float(0xc1066ae4), SkBits2Float(0x4219da96), SkBits2Float(0xc0be6f82), SkBits2Float(0x42196502)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x42106507), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.close(); +path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x41000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ed7d86), SkBits2Float(0xc0b504f3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x41c80000), SkBits2Float(0x00000000), SkBits2Float(0x41c80000)); +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x41c80000), SkBits2Float(0x40b504f3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_378(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x41013776), SkBits2Float(0xc25007a8)); +path.quadTo(SkBits2Float(0x412f219e), SkBits2Float(0xc256a86f), SkBits2Float(0x41625842), SkBits2Float(0xc2533a60)); +path.quadTo(SkBits2Float(0x418ac776), SkBits2Float(0xc24fcc52), SkBits2Float(0x41980904), SkBits2Float(0xc24451c8)); +path.quadTo(SkBits2Float(0x41a54a92), SkBits2Float(0xc238d73e), SkBits2Float(0x419e6e72), SkBits2Float(0xc22c0994)); +path.quadTo(SkBits2Float(0x41979256), SkBits2Float(0xc21f3bea), SkBits2Float(0x41809d42), SkBits2Float(0xc2189b23)); +path.quadTo(SkBits2Float(0x4153505c), SkBits2Float(0xc211fa5c), SkBits2Float(0x412019b5), SkBits2Float(0xc215686b)); +path.quadTo(SkBits2Float(0x40d9c61e), SkBits2Float(0xc218d67a), SkBits2Float(0x40a4bfe8), SkBits2Float(0xc2245104)); +path.quadTo(SkBits2Float(0x405f7360), SkBits2Float(0xc22fcb8e), SkBits2Float(0x408b2a24), SkBits2Float(0xc23c9937)); +path.quadTo(SkBits2Float(0x40a69a9c), SkBits2Float(0xc24966e1), SkBits2Float(0x41013776), SkBits2Float(0xc25007a8)); +path.close(); +path.moveTo(SkBits2Float(0xc21aa3d0), SkBits2Float(0xc21a9d6c)); +path.quadTo(SkBits2Float(0xc21144a0), SkBits2Float(0xc223fd00), SkBits2Float(0xc2040363), SkBits2Float(0xc223fd46)); +path.quadTo(SkBits2Float(0xc1ed844d), SkBits2Float(0xc223fd8c), SkBits2Float(0xc1dac526), SkBits2Float(0xc21a9e5c)); +path.quadTo(SkBits2Float(0xc1c80600), SkBits2Float(0xc2113f2c), SkBits2Float(0xc1c80574), SkBits2Float(0xc203fdef)); +path.quadTo(SkBits2Float(0xc1c804e8), SkBits2Float(0xc1ed7964), SkBits2Float(0xc1dac348), SkBits2Float(0xc1daba3e)); +path.quadTo(SkBits2Float(0xc1ed81a8), SkBits2Float(0xc1c7fb18), SkBits2Float(0xc2040211), SkBits2Float(0xc1c7fa8c)); +path.quadTo(SkBits2Float(0xc211434e), SkBits2Float(0xc1c7fa00), SkBits2Float(0xc21aa2e0), SkBits2Float(0xc1dab860)); +path.quadTo(SkBits2Float(0xc2240274), SkBits2Float(0xc1ed76bf), SkBits2Float(0xc22402ba), SkBits2Float(0xc203fc9d)); +path.quadTo(SkBits2Float(0xc2240300), SkBits2Float(0xc2113dda), SkBits2Float(0xc21aa3d0), SkBits2Float(0xc21a9d6c)); +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.close(); +path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200)); +path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); +path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176)); +path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330)); +path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); +path.quadTo(SkBits2Float(0xc24fcaa2), SkBits2Float(0x418ace05), SkBits2Float(0xc2533929), SkBits2Float(0x41626a5d)); +path.lineTo(SkBits2Float(0xc25338d1), SkBits2Float(0x41626f8c)); +path.quadTo(SkBits2Float(0xc24fca68), SkBits2Float(0x418ad2e8), SkBits2Float(0xc2444fb0), SkBits2Float(0x419813d4)); +path.quadTo(SkBits2Float(0xc238d4f6), SkBits2Float(0x41a554c0), SkBits2Float(0xc22c0765), SkBits2Float(0x419e77ee)); +path.quadTo(SkBits2Float(0xc21f39d4), SkBits2Float(0x41979b1c), SkBits2Float(0xc218995e), SkBits2Float(0x4180a5aa)); +path.quadTo(SkBits2Float(0xc211f8e8), SkBits2Float(0x41536071), SkBits2Float(0xc2156751), SkBits2Float(0x41202a2d)); +path.quadTo(SkBits2Float(0xc2156774), SkBits2Float(0x41202819), SkBits2Float(0xc2156798), SkBits2Float(0x41202604)); +path.quadTo(SkBits2Float(0xc2156d3e), SkBits2Float(0x411fd1b7), SkBits2Float(0xc2157321), SkBits2Float(0x411f7b6e)); +path.quadTo(SkBits2Float(0xc218e910), SkBits2Float(0x40d986da), SkBits2Float(0xc2245097), SkBits2Float(0x40a4daf8)); +path.quadTo(SkBits2Float(0xc22fcb44), SkBits2Float(0x405fad48), SkBits2Float(0xc23c98dc), SkBits2Float(0x408b493c)); +path.quadTo(SkBits2Float(0xc2496673), SkBits2Float(0x40a6bbcc), SkBits2Float(0xc25006fe), SkBits2Float(0x4101489a)); +path.quadTo(SkBits2Float(0xc256a729), SkBits2Float(0x412f30b9), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); +path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec)); +path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.close(); +path.moveTo(SkBits2Float(0xc2533937), SkBits2Float(0x41626995)); +path.quadTo(SkBits2Float(0xc2533968), SkBits2Float(0x416266bf), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); +path.lineTo(SkBits2Float(0x41dac1c6), SkBits2Float(0x41dabbc0)); +path.quadTo(SkBits2Float(0x41dac044), SkBits2Float(0x41dabd41), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x421aa1af), SkBits2Float(0x421a9f8e), SkBits2Float(0x421aa2bf), SkBits2Float(0x421a9e7d)); +path.quadTo(SkBits2Float(0x42240200), SkBits2Float(0x42113efb), SkBits2Float(0x422401d1), SkBits2Float(0x4203fdbe)); +path.quadTo(SkBits2Float(0x422401a3), SkBits2Float(0x41ed7902), SkBits2Float(0x421aa220), SkBits2Float(0x41daba81)); +path.quadTo(SkBits2Float(0x4211429e), SkBits2Float(0x41c7fc00), SkBits2Float(0x42040161), SkBits2Float(0x41c7fc5d)); +path.quadTo(SkBits2Float(0x41ed8047), SkBits2Float(0x41c7fcbb), SkBits2Float(0x41dac1c6), SkBits2Float(0x41dabbc0)); +path.lineTo(SkBits2Float(0xc2533937), SkBits2Float(0x41626995)); +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0xc2444fb0), SkBits2Float(0x419813d4)); +path.quadTo(SkBits2Float(0xc24fca68), SkBits2Float(0x418ad2e8), SkBits2Float(0xc25338d1), SkBits2Float(0x41626f8c)); +path.quadTo(SkBits2Float(0xc256a73a), SkBits2Float(0x412f3944), SkBits2Float(0xc25006c4), SkBits2Float(0x41014e62)); +path.quadTo(SkBits2Float(0xc249664e), SkBits2Float(0x40a6c6fc), SkBits2Float(0xc23c98bd), SkBits2Float(0x408b53b8)); +path.quadTo(SkBits2Float(0xc22fcb2b), SkBits2Float(0x405fc0d8), SkBits2Float(0xc2245073), SkBits2Float(0x40a4e41c)); +path.quadTo(SkBits2Float(0xc218d5ba), SkBits2Float(0x40d9e7cc), SkBits2Float(0xc2156751), SkBits2Float(0x41202a2d)); +path.quadTo(SkBits2Float(0xc211f8e8), SkBits2Float(0x41536071), SkBits2Float(0xc218995e), SkBits2Float(0x4180a5aa)); +path.quadTo(SkBits2Float(0xc21f39d4), SkBits2Float(0x41979b1c), SkBits2Float(0xc22c0765), SkBits2Float(0x419e77ee)); +path.quadTo(SkBits2Float(0xc238d4f6), SkBits2Float(0x41a554c0), SkBits2Float(0xc2444fb0), SkBits2Float(0x419813d4)); +path.close(); + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_378b(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(-47.1494f, 4.35143f); +path.quadTo(-39.8075f, 18.9486f, -43.0083f, 19.8062f); +path.quadTo(-50.35f, 5.21042f, -52.0068f, 8.08022f); +path.quadTo(-53.6632f, 10.9494f, -52.8062f, 14.1494f); +path.quadTo(-53.6639f, 10.9486f, -52.007f, 8.07884f); +path.quadTo(-50.3502f, 5.20908f, -47.1494f, 4.35143f); +path.close(); + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0xc2444fb0), SkBits2Float(0x419813d4)); +path.quadTo(SkBits2Float(0xc24fca68), SkBits2Float(0x418ad2e8), SkBits2Float(0xc25338d1), SkBits2Float(0x41626f8c)); +path.quadTo(SkBits2Float(0xc256a73a), SkBits2Float(0x412f3944), SkBits2Float(0xc25006c4), SkBits2Float(0x41014e62)); +path.quadTo(SkBits2Float(0xc21f39d4), SkBits2Float(0x41979b1c), SkBits2Float(0xc22c0765), SkBits2Float(0x419e77ee)); +path.quadTo(SkBits2Float(0xc238d4f6), SkBits2Float(0x41a554c0), SkBits2Float(0xc2444fb0), SkBits2Float(0x419813d4)); +path.close(); + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_378c(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + path.moveTo(-47.1494f, 4.35143f); + path.quadTo(-46.208f, 20.6664f, -43.0072f, 19.8086f); + path.quadTo(-39.8065f, 18.9507f, -38.1498f, 16.0809f); + path.quadTo(-36.4931f, 13.211f, -37.3509f, 10.0103f); + path.quadTo(-37.351f, 10.0098f, -37.3512f, 10.0093f); + path.close(); + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); + path.moveTo(-49.0778f, 19.0097f); + path.quadTo(-38.2087f, 6.80955f, -37.3509f, 10.0103f); + path.quadTo(-36.4931f, 13.211f, -38.1498f, 16.0809f); + path.quadTo(-39.8065f, 18.9507f, -43.0072f, 19.8086f); + path.close(); + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_378d(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(-47.1494f, 4.35143f); +path.quadTo(-38.2091f, 6.80749f, -37.3514f, 10.0083f); // required +path.quadTo(-36.4938f, 13.2091f, -38.1506f, 16.0788f); // required +path.close(); + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(-49.0778f, 19.0097f); +path.quadTo(-38.2087f, 6.80955f, -37.3509f, 10.0103f); +path.quadTo(-36.4931f, 13.211f, -38.1498f, 16.0809f); +path.close(); + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_558(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x41c95d06), SkBits2Float(0xc238e312)); +path.quadTo(SkBits2Float(0x41e37302), SkBits2Float(0xc23b3f66), SkBits2Float(0x41f93bb2), SkBits2Float(0xc233b1b3)); +path.quadTo(SkBits2Float(0x42025d9e), SkBits2Float(0xc22fb50a), SkBits2Float(0x4205bcea), SkBits2Float(0xc22a30db)); +path.quadTo(SkBits2Float(0x420be531), SkBits2Float(0xc22837fe), SkBits2Float(0x421120f1), SkBits2Float(0xc2239353)); +path.quadTo(SkBits2Float(0x421b0b2d), SkBits2Float(0xc21ac757), SkBits2Float(0x421bd594), SkBits2Float(0xc20d8c25)); +path.quadTo(SkBits2Float(0x421c9ffc), SkBits2Float(0xc20050f2), SkBits2Float(0x4213d3fe), SkBits2Float(0xc1eccd6f)); +path.quadTo(SkBits2Float(0x420b0802), SkBits2Float(0xc1d8f8fa), SkBits2Float(0x41fb99a0), SkBits2Float(0xc1d7642b)); +path.quadTo(SkBits2Float(0x41e1233b), SkBits2Float(0xc1d5cf5c), SkBits2Float(0x41cd4ec5), SkBits2Float(0xc1e76755)); +path.quadTo(SkBits2Float(0x41c5ef3d), SkBits2Float(0xc1edf201), SkBits2Float(0x41c11591), SkBits2Float(0xc1f5b68f)); +path.quadTo(SkBits2Float(0x41b863c9), SkBits2Float(0xc1f896c5), SkBits2Float(0x41b04a41), SkBits2Float(0xc1fe34bf)); +path.quadTo(SkBits2Float(0x419a8190), SkBits2Float(0xc206a812), SkBits2Float(0x4195c8e8), SkBits2Float(0xc213b310)); +path.quadTo(SkBits2Float(0x41911040), SkBits2Float(0xc220be0e), SkBits2Float(0x41a02ba6), SkBits2Float(0xc22ba266)); +path.quadTo(SkBits2Float(0x41af470a), SkBits2Float(0xc23686bf), SkBits2Float(0x41c95d06), SkBits2Float(0xc238e312)); +path.close(); +path.moveTo(SkBits2Float(0xc2169738), SkBits2Float(0xc2131d1b)); +path.quadTo(SkBits2Float(0xc2096e21), SkBits2Float(0xc214b131), SkBits2Float(0xc1fe042e), SkBits2Float(0xc20c809d)); +path.quadTo(SkBits2Float(0xc1e92c1a), SkBits2Float(0xc204500a), SkBits2Float(0xc1e603ef), SkBits2Float(0xc1ee4de5)); +path.quadTo(SkBits2Float(0xc1e2dbc3), SkBits2Float(0xc1d3fbb6), SkBits2Float(0xc1f33ce9), SkBits2Float(0xc1bf23a3)); +path.quadTo(SkBits2Float(0xc201cf08), SkBits2Float(0xc1aa4b8f), SkBits2Float(0xc20ef820), SkBits2Float(0xc1a72363)); +path.quadTo(SkBits2Float(0xc21c2138), SkBits2Float(0xc1a3fb38), SkBits2Float(0xc2268d41), SkBits2Float(0xc1b45c5e)); +path.quadTo(SkBits2Float(0xc230f94b), SkBits2Float(0xc1c4bd85), SkBits2Float(0xc2328d61), SkBits2Float(0xc1df0fb4)); +path.quadTo(SkBits2Float(0xc2342177), SkBits2Float(0xc1f961e4), SkBits2Float(0xc22bf0e3), SkBits2Float(0xc2071cfb)); +path.quadTo(SkBits2Float(0xc223c050), SkBits2Float(0xc2118905), SkBits2Float(0xc2169738), SkBits2Float(0xc2131d1b)); +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0xbe8f799b), SkBits2Float(0x42240000), SkBits2Float(0xbf0db675), SkBits2Float(0x4223eed6)); +path.quadTo(SkBits2Float(0xc060c2a3), SkBits2Float(0x42233513), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); +path.lineTo(SkBits2Float(0xc0c24f68), SkBits2Float(0x4218d9ff)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x421005d8), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.close(); +path.moveTo(SkBits2Float(0x42215fd9), SkBits2Float(0xc1c64bb4)); +path.quadTo(SkBits2Float(0x422dad8e), SkBits2Float(0xc1d0284f), SkBits2Float(0x4239dd52), SkBits2Float(0xc1c5bb1d)); +path.quadTo(SkBits2Float(0x42460d14), SkBits2Float(0xc1bb4dea), SkBits2Float(0x424afb61), SkBits2Float(0xc1a2b282)); +path.quadTo(SkBits2Float(0x424fe9af), SkBits2Float(0xc18a1717), SkBits2Float(0x424ab316), SkBits2Float(0xc1636f22)); +path.quadTo(SkBits2Float(0x42457c7c), SkBits2Float(0xc132b016), SkBits2Float(0x42392ec8), SkBits2Float(0xc11ef6e3)); +path.quadTo(SkBits2Float(0x422ce113), SkBits2Float(0xc10b3dad), SkBits2Float(0x4220b150), SkBits2Float(0xc1201812)); +path.quadTo(SkBits2Float(0x4214818d), SkBits2Float(0xc134f276), SkBits2Float(0x420f9340), SkBits2Float(0xc1662949)); +path.quadTo(SkBits2Float(0x420aa4f2), SkBits2Float(0xc18bb00e), SkBits2Float(0x420fdb8c), SkBits2Float(0xc1a40f94)); +path.quadTo(SkBits2Float(0x42151225), SkBits2Float(0xc1bc6f1a), SkBits2Float(0x42215fd9), SkBits2Float(0xc1c64bb4)); +path.close(); +path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200)); +path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); +path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176)); +path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330)); +path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); +path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); +path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec)); +path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.close(); +path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0xbfe9fe20), SkBits2Float(0x42526568)); +path.quadTo(SkBits2Float(0xc08f5cf4), SkBits2Float(0x425a57e7), SkBits2Float(0xc0f853f0), SkBits2Float(0x4258763b)); +path.quadTo(SkBits2Float(0xc130a57c), SkBits2Float(0x42569490), SkBits2Float(0xc1506f76), SkBits2Float(0x424bf8e3)); +path.quadTo(SkBits2Float(0xc1703970), SkBits2Float(0x42415d36), SkBits2Float(0xc168b2c0), SkBits2Float(0x42343e56)); +path.quadTo(SkBits2Float(0xc1612c17), SkBits2Float(0x42271f76), SkBits2Float(0xc136bd61), SkBits2Float(0x421f2cf7)); +path.quadTo(SkBits2Float(0xc10c4ead), SkBits2Float(0x42173a78), SkBits2Float(0xc0afa654), SkBits2Float(0x42191c24)); +path.quadTo(SkBits2Float(0xc00d5ea8), SkBits2Float(0x421afdcf), SkBits2Float(0xbe636c00), SkBits2Float(0x4225997c)); +path.quadTo(SkBits2Float(0x3fe1e250), SkBits2Float(0x42303529), SkBits2Float(0x3fa5acf0), SkBits2Float(0x423d5409)); +path.quadTo(SkBits2Float(0x3f52ef00), SkBits2Float(0x424a72ea), SkBits2Float(0xbfe9fe20), SkBits2Float(0x42526568)); +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_378a(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x40f4c1fc), SkBits2Float(0xc25049c6)); +path.quadTo(SkBits2Float(0x41281306), SkBits2Float(0xc25702a0), SkBits2Float(0x415b6610), SkBits2Float(0xc253af82)); +path.quadTo(SkBits2Float(0x41875c90), SkBits2Float(0xc2505c66), SkBits2Float(0x4194ce44), SkBits2Float(0xc244efe4)); +path.quadTo(SkBits2Float(0x41a23ff8), SkBits2Float(0xc2398363), SkBits2Float(0x419b99bc), SkBits2Float(0xc22caea0)); +path.quadTo(SkBits2Float(0x4194f385), SkBits2Float(0xc21fd9dc), SkBits2Float(0x417c3502), SkBits2Float(0xc2192102)); +path.quadTo(SkBits2Float(0x414e82fc), SkBits2Float(0xc2126828), SkBits2Float(0x411b2fef), SkBits2Float(0xc215bb45)); +path.quadTo(SkBits2Float(0x40cfb9c4), SkBits2Float(0xc2190e62), SkBits2Float(0x4099f2f4), SkBits2Float(0xc2247ae4)); +path.quadTo(SkBits2Float(0x40485848), SkBits2Float(0xc22fe766), SkBits2Float(0x407d8a18), SkBits2Float(0xc23cbc28)); +path.quadTo(SkBits2Float(0x40995df0), SkBits2Float(0xc24990ec), SkBits2Float(0x40f4c1fc), SkBits2Float(0xc25049c6)); +path.close(); +path.moveTo(SkBits2Float(0xc20605f2), SkBits2Float(0xc22259cd)); +path.quadTo(SkBits2Float(0xc1f189ee), SkBits2Float(0xc22283df), SkBits2Float(0xc1de900b), SkBits2Float(0xc219426c)); +path.quadTo(SkBits2Float(0xc1cb9626), SkBits2Float(0xc21000fa), SkBits2Float(0xc1cb4202), SkBits2Float(0xc202c000)); +path.quadTo(SkBits2Float(0xc1caeddd), SkBits2Float(0xc1eafe0b), SkBits2Float(0xc1dd70c3), SkBits2Float(0xc1d80427)); +path.quadTo(SkBits2Float(0xc1eff3a7), SkBits2Float(0xc1c50a43), SkBits2Float(0xc2053ace), SkBits2Float(0xc1c4b61e)); +path.quadTo(SkBits2Float(0xc2127bc8), SkBits2Float(0xc1c461fa), SkBits2Float(0xc21bf8ba), SkBits2Float(0xc1d6e4df)); +path.quadTo(SkBits2Float(0xc22575ad), SkBits2Float(0xc1e967c4), SkBits2Float(0xc2259fbf), SkBits2Float(0xc201f4dc)); +path.quadTo(SkBits2Float(0xc225c9d1), SkBits2Float(0xc20f35d6), SkBits2Float(0xc21c885e), SkBits2Float(0xc218b2c8)); +path.quadTo(SkBits2Float(0xc21346ec), SkBits2Float(0xc2222fbb), SkBits2Float(0xc20605f2), SkBits2Float(0xc22259cd)); +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.close(); +path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200)); +path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); +path.quadTo(SkBits2Float(0xc2155d3d), SkBits2Float(0x4120c08f), SkBits2Float(0xc2155303), SkBits2Float(0x41215e9f)); +path.quadTo(SkBits2Float(0xc21547f6), SkBits2Float(0x4121fb98), SkBits2Float(0xc2153d2f), SkBits2Float(0x412299db)); +path.quadTo(SkBits2Float(0xc2153265), SkBits2Float(0x41233845), SkBits2Float(0xc21527fc), SkBits2Float(0x4123d684)); +path.quadTo(SkBits2Float(0xc2151cc4), SkBits2Float(0x41247361), SkBits2Float(0xc21511d9), SkBits2Float(0x41251125)); +path.quadTo(SkBits2Float(0xc211888d), SkBits2Float(0x41582a1e), SkBits2Float(0xc21810d2), SkBits2Float(0x4183262b)); +path.quadTo(SkBits2Float(0xc21e9918), SkBits2Float(0x419a3747), SkBits2Float(0xc22b5f56), SkBits2Float(0x41a149de)); +path.quadTo(SkBits2Float(0xc2382594), SkBits2Float(0x41a85c76), SkBits2Float(0xc243ae22), SkBits2Float(0x419b4beb)); +path.quadTo(SkBits2Float(0xc24f36b0), SkBits2Float(0x418e3b60), SkBits2Float(0xc252bffc), SkBits2Float(0x41695dc8)); +path.quadTo(SkBits2Float(0xc252cf70), SkBits2Float(0x41687e79), SkBits2Float(0xc252de2e), SkBits2Float(0x41679ef4)); +path.quadTo(SkBits2Float(0xc252ee09), SkBits2Float(0x4166c0c4), SkBits2Float(0xc252fd41), SkBits2Float(0x4165e14c)); +path.quadTo(SkBits2Float(0xc2530c80), SkBits2Float(0x41650165), SkBits2Float(0xc2531afd), SkBits2Float(0x416421f9)); +path.quadTo(SkBits2Float(0xc2532a97), SkBits2Float(0x416343e9), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); +path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec)); +path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.close(); +path.moveTo(SkBits2Float(0x4204d274), SkBits2Float(0x41c5cf9d)); +path.quadTo(SkBits2Float(0x42121393), SkBits2Float(0x41c59784), SkBits2Float(0x421b86b4), SkBits2Float(0x41d82e73)); +path.quadTo(SkBits2Float(0x4224f9d6), SkBits2Float(0x41eac561), SkBits2Float(0x422515e3), SkBits2Float(0x4202a3d1)); +path.quadTo(SkBits2Float(0x422531ef), SkBits2Float(0x420fe4f0), SkBits2Float(0x421be677), SkBits2Float(0x42195811)); +path.quadTo(SkBits2Float(0x421b94ff), SkBits2Float(0x4219aae4), SkBits2Float(0x421b423a), SkBits2Float(0x4219fb06)); +path.lineTo(SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41db32ee), SkBits2Float(0x41da4a98), SkBits2Float(0x41dba82d), SkBits2Float(0x41d9d952)); +path.quadTo(SkBits2Float(0x41dc1880), SkBits2Float(0x41d9631e), SkBits2Float(0x41dc8bb9), SkBits2Float(0x41d8edf9)); +path.quadTo(SkBits2Float(0x41ef22a8), SkBits2Float(0x41c607b5), SkBits2Float(0x4204d274), SkBits2Float(0x41c5cf9d)); +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0xc243ae22), SkBits2Float(0x419b4beb)); +path.quadTo(SkBits2Float(0xc24f36b0), SkBits2Float(0x418e3b60), SkBits2Float(0xc252bffc), SkBits2Float(0x41695dc8)); +path.quadTo(SkBits2Float(0xc2564948), SkBits2Float(0x413644ce), SkBits2Float(0xc24fc102), SkBits2Float(0x41082296)); +path.quadTo(SkBits2Float(0xc24938bd), SkBits2Float(0x40b400bc), SkBits2Float(0xc23c727f), SkBits2Float(0x4097b660)); +path.quadTo(SkBits2Float(0xc22fac40), SkBits2Float(0x4076d800), SkBits2Float(0xc22423b2), SkBits2Float(0x40afae2c)); +path.quadTo(SkBits2Float(0xc2189b24), SkBits2Float(0x40e3f058), SkBits2Float(0xc21511d9), SkBits2Float(0x41251125)); +path.quadTo(SkBits2Float(0xc211888d), SkBits2Float(0x41582a1e), SkBits2Float(0xc21810d2), SkBits2Float(0x4183262b)); +path.quadTo(SkBits2Float(0xc21e9918), SkBits2Float(0x419a3747), SkBits2Float(0xc22b5f56), SkBits2Float(0x41a149de)); +path.quadTo(SkBits2Float(0xc2382594), SkBits2Float(0x41a85c76), SkBits2Float(0xc243ae22), SkBits2Float(0x419b4beb)); +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + + +static void fuzz763_378a_1(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200)); +path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); +path.quadTo(SkBits2Float(0xc2155d3d), SkBits2Float(0x4120c08f), SkBits2Float(0xc2155303), SkBits2Float(0x41215e9f)); +path.quadTo(SkBits2Float(0xc21547f6), SkBits2Float(0x4121fb98), SkBits2Float(0xc2153d2f), SkBits2Float(0x412299db)); +path.quadTo(SkBits2Float(0xc2153265), SkBits2Float(0x41233845), SkBits2Float(0xc21527fc), SkBits2Float(0x4123d684)); +path.quadTo(SkBits2Float(0xc2151cc4), SkBits2Float(0x41247361), SkBits2Float(0xc21511d9), SkBits2Float(0x41251125)); +path.quadTo(SkBits2Float(0xc211888d), SkBits2Float(0x41582a1e), SkBits2Float(0xc21810d2), SkBits2Float(0x4183262b)); +path.quadTo(SkBits2Float(0xc21e9918), SkBits2Float(0x419a3747), SkBits2Float(0xc22b5f56), SkBits2Float(0x41a149de)); +path.quadTo(SkBits2Float(0xc2382594), SkBits2Float(0x41a85c76), SkBits2Float(0xc243ae22), SkBits2Float(0x419b4beb)); +path.quadTo(SkBits2Float(0xc24f36b0), SkBits2Float(0x418e3b60), SkBits2Float(0xc252bffc), SkBits2Float(0x41695dc8)); +path.quadTo(SkBits2Float(0xc252cf70), SkBits2Float(0x41687e79), SkBits2Float(0xc252de2e), SkBits2Float(0x41679ef4)); +path.quadTo(SkBits2Float(0xc252ee09), SkBits2Float(0x4166c0c4), SkBits2Float(0xc252fd41), SkBits2Float(0x4165e14c)); +path.quadTo(SkBits2Float(0xc2530c80), SkBits2Float(0x41650165), SkBits2Float(0xc2531afd), SkBits2Float(0x416421f9)); +path.quadTo(SkBits2Float(0xc2532a97), SkBits2Float(0x416343e9), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); +path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec)); +path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0xc243ae22), SkBits2Float(0x419b4beb)); +path.quadTo(SkBits2Float(0xc24f36b0), SkBits2Float(0x418e3b60), SkBits2Float(0xc252bffc), SkBits2Float(0x41695dc8)); +path.quadTo(SkBits2Float(0xc2564948), SkBits2Float(0x413644ce), SkBits2Float(0xc24fc102), SkBits2Float(0x41082296)); +path.quadTo(SkBits2Float(0xc24938bd), SkBits2Float(0x40b400bc), SkBits2Float(0xc23c727f), SkBits2Float(0x4097b660)); +path.quadTo(SkBits2Float(0xc22fac40), SkBits2Float(0x4076d800), SkBits2Float(0xc22423b2), SkBits2Float(0x40afae2c)); +path.quadTo(SkBits2Float(0xc2189b24), SkBits2Float(0x40e3f058), SkBits2Float(0xc21511d9), SkBits2Float(0x41251125)); +path.quadTo(SkBits2Float(0xc211888d), SkBits2Float(0x41582a1e), SkBits2Float(0xc21810d2), SkBits2Float(0x4183262b)); +path.quadTo(SkBits2Float(0xc21e9918), SkBits2Float(0x419a3747), SkBits2Float(0xc22b5f56), SkBits2Float(0x41a149de)); +path.quadTo(SkBits2Float(0xc2382594), SkBits2Float(0x41a85c76), SkBits2Float(0xc243ae22), SkBits2Float(0x419b4beb)); +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_8712(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x40dce520), SkBits2Float(0xc250b45c)); +path.quadTo(SkBits2Float(0x411bc0ec), SkBits2Float(0xc25796e0), SkBits2Float(0x414f4352), SkBits2Float(0xc25472d6)); +path.quadTo(SkBits2Float(0x418162dd), SkBits2Float(0xc2514ece), SkBits2Float(0x418f27e4), SkBits2Float(0xc245fb37)); +path.quadTo(SkBits2Float(0x419cecea), SkBits2Float(0xc23aa7a0), SkBits2Float(0x4196a4d8), SkBits2Float(0xc22dc706)); +path.quadTo(SkBits2Float(0x41905cc8), SkBits2Float(0xc220e66c), SkBits2Float(0x41736b34), SkBits2Float(0xc21a03e9)); +path.quadTo(SkBits2Float(0x41461cda), SkBits2Float(0xc2132166), SkBits2Float(0x41129a71), SkBits2Float(0xc216456f)); +path.quadTo(SkBits2Float(0x40be3010), SkBits2Float(0xc2196978), SkBits2Float(0x40871bf8), SkBits2Float(0xc224bd0e)); +path.quadTo(SkBits2Float(0x40200fb8), SkBits2Float(0xc23010a5), SkBits2Float(0x40525050), SkBits2Float(0xc23cf13e)); +path.quadTo(SkBits2Float(0x4082486c), SkBits2Float(0xc249d1d9), SkBits2Float(0x40dce520), SkBits2Float(0xc250b45c)); +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.close(); +path.moveTo(SkBits2Float(0x422b20ca), SkBits2Float(0xc1a252a8)); +path.quadTo(SkBits2Float(0x4237e448), SkBits2Float(0xc1a97900), SkBits2Float(0x424371e0), SkBits2Float(0xc19c7a4f)); +path.quadTo(SkBits2Float(0x424eff77), SkBits2Float(0xc18f7b9e), SkBits2Float(0x425292a1), SkBits2Float(0xc16be93c)); +path.quadTo(SkBits2Float(0x425625cd), SkBits2Float(0xc138db44), SkBits2Float(0x424fa674), SkBits2Float(0xc10aa4e6)); +path.quadTo(SkBits2Float(0x4249271c), SkBits2Float(0xc0b8dd14), SkBits2Float(0x423c639c), SkBits2Float(0xc09c43bc)); +path.quadTo(SkBits2Float(0x422fa01e), SkBits2Float(0xc07f54c8), SkBits2Float(0x42241287), SkBits2Float(0xc0b3a528)); +path.quadTo(SkBits2Float(0x421884f0), SkBits2Float(0xc0e79fee), SkBits2Float(0x4214f1c4), SkBits2Float(0xc126ddf2)); +path.quadTo(SkBits2Float(0x42115e99), SkBits2Float(0xc159ebed), SkBits2Float(0x4217ddf2), SkBits2Float(0xc1841124)); +path.quadTo(SkBits2Float(0x421e5d4a), SkBits2Float(0xc19b2c54), SkBits2Float(0x422b20ca), SkBits2Float(0xc1a252a8)); +path.close(); +path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200)); +path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); +path.quadTo(SkBits2Float(0xc2152d73), SkBits2Float(0x412389fb), SkBits2Float(0xc214fe6a), SkBits2Float(0x4126ec3a)); +path.quadTo(SkBits2Float(0xc214b621), SkBits2Float(0x412a3217), SkBits2Float(0xc21476d0), SkBits2Float(0x412d948d)); +path.quadTo(SkBits2Float(0xc210bed3), SkBits2Float(0x41607862), SkBits2Float(0xc2171cb0), SkBits2Float(0x41877c8b)); +path.quadTo(SkBits2Float(0xc21d7a8c), SkBits2Float(0x419ebce4), SkBits2Float(0xc22a3381), SkBits2Float(0x41a62cde)); +path.quadTo(SkBits2Float(0xc236ec77), SkBits2Float(0x41ad9cd6), SkBits2Float(0xc2428ca4), SkBits2Float(0x41a0e11e)); +path.quadTo(SkBits2Float(0xc24e2cd0), SkBits2Float(0x41942565), SkBits2Float(0xc251e4cc), SkBits2Float(0x417566f6)); +path.quadTo(SkBits2Float(0xc2523ea2), SkBits2Float(0x41709990), SkBits2Float(0xc252817b), SkBits2Float(0x416bd61f)); +path.quadTo(SkBits2Float(0xc252e6b7), SkBits2Float(0x41673927), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); +path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec)); +path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.close(); +path.moveTo(SkBits2Float(0x42074f3a), SkBits2Float(0x41bef2d8)); +path.quadTo(SkBits2Float(0x42148e85), SkBits2Float(0x41be0d1d), SkBits2Float(0x421e3dbf), SkBits2Float(0x41d026ae)); +path.quadTo(SkBits2Float(0x4227ecfa), SkBits2Float(0x41e24040), SkBits2Float(0x42285fd8), SkBits2Float(0x41fcbed6)); +path.quadTo(SkBits2Float(0x4228d2b5), SkBits2Float(0x420b9eb6), SkBits2Float(0x421fc5ec), SkBits2Float(0x42154df0)); +path.quadTo(SkBits2Float(0x421f5958), SkBits2Float(0x4215c221), SkBits2Float(0x421eea62), SkBits2Float(0x42163126)); +path.quadTo(SkBits2Float(0x421e81d1), SkBits2Float(0x4216a62c), SkBits2Float(0x421e13f4), SkBits2Float(0x4217191c)); +path.quadTo(SkBits2Float(0x421d36b1), SkBits2Float(0x42180097), SkBits2Float(0x421c5020), SkBits2Float(0x4218d2e4)); +path.quadTo(SkBits2Float(0x421bb44d), SkBits2Float(0x421985ae), SkBits2Float(0x421b0c17), SkBits2Float(0x421a3367)); +path.lineTo(SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41dbfdf8), SkBits2Float(0x41d97f8e), SkBits2Float(0x41dd45f8), SkBits2Float(0x41d85595)); +path.quadTo(SkBits2Float(0x41de6877), SkBits2Float(0x41d706ef), SkBits2Float(0x41dfa063), SkBits2Float(0x41d5c09b)); +path.quadTo(SkBits2Float(0x41e03b86), SkBits2Float(0x41d51e4d), SkBits2Float(0x41e0d904), SkBits2Float(0x41d48124)); +path.quadTo(SkBits2Float(0x41e16d06), SkBits2Float(0x41d3db0f), SkBits2Float(0x41e2064d), SkBits2Float(0x41d33709)); +path.quadTo(SkBits2Float(0x41f41fdf), SkBits2Float(0x41bfd894), SkBits2Float(0x42074f3a), SkBits2Float(0x41bef2d8)); +path.close(); + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0xc2428ca4), SkBits2Float(0x41a0e11e)); +path.quadTo(SkBits2Float(0xc24e2cd0), SkBits2Float(0x41942565), SkBits2Float(0xc251e4cc), SkBits2Float(0x417566f6)); +path.quadTo(SkBits2Float(0xc2559cca), SkBits2Float(0x4142831e), SkBits2Float(0xc24f3eed), SkBits2Float(0x4114026c)); +path.quadTo(SkBits2Float(0xc248e111), SkBits2Float(0x40cb0370), SkBits2Float(0xc23c281b), SkBits2Float(0x40ad4390)); +path.quadTo(SkBits2Float(0xc22f6f26), SkBits2Float(0x408f83a8), SkBits2Float(0xc223cefa), SkBits2Float(0x40c2728a)); +path.quadTo(SkBits2Float(0xc2182ecc), SkBits2Float(0x40f5616e), SkBits2Float(0xc21476d0), SkBits2Float(0x412d948d)); +path.quadTo(SkBits2Float(0xc210bed3), SkBits2Float(0x41607862), SkBits2Float(0xc2171cb0), SkBits2Float(0x41877c8b)); +path.quadTo(SkBits2Float(0xc21d7a8c), SkBits2Float(0x419ebce4), SkBits2Float(0xc22a3381), SkBits2Float(0x41a62cde)); +path.quadTo(SkBits2Float(0xc236ec77), SkBits2Float(0x41ad9cd6), SkBits2Float(0xc2428ca4), SkBits2Float(0x41a0e11e)); +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_8712a(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200)); +path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); +path.quadTo(SkBits2Float(0xc2152d73), SkBits2Float(0x412389fb), SkBits2Float(0xc214fe6a), SkBits2Float(0x4126ec3a)); +path.quadTo(SkBits2Float(0xc214b621), SkBits2Float(0x412a3217), SkBits2Float(0xc21476d0), SkBits2Float(0x412d948d)); +path.quadTo(SkBits2Float(0xc210bed3), SkBits2Float(0x41607862), SkBits2Float(0xc2171cb0), SkBits2Float(0x41877c8b)); +path.quadTo(SkBits2Float(0xc21d7a8c), SkBits2Float(0x419ebce4), SkBits2Float(0xc22a3381), SkBits2Float(0x41a62cde)); +path.quadTo(SkBits2Float(0xc236ec77), SkBits2Float(0x41ad9cd6), SkBits2Float(0xc2428ca4), SkBits2Float(0x41a0e11e)); +path.quadTo(SkBits2Float(0xc24e2cd0), SkBits2Float(0x41942565), SkBits2Float(0xc251e4cc), SkBits2Float(0x417566f6)); +path.quadTo(SkBits2Float(0xc2523ea2), SkBits2Float(0x41709990), SkBits2Float(0xc252817b), SkBits2Float(0x416bd61f)); +path.quadTo(SkBits2Float(0xc252e6b7), SkBits2Float(0x41673927), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); +path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec)); +path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.close(); + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0xc2428ca4), SkBits2Float(0x41a0e11e)); +path.quadTo(SkBits2Float(0xc24e2cd0), SkBits2Float(0x41942565), SkBits2Float(0xc251e4cc), SkBits2Float(0x417566f6)); +path.quadTo(SkBits2Float(0xc2559cca), SkBits2Float(0x4142831e), SkBits2Float(0xc24f3eed), SkBits2Float(0x4114026c)); +path.quadTo(SkBits2Float(0xc248e111), SkBits2Float(0x40cb0370), SkBits2Float(0xc23c281b), SkBits2Float(0x40ad4390)); +path.quadTo(SkBits2Float(0xc22f6f26), SkBits2Float(0x408f83a8), SkBits2Float(0xc223cefa), SkBits2Float(0x40c2728a)); +path.quadTo(SkBits2Float(0xc2182ecc), SkBits2Float(0x40f5616e), SkBits2Float(0xc21476d0), SkBits2Float(0x412d948d)); +path.quadTo(SkBits2Float(0xc210bed3), SkBits2Float(0x41607862), SkBits2Float(0xc2171cb0), SkBits2Float(0x41877c8b)); +path.quadTo(SkBits2Float(0xc21d7a8c), SkBits2Float(0x419ebce4), SkBits2Float(0xc22a3381), SkBits2Float(0x41a62cde)); +path.quadTo(SkBits2Float(0xc236ec77), SkBits2Float(0x41ad9cd6), SkBits2Float(0xc2428ca4), SkBits2Float(0x41a0e11e)); +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_4014(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x4126977e), SkBits2Float(0xc24e5cc8)); +path.quadTo(SkBits2Float(0x4155a79e), SkBits2Float(0xc2547762), SkBits2Float(0x41841952), SkBits2Float(0xc250767b)); +path.quadTo(SkBits2Float(0x419d5ed4), SkBits2Float(0xc24c7594), SkBits2Float(0x41a99408), SkBits2Float(0xc240b18c)); +path.quadTo(SkBits2Float(0x41b5c93d), SkBits2Float(0xc234ed84), SkBits2Float(0x41adc770), SkBits2Float(0xc2284ac3)); +path.quadTo(SkBits2Float(0x41a5c5a2), SkBits2Float(0xc21ba802), SkBits2Float(0x418e3d92), SkBits2Float(0xc2158d68)); +path.quadTo(SkBits2Float(0x416d6b02), SkBits2Float(0xc20f72ce), SkBits2Float(0x413adfff), SkBits2Float(0xc21373b4)); +path.quadTo(SkBits2Float(0x410854fa), SkBits2Float(0xc217749a), SkBits2Float(0x40dfd522), SkBits2Float(0xc22338a3)); +path.quadTo(SkBits2Float(0x40af0050), SkBits2Float(0xc22efcab), SkBits2Float(0x40cf0788), SkBits2Float(0xc23b9f6c)); +path.quadTo(SkBits2Float(0x40ef0eb8), SkBits2Float(0xc248422e), SkBits2Float(0x4126977e), SkBits2Float(0xc24e5cc8)); +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.close(); +path.moveTo(SkBits2Float(0x422dc5fa), SkBits2Float(0xc196a9b4)); +path.quadTo(SkBits2Float(0x423aa688), SkBits2Float(0xc19cf222), SkBits2Float(0x4245fa38), SkBits2Float(0xc18f2d6c)); +path.quadTo(SkBits2Float(0x42514de7), SkBits2Float(0xc18168b7), SkBits2Float(0x4254721e), SkBits2Float(0xc14f4f32)); +path.quadTo(SkBits2Float(0x42579654), SkBits2Float(0xc11bccf8), SkBits2Float(0x4250b3fa), SkBits2Float(0xc0dcfc74)); +path.quadTo(SkBits2Float(0x4249d19f), SkBits2Float(0xc0825efc), SkBits2Float(0x423cf110), SkBits2Float(0xc0527a88)); +path.quadTo(SkBits2Float(0x42301082), SkBits2Float(0xc0203718), SkBits2Float(0x4224bcd2), SkBits2Float(0xc0872e60)); +path.quadTo(SkBits2Float(0x42196923), SkBits2Float(0xc0be4136), SkBits2Float(0x421644ec), SkBits2Float(0xc112a2d8)); +path.quadTo(SkBits2Float(0x421320b5), SkBits2Float(0xc1462514), SkBits2Float(0x421a0310), SkBits2Float(0xc17373d0)); +path.quadTo(SkBits2Float(0x4220e56a), SkBits2Float(0xc1906147), SkBits2Float(0x422dc5fa), SkBits2Float(0xc196a9b4)); +path.close(); +path.moveTo(SkBits2Float(0xc23d30a0), SkBits2Float(0x400e5c28)); +path.quadTo(SkBits2Float(0xc2303ecc), SkBits2Float(0x3fc17f10), SkBits2Float(0xc2251387), SkBits2Float(0x4052f2a8)); +path.quadTo(SkBits2Float(0xc219e842), SkBits2Float(0x40a292e2), SkBits2Float(0xc2170e78), SkBits2Float(0x410510c3)); +path.quadTo(SkBits2Float(0xc216ac8b), SkBits2Float(0x410c0373), SkBits2Float(0xc21678a8), SkBits2Float(0x4112d552)); +path.quadTo(SkBits2Float(0xc215ddb7), SkBits2Float(0x411942a8), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); +path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176)); +path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330)); +path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); +path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); +path.quadTo(SkBits2Float(0xc253e12c), SkBits2Float(0x41589e3d), SkBits2Float(0xc2542b01), SkBits2Float(0x414f09d7)); +path.quadTo(SkBits2Float(0xc25503cd), SkBits2Float(0x414600f6), SkBits2Float(0xc2558f0e), SkBits2Float(0x413c1fa8)); +path.quadTo(SkBits2Float(0xc25868d8), SkBits2Float(0x41085856), SkBits2Float(0xc25145a6), SkBits2Float(0x40b75684)); +path.quadTo(SkBits2Float(0xc24a2274), SkBits2Float(0x403bf8b8), SkBits2Float(0xc23d30a0), SkBits2Float(0x400e5c28)); +path.close(); +path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x4218d6c4), SkBits2Float(0x421c6a78), SkBits2Float(0x4216e8ba), SkBits2Float(0x421ddcf4)); +path.quadTo(SkBits2Float(0x42156061), SkBits2Float(0x421fb9cf), SkBits2Float(0x42138263), SkBits2Float(0x42216e93)); +path.quadTo(SkBits2Float(0x42129692), SkBits2Float(0x4222460e), SkBits2Float(0x4211a2ed), SkBits2Float(0x422307e2)); +path.quadTo(SkBits2Float(0x4210c6f1), SkBits2Float(0x4223e438), SkBits2Float(0x420fd652), SkBits2Float(0x4224b658)); +path.quadTo(SkBits2Float(0x4205da74), SkBits2Float(0x422d6e4b), SkBits2Float(0x41f141e8), SkBits2Float(0x422c893f)); +path.quadTo(SkBits2Float(0x41d6cee9), SkBits2Float(0x422ba432), SkBits2Float(0x41c55f04), SkBits2Float(0x4221a853)); +path.quadTo(SkBits2Float(0x41b3ef1f), SkBits2Float(0x4217ac75), SkBits2Float(0x41b5b938), SkBits2Float(0x420a72f5)); +path.quadTo(SkBits2Float(0x41b78350), SkBits2Float(0x41fa72eb), SkBits2Float(0x41cb7b0e), SkBits2Float(0x41e90306)); +path.quadTo(SkBits2Float(0x41ccce3f), SkBits2Float(0x41e7dad2), SkBits2Float(0x41ce28c1), SkBits2Float(0x41e6c848)); +path.quadTo(SkBits2Float(0x41cf607c), SkBits2Float(0x41e58ed0), SkBits2Float(0x41d0aced), SkBits2Float(0x41e45f0b)); +path.quadTo(SkBits2Float(0x41d34d34), SkBits2Float(0x41e1f8bf), SkBits2Float(0x41d60d52), SkBits2Float(0x41dfea66)); +path.quadTo(SkBits2Float(0x41d83ad5), SkBits2Float(0x41dd42b1), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.close(); + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0xc2478a00), SkBits2Float(0x418639e0)); +path.quadTo(SkBits2Float(0xc252b546), SkBits2Float(0x416fe6f8), SkBits2Float(0xc2558f0e), SkBits2Float(0x413c1fa8)); +path.quadTo(SkBits2Float(0xc25868d8), SkBits2Float(0x41085856), SkBits2Float(0xc25145a6), SkBits2Float(0x40b75684)); +path.quadTo(SkBits2Float(0xc24a2274), SkBits2Float(0x403bf8b8), SkBits2Float(0xc23d30a0), SkBits2Float(0x400e5c28)); +path.quadTo(SkBits2Float(0xc2303ecc), SkBits2Float(0x3fc17f10), SkBits2Float(0xc2251387), SkBits2Float(0x4052f2a8)); +path.quadTo(SkBits2Float(0xc219e842), SkBits2Float(0x40a292e2), SkBits2Float(0xc2170e78), SkBits2Float(0x410510c3)); +path.quadTo(SkBits2Float(0xc21434af), SkBits2Float(0x4138d815), SkBits2Float(0xc21b57e0), SkBits2Float(0x41658529)); +path.quadTo(SkBits2Float(0xc2227b12), SkBits2Float(0x4189191e), SkBits2Float(0xc22f6ce6), SkBits2Float(0x418eccb0)); +path.quadTo(SkBits2Float(0xc23c5ebc), SkBits2Float(0x41948044), SkBits2Float(0xc2478a00), SkBits2Float(0x418639e0)); +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_4014a(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0xc23d30a0), SkBits2Float(0x400e5c28)); // -47.2975f, 2.22437f +path.quadTo(SkBits2Float(0xc2303ecc), SkBits2Float(0x3fc17f10), SkBits2Float(0xc2251387), SkBits2Float(0x4052f2a8)); // -44.0613f, 1.51169f, -41.2691f, 3.29606f +path.quadTo(SkBits2Float(0xc219e842), SkBits2Float(0x40a292e2), SkBits2Float(0xc2170e78), SkBits2Float(0x410510c3)); // -38.4768f, 5.08043f, -37.7641f, 8.31659f +path.quadTo(SkBits2Float(0xc216ac8b), SkBits2Float(0x410c0373), SkBits2Float(0xc21678a8), SkBits2Float(0x4112d552)); // -37.6685f, 8.75084f, -37.6178f, 9.17708f +path.quadTo(SkBits2Float(0xc215ddb7), SkBits2Float(0x411942a8), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); // -37.4665f, 9.57877f, -37.3514f, 10.0083f +path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176)); // -36.4938f, 13.2091f, -38.1506f, 16.0788f +path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330)); // -39.8075f, 18.9486f, -43.0083f, 19.8062f +path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); // -46.2091f, 20.6639f, -49.0788f, 19.007f +path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); // -51.9486f, 17.3502f, -52.8062f, 14.1494f +path.quadTo(SkBits2Float(0xc253e12c), SkBits2Float(0x41589e3d), SkBits2Float(0xc2542b01), SkBits2Float(0x414f09d7)); // -52.9699f, 13.5386f, -53.042f, 12.9399f +path.quadTo(SkBits2Float(0xc25503cd), SkBits2Float(0x414600f6), SkBits2Float(0xc2558f0e), SkBits2Float(0x413c1fa8)); // -53.2537f, 12.3752f, -53.3897f, 11.7577f +path.quadTo(SkBits2Float(0xc25868d8), SkBits2Float(0x41085856), SkBits2Float(0xc25145a6), SkBits2Float(0x40b75684)); // -54.1024f, 8.52157f, -52.318f, 5.72931f +path.quadTo(SkBits2Float(0xc24a2274), SkBits2Float(0x403bf8b8), SkBits2Float(0xc23d30a0), SkBits2Float(0x400e5c28)); // -50.5336f, 2.93706f, -47.2975f, 2.22437f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0xc2478a00), SkBits2Float(0x418639e0)); // -49.8848f, 16.7783f +path.quadTo(SkBits2Float(0xc252b546), SkBits2Float(0x416fe6f8), SkBits2Float(0xc2558f0e), SkBits2Float(0x413c1fa8)); // -52.677f, 14.9939f, -53.3897f, 11.7577f +path.quadTo(SkBits2Float(0xc25868d8), SkBits2Float(0x41085856), SkBits2Float(0xc25145a6), SkBits2Float(0x40b75684)); // -54.1024f, 8.52157f, -52.318f, 5.72931f +path.quadTo(SkBits2Float(0xc24a2274), SkBits2Float(0x403bf8b8), SkBits2Float(0xc23d30a0), SkBits2Float(0x400e5c28)); // -50.5336f, 2.93706f, -47.2975f, 2.22437f +path.quadTo(SkBits2Float(0xc2303ecc), SkBits2Float(0x3fc17f10), SkBits2Float(0xc2251387), SkBits2Float(0x4052f2a8)); // -44.0613f, 1.51169f, -41.2691f, 3.29606f +path.quadTo(SkBits2Float(0xc219e842), SkBits2Float(0x40a292e2), SkBits2Float(0xc2170e78), SkBits2Float(0x410510c3)); // -38.4768f, 5.08043f, -37.7641f, 8.31659f +path.quadTo(SkBits2Float(0xc21434af), SkBits2Float(0x4138d815), SkBits2Float(0xc21b57e0), SkBits2Float(0x41658529)); // -37.0514f, 11.5528f, -38.8358f, 14.345f +path.quadTo(SkBits2Float(0xc2227b12), SkBits2Float(0x4189191e), SkBits2Float(0xc22f6ce6), SkBits2Float(0x418eccb0)); // -40.6202f, 17.1373f, -43.8563f, 17.8499f +path.quadTo(SkBits2Float(0xc23c5ebc), SkBits2Float(0x41948044), SkBits2Float(0xc2478a00), SkBits2Float(0x418639e0)); // -47.0925f, 18.5626f, -49.8848f, 16.7783f +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_1404(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x419b2e3e), SkBits2Float(0xc243b405)); +path.quadTo(SkBits2Float(0x41b4811d), SkBits2Float(0xc2479f9a), SkBits2Float(0x41cbf476), SkBits2Float(0xc2417131)); +path.quadTo(SkBits2Float(0x41e19882), SkBits2Float(0xc23bbceb), SkBits2Float(0x41e9f15f), SkBits2Float(0xc23083cb)); +path.quadTo(SkBits2Float(0x4200ef06), SkBits2Float(0xc2310b91), SkBits2Float(0x420a6762), SkBits2Float(0xc2294dac)); +path.quadTo(SkBits2Float(0x4214aa80), SkBits2Float(0xc220ea0a), SkBits2Float(0x4215fd8a), SkBits2Float(0xc213b9c8)); +path.quadTo(SkBits2Float(0x42175094), SkBits2Float(0xc2068986), SkBits2Float(0x420eecf0), SkBits2Float(0xc1f88cd3)); +path.quadTo(SkBits2Float(0x4206894d), SkBits2Float(0xc1e4069a), SkBits2Float(0x41f2b216), SkBits2Float(0xc1e16085)); +path.quadTo(SkBits2Float(0x41d85192), SkBits2Float(0xc1deba71), SkBits2Float(0x41c3cb5a), SkBits2Float(0xc1ef81b8)); +path.quadTo(SkBits2Float(0x41b61bc0), SkBits2Float(0xc1fab1e0), SkBits2Float(0x41b05ee8), SkBits2Float(0xc2051350)); +path.quadTo(SkBits2Float(0x419fe690), SkBits2Float(0xc204b4a5), SkBits2Float(0x4190436e), SkBits2Float(0xc208d3d1)); +path.quadTo(SkBits2Float(0x4171a027), SkBits2Float(0xc20f0238), SkBits2Float(0x4161f1d7), SkBits2Float(0xc21baba8)); +path.quadTo(SkBits2Float(0x41524386), SkBits2Float(0xc2285517), SkBits2Float(0x416afd23), SkBits2Float(0xc2340ec3)); +path.quadTo(SkBits2Float(0x4181db5f), SkBits2Float(0xc23fc871), SkBits2Float(0x419b2e3e), SkBits2Float(0xc243b405)); +path.close(); +path.moveTo(SkBits2Float(0xc221f910), SkBits2Float(0xc2067acc)); +path.quadTo(SkBits2Float(0xc214fb93), SkBits2Float(0xc2091d7b), SkBits2Float(0xc209ef07), SkBits2Float(0xc201cb14)); +path.quadTo(SkBits2Float(0xc1fdc4f6), SkBits2Float(0xc1f4f15c), SkBits2Float(0xc1f87f97), SkBits2Float(0xc1daf662)); +path.quadTo(SkBits2Float(0xc1f33a38), SkBits2Float(0xc1c0fb68), SkBits2Float(0xc200ef83), SkBits2Float(0xc1aae250)); +path.quadTo(SkBits2Float(0xc20841e9), SkBits2Float(0xc194c938), SkBits2Float(0xc2153f65), SkBits2Float(0xc18f83d9)); +path.quadTo(SkBits2Float(0xc2223ce2), SkBits2Float(0xc18a3e7a), SkBits2Float(0xc22d496e), SkBits2Float(0xc198e348)); +path.quadTo(SkBits2Float(0xc23855fb), SkBits2Float(0xc1a78814), SkBits2Float(0xc23af8aa), SkBits2Float(0xc1c1830c)); +path.quadTo(SkBits2Float(0xc23d9b5a), SkBits2Float(0xc1db7e06), SkBits2Float(0xc23648f3), SkBits2Float(0xc1f1971e)); +path.quadTo(SkBits2Float(0xc22ef68d), SkBits2Float(0xc203d81c), SkBits2Float(0xc221f910), SkBits2Float(0xc2067acc)); +path.close(); +path.moveTo(SkBits2Float(0x4218d883), SkBits2Float(0xc1dfb2a2)); +path.quadTo(SkBits2Float(0x4224b610), SkBits2Float(0xc1eb836c), SkBits2Float(0x4231475d), SkBits2Float(0xc1e31687)); +path.quadTo(SkBits2Float(0x423dd8aa), SkBits2Float(0xc1daa9a1), SkBits2Float(0x4243c10e), SkBits2Float(0xc1c2ee88)); +path.quadTo(SkBits2Float(0x4249a974), SkBits2Float(0xc1ab336d), SkBits2Float(0x42457300), SkBits2Float(0xc19210d4)); +path.quadTo(SkBits2Float(0x42413c8e), SkBits2Float(0xc171dc76), SkBits2Float(0x42355f01), SkBits2Float(0xc15a3ae1)); +path.quadTo(SkBits2Float(0x42298174), SkBits2Float(0xc142994c), SkBits2Float(0x421cf027), SkBits2Float(0xc1537318)); +path.quadTo(SkBits2Float(0x42105edb), SkBits2Float(0xc1644ce3), SkBits2Float(0x420a7675), SkBits2Float(0xc189e18c)); +path.quadTo(SkBits2Float(0x42048e10), SkBits2Float(0xc1a19ca6), SkBits2Float(0x4208c483), SkBits2Float(0xc1babf40)); +path.quadTo(SkBits2Float(0x420cfaf6), SkBits2Float(0xc1d3e1d8), SkBits2Float(0x4218d883), SkBits2Float(0xc1dfb2a2)); +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x4087d82a), SkBits2Float(0x42204637), SkBits2Float(0x401ecaaa), SkBits2Float(0x422284dc)); +path.quadTo(SkBits2Float(0x4033f0a5), SkBits2Float(0x4223bc28), SkBits2Float(0x4047ae10), SkBits2Float(0x4225218a)); +path.quadTo(SkBits2Float(0x40aa0f54), SkBits2Float(0x422f1027), SkBits2Float(0x40a38748), SkBits2Float(0x423c4af2)); +path.quadTo(SkBits2Float(0x409cff44), SkBits2Float(0x424985be), SkBits2Float(0x401b14b8), SkBits2Float(0x42524cc7)); +path.quadTo(SkBits2Float(0xbd754800), SkBits2Float(0x425b13d0), SkBits2Float(0xc05781d0), SkBits2Float(0x425a42cf)); +path.quadTo(SkBits2Float(0xc0d59744), SkBits2Float(0x425971cf), SkBits2Float(0xc10de7c8), SkBits2Float(0x424f8332)); +path.quadTo(SkBits2Float(0xc13103ee), SkBits2Float(0x42459494), SkBits2Float(0xc12dbfea), SkBits2Float(0x423859c9)); +path.quadTo(SkBits2Float(0xc12a7be8), SkBits2Float(0x422b1efe), SkBits2Float(0xc102c172), SkBits2Float(0x422257f4)); +path.quadTo(SkBits2Float(0xc0dbff18), SkBits2Float(0x421dc1e9), SkBits2Float(0xc0ab47a0), SkBits2Float(0x421bca58)); +path.quadTo(SkBits2Float(0xc0ad79af), SkBits2Float(0x421b8a4a), SkBits2Float(0xc0afa610), SkBits2Float(0x421b4830)); +path.quadTo(SkBits2Float(0xc0b25ad5), SkBits2Float(0x421af5e2), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.close(); +path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200)); +path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); +path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176)); +path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330)); +path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); +path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); +path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec)); +path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.close(); +path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x401b14b8), SkBits2Float(0x42524cc7)); +path.quadTo(SkBits2Float(0xbd754800), SkBits2Float(0x425b13d0), SkBits2Float(0xc05781d0), SkBits2Float(0x425a42cf)); +path.quadTo(SkBits2Float(0xc0d59744), SkBits2Float(0x425971cf), SkBits2Float(0xc10de7c8), SkBits2Float(0x424f8332)); +path.quadTo(SkBits2Float(0xc13103ee), SkBits2Float(0x42459494), SkBits2Float(0xc12dbfea), SkBits2Float(0x423859c9)); +path.quadTo(SkBits2Float(0xc12a7be8), SkBits2Float(0x422b1efe), SkBits2Float(0xc102c172), SkBits2Float(0x422257f4)); +path.quadTo(SkBits2Float(0xc0b60dfc), SkBits2Float(0x421990ea), SkBits2Float(0xc0186f48), SkBits2Float(0x421a61ec)); +path.quadTo(SkBits2Float(0x3f6cf5e0), SkBits2Float(0x421b32ec), SkBits2Float(0x4047ae10), SkBits2Float(0x4225218a)); +path.quadTo(SkBits2Float(0x40aa0f54), SkBits2Float(0x422f1027), SkBits2Float(0x40a38748), SkBits2Float(0x423c4af2)); +path.quadTo(SkBits2Float(0x409cff44), SkBits2Float(0x424985be), SkBits2Float(0x401b14b8), SkBits2Float(0x42524cc7)); +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_4713(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x40f7bc68), SkBits2Float(0xc2503bb0)); +path.quadTo(SkBits2Float(0x41299c84), SkBits2Float(0xc256ef4e), SkBits2Float(0x415ce976), SkBits2Float(0xc2539652)); +path.quadTo(SkBits2Float(0x41881b33), SkBits2Float(0xc2503d58), SkBits2Float(0x41958271), SkBits2Float(0xc244cdc4)); +path.quadTo(SkBits2Float(0x41a2e9af), SkBits2Float(0xc2395e30), SkBits2Float(0x419c37b8), SkBits2Float(0xc22c8af3)); +path.quadTo(SkBits2Float(0x419585c2), SkBits2Float(0xc21fb7b7), SkBits2Float(0x417d4d34), SkBits2Float(0xc2190418)); +path.quadTo(SkBits2Float(0x414f8ee4), SkBits2Float(0xc2125079), SkBits2Float(0x411c41f2), SkBits2Float(0xc215a974)); +path.quadTo(SkBits2Float(0x40d1ea00), SkBits2Float(0xc2190270), SkBits2Float(0x409c4d08), SkBits2Float(0xc2247204)); +path.quadTo(SkBits2Float(0x404d6020), SkBits2Float(0xc22fe198), SkBits2Float(0x408177f0), SkBits2Float(0xc23cb4d4)); +path.quadTo(SkBits2Float(0x409c3fc8), SkBits2Float(0xc2498810), SkBits2Float(0x40f7bc68), SkBits2Float(0xc2503bb0)); +path.close(); +path.moveTo(SkBits2Float(0xc20487d4), SkBits2Float(0xc2239250)); +path.quadTo(SkBits2Float(0xc1ee8d37), SkBits2Float(0xc2239d4e), SkBits2Float(0xc1dbbeef), SkBits2Float(0xc21a45b5)); +path.quadTo(SkBits2Float(0xc1c8f0a7), SkBits2Float(0xc210ee1d), SkBits2Float(0xc1c8daab), SkBits2Float(0xc203ace5)); +path.quadTo(SkBits2Float(0xc1c8c4af), SkBits2Float(0xc1ecd758), SkBits2Float(0xc1db73e0), SkBits2Float(0xc1da0910)); +path.quadTo(SkBits2Float(0xc1ee2310), SkBits2Float(0xc1c73ac7), SkBits2Float(0xc20452c1), SkBits2Float(0xc1c724cb)); +path.quadTo(SkBits2Float(0xc21193f9), SkBits2Float(0xc1c70ecf), SkBits2Float(0xc21afb1d), SkBits2Float(0xc1d9be01)); +path.quadTo(SkBits2Float(0xc2246242), SkBits2Float(0xc1ec6d31), SkBits2Float(0xc2246d40), SkBits2Float(0xc20377d2)); +path.quadTo(SkBits2Float(0xc224783e), SkBits2Float(0xc210b90a), SkBits2Float(0xc21b20a5), SkBits2Float(0xc21a202d)); +path.quadTo(SkBits2Float(0xc211c90c), SkBits2Float(0xc2238752), SkBits2Float(0xc20487d4), SkBits2Float(0xc2239250)); +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.close(); +path.moveTo(SkBits2Float(0xc23c7b18), SkBits2Float(0x40950470)); +path.quadTo(SkBits2Float(0xc22fb33d), SkBits2Float(0x4071d1b0), SkBits2Float(0xc2242dae), SkBits2Float(0x40ad5534)); +path.quadTo(SkBits2Float(0xc218a81f), SkBits2Float(0x40e1c194), SkBits2Float(0xc21524ab), SkBits2Float(0x41240037)); +path.quadTo(SkBits2Float(0xc211a138), SkBits2Float(0x41571fa5), SkBits2Float(0xc2182ec4), SkBits2Float(0x41829af0)); +path.quadTo(SkBits2Float(0xc21ebc50), SkBits2Float(0x4199a610), SkBits2Float(0xc22b842b), SkBits2Float(0x41a0acf4)); +path.quadTo(SkBits2Float(0xc2384c07), SkBits2Float(0x41a7b3dc), SkBits2Float(0xc243d196), SkBits2Float(0x419a98c4)); +path.quadTo(SkBits2Float(0xc24f5726), SkBits2Float(0x418d7dad), SkBits2Float(0xc252da98), SkBits2Float(0x4167dbea)); +path.quadTo(SkBits2Float(0xc2565e0c), SkBits2Float(0x4134bc7e), SkBits2Float(0xc24fd080), SkBits2Float(0x4106a640)); +path.quadTo(SkBits2Float(0xc24942f4), SkBits2Float(0x40b12008), SkBits2Float(0xc23c7b18), SkBits2Float(0x40950470)); +path.close(); +path.moveTo(SkBits2Float(0x4204f72e), SkBits2Float(0x41c56cd2)); +path.quadTo(SkBits2Float(0x42123842), SkBits2Float(0x41c52adf), SkBits2Float(0x421baed7), SkBits2Float(0x41d7bac6)); +path.quadTo(SkBits2Float(0x4225256d), SkBits2Float(0x41ea4aad), SkBits2Float(0x42254667), SkBits2Float(0x4202666b)); +path.quadTo(SkBits2Float(0x42256760), SkBits2Float(0x420fa77f), SkBits2Float(0x421c1f6c), SkBits2Float(0x42191e14)); +path.quadTo(SkBits2Float(0x421bff97), SkBits2Float(0x42193e89), SkBits2Float(0x421bdf6b), SkBits2Float(0x42195eb8)); +path.quadTo(SkBits2Float(0x421bbff6), SkBits2Float(0x42197f32), SkBits2Float(0x421ba03b), SkBits2Float(0x42199f57)); +path.quadTo(SkBits2Float(0x421b605e), SkBits2Float(0x4219e00a), SkBits2Float(0x421b1fa8), SkBits2Float(0x421a1f22)); +path.quadTo(SkBits2Float(0x421ae0f1), SkBits2Float(0x421a604b), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41db19b1), SkBits2Float(0x41da63d5), SkBits2Float(0x41db755b), SkBits2Float(0x41da0a9b)); +path.quadTo(SkBits2Float(0x41dbce01), SkBits2Float(0x41d9ae59), SkBits2Float(0x41dc285e), SkBits2Float(0x41d952ce)); +path.quadTo(SkBits2Float(0x41dc55b6), SkBits2Float(0x41d924df), SkBits2Float(0x41dc82cd), SkBits2Float(0x41d8f7cd)); +path.quadTo(SkBits2Float(0x41dcaf1e), SkBits2Float(0x41d8ca01), SkBits2Float(0x41dcdc4c), SkBits2Float(0x41d89bf0)); +path.quadTo(SkBits2Float(0x41ef6c33), SkBits2Float(0x41c5aec5), SkBits2Float(0x4204f72e), SkBits2Float(0x41c56cd2)); +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42240000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_24588(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x413a5194), SkBits2Float(0xc24d4e33)); // 11.6449f, -51.3264f +path.quadTo(SkBits2Float(0x4169f3fc), SkBits2Float(0xc2532032), SkBits2Float(0x418e0c8b), SkBits2Float(0xc24ed218)); // 14.6221f, -52.7814f, 17.7561f, -51.7052f +path.quadTo(SkBits2Float(0x41a71f17), SkBits2Float(0xc24a83ff), SkBits2Float(0x41b2c316), SkBits2Float(0xc23e9b65)); // 20.8902f, -50.6289f, 22.3453f, -47.6518f +path.quadTo(SkBits2Float(0x41be6714), SkBits2Float(0xc232b2cb), SkBits2Float(0x41b5cae0), SkBits2Float(0xc2262985)); // 23.8003f, -44.6746f, 22.7241f, -41.5405f +path.quadTo(SkBits2Float(0x41ad2ead), SkBits2Float(0xc219a03f), SkBits2Float(0x41955d79), SkBits2Float(0xc213ce40)); // 21.6478f, -38.4065f, 18.6706f, -36.9514f +path.quadTo(SkBits2Float(0x417b188a), SkBits2Float(0xc20dfc40), SkBits2Float(0x4148f373), SkBits2Float(0xc2124a5a)); // 15.6935f, -35.4963f, 12.5594f, -36.5726f +path.quadTo(SkBits2Float(0x4116ce5a), SkBits2Float(0xc2169874), SkBits2Float(0x40ff0cba), SkBits2Float(0xc222810e)); // 9.42538f, -37.6489f, 7.9703f, -40.626f +path.quadTo(SkBits2Float(0x40d07cc0), SkBits2Float(0xc22e69a8), SkBits2Float(0x40f2ed90), SkBits2Float(0xc23af2ee)); // 6.51523f, -43.6032f, 7.5915f, -46.7372f +path.quadTo(SkBits2Float(0x410aaf2c), SkBits2Float(0xc2477c34), SkBits2Float(0x413a5194), SkBits2Float(0xc24d4e33)); // 8.66777f, -49.8713f, 11.6449f, -51.3264f +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); // -19.799f, -19.799f +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); // -11.598f, -28, 0, -28 +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); // 11.598f, -28, 19.799f, -19.799f +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); // 28, -11.598f, 28, 0 +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); // 28, 11.598f, 19.799f, 19.799f +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); // 13.7163f, 25.8817f, 5.76477f, 27.4529f +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); // 8, 29.7635f, 8, 33 +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); // 8, 36.3137f, 5.65685f, 38.6569f +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); // 3.31371f, 41, 0, 41 +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); // -3.31371f, 41, -5.65685f, 38.6569f +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); // -8, 36.3137f, -8, 33 +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); // -8, 29.7635f, -5.76477f, 27.4529f +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); // -13.7163f, 25.8817f, -19.799f, 19.799f +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); // -28, 11.598f, -28, 0 +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); // -28, -11.598f, -19.799f, -19.799f +path.close(); +path.moveTo(SkBits2Float(0xc23d594a), SkBits2Float(0x3f8b9aa0)); // -47.3372f, 1.09066f +path.quadTo(SkBits2Float(0xc23056ee), SkBits2Float(0x3ee95200), SkBits2Float(0xc2255841), SkBits2Float(0x40139cf0)); // -44.0849f, 0.455704f, -41.3362f, 2.30645f +path.quadTo(SkBits2Float(0xc21a5994), SkBits2Float(0x408507d0), SkBits2Float(0xc217cf63), SkBits2Float(0x40ed1ab6)); // -38.5875f, 4.1572f, -37.9525f, 7.40951f +path.quadTo(SkBits2Float(0xc21747fe), SkBits2Float(0x41016369), SkBits2Float(0xc2172ef9), SkBits2Float(0x410bdff7)); // -37.8203f, 8.08677f, -37.7959f, 8.74218f +path.quadTo(SkBits2Float(0xc2161ebf), SkBits2Float(0x411577cf), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); // -37.53f, 9.34175f, -37.3514f, 10.0083f +path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176)); // -36.4938f, 13.2091f, -38.1506f, 16.0788f +path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330)); // -39.8075f, 18.9486f, -43.0083f, 19.8062f +path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); // -46.2091f, 20.6639f, -49.0788f, 19.007f +path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); // -51.9486f, 17.3502f, -52.8062f, 14.1494f +path.quadTo(SkBits2Float(0xc2543df8), SkBits2Float(0x415334fe), SkBits2Float(0xc2546005), SkBits2Float(0x41447d40)); // -53.0605f, 13.2004f, -53.0938f, 12.2806f +path.quadTo(SkBits2Float(0xc255df09), SkBits2Float(0x41370862), SkBits2Float(0xc2569fcc), SkBits2Float(0x41279af0)); // -53.4678f, 11.4395f, -53.6561f, 10.4753f +path.quadTo(SkBits2Float(0xc25929fe), SkBits2Float(0x40e722fc), SkBits2Float(0xc251c2d2), SkBits2Float(0x408f2d94)); // -54.291f, 7.22302f, -52.4403f, 4.47431f +path.quadTo(SkBits2Float(0xc24a5ba8), SkBits2Float(0x3fdce0a0), SkBits2Float(0xc23d594a), SkBits2Float(0x3f8b9aa0)); // -50.5895f, 1.72561f, -47.3372f, 1.09066f +path.close(); +path.moveTo(SkBits2Float(0xc18b14a2), SkBits2Float(0x42164b25)); // -17.3851f, 37.5734f +path.quadTo(SkBits2Float(0xc1675bab), SkBits2Float(0x421010e4), SkBits2Float(0xc134a62c), SkBits2Float(0x4213efa5)); // -14.4599f, 36.0165f, -11.2906f, 36.984f +path.quadTo(SkBits2Float(0xc101f0aa), SkBits2Float(0x4217ce66), SkBits2Float(0xc0d20f46), SkBits2Float(0x422381cc)); // -8.12126f, 37.9516f, -6.56436f, 40.8768f +path.quadTo(SkBits2Float(0xc0a03d38), SkBits2Float(0x422f3532), SkBits2Float(0xc0bf3344), SkBits2Float(0x423be292)); // -5.00747f, 43.8019f, -5.97501f, 46.9713f +path.quadTo(SkBits2Float(0xc0de294c), SkBits2Float(0x42488ff2), SkBits2Float(0xc11de23e), SkBits2Float(0x424eca34)); // -6.94254f, 50.1406f, -9.86773f, 51.6975f +path.quadTo(SkBits2Float(0xc14cafd4), SkBits2Float(0x42550476), SkBits2Float(0xc17f6556), SkBits2Float(0x425125b4)); // -12.7929f, 53.2544f, -15.9622f, 52.2868f +path.quadTo(SkBits2Float(0xc1990d6c), SkBits2Float(0x424d46f3), SkBits2Float(0xc1a581f0), SkBits2Float(0x4241938e)); // -19.1316f, 51.3193f, -20.6884f, 48.3941f +path.quadTo(SkBits2Float(0xc1b1f673), SkBits2Float(0x4235e028), SkBits2Float(0xc1aa38f0), SkBits2Float(0x422932c8)); // -22.2453f, 45.4689f, -21.2778f, 42.2996f +path.quadTo(SkBits2Float(0xc1a27b6c), SkBits2Float(0x421c8567), SkBits2Float(0xc18b14a2), SkBits2Float(0x42164b25)); // -20.3103f, 39.1303f, -17.3851f, 37.5734f +path.close(); +path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); // 27.3431f, 27.3431f +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); // 29.6863f, 25, 33, 25 +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); // 36.3137f, 25, 38.6569f, 27.3431f +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); // 41, 29.6863f, 41, 33 +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); // 41, 36.3137f, 38.6569f, 38.6569f +path.quadTo(SkBits2Float(0x4217d943), SkBits2Float(0x421d67f9), SkBits2Float(0x4214ba8d), SkBits2Float(0x421f5c6e)); // 37.9622f, 39.3515f, 37.1822f, 39.8403f +path.quadTo(SkBits2Float(0x42129039), SkBits2Float(0x422256bd), SkBits2Float(0x420f9986), SkBits2Float(0x4224eb5c)); // 36.6408f, 40.5847f, 35.8999f, 41.2298f +path.quadTo(SkBits2Float(0x420e25cf), SkBits2Float(0x42262f05), SkBits2Float(0x420ca0a4), SkBits2Float(0x42273ec0)); // 35.5369f, 41.5459f, 35.1569f, 41.8113f +path.quadTo(SkBits2Float(0x420b5228), SkBits2Float(0x42288f80), SkBits2Float(0x4209d382), SkBits2Float(0x4229c624)); // 34.8302f, 42.1401f, 34.4566f, 42.4435f +path.quadTo(SkBits2Float(0x41ff1232), SkBits2Float(0x423220d2), SkBits2Float(0x41e4b406), SkBits2Float(0x4230c247)); // 31.8839f, 44.5321f, 28.5879f, 44.1897f +path.quadTo(SkBits2Float(0x41ca55dc), SkBits2Float(0x422f63bd), SkBits2Float(0x41b9a084), SkBits2Float(0x42251952)); // 25.2919f, 43.8474f, 23.2034f, 41.2747f +path.quadTo(SkBits2Float(0x41a8eb2b), SkBits2Float(0x421acee9), SkBits2Float(0x41aba840), SkBits2Float(0x420d9fd3)); // 21.1148f, 38.7021f, 21.4572f, 35.4061f +path.quadTo(SkBits2Float(0x41ae6555), SkBits2Float(0x420070be), SkBits2Float(0x41c2fa28), SkBits2Float(0x41f02c24)); // 21.7995f, 32.1101f, 24.3721f, 30.0216f +path.quadTo(SkBits2Float(0x41c514db), SkBits2Float(0x41ee76d0), SkBits2Float(0x41c73f0f), SkBits2Float(0x41ecf584)); // 24.6352f, 29.808f, 24.9058f, 29.6199f +path.quadTo(SkBits2Float(0x41c919c1), SkBits2Float(0x41eb15ab), SkBits2Float(0x41cb250b), SkBits2Float(0x41e94e07)); // 25.1376f, 29.3856f, 25.3931f, 29.1631f +path.quadTo(SkBits2Float(0x41cf4ed6), SkBits2Float(0x41e5ae04), SkBits2Float(0x41d3c048), SkBits2Float(0x41e2e387)); // 25.9135f, 28.71f, 26.4689f, 28.3611f +path.quadTo(SkBits2Float(0x41d4d649), SkBits2Float(0x41e1661f), SkBits2Float(0x41d605fb), SkBits2Float(0x41dff35b)); // 26.6046f, 28.1749f, 26.7529f, 27.9938f +path.quadTo(SkBits2Float(0x41d7094e), SkBits2Float(0x41deb6c2), SkBits2Float(0x41d81f4d), SkBits2Float(0x41dd81fd)); // 26.8795f, 27.8392f, 27.0153f, 27.6885f +path.lineTo(SkBits2Float(0x41d81f53), SkBits2Float(0x41dd81f7)); // 27.0153f, 27.6885f +path.quadTo(SkBits2Float(0x41d96269), SkBits2Float(0x41dc1b1d), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); // 27.1731f, 27.5132f, 27.3431f, 27.3431f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x421a8288), SkBits2Float(0x420efdef)); // 38.6275f, 35.748f +path.quadTo(SkBits2Float(0x4219989b), SkBits2Float(0x421c3719), SkBits2Float(0x420f9986), SkBits2Float(0x4224eb5c)); // 38.399f, 39.0538f, 35.8999f, 41.2298f +path.quadTo(SkBits2Float(0x42059a71), SkBits2Float(0x422d9f9f), SkBits2Float(0x41f0c28e), SkBits2Float(0x422cb5b2)); // 33.4008f, 43.4059f, 30.095f, 43.1774f +path.quadTo(SkBits2Float(0x41d65038), SkBits2Float(0x422bcbc5), SkBits2Float(0x41c4e7b3), SkBits2Float(0x4221ccb0)); // 26.7892f, 42.949f, 24.6131f, 40.4499f +path.quadTo(SkBits2Float(0x41b37f2c), SkBits2Float(0x4217cd9b), SkBits2Float(0x41b55306), SkBits2Float(0x420a9471)); // 22.4371f, 37.9508f, 22.6655f, 34.645f +path.quadTo(SkBits2Float(0x41b726e0), SkBits2Float(0x41fab68c), SkBits2Float(0x41cb250b), SkBits2Float(0x41e94e07)); // 22.894f, 31.3391f, 25.3931f, 29.1631f +path.quadTo(SkBits2Float(0x41df2336), SkBits2Float(0x41d7e580), SkBits2Float(0x41f9958b), SkBits2Float(0x41d9b95a)); // 27.8922f, 26.9871f, 31.198f, 27.2155f +path.quadTo(SkBits2Float(0x420a03ef), SkBits2Float(0x41db8d34), SkBits2Float(0x4212b832), SkBits2Float(0x41ef8b5f)); // 34.5038f, 27.4439f, 36.6799f, 29.9431f +path.quadTo(SkBits2Float(0x421b6c75), SkBits2Float(0x4201c4c5), SkBits2Float(0x421a8288), SkBits2Float(0x420efdef)); // 38.8559f, 32.4422f, 38.6275f, 35.748f +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +// SkDQuadIntersection.cpp:594: failed assertion "way_roughly_zero(fT[0][index]) +static void fuzz763_20016(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x41e88c66), SkBits2Float(0xc22f800b)); // 29.0686f, -43.875f +path.quadTo(SkBits2Float(0x420178e8), SkBits2Float(0xc230b9b9), SkBits2Float(0x420babd1), SkBits2Float(0xc228426b)); // 32.3681f, -44.1814f, 34.9178f, -42.0649f +path.quadTo(SkBits2Float(0x4215debb), SkBits2Float(0xc21fcb1e), SkBits2Float(0x42171869), SkBits2Float(0xc2129869)); // 37.4675f, -39.9484f, 37.7738f, -36.6488f +path.quadTo(SkBits2Float(0x42185217), SkBits2Float(0xc20565b3), SkBits2Float(0x420fdac9), SkBits2Float(0xc1f66594)); // 38.0802f, -33.3493f, 35.9637f, -30.7996f +path.quadTo(SkBits2Float(0x4207637c), SkBits2Float(0xc1e1ffc1), SkBits2Float(0x41f4618e), SkBits2Float(0xc1df8c65)); // 33.8472f, -28.2499f, 30.5476f, -27.9436f +path.quadTo(SkBits2Float(0x41d9fc22), SkBits2Float(0xc1dd190a), SkBits2Float(0x41c59650), SkBits2Float(0xc1ee07a4)); // 27.2481f, -27.6372f, 24.6984f, -29.7537f +path.quadTo(SkBits2Float(0x41b1307c), SkBits2Float(0xc1fef63e), SkBits2Float(0x41aebd21), SkBits2Float(0xc20cadd5)); // 22.1487f, -31.8702f, 21.8423f, -35.1698f +path.quadTo(SkBits2Float(0x41ac49c5), SkBits2Float(0xc219e08a), SkBits2Float(0x41bd3860), SkBits2Float(0xc2241373)); // 21.536f, -38.4693f, 23.6525f, -41.019f +path.quadTo(SkBits2Float(0x41ce26fa), SkBits2Float(0xc22e465d), SkBits2Float(0x41e88c66), SkBits2Float(0xc22f800b)); // 25.769f, -43.5687f, 29.0686f, -43.875f +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); // -19.799f, -19.799f +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); // -11.598f, -28, 0, -28 +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); // 11.598f, -28, 19.799f, -19.799f +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); // 28, -11.598f, 28, 0 +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); // 28, 11.598f, 19.799f, 19.799f +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); // 13.7163f, 25.8817f, 5.76477f, 27.4529f +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); // 8, 29.7635f, 8, 33 +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x42100ef9), SkBits2Float(0x40c1f194), SkBits2Float(0x4218e765)); // 8, 36.0146f, 6.06074f, 38.226f +path.quadTo(SkBits2Float(0x4100db87), SkBits2Float(0x42186e1c), SkBits2Float(0x411cd246), SkBits2Float(0x421bbc51)); // 8.0536f, 38.1075f, 9.80134f, 38.9339f +path.quadTo(SkBits2Float(0x41326be0), SkBits2Float(0x42146b95), SkBits2Float(0x4156b15b), SkBits2Float(0x421110b0)); // 11.1513f, 37.1051f, 13.4183f, 36.2663f +path.quadTo(SkBits2Float(0x41843577), SkBits2Float(0x420c7739), SkBits2Float(0x419c4b3e), SkBits2Float(0x421200ec)); // 16.5261f, 35.1164f, 19.5367f, 36.5009f +path.quadTo(SkBits2Float(0x41b46104), SkBits2Float(0x42178a9f), SkBits2Float(0x41bd93f2), SkBits2Float(0x4223f904)); // 22.5474f, 37.8854f, 23.6972f, 40.9932f +path.quadTo(SkBits2Float(0x41c6c6e0), SkBits2Float(0x42306768), SkBits2Float(0x41bbb37b), SkBits2Float(0x423c724c)); // 24.8471f, 44.101f, 23.4626f, 47.1116f +path.quadTo(SkBits2Float(0x41b0a015), SkBits2Float(0x42487d2f), SkBits2Float(0x4197c34c), SkBits2Float(0x424d16a6)); // 22.0782f, 50.1222f, 18.9704f, 51.2721f +path.quadTo(SkBits2Float(0x417dcd04), SkBits2Float(0x4251b01e), SkBits2Float(0x414da178), SkBits2Float(0x424c266a)); // 15.8626f, 52.422f, 12.8519f, 51.0375f +path.quadTo(SkBits2Float(0x414d992c), SkBits2Float(0x424c2576), SkBits2Float(0x414d90e0), SkBits2Float(0x424c2481)); // 12.8499f, 51.0366f, 12.8479f, 51.0356f +path.quadTo(SkBits2Float(0x414d8b5f), SkBits2Float(0x424c2655), SkBits2Float(0x414d85dc), SkBits2Float(0x424c2828)); // 12.8465f, 51.0374f, 12.8452f, 51.0392f +path.quadTo(SkBits2Float(0x412d952c), SkBits2Float(0x4256bc8e), SkBits2Float(0x40f225c0), SkBits2Float(0x4258923c)); // 10.8489f, 53.6841f, 7.56711f, 54.1428f +path.quadTo(SkBits2Float(0x4089212c), SkBits2Float(0x425a67eb), SkBits2Float(0x3fd1f7e0), SkBits2Float(0x42526bbf)); // 4.2853f, 54.6015f, 1.64038f, 52.6052f +path.quadTo(SkBits2Float(0xbf8094f0), SkBits2Float(0x424a6f94), SkBits2Float(0xbfbb4ab0), SkBits2Float(0x423d4f00)); // -1.00455f, 50.609f, -1.46322f, 47.3271f +path.quadTo(SkBits2Float(0xbff60080), SkBits2Float(0x42302e6e), SkBits2Float(0x3d985000), SkBits2Float(0x42259a07)); // -1.92189f, 44.0453f, 0.0743713f, 41.4004f +path.quadTo(SkBits2Float(0x3e6fb042), SkBits2Float(0x4224c15b), SkBits2Float(0x3ecdd2ea), SkBits2Float(0x4223f703)); // 0.234071f, 41.1888f, 0.402f, 40.9912f +path.quadTo(SkBits2Float(0x3e4fb040), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); // 0.202821f, 41, 0, 41 +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); // -3.31371f, 41, -5.65685f, 38.6569f +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); // -8, 36.3137f, -8, 33 +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); // -8, 29.7635f, -5.76477f, 27.4529f +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); // -13.7163f, 25.8817f, -19.799f, 19.799f +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); // -28, 11.598f, -28, 0 +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); // -28, -11.598f, -19.799f, -19.799f +path.close(); +path.moveTo(SkBits2Float(0xc24a80c4), SkBits2Float(0xc16637fc)); // -50.6257f, -14.3887f +path.quadTo(SkBits2Float(0xc23faf8f), SkBits2Float(0xc1826e03), SkBits2Float(0xc2329eca), SkBits2Float(0xc17bee18)); // -47.9214f, -16.3037f, -44.6551f, -15.7456f +path.quadTo(SkBits2Float(0xc2258e06), SkBits2Float(0xc173002c), SkBits2Float(0xc21de504), SkBits2Float(0xc147bb58)); // -41.3887f, -15.1875f, -39.4736f, -12.4832f +path.quadTo(SkBits2Float(0xc2163c02), SkBits2Float(0xc11c7684), SkBits2Float(0xc218777c), SkBits2Float(0xc0d066e4)); // -37.5586f, -9.77893f, -38.1167f, -6.51256f +path.quadTo(SkBits2Float(0xc21ab2f8), SkBits2Float(0xc04fc188), SkBits2Float(0xc225842d), SkBits2Float(0xbfaa62c0)); // -38.6748f, -3.24619f, -41.3791f, -1.33114f +path.quadTo(SkBits2Float(0xc2305562), SkBits2Float(0x3f157b20), SkBits2Float(0xc23d6626), SkBits2Float(0x3cd38800)); // -44.0834f, 0.58391f, -47.3498f, 0.0258217f +path.quadTo(SkBits2Float(0xc24a76ea), SkBits2Float(0xbf084280), SkBits2Float(0xc2521fed), SkBits2Float(0xc04f23f0)); // -50.6161f, -0.532265f, -52.5312f, -3.23657f +path.quadTo(SkBits2Float(0xc259c8f0), SkBits2Float(0xc0be1ba0), SkBits2Float(0xc2578d74), SkBits2Float(0xc11350e2)); // -54.4462f, -5.94087f, -53.8881f, -9.20725f +path.quadTo(SkBits2Float(0xc25551f9), SkBits2Float(0xc14793f2), SkBits2Float(0xc24a80c4), SkBits2Float(0xc16637fc)); // -53.3301f, -12.4736f, -50.6257f, -14.3887f +path.close(); +path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); // -47.1494f, 4.35143f +path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200)); // -43.9486f, 3.49378f, -41.0788f, 5.15063f +path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); // -38.2091f, 6.80749f, -37.3514f, 10.0083f +path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176)); // -36.4938f, 13.2091f, -38.1506f, 16.0788f +path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330)); // -39.8075f, 18.9486f, -43.0083f, 19.8062f +path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); // -46.2091f, 20.6639f, -49.0788f, 19.007f +path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); // -51.9486f, 17.3502f, -52.8062f, 14.1494f +path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec)); // -53.6639f, 10.9486f, -52.007f, 8.07884f +path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); // -50.3502f, 5.20908f, -47.1494f, 4.35143f +path.close(); +path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); // 27.3431f, 27.3431f +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); // 29.6863f, 25, 33, 25 +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); // 36.3137f, 25, 38.6569f, 27.3431f +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); // 41, 29.6863f, 41, 33 +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); // 41, 36.3137f, 38.6569f, 38.6569f +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); // 36.3137f, 41, 33, 41 +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); // 29.6863f, 41, 27.3431f, 38.6569f +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); // 25, 36.3137f, 25, 33 +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); // 25, 29.6863f, 27.3431f, 27.3431f +path.close(); +path.moveTo(SkBits2Float(0xc1beec1e), SkBits2Float(0x4207519a)); // -23.8653f, 33.8297f +path.quadTo(SkBits2Float(0xc1a5a92e), SkBits2Float(0x42034ca6), SkBits2Float(0xc18e1d36), SkBits2Float(0x42096379)); // -20.7076f, 32.8249f, -17.7643f, 34.3471f +path.quadTo(SkBits2Float(0xc16d2279), SkBits2Float(0x420f7a4d), SkBits2Float(0xc15d0ea8), SkBits2Float(0x421c1bc5)); // -14.8209f, 35.8694f, -13.8161f, 39.0271f +path.quadTo(SkBits2Float(0xc14cfad8), SkBits2Float(0x4228bd3d), SkBits2Float(0xc1655627), SkBits2Float(0x42348339)); // -12.8112f, 42.1848f, -14.3335f, 45.1281f +path.quadTo(SkBits2Float(0xc17db174), SkBits2Float(0x42404936), SkBits2Float(0xc1981baa), SkBits2Float(0x42444e2a)); // -15.8558f, 48.0715f, -19.0135f, 49.0763f +path.quadTo(SkBits2Float(0xc1b15e9a), SkBits2Float(0x4248531e), SkBits2Float(0xc1c8ea94), SkBits2Float(0x42423c4a)); // -22.1712f, 50.0812f, -25.1145f, 48.5589f +path.quadTo(SkBits2Float(0xc1e0768c), SkBits2Float(0x423c2577), SkBits2Float(0xc1e88074), SkBits2Float(0x422f83ff)); // -28.0579f, 47.0366f, -29.0627f, 43.8789f +path.quadTo(SkBits2Float(0xc1f08a5c), SkBits2Float(0x4222e287), SkBits2Float(0xc1e45cb6), SkBits2Float(0x42171c8a)); // -30.0676f, 40.7212f, -28.5453f, 37.7779f +path.quadTo(SkBits2Float(0xc1d82f0e), SkBits2Float(0x420b568e), SkBits2Float(0xc1beec1e), SkBits2Float(0x4207519a)); // -27.023f, 34.8345f, -23.8653f, 33.8297f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x41bbb37b), SkBits2Float(0x423c724c)); // 23.4626f, 47.1116f +path.quadTo(SkBits2Float(0x41b0a015), SkBits2Float(0x42487d2f), SkBits2Float(0x4197c34c), SkBits2Float(0x424d16a6)); // 22.0782f, 50.1222f, 18.9704f, 51.2721f +path.quadTo(SkBits2Float(0x417dcd04), SkBits2Float(0x4251b01e), SkBits2Float(0x414da178), SkBits2Float(0x424c266a)); // 15.8626f, 52.422f, 12.8519f, 51.0375f +path.quadTo(SkBits2Float(0x411d75ea), SkBits2Float(0x42469cb8), SkBits2Float(0x410b100e), SkBits2Float(0x423a2e53)); // 9.84129f, 49.653f, 8.69142f, 46.5452f +path.quadTo(SkBits2Float(0x40f15460), SkBits2Float(0x422dbfee), SkBits2Float(0x410ed0fc), SkBits2Float(0x4221b50b)); // 7.54155f, 43.4374f, 8.92602f, 40.4268f +path.quadTo(SkBits2Float(0x4124f7c7), SkBits2Float(0x4215aa28), SkBits2Float(0x4156b15b), SkBits2Float(0x421110b0)); // 10.3105f, 37.4162f, 13.4183f, 36.2663f +path.quadTo(SkBits2Float(0x41843577), SkBits2Float(0x420c7739), SkBits2Float(0x419c4b3e), SkBits2Float(0x421200ec)); // 16.5261f, 35.1164f, 19.5367f, 36.5009f +path.quadTo(SkBits2Float(0x41b46104), SkBits2Float(0x42178a9f), SkBits2Float(0x41bd93f2), SkBits2Float(0x4223f904)); // 22.5474f, 37.8854f, 23.6972f, 40.9932f +path.quadTo(SkBits2Float(0x41c6c6e0), SkBits2Float(0x42306768), SkBits2Float(0x41bbb37b), SkBits2Float(0x423c724c)); // 24.8471f, 44.101f, 23.4626f, 47.1116f +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_17370(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x41fb8980), SkBits2Float(0xc20d9cf4)); // 31.4421f, -35.4033f +path.quadTo(SkBits2Float(0x42081e43), SkBits2Float(0xc215e4e6), SkBits2Float(0x42154ac6), SkBits2Float(0xc2146e6f)); // 34.0296f, -37.4735f, 37.323f, -37.1078f +path.quadTo(SkBits2Float(0x4222774a), SkBits2Float(0xc212f7f8), SkBits2Float(0x422abf3a), SkBits2Float(0xc2089e76)); // 40.6165f, -36.7422f, 42.6867f, -34.1547f +path.quadTo(SkBits2Float(0x4233072c), SkBits2Float(0xc1fc89e5), SkBits2Float(0x423190b5), SkBits2Float(0xc1e230df)); // 44.757f, -31.5673f, 44.3913f, -28.2739f +path.quadTo(SkBits2Float(0x42301a3e), SkBits2Float(0xc1c7d7d8), SkBits2Float(0x4225c0bc), SkBits2Float(0xc1b747f6)); // 44.0256f, -24.9804f, 41.4382f, -22.9101f +path.quadTo(SkBits2Float(0x421b6738), SkBits2Float(0xc1a6b815), SkBits2Float(0x420e3ab6), SkBits2Float(0xc1a9a502)); // 38.8508f, -20.8399f, 35.5573f, -21.2056f +path.quadTo(SkBits2Float(0x42010e32), SkBits2Float(0xc1ac91ef), SkBits2Float(0x41f18c82), SkBits2Float(0xc1c144f4)); // 32.2639f, -21.5713f, 30.1936f, -24.1587f +path.quadTo(SkBits2Float(0x41e0fca1), SkBits2Float(0xc1d5f7fa), SkBits2Float(0x41e3e98e), SkBits2Float(0xc1f05101)); // 28.1234f, -26.7461f, 28.489f, -30.0396f +path.quadTo(SkBits2Float(0x41e6d67b), SkBits2Float(0xc2055504), SkBits2Float(0x41fb8980), SkBits2Float(0xc20d9cf4)); // 28.8547f, -33.333f, 31.4421f, -35.4033f +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); // -19.799f, -19.799f +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); // -11.598f, -28, 0, -28 +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); // 11.598f, -28, 19.799f, -19.799f +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); // 28, -11.598f, 28, 0 +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); // 28, 11.598f, 19.799f, 19.799f +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); // 13.7163f, 25.8817f, 5.76477f, 27.4529f +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); // 8, 29.7635f, 8, 33 +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x420ff6ba), SkBits2Float(0x40c2ea2a), SkBits2Float(0x4218c3c6)); // 8, 35.9909f, 6.09108f, 38.1912f +path.quadTo(SkBits2Float(0x41135b8a), SkBits2Float(0x42173bf5), SkBits2Float(0x413c53fb), SkBits2Float(0x421ec49c)); // 9.20985f, 37.8086f, 11.7705f, 39.692f +path.quadTo(SkBits2Float(0x416709c9), SkBits2Float(0x42269f2a), SkBits2Float(0x416f0674), SkBits2Float(0x4233b9ad)); // 14.4399f, 41.6554f, 14.9391f, 44.9313f +path.quadTo(SkBits2Float(0x41770320), SkBits2Float(0x4240d431), SkBits2Float(0x415798ee), SkBits2Float(0x424b81a4)); // 15.4383f, 48.2072f, 13.4748f, 50.8766f +path.quadTo(SkBits2Float(0x41382eba), SkBits2Float(0x42562f18), SkBits2Float(0x4103c4ac), SkBits2Float(0x42582e42)); // 11.5114f, 53.546f, 8.23552f, 54.0452f +path.quadTo(SkBits2Float(0x409eb53c), SkBits2Float(0x425a2d6e), SkBits2Float(0x40129340), SkBits2Float(0x425252e0)); // 4.95962f, 54.5444f, 2.29024f, 52.5809f +path.quadTo(SkBits2Float(0x3ee54581), SkBits2Float(0x424ce72c), SkBits2Float(0xbeb8b807), SkBits2Float(0x4244fb36)); // 0.447796f, 51.2258f, -0.360779f, 49.2453f +path.quadTo(SkBits2Float(0xbf99615c), SkBits2Float(0x424cdad2), SkBits2Float(0xc043dd58), SkBits2Float(0x42522abc)); // -1.19828f, 51.2137f, -3.06038f, 52.5417f +path.quadTo(SkBits2Float(0xc0b84398), SkBits2Float(0x4259dd06), SkBits2Float(0xc1106c72), SkBits2Float(0x4257acc2)); // -5.75825f, 54.4658f, -9.02648f, 53.9187f +path.quadTo(SkBits2Float(0xc144b71a), SkBits2Float(0x42557c80), SkBits2Float(0xc163803e), SkBits2Float(0x424ab1e2)); // -12.2947f, 53.3716f, -14.2188f, 50.6737f +path.quadTo(SkBits2Float(0xc18124b1), SkBits2Float(0x423fe745), SkBits2Float(0xc1798856), SkBits2Float(0x4232d49b)); // -16.1429f, 47.9758f, -15.5958f, 44.7076f +path.quadTo(SkBits2Float(0xc170c74c), SkBits2Float(0x4225c1f0), SkBits2Float(0xc1459cd6), SkBits2Float(0x421e0fa8)); // -15.0487f, 41.4394f, -12.3508f, 39.5153f +path.quadTo(SkBits2Float(0xc11a7260), SkBits2Float(0x42165d5e), SkBits2Float(0xc0cc4f70), SkBits2Float(0x42188da2)); // -9.65292f, 37.5912f, -6.3847f, 38.1383f +path.quadTo(SkBits2Float(0xc0c78c19), SkBits2Float(0x4218a726), SkBits2Float(0xc0c2e01e), SkBits2Float(0x4218c538)); // -6.23585f, 38.1632f, -6.08986f, 38.1926f +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x420ff7b5), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); // -8, 35.9919f, -8, 33 +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); // -8, 29.7635f, -5.76477f, 27.4529f +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); // -13.7163f, 25.8817f, -19.799f, 19.799f +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); // -28, 11.598f, -28, 0 +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); // -28, -11.598f, -19.799f, -19.799f +path.close(); +path.moveTo(SkBits2Float(0xc23ab9e7), SkBits2Float(0xc1c274dd)); // -46.6815f, -24.3071f +path.quadTo(SkBits2Float(0xc22e95fe), SkBits2Float(0xc1cd18ca), SkBits2Float(0xc2223d50), SkBits2Float(0xc1c373a5)); // -43.6465f, -25.6371f, -40.5599f, -24.4315f +path.quadTo(SkBits2Float(0xc215e4a2), SkBits2Float(0xc1b9ce80), SkBits2Float(0xc21092ac), SkBits2Float(0xc1a186ac)); // -37.4733f, -23.2258f, -36.1432f, -20.1908f +path.quadTo(SkBits2Float(0xc20b40b5), SkBits2Float(0xc1893ed9), SkBits2Float(0xc2101348), SkBits2Float(0xc1611afc)); // -34.8132f, -17.1557f, -36.0188f, -14.0691f +path.quadTo(SkBits2Float(0xc214e5da), SkBits2Float(0xc12fb844), SkBits2Float(0xc22109c4), SkBits2Float(0xc11a706a)); // -37.2245f, -10.9825f, -40.2595f, -9.65244f +path.quadTo(SkBits2Float(0xc22d2dae), SkBits2Float(0xc1052890), SkBits2Float(0xc239865c), SkBits2Float(0xc11872dd)); // -43.2946f, -8.3224f, -46.3812f, -9.52804f +path.quadTo(SkBits2Float(0xc245df09), SkBits2Float(0xc12bbd26), SkBits2Float(0xc24b3100), SkBits2Float(0xc15c4ccc)); // -49.4678f, -10.7337f, -50.7979f, -13.7687f +path.quadTo(SkBits2Float(0xc25082f6), SkBits2Float(0xc1866e3a), SkBits2Float(0xc24bb063), SkBits2Float(0xc19f1f96)); // -52.1279f, -16.8038f, -50.9223f, -19.8904f +path.quadTo(SkBits2Float(0xc246ddd1), SkBits2Float(0xc1b7d0f0), SkBits2Float(0xc23ab9e7), SkBits2Float(0xc1c274dd)); // -49.7166f, -22.977f, -46.6815f, -24.3071f +path.close(); +path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); // -47.1494f, 4.35143f +path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200)); // -43.9486f, 3.49378f, -41.0788f, 5.15063f +path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); // -38.2091f, 6.80749f, -37.3514f, 10.0083f +path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176)); // -36.4938f, 13.2091f, -38.1506f, 16.0788f +path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330)); // -39.8075f, 18.9486f, -43.0083f, 19.8062f +path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); // -46.2091f, 20.6639f, -49.0788f, 19.007f +path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); // -51.9486f, 17.3502f, -52.8062f, 14.1494f +path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec)); // -53.6639f, 10.9486f, -52.007f, 8.07884f +path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); // -50.3502f, 5.20908f, -47.1494f, 4.35143f +path.close(); +path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); // 27.3431f, 27.3431f +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); // 29.6863f, 25, 33, 25 +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); // 36.3137f, 25, 38.6569f, 27.3431f +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); // 41, 29.6863f, 41, 33 +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); // 41, 36.3137f, 38.6569f, 38.6569f +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); // 36.3137f, 41, 33, 41 +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); // 29.6863f, 41, 27.3431f, 38.6569f +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); // 25, 36.3137f, 25, 33 +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); // 25, 29.6863f, 27.3431f, 27.3431f +path.close(); +path.moveTo(SkBits2Float(0xc1d961c1), SkBits2Float(0x41f9e1da)); // -27.1727f, 31.2353f +path.quadTo(SkBits2Float(0xc1bf6f78), SkBits2Float(0x41f47252), SkBits2Float(0xc1a93eb6), SkBits2Float(0x42017995)); // -23.9294f, 30.5558f, -21.1556f, 32.3687f +path.quadTo(SkBits2Float(0xc1930df4), SkBits2Float(0x4208ba01), SkBits2Float(0xc18d9e6c), SkBits2Float(0x4215b325)); // -18.3818f, 34.1816f, -17.7024f, 37.4249f +path.quadTo(SkBits2Float(0xc1882ee5), SkBits2Float(0x4222ac49), SkBits2Float(0xc196afbd), SkBits2Float(0x422dc4aa)); // -17.0229f, 40.6682f, -18.8358f, 43.4421f +path.quadTo(SkBits2Float(0xc1a53094), SkBits2Float(0x4238dd0c), SkBits2Float(0xc1bf22dd), SkBits2Float(0x423b94cf)); // -20.6487f, 46.2159f, -23.892f, 46.8953f +path.quadTo(SkBits2Float(0xc1d91525), SkBits2Float(0x423e4c93), SkBits2Float(0xc1ef45e7), SkBits2Float(0x42370c27)); // -27.1353f, 47.5748f, -29.9091f, 45.7619f +path.quadTo(SkBits2Float(0xc202bb55), SkBits2Float(0x422fcbbc), SkBits2Float(0xc2057319), SkBits2Float(0x4222d298)); // -32.6829f, 43.949f, -33.3624f, 40.7057f +path.quadTo(SkBits2Float(0xc2082adc), SkBits2Float(0x4215d973), SkBits2Float(0xc200ea70), SkBits2Float(0x420ac112)); // -34.0419f, 37.4624f, -32.2289f, 34.6885f +path.quadTo(SkBits2Float(0xc1f35409), SkBits2Float(0x41ff5161), SkBits2Float(0xc1d961c1), SkBits2Float(0x41f9e1da)); // -30.416f, 31.9147f, -27.1727f, 31.2353f +path.close(); +path.moveTo(SkBits2Float(0xbfccf162), SkBits2Float(0x42236913)); // -1.60112f, 40.8526f +path.quadTo(SkBits2Float(0xbf54c171), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); // -0.831077f, 41, 0, 41 +path.quadTo(SkBits2Float(0x3dcda9e6), SkBits2Float(0x42240000), SkBits2Float(0x3e4cbe2e), SkBits2Float(0x4223fdcc)); // 0.100422f, 41, 0.199944f, 40.9978f +path.quadTo(SkBits2Float(0x3f12dfd9), SkBits2Float(0x4223f586), SkBits2Float(0x3f6f571b), SkBits2Float(0x4223ce2c)); // 0.573728f, 40.9898f, 0.934923f, 40.9513f +path.quadTo(SkBits2Float(0x3f4168dc), SkBits2Float(0x4224a9bb), SkBits2Float(0x3f15fde0), SkBits2Float(0x422595d9)); // 0.755506f, 41.1658f, 0.585905f, 41.3963f +path.quadTo(SkBits2Float(0x3d27275b), SkBits2Float(0x42288cb9), SkBits2Float(0xbea10331), SkBits2Float(0x422bb375)); // 0.040809f, 42.1374f, -0.314477f, 42.9253f +path.quadTo(SkBits2Float(0xbf287eab), SkBits2Float(0x4228877a), SkBits2Float(0xbf989f50), SkBits2Float(0x42258882)); // -0.658183f, 42.1323f, -1.19236f, 41.3833f +path.quadTo(SkBits2Float(0xbfb1e0f9), SkBits2Float(0x42246d34), SkBits2Float(0xbfccf162), SkBits2Float(0x42236913)); // -1.38968f, 41.1066f, -1.60112f, 40.8526f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x415798ee), SkBits2Float(0x424b81a4)); // 13.4748f, 50.8766f +path.quadTo(SkBits2Float(0x41382eba), SkBits2Float(0x42562f18), SkBits2Float(0x4103c4ac), SkBits2Float(0x42582e42)); // 11.5114f, 53.546f, 8.23552f, 54.0452f +path.quadTo(SkBits2Float(0x409eb53c), SkBits2Float(0x425a2d6e), SkBits2Float(0x40129340), SkBits2Float(0x425252e0)); // 4.95962f, 54.5444f, 2.29024f, 52.5809f +path.quadTo(SkBits2Float(0xbec21f80), SkBits2Float(0x424a7854), SkBits2Float(0xbf60da80), SkBits2Float(0x423d5dd0)); // -0.379147f, 50.6175f, -0.878334f, 47.3416f +path.quadTo(SkBits2Float(0xbfb052b0), SkBits2Float(0x4230434d), SkBits2Float(0x3f15fde0), SkBits2Float(0x422595d9)); // -1.37752f, 44.0657f, 0.585905f, 41.3963f +path.quadTo(SkBits2Float(0x40232840), SkBits2Float(0x421ae866), SkBits2Float(0x40ba6840), SkBits2Float(0x4218e93b)); // 2.54933f, 38.727f, 5.82523f, 38.2278f +path.quadTo(SkBits2Float(0x41119e2f), SkBits2Float(0x4216ea10), SkBits2Float(0x413c53fb), SkBits2Float(0x421ec49c)); // 9.10112f, 37.7286f, 11.7705f, 39.692f +path.quadTo(SkBits2Float(0x416709c9), SkBits2Float(0x42269f2a), SkBits2Float(0x416f0674), SkBits2Float(0x4233b9ad)); // 14.4399f, 41.6554f, 14.9391f, 44.9313f +path.quadTo(SkBits2Float(0x41770320), SkBits2Float(0x4240d431), SkBits2Float(0x415798ee), SkBits2Float(0x424b81a4)); // 15.4383f, 48.2072f, 13.4748f, 50.8766f +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +// SkDQuadIntersection.cpp:598: failed assertion "way_roughly_equal(fT[0][index], 1)" +static void fuzz763_35322(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x41042400), SkBits2Float(0xc24fea42)); // 8.25879f, -51.9788f +path.quadTo(SkBits2Float(0x413225f2), SkBits2Float(0xc25680b3), SkBits2Float(0x4165502a), SkBits2Float(0xc2530720)); // 11.1343f, -53.6257f, 14.3321f, -52.757f +path.quadTo(SkBits2Float(0x418c3d32), SkBits2Float(0xc24f8d8e), SkBits2Float(0x41996a13), SkBits2Float(0xc2440d11)); // 17.5299f, -51.8882f, 19.1768f, -49.0128f +path.quadTo(SkBits2Float(0x41a696f3), SkBits2Float(0xc2388c95), SkBits2Float(0x419fa3cc), SkBits2Float(0xc22bc206)); // 20.8237f, -46.1373f, 19.955f, -42.9395f +path.quadTo(SkBits2Float(0x4198b0a8), SkBits2Float(0xc21ef778), SkBits2Float(0x4181afaf), SkBits2Float(0xc2186108)); // 19.0863f, -39.7417f, 16.2108f, -38.0948f +path.quadTo(SkBits2Float(0x41555d6e), SkBits2Float(0xc211ca98), SkBits2Float(0x41223335), SkBits2Float(0xc215442b)); // 13.3353f, -36.4478f, 10.1375f, -37.3166f +path.quadTo(SkBits2Float(0x40de11f8), SkBits2Float(0xc218bdbe), SkBits2Float(0x40a95e78), SkBits2Float(0xc2243e3a)); // 6.93969f, -38.1853f, 5.29278f, -41.0608f +path.quadTo(SkBits2Float(0x406955e8), SkBits2Float(0xc22fbeb6), SkBits2Float(0x4090778c), SkBits2Float(0xc23c8944)); // 3.64587f, -43.9362f, 4.51459f, -47.134f +path.quadTo(SkBits2Float(0x40ac4420), SkBits2Float(0xc24953d2), SkBits2Float(0x41042400), SkBits2Float(0xc24fea42)); // 5.38332f, -50.3319f, 8.25879f, -51.9788f +path.close(); +path.moveTo(SkBits2Float(0x41cb7543), SkBits2Float(0xc2385013)); // 25.4323f, -46.0782f +path.quadTo(SkBits2Float(0x41e591fd), SkBits2Float(0xc23a9972), SkBits2Float(0x41fb44a5), SkBits2Float(0xc232fbf5)); // 28.6963f, -46.6498f, 31.4085f, -44.7461f +path.quadTo(SkBits2Float(0x42087ba8), SkBits2Float(0xc22b5e79), SkBits2Float(0x420ac507), SkBits2Float(0xc21e501c)); // 34.1208f, -42.8423f, 34.6924f, -39.5782f +path.quadTo(SkBits2Float(0x420d0e67), SkBits2Float(0xc21141bf), SkBits2Float(0x420570ea), SkBits2Float(0xc206686a)); // 35.2641f, -36.3142f, 33.3603f, -33.602f +path.quadTo(SkBits2Float(0x41fba6dc), SkBits2Float(0xc1f71e2c), SkBits2Float(0x41e18a22), SkBits2Float(0xc1f28b6c)); // 31.4565f, -30.8897f, 28.1924f, -30.3181f +path.quadTo(SkBits2Float(0x41c76d67), SkBits2Float(0xc1edf8ad), SkBits2Float(0x41b1babe), SkBits2Float(0xc1fd33a6)); // 24.9284f, -29.7464f, 22.2162f, -31.6502f +path.quadTo(SkBits2Float(0x419c0815), SkBits2Float(0xc2063750), SkBits2Float(0x41977556), SkBits2Float(0xc21345ad)); // 19.5039f, -33.554f, 18.9323f, -36.818f +path.quadTo(SkBits2Float(0x4192e296), SkBits2Float(0xc220540a), SkBits2Float(0x41a21d8f), SkBits2Float(0xc22b2d5e)); // 18.3606f, -40.0821f, 20.2644f, -42.7943f +path.quadTo(SkBits2Float(0x41b15888), SkBits2Float(0xc23606b3), SkBits2Float(0x41cb7543), SkBits2Float(0xc2385013)); // 22.1682f, -45.5065f, 25.4323f, -46.0782f +path.close(); +path.moveTo(SkBits2Float(0x4206de71), SkBits2Float(0xc204f99f)); // 33.7172f, -33.2438f +path.quadTo(SkBits2Float(0x4211be80), SkBits2Float(0xc20c8d7c), SkBits2Float(0x421ecad2), SkBits2Float(0xc20a388c)); // 36.436f, -35.1382f, 39.6981f, -34.5552f +path.quadTo(SkBits2Float(0x422bd724), SkBits2Float(0xc207e39b), SkBits2Float(0x42336b00), SkBits2Float(0xc1fa0718)); // 42.9601f, -33.9723f, 44.8545f, -31.2535f +path.quadTo(SkBits2Float(0x423afedd), SkBits2Float(0xc1e446f9), SkBits2Float(0x4238a9ec), SkBits2Float(0xc1ca2e57)); // 46.7489f, -28.5347f, 46.1659f, -25.2726f +path.quadTo(SkBits2Float(0x423654fc), SkBits2Float(0xc1b015b3), SkBits2Float(0x422b74ed), SkBits2Float(0xc1a0edfa)); // 45.583f, -22.0106f, 42.8642f, -20.1162f +path.quadTo(SkBits2Float(0x422094de), SkBits2Float(0xc191c640), SkBits2Float(0x4213888c), SkBits2Float(0xc1967021)); // 40.1454f, -18.2218f, 36.8833f, -18.8048f +path.quadTo(SkBits2Float(0x42067c3a), SkBits2Float(0xc19b1a02), SkBits2Float(0x41fdd0bc), SkBits2Float(0xc1b0da20)); // 33.6213f, -19.3877f, 31.7269f, -22.1065f +path.quadTo(SkBits2Float(0x41eea902), SkBits2Float(0xc1c69a3f), SkBits2Float(0x41f352e3), SkBits2Float(0xc1e0b2e3)); // 29.8325f, -24.8253f, 30.4155f, -28.0873f +path.quadTo(SkBits2Float(0x41f7fcc4), SkBits2Float(0xc1facb85), SkBits2Float(0x4206de71), SkBits2Float(0xc204f99f)); // 30.9984f, -31.3494f, 33.7172f, -33.2438f +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); // -19.799f, -19.799f +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); // -11.598f, -28, 0, -28 +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); // 11.598f, -28, 19.799f, -19.799f +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); // 28, -11.598f, 28, 0 +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); // 28, 11.598f, 19.799f, 19.799f +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); // 13.7163f, 25.8817f, 5.76477f, 27.4529f +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); // 8, 29.7635f, 8, 33 +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); // 8, 36.3137f, 5.65685f, 38.6569f +path.quadTo(SkBits2Float(0x40b4fa00), SkBits2Float(0x421aa1fd), SkBits2Float(0x40b4ef0c), SkBits2Float(0x421aa35b)); // 5.65552f, 38.6582f, 5.65418f, 38.6595f +path.quadTo(SkBits2Float(0x40f11f68), SkBits2Float(0x421c342c), SkBits2Float(0x4111ddac), SkBits2Float(0x42218978)); // 7.53508f, 39.0509f, 9.11662f, 40.3842f +path.quadTo(SkBits2Float(0x413a6700), SkBits2Float(0x422a1497), SkBits2Float(0x413ee6c0), SkBits2Float(0x42374996)); // 11.6501f, 42.5201f, 11.9313f, 45.8219f +path.quadTo(SkBits2Float(0x41436686), SkBits2Float(0x42447e96), SkBits2Float(0x41213a06), SkBits2Float(0x424ea0eb)); // 12.2125f, 49.1236f, 10.0767f, 51.6571f +path.quadTo(SkBits2Float(0x40fe1b10), SkBits2Float(0x4258c340), SkBits2Float(0x40947314), SkBits2Float(0x4259e330)); // 7.9408f, 54.1907f, 4.63905f, 54.4719f +path.quadTo(SkBits2Float(0x3fab2c60), SkBits2Float(0x425b0321), SkBits2Float(0xbf991e40), SkBits2Float(0x42527802)); // 1.33729f, 54.7531f, -1.19624f, 52.6172f +path.quadTo(SkBits2Float(0xc06eb470), SkBits2Float(0x4249ece2), SkBits2Float(0xc08059b8), SkBits2Float(0x423cb7e2)); // -3.72976f, 50.4813f, -4.01095f, 47.1796f +path.quadTo(SkBits2Float(0xc0895940), SkBits2Float(0x422f82e3), SkBits2Float(0xc00a0088), SkBits2Float(0x4225608e)); // -4.29214f, 43.8778f, -2.15628f, 41.3443f +path.quadTo(SkBits2Float(0xbff725c6), SkBits2Float(0x42244eb9), SkBits2Float(0xbfd8a182), SkBits2Float(0x4223569a)); // -1.93084f, 41.0769f, -1.69243f, 40.8346f +path.quadTo(SkBits2Float(0xc07bec59), SkBits2Float(0x42218277), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); // -3.9363f, 40.3774f, -5.65685f, 38.6569f +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); // -8, 36.3137f, -8, 33 +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); // -8, 29.7635f, -5.76477f, 27.4529f +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); // -13.7163f, 25.8817f, -19.799f, 19.799f +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); // -28, 11.598f, -28, 0 +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); // -28, -11.598f, -19.799f, -19.799f +path.close(); +path.moveTo(SkBits2Float(0xc233e0fb), SkBits2Float(0xc1dac1ac)); // -44.9697f, -27.3446f +path.quadTo(SkBits2Float(0xc22769b6), SkBits2Float(0xc1e3c410), SkBits2Float(0xc21b69b6), SkBits2Float(0xc1d881ca)); // -41.8532f, -28.4707f, -38.8532f, -27.0634f +path.quadTo(SkBits2Float(0xc20f69b6), SkBits2Float(0xc1cd3f84), SkBits2Float(0xc20ae884), SkBits2Float(0xc1b450fa)); // -35.8532f, -25.656f, -34.7271f, -22.5395f +path.quadTo(SkBits2Float(0xc2066752), SkBits2Float(0xc19b6270), SkBits2Float(0xc20c0875), SkBits2Float(0xc1836270)); // -33.6009f, -19.4231f, -35.0083f, -16.4231f +path.quadTo(SkBits2Float(0xc211a998), SkBits2Float(0xc156c4e1), SkBits2Float(0xc21e20dd), SkBits2Float(0xc144c018)); // -36.4156f, -13.4231f, -39.5321f, -12.2969f +path.quadTo(SkBits2Float(0xc22a9822), SkBits2Float(0xc132bb50), SkBits2Float(0xc2369821), SkBits2Float(0xc1493fdc)); // -42.6486f, -11.1707f, -45.6486f, -12.5781f +path.quadTo(SkBits2Float(0xc2429822), SkBits2Float(0xc15fc467), SkBits2Float(0xc2471954), SkBits2Float(0xc188d0be)); // -48.6486f, -13.9854f, -49.7747f, -17.1019f +path.quadTo(SkBits2Float(0xc24b9a86), SkBits2Float(0xc1a1bf48), SkBits2Float(0xc245f962), SkBits2Float(0xc1b9bf47)); // -50.9009f, -20.2184f, -49.4935f, -23.2184f +path.quadTo(SkBits2Float(0xc2405840), SkBits2Float(0xc1d1bf48), SkBits2Float(0xc233e0fb), SkBits2Float(0xc1dac1ac)); // -48.0862f, -26.2184f, -44.9697f, -27.3446f +path.close(); +path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); // -47.1494f, 4.35143f +path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200)); // -43.9486f, 3.49378f, -41.0788f, 5.15063f +path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); // -38.2091f, 6.80749f, -37.3514f, 10.0083f +path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176)); // -36.4938f, 13.2091f, -38.1506f, 16.0788f +path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330)); // -39.8075f, 18.9486f, -43.0083f, 19.8062f +path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); // -46.2091f, 20.6639f, -49.0788f, 19.007f +path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); // -51.9486f, 17.3502f, -52.8062f, 14.1494f +path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec)); // -53.6639f, 10.9486f, -52.007f, 8.07884f +path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); // -50.3502f, 5.20908f, -47.1494f, 4.35143f +path.close(); +path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); // 27.3431f, 27.3431f +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); // 29.6863f, 25, 33, 25 +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); // 36.3137f, 25, 38.6569f, 27.3431f +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); // 41, 29.6863f, 41, 33 +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); // 41, 36.3137f, 38.6569f, 38.6569f +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); // 36.3137f, 41, 33, 41 +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); // 29.6863f, 41, 27.3431f, 38.6569f +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); // 25, 36.3137f, 25, 33 +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); // 25, 29.6863f, 27.3431f, 27.3431f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x41213a06), SkBits2Float(0x424ea0eb)); // 10.0767f, 51.6571f +path.quadTo(SkBits2Float(0x40fe1b10), SkBits2Float(0x4258c340), SkBits2Float(0x40947314), SkBits2Float(0x4259e330)); // 7.9408f, 54.1907f, 4.63905f, 54.4719f +path.quadTo(SkBits2Float(0x3fab2c60), SkBits2Float(0x425b0321), SkBits2Float(0xbf991e40), SkBits2Float(0x42527802)); // 1.33729f, 54.7531f, -1.19624f, 52.6172f +path.quadTo(SkBits2Float(0xc06eb470), SkBits2Float(0x4249ece2), SkBits2Float(0xc08059b8), SkBits2Float(0x423cb7e2)); // -3.72976f, 50.4813f, -4.01095f, 47.1796f +path.quadTo(SkBits2Float(0xc0895940), SkBits2Float(0x422f82e3), SkBits2Float(0xc00a0088), SkBits2Float(0x4225608e)); // -4.29214f, 43.8778f, -2.15628f, 41.3443f +path.quadTo(SkBits2Float(0xbca74400), SkBits2Float(0x421b3e39), SkBits2Float(0x40520168), SkBits2Float(0x421a1e48)); // -0.0204182f, 38.8108f, 3.28134f, 38.5296f +path.quadTo(SkBits2Float(0x40d2a8b0), SkBits2Float(0x4218fe58), SkBits2Float(0x4111ddac), SkBits2Float(0x42218978)); // 6.58309f, 38.2484f, 9.11662f, 40.3842f +path.quadTo(SkBits2Float(0x413a6700), SkBits2Float(0x422a1497), SkBits2Float(0x413ee6c0), SkBits2Float(0x42374996)); // 11.6501f, 42.5201f, 11.9313f, 45.8219f +path.quadTo(SkBits2Float(0x41436686), SkBits2Float(0x42447e96), SkBits2Float(0x41213a06), SkBits2Float(0x424ea0eb)); // 12.2125f, 49.1236f, 10.0767f, 51.6571f +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +// SkPathOpsOp.cpp:52: failed assertion "angle != firstAngle || !loop" +static void fuzz763_849020(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x411e4374), SkBits2Float(0xc24ec58f)); // 9.89147f, -51.6929f +path.quadTo(SkBits2Float(0x414d13fa), SkBits2Float(0xc254fe70), SkBits2Float(0x417fc7a6), SkBits2Float(0xc2511e30)); // 12.8174f, -53.2485f, 15.9862f, -52.2795f +path.quadTo(SkBits2Float(0x41993dab), SkBits2Float(0xc24d3df4), SkBits2Float(0x41a5af6e), SkBits2Float(0xc24189d2)); // 19.1551f, -51.3105f, 20.7107f, -48.3846f +path.quadTo(SkBits2Float(0x41b22132), SkBits2Float(0xc235d5b1), SkBits2Float(0x41aa60b2), SkBits2Float(0xc22928c4)); // 22.2662f, -45.4587f, 21.2972f, -42.2898f +path.quadTo(SkBits2Float(0x41a2a038), SkBits2Float(0xc21c7bda), SkBits2Float(0x418b37f4), SkBits2Float(0xc21642f8)); // 20.3282f, -39.1209f, 17.4023f, -37.5654f +path.quadTo(SkBits2Float(0x41679f65), SkBits2Float(0xc2100a16), SkBits2Float(0x4134ebb5), SkBits2Float(0xc213ea55)); // 14.4764f, -36.0098f, 11.3075f, -36.9788f +path.quadTo(SkBits2Float(0x41023808), SkBits2Float(0xc217ca94), SkBits2Float(0x40d2a902), SkBits2Float(0xc2237eb5)); // 8.13868f, -37.9478f, 6.58313f, -40.8737f +path.quadTo(SkBits2Float(0x40a0e1f4), SkBits2Float(0xc22f32d6), SkBits2Float(0x40bfe3ec), SkBits2Float(0xc23bdfc1)); // 5.02758f, -43.7996f, 5.99657f, -46.9685f +path.quadTo(SkBits2Float(0x40dee5e0), SkBits2Float(0xc2488cad), SkBits2Float(0x411e4374), SkBits2Float(0xc24ec58f)); // 6.96556f, -50.1374f, 9.89147f, -51.6929f +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); // -19.799f, -19.799f +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); // -11.598f, -28, 0, -28 +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); // 11.598f, -28, 19.799f, -19.799f +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); // 28, -11.598f, 28, 0 +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); // 28, 11.598f, 19.799f, 19.799f +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); // 13.7163f, 25.8817f, 5.76477f, 27.4529f +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); // 8, 29.7635f, 8, 33 +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); // 8, 36.3137f, 5.65685f, 38.6569f +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); // 3.31371f, 41, 0, 41 +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); // -3.31371f, 41, -5.65685f, 38.6569f +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); // -8, 36.3137f, -8, 33 +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); // -8, 29.7635f, -5.76477f, 27.4529f +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); // -13.7163f, 25.8817f, -19.799f, 19.799f +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); // -28, 11.598f, -28, 0 +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); // -28, -11.598f, -19.799f, -19.799f +path.close(); +path.moveTo(SkBits2Float(0xc23d1735), SkBits2Float(0x402cdce8)); // -47.2727f, 2.70098f +path.quadTo(SkBits2Float(0xc2302ce7), SkBits2Float(0x3ffa54f0), SkBits2Float(0xc224ef5c), SkBits2Float(0x406d8f00)); // -44.0438f, 1.95572f, -41.2337f, 3.71185f +path.quadTo(SkBits2Float(0xc219b1d2), SkBits2Float(0x40aef9c4), SkBits2Float(0xc216b6ab), SkBits2Float(0x410b261b)); // -38.4237f, 5.46799f, -37.6784f, 8.6968f +path.quadTo(SkBits2Float(0xc2166795), SkBits2Float(0x411080a8), SkBits2Float(0xc2163402), SkBits2Float(0x4115c8a0)); // -37.6012f, 9.03141f, -37.5508f, 9.36148f +path.quadTo(SkBits2Float(0xc215c2e1), SkBits2Float(0x411ad33f), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); // -37.4403f, 9.67657f, -37.3514f, 10.0083f +path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176)); // -36.4938f, 13.2091f, -38.1506f, 16.0788f +path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330)); // -39.8075f, 18.9486f, -43.0083f, 19.8062f +path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); // -46.2091f, 20.6639f, -49.0788f, 19.007f +path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); // -51.9486f, 17.3502f, -52.8062f, 14.1494f +path.quadTo(SkBits2Float(0xc253bae4), SkBits2Float(0x415ad9be), SkBits2Float(0xc2540461), SkBits2Float(0x41536cea)); // -52.9325f, 13.6782f, -53.0043f, 13.2141f +path.quadTo(SkBits2Float(0xc254a293), SkBits2Float(0x414c5480), SkBits2Float(0xc25512ee), SkBits2Float(0x4144b962)); // -53.1588f, 12.7706f, -53.2685f, 12.2953f +path.quadTo(SkBits2Float(0xc2580e14), SkBits2Float(0x41111028), SkBits2Float(0xc25107cc), SkBits2Float(0x40c833fc)); // -54.0137f, 9.06644f, -52.2576f, 6.25635f +path.quadTo(SkBits2Float(0xc24a0183), SkBits2Float(0x405c8f50), SkBits2Float(0xc23d1735), SkBits2Float(0x402cdce8)); // -50.5015f, 3.44625f, -47.2727f, 2.70098f +path.close(); +path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); // 27.3431f, 27.3431f +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); // 29.6863f, 25, 33, 25 +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); // 36.3137f, 25, 38.6569f, 27.3431f +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); // 41, 29.6863f, 41, 33 +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); // 41, 36.3137f, 38.6569f, 38.6569f +path.quadTo(SkBits2Float(0x421a60e3), SkBits2Float(0x421ae059), SkBits2Float(0x421a205f), SkBits2Float(0x421b1e77)); // 38.5946f, 38.7191f, 38.5316f, 38.7798f +path.lineTo(SkBits2Float(0x421a2049), SkBits2Float(0x421b1e8d)); // 38.5315f, 38.7798f +path.quadTo(SkBits2Float(0x4218fba8), SkBits2Float(0x421c384c), SkBits2Float(0x4217c88e), SkBits2Float(0x421d2f21)); // 38.2458f, 39.055f, 37.9459f, 39.296f +path.quadTo(SkBits2Float(0x42168e68), SkBits2Float(0x421e9b27), SkBits2Float(0x42152104), SkBits2Float(0x421fefda)); // 37.6391f, 39.6515f, 37.2822f, 39.9842f +path.quadTo(SkBits2Float(0x42146c3b), SkBits2Float(0x4220986c), SkBits2Float(0x4213b29d), SkBits2Float(0x42213416)); // 37.1057f, 40.1488f, 36.9244f, 40.3009f +path.quadTo(SkBits2Float(0x42130756), SkBits2Float(0x4221df6b), SkBits2Float(0x42124f9e), SkBits2Float(0x422284d0)); // 36.7572f, 40.4682f, 36.5778f, 40.6297f +path.quadTo(SkBits2Float(0x420875c9), SkBits2Float(0x422b6326), SkBits2Float(0x41f6726d), SkBits2Float(0x422ab150)); // 34.115f, 42.8468f, 30.8059f, 42.6732f +path.quadTo(SkBits2Float(0x41dbf947), SkBits2Float(0x4229ff79), SkBits2Float(0x41ca3c9c), SkBits2Float(0x422025a4)); // 27.4967f, 42.4995f, 25.2796f, 40.0368f +path.quadTo(SkBits2Float(0x41b87ff1), SkBits2Float(0x42164bcf), SkBits2Float(0x41b9e39e), SkBits2Float(0x42090f3c)); // 23.0625f, 37.574f, 23.2361f, 34.2649f +path.quadTo(SkBits2Float(0x41bb474a), SkBits2Float(0x41f7a551), SkBits2Float(0x41cefaf4), SkBits2Float(0x41e5e8a6)); // 23.4098f, 30.9557f, 25.8725f, 28.7386f +path.quadTo(SkBits2Float(0x41cffe18), SkBits2Float(0x41e4ff5a), SkBits2Float(0x41d105e6), SkBits2Float(0x41e422e9)); // 25.9991f, 28.6247f, 26.1279f, 28.517f +path.quadTo(SkBits2Float(0x41d1f880), SkBits2Float(0x41e32f61), SkBits2Float(0x41d2f77e), SkBits2Float(0x41e2419e)); // 26.2463f, 28.3981f, 26.3708f, 28.282f +path.quadTo(SkBits2Float(0x41d4f9d5), SkBits2Float(0x41e06208), SkBits2Float(0x41d70fba), SkBits2Float(0x41deb6ad)); // 26.622f, 28.0479f, 26.8827f, 27.8392f +path.quadTo(SkBits2Float(0x41d8cd7c), SkBits2Float(0x41dcb00a), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); // 27.1003f, 27.586f, 27.3431f, 27.3431f +path.close(); +path.moveTo(SkBits2Float(0xc185f1f2), SkBits2Float(0x42177488)); // -16.7431f, 37.8638f +path.quadTo(SkBits2Float(0xc15d848f), SkBits2Float(0x42110787), SkBits2Float(0xc12a8d7f), SkBits2Float(0x4214aee9)); // -13.8449f, 36.2574f, -10.6595f, 37.1708f +path.quadTo(SkBits2Float(0xc0ef2cde), SkBits2Float(0x4218564b), SkBits2Float(0xc0bbc4da), SkBits2Float(0x4223ee21)); // -7.47423f, 38.0843f, -5.86778f, 40.9825f +path.quadTo(SkBits2Float(0xc0885cd4), SkBits2Float(0x422f85f6), SkBits2Float(0xc0a597e8), SkBits2Float(0x423c43ba)); // -4.26133f, 43.8808f, -5.17479f, 47.0661f +path.quadTo(SkBits2Float(0xc0c2d2f4), SkBits2Float(0x4249017e), SkBits2Float(0xc10fc8d0), SkBits2Float(0x424f6e7e)); // -6.08825f, 50.2515f, -8.98653f, 51.8579f +path.quadTo(SkBits2Float(0xc13e2826), SkBits2Float(0x4255db7f), SkBits2Float(0xc1711f36), SkBits2Float(0x4252341c)); // -11.8848f, 53.4644f, -15.0701f, 52.5509f +path.quadTo(SkBits2Float(0xc1920b22), SkBits2Float(0x424e8cbc), SkBits2Float(0xc19ee524), SkBits2Float(0x4242f4e6)); // -18.2554f, 51.6374f, -19.8619f, 48.7392f +path.quadTo(SkBits2Float(0xc1abbf24), SkBits2Float(0x42375d10), SkBits2Float(0xc1a47060), SkBits2Float(0x422a9f4c)); // -21.4683f, 45.8409f, -20.5549f, 42.6556f +path.quadTo(SkBits2Float(0xc19d219e), SkBits2Float(0x421de188), SkBits2Float(0xc185f1f2), SkBits2Float(0x42177488)); // -19.6414f, 39.4702f, -16.7431f, 37.8638f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x421f4961), SkBits2Float(0x4209a6a0)); // 39.8217f, 34.4127f +path.quadTo(SkBits2Float(0x421ed2ca), SkBits2Float(0x4216e5cb), SkBits2Float(0x42152104), SkBits2Float(0x421fefda)); // 39.7058f, 37.7244f, 37.2822f, 39.9842f +path.quadTo(SkBits2Float(0x420b6f41), SkBits2Float(0x4228f9ea), SkBits2Float(0x41fc602b), SkBits2Float(0x42288353)); // 34.8586f, 42.2441f, 31.547f, 42.1282f +path.quadTo(SkBits2Float(0x41e1e1d7), SkBits2Float(0x42280cbd), SkBits2Float(0x41cfcdb9), SkBits2Float(0x421e5af7)); // 28.2353f, 42.0124f, 25.9755f, 39.5888f +path.quadTo(SkBits2Float(0x41bdb999), SkBits2Float(0x4214a933), SkBits2Float(0x41bea6c6), SkBits2Float(0x42076a08)); // 23.7156f, 37.1652f, 23.8314f, 33.8535f +path.quadTo(SkBits2Float(0x41bf93f3), SkBits2Float(0x41f455bd), SkBits2Float(0x41d2f77e), SkBits2Float(0x41e2419e)); // 23.9472f, 30.5419f, 26.3708f, 28.282f +path.quadTo(SkBits2Float(0x41e65b07), SkBits2Float(0x41d02d7f), SkBits2Float(0x42006cae), SkBits2Float(0x41d11aac)); // 28.7944f, 26.0222f, 32.1061f, 26.138f +path.quadTo(SkBits2Float(0x420dabd9), SkBits2Float(0x41d207d9), SkBits2Float(0x4216b5e7), SkBits2Float(0x41e56b63)); // 35.4178f, 26.2538f, 37.6776f, 28.6774f +path.quadTo(SkBits2Float(0x421fbff7), SkBits2Float(0x41f8ceed), SkBits2Float(0x421f4961), SkBits2Float(0x4209a6a0)); // 39.9375f, 31.101f, 39.8217f, 34.4127f +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_1597464(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x4101092a), SkBits2Float(0xc2500973)); // 8.06474f, -52.0092f +path.quadTo(SkBits2Float(0x412ef1d8), SkBits2Float(0xc256aade), SkBits2Float(0x41622940), SkBits2Float(0xc2533d84)); // 10.934f, -53.6669f, 14.1351f, -52.8101f +path.quadTo(SkBits2Float(0x418ab055), SkBits2Float(0xc24fd02d), SkBits2Float(0x4197f32a), SkBits2Float(0xc2445602)); // 17.3361f, -51.9533f, 18.9937f, -49.084f +path.quadTo(SkBits2Float(0x41a535ff), SkBits2Float(0xc238dbd6), SkBits2Float(0x419e5b4c), SkBits2Float(0xc22c0dfb)); // 20.6514f, -46.2147f, 19.7946f, -43.0137f +path.quadTo(SkBits2Float(0x4197809e), SkBits2Float(0xc21f4021), SkBits2Float(0x41808c46), SkBits2Float(0xc2189eb6)); // 18.9378f, -39.8126f, 16.0685f, -38.155f +path.quadTo(SkBits2Float(0x41532fdd), SkBits2Float(0xc211fd4c), SkBits2Float(0x411ff875), SkBits2Float(0xc2156aa5)); // 13.1992f, -36.4974f, 9.99816f, -37.3541f +path.quadTo(SkBits2Float(0x40d98218), SkBits2Float(0xc218d7fd), SkBits2Float(0x40a476c4), SkBits2Float(0xc2245229)); // 6.79713f, -38.2109f, 5.1395f, -41.0802f +path.quadTo(SkBits2Float(0x405ed6e0), SkBits2Float(0xc22fcc54), SkBits2Float(0x408ad638), SkBits2Float(0xc23c9a2e)); // 3.48186f, -43.9495f, 4.33865f, -47.1506f +path.quadTo(SkBits2Float(0x40a640f4), SkBits2Float(0xc2496808), SkBits2Float(0x4101092a), SkBits2Float(0xc2500973)); // 5.19543f, -50.3516f, 8.06474f, -52.0092f +path.close(); +path.moveTo(SkBits2Float(0xc21ab0b3), SkBits2Float(0xc21a9087)); // -38.6726f, -38.6411f +path.quadTo(SkBits2Float(0xc211524c), SkBits2Float(0xc223f0e2), SkBits2Float(0xc204110f), SkBits2Float(0xc223f243)); // -36.3304f, -40.9852f, -33.0167f, -40.9866f +path.quadTo(SkBits2Float(0xc1ed9fa5), SkBits2Float(0xc223f3a4), SkBits2Float(0xc1dadeef), SkBits2Float(0xc21a953c)); // -29.703f, -40.9879f, -27.3589f, -38.6457f +path.quadTo(SkBits2Float(0xc1c81e38), SkBits2Float(0xc21136d5), SkBits2Float(0xc1c81b76), SkBits2Float(0xc203f598)); // -25.0148f, -36.3035f, -25.0134f, -32.9898f +path.quadTo(SkBits2Float(0xc1c818b4), SkBits2Float(0xc1ed68b6), SkBits2Float(0xc1dad584), SkBits2Float(0xc1daa800)); // -25.0121f, -29.6761f, -27.3543f, -27.332f +path.quadTo(SkBits2Float(0xc1ed9254), SkBits2Float(0xc1c7e74a), SkBits2Float(0xc2040a66), SkBits2Float(0xc1c7e488)); // -29.6964f, -24.9879f, -33.0102f, -24.9866f +path.quadTo(SkBits2Float(0xc2114ba3), SkBits2Float(0xc1c7e1c6), SkBits2Float(0xc21aabfe), SkBits2Float(0xc1da9e95)); // -36.3239f, -24.9852f, -38.668f, -27.3274f +path.quadTo(SkBits2Float(0xc2240c5a), SkBits2Float(0xc1ed5b65), SkBits2Float(0xc2240dbb), SkBits2Float(0xc203eeef)); // -41.0121f, -29.6696f, -41.0134f, -32.9833f +path.quadTo(SkBits2Float(0xc2240f1c), SkBits2Float(0xc211302c), SkBits2Float(0xc21ab0b3), SkBits2Float(0xc21a9087)); // -41.0148f, -36.297f, -38.6726f, -38.6411f +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); // -19.799f, -19.799f +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); // -11.598f, -28, 0, -28 +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); // 11.598f, -28, 19.799f, -19.799f +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); // 28, -11.598f, 28, 0 +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); // 28, 11.598f, 19.799f, 19.799f +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); // 13.7163f, 25.8817f, 5.76477f, 27.4529f +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); // 8, 29.7635f, 8, 33 +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); // 8, 36.3137f, 5.65685f, 38.6569f +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); // 3.31371f, 41, 0, 41 +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); // -3.31371f, 41, -5.65685f, 38.6569f +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); // -8, 36.3137f, -8, 33 +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); // -8, 29.7635f, -5.76477f, 27.4529f +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); // -13.7163f, 25.8817f, -19.799f, 19.799f +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); // -28, 11.598f, -28, 0 +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); // -28, -11.598f, -19.799f, -19.799f +path.close(); +path.moveTo(SkBits2Float(0xc21564da), SkBits2Float(0x41204f08)); // -37.3485f, 10.0193f +path.quadTo(SkBits2Float(0xc211fc8d), SkBits2Float(0x41536ca0), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176)); // -36.4966f, 13.214f, -38.1506f, 16.0788f +path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330)); // -39.8075f, 18.9486f, -43.0083f, 19.8062f +path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); // -46.2091f, 20.6639f, -49.0788f, 19.007f +path.quadTo(SkBits2Float(0xc24fc815), SkBits2Float(0x418ad0f8), SkBits2Float(0xc25337a2), SkBits2Float(0x4162812a)); // -51.9454f, 17.352f, -52.8043f, 14.1565f +path.quadTo(SkBits2Float(0xc25336a6), SkBits2Float(0x41628fdb), SkBits2Float(0xc25335aa), SkBits2Float(0x41629e8a)); // -52.8034f, 14.1601f, -52.8024f, 14.1637f +path.quadTo(SkBits2Float(0xc24fc68b), SkBits2Float(0x418aea08), SkBits2Float(0xc2444b74), SkBits2Float(0x419829ad)); // -51.9439f, 17.3643f, -49.0737f, 19.0203f +path.quadTo(SkBits2Float(0xc238d05c), SkBits2Float(0x41a56952), SkBits2Float(0xc22c02fc), SkBits2Float(0x419e8b10)); // -46.2035f, 20.6764f, -43.0029f, 19.8179f +path.quadTo(SkBits2Float(0xc21f359c), SkBits2Float(0x4197acd2), SkBits2Float(0xc21895c9), SkBits2Float(0x4180b6a4)); // -39.8024f, 18.9594f, -38.1463f, 16.0892f +path.quadTo(SkBits2Float(0xc211f634), SkBits2Float(0x41538295), SkBits2Float(0xc21564da), SkBits2Float(0x41204f08)); // -36.4904f, 13.2194f, -37.3485f, 10.0193f +path.close(); +path.moveTo(SkBits2Float(0x41dacdf1), SkBits2Float(0x41daaf93)); // 27.3506f, 27.3357f +path.quadTo(SkBits2Float(0x41ed8b66), SkBits2Float(0x41c7ef83), SkBits2Float(0x420406f0), SkBits2Float(0x41c7edac)); // 29.6931f, 24.9919f, 33.0068f, 24.9911f +path.quadTo(SkBits2Float(0x4211482d), SkBits2Float(0x41c7ebd5), SkBits2Float(0x421aa834), SkBits2Float(0x41daa94b)); // 36.3205f, 24.9902f, 38.6643f, 27.3327f +path.quadTo(SkBits2Float(0x4224083d), SkBits2Float(0x41ed66c1), SkBits2Float(0x42240928), SkBits2Float(0x4203f49d)); // 41.008f, 29.6752f, 41.0089f, 32.9889f +path.quadTo(SkBits2Float(0x42240a14), SkBits2Float(0x421135da), SkBits2Float(0x421aab58), SkBits2Float(0x421a95e2)); // 41.0098f, 36.3026f, 38.6673f, 38.6464f +path.quadTo(SkBits2Float(0x421aa5fc), SkBits2Float(0x421a9b42), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); // 38.6621f, 38.6516f, 38.6569f, 38.6569f +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); // 36.3137f, 41, 33, 41 +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); // 29.6863f, 41, 27.3431f, 38.6569f +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); // 25, 36.3137f, 25, 33 +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); // 25, 29.6863f, 27.3431f, 27.3431f +path.quadTo(SkBits2Float(0x41dac1e5), SkBits2Float(0x41dabba1), SkBits2Float(0x41dac507), SkBits2Float(0x41dab880)); // 27.3447f, 27.3416f, 27.3462f, 27.3401f +path.lineTo(SkBits2Float(0x41dac551), SkBits2Float(0x41dab836)); // 27.3463f, 27.3399f +path.quadTo(SkBits2Float(0x41dac9a0), SkBits2Float(0x41dab3e4), SkBits2Float(0x41dacdf1), SkBits2Float(0x41daaf93)); // 27.3484f, 27.3378f, 27.3506f, 27.3357f +path.close(); +path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); // -47.1494f, 4.35143f +path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200)); // -43.9486f, 3.49378f, -41.0788f, 5.15063f +path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); // -38.2091f, 6.80749f, -37.3514f, 10.0083f +path.quadTo(SkBits2Float(0xc215672b), SkBits2Float(0x41202c49), SkBits2Float(0xc215667a), SkBits2Float(0x412036a3)); // -37.3507f, 10.0108f, -37.3501f, 10.0133f +path.lineTo(SkBits2Float(0xc2156516), SkBits2Float(0x41204b6b)); // -37.3487f, 10.0184f +path.lineTo(SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); // -52.8062f, 14.1494f +path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec)); // -53.6639f, 10.9486f, -52.007f, 8.07884f +path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); // -50.3502f, 5.20908f, -47.1494f, 4.35143f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0xc2444b74), SkBits2Float(0x419829ad)); // -49.0737f, 19.0203f +path.quadTo(SkBits2Float(0xc24fc68b), SkBits2Float(0x418aea08), SkBits2Float(0xc25335aa), SkBits2Float(0x41629e8a)); // -51.9439f, 17.3643f, -52.8024f, 14.1637f +path.quadTo(SkBits2Float(0xc256a4ca), SkBits2Float(0x412f6908), SkBits2Float(0xc25004f8), SkBits2Float(0x41017cac)); // -53.6609f, 10.9631f, -52.0049f, 8.09294f +path.quadTo(SkBits2Float(0xc2496525), SkBits2Float(0x40a7209c), SkBits2Float(0xc23c97c4), SkBits2Float(0x408ba7a8)); // -50.3488f, 5.22273f, -47.1482f, 4.36422f +path.quadTo(SkBits2Float(0xc22fca64), SkBits2Float(0x40605d58), SkBits2Float(0xc2244f4d), SkBits2Float(0x40a52d40)); // -43.9476f, 3.5057f, -41.0774f, 5.16177f +path.quadTo(SkBits2Float(0xc218d435), SkBits2Float(0x40da2bd2), SkBits2Float(0xc2156516), SkBits2Float(0x41204b6b)); // -38.2072f, 6.81785f, -37.3487f, 10.0184f +path.quadTo(SkBits2Float(0xc211f5f7), SkBits2Float(0x415380eb), SkBits2Float(0xc21895c9), SkBits2Float(0x4180b6a4)); // -36.4902f, 13.219f, -38.1463f, 16.0892f +path.quadTo(SkBits2Float(0xc21f359c), SkBits2Float(0x4197acd2), SkBits2Float(0xc22c02fc), SkBits2Float(0x419e8b10)); // -39.8024f, 18.9594f, -43.0029f, 19.8179f +path.quadTo(SkBits2Float(0xc238d05c), SkBits2Float(0x41a56952), SkBits2Float(0xc2444b74), SkBits2Float(0x419829ad)); // -46.2035f, 20.6764f, -49.0737f, 19.0203f +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_34974(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +#if 00 +path.moveTo(SkBits2Float(0x41015326), SkBits2Float(0xc2500694)); +path.quadTo(SkBits2Float(0x412f3e30), SkBits2Float(0xc256a6fa), SkBits2Float(0x41627462), SkBits2Float(0xc253387e)); +path.quadTo(SkBits2Float(0x418ad549), SkBits2Float(0xc24fca02), SkBits2Float(0x41981613), SkBits2Float(0xc2444f40)); +path.quadTo(SkBits2Float(0x41a556de), SkBits2Float(0xc238d47d), SkBits2Float(0x419e79e6), SkBits2Float(0xc22c06f0)); +path.quadTo(SkBits2Float(0x41979cee), SkBits2Float(0xc21f3964), SkBits2Float(0x4180a76a), SkBits2Float(0xc21898ff)); +path.quadTo(SkBits2Float(0x415363c9), SkBits2Float(0xc211f89a), SkBits2Float(0x41202d96), SkBits2Float(0xc2156716)); +path.quadTo(SkBits2Float(0x40d9eeca), SkBits2Float(0xc218d592), SkBits2Float(0x40a4eba0), SkBits2Float(0xc2245054)); +path.quadTo(SkBits2Float(0x405fd0f0), SkBits2Float(0xc22fcb17), SkBits2Float(0x408b5c58), SkBits2Float(0xc23c98a3)); +path.quadTo(SkBits2Float(0x40a6d038), SkBits2Float(0xc249662f), SkBits2Float(0x41015326), SkBits2Float(0xc2500694)); +path.close(); +#endif +#if 000 +path.moveTo(SkBits2Float(0xc21a9c18), SkBits2Float(0xc21aa524)); +path.quadTo(SkBits2Float(0xc2113c71), SkBits2Float(0xc2240440), SkBits2Float(0xc203fb34), SkBits2Float(0xc22403dc)); +path.quadTo(SkBits2Float(0xc1ed73ee), SkBits2Float(0xc2240379), SkBits2Float(0xc1dab5b7), SkBits2Float(0xc21aa3d1)); +path.quadTo(SkBits2Float(0xc1c7f781), SkBits2Float(0xc211442a), SkBits2Float(0xc1c7f847), SkBits2Float(0xc20402ed)); +path.quadTo(SkBits2Float(0xc1c7f90e), SkBits2Float(0xc1ed835f), SkBits2Float(0xc1dab85d), SkBits2Float(0xc1dac529)); +path.quadTo(SkBits2Float(0xc1ed77ad), SkBits2Float(0xc1c806f2), SkBits2Float(0xc203fd13), SkBits2Float(0xc1c807b9)); +path.quadTo(SkBits2Float(0xc2113e50), SkBits2Float(0xc1c8087f), SkBits2Float(0xc21a9d6b), SkBits2Float(0xc1dac7cf)); +path.quadTo(SkBits2Float(0xc223fc87), SkBits2Float(0xc1ed871e), SkBits2Float(0xc223fc24), SkBits2Float(0xc20404cc)); +path.quadTo(SkBits2Float(0xc223fbc0), SkBits2Float(0xc2114609), SkBits2Float(0xc21a9c18), SkBits2Float(0xc21aa524)); +path.close(); +#endif +#if 00 +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.close(); +#endif +#if 01 +path.moveTo(SkBits2Float(0xc2533a24), SkBits2Float(0x41625bba)); +path.lineTo(SkBits2Float(0xc2533ab2), SkBits2Float(0x4162536e)); +path.lineTo(SkBits2Float(0xc2533af7), SkBits2Float(0x41624f68)); +path.quadTo(SkBits2Float(0xc2533a8e), SkBits2Float(0x41625591), SkBits2Float(0xc2533a24), SkBits2Float(0x41625bba)); +path.close(); +#endif +#if 0 +path.moveTo(SkBits2Float(0x41dac664), SkBits2Float(0x41dab723)); +path.quadTo(SkBits2Float(0x41ed82ea), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); +path.lineTo(SkBits2Float(0x421a9d9a), SkBits2Float(0x421aa3a2)); +path.quadTo(SkBits2Float(0x42113e0a), SkBits2Float(0x422402d5), SkBits2Float(0x4203fccd), SkBits2Float(0x42240293)); +path.quadTo(SkBits2Float(0x41ed7721), SkBits2Float(0x42240251), SkBits2Float(0x41dab8bb), SkBits2Float(0x421aa2c0)); +path.quadTo(SkBits2Float(0x41c7fa56), SkBits2Float(0x42114330), SkBits2Float(0x41c7fada), SkBits2Float(0x420401f3)); +path.quadTo(SkBits2Float(0x41c7fb5f), SkBits2Float(0x41ed9352), SkBits2Float(0x41daa13c), SkBits2Float(0x41dadc57)); +path.quadTo(SkBits2Float(0x41daa91d), SkBits2Float(0x41dad46f), SkBits2Float(0x41dab101), SkBits2Float(0x41dacc89)); +path.quadTo(SkBits2Float(0x41dab5bf), SkBits2Float(0x41dac7c8), SkBits2Float(0x41daba7f), SkBits2Float(0x41dac307)); +path.lineTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41dac293), SkBits2Float(0x41dabaf3), SkBits2Float(0x41dac664), SkBits2Float(0x41dab723)); +path.close(); +#endif +#if 00001 +path.moveTo(SkBits2Float(0xc23c9951), SkBits2Float(0x408b2180)); +path.quadTo(SkBits2Float(0xc22fcba2), SkBits2Float(0x405f6340), SkBits2Float(0xc2245122), SkBits2Float(0x40a4b85c)); +path.quadTo(SkBits2Float(0xc218dd36), SkBits2Float(0x40d9a0b8), SkBits2Float(0xc2156c96), SkBits2Float(0x411fdb9a)); +path.lineTo(SkBits2Float(0xc2156b9c), SkBits2Float(0x411fea15)); +path.quadTo(SkBits2Float(0xc2156a20), SkBits2Float(0x4120002c), SkBits2Float(0xc21568a5), SkBits2Float(0x41201647)); +path.lineTo(SkBits2Float(0xc21568a3), SkBits2Float(0x41201660)); +path.lineTo(SkBits2Float(0xc2156841), SkBits2Float(0x41201c29)); +path.quadTo(SkBits2Float(0xc215680f), SkBits2Float(0x41201f0a), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); +path.quadTo(SkBits2Float(0xc21562d2), SkBits2Float(0x41206d52), SkBits2Float(0xc2155ca3), SkBits2Float(0x4120cb63)); +path.quadTo(SkBits2Float(0xc212057d), SkBits2Float(0x4153a15f), SkBits2Float(0xc2189adf), SkBits2Float(0x41809e82)); +path.quadTo(SkBits2Float(0xc21f3b9a), SkBits2Float(0x419793a4), SkBits2Float(0xc22c0940), SkBits2Float(0x419e6fdc)); +path.quadTo(SkBits2Float(0xc238d6e6), SkBits2Float(0x41a54c16), SkBits2Float(0xc2445177), SkBits2Float(0x41980aa0)); +path.quadTo(SkBits2Float(0xc24fcb1e), SkBits2Float(0x418aca39), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); +path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); +path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330)); +path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176)); +path.lineTo(SkBits2Float(0xc2533b22), SkBits2Float(0x41624cea)); +path.quadTo(SkBits2Float(0xc256a842), SkBits2Float(0x412f19c8), SkBits2Float(0xc25007d7), SkBits2Float(0x410132b2)); +path.quadTo(SkBits2Float(0xc24966ff), SkBits2Float(0x40a69160), SkBits2Float(0xc23c9951), SkBits2Float(0x408b2180)); +path.close(); +#endif + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +#if 01 +path.moveTo(SkBits2Float(0xc2445236), SkBits2Float(0x419806c2)); +path.quadTo(SkBits2Float(0xc24fccb6), SkBits2Float(0x418ac513), SkBits2Float(0xc2533ab2), SkBits2Float(0x4162536e)); +path.quadTo(SkBits2Float(0xc256a8ae), SkBits2Float(0x412f1cb2), SkBits2Float(0xc25007d7), SkBits2Float(0x410132b2)); +path.quadTo(SkBits2Float(0xc24966ff), SkBits2Float(0x40a69160), SkBits2Float(0xc23c9951), SkBits2Float(0x408b2180)); +path.quadTo(SkBits2Float(0xc22fcba2), SkBits2Float(0x405f6340), SkBits2Float(0xc2245122), SkBits2Float(0x40a4b85c)); +path.quadTo(SkBits2Float(0xc218d6a2), SkBits2Float(0x40d9bf1c), SkBits2Float(0xc21568a5), SkBits2Float(0x41201647)); +path.quadTo(SkBits2Float(0xc211faaa), SkBits2Float(0x41534d02), SkBits2Float(0xc2189b82), SkBits2Float(0x41809b82)); +path.quadTo(SkBits2Float(0xc21f3c59), SkBits2Float(0x41979082), SkBits2Float(0xc22c0a07), SkBits2Float(0x419e6c7a)); +path.quadTo(SkBits2Float(0xc238d7b6), SkBits2Float(0x41a54872), SkBits2Float(0xc2445236), SkBits2Float(0x419806c2)); +path.close(); +#endif + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_2211264(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x41017a68), SkBits2Float(0xc250050e)); +path.quadTo(SkBits2Float(0x412f66b2), SkBits2Float(0xc256a4e9), SkBits2Float(0x41629c3c), SkBits2Float(0xc25335d1)); +path.quadTo(SkBits2Float(0x418ae8e6), SkBits2Float(0xc24fc6bc), SkBits2Float(0x4198289b), SkBits2Float(0xc2444ba9)); +path.quadTo(SkBits2Float(0x41a56850), SkBits2Float(0xc238d096), SkBits2Float(0x419e8a20), SkBits2Float(0xc22c0333)); +path.quadTo(SkBits2Float(0x4197abf4), SkBits2Float(0xc21f35d0), SkBits2Float(0x4180b5d0), SkBits2Float(0xc21895f6)); +path.quadTo(SkBits2Float(0x41537f55), SkBits2Float(0xc211f61c), SkBits2Float(0x412049c9), SkBits2Float(0xc2156532)); +path.quadTo(SkBits2Float(0x40da287c), SkBits2Float(0xc218d449), SkBits2Float(0x40a529a8), SkBits2Float(0xc2244f5b)); +path.quadTo(SkBits2Float(0x406055a8), SkBits2Float(0xc22fca6e), SkBits2Float(0x408ba388), SkBits2Float(0xc23c97d0)); +path.quadTo(SkBits2Float(0x40a71c3c), SkBits2Float(0xc2496534), SkBits2Float(0x41017a68), SkBits2Float(0xc250050e)); +path.close(); +path.moveTo(SkBits2Float(0xc21a9126), SkBits2Float(0xc21ab014)); +path.quadTo(SkBits2Float(0xc21130d5), SkBits2Float(0xc2240e86), SkBits2Float(0xc203ef98), SkBits2Float(0xc2240d33)); +path.quadTo(SkBits2Float(0xc1ed5cb7), SkBits2Float(0xc2240bdf), SkBits2Float(0xc1da9fd4), SkBits2Float(0xc21aab8d)); +path.quadTo(SkBits2Float(0xc1c7e2f1), SkBits2Float(0xc2114b3c), SkBits2Float(0xc1c7e598), SkBits2Float(0xc20409ff)); +path.quadTo(SkBits2Float(0xc1c7e83e), SkBits2Float(0xc1ed9186), SkBits2Float(0xc1daa8e1), SkBits2Float(0xc1dad4a3)); +path.quadTo(SkBits2Float(0xc1ed6984), SkBits2Float(0xc1c817c0), SkBits2Float(0xc203f5ff), SkBits2Float(0xc1c81a66)); +path.quadTo(SkBits2Float(0xc211373c), SkBits2Float(0xc1c81d0d), SkBits2Float(0xc21a95ad), SkBits2Float(0xc1daddb0)); +path.quadTo(SkBits2Float(0xc223f41f), SkBits2Float(0xc1ed9e53), SkBits2Float(0xc223f2cb), SkBits2Float(0xc2041066)); +path.quadTo(SkBits2Float(0xc223f178), SkBits2Float(0xc21151a3), SkBits2Float(0xc21a9126), SkBits2Float(0xc21ab014)); +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.close(); +path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x421a9e0c), SkBits2Float(0x421aa331), SkBits2Float(0x421a9b79), SkBits2Float(0x421aa5c3)); +path.quadTo(SkBits2Float(0x421a98e4), SkBits2Float(0x421aa858), SkBits2Float(0x421a964e), SkBits2Float(0x421aaaec)); +path.lineTo(SkBits2Float(0x421a943a), SkBits2Float(0x421aad00)); +path.quadTo(SkBits2Float(0x421134d5), SkBits2Float(0x422409ae), SkBits2Float(0x4203f510), SkBits2Float(0x422408cc)); +path.quadTo(SkBits2Float(0x41ed67a7), SkBits2Float(0x422407e9), SkBits2Float(0x41daaa24), SkBits2Float(0x421aa7e8)); +path.quadTo(SkBits2Float(0x41c7eca1), SkBits2Float(0x421147e7), SkBits2Float(0x41c7ee66), SkBits2Float(0x420406aa)); +path.quadTo(SkBits2Float(0x41c7f02a), SkBits2Float(0x41ed8ada), SkBits2Float(0x41dab02f), SkBits2Float(0x41dacd55)); +path.lineTo(SkBits2Float(0x41dab02d), SkBits2Float(0x41dacd57)); +path.quadTo(SkBits2Float(0x41dab3d4), SkBits2Float(0x41dac9b0), SkBits2Float(0x41dab77c), SkBits2Float(0x41dac60a)); +path.quadTo(SkBits2Float(0x41dab83b), SkBits2Float(0x41dac54b), SkBits2Float(0x41dab8fa), SkBits2Float(0x41dac48d)); +path.quadTo(SkBits2Float(0x41dabbde), SkBits2Float(0x41dac1a8), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0xc24455cc), SkBits2Float(0x4197f43e)); +path.quadTo(SkBits2Float(0xc24fcffc), SkBits2Float(0x418ab179), SkBits2Float(0xc2533d5e), SkBits2Float(0x41622b92)); +path.quadTo(SkBits2Float(0xc256aac0), SkBits2Float(0x412ef432), SkBits2Float(0xc250095d), SkBits2Float(0x41010b70)); +path.quadTo(SkBits2Float(0xc24967fb), SkBits2Float(0x40a64560), SkBits2Float(0xc23c9a22), SkBits2Float(0x408ada54)); +path.quadTo(SkBits2Float(0xc22fcc4a), SkBits2Float(0x405ede90), SkBits2Float(0xc224521a), SkBits2Float(0x40a47a5c)); +path.quadTo(SkBits2Float(0xc218d7ea), SkBits2Float(0x40d9856e), SkBits2Float(0xc2156a88), SkBits2Float(0x411ffa16)); +path.quadTo(SkBits2Float(0xc211fd27), SkBits2Float(0x41533178), SkBits2Float(0xc2189e8a), SkBits2Float(0x41808d1c)); +path.quadTo(SkBits2Float(0xc21f3fec), SkBits2Float(0x4197817d), SkBits2Float(0xc22c0dc4), SkBits2Float(0x419e5c3f)); +path.quadTo(SkBits2Float(0xc238db9c), SkBits2Float(0x41a53702), SkBits2Float(0xc24455cc), SkBits2Float(0x4197f43e)); +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_4628016(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x41029678), SkBits2Float(0xc24ff9f4)); // 8.16174f, -51.9941f +path.quadTo(SkBits2Float(0x41308bcc), SkBits2Float(0xc25695e3), SkBits2Float(0x4163bca4), SkBits2Float(0xc253226d)); // 11.0341f, -53.6464f, 14.2336f, -52.7836f +path.quadTo(SkBits2Float(0x4164114c), SkBits2Float(0xc2531cb8), SkBits2Float(0x41646770), SkBits2Float(0xc25316ce)); // 14.2542f, -52.778f, 14.2753f, -52.7723f +path.quadTo(SkBits2Float(0x4164bf2b), SkBits2Float(0xc25310f7), SkBits2Float(0x4165153a), SkBits2Float(0xc2530b20)); // 14.2967f, -52.7666f, 14.3177f, -52.7609f +path.quadTo(SkBits2Float(0x418c2035), SkBits2Float(0xc24f9272), SkBits2Float(0x41994eb0), SkBits2Float(0xc244126c)); // 17.5157f, -51.893f, 19.1634f, -49.018f +path.quadTo(SkBits2Float(0x41a67d2c), SkBits2Float(0xc2389265), SkBits2Float(0x419f8bd0), SkBits2Float(0xc22bc798)); // 20.8111f, -46.143f, 19.9433f, -42.9449f +path.quadTo(SkBits2Float(0x41989a74), SkBits2Float(0xc21efccc), SkBits2Float(0x41819a68), SkBits2Float(0xc218658e)); // 19.0754f, -39.7469f, 16.2004f, -38.0992f +path.quadTo(SkBits2Float(0x415534b5), SkBits2Float(0xc211ce50), SkBits2Float(0x41220985), SkBits2Float(0xc21546ff)); // 13.3254f, -36.4515f, 10.1273f, -37.3193f +path.quadTo(SkBits2Float(0x4121cc4c), SkBits2Float(0xc2154b26), SkBits2Float(0x41218f63), SkBits2Float(0xc2154f56)); // 10.1124f, -37.3234f, 10.0975f, -37.3275f +path.quadTo(SkBits2Float(0x412152b0), SkBits2Float(0xc2155360), SkBits2Float(0x412115c8), SkBits2Float(0xc215577b)); // 10.0827f, -37.3314f, 10.0678f, -37.3354f +path.quadTo(SkBits2Float(0x40dbc9e2), SkBits2Float(0xc218caf0), SkBits2Float(0x40a6ea6c), SkBits2Float(0xc2244845)); // 6.86839f, -38.1982f, 5.21612f, -41.0706f +path.quadTo(SkBits2Float(0x406415f0), SkBits2Float(0xc22fc59a), SkBits2Float(0x408da6a4), SkBits2Float(0xc23c91d0)); // 3.56384f, -43.943f, 4.42659f, -47.1424f +path.quadTo(SkBits2Float(0x40a94248), SkBits2Float(0xc2495e06), SkBits2Float(0x41029678), SkBits2Float(0xc24ff9f4)); // 5.28934f, -50.3418f, 8.16174f, -51.9941f +path.close(); +path.moveTo(SkBits2Float(0xc219e2da), SkBits2Float(0xc21b5d7a)); // -38.4715f, -38.8413f +path.quadTo(SkBits2Float(0xc2107806), SkBits2Float(0xc224b15c), SkBits2Float(0xc20336d3), SkBits2Float(0xc224a121)); // -36.1172f, -41.1732f, -32.8035f, -41.1574f +path.quadTo(SkBits2Float(0xc1ebeb41), SkBits2Float(0xc22490e7), SkBits2Float(0xc1d9437f), SkBits2Float(0xc21b2612)); // -29.4899f, -41.1415f, -27.158f, -38.7872f +path.quadTo(SkBits2Float(0xc1c69bbd), SkBits2Float(0xc211bb3f), SkBits2Float(0xc1c6bc32), SkBits2Float(0xc2047a0c)); // -24.826f, -36.4329f, -24.8419f, -33.1192f +path.quadTo(SkBits2Float(0xc1c6dca7), SkBits2Float(0xc1ee71b1), SkBits2Float(0xc1d9b24f), SkBits2Float(0xc1dbc9ef)); // -24.8577f, -29.8055f, -27.2121f, -27.4736f +path.quadTo(SkBits2Float(0xc1ec87f7), SkBits2Float(0xc1c9222d), SkBits2Float(0xc203852e), SkBits2Float(0xc1c942a2)); // -29.5664f, -25.1417f, -32.8801f, -25.1575f +path.quadTo(SkBits2Float(0xc210c661), SkBits2Float(0xc1c96317), SkBits2Float(0xc21a1a42), SkBits2Float(0xc1dc38bf)); // -36.1937f, -25.1734f, -38.5256f, -27.5277f +path.quadTo(SkBits2Float(0xc2236e23), SkBits2Float(0xc1ef0e67), SkBits2Float(0xc2235de9), SkBits2Float(0xc204c867)); // -40.8576f, -29.882f, -40.8417f, -33.1957f +path.quadTo(SkBits2Float(0xc2234dae), SkBits2Float(0xc212099a), SkBits2Float(0xc219e2da), SkBits2Float(0xc21b5d7a)); // -40.8259f, -36.5094f, -38.4715f, -38.8413f +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); // -19.799f, -19.799f +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); // -11.598f, -28, 0, -28 +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); // 11.598f, -28, 19.799f, -19.799f +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); // 28, -11.598f, 28, 0 +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); // 28, 11.598f, 19.799f, 19.799f +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); // 13.7163f, 25.8817f, 5.76477f, 27.4529f +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); // 8, 29.7635f, 8, 33 +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); // 8, 36.3137f, 5.65685f, 38.6569f +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); // 3.31371f, 41, 0, 41 +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); // -3.31371f, 41, -5.65685f, 38.6569f +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); // -8, 36.3137f, -8, 33 +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); // -8, 29.7635f, -5.76477f, 27.4529f +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); // -13.7163f, 25.8817f, -19.799f, 19.799f +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); // -28, 11.598f, -28, 0 +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); // -28, -11.598f, -19.799f, -19.799f +path.close(); +path.moveTo(SkBits2Float(0xc23ca6f0), SkBits2Float(0x40866f38)); // -47.163f, 4.20108f +path.quadTo(SkBits2Float(0xc22fd68a), SkBits2Float(0x4056a1f8), SkBits2Float(0xc2246156), SkBits2Float(0x40a0a0d4)); // -43.9595f, 3.35364f, -41.0951f, 5.01963f +path.quadTo(SkBits2Float(0xc218ec21), SkBits2Float(0x40d5f0aa), SkBits2Float(0xc2158859), SkBits2Float(0x411e39ec)); // -38.2306f, 6.68563f, -37.3832f, 9.88914f +path.quadTo(SkBits2Float(0xc2158055), SkBits2Float(0x411eb323), SkBits2Float(0xc2157870), SkBits2Float(0x411f2dc3)); // -37.3753f, 9.91873f, -37.3676f, 9.94867f +path.quadTo(SkBits2Float(0xc2157002), SkBits2Float(0x411fa855), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); // -37.3594f, 9.9786f, -37.3514f, 10.0083f +path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176)); // -36.4938f, 13.2091f, -38.1506f, 16.0788f +path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330)); // -39.8075f, 18.9486f, -43.0083f, 19.8062f +path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); // -46.2091f, 20.6639f, -49.0788f, 19.007f +path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); // -51.9486f, 17.3502f, -52.8062f, 14.1494f +path.quadTo(SkBits2Float(0xc2534522), SkBits2Float(0x4161b7a0), SkBits2Float(0xc253504c), SkBits2Float(0x41610a87)); // -52.8175f, 14.1073f, -52.8284f, 14.0651f +path.quadTo(SkBits2Float(0xc2535c1f), SkBits2Float(0x41605e45), SkBits2Float(0xc2536784), SkBits2Float(0x415fb1f8)); // -52.84f, 14.023f, -52.8511f, 13.9809f +path.quadTo(SkBits2Float(0xc256cb4c), SkBits2Float(0x412c7060), SkBits2Float(0xc2502151), SkBits2Float(0x40fd371c)); // -53.6985f, 10.7774f, -52.0325f, 7.91298f +path.quadTo(SkBits2Float(0xc2497756), SkBits2Float(0x40a18d74), SkBits2Float(0xc23ca6f0), SkBits2Float(0x40866f38)); // -50.3665f, 5.04852f, -47.163f, 4.20108f +path.close(); +path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); // 27.3431f, 27.3431f +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); // 29.6863f, 25, 33, 25 +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); // 36.3137f, 25, 38.6569f, 27.3431f +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); // 41, 29.6863f, 41, 33 +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); // 41, 36.3137f, 38.6569f, 38.6569f +path.quadTo(SkBits2Float(0x421a8115), SkBits2Float(0x421ac027), SkBits2Float(0x421a613d), SkBits2Float(0x421adf68)); // 38.6261f, 38.6876f, 38.595f, 38.7182f +path.quadTo(SkBits2Float(0x421a41da), SkBits2Float(0x421aff30), SkBits2Float(0x421a2234), SkBits2Float(0x421b1ea2)); // 38.5643f, 38.7492f, 38.5334f, 38.7799f +path.quadTo(SkBits2Float(0x4210bb30), SkBits2Float(0x4224765a), SkBits2Float(0x420379f7), SkBits2Float(0x42246b89)); // 36.1828f, 41.1156f, 32.8691f, 41.105f +path.quadTo(SkBits2Float(0x41ec717c), SkBits2Float(0x422460b7), SkBits2Float(0x41d9c20c), SkBits2Float(0x421af9b1)); // 29.5554f, 41.0944f, 27.2197f, 38.7438f +path.quadTo(SkBits2Float(0x41c7129d), SkBits2Float(0x421192ad), SkBits2Float(0x41c72841), SkBits2Float(0x42045174)); // 24.8841f, 36.3932f, 24.8947f, 33.0795f +path.quadTo(SkBits2Float(0x41c73de4), SkBits2Float(0x41ee2077), SkBits2Float(0x41da0bed), SkBits2Float(0x41db7107)); // 24.9052f, 29.7659f, 27.2558f, 27.4302f +path.quadTo(SkBits2Float(0x41da38d6), SkBits2Float(0x41db4467), SkBits2Float(0x41da65bd), SkBits2Float(0x41db185f)); // 27.2778f, 27.4084f, 27.2997f, 27.3869f +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41edbcc3), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); // 25, 29.7172f, 25, 33 +path.lineTo(SkBits2Float(0x41da65bd), SkBits2Float(0x41db185f)); // 27.2997f, 27.3869f +path.lineTo(SkBits2Float(0x41da65c4), SkBits2Float(0x41db1859)); // 27.2997f, 27.3869f +path.quadTo(SkBits2Float(0x41da921e), SkBits2Float(0x41daeb68), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); // 27.3213f, 27.3649f, 27.3431f, 27.3431f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4223940b), SkBits2Float(0x420485b1)); // 40.8946f, 33.1306f +path.quadTo(SkBits2Float(0x4223893a), SkBits2Float(0x4211c6ea), SkBits2Float(0x421a2234), SkBits2Float(0x421b1ea2)); // 40.884f, 36.4443f, 38.5334f, 38.7799f +path.quadTo(SkBits2Float(0x4210bb30), SkBits2Float(0x4224765a), SkBits2Float(0x420379f7), SkBits2Float(0x42246b89)); // 36.1828f, 41.1156f, 32.8691f, 41.105f +path.quadTo(SkBits2Float(0x41ec717c), SkBits2Float(0x422460b7), SkBits2Float(0x41d9c20c), SkBits2Float(0x421af9b1)); // 29.5554f, 41.0944f, 27.2197f, 38.7438f +path.quadTo(SkBits2Float(0x41c7129d), SkBits2Float(0x421192ad), SkBits2Float(0x41c72841), SkBits2Float(0x42045174)); // 24.8841f, 36.3932f, 24.8947f, 33.0795f +path.quadTo(SkBits2Float(0x41c73de4), SkBits2Float(0x41ee2077), SkBits2Float(0x41da0bed), SkBits2Float(0x41db7107)); // 24.9052f, 29.7659f, 27.2558f, 27.4302f +path.quadTo(SkBits2Float(0x41ecd9f7), SkBits2Float(0x41c8c198), SkBits2Float(0x4203ae34), SkBits2Float(0x41c8d73b)); // 29.6064f, 25.0945f, 32.9201f, 25.1051f +path.quadTo(SkBits2Float(0x4210ef6d), SkBits2Float(0x41c8ecdf), SkBits2Float(0x421a4725), SkBits2Float(0x41dbbae8)); // 36.2338f, 25.1157f, 38.5695f, 27.4663f +path.quadTo(SkBits2Float(0x42239edd), SkBits2Float(0x41ee88f2), SkBits2Float(0x4223940b), SkBits2Float(0x420485b1)); // 40.9051f, 29.8169f, 40.8946f, 33.1306f +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_6411089(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x410373c2), SkBits2Float(0xc24ff13e)); +path.quadTo(SkBits2Float(0x4131701e), SkBits2Float(0xc2568a1e), SkBits2Float(0x41649d46), SkBits2Float(0xc2531340)); +path.quadTo(SkBits2Float(0x418be539), SkBits2Float(0xc24f9c64), SkBits2Float(0x419916f9), SkBits2Float(0xc2441d4e)); +path.quadTo(SkBits2Float(0x41a648b8), SkBits2Float(0xc2389e37), SkBits2Float(0x419f5afe), SkBits2Float(0xc22bd2ec)); +path.quadTo(SkBits2Float(0x41986d46), SkBits2Float(0xc21f07a2), SkBits2Float(0x41816f18), SkBits2Float(0xc2186ec2)); +path.quadTo(SkBits2Float(0x4154e1d6), SkBits2Float(0xc211d5e2), SkBits2Float(0x4121b4ad), SkBits2Float(0xc2154cbf)); +path.quadTo(SkBits2Float(0x40dd0f06), SkBits2Float(0xc218c39c), SkBits2Float(0x40a84808), SkBits2Float(0xc22442b2)); +path.quadTo(SkBits2Float(0x40670210), SkBits2Float(0xc22fc1c9), SkBits2Float(0x408f37ec), SkBits2Float(0xc23c8d13)); +path.quadTo(SkBits2Float(0x40aaeed0), SkBits2Float(0xc249585e), SkBits2Float(0x410373c2), SkBits2Float(0xc24ff13e)); +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.close(); +path.moveTo(SkBits2Float(0xc23ca48b), SkBits2Float(0x408745a4)); +path.quadTo(SkBits2Float(0xc22fd4a1), SkBits2Float(0x405831a8), SkBits2Float(0xc2245e7a), SkBits2Float(0x40a15ba4)); +path.quadTo(SkBits2Float(0xc218e853), SkBits2Float(0x40d69e76), SkBits2Float(0xc21582b9), SkBits2Float(0x411e8ee7)); +path.quadTo(SkBits2Float(0xc2121d1f), SkBits2Float(0x4151ce91), SkBits2Float(0xc218c57a), SkBits2Float(0x417fa72d)); +path.quadTo(SkBits2Float(0xc21f6dd4), SkBits2Float(0x4196bfe4), SkBits2Float(0xc22c3dbe), SkBits2Float(0x419d8b16)); +path.quadTo(SkBits2Float(0xc2390da9), SkBits2Float(0x41a4564c), SkBits2Float(0xc24483d0), SkBits2Float(0x41970597)); +path.quadTo(SkBits2Float(0xc24ff9f7), SkBits2Float(0x4189b4e3), SkBits2Float(0xc2535f90), SkBits2Float(0x41602a18)); +path.quadTo(SkBits2Float(0xc256c52a), SkBits2Float(0x412cea70), SkBits2Float(0xc2501cd0), SkBits2Float(0x40fe23a8)); +path.quadTo(SkBits2Float(0xc2497476), SkBits2Float(0x40a27270), SkBits2Float(0xc23ca48b), SkBits2Float(0x408745a4)); +path.close(); +path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x421a8005), SkBits2Float(0x421ac137), SkBits2Float(0x421a5f6b), SkBits2Float(0x421ae132)); +path.lineTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.close(); +path.moveTo(SkBits2Float(0x421a52fa), SkBits2Float(0x421aed60)); +path.quadTo(SkBits2Float(0x421a59d5), SkBits2Float(0x421ae6ac), SkBits2Float(0x421a5f6b), SkBits2Float(0x421ae132)); +path.quadTo(SkBits2Float(0x421a5a2a), SkBits2Float(0x421ae65a), SkBits2Float(0x421a52fa), SkBits2Float(0x421aed60)); +path.close(); +path.moveTo(SkBits2Float(0x421a52fa), SkBits2Float(0x421aed60)); +path.quadTo(SkBits2Float(0x42110a85), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41edcbe0), SkBits2Float(0x41da509a), SkBits2Float(0x41db2dd4)); +path.quadTo(SkBits2Float(0x41da18a0), SkBits2Float(0x41db6476), SkBits2Float(0x41d9e121), SkBits2Float(0x41db9b85)); +path.quadTo(SkBits2Float(0x41c70f73), SkBits2Float(0x41ee474a), SkBits2Float(0x41c6f4a5), SkBits2Float(0x420464db)); +path.quadTo(SkBits2Float(0x41c6d9d6), SkBits2Float(0x4211a612), SkBits2Float(0x41d9859b), SkBits2Float(0x421b0ee8)); +path.quadTo(SkBits2Float(0x41ec315f), SkBits2Float(0x422477bf), SkBits2Float(0x420359e6), SkBits2Float(0x42248527)); +path.quadTo(SkBits2Float(0x42109b1d), SkBits2Float(0x4224928e), SkBits2Float(0x421a03f3), SkBits2Float(0x421b3cab)); +path.quadTo(SkBits2Float(0x421a2b68), SkBits2Float(0x421b1585), SkBits2Float(0x421a5203), SkBits2Float(0x421aee52)); +path.quadTo(SkBits2Float(0x421a527f), SkBits2Float(0x421aedd9), SkBits2Float(0x421a52fa), SkBits2Float(0x421aed60)); +path.close(); +path.moveTo(SkBits2Float(0xc1810850), SkBits2Float(0x4218848a)); +path.quadTo(SkBits2Float(0xc1541d2b), SkBits2Float(0x4211e7ca), SkBits2Float(0xc120eb59), SkBits2Float(0x42155a57)); +path.quadTo(SkBits2Float(0xc0db730e), SkBits2Float(0x4218cce3), SkBits2Float(0xc0a68d10), SkBits2Float(0x422449c0)); +path.quadTo(SkBits2Float(0xc0634e28), SkBits2Float(0x422fc69d), SkBits2Float(0xc08d3b78), SkBits2Float(0x423c9311)); +path.quadTo(SkBits2Float(0xc0a8cfdc), SkBits2Float(0x42495f86), SkBits2Float(0xc1025b62), SkBits2Float(0x424ffc46)); +path.quadTo(SkBits2Float(0xc1304ed4), SkBits2Float(0x42569906), SkBits2Float(0xc16380a6), SkBits2Float(0x42532678)); +path.quadTo(SkBits2Float(0xc18b593d), SkBits2Float(0x424fb3ec), SkBits2Float(0xc19892bc), SkBits2Float(0x42443710)); +path.quadTo(SkBits2Float(0xc1a5cc3c), SkBits2Float(0x4238ba32), SkBits2Float(0xc19ee722), SkBits2Float(0x422bedbe)); +path.quadTo(SkBits2Float(0xc1980208), SkBits2Float(0x421f214a), SkBits2Float(0xc1810850), SkBits2Float(0x4218848a)); +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); +path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); +path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec)); +path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200)); +path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); +path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176)); +path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330)); +path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_3283699(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x411032d0), SkBits2Float(0xc24f69e5)); +path.quadTo(SkBits2Float(0x413e956c), SkBits2Float(0xc255d56a), SkBits2Float(0x41718a9c), SkBits2Float(0xc2522c67)); +path.quadTo(SkBits2Float(0x41755c61), SkBits2Float(0xc251e62c), SkBits2Float(0x417909ca), SkBits2Float(0xc2519487)); +path.quadTo(SkBits2Float(0x417cd0ce), SkBits2Float(0xc2515870), SkBits2Float(0x41804eae), SkBits2Float(0xc2510dd3)); +path.quadTo(SkBits2Float(0x4199a689), SkBits2Float(0xc24d2a58), SkBits2Float(0x41a61251), SkBits2Float(0xc24174a0)); +path.quadTo(SkBits2Float(0x41b27e19), SkBits2Float(0xc235bee8), SkBits2Float(0x41aab721), SkBits2Float(0xc22912fa)); +path.quadTo(SkBits2Float(0x41a2f02a), SkBits2Float(0xc21c670e), SkBits2Float(0x418b84ba), SkBits2Float(0xc216312a)); +path.quadTo(SkBits2Float(0x41683297), SkBits2Float(0xc20ffb46), SkBits2Float(0x413582e0), SkBits2Float(0xc213dec1)); +path.quadTo(SkBits2Float(0x4132d4e6), SkBits2Float(0xc2141362), SkBits2Float(0x4130395d), SkBits2Float(0xc2144d9c)); +path.quadTo(SkBits2Float(0x412d8b06), SkBits2Float(0xc21477ef), SkBits2Float(0x412ad979), SkBits2Float(0xc214a976)); +path.quadTo(SkBits2Float(0x40efc890), SkBits2Float(0xc2185278), SkBits2Float(0x40bc6c64), SkBits2Float(0xc223eb20)); +path.quadTo(SkBits2Float(0x4089103c), SkBits2Float(0xc22f83c7), SkBits2Float(0x40a65850), SkBits2Float(0xc23c4113)); +path.quadTo(SkBits2Float(0x40c3a064), SkBits2Float(0xc248fe60), SkBits2Float(0x411032d0), SkBits2Float(0xc24f69e5)); +path.close(); +path.moveTo(SkBits2Float(0xc2121147), SkBits2Float(0xc222bcdb)); +path.quadTo(SkBits2Float(0xc208340d), SkBits2Float(0xc22b9769), SkBits2Float(0xc1f5ef7e), SkBits2Float(0xc22ae080)); +path.quadTo(SkBits2Float(0xc1db76e2), SkBits2Float(0xc22a2997), SkBits2Float(0xc1c9c1c6), SkBits2Float(0xc2204c5c)); +path.quadTo(SkBits2Float(0xc1b80ca9), SkBits2Float(0xc2166f21), SkBits2Float(0xc1b97a7c), SkBits2Float(0xc20932d3)); +path.quadTo(SkBits2Float(0xc1bae84e), SkBits2Float(0xc1f7ed0b), SkBits2Float(0xc1cea2c4), SkBits2Float(0xc1e637ee)); +path.quadTo(SkBits2Float(0xc1e25d39), SkBits2Float(0xc1d482d2), SkBits2Float(0xc1fcd5d5), SkBits2Float(0xc1d5f0a4)); +path.quadTo(SkBits2Float(0xc20ba739), SkBits2Float(0xc1d75e77), SkBits2Float(0xc21481c6), SkBits2Float(0xc1eb18ec)); +path.quadTo(SkBits2Float(0xc21d5c55), SkBits2Float(0xc1fed362), SkBits2Float(0xc21ca56c), SkBits2Float(0xc20ca5ff)); +path.quadTo(SkBits2Float(0xc21bee83), SkBits2Float(0xc219e24d), SkBits2Float(0xc2121147), SkBits2Float(0xc222bcdb)); +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.close(); +path.moveTo(SkBits2Float(0xc23d19f2), SkBits2Float(0x4029d790)); +path.quadTo(SkBits2Float(0xc2302ee1), SkBits2Float(0x3ff4b3e0), SkBits2Float(0xc224f322), SkBits2Float(0x406aec68)); +path.quadTo(SkBits2Float(0xc219b764), SkBits2Float(0x40adbf72), SkBits2Float(0xc216bf8a), SkBits2Float(0x410a8bfe)); +path.quadTo(SkBits2Float(0xc213c7b1), SkBits2Float(0x413e3841), SkBits2Float(0xc21ad0d8), SkBits2Float(0x416b273b)); +path.quadTo(SkBits2Float(0xc221da00), SkBits2Float(0x418c0b1a), SkBits2Float(0xc22ec511), SkBits2Float(0x4191facd)); +path.quadTo(SkBits2Float(0xc23bb022), SkBits2Float(0x4197ea80), SkBits2Float(0xc246ebe0), SkBits2Float(0x4189d830)); +path.quadTo(SkBits2Float(0xc252279f), SkBits2Float(0x41778bc2), SkBits2Float(0xc2551f78), SkBits2Float(0x4143df80)); +path.quadTo(SkBits2Float(0xc2581752), SkBits2Float(0x4110333c), SkBits2Float(0xc2510e2a), SkBits2Float(0x40c68884)); +path.quadTo(SkBits2Float(0xc24a0502), SkBits2Float(0x40595520), SkBits2Float(0xc23d19f2), SkBits2Float(0x4029d790)); +path.close(); +path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x421951ff), SkBits2Float(0x421bef3d), SkBits2Float(0x4217f013), SkBits2Float(0x421d0f2c)); +path.lineTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.close(); +path.moveTo(SkBits2Float(0x4217c478), SkBits2Float(0x421d326a)); +path.quadTo(SkBits2Float(0x4217da16), SkBits2Float(0x421d2110), SkBits2Float(0x4217f013), SkBits2Float(0x421d0f2c)); +path.quadTo(SkBits2Float(0x4217da0a), SkBits2Float(0x421d2119), SkBits2Float(0x4217c478), SkBits2Float(0x421d326a)); +path.close(); +path.moveTo(SkBits2Float(0x4217b2af), SkBits2Float(0x421d40a6)); +path.quadTo(SkBits2Float(0x4217bc6a), SkBits2Float(0x421d38e1), SkBits2Float(0x4217c478), SkBits2Float(0x421d326a)); +path.quadTo(SkBits2Float(0x4217bc41), SkBits2Float(0x421d3902), SkBits2Float(0x4217b2af), SkBits2Float(0x421d40a6)); +path.close(); +path.moveTo(SkBits2Float(0x4217b2af), SkBits2Float(0x421d40a6)); +path.quadTo(SkBits2Float(0x42167086), SkBits2Float(0x421eb7af), SkBits2Float(0x4214f81f), SkBits2Float(0x422015f3)); +path.quadTo(SkBits2Float(0x420b440b), SkBits2Float(0x42291d88), SkBits2Float(0x41fc09ff), SkBits2Float(0x4228a38f)); +path.quadTo(SkBits2Float(0x41e18be8), SkBits2Float(0x42282996), SkBits2Float(0x41cf7cbe), SkBits2Float(0x421e7581)); +path.quadTo(SkBits2Float(0x41bd6d94), SkBits2Float(0x4214c16e), SkBits2Float(0x41be6187), SkBits2Float(0x42078262)); +path.quadTo(SkBits2Float(0x41bf557a), SkBits2Float(0x41f486ac), SkBits2Float(0x41d2bda2), SkBits2Float(0x41e27782)); +path.quadTo(SkBits2Float(0x41d4cf6c), SkBits2Float(0x41e08a82), SkBits2Float(0x41d6f5de), SkBits2Float(0x41ded4d5)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41f0513a), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0x420f3f16), SkBits2Float(0x42240000), SkBits2Float(0x4217b2af), SkBits2Float(0x421d40a6)); +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); +path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); +path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec)); +path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200)); +path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); +path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176)); +path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330)); +path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_1026368(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x4101c02c), SkBits2Float(0xc2500256)); // 8.10942f, -52.0023f +path.quadTo(SkBits2Float(0x412faeae), SkBits2Float(0xc256a13a), SkBits2Float(0x4162e312), SkBits2Float(0xc2533110)); // 10.9801f, -53.6574f, 14.1804f, -52.7979f +path.quadTo(SkBits2Float(0x418b0bbc), SkBits2Float(0xc24fc0e8), SkBits2Float(0x41984984), SkBits2Float(0xc2444547)); // 17.3807f, -51.9384f, 19.0359f, -49.0677f +path.quadTo(SkBits2Float(0x41a5874c), SkBits2Float(0xc238c9a6), SkBits2Float(0x419ea6f8), SkBits2Float(0xc22bfc8c)); // 20.6911f, -46.1969f, 19.8315f, -42.9966f +path.quadTo(SkBits2Float(0x4197c6a7), SkBits2Float(0xc21f2f74), SkBits2Float(0x4180cf66), SkBits2Float(0xc2189090)); // 18.972f, -39.7963f, 16.1013f, -38.1412f +path.quadTo(SkBits2Float(0x4153b048), SkBits2Float(0xc211f1ac), SkBits2Float(0x41207be4), SkBits2Float(0xc21561d5)); // 13.2305f, -36.486f, 10.0302f, -37.3455f +path.quadTo(SkBits2Float(0x40da8f00), SkBits2Float(0xc218d1fe), SkBits2Float(0x40a597e0), SkBits2Float(0xc2244d9f)); // 6.82996f, -38.2051f, 5.17479f, -41.0758f +path.quadTo(SkBits2Float(0x40614180), SkBits2Float(0xc22fc940), SkBits2Float(0x408c220c), SkBits2Float(0xc23c9658)); // 3.51962f, -43.9465f, 4.37916f, -47.1468f +path.quadTo(SkBits2Float(0x40a7a350), SkBits2Float(0xc2496372), SkBits2Float(0x4101c02c), SkBits2Float(0xc2500256)); // 5.23869f, -50.3471f, 8.10942f, -52.0023f +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); // -19.799f, -19.799f +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); // -11.598f, -28, 0, -28 +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); // 11.598f, -28, 19.799f, -19.799f +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); // 28, -11.598f, 28, 0 +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); // 28, 11.598f, 19.799f, 19.799f +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); // 13.7163f, 25.8817f, 5.76477f, 27.4529f +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); // 8, 29.7635f, 8, 33 +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); // 8, 36.3137f, 5.65685f, 38.6569f +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); // 3.31371f, 41, 0, 41 +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); // -3.31371f, 41, -5.65685f, 38.6569f +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); // -8, 36.3137f, -8, 33 +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); // -8, 29.7635f, -5.76477f, 27.4529f +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); // -13.7163f, 25.8817f, -19.799f, 19.799f +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); // -28, 11.598f, -28, 0 +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); // -28, -11.598f, -19.799f, -19.799f +path.close(); +path.moveTo(SkBits2Float(0xc23c9b95), SkBits2Float(0x408a5bd8)); // -47.1519f, 4.32371f +path.quadTo(SkBits2Float(0xc22fcd74), SkBits2Float(0x405df2c8), SkBits2Float(0xc22453d3), SkBits2Float(0x40a40c28)); // -43.9506f, 3.46794f, -41.0819f, 5.12648f +path.quadTo(SkBits2Float(0xc218da31), SkBits2Float(0x40d91eee), SkBits2Float(0xc2156de2), SkBits2Float(0x411fc7fd)); // -38.2131f, 6.78503f, -37.3573f, 9.98633f +path.quadTo(SkBits2Float(0xc2120193), SkBits2Float(0x41530084), SkBits2Float(0xc218a3ec), SkBits2Float(0x41807386)); // -36.5015f, 13.1876f, -38.1601f, 16.0564f +path.quadTo(SkBits2Float(0xc21f4644), SkBits2Float(0x419766c9), SkBits2Float(0xc22c1466), SkBits2Float(0x419e3f66)); // -39.8186f, 18.9252f, -43.0199f, 19.781f +path.quadTo(SkBits2Float(0xc238e288), SkBits2Float(0x41a51804), SkBits2Float(0xc2445c2a), SkBits2Float(0x4197d353)); // -46.2212f, 20.6367f, -49.09f, 18.9782f +path.quadTo(SkBits2Float(0xc24fd5cc), SkBits2Float(0x418a8ea2), SkBits2Float(0xc253421a), SkBits2Float(0x4161e4ba)); // -51.9588f, 17.3196f, -52.8146f, 14.1183f +path.quadTo(SkBits2Float(0xc256ae69), SkBits2Float(0x412eac36), SkBits2Float(0xc2500c10), SkBits2Float(0x4100c5b0)); // -53.6703f, 10.917f, -52.0118f, 8.04826f +path.quadTo(SkBits2Float(0xc24969b8), SkBits2Float(0x40a5be50), SkBits2Float(0xc23c9b95), SkBits2Float(0x408a5bd8)); // -50.3532f, 5.17948f, -47.1519f, 4.32371f +path.close(); +path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); // 27.3431f, 27.3431f +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); // 29.6863f, 25, 33, 25 +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); // 36.3137f, 25, 38.6569f, 27.3431f +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); // 41, 29.6863f, 41, 33 +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); // 41, 36.3137f, 38.6569f, 38.6569f +path.quadTo(SkBits2Float(0x421a9aa6), SkBits2Float(0x421aa696), SkBits2Float(0x421a9438), SkBits2Float(0x421aacfe)); // 38.651f, 38.6627f, 38.6447f, 38.6689f +path.quadTo(SkBits2Float(0x421a8ec8), SkBits2Float(0x421ab270), SkBits2Float(0x421a8956), SkBits2Float(0x421ab7e2)); // 38.6394f, 38.6743f, 38.6341f, 38.6796f +path.quadTo(SkBits2Float(0x421a8386), SkBits2Float(0x421abdb2), SkBits2Float(0x421a7db3), SkBits2Float(0x421ac381)); // 38.6284f, 38.6852f, 38.6228f, 38.6909f +path.quadTo(SkBits2Float(0x42111c34), SkBits2Float(0x422420c6), SkBits2Float(0x4203daf7), SkBits2Float(0x42241dc8)); // 36.2775f, 41.032f, 32.9638f, 41.0291f +path.quadTo(SkBits2Float(0x41ed3376), SkBits2Float(0x42241aca), SkBits2Float(0x41da78ee), SkBits2Float(0x421ab94a)); // 29.6501f, 41.0262f, 27.309f, 38.6809f +path.quadTo(SkBits2Float(0x41c7be68), SkBits2Float(0x421157cc), SkBits2Float(0x41c7c464), SkBits2Float(0x4204168f)); // 24.968f, 36.3357f, 24.9709f, 33.022f +path.quadTo(SkBits2Float(0x41c7ca5f), SkBits2Float(0x41edaaa5), SkBits2Float(0x41da8d5d), SkBits2Float(0x41daf01d)); // 24.9738f, 29.7083f, 27.319f, 27.3672f +path.quadTo(SkBits2Float(0x41da915b), SkBits2Float(0x41daec21), SkBits2Float(0x41da955a), SkBits2Float(0x41dae825)); // 27.321f, 27.3653f, 27.3229f, 27.3634f +path.quadTo(SkBits2Float(0x41da9997), SkBits2Float(0x41dae3e8), SkBits2Float(0x41da9dd4), SkBits2Float(0x41dadfac)); // 27.325f, 27.3613f, 27.3271f, 27.3592f +path.quadTo(SkBits2Float(0x41daa667), SkBits2Float(0x41dad71b), SkBits2Float(0x41daaefc), SkBits2Float(0x41dace8e)); // 27.3313f, 27.355f, 27.3354f, 27.3509f +path.quadTo(SkBits2Float(0x41daaa91), SkBits2Float(0x41dad2fb), SkBits2Float(0x41daa628), SkBits2Float(0x41dad768)); // 27.3333f, 27.353f, 27.3311f, 27.3552f +path.quadTo(SkBits2Float(0x41dab217), SkBits2Float(0x41dacb89), SkBits2Float(0x41dabedf), SkBits2Float(0x41dabedf)); // 27.337f, 27.3494f, 27.3432f, 27.3432f +path.lineTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); // 27.3431f, 27.3431f +path.close(); +path.moveTo(SkBits2Float(0xc180b86e), SkBits2Float(0x42189568)); // -16.0901f, 38.1459f +path.quadTo(SkBits2Float(0xc1538459), SkBits2Float(0x4211f5a8), SkBits2Float(0xc1204eec), SkBits2Float(0x421564da)); // -13.2198f, 36.4899f, -10.0193f, 37.3485f +path.quadTo(SkBits2Float(0xc0da32fc), SkBits2Float(0x4218d40d), SkBits2Float(0xc0a534f4), SkBits2Float(0x42244f2e)); // -6.81872f, 38.2071f, -5.16271f, 41.0773f +path.quadTo(SkBits2Float(0xc0606dd0), SkBits2Float(0x422fca4f), SkBits2Float(0xc08bb080), SkBits2Float(0x423c97aa)); // -3.5067f, 43.9476f, -4.3653f, 47.1481f +path.quadTo(SkBits2Float(0xc0a72a14), SkBits2Float(0x42496506), SkBits2Float(0xc101818e), SkBits2Float(0x425004c7)); // -5.22389f, 50.3487f, -8.09413f, 52.0047f +path.quadTo(SkBits2Float(0xc12f6e12), SkBits2Float(0x4256a488), SkBits2Float(0xc162a37e), SkBits2Float(0x42533554)); // -10.9644f, 53.6607f, -14.1649f, 52.8021f +path.quadTo(SkBits2Float(0xc18aec77), SkBits2Float(0x424fc622), SkBits2Float(0xc1982bfa), SkBits2Float(0x42444b02)); // -17.3655f, 51.9435f, -19.0215f, 49.0732f +path.quadTo(SkBits2Float(0xc1a56b7c), SkBits2Float(0x4238cfe0), SkBits2Float(0xc19e8d15), SkBits2Float(0x422c0285)); // -20.6775f, 46.203f, -19.8189f, 43.0025f +path.quadTo(SkBits2Float(0xc197aeb0), SkBits2Float(0x421f352a), SkBits2Float(0xc180b86e), SkBits2Float(0x42189568)); // -18.9603f, 39.8019f, -16.0901f, 38.1459f +path.close(); +path.moveTo(SkBits2Float(0x421a8f2f), SkBits2Float(0x421ab201)); // 38.6398f, 38.6738f +path.lineTo(SkBits2Float(0x421a9433), SkBits2Float(0x421aacf9)); // 38.6447f, 38.6689f +path.lineTo(SkBits2Float(0x421a9438), SkBits2Float(0x421aacfe)); // 38.6447f, 38.6689f +path.quadTo(SkBits2Float(0x421a91b4), SkBits2Float(0x421aaf80), SkBits2Float(0x421a8f2f), SkBits2Float(0x421ab201)); // 38.6423f, 38.6714f, 38.6398f, 38.6738f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42240000), SkBits2Float(0x42040000)); // 41, 33 +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); // 41, 36.3137f, 38.6569f, 38.6569f +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); // 36.3137f, 41, 33, 41 +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); // 29.6863f, 41, 27.3431f, 38.6569f +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); // 25, 36.3137f, 25, 33 +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); // 25, 29.6863f, 27.3431f, 27.3431f +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); // 29.6863f, 25, 33, 25 +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); // 36.3137f, 25, 38.6569f, 27.3431f +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); // 41, 29.6863f, 41, 33 +path.close(); + + SkPath path2(path); + // DEBUG_UNDER_DEVELOPMENT fuzz763_1026368 disable expectation check for now + testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, !SkOpGlobalState::DebugRunFail()); +} + +static void fuzz763_5485218(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0xc1b1a434), SkBits2Float(0xc247d348)); +path.quadTo(SkBits2Float(0xc1996ac1), SkBits2Float(0xc24d3588), SkBits2Float(0xc180ac87), SkBits2Float(0xc248738e)); +path.quadTo(SkBits2Float(0xc14fdc9c), SkBits2Float(0xc243b194), SkBits2Float(0xc13a53a0), SkBits2Float(0xc23794da)); +path.quadTo(SkBits2Float(0xc124caa4), SkBits2Float(0xc22b7821), SkBits2Float(0xc137d28c), SkBits2Float(0xc21f1904)); +path.quadTo(SkBits2Float(0xc14ada74), SkBits2Float(0xc212b9e7), SkBits2Float(0xc17b4d59), SkBits2Float(0xc20d57a8)); +path.quadTo(SkBits2Float(0xc195e020), SkBits2Float(0xc207f569), SkBits2Float(0xc1ae9e58), SkBits2Float(0xc20cb763)); +path.quadTo(SkBits2Float(0xc1c75c92), SkBits2Float(0xc211795d), SkBits2Float(0xc1d22110), SkBits2Float(0xc21d9616)); +path.quadTo(SkBits2Float(0xc1dce590), SkBits2Float(0xc229b2d0), SkBits2Float(0xc1d3619c), SkBits2Float(0xc23611ec)); +path.quadTo(SkBits2Float(0xc1c9dda8), SkBits2Float(0xc242710a), SkBits2Float(0xc1b1a434), SkBits2Float(0xc247d348)); +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x40b3b5dc), SkBits2Float(0x421aca81), SkBits2Float(0x40b26487), SkBits2Float(0x421af3a3)); +path.quadTo(SkBits2Float(0x40eb2464), SkBits2Float(0x421ca9a9), SkBits2Float(0x410d8414), SkBits2Float(0x4221c755)); +path.quadTo(SkBits2Float(0x4135d252), SkBits2Float(0x422a63dc), SkBits2Float(0x4139f710), SkBits2Float(0x42379ab7)); +path.quadTo(SkBits2Float(0x413e1bd4), SkBits2Float(0x4244d193), SkBits2Float(0x411ba9b8), SkBits2Float(0x424ee522)); +path.quadTo(SkBits2Float(0x40f26f3c), SkBits2Float(0x4258f8b2), SkBits2Float(0x4088b85c), SkBits2Float(0x425a01e2)); +path.quadTo(SkBits2Float(0x3f780c00), SkBits2Float(0x425b0b12), SkBits2Float(0xbfc66bf0), SkBits2Float(0x42526e8c)); +path.quadTo(SkBits2Float(0xc0823778), SkBits2Float(0x4249d205), SkBits2Float(0xc08a80f8), SkBits2Float(0x423c9b29)); +path.quadTo(SkBits2Float(0xc092ca7c), SkBits2Float(0x422f644e), SkBits2Float(0xc01bcc90), SkBits2Float(0x422550be)); +path.quadTo(SkBits2Float(0xc00c0f96), SkBits2Float(0x42242a18), SkBits2Float(0xbff6b9b1), SkBits2Float(0x422321a7)); +path.quadTo(SkBits2Float(0xc080dd2a), SkBits2Float(0x42212597), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.close(); +path.moveTo(SkBits2Float(0x422e1dec), SkBits2Float(0xc1951160)); +path.quadTo(SkBits2Float(0x423b0222), SkBits2Float(0xc19b3b8f), SkBits2Float(0x42464db6), SkBits2Float(0xc18d5c4c)); +path.quadTo(SkBits2Float(0x42519948), SkBits2Float(0xc17efa12), SkBits2Float(0x4254ae60), SkBits2Float(0xc14b693a)); +path.quadTo(SkBits2Float(0x4257c378), SkBits2Float(0xc117d862), SkBits2Float(0x4250d3d6), SkBits2Float(0xc0d5542c)); +path.quadTo(SkBits2Float(0x4249e434), SkBits2Float(0xc075ef28), SkBits2Float(0x423cfffe), SkBits2Float(0xc0449db8)); +path.quadTo(SkBits2Float(0x423cdcf0), SkBits2Float(0xc044179d), SkBits2Float(0x423cb9cd), SkBits2Float(0xc04395ab)); +path.quadTo(SkBits2Float(0x423cdda4), SkBits2Float(0xc0435d6a), SkBits2Float(0x423d018a), SkBits2Float(0xc04320b0)); +path.quadTo(SkBits2Float(0x424a2ff8), SkBits2Float(0xc02cd470), SkBits2Float(0x425285c7), SkBits2Float(0xbdfdd900)); +path.quadTo(SkBits2Float(0x425adb97), SkBits2Float(0x401cf6e8), SkBits2Float(0x425976d3), SkBits2Float(0x40b7eee0)); +path.quadTo(SkBits2Float(0x42581210), SkBits2Float(0x4110b128), SkBits2Float(0x424dc3b4), SkBits2Float(0x41320868)); +path.quadTo(SkBits2Float(0x4243755a), SkBits2Float(0x41535fa6), SkBits2Float(0x423646ec), SkBits2Float(0x414dcc96)); +path.quadTo(SkBits2Float(0x4229187e), SkBits2Float(0x41483989), SkBits2Float(0x4220c2ae), SkBits2Float(0x411f001d)); +path.quadTo(SkBits2Float(0x42186cde), SkBits2Float(0x40eb8d66), SkBits2Float(0x4219d1a2), SkBits2Float(0x408219f4)); +path.quadTo(SkBits2Float(0x421b3666), SkBits2Float(0x3f453420), SkBits2Float(0x422584c1), SkBits2Float(0xbfa81fe0)); +path.quadTo(SkBits2Float(0x422b4324), SkBits2Float(0xc01e631a), SkBits2Float(0x4231e626), SkBits2Float(0xc0385d42)); +path.quadTo(SkBits2Float(0x422b1bb8), SkBits2Float(0xc0446d07), SkBits2Float(0x4224d036), SkBits2Float(0xc0812328)); +path.quadTo(SkBits2Float(0x421984a2), SkBits2Float(0xc0b8a034), SkBits2Float(0x42166f8b), SkBits2Float(0xc10fe0f3)); +path.quadTo(SkBits2Float(0x42135a74), SkBits2Float(0xc14371ca), SkBits2Float(0x421a4a15), SkBits2Float(0xc170a016)); +path.quadTo(SkBits2Float(0x422139b6), SkBits2Float(0xc18ee731), SkBits2Float(0x422e1dec), SkBits2Float(0xc1951160)); +path.close(); +path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200)); +path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); +path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176)); +path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330)); +path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); +path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); +path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec)); +path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.close(); +path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.close(); +path.moveTo(SkBits2Float(0xc1e59c15), SkBits2Float(0x41eeb1f9)); +path.quadTo(SkBits2Float(0xc1cb6c81), SkBits2Float(0x41ea9074), SkBits2Float(0xc1b5fc96), SkBits2Float(0x41fa28d2)); +path.quadTo(SkBits2Float(0xc1a08cac), SkBits2Float(0x4204e098), SkBits2Float(0xc19c6b27), SkBits2Float(0x4211f862)); +path.quadTo(SkBits2Float(0xc19849a3), SkBits2Float(0x421f102b), SkBits2Float(0xc1a7e200), SkBits2Float(0x4229c820)); +path.quadTo(SkBits2Float(0xc1b77a5e), SkBits2Float(0x42348016), SkBits2Float(0xc1d1a9f2), SkBits2Float(0x423690d8)); +path.quadTo(SkBits2Float(0xc1ebd984), SkBits2Float(0x4238a19a), SkBits2Float(0xc200a4b7), SkBits2Float(0x4230d56b)); +path.quadTo(SkBits2Float(0xc20b5cae), SkBits2Float(0x4229093c), SkBits2Float(0xc20d6d70), SkBits2Float(0x421bf173)); +path.quadTo(SkBits2Float(0xc20f7e32), SkBits2Float(0x420ed9a9), SkBits2Float(0xc207b202), SkBits2Float(0x420421b4)); +path.quadTo(SkBits2Float(0xc1ffcba7), SkBits2Float(0x41f2d37d), SkBits2Float(0xc1e59c15), SkBits2Float(0x41eeb1f9)); +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x425285c7), SkBits2Float(0xbdfdd900)); +path.quadTo(SkBits2Float(0x425adb97), SkBits2Float(0x401cf6e8), SkBits2Float(0x425976d3), SkBits2Float(0x40b7eee0)); +path.quadTo(SkBits2Float(0x42581210), SkBits2Float(0x4110b128), SkBits2Float(0x424dc3b4), SkBits2Float(0x41320868)); +path.quadTo(SkBits2Float(0x4243755a), SkBits2Float(0x41535fa6), SkBits2Float(0x423646ec), SkBits2Float(0x414dcc96)); +path.quadTo(SkBits2Float(0x4229187e), SkBits2Float(0x41483989), SkBits2Float(0x4220c2ae), SkBits2Float(0x411f001d)); +path.quadTo(SkBits2Float(0x42186cde), SkBits2Float(0x40eb8d66), SkBits2Float(0x4219d1a2), SkBits2Float(0x408219f4)); +path.quadTo(SkBits2Float(0x421b3666), SkBits2Float(0x3f453420), SkBits2Float(0x422584c1), SkBits2Float(0xbfa81fe0)); +path.quadTo(SkBits2Float(0x422fd31c), SkBits2Float(0xc0596cf0), SkBits2Float(0x423d018a), SkBits2Float(0xc04320b0)); +path.quadTo(SkBits2Float(0x424a2ff8), SkBits2Float(0xc02cd470), SkBits2Float(0x425285c7), SkBits2Float(0xbdfdd900)); +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_2674194(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0xbfb16e10), SkBits2Float(0xc252733b)); +path.quadTo(SkBits2Float(0x3f91df50), SkBits2Float(0xc25b07b4), SkBits2Float(0x408e27f4), SkBits2Float(0xc259f3f8)); +path.quadTo(SkBits2Float(0x40f7d814), SkBits2Float(0xc258e03e), SkBits2Float(0x411e3df0), SkBits2Float(0xc24ec5d2)); +path.quadTo(SkBits2Float(0x41408fd4), SkBits2Float(0xc244ab67), SkBits2Float(0x413c40e6), SkBits2Float(0xc2377562)); +path.quadTo(SkBits2Float(0x4137f1f8), SkBits2Float(0xc22a3f5f), SkBits2Float(0x410f884c), SkBits2Float(0xc221aae6)); +path.quadTo(SkBits2Float(0x40ce3d3c), SkBits2Float(0xc219166c), SkBits2Float(0x40491a38), SkBits2Float(0xc21a2a28)); +path.quadTo(SkBits2Float(0xbe246080), SkBits2Float(0xc21b3de4), SkBits2Float(0xc0138d98), SkBits2Float(0xc225584f)); +path.quadTo(SkBits2Float(0xc08e6a98), SkBits2Float(0xc22f72ba), SkBits2Float(0xc085ccbc), SkBits2Float(0xc23ca8be)); +path.quadTo(SkBits2Float(0xc07a5dc0), SkBits2Float(0xc249dec2), SkBits2Float(0xbfb16e10), SkBits2Float(0xc252733b)); +path.close(); +path.moveTo(SkBits2Float(0x41b47dea), SkBits2Float(0xc23e32f4)); +path.quadTo(SkBits2Float(0x41ce465c), SkBits2Float(0xc24147ff), SkBits2Float(0x41e4dd74), SkBits2Float(0xc23a5854)); +path.quadTo(SkBits2Float(0x41fb748e), SkBits2Float(0xc23368a8), SkBits2Float(0x4200cf52), SkBits2Float(0xc226846f)); +path.quadTo(SkBits2Float(0x42022efd), SkBits2Float(0xc220c591), SkBits2Float(0x42019135), SkBits2Float(0xc21b57df)); +path.quadTo(SkBits2Float(0x4206cc04), SkBits2Float(0xc21cec81), SkBits2Float(0x420cb220), SkBits2Float(0xc21c9a86)); +path.quadTo(SkBits2Float(0x4219ee60), SkBits2Float(0xc21be297), SkBits2Float(0x4222c82a), SkBits2Float(0xc21204ac)); +path.quadTo(SkBits2Float(0x422ba1f5), SkBits2Float(0xc20826c2), SkBits2Float(0x422aea06), SkBits2Float(0xc1f5d504)); +path.quadTo(SkBits2Float(0x422a3216), SkBits2Float(0xc1db5c85), SkBits2Float(0x4220542b), SkBits2Float(0xc1c9a8f0)); +path.quadTo(SkBits2Float(0x42167641), SkBits2Float(0xc1b7f55b), SkBits2Float(0x42093a01), SkBits2Float(0xc1b9653a)); +path.quadTo(SkBits2Float(0x41f7fb83), SkBits2Float(0xc1bad519), SkBits2Float(0x41e647ee), SkBits2Float(0xc1ce90ee)); +path.quadTo(SkBits2Float(0x41d49459), SkBits2Float(0xc1e24cc3), SkBits2Float(0x41d60438), SkBits2Float(0xc1fcc542)); +path.quadTo(SkBits2Float(0x41d62223), SkBits2Float(0xc1feec5b), SkBits2Float(0x41d65f09), SkBits2Float(0xc2008251)); +path.quadTo(SkBits2Float(0x41d45a68), SkBits2Float(0xc200343d), SkBits2Float(0x41d2419c), SkBits2Float(0xc1ffe823)); +path.quadTo(SkBits2Float(0x41b8792a), SkBits2Float(0xc1f9be0c), SkBits2Float(0x41a1e211), SkBits2Float(0xc203ceb1)); +path.quadTo(SkBits2Float(0x418b4af8), SkBits2Float(0xc20abe5d), SkBits2Float(0x418520e1), SkBits2Float(0xc217a296)); +path.quadTo(SkBits2Float(0x417ded93), SkBits2Float(0xc22486cf), SkBits2Float(0x418cd620), SkBits2Float(0xc22fd25b)); +path.quadTo(SkBits2Float(0x419ab578), SkBits2Float(0xc23b1de8), SkBits2Float(0x41b47dea), SkBits2Float(0xc23e32f4)); +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); +path.lineTo(SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); +path.close(); +path.moveTo(SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x40b3bf9e), SkBits2Float(0x421ac949), SkBits2Float(0x40b278ad), SkBits2Float(0x421af12e)); +path.quadTo(SkBits2Float(0x40d4fa46), SkBits2Float(0x42187664), SkBits2Float(0x40ff010e), SkBits2Float(0x42175b2b)); +path.quadTo(SkBits2Float(0x41336223), SkBits2Float(0x42149fe4), SkBits2Float(0x415fcb81), SkBits2Float(0x421bdd50)); +path.quadTo(SkBits2Float(0x41861a70), SkBits2Float(0x42231aba), SkBits2Float(0x418b90fc), SkBits2Float(0x42301322)); +path.quadTo(SkBits2Float(0x4191078a), SkBits2Float(0x423d0b88), SkBits2Float(0x41828cb4), SkBits2Float(0x424825e0)); +path.quadTo(SkBits2Float(0x416823bc), SkBits2Float(0x42534038), SkBits2Float(0x41344220), SkBits2Float(0x4255fb7e)); +path.quadTo(SkBits2Float(0x41006082), SkBits2Float(0x4258b6c5), SkBits2Float(0x40a7ee48), SkBits2Float(0x4251795a)); +path.quadTo(SkBits2Float(0x401e3718), SkBits2Float(0x424a3bef), SkBits2Float(0x3fe50570), SkBits2Float(0x423d4388)); +path.quadTo(SkBits2Float(0x3f8d9c90), SkBits2Float(0x42304b20), SkBits2Float(0x403aa4f8), SkBits2Float(0x422530c9)); +path.quadTo(SkBits2Float(0x4059e097), SkBits2Float(0x4222326b), SkBits2Float(0x407fc660), SkBits2Float(0x421fcfdc)); +path.lineTo(SkBits2Float(0x407fc672), SkBits2Float(0x421fcfdb)); +path.quadTo(SkBits2Float(0x409c2918), SkBits2Float(0x421dbc1a), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); +path.close(); +path.moveTo(SkBits2Float(0xc23fe7ce), SkBits2Float(0xc1ad1dad)); +path.quadTo(SkBits2Float(0xc2341588), SkBits2Float(0xc1b91b57), SkBits2Float(0xc2277c56), SkBits2Float(0xc1b0de1d)); +path.quadTo(SkBits2Float(0xc21ae326), SkBits2Float(0xc1a8a0e6), SkBits2Float(0xc214e451), SkBits2Float(0xc190fc58)); +path.quadTo(SkBits2Float(0xc20ee57c), SkBits2Float(0xc172af93), SkBits2Float(0xc2130419), SkBits2Float(0xc1404ad0)); +path.quadTo(SkBits2Float(0xc21722b5), SkBits2Float(0xc10de60c), SkBits2Float(0xc222f4fc), SkBits2Float(0xc0ebd570)); +path.quadTo(SkBits2Float(0xc22ec743), SkBits2Float(0xc0bbdec8), SkBits2Float(0xc23b6074), SkBits2Float(0xc0dcd3b4)); +path.quadTo(SkBits2Float(0xc247f9a4), SkBits2Float(0xc0fdc894), SkBits2Float(0xc24df87a), SkBits2Float(0xc12e2d66)); +path.quadTo(SkBits2Float(0xc253f74e), SkBits2Float(0xc15d7682), SkBits2Float(0xc24fd8b1), SkBits2Float(0xc187eda2)); +path.quadTo(SkBits2Float(0xc24bba16), SkBits2Float(0xc1a12003), SkBits2Float(0xc23fe7ce), SkBits2Float(0xc1ad1dad)); +path.close(); +path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200)); +path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef)); +path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176)); +path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330)); +path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c)); +path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8)); +path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec)); +path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec)); +path.close(); +path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x41828cb4), SkBits2Float(0x424825e0)); +path.quadTo(SkBits2Float(0x416823bc), SkBits2Float(0x42534038), SkBits2Float(0x41344220), SkBits2Float(0x4255fb7e)); +path.quadTo(SkBits2Float(0x41006082), SkBits2Float(0x4258b6c5), SkBits2Float(0x40a7ee48), SkBits2Float(0x4251795a)); +path.quadTo(SkBits2Float(0x401e3718), SkBits2Float(0x424a3bef), SkBits2Float(0x3fe50570), SkBits2Float(0x423d4388)); +path.quadTo(SkBits2Float(0x3f8d9c90), SkBits2Float(0x42304b20), SkBits2Float(0x403aa4f8), SkBits2Float(0x422530c9)); +path.quadTo(SkBits2Float(0x40973dd4), SkBits2Float(0x421a1671), SkBits2Float(0x40ff010e), SkBits2Float(0x42175b2b)); +path.quadTo(SkBits2Float(0x41336223), SkBits2Float(0x42149fe4), SkBits2Float(0x415fcb81), SkBits2Float(0x421bdd50)); +path.quadTo(SkBits2Float(0x41861a70), SkBits2Float(0x42231aba), SkBits2Float(0x418b90fc), SkBits2Float(0x42301322)); +path.quadTo(SkBits2Float(0x4191078a), SkBits2Float(0x423d0b88), SkBits2Float(0x41828cb4), SkBits2Float(0x424825e0)); +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_10022998(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x40f23d54), SkBits2Float(0xc250558c)); // 7.56999f, -52.0835f +path.quadTo(SkBits2Float(0x4126c646), SkBits2Float(0xc25712d1), SkBits2Float(0x415a1e76), SkBits2Float(0xc253c4aa)); // 10.4234f, -53.7684f, 13.6324f, -52.9421f +path.quadTo(SkBits2Float(0x4186bb52), SkBits2Float(0xc2507686), SkBits2Float(0x419435db), SkBits2Float(0xc2450c9e)); // 16.8415f, -52.1157f, 18.5263f, -49.2623f +path.quadTo(SkBits2Float(0x41a1b065), SkBits2Float(0xc239a2b8), SkBits2Float(0x419b1418), SkBits2Float(0xc22cccac)); // 20.2111f, -46.4089f, 19.3848f, -43.1999f +path.quadTo(SkBits2Float(0x419477ce), SkBits2Float(0xc21ff6a0), SkBits2Float(0x417b47ff), SkBits2Float(0xc219395c)); // 18.5585f, -39.9908f, 15.7051f, -38.306f +path.quadTo(SkBits2Float(0x414da063), SkBits2Float(0xc2127c16), SkBits2Float(0x411a4835), SkBits2Float(0xc215ca3d)); // 12.8517f, -36.6212f, 9.64263f, -37.4475f +path.quadTo(SkBits2Float(0x40cde00c), SkBits2Float(0xc2191862), SkBits2Float(0x4097f5e4), SkBits2Float(0xc2248249)); // 6.4336f, -38.2738f, 4.74877f, -41.1272f +path.quadTo(SkBits2Float(0x40441780), SkBits2Float(0xc22fec30), SkBits2Float(0x4078f9e8), SkBits2Float(0xc23cc23c)); // 3.06393f, -43.9807f, 3.89025f, -47.1897f +path.quadTo(SkBits2Float(0x4096ee1c), SkBits2Float(0xc2499848), SkBits2Float(0x40f23d54), SkBits2Float(0xc250558c)); // 4.71657f, -50.3987f, 7.56999f, -52.0835f +path.close(); +path.moveTo(SkBits2Float(0xc2066415), SkBits2Float(0xc2220be8)); // -33.5977f, -40.5116f +path.quadTo(SkBits2Float(0xc1f2466b), SkBits2Float(0xc2223dac), SkBits2Float(0xc1df41cc), SkBits2Float(0xc21901bc)); // -30.2844f, -40.5602f, -27.9071f, -38.2517f +path.quadTo(SkBits2Float(0xc1cc3d2d), SkBits2Float(0xc20fc5cd), SkBits2Float(0xc1cbd9a6), SkBits2Float(0xc20284ee)); // -25.5299f, -35.9432f, -25.4813f, -32.6298f +path.quadTo(SkBits2Float(0xc1cb761f), SkBits2Float(0xc1ea881c), SkBits2Float(0xc1ddedfe), SkBits2Float(0xc1d7837e)); // -25.4327f, -29.3165f, -27.7412f, -26.9392f +path.quadTo(SkBits2Float(0xc1f065dc), SkBits2Float(0xc1c47edf), SkBits2Float(0xc20573ce), SkBits2Float(0xc1c41b58)); // -30.0497f, -24.5619f, -33.3631f, -24.5134f +path.quadTo(SkBits2Float(0xc212b4ad), SkBits2Float(0xc1c3b7d1), SkBits2Float(0xc21c36fc), SkBits2Float(0xc1d62fb0)); // -36.6764f, -24.4648f, -39.0537f, -26.7733f +path.quadTo(SkBits2Float(0xc225b94c), SkBits2Float(0xc1e8a78d), SkBits2Float(0xc225eb10), SkBits2Float(0xc20194a7)); // -41.431f, -29.0818f, -41.4796f, -32.3952f +path.quadTo(SkBits2Float(0xc2261cd3), SkBits2Float(0xc20ed586), SkBits2Float(0xc21ce0e3), SkBits2Float(0xc21857d5)); // -41.5281f, -35.7085f, -39.2196f, -38.0858f +path.quadTo(SkBits2Float(0xc213a4f4), SkBits2Float(0xc221da25), SkBits2Float(0xc2066415), SkBits2Float(0xc2220be8)); // -36.9111f, -40.463f, -33.5977f, -40.5116f +path.close(); +path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); // -19.799f, -19.799f +path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000)); // -11.598f, -28, 0, -28 +path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455)); // 11.598f, -28, 19.799f, -19.799f +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000)); // 28, -11.598f, 28, 0 +path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455)); // 28, 11.598f, 19.799f, 19.799f +path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74)); // 13.7163f, 25.8817f, 5.76477f, 27.4529f +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000)); // 8, 29.7635f, 8, 33 +path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e)); // 8, 36.3137f, 5.65685f, 38.6569f +path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000)); // 3.31371f, 41, 0, 41 +path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e)); // -3.31371f, 41, -5.65685f, 38.6569f +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000)); // -8, 36.3137f, -8, 33 +path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74)); // -8, 29.7635f, -5.76477f, 27.4529f +path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455)); // -13.7163f, 25.8817f, -19.799f, 19.799f +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000)); // -28, 11.598f, -28, 0 +path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455)); // -28, -11.598f, -19.799f, -19.799f +path.close(); +path.moveTo(SkBits2Float(0xc23c6b1a), SkBits2Float(0x4099fd84)); // -47.1046f, 4.8122f +path.quadTo(SkBits2Float(0xc22fa63c), SkBits2Float(0x407b1738), SkBits2Float(0xc2241b28), SkBits2Float(0x40b1aa10)); // -43.9123f, 3.92329f, -41.0265f, 5.55201f +path.quadTo(SkBits2Float(0xc2189014), SkBits2Float(0x40e5c886), SkBits2Float(0xc21501d7), SkBits2Float(0x4125f7c0)); // -38.1407f, 7.18073f, -37.2518f, 10.373f +path.quadTo(SkBits2Float(0xc211739a), SkBits2Float(0x41590b3e), SkBits2Float(0xc217f769), SkBits2Float(0x41839bc7)); // -36.3629f, 13.5652f, -37.9916f, 16.4511f +path.quadTo(SkBits2Float(0xc21e7b38), SkBits2Float(0x419ab1ef), SkBits2Float(0xc22b4016), SkBits2Float(0x41a1ce67)); // -39.6203f, 19.3369f, -42.8126f, 20.2258f +path.quadTo(SkBits2Float(0xc23804f6), SkBits2Float(0x41a8eae0), SkBits2Float(0xc243900a), SkBits2Float(0x419be343)); // -46.0048f, 21.1147f, -48.8907f, 19.486f +path.quadTo(SkBits2Float(0xc24f1b1e), SkBits2Float(0x418edba6), SkBits2Float(0xc252a95a), SkBits2Float(0x416aa3d0)); // -51.7765f, 17.8573f, -52.6654f, 14.665f +path.quadTo(SkBits2Float(0xc2563797), SkBits2Float(0x41379054), SkBits2Float(0xc24fb3c8), SkBits2Float(0x41096404)); // -53.5543f, 11.4727f, -51.9256f, 8.58692f +path.quadTo(SkBits2Float(0xc2492ffa), SkBits2Float(0x40b66f68), SkBits2Float(0xc23c6b1a), SkBits2Float(0x4099fd84)); // -50.2969f, 5.7011f, -47.1046f, 4.8122f +path.close(); +path.moveTo(SkBits2Float(0x4204f8ac), SkBits2Float(0x41c568cb)); // 33.2428f, 24.6762f +path.quadTo(SkBits2Float(0x421239c0), SkBits2Float(0x41c52671), SkBits2Float(0x421bb079), SkBits2Float(0x41d7b610)); // 36.5564f, 24.6438f, 38.9223f, 26.9639f +path.quadTo(SkBits2Float(0x42252732), SkBits2Float(0x41ea45ad), SkBits2Float(0x4225485f), SkBits2Float(0x420263e9)); // 41.2883f, 29.284f, 41.3207f, 32.5976f +path.quadTo(SkBits2Float(0x4225698c), SkBits2Float(0x420fa4fd), SkBits2Float(0x421c21bd), SkBits2Float(0x42191bb7)); // 41.3531f, 35.9111f, 39.0329f, 38.2771f +path.quadTo(SkBits2Float(0x421bf1ce), SkBits2Float(0x42194c98), SkBits2Float(0x421bc136), SkBits2Float(0x42197cbe)); // 38.9861f, 38.3248f, 38.9387f, 38.3718f +path.quadTo(SkBits2Float(0x421b91ce), SkBits2Float(0x4219ae03), SkBits2Float(0x421b61a7), SkBits2Float(0x4219dea3)); // 38.8924f, 38.4199f, 38.8454f, 38.4674f +path.quadTo(SkBits2Float(0x421b3158), SkBits2Float(0x421a0f6b), SkBits2Float(0x421b00a6), SkBits2Float(0x421a3f33)); // 38.7982f, 38.5151f, 38.7506f, 38.5617f +path.quadTo(SkBits2Float(0x421ad122), SkBits2Float(0x421a701a), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); // 38.7042f, 38.6095f, 38.6569f, 38.6569f +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); // 36.3137f, 41, 33, 41 +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); // 29.6863f, 41, 27.3431f, 38.6569f +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); // 25, 36.3137f, 25, 33 +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); // 25, 29.6863f, 27.3431f, 27.3431f +path.quadTo(SkBits2Float(0x41db0360), SkBits2Float(0x41da7a26), SkBits2Float(0x41db4867), SkBits2Float(0x41da3682)); // 27.3766f, 27.3096f, 27.4104f, 27.2766f +path.quadTo(SkBits2Float(0x41db8ba6), SkBits2Float(0x41d9f135), SkBits2Float(0x41dbcfd8), SkBits2Float(0x41d9ac58)); // 27.4432f, 27.2428f, 27.4765f, 27.2092f +path.quadTo(SkBits2Float(0x41dc13ed), SkBits2Float(0x41d96798), SkBits2Float(0x41dc58ba), SkBits2Float(0x41d92383)); // 27.5097f, 27.1756f, 27.5433f, 27.1423f +path.quadTo(SkBits2Float(0x41dc9bcc), SkBits2Float(0x41d8ddb7), SkBits2Float(0x41dcdf94), SkBits2Float(0x41d89898)); // 27.5761f, 27.1083f, 27.6092f, 27.0745f +path.quadTo(SkBits2Float(0x41ef6f31), SkBits2Float(0x41c5ab25), SkBits2Float(0x4204f8ac), SkBits2Float(0x41c568cb)); // 29.9293f, 24.7086f, 33.2428f, 24.6762f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42240000), SkBits2Float(0x42040000)); // 41, 33 +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); // 41, 36.3137f, 38.6569f, 38.6569f +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); // 36.3137f, 41, 33, 41 +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); // 29.6863f, 41, 27.3431f, 38.6569f +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); // 25, 36.3137f, 25, 33 +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); // 25, 29.6863f, 27.3431f, 27.3431f +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); // 29.6863f, 25, 33, 25 +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); // 36.3137f, 25, 38.6569f, 27.3431f +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); // 41, 29.6863f, 41, 33 +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0; +static void (*firstTest)(skiatest::Reporter* , const char* filename) = fuzz763_1026368; +static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0; + +static struct TestDesc tests[] = { + TEST(fuzz763_10022998), + TEST(fuzz763_2674194), + TEST(fuzz763_5485218), + TEST(fuzz763_1026368), + TEST(fuzz763_3283699), + TEST(fuzz763_6411089), + TEST(fuzz763_4628016), + TEST(fuzz763_2211264), + TEST(fuzz763_34974), + TEST(fuzz763_1597464), + TEST(fuzz763_849020), + TEST(fuzz763_24588), + TEST(fuzz763_20016), + TEST(fuzz763_17370), + TEST(fuzz763_35322), + TEST(fuzz763_8712), + TEST(fuzz763_8712a), + TEST(fuzz763_4713), + TEST(fuzz763_4014), + TEST(fuzz763_4014a), + TEST(fuzz763_1404), + TEST(fuzz763_378), + TEST(fuzz763_378b), + TEST(fuzz763_378d), + TEST(fuzz763_378c), + TEST(fuzz763_3084), + TEST(fuzz763_1823), + TEST(fuzz763_558), + TEST(fuzz763_378a), + TEST(fuzz763_378a_1), +}; + +static const size_t testCount = SK_ARRAY_COUNT(tests); + +static bool runReverse = false; + +DEF_TEST(PathOpsFuzz763, reporter) { +#if DEBUG_SHOW_TEST_NAME + strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH); +#endif + RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse); +}
diff --git a/src/third_party/skia/tests/PathOpsInverseTest.cpp b/src/third_party/skia/tests/PathOpsInverseTest.cpp index 23dbd1f..929851e 100644 --- a/src/third_party/skia/tests/PathOpsInverseTest.cpp +++ b/src/third_party/skia/tests/PathOpsInverseTest.cpp
@@ -8,7 +8,7 @@ DEF_TEST(PathOpsInverse, reporter) { SkPath one, two; - for (int op = kDifference_PathOp; op <= kReverseDifference_PathOp; ++op) { + for (int op = kDifference_SkPathOp; op <= kReverseDifference_SkPathOp; ++op) { for (int oneFill = SkPath::kWinding_FillType; oneFill <= SkPath::kInverseEvenOdd_FillType; ++oneFill) { for (int oneDir = SkPath::kCW_Direction; oneDir != SkPath::kCCW_Direction; ++oneDir) {
diff --git a/src/third_party/skia/tests/PathOpsIssue3651.cpp b/src/third_party/skia/tests/PathOpsIssue3651.cpp new file mode 100644 index 0000000..bd4ed2d --- /dev/null +++ b/src/third_party/skia/tests/PathOpsIssue3651.cpp
@@ -0,0 +1,1686 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "PathOpsExtendedTest.h" +#include "PathOpsTestCommon.h" + +#define TEST(name) { name, #name } + +static SkPath path1() { + SkPath path; +path.moveTo(SkBits2Float(0x431d8000), SkBits2Float(0x42823333)); // 157.5f, 65.1f +path.lineTo(SkBits2Float(0x431d8000), SkBits2Float(0x42823333)); // 157.5f, 65.1f +path.cubicTo(SkBits2Float(0x431e3333), SkBits2Float(0x42823333), SkBits2Float(0x431ee666), SkBits2Float(0x4282368d), SkBits2Float(0x431f999a), SkBits2Float(0x42823333)); // 158.2f, 65.1f, 158.9f, 65.1065f, 159.6f, 65.1f +path.cubicTo(SkBits2Float(0x43204ccd), SkBits2Float(0x42822fd9), SkBits2Float(0x43210000), SkBits2Float(0x42822861), SkBits2Float(0x4321b333), SkBits2Float(0x42821f17)); // 160.3f, 65.0935f, 161, 65.0789f, 161.7f, 65.0607f +path.cubicTo(SkBits2Float(0x43226666), SkBits2Float(0x428215ce), SkBits2Float(0x4323199a), SkBits2Float(0x4282071d), SkBits2Float(0x4323cccd), SkBits2Float(0x4281fb7b)); // 162.4f, 65.0426f, 163.1f, 65.0139f, 163.8f, 64.9912f +path.cubicTo(SkBits2Float(0x43248000), SkBits2Float(0x4281efd8), SkBits2Float(0x43253333), SkBits2Float(0x4281e467), SkBits2Float(0x4325e666), SkBits2Float(0x4281d94a)); // 164.5f, 64.9684f, 165.2f, 64.9461f, 165.9f, 64.9244f +path.cubicTo(SkBits2Float(0x4326999a), SkBits2Float(0x4281ce2c), SkBits2Float(0x43274ccd), SkBits2Float(0x4281c15d), SkBits2Float(0x43280000), SkBits2Float(0x4281b8cb)); // 166.6f, 64.9027f, 167.3f, 64.8777f, 168, 64.8609f +path.cubicTo(SkBits2Float(0x4328b333), SkBits2Float(0x4281b039), SkBits2Float(0x43296666), SkBits2Float(0x4281a66d), SkBits2Float(0x432a199a), SkBits2Float(0x4281a5dd)); // 168.7f, 64.8442f, 169.4f, 64.8251f, 170.1f, 64.824f +path.cubicTo(SkBits2Float(0x432acccd), SkBits2Float(0x4281a54c), SkBits2Float(0x432b8000), SkBits2Float(0x4281aecf), SkBits2Float(0x432c3333), SkBits2Float(0x4281b566)); // 170.8f, 64.8228f, 171.5f, 64.8414f, 172.2f, 64.8543f +path.cubicTo(SkBits2Float(0x432ce666), SkBits2Float(0x4281bbfe), SkBits2Float(0x432d999a), SkBits2Float(0x4281c612), SkBits2Float(0x432e4ccd), SkBits2Float(0x4281cd6b)); // 172.9f, 64.8672f, 173.6f, 64.8869f, 174.3f, 64.9012f +path.cubicTo(SkBits2Float(0x432f0000), SkBits2Float(0x4281d4c4), SkBits2Float(0x432fb333), SkBits2Float(0x4281dc73), SkBits2Float(0x43306666), SkBits2Float(0x4281e17e)); // 175, 64.9156f, 175.7f, 64.9306f, 176.4f, 64.9404f +path.cubicTo(SkBits2Float(0x4331199a), SkBits2Float(0x4281e688), SkBits2Float(0x4331cccd), SkBits2Float(0x4281e967), SkBits2Float(0x43328000), SkBits2Float(0x4281ebaa)); // 177.1f, 64.9503f, 177.8f, 64.9559f, 178.5f, 64.9603f +path.cubicTo(SkBits2Float(0x43333333), SkBits2Float(0x4281eded), SkBits2Float(0x4333e666), SkBits2Float(0x4281eec6), SkBits2Float(0x4334999a), SkBits2Float(0x4281ef0f)); // 179.2f, 64.9647f, 179.9f, 64.9664f, 180.6f, 64.9669f +path.cubicTo(SkBits2Float(0x43354ccd), SkBits2Float(0x4281ef57), SkBits2Float(0x43360000), SkBits2Float(0x4281eeba), SkBits2Float(0x4336b333), SkBits2Float(0x4281ed5c)); // 181.3f, 64.9675f, 182, 64.9663f, 182.7f, 64.9636f +path.cubicTo(SkBits2Float(0x43376666), SkBits2Float(0x4281ebfe), SkBits2Float(0x4338199a), SkBits2Float(0x4281e8c9), SkBits2Float(0x4338cccd), SkBits2Float(0x4281e6db)); // 183.4f, 64.9609f, 184.1f, 64.9547f, 184.8f, 64.9509f +path.cubicTo(SkBits2Float(0x43398000), SkBits2Float(0x4281e4ec), SkBits2Float(0x433a3333), SkBits2Float(0x4281e29d), SkBits2Float(0x433ae666), SkBits2Float(0x4281e1c4)); // 185.5f, 64.9471f, 186.2f, 64.9426f, 186.9f, 64.9409f +path.cubicTo(SkBits2Float(0x433b999a), SkBits2Float(0x4281e0eb), SkBits2Float(0x433c4ccd), SkBits2Float(0x4281e188), SkBits2Float(0x433d0000), SkBits2Float(0x4281e1c4)); // 187.6f, 64.9393f, 188.3f, 64.9405f, 189, 64.9409f +path.cubicTo(SkBits2Float(0x433db333), SkBits2Float(0x4281e201), SkBits2Float(0x433e6666), SkBits2Float(0x4281e415), SkBits2Float(0x433f199a), SkBits2Float(0x4281e330)); // 189.7f, 64.9414f, 190.4f, 64.9455f, 191.1f, 64.9437f +path.cubicTo(SkBits2Float(0x433fcccd), SkBits2Float(0x4281e24b), SkBits2Float(0x43408000), SkBits2Float(0x4281df77), SkBits2Float(0x43413333), SkBits2Float(0x4281dc67)); // 191.8f, 64.942f, 192.5f, 64.9365f, 193.2f, 64.9305f +path.cubicTo(SkBits2Float(0x4341e666), SkBits2Float(0x4281d957), SkBits2Float(0x4342999a), SkBits2Float(0x4281d35a), SkBits2Float(0x43434ccd), SkBits2Float(0x4281d0cf)); // 193.9f, 64.9245f, 194.6f, 64.9128f, 195.3f, 64.9078f +path.cubicTo(SkBits2Float(0x43440000), SkBits2Float(0x4281ce44), SkBits2Float(0x4344b333), SkBits2Float(0x4281cd6c), SkBits2Float(0x43456666), SkBits2Float(0x4281cd24)); // 196, 64.9029f, 196.7f, 64.9012f, 197.4f, 64.9007f +path.cubicTo(SkBits2Float(0x4346199a), SkBits2Float(0x4281ccdc), SkBits2Float(0x4346cccd), SkBits2Float(0x4281cf1d), SkBits2Float(0x43478000), SkBits2Float(0x4281cf1d)); // 198.1f, 64.9001f, 198.8f, 64.9045f, 199.5f, 64.9045f +path.cubicTo(SkBits2Float(0x43483333), SkBits2Float(0x4281cf1d), SkBits2Float(0x4348e666), SkBits2Float(0x4281ce8e), SkBits2Float(0x4349999a), SkBits2Float(0x4281cd24)); // 200.2f, 64.9045f, 200.9f, 64.9034f, 201.6f, 64.9007f +path.cubicTo(SkBits2Float(0x434a4ccd), SkBits2Float(0x4281cbba), SkBits2Float(0x434b0000), SkBits2Float(0x4281c854), SkBits2Float(0x434bb333), SkBits2Float(0x4281c6a2)); // 202.3f, 64.8979f, 203, 64.8913f, 203.7f, 64.888f +path.cubicTo(SkBits2Float(0x434c6666), SkBits2Float(0x4281c4f0), SkBits2Float(0x434d199a), SkBits2Float(0x4281c46d), SkBits2Float(0x434dcccd), SkBits2Float(0x4281c2f7)); // 204.4f, 64.8846f, 205.1f, 64.8836f, 205.8f, 64.8808f +path.cubicTo(SkBits2Float(0x434e8000), SkBits2Float(0x4281c182), SkBits2Float(0x434f3333), SkBits2Float(0x4281bf4b), SkBits2Float(0x434fe666), SkBits2Float(0x4281bde1)); // 206.5f, 64.8779f, 207.2f, 64.8736f, 207.9f, 64.8709f +path.cubicTo(SkBits2Float(0x4350999a), SkBits2Float(0x4281bc77), SkBits2Float(0x43514ccd), SkBits2Float(0x4281bb92), SkBits2Float(0x43520000), SkBits2Float(0x4281ba7d)); // 208.6f, 64.8681f, 209.3f, 64.8663f, 210, 64.8642f +path.cubicTo(SkBits2Float(0x4352b333), SkBits2Float(0x4281b967), SkBits2Float(0x43536666), SkBits2Float(0x4281b95a), SkBits2Float(0x4354199a), SkBits2Float(0x4281b75f)); // 210.7f, 64.8621f, 211.4f, 64.862f, 212.1f, 64.8581f +path.cubicTo(SkBits2Float(0x4354cccd), SkBits2Float(0x4281b565), SkBits2Float(0x43558000), SkBits2Float(0x4281b0a4), SkBits2Float(0x43563333), SkBits2Float(0x4281ae9e)); // 212.8f, 64.8543f, 213.5f, 64.845f, 214.2f, 64.841f +path.cubicTo(SkBits2Float(0x4356e666), SkBits2Float(0x4281ac98), SkBits2Float(0x4357999a), SkBits2Float(0x4281aca3), SkBits2Float(0x43584ccd), SkBits2Float(0x4281ab3a)); // 214.9f, 64.8371f, 215.6f, 64.8372f, 216.3f, 64.8344f +path.cubicTo(SkBits2Float(0x43590000), SkBits2Float(0x4281a9d0), SkBits2Float(0x4359b333), SkBits2Float(0x4281a82a), SkBits2Float(0x435a6666), SkBits2Float(0x4281a623)); // 217, 64.8317f, 217.7f, 64.8284f, 218.4f, 64.8245f +path.cubicTo(SkBits2Float(0x435b199a), SkBits2Float(0x4281a41d), SkBits2Float(0x435bcccd), SkBits2Float(0x4281a157), SkBits2Float(0x435c8000), SkBits2Float(0x42819f14)); // 219.1f, 64.8205f, 219.8f, 64.8151f, 220.5f, 64.8107f +path.cubicTo(SkBits2Float(0x435d3333), SkBits2Float(0x42819cd1), SkBits2Float(0x435de666), SkBits2Float(0x42819a39), SkBits2Float(0x435e999a), SkBits2Float(0x42819892)); // 221.2f, 64.8063f, 221.9f, 64.8012f, 222.6f, 64.798f +path.cubicTo(SkBits2Float(0x435f4ccd), SkBits2Float(0x428196ec), SkBits2Float(0x43600000), SkBits2Float(0x42819455), SkBits2Float(0x4360b333), SkBits2Float(0x4281952e)); // 223.3f, 64.7948f, 224, 64.7897f, 224.7f, 64.7914f +path.cubicTo(SkBits2Float(0x43616666), SkBits2Float(0x42819607), SkBits2Float(0x4362199a), SkBits2Float(0x428198e7), SkBits2Float(0x4362cccd), SkBits2Float(0x42819da9)); // 225.4f, 64.793f, 226.1f, 64.7986f, 226.8f, 64.8079f +path.cubicTo(SkBits2Float(0x43638000), SkBits2Float(0x4281a26b), SkBits2Float(0x43643333), SkBits2Float(0x4281ad8a), SkBits2Float(0x4364e666), SkBits2Float(0x4281b1bc)); // 227.5f, 64.8172f, 228.2f, 64.8389f, 228.9f, 64.8471f +path.cubicTo(SkBits2Float(0x4365999a), SkBits2Float(0x4281b5ed), SkBits2Float(0x43664ccd), SkBits2Float(0x4281b70f), SkBits2Float(0x43670000), SkBits2Float(0x4281b6d2)); // 229.6f, 64.8553f, 230.3f, 64.8575f, 231, 64.8571f +path.cubicTo(SkBits2Float(0x4367b333), SkBits2Float(0x4281b695), SkBits2Float(0x43686666), SkBits2Float(0x4281b2db), SkBits2Float(0x4369199a), SkBits2Float(0x4281b050)); // 231.7f, 64.8566f, 232.4f, 64.8493f, 233.1f, 64.8444f +path.cubicTo(SkBits2Float(0x4369cccd), SkBits2Float(0x4281adc5), SkBits2Float(0x436a8000), SkBits2Float(0x4281a9e9), SkBits2Float(0x436b3333), SkBits2Float(0x4281a78f)); // 233.8f, 64.8394f, 234.5f, 64.8319f, 235.2f, 64.8273f +path.cubicTo(SkBits2Float(0x436be666), SkBits2Float(0x4281a535), SkBits2Float(0x436c999a), SkBits2Float(0x4281a55a), SkBits2Float(0x436d4ccd), SkBits2Float(0x4281a232)); // 235.9f, 64.8227f, 236.6f, 64.823f, 237.3f, 64.8168f +path.cubicTo(SkBits2Float(0x436e0000), SkBits2Float(0x42819f0a), SkBits2Float(0x436eb333), SkBits2Float(0x42819ad9), SkBits2Float(0x436f6666), SkBits2Float(0x428194a1)); // 238, 64.8106f, 238.7f, 64.8024f, 239.4f, 64.7903f +path.cubicTo(SkBits2Float(0x4370199a), SkBits2Float(0x42818e69), SkBits2Float(0x4370cccd), SkBits2Float(0x4281843c), SkBits2Float(0x43718000), SkBits2Float(0x42817ce3)); // 240.1f, 64.7781f, 240.8f, 64.7583f, 241.5f, 64.7439f +path.cubicTo(SkBits2Float(0x43723333), SkBits2Float(0x4281758a), SkBits2Float(0x4372e666), SkBits2Float(0x42816c36), SkBits2Float(0x4373999a), SkBits2Float(0x4281688a)); // 242.2f, 64.7296f, 242.9f, 64.7113f, 243.6f, 64.7042f +path.cubicTo(SkBits2Float(0x43744ccd), SkBits2Float(0x428164dd), SkBits2Float(0x43750000), SkBits2Float(0x428167a5), SkBits2Float(0x4375b333), SkBits2Float(0x428166d8)); // 244.3f, 64.697f, 245, 64.7024f, 245.7f, 64.7009f +path.cubicTo(SkBits2Float(0x43766666), SkBits2Float(0x4281660a), SkBits2Float(0x4377199a), SkBits2Float(0x42816651), SkBits2Float(0x4377cccd), SkBits2Float(0x428163ba)); // 246.4f, 64.6993f, 247.1f, 64.6998f, 247.8f, 64.6948f +path.cubicTo(SkBits2Float(0x43788000), SkBits2Float(0x42816123), SkBits2Float(0x43793333), SkBits2Float(0x42815b5b), SkBits2Float(0x4379e666), SkBits2Float(0x4281574e)); // 248.5f, 64.6897f, 249.2f, 64.6784f, 249.9f, 64.6705f +path.cubicTo(SkBits2Float(0x437a999a), SkBits2Float(0x42815342), SkBits2Float(0x437b4ccd), SkBits2Float(0x42814fad), SkBits2Float(0x437c0000), SkBits2Float(0x42814b6f)); // 250.6f, 64.6626f, 251.3f, 64.6556f, 252, 64.6473f +path.cubicTo(SkBits2Float(0x437cb333), SkBits2Float(0x42814732), SkBits2Float(0x437d6666), SkBits2Float(0x42813eb7), SkBits2Float(0x437e199a), SkBits2Float(0x42813dde)); // 252.7f, 64.6391f, 253.4f, 64.6225f, 254.1f, 64.6208f +path.cubicTo(SkBits2Float(0x437ecccd), SkBits2Float(0x42813d05), SkBits2Float(0x437f8000), SkBits2Float(0x428137d7), SkBits2Float(0x4380199a), SkBits2Float(0x42814659)); // 254.8f, 64.6192f, 255.5f, 64.6091f, 256.2f, 64.6374f +path.cubicTo(SkBits2Float(0x43807333), SkBits2Float(0x428154da), SkBits2Float(0x4380cccd), SkBits2Float(0x42817565), SkBits2Float(0x43812666), SkBits2Float(0x428194e8)); // 256.9f, 64.6657f, 257.6f, 64.7293f, 258.3f, 64.7908f +path.cubicTo(SkBits2Float(0x43818000), SkBits2Float(0x4281b46a), SkBits2Float(0x4381d99a), SkBits2Float(0x4281e906), SkBits2Float(0x43823333), SkBits2Float(0x42820368)); // 259, 64.8524f, 259.7f, 64.9551f, 260.4f, 65.0067f +path.cubicTo(SkBits2Float(0x43828ccd), SkBits2Float(0x42821dca), SkBits2Float(0x4382e666), SkBits2Float(0x42822b3c), SkBits2Float(0x43834000), SkBits2Float(0x42823333)); // 261.1f, 65.0582f, 261.8f, 65.0844f, 262.5f, 65.1f +path.cubicTo(SkBits2Float(0x4383999a), SkBits2Float(0x42823b2a), SkBits2Float(0x4383f333), SkBits2Float(0x42823333), SkBits2Float(0x43844ccd), SkBits2Float(0x42823333)); // 263.2f, 65.1156f, 263.9f, 65.1f, 264.6f, 65.1f +path.lineTo(SkBits2Float(0x43844ccd), SkBits2Float(0x42823333)); // 264.6f, 65.1f +path.lineTo(SkBits2Float(0x431d8000), SkBits2Float(0x42823333)); // 157.5f, 65.1f +path.close(); +path.moveTo(SkBits2Float(0x438dc000), SkBits2Float(0x42823333)); // 283.5f, 65.1f +path.lineTo(SkBits2Float(0x438dc000), SkBits2Float(0x42823333)); // 283.5f, 65.1f +path.cubicTo(SkBits2Float(0x438e199a), SkBits2Float(0x428230fb), SkBits2Float(0x438e7333), SkBits2Float(0x4282293a), SkBits2Float(0x438ecccd), SkBits2Float(0x428225e0)); // 284.2f, 65.0957f, 284.9f, 65.0805f, 285.6f, 65.074f +path.cubicTo(SkBits2Float(0x438f2666), SkBits2Float(0x42822286), SkBits2Float(0x438f8000), SkBits2Float(0x42821cde), SkBits2Float(0x438fd99a), SkBits2Float(0x42821f17)); // 286.3f, 65.0674f, 287, 65.0564f, 287.7f, 65.0607f +path.cubicTo(SkBits2Float(0x43903333), SkBits2Float(0x42822150), SkBits2Float(0x43908ccd), SkBits2Float(0x42822fd9), SkBits2Float(0x4390e666), SkBits2Float(0x42823333)); // 288.4f, 65.0651f, 289.1f, 65.0935f, 289.8f, 65.1f +path.lineTo(SkBits2Float(0x4390e666), SkBits2Float(0x42823333)); // 289.8f, 65.1f +path.lineTo(SkBits2Float(0x438dc000), SkBits2Float(0x42823333)); // 283.5f, 65.1f +path.close(); +path.moveTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333)); // 306.6f, 65.1f +path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333)); // 306.6f, 65.1f +path.cubicTo(SkBits2Float(0x4399a666), SkBits2Float(0x42823332), SkBits2Float(0x439a0000), SkBits2Float(0x42823842), SkBits2Float(0x439a599a), SkBits2Float(0x4282332a)); // 307.3f, 65.1f, 308, 65.1099f, 308.7f, 65.0999f +path.cubicTo(SkBits2Float(0x439ab333), SkBits2Float(0x42822e12), SkBits2Float(0x439b0ccd), SkBits2Float(0x42821e94), SkBits2Float(0x439b6666), SkBits2Float(0x428214a4)); // 309.4f, 65.09f, 310.1f, 65.0597f, 310.8f, 65.0403f +path.cubicTo(SkBits2Float(0x439bc000), SkBits2Float(0x42820ab4), SkBits2Float(0x439c199a), SkBits2Float(0x42820185), SkBits2Float(0x439c7333), SkBits2Float(0x4281f789)); // 311.5f, 65.0209f, 312.2f, 65.003f, 312.9f, 64.9835f +path.cubicTo(SkBits2Float(0x439ccccd), SkBits2Float(0x4281ed8d), SkBits2Float(0x439d2666), SkBits2Float(0x4281e391), SkBits2Float(0x439d8000), SkBits2Float(0x4281d8bc)); // 313.6f, 64.964f, 314.3f, 64.9445f, 315, 64.9233f +path.cubicTo(SkBits2Float(0x439dd99a), SkBits2Float(0x4281cde7), SkBits2Float(0x439e3333), SkBits2Float(0x4281c0c4), SkBits2Float(0x439e8ccd), SkBits2Float(0x4281b68b)); // 315.7f, 64.9022f, 316.4f, 64.8765f, 317.1f, 64.8565f +path.cubicTo(SkBits2Float(0x439ee666), SkBits2Float(0x4281ac53), SkBits2Float(0x439f4000), SkBits2Float(0x4281a27a), SkBits2Float(0x439f999a), SkBits2Float(0x42819b69)); // 317.8f, 64.8366f, 318.5f, 64.8173f, 319.2f, 64.8035f +path.cubicTo(SkBits2Float(0x439ff333), SkBits2Float(0x42819459), SkBits2Float(0x43a04ccd), SkBits2Float(0x42818f8b), SkBits2Float(0x43a0a666), SkBits2Float(0x42818c26)); // 319.9f, 64.7897f, 320.6f, 64.7804f, 321.3f, 64.7737f +path.cubicTo(SkBits2Float(0x43a10000), SkBits2Float(0x428188c2), SkBits2Float(0x43a1599a), SkBits2Float(0x42818795), SkBits2Float(0x43a1b333), SkBits2Float(0x42818710)); // 322, 64.7671f, 322.7f, 64.7648f, 323.4f, 64.7638f +path.cubicTo(SkBits2Float(0x43a20ccd), SkBits2Float(0x4281868b), SkBits2Float(0x43a26666), SkBits2Float(0x42818824), SkBits2Float(0x43a2c000), SkBits2Float(0x42818909)); // 324.1f, 64.7628f, 324.8f, 64.7659f, 325.5f, 64.7676f +path.cubicTo(SkBits2Float(0x43a3199a), SkBits2Float(0x428189ee), SkBits2Float(0x43a37333), SkBits2Float(0x42818de2), SkBits2Float(0x43a3cccd), SkBits2Float(0x42818c6d)); // 326.2f, 64.7694f, 326.9f, 64.7771f, 327.6f, 64.7743f +path.cubicTo(SkBits2Float(0x43a42666), SkBits2Float(0x42818af7), SkBits2Float(0x43a48000), SkBits2Float(0x428185be), SkBits2Float(0x43a4d99a), SkBits2Float(0x42818048)); // 328.3f, 64.7714f, 329, 64.7612f, 329.7f, 64.7505f +path.cubicTo(SkBits2Float(0x43a53333), SkBits2Float(0x42817ad1), SkBits2Float(0x43a58ccd), SkBits2Float(0x42816e33), SkBits2Float(0x43a5e666), SkBits2Float(0x42816ba7)); // 330.4f, 64.7399f, 331.1f, 64.7152f, 331.8f, 64.7103f +path.cubicTo(SkBits2Float(0x43a64000), SkBits2Float(0x4281691c), SkBits2Float(0x43a6999a), SkBits2Float(0x42816b46), SkBits2Float(0x43a6f333), SkBits2Float(0x42817104)); // 332.5f, 64.7053f, 333.2f, 64.7095f, 333.9f, 64.7207f +path.cubicTo(SkBits2Float(0x43a74ccd), SkBits2Float(0x428176c3), SkBits2Float(0x43a7a666), SkBits2Float(0x42817fa9), SkBits2Float(0x43a80000), SkBits2Float(0x42818e1f)); // 334.6f, 64.732f, 335.3f, 64.7493f, 336, 64.7776f +path.cubicTo(SkBits2Float(0x43a8599a), SkBits2Float(0x42819c95), SkBits2Float(0x43a8b333), SkBits2Float(0x4281b1ec), SkBits2Float(0x43a90ccd), SkBits2Float(0x4281c7c7)); // 336.7f, 64.8058f, 337.4f, 64.8475f, 338.1f, 64.8902f +path.cubicTo(SkBits2Float(0x43a96666), SkBits2Float(0x4281dda2), SkBits2Float(0x43a9c000), SkBits2Float(0x428209cf), SkBits2Float(0x43aa199a), SkBits2Float(0x42821140)); // 338.8f, 64.9329f, 339.5f, 65.0192f, 340.2f, 65.0337f +path.cubicTo(SkBits2Float(0x43aa7333), SkBits2Float(0x428218b0), SkBits2Float(0x43aacccd), SkBits2Float(0x42820dff), SkBits2Float(0x43ab2666), SkBits2Float(0x4281f46b)); // 340.9f, 65.0482f, 341.6f, 65.0273f, 342.3f, 64.9774f +path.cubicTo(SkBits2Float(0x43ab8000), SkBits2Float(0x4281dad8), SkBits2Float(0x43abd99a), SkBits2Float(0x42819956), SkBits2Float(0x43ac3333), SkBits2Float(0x428177cd)); // 343, 64.9274f, 343.7f, 64.7995f, 344.4f, 64.734f +path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42815644), SkBits2Float(0x43ace666), SkBits2Float(0x42813910), SkBits2Float(0x43ad4000), SkBits2Float(0x42812b37)); // 345.1f, 64.6685f, 345.8f, 64.6115f, 346.5f, 64.5844f +path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42811d5e), SkBits2Float(0x43adf333), SkBits2Float(0x42812394), SkBits2Float(0x43ae4ccd), SkBits2Float(0x428124b5)); // 347.2f, 64.5574f, 347.9f, 64.5695f, 348.6f, 64.5717f +path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x428125d6), SkBits2Float(0x43af0000), SkBits2Float(0x42812c1c), SkBits2Float(0x43af599a), SkBits2Float(0x428131ff)); // 349.3f, 64.5739f, 350, 64.5862f, 350.7f, 64.5976f +path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x428137e3), SkBits2Float(0x43b00ccd), SkBits2Float(0x4281417f), SkBits2Float(0x43b06666), SkBits2Float(0x4281480b)); // 351.4f, 64.6092f, 352.1f, 64.6279f, 352.8f, 64.6407f +path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x42814e97), SkBits2Float(0x43b1199a), SkBits2Float(0x4281534c), SkBits2Float(0x43b17333), SkBits2Float(0x42815947)); // 353.5f, 64.6535f, 354.2f, 64.6627f, 354.9f, 64.6744f +path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42815f42), SkBits2Float(0x43b22666), SkBits2Float(0x428165ff), SkBits2Float(0x43b28000), SkBits2Float(0x42816bee)); // 355.6f, 64.6861f, 356.3f, 64.6992f, 357, 64.7108f +path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x428171de), SkBits2Float(0x43b33333), SkBits2Float(0x42817af5), SkBits2Float(0x43b38ccd), SkBits2Float(0x42817ce3)); // 357.7f, 64.7224f, 358.4f, 64.7402f, 359.1f, 64.7439f +path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x42817ed2), SkBits2Float(0x43b44000), SkBits2Float(0x42817bcf), SkBits2Float(0x43b4999a), SkBits2Float(0x42817786)); // 359.8f, 64.7477f, 360.5f, 64.7418f, 361.2f, 64.7334f +path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x4281733d), SkBits2Float(0x43b54ccd), SkBits2Float(0x428167a7), SkBits2Float(0x43b5a666), SkBits2Float(0x4281632d)); // 361.9f, 64.7251f, 362.6f, 64.7024f, 363.3f, 64.6937f +path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x42815eb3), SkBits2Float(0x43b6599a), SkBits2Float(0x42815b7e), SkBits2Float(0x43b6b333), SkBits2Float(0x42815cab)); // 364, 64.685f, 364.7f, 64.6787f, 365.4f, 64.681f +path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x42815dd8), SkBits2Float(0x43b76666), SkBits2Float(0x4281644d), SkBits2Float(0x43b7c000), SkBits2Float(0x42816a3c)); // 366.1f, 64.6833f, 366.8f, 64.6959f, 367.5f, 64.7075f +path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x4281702b), SkBits2Float(0x43b87333), SkBits2Float(0x428179d3), SkBits2Float(0x43b8cccd), SkBits2Float(0x42818048)); // 368.2f, 64.7191f, 368.9f, 64.7379f, 369.6f, 64.7505f +path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x428186bc), SkBits2Float(0x43b98000), SkBits2Float(0x42818d4a), SkBits2Float(0x43b9d99a), SkBits2Float(0x428190f6)); // 370.3f, 64.7632f, 371, 64.776f, 371.7f, 64.7831f +path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x428194a3), SkBits2Float(0x43ba8ccd), SkBits2Float(0x428193b0), SkBits2Float(0x43bae666), SkBits2Float(0x42819653)); // 372.4f, 64.7903f, 373.1f, 64.7885f, 373.8f, 64.7936f +path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x428198f6), SkBits2Float(0x43bb999a), SkBits2Float(0x42819840), SkBits2Float(0x43bbf333), SkBits2Float(0x4281a0c6)); // 374.5f, 64.7988f, 375.2f, 64.7974f, 375.9f, 64.814f +path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x4281a94d), SkBits2Float(0x43bca666), SkBits2Float(0x4281bc0d), SkBits2Float(0x43bd0000), SkBits2Float(0x4281c979)); // 376.6f, 64.8307f, 377.3f, 64.8673f, 378, 64.8935f +path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x4281d6e5), SkBits2Float(0x43bdb333), SkBits2Float(0x4281e6fe), SkBits2Float(0x43be0ccd), SkBits2Float(0x4281f14e)); // 378.7f, 64.9197f, 379.4f, 64.9512f, 380.1f, 64.9713f +path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x4281fb9e), SkBits2Float(0x43bec000), SkBits2Float(0x4281fd75), SkBits2Float(0x43bf199a), SkBits2Float(0x42820759)); // 380.8f, 64.9914f, 381.5f, 64.995f, 382.2f, 65.0144f +path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x4282113e), SkBits2Float(0x43bfcccd), SkBits2Float(0x42822559), SkBits2Float(0x43c02666), SkBits2Float(0x42822ca8)); // 382.9f, 65.0337f, 383.6f, 65.0729f, 384.3f, 65.0872f +path.lineTo(SkBits2Float(0x43c02666), SkBits2Float(0x42823333)); // 384.3f, 65.1f +path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333)); // 306.6f, 65.1f +path.close(); +path.moveTo(SkBits2Float(0x43c24000), SkBits2Float(0x42823333)); // 388.5f, 65.1f +path.lineTo(SkBits2Float(0x43c24000), SkBits2Float(0x42823333)); // 388.5f, 65.1f +path.cubicTo(SkBits2Float(0x43c2999a), SkBits2Float(0x42823333), SkBits2Float(0x43c2f333), SkBits2Float(0x428239f1), SkBits2Float(0x43c34ccd), SkBits2Float(0x42823333)); // 389.2f, 65.1f, 389.9f, 65.1132f, 390.6f, 65.1f +path.cubicTo(SkBits2Float(0x43c3a666), SkBits2Float(0x42822c75), SkBits2Float(0x43c40000), SkBits2Float(0x42822289), SkBits2Float(0x43c4599a), SkBits2Float(0x42820abe)); // 391.3f, 65.0868f, 392, 65.0675f, 392.7f, 65.021f +path.cubicTo(SkBits2Float(0x43c4b333), SkBits2Float(0x4281f2f3), SkBits2Float(0x43c50ccd), SkBits2Float(0x4281be4d), SkBits2Float(0x43c56666), SkBits2Float(0x4281a471)); // 393.4f, 64.9745f, 394.1f, 64.8717f, 394.8f, 64.8212f +path.cubicTo(SkBits2Float(0x43c5c000), SkBits2Float(0x42818a96), SkBits2Float(0x43c6199a), SkBits2Float(0x428177e3), SkBits2Float(0x43c67333), SkBits2Float(0x42816f99)); // 395.5f, 64.7707f, 396.2f, 64.7342f, 396.9f, 64.718f +path.cubicTo(SkBits2Float(0x43c6cccd), SkBits2Float(0x4281674f), SkBits2Float(0x43c72666), SkBits2Float(0x42817195), SkBits2Float(0x43c78000), SkBits2Float(0x428172b7)); // 397.6f, 64.7018f, 398.3f, 64.7218f, 399, 64.7241f +path.cubicTo(SkBits2Float(0x43c7d99a), SkBits2Float(0x428173d8), SkBits2Float(0x43c83333), SkBits2Float(0x42817528), SkBits2Float(0x43c88ccd), SkBits2Float(0x42817661)); // 399.7f, 64.7263f, 400.4f, 64.7288f, 401.1f, 64.7312f +path.cubicTo(SkBits2Float(0x43c8e666), SkBits2Float(0x4281779a), SkBits2Float(0x43c94000), SkBits2Float(0x4281778d), SkBits2Float(0x43c9999a), SkBits2Float(0x42817a0c)); // 401.8f, 64.7336f, 402.5f, 64.7335f, 403.2f, 64.7384f +path.cubicTo(SkBits2Float(0x43c9f333), SkBits2Float(0x42817c8c), SkBits2Float(0x43ca4ccd), SkBits2Float(0x42817f49), SkBits2Float(0x43caa666), SkBits2Float(0x4281855e)); // 403.9f, 64.7433f, 404.6f, 64.7486f, 405.3f, 64.7605f +path.cubicTo(SkBits2Float(0x43cb0000), SkBits2Float(0x42818b72), SkBits2Float(0x43cb599a), SkBits2Float(0x4281985b), SkBits2Float(0x43cbb333), SkBits2Float(0x42819e87)); // 406, 64.7724f, 406.7f, 64.7976f, 407.4f, 64.8096f +path.cubicTo(SkBits2Float(0x43cc0ccd), SkBits2Float(0x4281a4b3), SkBits2Float(0x43cc6666), SkBits2Float(0x4281a6ea), SkBits2Float(0x43ccc000), SkBits2Float(0x4281aa66)); // 408.1f, 64.8217f, 408.8f, 64.826f, 409.5f, 64.8328f +path.cubicTo(SkBits2Float(0x43cd199a), SkBits2Float(0x4281ade2), SkBits2Float(0x43cd7333), SkBits2Float(0x4281aad0), SkBits2Float(0x43cdcccd), SkBits2Float(0x4281b36e)); // 410.2f, 64.8396f, 410.9f, 64.8336f, 411.6f, 64.8504f +path.cubicTo(SkBits2Float(0x43ce2666), SkBits2Float(0x4281bc0c), SkBits2Float(0x43ce8000), SkBits2Float(0x4281d071), SkBits2Float(0x43ced99a), SkBits2Float(0x4281de19)); // 412.3f, 64.8673f, 413, 64.9071f, 413.7f, 64.9338f +path.cubicTo(SkBits2Float(0x43cf3333), SkBits2Float(0x4281ebc2), SkBits2Float(0x43cf8ccd), SkBits2Float(0x4281fb65), SkBits2Float(0x43cfe666), SkBits2Float(0x42820561)); // 414.4f, 64.9605f, 415.1f, 64.991f, 415.8f, 65.0105f +path.cubicTo(SkBits2Float(0x43d04000), SkBits2Float(0x42820f5d), SkBits2Float(0x43d0999a), SkBits2Float(0x428217a6), SkBits2Float(0x43d0f333), SkBits2Float(0x42821a01)); // 416.5f, 65.03f, 417.2f, 65.0462f, 417.9f, 65.0508f +path.cubicTo(SkBits2Float(0x43d14ccd), SkBits2Float(0x42821c5b), SkBits2Float(0x43d1a666), SkBits2Float(0x42821a47), SkBits2Float(0x43d20000), SkBits2Float(0x4282137f)); // 418.6f, 65.0554f, 419.3f, 65.0513f, 420, 65.0381f +path.cubicTo(SkBits2Float(0x43d2599a), SkBits2Float(0x42820cb6), SkBits2Float(0x43d2b333), SkBits2Float(0x4281fcb3), SkBits2Float(0x43d30ccd), SkBits2Float(0x4281f14e)); // 420.7f, 65.0248f, 421.4f, 64.9936f, 422.1f, 64.9713f +path.cubicTo(SkBits2Float(0x43d36666), SkBits2Float(0x4281e5e8), SkBits2Float(0x43d3c000), SkBits2Float(0x4281d645), SkBits2Float(0x43d4199a), SkBits2Float(0x4281cf1d)); // 422.8f, 64.949f, 423.5f, 64.9185f, 424.2f, 64.9045f +path.cubicTo(SkBits2Float(0x43d47333), SkBits2Float(0x4281c7f4), SkBits2Float(0x43d4cccd), SkBits2Float(0x4281c5d7), SkBits2Float(0x43d52666), SkBits2Float(0x4281c65c)); // 424.9f, 64.8905f, 425.6f, 64.8864f, 426.3f, 64.8874f +path.cubicTo(SkBits2Float(0x43d58000), SkBits2Float(0x4281c6e1), SkBits2Float(0x43d5d99a), SkBits2Float(0x4281d040), SkBits2Float(0x43d63333), SkBits2Float(0x4281d23a)); // 427, 64.8884f, 427.7f, 64.9067f, 428.4f, 64.9106f +path.cubicTo(SkBits2Float(0x43d68ccd), SkBits2Float(0x4281d435), SkBits2Float(0x43d6e666), SkBits2Float(0x4281d7ed), SkBits2Float(0x43d74000), SkBits2Float(0x4281d23a)); // 429.1f, 64.9145f, 429.8f, 64.9217f, 430.5f, 64.9106f +path.cubicTo(SkBits2Float(0x43d7999a), SkBits2Float(0x4281cc88), SkBits2Float(0x43d7f333), SkBits2Float(0x4281ba4e), SkBits2Float(0x43d84ccd), SkBits2Float(0x4281b009)); // 431.2f, 64.8995f, 431.9f, 64.8639f, 432.6f, 64.8438f +path.cubicTo(SkBits2Float(0x43d8a666), SkBits2Float(0x4281a5c5), SkBits2Float(0x43d90000), SkBits2Float(0x4281997b), SkBits2Float(0x43d9599a), SkBits2Float(0x428194a1)); // 433.3f, 64.8238f, 434, 64.7998f, 434.7f, 64.7903f +path.cubicTo(SkBits2Float(0x43d9b333), SkBits2Float(0x42818fc7), SkBits2Float(0x43da0ccd), SkBits2Float(0x4281929b), SkBits2Float(0x43da6666), SkBits2Float(0x428192ef)); // 435.4f, 64.7808f, 436.1f, 64.7863f, 436.8f, 64.787f +path.cubicTo(SkBits2Float(0x43dac000), SkBits2Float(0x42819343), SkBits2Float(0x43db199a), SkBits2Float(0x428194dc), SkBits2Float(0x43db7333), SkBits2Float(0x4281969a)); // 437.5f, 64.7876f, 438.2f, 64.7907f, 438.9f, 64.7941f +path.cubicTo(SkBits2Float(0x43dbcccd), SkBits2Float(0x42819858), SkBits2Float(0x43dc2666), SkBits2Float(0x42819925), SkBits2Float(0x43dc8000), SkBits2Float(0x42819d62)); // 439.6f, 64.7975f, 440.3f, 64.7991f, 441, 64.8074f +path.cubicTo(SkBits2Float(0x43dcd99a), SkBits2Float(0x4281a19f), SkBits2Float(0x43dd3333), SkBits2Float(0x4281a9d2), SkBits2Float(0x43dd8ccd), SkBits2Float(0x4281b009)); // 441.7f, 64.8157f, 442.4f, 64.8317f, 443.1f, 64.8438f +path.cubicTo(SkBits2Float(0x43dde666), SkBits2Float(0x4281b641), SkBits2Float(0x43de4000), SkBits2Float(0x4281be1f), SkBits2Float(0x43de999a), SkBits2Float(0x4281c2b1)); // 443.8f, 64.856f, 444.5f, 64.8713f, 445.2f, 64.8803f +path.cubicTo(SkBits2Float(0x43def333), SkBits2Float(0x4281c742), SkBits2Float(0x43df4ccd), SkBits2Float(0x4281ca45), SkBits2Float(0x43dfa666), SkBits2Float(0x4281cb72)); // 445.9f, 64.8892f, 446.6f, 64.8951f, 447.3f, 64.8974f +path.cubicTo(SkBits2Float(0x43e00000), SkBits2Float(0x4281cc9f), SkBits2Float(0x43e0599a), SkBits2Float(0x4281cb72), SkBits2Float(0x43e0b333), SkBits2Float(0x4281c9c0)); // 448, 64.8997f, 448.7f, 64.8974f, 449.4f, 64.894f +path.cubicTo(SkBits2Float(0x43e10ccd), SkBits2Float(0x4281c80e), SkBits2Float(0x43e16666), SkBits2Float(0x4281c34c), SkBits2Float(0x43e1c000), SkBits2Float(0x4281c145)); // 450.1f, 64.8907f, 450.8f, 64.8814f, 451.5f, 64.8775f +path.cubicTo(SkBits2Float(0x43e2199a), SkBits2Float(0x4281bf3f), SkBits2Float(0x43e27333), SkBits2Float(0x4281c026), SkBits2Float(0x43e2cccd), SkBits2Float(0x4281bd9a)); // 452.2f, 64.8735f, 452.9f, 64.8753f, 453.6f, 64.8703f +path.cubicTo(SkBits2Float(0x43e32666), SkBits2Float(0x4281bb0f), SkBits2Float(0x43e38000), SkBits2Float(0x4281b877), SkBits2Float(0x43e3d99a), SkBits2Float(0x4281b202)); // 454.3f, 64.8653f, 455, 64.8603f, 455.7f, 64.8477f +path.cubicTo(SkBits2Float(0x43e43333), SkBits2Float(0x4281ab8e), SkBits2Float(0x43e48ccd), SkBits2Float(0x4281a1fe), SkBits2Float(0x43e4e666), SkBits2Float(0x428196e0)); // 456.4f, 64.8351f, 457.1f, 64.8164f, 457.8f, 64.7947f +path.cubicTo(SkBits2Float(0x43e54000), SkBits2Float(0x42818bc3), SkBits2Float(0x43e5999a), SkBits2Float(0x42817cb2), SkBits2Float(0x43e5f333), SkBits2Float(0x42816f52)); // 458.5f, 64.773f, 459.2f, 64.7435f, 459.9f, 64.7174f +path.cubicTo(SkBits2Float(0x43e64ccd), SkBits2Float(0x428161f2), SkBits2Float(0x43e6a666), SkBits2Float(0x428151a5), SkBits2Float(0x43e70000), SkBits2Float(0x4281469f)); // 460.6f, 64.6913f, 461.3f, 64.6595f, 462, 64.6379f +path.cubicTo(SkBits2Float(0x43e7599a), SkBits2Float(0x42813b9a), SkBits2Float(0x43e7b333), SkBits2Float(0x428132d7), SkBits2Float(0x43e80ccd), SkBits2Float(0x42812d30)); // 462.7f, 64.6164f, 463.4f, 64.5993f, 464.1f, 64.5883f +path.cubicTo(SkBits2Float(0x43e86666), SkBits2Float(0x42812789), SkBits2Float(0x43e8c000), SkBits2Float(0x428125d6), SkBits2Float(0x43e9199a), SkBits2Float(0x428124b5)); // 464.8f, 64.5772f, 465.5f, 64.5739f, 466.2f, 64.5717f +path.cubicTo(SkBits2Float(0x43e97333), SkBits2Float(0x42812394), SkBits2Float(0x43e9cccd), SkBits2Float(0x4281258e), SkBits2Float(0x43ea2666), SkBits2Float(0x42812667)); // 466.9f, 64.5695f, 467.6f, 64.5733f, 468.3f, 64.575f +path.cubicTo(SkBits2Float(0x43ea8000), SkBits2Float(0x42812740), SkBits2Float(0x43ead99a), SkBits2Float(0x42812819), SkBits2Float(0x43eb3333), SkBits2Float(0x428129cb)); // 469, 64.5767f, 469.7f, 64.5783f, 470.4f, 64.5816f +path.cubicTo(SkBits2Float(0x43eb8ccd), SkBits2Float(0x42812b7e), SkBits2Float(0x43ebe666), SkBits2Float(0x42812734), SkBits2Float(0x43ec4000), SkBits2Float(0x42813094)); // 471.1f, 64.5849f, 471.8f, 64.5766f, 472.5f, 64.5949f +path.cubicTo(SkBits2Float(0x43ec999a), SkBits2Float(0x428139f3), SkBits2Float(0x43ecf333), SkBits2Float(0x42814838), SkBits2Float(0x43ed4ccd), SkBits2Float(0x42816208)); // 473.2f, 64.6132f, 473.9f, 64.6411f, 474.6f, 64.6915f +path.cubicTo(SkBits2Float(0x43eda666), SkBits2Float(0x42817bd8), SkBits2Float(0x43ee0000), SkBits2Float(0x4281a8c8), SkBits2Float(0x43ee599a), SkBits2Float(0x4281cb72)); // 475.3f, 64.7419f, 476, 64.8297f, 476.7f, 64.8974f +path.cubicTo(SkBits2Float(0x43eeb333), SkBits2Float(0x4281ee1c), SkBits2Float(0x43ef0ccd), SkBits2Float(0x428220ba), SkBits2Float(0x43ef6666), SkBits2Float(0x42823205)); // 477.4f, 64.9651f, 478.1f, 65.0639f, 478.8f, 65.0977f +path.cubicTo(SkBits2Float(0x43efc000), SkBits2Float(0x42824350), SkBits2Float(0x43f0199a), SkBits2Float(0x42823301), SkBits2Float(0x43f07333), SkBits2Float(0x42823333)); // 479.5f, 65.1315f, 480.2f, 65.0996f, 480.9f, 65.1f +path.lineTo(SkBits2Float(0x43f07333), SkBits2Float(0x42823333)); // 480.9f, 65.1f +path.lineTo(SkBits2Float(0x43c24000), SkBits2Float(0x42823333)); // 388.5f, 65.1f +path.close(); +path.moveTo(SkBits2Float(0x43fc0000), SkBits2Float(0x42823333)); // 504, 65.1f +path.lineTo(SkBits2Float(0x43fc0000), SkBits2Float(0x42823333)); // 504, 65.1f +path.cubicTo(SkBits2Float(0x43fc599a), SkBits2Float(0x42823333), SkBits2Float(0x43fcb333), SkBits2Float(0x42823772), SkBits2Float(0x43fd0ccd), SkBits2Float(0x42823333)); // 504.7f, 65.1f, 505.4f, 65.1083f, 506.1f, 65.1f +path.cubicTo(SkBits2Float(0x43fd6666), SkBits2Float(0x42822ef4), SkBits2Float(0x43fdc000), SkBits2Float(0x428227e9), SkBits2Float(0x43fe199a), SkBits2Float(0x428219ba)); // 506.8f, 65.0917f, 507.5f, 65.0779f, 508.2f, 65.0502f +path.cubicTo(SkBits2Float(0x43fe7333), SkBits2Float(0x42820b8b), SkBits2Float(0x43fecccd), SkBits2Float(0x4281ebf3), SkBits2Float(0x43ff2666), SkBits2Float(0x4281de19)); // 508.9f, 65.0225f, 509.6f, 64.9608f, 510.3f, 64.9338f +path.cubicTo(SkBits2Float(0x43ff8000), SkBits2Float(0x4281d040), SkBits2Float(0x43ffd99a), SkBits2Float(0x4281cbde), SkBits2Float(0x4400199a), SkBits2Float(0x4281c6a2)); // 511, 64.9067f, 511.7f, 64.8982f, 512.4f, 64.888f +path.cubicTo(SkBits2Float(0x44004666), SkBits2Float(0x4281c167), SkBits2Float(0x44007333), SkBits2Float(0x4281bf82), SkBits2Float(0x4400a000), SkBits2Float(0x4281beb5)); // 513.1f, 64.8777f, 513.8f, 64.874f, 514.5f, 64.8725f +path.cubicTo(SkBits2Float(0x4400cccd), SkBits2Float(0x4281bde8), SkBits2Float(0x4400f99a), SkBits2Float(0x4281bfe4), SkBits2Float(0x44012666), SkBits2Float(0x4281c1d2)); // 515.2f, 64.8709f, 515.9f, 64.8748f, 516.6f, 64.8786f +path.cubicTo(SkBits2Float(0x44015333), SkBits2Float(0x4281c3c1), SkBits2Float(0x44018000), SkBits2Float(0x4281c822), SkBits2Float(0x4401accd), SkBits2Float(0x4281ca4d)); // 517.3f, 64.8823f, 518, 64.8909f, 518.7f, 64.8951f +path.cubicTo(SkBits2Float(0x4401d99a), SkBits2Float(0x4281cc78), SkBits2Float(0x44020666), SkBits2Float(0x4281cf4e), SkBits2Float(0x44023333), SkBits2Float(0x4281ced6)); // 519.4f, 64.8994f, 520.1f, 64.9049f, 520.8f, 64.904f +path.cubicTo(SkBits2Float(0x44026000), SkBits2Float(0x4281ce5f), SkBits2Float(0x44028ccd), SkBits2Float(0x4281cb81), SkBits2Float(0x4402b99a), SkBits2Float(0x4281c781)); // 521.5f, 64.9031f, 522.2f, 64.8975f, 522.9f, 64.8897f +path.cubicTo(SkBits2Float(0x4402e666), SkBits2Float(0x4281c380), SkBits2Float(0x44031333), SkBits2Float(0x4281babb), SkBits2Float(0x44034000), SkBits2Float(0x4281b6d2)); // 523.6f, 64.8818f, 524.3f, 64.8647f, 525, 64.8571f +path.cubicTo(SkBits2Float(0x44036ccd), SkBits2Float(0x4281b2e9), SkBits2Float(0x4403999a), SkBits2Float(0x4281b0a6), SkBits2Float(0x4403c666), SkBits2Float(0x4281b009)); // 525.7f, 64.8494f, 526.4f, 64.845f, 527.1f, 64.8438f +path.cubicTo(SkBits2Float(0x4403f333), SkBits2Float(0x4281af6d), SkBits2Float(0x44042000), SkBits2Float(0x42819d4b), SkBits2Float(0x44044ccd), SkBits2Float(0x4281b327)); // 527.8f, 64.8426f, 528.5f, 64.8072f, 529.2f, 64.8499f +path.cubicTo(SkBits2Float(0x4404799a), SkBits2Float(0x4281c903), SkBits2Float(0x4404a666), SkBits2Float(0x42821ddc), SkBits2Float(0x4404d333), SkBits2Float(0x42823333)); // 529.9f, 64.8926f, 530.6f, 65.0583f, 531.3f, 65.1f +path.cubicTo(SkBits2Float(0x44050000), SkBits2Float(0x4282488b), SkBits2Float(0x44052ccd), SkBits2Float(0x42823333), SkBits2Float(0x4405599a), SkBits2Float(0x42823333)); // 532, 65.1417f, 532.7f, 65.1f, 533.4f, 65.1f +path.lineTo(SkBits2Float(0x4405599a), SkBits2Float(0x42823333)); // 533.4f, 65.1f +path.lineTo(SkBits2Float(0x43fc0000), SkBits2Float(0x42823333)); // 504, 65.1f +path.close(); + return path; +} + +static SkPath path2() { + SkPath path; +path.moveTo(SkBits2Float(0x431d8000), SkBits2Float(0x42823333)); // 157.5f, 65.1f +path.lineTo(SkBits2Float(0x431d8000), SkBits2Float(0x42823333)); // 157.5f, 65.1f +path.cubicTo(SkBits2Float(0x431e3333), SkBits2Float(0x42823333), SkBits2Float(0x431ee666), SkBits2Float(0x42822fd9), SkBits2Float(0x431f999a), SkBits2Float(0x42823333)); // 158.2f, 65.1f, 158.9f, 65.0935f, 159.6f, 65.1f +path.cubicTo(SkBits2Float(0x43204ccd), SkBits2Float(0x4282368d), SkBits2Float(0x43210000), SkBits2Float(0x42823e05), SkBits2Float(0x4321b333), SkBits2Float(0x4282474f)); // 160.3f, 65.1065f, 161, 65.1211f, 161.7f, 65.1393f +path.cubicTo(SkBits2Float(0x43226666), SkBits2Float(0x42825098), SkBits2Float(0x4323199a), SkBits2Float(0x42825f49), SkBits2Float(0x4323cccd), SkBits2Float(0x42826aeb)); // 162.4f, 65.1574f, 163.1f, 65.1861f, 163.8f, 65.2088f +path.cubicTo(SkBits2Float(0x43248000), SkBits2Float(0x4282768e), SkBits2Float(0x43253333), SkBits2Float(0x428281ff), SkBits2Float(0x4325e666), SkBits2Float(0x42828d1c)); // 164.5f, 65.2316f, 165.2f, 65.2539f, 165.9f, 65.2756f +path.cubicTo(SkBits2Float(0x4326999a), SkBits2Float(0x4282983a), SkBits2Float(0x43274ccd), SkBits2Float(0x4282a509), SkBits2Float(0x43280000), SkBits2Float(0x4282ad9b)); // 166.6f, 65.2973f, 167.3f, 65.3223f, 168, 65.3391f +path.cubicTo(SkBits2Float(0x4328b333), SkBits2Float(0x4282b62d), SkBits2Float(0x43296666), SkBits2Float(0x4282bff9), SkBits2Float(0x432a199a), SkBits2Float(0x4282c089)); // 168.7f, 65.3558f, 169.4f, 65.3749f, 170.1f, 65.376f +path.cubicTo(SkBits2Float(0x432acccd), SkBits2Float(0x4282c11a), SkBits2Float(0x432b8000), SkBits2Float(0x4282b797), SkBits2Float(0x432c3333), SkBits2Float(0x4282b100)); // 170.8f, 65.3772f, 171.5f, 65.3586f, 172.2f, 65.3457f +path.cubicTo(SkBits2Float(0x432ce666), SkBits2Float(0x4282aa68), SkBits2Float(0x432d999a), SkBits2Float(0x4282a054), SkBits2Float(0x432e4ccd), SkBits2Float(0x428298fb)); // 172.9f, 65.3328f, 173.6f, 65.3131f, 174.3f, 65.2988f +path.cubicTo(SkBits2Float(0x432f0000), SkBits2Float(0x428291a2), SkBits2Float(0x432fb333), SkBits2Float(0x428289f3), SkBits2Float(0x43306666), SkBits2Float(0x428284e8)); // 175, 65.2844f, 175.7f, 65.2694f, 176.4f, 65.2596f +path.cubicTo(SkBits2Float(0x4331199a), SkBits2Float(0x42827fde), SkBits2Float(0x4331cccd), SkBits2Float(0x42827cff), SkBits2Float(0x43328000), SkBits2Float(0x42827abc)); // 177.1f, 65.2497f, 177.8f, 65.2441f, 178.5f, 65.2397f +path.cubicTo(SkBits2Float(0x43333333), SkBits2Float(0x42827879), SkBits2Float(0x4333e666), SkBits2Float(0x428277a0), SkBits2Float(0x4334999a), SkBits2Float(0x42827757)); // 179.2f, 65.2353f, 179.9f, 65.2336f, 180.6f, 65.2331f +path.cubicTo(SkBits2Float(0x43354ccd), SkBits2Float(0x4282770f), SkBits2Float(0x43360000), SkBits2Float(0x428277ac), SkBits2Float(0x4336b333), SkBits2Float(0x4282790a)); // 181.3f, 65.2325f, 182, 65.2337f, 182.7f, 65.2364f +path.cubicTo(SkBits2Float(0x43376666), SkBits2Float(0x42827a68), SkBits2Float(0x4338199a), SkBits2Float(0x42827d9d), SkBits2Float(0x4338cccd), SkBits2Float(0x42827f8b)); // 183.4f, 65.2391f, 184.1f, 65.2453f, 184.8f, 65.2491f +path.cubicTo(SkBits2Float(0x43398000), SkBits2Float(0x4282817a), SkBits2Float(0x433a3333), SkBits2Float(0x428283c9), SkBits2Float(0x433ae666), SkBits2Float(0x428284a2)); // 185.5f, 65.2529f, 186.2f, 65.2574f, 186.9f, 65.259f +path.cubicTo(SkBits2Float(0x433b999a), SkBits2Float(0x4282857b), SkBits2Float(0x433c4ccd), SkBits2Float(0x428284de), SkBits2Float(0x433d0000), SkBits2Float(0x428284a2)); // 187.6f, 65.2607f, 188.3f, 65.2595f, 189, 65.259f +path.cubicTo(SkBits2Float(0x433db333), SkBits2Float(0x42828465), SkBits2Float(0x433e6666), SkBits2Float(0x42828251), SkBits2Float(0x433f199a), SkBits2Float(0x42828336)); // 189.7f, 65.2586f, 190.4f, 65.2545f, 191.1f, 65.2563f +path.cubicTo(SkBits2Float(0x433fcccd), SkBits2Float(0x4282841b), SkBits2Float(0x43408000), SkBits2Float(0x428286ef), SkBits2Float(0x43413333), SkBits2Float(0x428289ff)); // 191.8f, 65.258f, 192.5f, 65.2635f, 193.2f, 65.2695f +path.cubicTo(SkBits2Float(0x4341e666), SkBits2Float(0x42828d0f), SkBits2Float(0x4342999a), SkBits2Float(0x4282930c), SkBits2Float(0x43434ccd), SkBits2Float(0x42829597)); // 193.9f, 65.2755f, 194.6f, 65.2872f, 195.3f, 65.2922f +path.cubicTo(SkBits2Float(0x43440000), SkBits2Float(0x42829822), SkBits2Float(0x4344b333), SkBits2Float(0x428298fa), SkBits2Float(0x43456666), SkBits2Float(0x42829942)); // 196, 65.2971f, 196.7f, 65.2988f, 197.4f, 65.2993f +path.cubicTo(SkBits2Float(0x4346199a), SkBits2Float(0x4282998a), SkBits2Float(0x4346cccd), SkBits2Float(0x42829749), SkBits2Float(0x43478000), SkBits2Float(0x42829749)); // 198.1f, 65.2999f, 198.8f, 65.2955f, 199.5f, 65.2955f +path.cubicTo(SkBits2Float(0x43483333), SkBits2Float(0x42829749), SkBits2Float(0x4348e666), SkBits2Float(0x428297d8), SkBits2Float(0x4349999a), SkBits2Float(0x42829942)); // 200.2f, 65.2955f, 200.9f, 65.2966f, 201.6f, 65.2993f +path.cubicTo(SkBits2Float(0x434a4ccd), SkBits2Float(0x42829aac), SkBits2Float(0x434b0000), SkBits2Float(0x42829e12), SkBits2Float(0x434bb333), SkBits2Float(0x42829fc4)); // 202.3f, 65.3021f, 203, 65.3087f, 203.7f, 65.312f +path.cubicTo(SkBits2Float(0x434c6666), SkBits2Float(0x4282a176), SkBits2Float(0x434d199a), SkBits2Float(0x4282a1f9), SkBits2Float(0x434dcccd), SkBits2Float(0x4282a36f)); // 204.4f, 65.3154f, 205.1f, 65.3164f, 205.8f, 65.3192f +path.cubicTo(SkBits2Float(0x434e8000), SkBits2Float(0x4282a4e4), SkBits2Float(0x434f3333), SkBits2Float(0x4282a71b), SkBits2Float(0x434fe666), SkBits2Float(0x4282a885)); // 206.5f, 65.3221f, 207.2f, 65.3264f, 207.9f, 65.3291f +path.cubicTo(SkBits2Float(0x4350999a), SkBits2Float(0x4282a9ef), SkBits2Float(0x43514ccd), SkBits2Float(0x4282aad4), SkBits2Float(0x43520000), SkBits2Float(0x4282abe9)); // 208.6f, 65.3319f, 209.3f, 65.3336f, 210, 65.3358f +path.cubicTo(SkBits2Float(0x4352b333), SkBits2Float(0x4282acff), SkBits2Float(0x43536666), SkBits2Float(0x4282ad0c), SkBits2Float(0x4354199a), SkBits2Float(0x4282af07)); // 210.7f, 65.3379f, 211.4f, 65.338f, 212.1f, 65.3419f +path.cubicTo(SkBits2Float(0x4354cccd), SkBits2Float(0x4282b101), SkBits2Float(0x43558000), SkBits2Float(0x4282b5c2), SkBits2Float(0x43563333), SkBits2Float(0x4282b7c8)); // 212.8f, 65.3457f, 213.5f, 65.355f, 214.2f, 65.3589f +path.cubicTo(SkBits2Float(0x4356e666), SkBits2Float(0x4282b9ce), SkBits2Float(0x4357999a), SkBits2Float(0x4282b9c3), SkBits2Float(0x43584ccd), SkBits2Float(0x4282bb2c)); // 214.9f, 65.3629f, 215.6f, 65.3628f, 216.3f, 65.3656f +path.cubicTo(SkBits2Float(0x43590000), SkBits2Float(0x4282bc96), SkBits2Float(0x4359b333), SkBits2Float(0x4282be3c), SkBits2Float(0x435a6666), SkBits2Float(0x4282c043)); // 217, 65.3683f, 217.7f, 65.3716f, 218.4f, 65.3755f +path.cubicTo(SkBits2Float(0x435b199a), SkBits2Float(0x4282c249), SkBits2Float(0x435bcccd), SkBits2Float(0x4282c50f), SkBits2Float(0x435c8000), SkBits2Float(0x4282c752)); // 219.1f, 65.3795f, 219.8f, 65.3849f, 220.5f, 65.3893f +path.cubicTo(SkBits2Float(0x435d3333), SkBits2Float(0x4282c995), SkBits2Float(0x435de666), SkBits2Float(0x4282cc2d), SkBits2Float(0x435e999a), SkBits2Float(0x4282cdd4)); // 221.2f, 65.3937f, 221.9f, 65.3988f, 222.6f, 65.402f +path.cubicTo(SkBits2Float(0x435f4ccd), SkBits2Float(0x4282cf7a), SkBits2Float(0x43600000), SkBits2Float(0x4282d211), SkBits2Float(0x4360b333), SkBits2Float(0x4282d138)); // 223.3f, 65.4052f, 224, 65.4103f, 224.7f, 65.4086f +path.cubicTo(SkBits2Float(0x43616666), SkBits2Float(0x4282d05f), SkBits2Float(0x4362199a), SkBits2Float(0x4282cd7f), SkBits2Float(0x4362cccd), SkBits2Float(0x4282c8bd)); // 225.4f, 65.407f, 226.1f, 65.4014f, 226.8f, 65.3921f +path.cubicTo(SkBits2Float(0x43638000), SkBits2Float(0x4282c3fb), SkBits2Float(0x43643333), SkBits2Float(0x4282b8dc), SkBits2Float(0x4364e666), SkBits2Float(0x4282b4aa)); // 227.5f, 65.3828f, 228.2f, 65.3611f, 228.9f, 65.3529f +path.cubicTo(SkBits2Float(0x4365999a), SkBits2Float(0x4282b079), SkBits2Float(0x43664ccd), SkBits2Float(0x4282af57), SkBits2Float(0x43670000), SkBits2Float(0x4282af94)); // 229.6f, 65.3447f, 230.3f, 65.3425f, 231, 65.3429f +path.cubicTo(SkBits2Float(0x4367b333), SkBits2Float(0x4282afd1), SkBits2Float(0x43686666), SkBits2Float(0x4282b38b), SkBits2Float(0x4369199a), SkBits2Float(0x4282b616)); // 231.7f, 65.3434f, 232.4f, 65.3507f, 233.1f, 65.3556f +path.cubicTo(SkBits2Float(0x4369cccd), SkBits2Float(0x4282b8a1), SkBits2Float(0x436a8000), SkBits2Float(0x4282bc7d), SkBits2Float(0x436b3333), SkBits2Float(0x4282bed7)); // 233.8f, 65.3606f, 234.5f, 65.3681f, 235.2f, 65.3727f +path.cubicTo(SkBits2Float(0x436be666), SkBits2Float(0x4282c131), SkBits2Float(0x436c999a), SkBits2Float(0x4282c10c), SkBits2Float(0x436d4ccd), SkBits2Float(0x4282c434)); // 235.9f, 65.3773f, 236.6f, 65.377f, 237.3f, 65.3832f +path.cubicTo(SkBits2Float(0x436e0000), SkBits2Float(0x4282c75c), SkBits2Float(0x436eb333), SkBits2Float(0x4282cb8d), SkBits2Float(0x436f6666), SkBits2Float(0x4282d1c5)); // 238, 65.3894f, 238.7f, 65.3976f, 239.4f, 65.4097f +path.cubicTo(SkBits2Float(0x4370199a), SkBits2Float(0x4282d7fd), SkBits2Float(0x4370cccd), SkBits2Float(0x4282e22a), SkBits2Float(0x43718000), SkBits2Float(0x4282e983)); // 240.1f, 65.4219f, 240.8f, 65.4417f, 241.5f, 65.4561f +path.cubicTo(SkBits2Float(0x43723333), SkBits2Float(0x4282f0dc), SkBits2Float(0x4372e666), SkBits2Float(0x4282fa30), SkBits2Float(0x4373999a), SkBits2Float(0x4282fddc)); // 242.2f, 65.4704f, 242.9f, 65.4886f, 243.6f, 65.4958f +path.cubicTo(SkBits2Float(0x43744ccd), SkBits2Float(0x42830189), SkBits2Float(0x43750000), SkBits2Float(0x4282fec1), SkBits2Float(0x4375b333), SkBits2Float(0x4282ff8e)); // 244.3f, 65.503f, 245, 65.4976f, 245.7f, 65.4991f +path.cubicTo(SkBits2Float(0x43766666), SkBits2Float(0x4283005c), SkBits2Float(0x4377199a), SkBits2Float(0x42830015), SkBits2Float(0x4377cccd), SkBits2Float(0x428302ac)); // 246.4f, 65.5007f, 247.1f, 65.5002f, 247.8f, 65.5052f +path.cubicTo(SkBits2Float(0x43788000), SkBits2Float(0x42830543), SkBits2Float(0x43793333), SkBits2Float(0x42830b0b), SkBits2Float(0x4379e666), SkBits2Float(0x42830f18)); // 248.5f, 65.5103f, 249.2f, 65.5216f, 249.9f, 65.5295f +path.cubicTo(SkBits2Float(0x437a999a), SkBits2Float(0x42831324), SkBits2Float(0x437b4ccd), SkBits2Float(0x428316b9), SkBits2Float(0x437c0000), SkBits2Float(0x42831af7)); // 250.6f, 65.5374f, 251.3f, 65.5444f, 252, 65.5527f +path.cubicTo(SkBits2Float(0x437cb333), SkBits2Float(0x42831f34), SkBits2Float(0x437d6666), SkBits2Float(0x428327af), SkBits2Float(0x437e199a), SkBits2Float(0x42832888)); // 252.7f, 65.5609f, 253.4f, 65.5775f, 254.1f, 65.5792f +path.cubicTo(SkBits2Float(0x437ecccd), SkBits2Float(0x42832961), SkBits2Float(0x437f8000), SkBits2Float(0x42832e8f), SkBits2Float(0x4380199a), SkBits2Float(0x4283200d)); // 254.8f, 65.5808f, 255.5f, 65.5909f, 256.2f, 65.5626f +path.cubicTo(SkBits2Float(0x43807333), SkBits2Float(0x4283118c), SkBits2Float(0x4380cccd), SkBits2Float(0x4282f101), SkBits2Float(0x43812666), SkBits2Float(0x4282d17e)); // 256.9f, 65.5343f, 257.6f, 65.4707f, 258.3f, 65.4092f +path.cubicTo(SkBits2Float(0x43818000), SkBits2Float(0x4282b1fc), SkBits2Float(0x4381d99a), SkBits2Float(0x42827d60), SkBits2Float(0x43823333), SkBits2Float(0x428262fe)); // 259, 65.3476f, 259.7f, 65.2449f, 260.4f, 65.1933f +path.cubicTo(SkBits2Float(0x43828ccd), SkBits2Float(0x4282489c), SkBits2Float(0x4382e666), SkBits2Float(0x42823b2a), SkBits2Float(0x43834000), SkBits2Float(0x42823333)); // 261.1f, 65.1418f, 261.8f, 65.1156f, 262.5f, 65.1f +path.cubicTo(SkBits2Float(0x4383999a), SkBits2Float(0x42822b3c), SkBits2Float(0x4383f333), SkBits2Float(0x42823333), SkBits2Float(0x43844ccd), SkBits2Float(0x42823333)); // 263.2f, 65.0844f, 263.9f, 65.1f, 264.6f, 65.1f +path.lineTo(SkBits2Float(0x43844ccd), SkBits2Float(0x42823333)); // 264.6f, 65.1f +path.lineTo(SkBits2Float(0x431d8000), SkBits2Float(0x42823333)); // 157.5f, 65.1f +path.close(); +path.moveTo(SkBits2Float(0x438dc000), SkBits2Float(0x42823333)); // 283.5f, 65.1f +path.lineTo(SkBits2Float(0x438dc000), SkBits2Float(0x42823333)); // 283.5f, 65.1f +path.cubicTo(SkBits2Float(0x438e199a), SkBits2Float(0x4282356b), SkBits2Float(0x438e7333), SkBits2Float(0x42823d2c), SkBits2Float(0x438ecccd), SkBits2Float(0x42824086)); // 284.2f, 65.1043f, 284.9f, 65.1195f, 285.6f, 65.126f +path.cubicTo(SkBits2Float(0x438f2666), SkBits2Float(0x428243e0), SkBits2Float(0x438f8000), SkBits2Float(0x42824988), SkBits2Float(0x438fd99a), SkBits2Float(0x4282474f)); // 286.3f, 65.1326f, 287, 65.1436f, 287.7f, 65.1393f +path.cubicTo(SkBits2Float(0x43903333), SkBits2Float(0x42824516), SkBits2Float(0x43908ccd), SkBits2Float(0x4282368d), SkBits2Float(0x4390e666), SkBits2Float(0x42823333)); // 288.4f, 65.1349f, 289.1f, 65.1065f, 289.8f, 65.1f +path.lineTo(SkBits2Float(0x4390e666), SkBits2Float(0x42823333)); // 289.8f, 65.1f +path.lineTo(SkBits2Float(0x438dc000), SkBits2Float(0x42823333)); // 283.5f, 65.1f +path.close(); +path.moveTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333)); // 306.6f, 65.1f +path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333)); // 306.6f, 65.1f +path.cubicTo(SkBits2Float(0x4399a666), SkBits2Float(0x42823334), SkBits2Float(0x439a0000), SkBits2Float(0x42822e24), SkBits2Float(0x439a599a), SkBits2Float(0x4282333c)); // 307.3f, 65.1f, 308, 65.0901f, 308.7f, 65.1001f +path.cubicTo(SkBits2Float(0x439ab333), SkBits2Float(0x42823854), SkBits2Float(0x439b0ccd), SkBits2Float(0x428247d2), SkBits2Float(0x439b6666), SkBits2Float(0x428251c2)); // 309.4f, 65.11f, 310.1f, 65.1403f, 310.8f, 65.1597f +path.cubicTo(SkBits2Float(0x439bc000), SkBits2Float(0x42825bb2), SkBits2Float(0x439c199a), SkBits2Float(0x428264e1), SkBits2Float(0x439c7333), SkBits2Float(0x42826edd)); // 311.5f, 65.1791f, 312.2f, 65.197f, 312.9f, 65.2165f +path.cubicTo(SkBits2Float(0x439ccccd), SkBits2Float(0x428278d9), SkBits2Float(0x439d2666), SkBits2Float(0x428282d5), SkBits2Float(0x439d8000), SkBits2Float(0x42828daa)); // 313.6f, 65.236f, 314.3f, 65.2555f, 315, 65.2767f +path.cubicTo(SkBits2Float(0x439dd99a), SkBits2Float(0x4282987f), SkBits2Float(0x439e3333), SkBits2Float(0x4282a5a2), SkBits2Float(0x439e8ccd), SkBits2Float(0x4282afdb)); // 315.7f, 65.2978f, 316.4f, 65.3235f, 317.1f, 65.3435f +path.cubicTo(SkBits2Float(0x439ee666), SkBits2Float(0x4282ba13), SkBits2Float(0x439f4000), SkBits2Float(0x4282c3ec), SkBits2Float(0x439f999a), SkBits2Float(0x4282cafd)); // 317.8f, 65.3634f, 318.5f, 65.3827f, 319.2f, 65.3965f +path.cubicTo(SkBits2Float(0x439ff333), SkBits2Float(0x4282d20d), SkBits2Float(0x43a04ccd), SkBits2Float(0x4282d6db), SkBits2Float(0x43a0a666), SkBits2Float(0x4282da40)); // 319.9f, 65.4103f, 320.6f, 65.4196f, 321.3f, 65.4263f +path.cubicTo(SkBits2Float(0x43a10000), SkBits2Float(0x4282dda4), SkBits2Float(0x43a1599a), SkBits2Float(0x4282ded1), SkBits2Float(0x43a1b333), SkBits2Float(0x4282df56)); // 322, 65.4329f, 322.7f, 65.4352f, 323.4f, 65.4362f +path.cubicTo(SkBits2Float(0x43a20ccd), SkBits2Float(0x4282dfdb), SkBits2Float(0x43a26666), SkBits2Float(0x4282de42), SkBits2Float(0x43a2c000), SkBits2Float(0x4282dd5d)); // 324.1f, 65.4372f, 324.8f, 65.4341f, 325.5f, 65.4324f +path.cubicTo(SkBits2Float(0x43a3199a), SkBits2Float(0x4282dc78), SkBits2Float(0x43a37333), SkBits2Float(0x4282d884), SkBits2Float(0x43a3cccd), SkBits2Float(0x4282d9f9)); // 326.2f, 65.4306f, 326.9f, 65.4229f, 327.6f, 65.4257f +path.cubicTo(SkBits2Float(0x43a42666), SkBits2Float(0x4282db6f), SkBits2Float(0x43a48000), SkBits2Float(0x4282e0a8), SkBits2Float(0x43a4d99a), SkBits2Float(0x4282e61e)); // 328.3f, 65.4286f, 329, 65.4388f, 329.7f, 65.4494f +path.cubicTo(SkBits2Float(0x43a53333), SkBits2Float(0x4282eb95), SkBits2Float(0x43a58ccd), SkBits2Float(0x4282f833), SkBits2Float(0x43a5e666), SkBits2Float(0x4282fabf)); // 330.4f, 65.4601f, 331.1f, 65.4848f, 331.8f, 65.4897f +path.cubicTo(SkBits2Float(0x43a64000), SkBits2Float(0x4282fd4a), SkBits2Float(0x43a6999a), SkBits2Float(0x4282fb20), SkBits2Float(0x43a6f333), SkBits2Float(0x4282f562)); // 332.5f, 65.4947f, 333.2f, 65.4905f, 333.9f, 65.4793f +path.cubicTo(SkBits2Float(0x43a74ccd), SkBits2Float(0x4282efa3), SkBits2Float(0x43a7a666), SkBits2Float(0x4282e6bd), SkBits2Float(0x43a80000), SkBits2Float(0x4282d847)); // 334.6f, 65.468f, 335.3f, 65.4507f, 336, 65.4224f +path.cubicTo(SkBits2Float(0x43a8599a), SkBits2Float(0x4282c9d1), SkBits2Float(0x43a8b333), SkBits2Float(0x4282b47a), SkBits2Float(0x43a90ccd), SkBits2Float(0x42829e9f)); // 336.7f, 65.3942f, 337.4f, 65.3525f, 338.1f, 65.3098f +path.cubicTo(SkBits2Float(0x43a96666), SkBits2Float(0x428288c4), SkBits2Float(0x43a9c000), SkBits2Float(0x42825c97), SkBits2Float(0x43aa199a), SkBits2Float(0x42825526)); // 338.8f, 65.2671f, 339.5f, 65.1808f, 340.2f, 65.1663f +path.cubicTo(SkBits2Float(0x43aa7333), SkBits2Float(0x42824db6), SkBits2Float(0x43aacccd), SkBits2Float(0x42825867), SkBits2Float(0x43ab2666), SkBits2Float(0x428271fb)); // 340.9f, 65.1518f, 341.6f, 65.1727f, 342.3f, 65.2226f +path.cubicTo(SkBits2Float(0x43ab8000), SkBits2Float(0x42828b8e), SkBits2Float(0x43abd99a), SkBits2Float(0x4282cd10), SkBits2Float(0x43ac3333), SkBits2Float(0x4282ee99)); // 343, 65.2726f, 343.7f, 65.4005f, 344.4f, 65.466f +path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42831022), SkBits2Float(0x43ace666), SkBits2Float(0x42832d56), SkBits2Float(0x43ad4000), SkBits2Float(0x42833b2f)); // 345.1f, 65.5315f, 345.8f, 65.5885f, 346.5f, 65.6156f +path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42834908), SkBits2Float(0x43adf333), SkBits2Float(0x428342d2), SkBits2Float(0x43ae4ccd), SkBits2Float(0x428341b1)); // 347.2f, 65.6426f, 347.9f, 65.6305f, 348.6f, 65.6283f +path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x42834090), SkBits2Float(0x43af0000), SkBits2Float(0x42833a4a), SkBits2Float(0x43af599a), SkBits2Float(0x42833467)); // 349.3f, 65.6261f, 350, 65.6138f, 350.7f, 65.6023f +path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x42832e83), SkBits2Float(0x43b00ccd), SkBits2Float(0x428324e7), SkBits2Float(0x43b06666), SkBits2Float(0x42831e5b)); // 351.4f, 65.5908f, 352.1f, 65.5721f, 352.8f, 65.5593f +path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x428317cf), SkBits2Float(0x43b1199a), SkBits2Float(0x4283131a), SkBits2Float(0x43b17333), SkBits2Float(0x42830d1f)); // 353.5f, 65.5465f, 354.2f, 65.5373f, 354.9f, 65.5256f +path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42830724), SkBits2Float(0x43b22666), SkBits2Float(0x42830067), SkBits2Float(0x43b28000), SkBits2Float(0x4282fa78)); // 355.6f, 65.5139f, 356.3f, 65.5008f, 357, 65.4892f +path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x4282f488), SkBits2Float(0x43b33333), SkBits2Float(0x4282eb71), SkBits2Float(0x43b38ccd), SkBits2Float(0x4282e983)); // 357.7f, 65.4776f, 358.4f, 65.4598f, 359.1f, 65.4561f +path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x4282e794), SkBits2Float(0x43b44000), SkBits2Float(0x4282ea97), SkBits2Float(0x43b4999a), SkBits2Float(0x4282eee0)); // 359.8f, 65.4523f, 360.5f, 65.4582f, 361.2f, 65.4666f +path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x4282f329), SkBits2Float(0x43b54ccd), SkBits2Float(0x4282febf), SkBits2Float(0x43b5a666), SkBits2Float(0x42830339)); // 361.9f, 65.4749f, 362.6f, 65.4976f, 363.3f, 65.5063f +path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x428307b3), SkBits2Float(0x43b6599a), SkBits2Float(0x42830ae8), SkBits2Float(0x43b6b333), SkBits2Float(0x428309bb)); // 364, 65.515f, 364.7f, 65.5213f, 365.4f, 65.519f +path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x4283088e), SkBits2Float(0x43b76666), SkBits2Float(0x42830219), SkBits2Float(0x43b7c000), SkBits2Float(0x4282fc2a)); // 366.1f, 65.5167f, 366.8f, 65.5041f, 367.5f, 65.4925f +path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x4282f63b), SkBits2Float(0x43b87333), SkBits2Float(0x4282ec93), SkBits2Float(0x43b8cccd), SkBits2Float(0x4282e61e)); // 368.2f, 65.4809f, 368.9f, 65.4621f, 369.6f, 65.4494f +path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x4282dfaa), SkBits2Float(0x43b98000), SkBits2Float(0x4282d91c), SkBits2Float(0x43b9d99a), SkBits2Float(0x4282d570)); // 370.3f, 65.4368f, 371, 65.424f, 371.7f, 65.4169f +path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x4282d1c3), SkBits2Float(0x43ba8ccd), SkBits2Float(0x4282d2b6), SkBits2Float(0x43bae666), SkBits2Float(0x4282d013)); // 372.4f, 65.4097f, 373.1f, 65.4115f, 373.8f, 65.4064f +path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x4282cd70), SkBits2Float(0x43bb999a), SkBits2Float(0x4282ce26), SkBits2Float(0x43bbf333), SkBits2Float(0x4282c5a0)); // 374.5f, 65.4012f, 375.2f, 65.4026f, 375.9f, 65.386f +path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x4282bd19), SkBits2Float(0x43bca666), SkBits2Float(0x4282aa59), SkBits2Float(0x43bd0000), SkBits2Float(0x42829ced)); // 376.6f, 65.3693f, 377.3f, 65.3327f, 378, 65.3065f +path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x42828f81), SkBits2Float(0x43bdb333), SkBits2Float(0x42827f68), SkBits2Float(0x43be0ccd), SkBits2Float(0x42827518)); // 378.7f, 65.2803f, 379.4f, 65.2488f, 380.1f, 65.2287f +path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x42826ac8), SkBits2Float(0x43bec000), SkBits2Float(0x428268f1), SkBits2Float(0x43bf199a), SkBits2Float(0x42825f0d)); // 380.8f, 65.2086f, 381.5f, 65.205f, 382.2f, 65.1856f +path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x42825528), SkBits2Float(0x43bfcccd), SkBits2Float(0x4282410d), SkBits2Float(0x43c02666), SkBits2Float(0x428239be)); // 382.9f, 65.1663f, 383.6f, 65.1271f, 384.3f, 65.1128f +path.lineTo(SkBits2Float(0x43c02666), SkBits2Float(0x42823333)); // 384.3f, 65.1f +path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333)); // 306.6f, 65.1f +path.close(); +path.moveTo(SkBits2Float(0x43c24000), SkBits2Float(0x42823333)); // 388.5f, 65.1f +path.lineTo(SkBits2Float(0x43c24000), SkBits2Float(0x42823333)); // 388.5f, 65.1f +path.cubicTo(SkBits2Float(0x43c2999a), SkBits2Float(0x42823333), SkBits2Float(0x43c2f333), SkBits2Float(0x42822c75), SkBits2Float(0x43c34ccd), SkBits2Float(0x42823333)); // 389.2f, 65.1f, 389.9f, 65.0868f, 390.6f, 65.1f +path.cubicTo(SkBits2Float(0x43c3a666), SkBits2Float(0x428239f1), SkBits2Float(0x43c40000), SkBits2Float(0x428243dd), SkBits2Float(0x43c4599a), SkBits2Float(0x42825ba8)); // 391.3f, 65.1132f, 392, 65.1325f, 392.7f, 65.179f +path.cubicTo(SkBits2Float(0x43c4b333), SkBits2Float(0x42827373), SkBits2Float(0x43c50ccd), SkBits2Float(0x4282a819), SkBits2Float(0x43c56666), SkBits2Float(0x4282c1f5)); // 393.4f, 65.2255f, 394.1f, 65.3283f, 394.8f, 65.3788f +path.cubicTo(SkBits2Float(0x43c5c000), SkBits2Float(0x4282dbd0), SkBits2Float(0x43c6199a), SkBits2Float(0x4282ee83), SkBits2Float(0x43c67333), SkBits2Float(0x4282f6cd)); // 395.5f, 65.4293f, 396.2f, 65.4658f, 396.9f, 65.482f +path.cubicTo(SkBits2Float(0x43c6cccd), SkBits2Float(0x4282ff17), SkBits2Float(0x43c72666), SkBits2Float(0x4282f4d1), SkBits2Float(0x43c78000), SkBits2Float(0x4282f3af)); // 397.6f, 65.4982f, 398.3f, 65.4782f, 399, 65.4759f +path.cubicTo(SkBits2Float(0x43c7d99a), SkBits2Float(0x4282f28e), SkBits2Float(0x43c83333), SkBits2Float(0x4282f13e), SkBits2Float(0x43c88ccd), SkBits2Float(0x4282f005)); // 399.7f, 65.4737f, 400.4f, 65.4712f, 401.1f, 65.4688f +path.cubicTo(SkBits2Float(0x43c8e666), SkBits2Float(0x4282eecc), SkBits2Float(0x43c94000), SkBits2Float(0x4282eed9), SkBits2Float(0x43c9999a), SkBits2Float(0x4282ec5a)); // 401.8f, 65.4664f, 402.5f, 65.4665f, 403.2f, 65.4616f +path.cubicTo(SkBits2Float(0x43c9f333), SkBits2Float(0x4282e9da), SkBits2Float(0x43ca4ccd), SkBits2Float(0x4282e71d), SkBits2Float(0x43caa666), SkBits2Float(0x4282e108)); // 403.9f, 65.4567f, 404.6f, 65.4514f, 405.3f, 65.4395f +path.cubicTo(SkBits2Float(0x43cb0000), SkBits2Float(0x4282daf4), SkBits2Float(0x43cb599a), SkBits2Float(0x4282ce0b), SkBits2Float(0x43cbb333), SkBits2Float(0x4282c7df)); // 406, 65.4276f, 406.7f, 65.4024f, 407.4f, 65.3904f +path.cubicTo(SkBits2Float(0x43cc0ccd), SkBits2Float(0x4282c1b3), SkBits2Float(0x43cc6666), SkBits2Float(0x4282bf7c), SkBits2Float(0x43ccc000), SkBits2Float(0x4282bc00)); // 408.1f, 65.3783f, 408.8f, 65.374f, 409.5f, 65.3672f +path.cubicTo(SkBits2Float(0x43cd199a), SkBits2Float(0x4282b884), SkBits2Float(0x43cd7333), SkBits2Float(0x4282bb96), SkBits2Float(0x43cdcccd), SkBits2Float(0x4282b2f8)); // 410.2f, 65.3604f, 410.9f, 65.3664f, 411.6f, 65.3495f +path.cubicTo(SkBits2Float(0x43ce2666), SkBits2Float(0x4282aa5a), SkBits2Float(0x43ce8000), SkBits2Float(0x428295f5), SkBits2Float(0x43ced99a), SkBits2Float(0x4282884d)); // 412.3f, 65.3327f, 413, 65.2929f, 413.7f, 65.2662f +path.cubicTo(SkBits2Float(0x43cf3333), SkBits2Float(0x42827aa4), SkBits2Float(0x43cf8ccd), SkBits2Float(0x42826b01), SkBits2Float(0x43cfe666), SkBits2Float(0x42826105)); // 414.4f, 65.2395f, 415.1f, 65.209f, 415.8f, 65.1895f +path.cubicTo(SkBits2Float(0x43d04000), SkBits2Float(0x42825709), SkBits2Float(0x43d0999a), SkBits2Float(0x42824ec0), SkBits2Float(0x43d0f333), SkBits2Float(0x42824c65)); // 416.5f, 65.17f, 417.2f, 65.1538f, 417.9f, 65.1492f +path.cubicTo(SkBits2Float(0x43d14ccd), SkBits2Float(0x42824a0b), SkBits2Float(0x43d1a666), SkBits2Float(0x42824c1f), SkBits2Float(0x43d20000), SkBits2Float(0x428252e7)); // 418.6f, 65.1446f, 419.3f, 65.1487f, 420, 65.1619f +path.cubicTo(SkBits2Float(0x43d2599a), SkBits2Float(0x428259b0), SkBits2Float(0x43d2b333), SkBits2Float(0x428269b3), SkBits2Float(0x43d30ccd), SkBits2Float(0x42827518)); // 420.7f, 65.1752f, 421.4f, 65.2064f, 422.1f, 65.2287f +path.cubicTo(SkBits2Float(0x43d36666), SkBits2Float(0x4282807e), SkBits2Float(0x43d3c000), SkBits2Float(0x42829021), SkBits2Float(0x43d4199a), SkBits2Float(0x42829749)); // 422.8f, 65.251f, 423.5f, 65.2815f, 424.2f, 65.2955f +path.cubicTo(SkBits2Float(0x43d47333), SkBits2Float(0x42829e72), SkBits2Float(0x43d4cccd), SkBits2Float(0x4282a08f), SkBits2Float(0x43d52666), SkBits2Float(0x4282a00a)); // 424.9f, 65.3095f, 425.6f, 65.3136f, 426.3f, 65.3126f +path.cubicTo(SkBits2Float(0x43d58000), SkBits2Float(0x42829f85), SkBits2Float(0x43d5d99a), SkBits2Float(0x42829626), SkBits2Float(0x43d63333), SkBits2Float(0x4282942c)); // 427, 65.3116f, 427.7f, 65.2933f, 428.4f, 65.2894f +path.cubicTo(SkBits2Float(0x43d68ccd), SkBits2Float(0x42829231), SkBits2Float(0x43d6e666), SkBits2Float(0x42828e79), SkBits2Float(0x43d74000), SkBits2Float(0x4282942c)); // 429.1f, 65.2855f, 429.8f, 65.2783f, 430.5f, 65.2894f +path.cubicTo(SkBits2Float(0x43d7999a), SkBits2Float(0x428299de), SkBits2Float(0x43d7f333), SkBits2Float(0x4282ac18), SkBits2Float(0x43d84ccd), SkBits2Float(0x4282b65d)); // 431.2f, 65.3005f, 431.9f, 65.3361f, 432.6f, 65.3562f +path.cubicTo(SkBits2Float(0x43d8a666), SkBits2Float(0x4282c0a1), SkBits2Float(0x43d90000), SkBits2Float(0x4282cceb), SkBits2Float(0x43d9599a), SkBits2Float(0x4282d1c5)); // 433.3f, 65.3762f, 434, 65.4002f, 434.7f, 65.4097f +path.cubicTo(SkBits2Float(0x43d9b333), SkBits2Float(0x4282d69f), SkBits2Float(0x43da0ccd), SkBits2Float(0x4282d3cb), SkBits2Float(0x43da6666), SkBits2Float(0x4282d377)); // 435.4f, 65.4192f, 436.1f, 65.4137f, 436.8f, 65.413f +path.cubicTo(SkBits2Float(0x43dac000), SkBits2Float(0x4282d323), SkBits2Float(0x43db199a), SkBits2Float(0x4282d18a), SkBits2Float(0x43db7333), SkBits2Float(0x4282cfcc)); // 437.5f, 65.4124f, 438.2f, 65.4093f, 438.9f, 65.4059f +path.cubicTo(SkBits2Float(0x43dbcccd), SkBits2Float(0x4282ce0e), SkBits2Float(0x43dc2666), SkBits2Float(0x4282cd41), SkBits2Float(0x43dc8000), SkBits2Float(0x4282c904)); // 439.6f, 65.4025f, 440.3f, 65.4009f, 441, 65.3926f +path.cubicTo(SkBits2Float(0x43dcd99a), SkBits2Float(0x4282c4c7), SkBits2Float(0x43dd3333), SkBits2Float(0x4282bc94), SkBits2Float(0x43dd8ccd), SkBits2Float(0x4282b65d)); // 441.7f, 65.3843f, 442.4f, 65.3683f, 443.1f, 65.3562f +path.cubicTo(SkBits2Float(0x43dde666), SkBits2Float(0x4282b025), SkBits2Float(0x43de4000), SkBits2Float(0x4282a847), SkBits2Float(0x43de999a), SkBits2Float(0x4282a3b5)); // 443.8f, 65.344f, 444.5f, 65.3287f, 445.2f, 65.3197f +path.cubicTo(SkBits2Float(0x43def333), SkBits2Float(0x42829f24), SkBits2Float(0x43df4ccd), SkBits2Float(0x42829c21), SkBits2Float(0x43dfa666), SkBits2Float(0x42829af4)); // 445.9f, 65.3108f, 446.6f, 65.3049f, 447.3f, 65.3026f +path.cubicTo(SkBits2Float(0x43e00000), SkBits2Float(0x428299c7), SkBits2Float(0x43e0599a), SkBits2Float(0x42829af4), SkBits2Float(0x43e0b333), SkBits2Float(0x42829ca6)); // 448, 65.3003f, 448.7f, 65.3026f, 449.4f, 65.306f +path.cubicTo(SkBits2Float(0x43e10ccd), SkBits2Float(0x42829e58), SkBits2Float(0x43e16666), SkBits2Float(0x4282a31a), SkBits2Float(0x43e1c000), SkBits2Float(0x4282a521)); // 450.1f, 65.3093f, 450.8f, 65.3186f, 451.5f, 65.3225f +path.cubicTo(SkBits2Float(0x43e2199a), SkBits2Float(0x4282a727), SkBits2Float(0x43e27333), SkBits2Float(0x4282a640), SkBits2Float(0x43e2cccd), SkBits2Float(0x4282a8cc)); // 452.2f, 65.3265f, 452.9f, 65.3247f, 453.6f, 65.3297f +path.cubicTo(SkBits2Float(0x43e32666), SkBits2Float(0x4282ab57), SkBits2Float(0x43e38000), SkBits2Float(0x4282adef), SkBits2Float(0x43e3d99a), SkBits2Float(0x4282b464)); // 454.3f, 65.3346f, 455, 65.3397f, 455.7f, 65.3523f +path.cubicTo(SkBits2Float(0x43e43333), SkBits2Float(0x4282bad8), SkBits2Float(0x43e48ccd), SkBits2Float(0x4282c468), SkBits2Float(0x43e4e666), SkBits2Float(0x4282cf86)); // 456.4f, 65.3649f, 457.1f, 65.3836f, 457.8f, 65.4053f +path.cubicTo(SkBits2Float(0x43e54000), SkBits2Float(0x4282daa3), SkBits2Float(0x43e5999a), SkBits2Float(0x4282e9b4), SkBits2Float(0x43e5f333), SkBits2Float(0x4282f714)); // 458.5f, 65.427f, 459.2f, 65.4565f, 459.9f, 65.4826f +path.cubicTo(SkBits2Float(0x43e64ccd), SkBits2Float(0x42830474), SkBits2Float(0x43e6a666), SkBits2Float(0x428314c1), SkBits2Float(0x43e70000), SkBits2Float(0x42831fc7)); // 460.6f, 65.5087f, 461.3f, 65.5405f, 462, 65.5621f +path.cubicTo(SkBits2Float(0x43e7599a), SkBits2Float(0x42832acc), SkBits2Float(0x43e7b333), SkBits2Float(0x4283338f), SkBits2Float(0x43e80ccd), SkBits2Float(0x42833936)); // 462.7f, 65.5836f, 463.4f, 65.6007f, 464.1f, 65.6117f +path.cubicTo(SkBits2Float(0x43e86666), SkBits2Float(0x42833edd), SkBits2Float(0x43e8c000), SkBits2Float(0x42834090), SkBits2Float(0x43e9199a), SkBits2Float(0x428341b1)); // 464.8f, 65.6228f, 465.5f, 65.6261f, 466.2f, 65.6283f +path.cubicTo(SkBits2Float(0x43e97333), SkBits2Float(0x428342d2), SkBits2Float(0x43e9cccd), SkBits2Float(0x428340d8), SkBits2Float(0x43ea2666), SkBits2Float(0x42833fff)); // 466.9f, 65.6305f, 467.6f, 65.6266f, 468.3f, 65.625f +path.cubicTo(SkBits2Float(0x43ea8000), SkBits2Float(0x42833f26), SkBits2Float(0x43ead99a), SkBits2Float(0x42833e4d), SkBits2Float(0x43eb3333), SkBits2Float(0x42833c9b)); // 469, 65.6233f, 469.7f, 65.6217f, 470.4f, 65.6184f +path.cubicTo(SkBits2Float(0x43eb8ccd), SkBits2Float(0x42833ae8), SkBits2Float(0x43ebe666), SkBits2Float(0x42833f32), SkBits2Float(0x43ec4000), SkBits2Float(0x428335d2)); // 471.1f, 65.6151f, 471.8f, 65.6234f, 472.5f, 65.6051f +path.cubicTo(SkBits2Float(0x43ec999a), SkBits2Float(0x42832c73), SkBits2Float(0x43ecf333), SkBits2Float(0x42831e2e), SkBits2Float(0x43ed4ccd), SkBits2Float(0x4283045e)); // 473.2f, 65.5868f, 473.9f, 65.5589f, 474.6f, 65.5085f +path.cubicTo(SkBits2Float(0x43eda666), SkBits2Float(0x4282ea8e), SkBits2Float(0x43ee0000), SkBits2Float(0x4282bd9e), SkBits2Float(0x43ee599a), SkBits2Float(0x42829af4)); // 475.3f, 65.4581f, 476, 65.3703f, 476.7f, 65.3026f +path.cubicTo(SkBits2Float(0x43eeb333), SkBits2Float(0x4282784a), SkBits2Float(0x43ef0ccd), SkBits2Float(0x428245ac), SkBits2Float(0x43ef6666), SkBits2Float(0x42823461)); // 477.4f, 65.2349f, 478.1f, 65.1361f, 478.8f, 65.1023f +path.cubicTo(SkBits2Float(0x43efc000), SkBits2Float(0x42822316), SkBits2Float(0x43f0199a), SkBits2Float(0x42823365), SkBits2Float(0x43f07333), SkBits2Float(0x42823333)); // 479.5f, 65.0685f, 480.2f, 65.1004f, 480.9f, 65.1f +path.lineTo(SkBits2Float(0x43f07333), SkBits2Float(0x42823333)); // 480.9f, 65.1f +path.lineTo(SkBits2Float(0x43c24000), SkBits2Float(0x42823333)); // 388.5f, 65.1f +path.close(); +path.moveTo(SkBits2Float(0x43fc0000), SkBits2Float(0x42823333)); // 504, 65.1f +path.lineTo(SkBits2Float(0x43fc0000), SkBits2Float(0x42823333)); // 504, 65.1f +path.cubicTo(SkBits2Float(0x43fc599a), SkBits2Float(0x42823333), SkBits2Float(0x43fcb333), SkBits2Float(0x42822ef4), SkBits2Float(0x43fd0ccd), SkBits2Float(0x42823333)); // 504.7f, 65.1f, 505.4f, 65.0917f, 506.1f, 65.1f +path.cubicTo(SkBits2Float(0x43fd6666), SkBits2Float(0x42823772), SkBits2Float(0x43fdc000), SkBits2Float(0x42823e7d), SkBits2Float(0x43fe199a), SkBits2Float(0x42824cac)); // 506.8f, 65.1083f, 507.5f, 65.122f, 508.2f, 65.1497f +path.cubicTo(SkBits2Float(0x43fe7333), SkBits2Float(0x42825adb), SkBits2Float(0x43fecccd), SkBits2Float(0x42827a73), SkBits2Float(0x43ff2666), SkBits2Float(0x4282884d)); // 508.9f, 65.1775f, 509.6f, 65.2392f, 510.3f, 65.2662f +path.cubicTo(SkBits2Float(0x43ff8000), SkBits2Float(0x42829626), SkBits2Float(0x43ffd99a), SkBits2Float(0x42829a88), SkBits2Float(0x4400199a), SkBits2Float(0x42829fc4)); // 511, 65.2933f, 511.7f, 65.3018f, 512.4f, 65.312f +path.cubicTo(SkBits2Float(0x44004666), SkBits2Float(0x4282a4ff), SkBits2Float(0x44007333), SkBits2Float(0x4282a6e4), SkBits2Float(0x4400a000), SkBits2Float(0x4282a7b1)); // 513.1f, 65.3223f, 513.8f, 65.326f, 514.5f, 65.3275f +path.cubicTo(SkBits2Float(0x4400cccd), SkBits2Float(0x4282a87e), SkBits2Float(0x4400f99a), SkBits2Float(0x4282a682), SkBits2Float(0x44012666), SkBits2Float(0x4282a494)); // 515.2f, 65.3291f, 515.9f, 65.3252f, 516.6f, 65.3214f +path.cubicTo(SkBits2Float(0x44015333), SkBits2Float(0x4282a2a5), SkBits2Float(0x44018000), SkBits2Float(0x42829e44), SkBits2Float(0x4401accd), SkBits2Float(0x42829c19)); // 517.3f, 65.3177f, 518, 65.3091f, 518.7f, 65.3049f +path.cubicTo(SkBits2Float(0x4401d99a), SkBits2Float(0x428299ee), SkBits2Float(0x44020666), SkBits2Float(0x42829718), SkBits2Float(0x44023333), SkBits2Float(0x42829790)); // 519.4f, 65.3006f, 520.1f, 65.2951f, 520.8f, 65.296f +path.cubicTo(SkBits2Float(0x44026000), SkBits2Float(0x42829807), SkBits2Float(0x44028ccd), SkBits2Float(0x42829ae5), SkBits2Float(0x4402b99a), SkBits2Float(0x42829ee5)); // 521.5f, 65.2969f, 522.2f, 65.3025f, 522.9f, 65.3103f +path.cubicTo(SkBits2Float(0x4402e666), SkBits2Float(0x4282a2e6), SkBits2Float(0x44031333), SkBits2Float(0x4282abab), SkBits2Float(0x44034000), SkBits2Float(0x4282af94)); // 523.6f, 65.3182f, 524.3f, 65.3353f, 525, 65.3429f +path.cubicTo(SkBits2Float(0x44036ccd), SkBits2Float(0x4282b37d), SkBits2Float(0x4403999a), SkBits2Float(0x4282b5c0), SkBits2Float(0x4403c666), SkBits2Float(0x4282b65d)); // 525.7f, 65.3506f, 526.4f, 65.355f, 527.1f, 65.3562f +path.cubicTo(SkBits2Float(0x4403f333), SkBits2Float(0x4282b6f9), SkBits2Float(0x44042000), SkBits2Float(0x4282c91b), SkBits2Float(0x44044ccd), SkBits2Float(0x4282b33f)); // 527.8f, 65.3574f, 528.5f, 65.3928f, 529.2f, 65.3501f +path.cubicTo(SkBits2Float(0x4404799a), SkBits2Float(0x42829d63), SkBits2Float(0x4404a666), SkBits2Float(0x4282488a), SkBits2Float(0x4404d333), SkBits2Float(0x42823333)); // 529.9f, 65.3074f, 530.6f, 65.1417f, 531.3f, 65.1f +path.cubicTo(SkBits2Float(0x44050000), SkBits2Float(0x42821ddb), SkBits2Float(0x44052ccd), SkBits2Float(0x42823333), SkBits2Float(0x4405599a), SkBits2Float(0x42823333)); // 532, 65.0583f, 532.7f, 65.1f, 533.4f, 65.1f +path.lineTo(SkBits2Float(0x4405599a), SkBits2Float(0x42823333)); // 533.4f, 65.1f +path.lineTo(SkBits2Float(0x43fc0000), SkBits2Float(0x42823333)); // 504, 65.1f +path.close(); + return path; +} + +static SkPath path1_a() { + SkPath path; +path.moveTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333)); // 306.6f, 65.1f +path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333)); // 306.6f, 65.1f +path.cubicTo(SkBits2Float(0x4399a666), SkBits2Float(0x42823332), SkBits2Float(0x439a0000), SkBits2Float(0x42823842), SkBits2Float(0x439a599a), SkBits2Float(0x4282332a)); // 307.3f, 65.1f, 308, 65.1099f, 308.7f, 65.0999f +path.cubicTo(SkBits2Float(0x439ab333), SkBits2Float(0x42822e12), SkBits2Float(0x439b0ccd), SkBits2Float(0x42821e94), SkBits2Float(0x439b6666), SkBits2Float(0x428214a4)); // 309.4f, 65.09f, 310.1f, 65.0597f, 310.8f, 65.0403f +path.cubicTo(SkBits2Float(0x439bc000), SkBits2Float(0x42820ab4), SkBits2Float(0x439c199a), SkBits2Float(0x42820185), SkBits2Float(0x439c7333), SkBits2Float(0x4281f789)); // 311.5f, 65.0209f, 312.2f, 65.003f, 312.9f, 64.9835f +#if 0 +path.cubicTo(SkBits2Float(0x439ccccd), SkBits2Float(0x4281ed8d), SkBits2Float(0x439d2666), SkBits2Float(0x4281e391), SkBits2Float(0x439d8000), SkBits2Float(0x4281d8bc)); // 313.6f, 64.964f, 314.3f, 64.9445f, 315, 64.9233f +#if 0 +path.cubicTo(SkBits2Float(0x439dd99a), SkBits2Float(0x4281cde7), SkBits2Float(0x439e3333), SkBits2Float(0x4281c0c4), SkBits2Float(0x439e8ccd), SkBits2Float(0x4281b68b)); // 315.7f, 64.9022f, 316.4f, 64.8765f, 317.1f, 64.8565f +path.cubicTo(SkBits2Float(0x439ee666), SkBits2Float(0x4281ac53), SkBits2Float(0x439f4000), SkBits2Float(0x4281a27a), SkBits2Float(0x439f999a), SkBits2Float(0x42819b69)); // 317.8f, 64.8366f, 318.5f, 64.8173f, 319.2f, 64.8035f +path.cubicTo(SkBits2Float(0x439ff333), SkBits2Float(0x42819459), SkBits2Float(0x43a04ccd), SkBits2Float(0x42818f8b), SkBits2Float(0x43a0a666), SkBits2Float(0x42818c26)); // 319.9f, 64.7897f, 320.6f, 64.7804f, 321.3f, 64.7737f +path.cubicTo(SkBits2Float(0x43a10000), SkBits2Float(0x428188c2), SkBits2Float(0x43a1599a), SkBits2Float(0x42818795), SkBits2Float(0x43a1b333), SkBits2Float(0x42818710)); // 322, 64.7671f, 322.7f, 64.7648f, 323.4f, 64.7638f +path.cubicTo(SkBits2Float(0x43a20ccd), SkBits2Float(0x4281868b), SkBits2Float(0x43a26666), SkBits2Float(0x42818824), SkBits2Float(0x43a2c000), SkBits2Float(0x42818909)); // 324.1f, 64.7628f, 324.8f, 64.7659f, 325.5f, 64.7676f +path.cubicTo(SkBits2Float(0x43a3199a), SkBits2Float(0x428189ee), SkBits2Float(0x43a37333), SkBits2Float(0x42818de2), SkBits2Float(0x43a3cccd), SkBits2Float(0x42818c6d)); // 326.2f, 64.7694f, 326.9f, 64.7771f, 327.6f, 64.7743f +path.cubicTo(SkBits2Float(0x43a42666), SkBits2Float(0x42818af7), SkBits2Float(0x43a48000), SkBits2Float(0x428185be), SkBits2Float(0x43a4d99a), SkBits2Float(0x42818048)); // 328.3f, 64.7714f, 329, 64.7612f, 329.7f, 64.7505f +path.cubicTo(SkBits2Float(0x43a53333), SkBits2Float(0x42817ad1), SkBits2Float(0x43a58ccd), SkBits2Float(0x42816e33), SkBits2Float(0x43a5e666), SkBits2Float(0x42816ba7)); // 330.4f, 64.7399f, 331.1f, 64.7152f, 331.8f, 64.7103f +path.cubicTo(SkBits2Float(0x43a64000), SkBits2Float(0x4281691c), SkBits2Float(0x43a6999a), SkBits2Float(0x42816b46), SkBits2Float(0x43a6f333), SkBits2Float(0x42817104)); // 332.5f, 64.7053f, 333.2f, 64.7095f, 333.9f, 64.7207f +path.cubicTo(SkBits2Float(0x43a74ccd), SkBits2Float(0x428176c3), SkBits2Float(0x43a7a666), SkBits2Float(0x42817fa9), SkBits2Float(0x43a80000), SkBits2Float(0x42818e1f)); // 334.6f, 64.732f, 335.3f, 64.7493f, 336, 64.7776f +path.cubicTo(SkBits2Float(0x43a8599a), SkBits2Float(0x42819c95), SkBits2Float(0x43a8b333), SkBits2Float(0x4281b1ec), SkBits2Float(0x43a90ccd), SkBits2Float(0x4281c7c7)); // 336.7f, 64.8058f, 337.4f, 64.8475f, 338.1f, 64.8902f +path.cubicTo(SkBits2Float(0x43a96666), SkBits2Float(0x4281dda2), SkBits2Float(0x43a9c000), SkBits2Float(0x428209cf), SkBits2Float(0x43aa199a), SkBits2Float(0x42821140)); // 338.8f, 64.9329f, 339.5f, 65.0192f, 340.2f, 65.0337f +path.cubicTo(SkBits2Float(0x43aa7333), SkBits2Float(0x428218b0), SkBits2Float(0x43aacccd), SkBits2Float(0x42820dff), SkBits2Float(0x43ab2666), SkBits2Float(0x4281f46b)); // 340.9f, 65.0482f, 341.6f, 65.0273f, 342.3f, 64.9774f +path.cubicTo(SkBits2Float(0x43ab8000), SkBits2Float(0x4281dad8), SkBits2Float(0x43abd99a), SkBits2Float(0x42819956), SkBits2Float(0x43ac3333), SkBits2Float(0x428177cd)); // 343, 64.9274f, 343.7f, 64.7995f, 344.4f, 64.734f +path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42815644), SkBits2Float(0x43ace666), SkBits2Float(0x42813910), SkBits2Float(0x43ad4000), SkBits2Float(0x42812b37)); // 345.1f, 64.6685f, 345.8f, 64.6115f, 346.5f, 64.5844f +path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42811d5e), SkBits2Float(0x43adf333), SkBits2Float(0x42812394), SkBits2Float(0x43ae4ccd), SkBits2Float(0x428124b5)); // 347.2f, 64.5574f, 347.9f, 64.5695f, 348.6f, 64.5717f +path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x428125d6), SkBits2Float(0x43af0000), SkBits2Float(0x42812c1c), SkBits2Float(0x43af599a), SkBits2Float(0x428131ff)); // 349.3f, 64.5739f, 350, 64.5862f, 350.7f, 64.5976f +path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x428137e3), SkBits2Float(0x43b00ccd), SkBits2Float(0x4281417f), SkBits2Float(0x43b06666), SkBits2Float(0x4281480b)); // 351.4f, 64.6092f, 352.1f, 64.6279f, 352.8f, 64.6407f +path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x42814e97), SkBits2Float(0x43b1199a), SkBits2Float(0x4281534c), SkBits2Float(0x43b17333), SkBits2Float(0x42815947)); // 353.5f, 64.6535f, 354.2f, 64.6627f, 354.9f, 64.6744f +path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42815f42), SkBits2Float(0x43b22666), SkBits2Float(0x428165ff), SkBits2Float(0x43b28000), SkBits2Float(0x42816bee)); // 355.6f, 64.6861f, 356.3f, 64.6992f, 357, 64.7108f +path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x428171de), SkBits2Float(0x43b33333), SkBits2Float(0x42817af5), SkBits2Float(0x43b38ccd), SkBits2Float(0x42817ce3)); // 357.7f, 64.7224f, 358.4f, 64.7402f, 359.1f, 64.7439f +path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x42817ed2), SkBits2Float(0x43b44000), SkBits2Float(0x42817bcf), SkBits2Float(0x43b4999a), SkBits2Float(0x42817786)); // 359.8f, 64.7477f, 360.5f, 64.7418f, 361.2f, 64.7334f +path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x4281733d), SkBits2Float(0x43b54ccd), SkBits2Float(0x428167a7), SkBits2Float(0x43b5a666), SkBits2Float(0x4281632d)); // 361.9f, 64.7251f, 362.6f, 64.7024f, 363.3f, 64.6937f +path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x42815eb3), SkBits2Float(0x43b6599a), SkBits2Float(0x42815b7e), SkBits2Float(0x43b6b333), SkBits2Float(0x42815cab)); // 364, 64.685f, 364.7f, 64.6787f, 365.4f, 64.681f +path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x42815dd8), SkBits2Float(0x43b76666), SkBits2Float(0x4281644d), SkBits2Float(0x43b7c000), SkBits2Float(0x42816a3c)); // 366.1f, 64.6833f, 366.8f, 64.6959f, 367.5f, 64.7075f +path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x4281702b), SkBits2Float(0x43b87333), SkBits2Float(0x428179d3), SkBits2Float(0x43b8cccd), SkBits2Float(0x42818048)); // 368.2f, 64.7191f, 368.9f, 64.7379f, 369.6f, 64.7505f +path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x428186bc), SkBits2Float(0x43b98000), SkBits2Float(0x42818d4a), SkBits2Float(0x43b9d99a), SkBits2Float(0x428190f6)); // 370.3f, 64.7632f, 371, 64.776f, 371.7f, 64.7831f +path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x428194a3), SkBits2Float(0x43ba8ccd), SkBits2Float(0x428193b0), SkBits2Float(0x43bae666), SkBits2Float(0x42819653)); // 372.4f, 64.7903f, 373.1f, 64.7885f, 373.8f, 64.7936f +path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x428198f6), SkBits2Float(0x43bb999a), SkBits2Float(0x42819840), SkBits2Float(0x43bbf333), SkBits2Float(0x4281a0c6)); // 374.5f, 64.7988f, 375.2f, 64.7974f, 375.9f, 64.814f +#endif +path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x4281a94d), SkBits2Float(0x43bca666), SkBits2Float(0x4281bc0d), SkBits2Float(0x43bd0000), SkBits2Float(0x4281c979)); // 376.6f, 64.8307f, 377.3f, 64.8673f, 378, 64.8935f +path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x4281d6e5), SkBits2Float(0x43bdb333), SkBits2Float(0x4281e6fe), SkBits2Float(0x43be0ccd), SkBits2Float(0x4281f14e)); // 378.7f, 64.9197f, 379.4f, 64.9512f, 380.1f, 64.9713f +path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x4281fb9e), SkBits2Float(0x43bec000), SkBits2Float(0x4281fd75), SkBits2Float(0x43bf199a), SkBits2Float(0x42820759)); // 380.8f, 64.9914f, 381.5f, 64.995f, 382.2f, 65.0144f +path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x4282113e), SkBits2Float(0x43bfcccd), SkBits2Float(0x42822559), SkBits2Float(0x43c02666), SkBits2Float(0x42822ca8)); // 382.9f, 65.0337f, 383.6f, 65.0729f, 384.3f, 65.0872f +#endif +path.lineTo(SkBits2Float(0x43c02666), SkBits2Float(0x42823333)); // 384.3f, 65.1f +path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333)); // 306.6f, 65.1f +path.close(); + return path; +} + +static SkPath path2_a() { + SkPath path; +path.moveTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333)); // 306.6f, 65.1f +path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333)); // 306.6f, 65.1f +path.cubicTo(SkBits2Float(0x4399a666), SkBits2Float(0x42823334), SkBits2Float(0x439a0000), SkBits2Float(0x42822e24), SkBits2Float(0x439a599a), SkBits2Float(0x4282333c)); // 307.3f, 65.1f, 308, 65.0901f, 308.7f, 65.1001f +path.cubicTo(SkBits2Float(0x439ab333), SkBits2Float(0x42823854), SkBits2Float(0x439b0ccd), SkBits2Float(0x428247d2), SkBits2Float(0x439b6666), SkBits2Float(0x428251c2)); // 309.4f, 65.11f, 310.1f, 65.1403f, 310.8f, 65.1597f +path.cubicTo(SkBits2Float(0x439bc000), SkBits2Float(0x42825bb2), SkBits2Float(0x439c199a), SkBits2Float(0x428264e1), SkBits2Float(0x439c7333), SkBits2Float(0x42826edd)); // 311.5f, 65.1791f, 312.2f, 65.197f, 312.9f, 65.2165f +#if 0 +path.cubicTo(SkBits2Float(0x439ccccd), SkBits2Float(0x428278d9), SkBits2Float(0x439d2666), SkBits2Float(0x428282d5), SkBits2Float(0x439d8000), SkBits2Float(0x42828daa)); // 313.6f, 65.236f, 314.3f, 65.2555f, 315, 65.2767f +#if 0 +path.cubicTo(SkBits2Float(0x439dd99a), SkBits2Float(0x4282987f), SkBits2Float(0x439e3333), SkBits2Float(0x4282a5a2), SkBits2Float(0x439e8ccd), SkBits2Float(0x4282afdb)); // 315.7f, 65.2978f, 316.4f, 65.3235f, 317.1f, 65.3435f +path.cubicTo(SkBits2Float(0x439ee666), SkBits2Float(0x4282ba13), SkBits2Float(0x439f4000), SkBits2Float(0x4282c3ec), SkBits2Float(0x439f999a), SkBits2Float(0x4282cafd)); // 317.8f, 65.3634f, 318.5f, 65.3827f, 319.2f, 65.3965f +path.cubicTo(SkBits2Float(0x439ff333), SkBits2Float(0x4282d20d), SkBits2Float(0x43a04ccd), SkBits2Float(0x4282d6db), SkBits2Float(0x43a0a666), SkBits2Float(0x4282da40)); // 319.9f, 65.4103f, 320.6f, 65.4196f, 321.3f, 65.4263f +path.cubicTo(SkBits2Float(0x43a10000), SkBits2Float(0x4282dda4), SkBits2Float(0x43a1599a), SkBits2Float(0x4282ded1), SkBits2Float(0x43a1b333), SkBits2Float(0x4282df56)); // 322, 65.4329f, 322.7f, 65.4352f, 323.4f, 65.4362f +path.cubicTo(SkBits2Float(0x43a20ccd), SkBits2Float(0x4282dfdb), SkBits2Float(0x43a26666), SkBits2Float(0x4282de42), SkBits2Float(0x43a2c000), SkBits2Float(0x4282dd5d)); // 324.1f, 65.4372f, 324.8f, 65.4341f, 325.5f, 65.4324f +path.cubicTo(SkBits2Float(0x43a3199a), SkBits2Float(0x4282dc78), SkBits2Float(0x43a37333), SkBits2Float(0x4282d884), SkBits2Float(0x43a3cccd), SkBits2Float(0x4282d9f9)); // 326.2f, 65.4306f, 326.9f, 65.4229f, 327.6f, 65.4257f +path.cubicTo(SkBits2Float(0x43a42666), SkBits2Float(0x4282db6f), SkBits2Float(0x43a48000), SkBits2Float(0x4282e0a8), SkBits2Float(0x43a4d99a), SkBits2Float(0x4282e61e)); // 328.3f, 65.4286f, 329, 65.4388f, 329.7f, 65.4494f +path.cubicTo(SkBits2Float(0x43a53333), SkBits2Float(0x4282eb95), SkBits2Float(0x43a58ccd), SkBits2Float(0x4282f833), SkBits2Float(0x43a5e666), SkBits2Float(0x4282fabf)); // 330.4f, 65.4601f, 331.1f, 65.4848f, 331.8f, 65.4897f +path.cubicTo(SkBits2Float(0x43a64000), SkBits2Float(0x4282fd4a), SkBits2Float(0x43a6999a), SkBits2Float(0x4282fb20), SkBits2Float(0x43a6f333), SkBits2Float(0x4282f562)); // 332.5f, 65.4947f, 333.2f, 65.4905f, 333.9f, 65.4793f +path.cubicTo(SkBits2Float(0x43a74ccd), SkBits2Float(0x4282efa3), SkBits2Float(0x43a7a666), SkBits2Float(0x4282e6bd), SkBits2Float(0x43a80000), SkBits2Float(0x4282d847)); // 334.6f, 65.468f, 335.3f, 65.4507f, 336, 65.4224f +path.cubicTo(SkBits2Float(0x43a8599a), SkBits2Float(0x4282c9d1), SkBits2Float(0x43a8b333), SkBits2Float(0x4282b47a), SkBits2Float(0x43a90ccd), SkBits2Float(0x42829e9f)); // 336.7f, 65.3942f, 337.4f, 65.3525f, 338.1f, 65.3098f +path.cubicTo(SkBits2Float(0x43a96666), SkBits2Float(0x428288c4), SkBits2Float(0x43a9c000), SkBits2Float(0x42825c97), SkBits2Float(0x43aa199a), SkBits2Float(0x42825526)); // 338.8f, 65.2671f, 339.5f, 65.1808f, 340.2f, 65.1663f +path.cubicTo(SkBits2Float(0x43aa7333), SkBits2Float(0x42824db6), SkBits2Float(0x43aacccd), SkBits2Float(0x42825867), SkBits2Float(0x43ab2666), SkBits2Float(0x428271fb)); // 340.9f, 65.1518f, 341.6f, 65.1727f, 342.3f, 65.2226f +path.cubicTo(SkBits2Float(0x43ab8000), SkBits2Float(0x42828b8e), SkBits2Float(0x43abd99a), SkBits2Float(0x4282cd10), SkBits2Float(0x43ac3333), SkBits2Float(0x4282ee99)); // 343, 65.2726f, 343.7f, 65.4005f, 344.4f, 65.466f +path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42831022), SkBits2Float(0x43ace666), SkBits2Float(0x42832d56), SkBits2Float(0x43ad4000), SkBits2Float(0x42833b2f)); // 345.1f, 65.5315f, 345.8f, 65.5885f, 346.5f, 65.6156f +path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42834908), SkBits2Float(0x43adf333), SkBits2Float(0x428342d2), SkBits2Float(0x43ae4ccd), SkBits2Float(0x428341b1)); // 347.2f, 65.6426f, 347.9f, 65.6305f, 348.6f, 65.6283f +path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x42834090), SkBits2Float(0x43af0000), SkBits2Float(0x42833a4a), SkBits2Float(0x43af599a), SkBits2Float(0x42833467)); // 349.3f, 65.6261f, 350, 65.6138f, 350.7f, 65.6023f +path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x42832e83), SkBits2Float(0x43b00ccd), SkBits2Float(0x428324e7), SkBits2Float(0x43b06666), SkBits2Float(0x42831e5b)); // 351.4f, 65.5908f, 352.1f, 65.5721f, 352.8f, 65.5593f +path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x428317cf), SkBits2Float(0x43b1199a), SkBits2Float(0x4283131a), SkBits2Float(0x43b17333), SkBits2Float(0x42830d1f)); // 353.5f, 65.5465f, 354.2f, 65.5373f, 354.9f, 65.5256f +path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42830724), SkBits2Float(0x43b22666), SkBits2Float(0x42830067), SkBits2Float(0x43b28000), SkBits2Float(0x4282fa78)); // 355.6f, 65.5139f, 356.3f, 65.5008f, 357, 65.4892f +path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x4282f488), SkBits2Float(0x43b33333), SkBits2Float(0x4282eb71), SkBits2Float(0x43b38ccd), SkBits2Float(0x4282e983)); // 357.7f, 65.4776f, 358.4f, 65.4598f, 359.1f, 65.4561f +path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x4282e794), SkBits2Float(0x43b44000), SkBits2Float(0x4282ea97), SkBits2Float(0x43b4999a), SkBits2Float(0x4282eee0)); // 359.8f, 65.4523f, 360.5f, 65.4582f, 361.2f, 65.4666f +path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x4282f329), SkBits2Float(0x43b54ccd), SkBits2Float(0x4282febf), SkBits2Float(0x43b5a666), SkBits2Float(0x42830339)); // 361.9f, 65.4749f, 362.6f, 65.4976f, 363.3f, 65.5063f +path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x428307b3), SkBits2Float(0x43b6599a), SkBits2Float(0x42830ae8), SkBits2Float(0x43b6b333), SkBits2Float(0x428309bb)); // 364, 65.515f, 364.7f, 65.5213f, 365.4f, 65.519f +path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x4283088e), SkBits2Float(0x43b76666), SkBits2Float(0x42830219), SkBits2Float(0x43b7c000), SkBits2Float(0x4282fc2a)); // 366.1f, 65.5167f, 366.8f, 65.5041f, 367.5f, 65.4925f +path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x4282f63b), SkBits2Float(0x43b87333), SkBits2Float(0x4282ec93), SkBits2Float(0x43b8cccd), SkBits2Float(0x4282e61e)); // 368.2f, 65.4809f, 368.9f, 65.4621f, 369.6f, 65.4494f +path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x4282dfaa), SkBits2Float(0x43b98000), SkBits2Float(0x4282d91c), SkBits2Float(0x43b9d99a), SkBits2Float(0x4282d570)); // 370.3f, 65.4368f, 371, 65.424f, 371.7f, 65.4169f +path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x4282d1c3), SkBits2Float(0x43ba8ccd), SkBits2Float(0x4282d2b6), SkBits2Float(0x43bae666), SkBits2Float(0x4282d013)); // 372.4f, 65.4097f, 373.1f, 65.4115f, 373.8f, 65.4064f +path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x4282cd70), SkBits2Float(0x43bb999a), SkBits2Float(0x4282ce26), SkBits2Float(0x43bbf333), SkBits2Float(0x4282c5a0)); // 374.5f, 65.4012f, 375.2f, 65.4026f, 375.9f, 65.386f +#endif +path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x4282bd19), SkBits2Float(0x43bca666), SkBits2Float(0x4282aa59), SkBits2Float(0x43bd0000), SkBits2Float(0x42829ced)); // 376.6f, 65.3693f, 377.3f, 65.3327f, 378, 65.3065f +path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x42828f81), SkBits2Float(0x43bdb333), SkBits2Float(0x42827f68), SkBits2Float(0x43be0ccd), SkBits2Float(0x42827518)); // 378.7f, 65.2803f, 379.4f, 65.2488f, 380.1f, 65.2287f +path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x42826ac8), SkBits2Float(0x43bec000), SkBits2Float(0x428268f1), SkBits2Float(0x43bf199a), SkBits2Float(0x42825f0d)); // 380.8f, 65.2086f, 381.5f, 65.205f, 382.2f, 65.1856f +path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x42825528), SkBits2Float(0x43bfcccd), SkBits2Float(0x4282410d), SkBits2Float(0x43c02666), SkBits2Float(0x428239be)); // 382.9f, 65.1663f, 383.6f, 65.1271f, 384.3f, 65.1128f +#endif +path.lineTo(SkBits2Float(0x43c02666), SkBits2Float(0x42823333)); // 384.3f, 65.1f +path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333)); // 306.6f, 65.1f +path.close(); + return path; +} + +static void issue3651_1a(skiatest::Reporter* reporter, const char* filename) { + SkPath path = path1_a(); + SkPath pathB = path2_a(); + // DEBUG_UNDER_DEVELOPMENT issue3651_1a disable expectation check for now + testPathOpCheck(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename, + !SkOpGlobalState::DebugRunFail()); +} + +static SkPath path3() { + SkPath path; +path.moveTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000)); // 88.2f, 94.5f +path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000)); // 88.2f, 94.5f +path.cubicTo(SkBits2Float(0x42b1cccd), SkBits2Float(0x42bd0000), SkBits2Float(0x42b33333), SkBits2Float(0x42bd2573), SkBits2Float(0x42b4999a), SkBits2Float(0x42bd0000)); // 88.9f, 94.5f, 89.6f, 94.5731f, 90.3f, 94.5f +path.cubicTo(SkBits2Float(0x42b60000), SkBits2Float(0x42bcda8d), SkBits2Float(0x42b76666), SkBits2Float(0x42bc4598), SkBits2Float(0x42b8cccd), SkBits2Float(0x42bc1f4b)); // 91, 94.4269f, 91.7f, 94.1359f, 92.4f, 94.0611f +path.cubicTo(SkBits2Float(0x42ba3333), SkBits2Float(0x42bbf8ff), SkBits2Float(0x42bb999a), SkBits2Float(0x42bc1b9e), SkBits2Float(0x42bd0000), SkBits2Float(0x42bc1a35)); // 93.1f, 93.9863f, 93.8f, 94.0539f, 94.5f, 94.0512f +path.cubicTo(SkBits2Float(0x42be6666), SkBits2Float(0x42bc18cb), SkBits2Float(0x42bfcccd), SkBits2Float(0x42bc17f2), SkBits2Float(0x42c13333), SkBits2Float(0x42bc16d0)); // 95.2f, 94.0484f, 95.9f, 94.0468f, 96.6f, 94.0446f +path.cubicTo(SkBits2Float(0x42c2999a), SkBits2Float(0x42bc15af), SkBits2Float(0x42c40000), SkBits2Float(0x42bc13fd), SkBits2Float(0x42c56666), SkBits2Float(0x42bc136c)); // 97.3f, 94.0424f, 98, 94.039f, 98.7f, 94.0379f +path.cubicTo(SkBits2Float(0x42c6cccd), SkBits2Float(0x42bc12dc), SkBits2Float(0x42c83333), SkBits2Float(0x42bc12e7), SkBits2Float(0x42c9999a), SkBits2Float(0x42bc136c)); // 99.4f, 94.0368f, 100.1f, 94.0369f, 100.8f, 94.0379f +path.cubicTo(SkBits2Float(0x42cb0000), SkBits2Float(0x42bc13f1), SkBits2Float(0x42cc6666), SkBits2Float(0x42bc15c8), SkBits2Float(0x42cdcccd), SkBits2Float(0x42bc168a)); // 101.5f, 94.0389f, 102.2f, 94.0425f, 102.9f, 94.044f +path.cubicTo(SkBits2Float(0x42cf3333), SkBits2Float(0x42bc174b), SkBits2Float(0x42d0999a), SkBits2Float(0x42bc17ad), SkBits2Float(0x42d20000), SkBits2Float(0x42bc17f5)); // 103.6f, 94.0455f, 104.3f, 94.0462f, 105, 94.0468f +path.cubicTo(SkBits2Float(0x42d36666), SkBits2Float(0x42bc183e), SkBits2Float(0x42d4cccd), SkBits2Float(0x42bc162a), SkBits2Float(0x42d63333), SkBits2Float(0x42bc183c)); // 105.7f, 94.0473f, 106.4f, 94.0433f, 107.1f, 94.0473f +path.cubicTo(SkBits2Float(0x42d7999a), SkBits2Float(0x42bc1a4e), SkBits2Float(0x42d90000), SkBits2Float(0x42bc1e66), SkBits2Float(0x42da6666), SkBits2Float(0x42bc2461)); // 107.8f, 94.0514f, 108.5f, 94.0594f, 109.2f, 94.0711f +path.cubicTo(SkBits2Float(0x42dbcccd), SkBits2Float(0x42bc2a5d), SkBits2Float(0x42dd3333), SkBits2Float(0x42bc33f9), SkBits2Float(0x42de999a), SkBits2Float(0x42bc3c1f)); // 109.9f, 94.0827f, 110.6f, 94.1015f, 111.3f, 94.1174f +path.cubicTo(SkBits2Float(0x42e00000), SkBits2Float(0x42bc4446), SkBits2Float(0x42e16666), SkBits2Float(0x42bc4cce), SkBits2Float(0x42e2cccd), SkBits2Float(0x42bc5548)); // 112, 94.1333f, 112.7f, 94.15f, 113.4f, 94.1666f +path.cubicTo(SkBits2Float(0x42e43333), SkBits2Float(0x42bc5dc3), SkBits2Float(0x42e5999a), SkBits2Float(0x42bc6472), SkBits2Float(0x42e70000), SkBits2Float(0x42bc6eff)); // 114.1f, 94.1831f, 114.8f, 94.1962f, 115.5f, 94.2168f +path.cubicTo(SkBits2Float(0x42e86666), SkBits2Float(0x42bc798b), SkBits2Float(0x42e9cccd), SkBits2Float(0x42bc8607), SkBits2Float(0x42eb3333), SkBits2Float(0x42bc9494)); // 116.2f, 94.2374f, 116.9f, 94.2618f, 117.6f, 94.2902f +path.cubicTo(SkBits2Float(0x42ec999a), SkBits2Float(0x42bca321), SkBits2Float(0x42ee0000), SkBits2Float(0x42bcb9f8), SkBits2Float(0x42ef6666), SkBits2Float(0x42bcc64f)); // 118.3f, 94.3186f, 119, 94.3632f, 119.7f, 94.3873f +path.cubicTo(SkBits2Float(0x42f0cccd), SkBits2Float(0x42bcd2a5), SkBits2Float(0x42f23333), SkBits2Float(0x42bcdb35), SkBits2Float(0x42f3999a), SkBits2Float(0x42bcde9a)); // 120.4f, 94.4114f, 121.1f, 94.4281f, 121.8f, 94.4348f +path.cubicTo(SkBits2Float(0x42f50000), SkBits2Float(0x42bce1fe), SkBits2Float(0x42f66666), SkBits2Float(0x42bcdd3f), SkBits2Float(0x42f7cccd), SkBits2Float(0x42bcdaa8)); // 122.5f, 94.4414f, 123.2f, 94.4321f, 123.9f, 94.4271f +path.cubicTo(SkBits2Float(0x42f93333), SkBits2Float(0x42bcd811), SkBits2Float(0x42fa999a), SkBits2Float(0x42bcd25d), SkBits2Float(0x42fc0000), SkBits2Float(0x42bccf10)); // 124.6f, 94.422f, 125.3f, 94.4109f, 126, 94.4044f +path.cubicTo(SkBits2Float(0x42fd6666), SkBits2Float(0x42bccbc3), SkBits2Float(0x42fecccd), SkBits2Float(0x42bcc95b), SkBits2Float(0x4300199a), SkBits2Float(0x42bcc6dc)); // 126.7f, 94.398f, 127.4f, 94.3933f, 128.1f, 94.3884f +path.cubicTo(SkBits2Float(0x4300cccd), SkBits2Float(0x42bcc45c), SkBits2Float(0x43018000), SkBits2Float(0x42bcc0ec), SkBits2Float(0x43023333), SkBits2Float(0x42bcc013)); // 128.8f, 94.3835f, 129.5f, 94.3768f, 130.2f, 94.3751f +path.cubicTo(SkBits2Float(0x4302e666), SkBits2Float(0x42bcbf3a), SkBits2Float(0x4303999a), SkBits2Float(0x42bcc0b0), SkBits2Float(0x43044ccd), SkBits2Float(0x42bcc1c5)); // 130.9f, 94.3735f, 131.6f, 94.3763f, 132.3f, 94.3785f +path.cubicTo(SkBits2Float(0x43050000), SkBits2Float(0x42bcc2db), SkBits2Float(0x4305b333), SkBits2Float(0x42bcc5bc), SkBits2Float(0x43066666), SkBits2Float(0x42bcc695)); // 133, 94.3806f, 133.7f, 94.3862f, 134.4f, 94.3879f +path.cubicTo(SkBits2Float(0x4307199a), SkBits2Float(0x42bcc76e), SkBits2Float(0x4307cccd), SkBits2Float(0x42bcc688), SkBits2Float(0x43088000), SkBits2Float(0x42bcc6dc)); // 135.1f, 94.3895f, 135.8f, 94.3878f, 136.5f, 94.3884f +path.cubicTo(SkBits2Float(0x43093333), SkBits2Float(0x42bcc730), SkBits2Float(0x4309e666), SkBits2Float(0x42bcc89a), SkBits2Float(0x430a999a), SkBits2Float(0x42bcc88e)); // 137.2f, 94.389f, 137.9f, 94.3918f, 138.6f, 94.3917f +path.cubicTo(SkBits2Float(0x430b4ccd), SkBits2Float(0x42bcc882), SkBits2Float(0x430c0000), SkBits2Float(0x42bcc76e), SkBits2Float(0x430cb333), SkBits2Float(0x42bcc695)); // 139.3f, 94.3916f, 140, 94.3895f, 140.7f, 94.3879f +path.cubicTo(SkBits2Float(0x430d6666), SkBits2Float(0x42bcc5bc), SkBits2Float(0x430e199a), SkBits2Float(0x42bcc445), SkBits2Float(0x430ecccd), SkBits2Float(0x42bcc378)); // 141.4f, 94.3862f, 142.1f, 94.3833f, 142.8f, 94.3818f +path.cubicTo(SkBits2Float(0x430f8000), SkBits2Float(0x42bcc2aa), SkBits2Float(0x43103333), SkBits2Float(0x42bcc32f), SkBits2Float(0x4310e666), SkBits2Float(0x42bcc1c5)); // 143.5f, 94.3802f, 144.2f, 94.3812f, 144.9f, 94.3785f +path.cubicTo(SkBits2Float(0x4311999a), SkBits2Float(0x42bcc05c), SkBits2Float(0x43124ccd), SkBits2Float(0x42bcbd88), SkBits2Float(0x43130000), SkBits2Float(0x42bcbafd)); // 145.6f, 94.3757f, 146.3f, 94.3702f, 147, 94.3652f +path.cubicTo(SkBits2Float(0x4313b333), SkBits2Float(0x42bcb872), SkBits2Float(0x43146666), SkBits2Float(0x42bcb50e), SkBits2Float(0x4315199a), SkBits2Float(0x42bcb282)); // 147.7f, 94.3602f, 148.4f, 94.3536f, 149.1f, 94.3486f +path.cubicTo(SkBits2Float(0x4315cccd), SkBits2Float(0x42bcaff7), SkBits2Float(0x43168000), SkBits2Float(0x42bcac56), SkBits2Float(0x43173333), SkBits2Float(0x42bcabba)); // 149.8f, 94.3437f, 150.5f, 94.3366f, 151.2f, 94.3354f +path.cubicTo(SkBits2Float(0x4317e666), SkBits2Float(0x42bcab1d), SkBits2Float(0x4318999a), SkBits2Float(0x42bcadfe), SkBits2Float(0x43194ccd), SkBits2Float(0x42bcaed8)); // 151.9f, 94.3342f, 152.6f, 94.3398f, 153.3f, 94.3415f +path.cubicTo(SkBits2Float(0x431a0000), SkBits2Float(0x42bcafb1), SkBits2Float(0x431ab333), SkBits2Float(0x42bcb034), SkBits2Float(0x431b6666), SkBits2Float(0x42bcb0d0)); // 154, 94.3431f, 154.7f, 94.3441f, 155.4f, 94.3453f +path.cubicTo(SkBits2Float(0x431c199a), SkBits2Float(0x42bcb16d), SkBits2Float(0x431ccccd), SkBits2Float(0x42bcb119), SkBits2Float(0x431d8000), SkBits2Float(0x42bcb282)); // 156.1f, 94.3465f, 156.8f, 94.3459f, 157.5f, 94.3486f +path.cubicTo(SkBits2Float(0x431e3333), SkBits2Float(0x42bcb3ec), SkBits2Float(0x431ee666), SkBits2Float(0x42bcb708), SkBits2Float(0x431f999a), SkBits2Float(0x42bcb94b)); // 158.2f, 94.3514f, 158.9f, 94.3575f, 159.6f, 94.3619f +path.cubicTo(SkBits2Float(0x43204ccd), SkBits2Float(0x42bcbb8e), SkBits2Float(0x43210000), SkBits2Float(0x42bcbef2), SkBits2Float(0x4321b333), SkBits2Float(0x42bcc013)); // 160.3f, 94.3663f, 161, 94.3729f, 161.7f, 94.3751f +path.cubicTo(SkBits2Float(0x43226666), SkBits2Float(0x42bcc135), SkBits2Float(0x4323199a), SkBits2Float(0x42bcbfe3), SkBits2Float(0x4323cccd), SkBits2Float(0x42bcc013)); // 162.4f, 94.3774f, 163.1f, 94.3748f, 163.8f, 94.3751f +path.cubicTo(SkBits2Float(0x43248000), SkBits2Float(0x42bcc044), SkBits2Float(0x43253333), SkBits2Float(0x42bcbf19), SkBits2Float(0x4325e666), SkBits2Float(0x42bcc138)); // 164.5f, 94.3755f, 165.2f, 94.3732f, 165.9f, 94.3774f +path.cubicTo(SkBits2Float(0x4326999a), SkBits2Float(0x42bcc358), SkBits2Float(0x43274ccd), SkBits2Float(0x42bcc7c6), SkBits2Float(0x43280000), SkBits2Float(0x42bcccd1)); // 166.6f, 94.3815f, 167.3f, 94.3902f, 168, 94.4f +path.cubicTo(SkBits2Float(0x4328b333), SkBits2Float(0x42bcd1db), SkBits2Float(0x43296666), SkBits2Float(0x42bcd9b9), SkBits2Float(0x432a199a), SkBits2Float(0x42bcdf78)); // 168.7f, 94.4099f, 169.4f, 94.4252f, 170.1f, 94.4365f +path.cubicTo(SkBits2Float(0x432acccd), SkBits2Float(0x42bce536), SkBits2Float(0x432b8000), SkBits2Float(0x42bceb84), SkBits2Float(0x432c3333), SkBits2Float(0x42bcef48)); // 170.8f, 94.4477f, 171.5f, 94.46f, 172.2f, 94.4673f +path.cubicTo(SkBits2Float(0x432ce666), SkBits2Float(0x42bcf30c), SkBits2Float(0x432d999a), SkBits2Float(0x42bcf3ce), SkBits2Float(0x432e4ccd), SkBits2Float(0x42bcf611)); // 172.9f, 94.4747f, 173.6f, 94.4762f, 174.3f, 94.4806f +path.cubicTo(SkBits2Float(0x432f0000), SkBits2Float(0x42bcf853), SkBits2Float(0x432fb333), SkBits2Float(0x42bcfb31), SkBits2Float(0x43306666), SkBits2Float(0x42bcfcd9)); // 175, 94.485f, 175.7f, 94.4906f, 176.4f, 94.4938f +path.lineTo(SkBits2Float(0x43306666), SkBits2Float(0x42bd0000)); // 176.4f, 94.5f +path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000)); // 88.2f, 94.5f +path.close(); +path.moveTo(SkBits2Float(0x43413333), SkBits2Float(0x42bd0000)); // 193.2f, 94.5f +path.lineTo(SkBits2Float(0x43413333), SkBits2Float(0x42bd0000)); // 193.2f, 94.5f +path.cubicTo(SkBits2Float(0x4341e666), SkBits2Float(0x42bd0000), SkBits2Float(0x4342999a), SkBits2Float(0x42bd0549), SkBits2Float(0x43434ccd), SkBits2Float(0x42bd0000)); // 193.9f, 94.5f, 194.6f, 94.5103f, 195.3f, 94.5f +path.cubicTo(SkBits2Float(0x43440000), SkBits2Float(0x42bcfab7), SkBits2Float(0x4344b333), SkBits2Float(0x42bcec20), SkBits2Float(0x43456666), SkBits2Float(0x42bce04c)); // 196, 94.4897f, 196.7f, 94.4612f, 197.4f, 94.4381f +path.cubicTo(SkBits2Float(0x4346199a), SkBits2Float(0x42bcd477), SkBits2Float(0x4346cccd), SkBits2Float(0x42bcc14e), SkBits2Float(0x43478000), SkBits2Float(0x42bcb904)); // 198.1f, 94.415f, 198.8f, 94.3775f, 199.5f, 94.3614f +path.cubicTo(SkBits2Float(0x43483333), SkBits2Float(0x42bcb0ba), SkBits2Float(0x4348e666), SkBits2Float(0x42bcaed9), SkBits2Float(0x4349999a), SkBits2Float(0x42bcae91)); // 200.2f, 94.3452f, 200.9f, 94.3415f, 201.6f, 94.341f +path.cubicTo(SkBits2Float(0x434a4ccd), SkBits2Float(0x42bcae49), SkBits2Float(0x434b0000), SkBits2Float(0x42bcb23c), SkBits2Float(0x434bb333), SkBits2Float(0x42bcb752)); // 202.3f, 94.3404f, 203, 94.3481f, 203.7f, 94.358f +path.cubicTo(SkBits2Float(0x434c6666), SkBits2Float(0x42bcbc69), SkBits2Float(0x434d199a), SkBits2Float(0x42bcc612), SkBits2Float(0x434dcccd), SkBits2Float(0x42bccd17)); // 204.4f, 94.368f, 205.1f, 94.3869f, 205.8f, 94.4006f +path.cubicTo(SkBits2Float(0x434e8000), SkBits2Float(0x42bcd41c), SkBits2Float(0x434f3333), SkBits2Float(0x42bcdc5a), SkBits2Float(0x434fe666), SkBits2Float(0x42bce171)); // 206.5f, 94.4143f, 207.2f, 94.4304f, 207.9f, 94.4403f +path.cubicTo(SkBits2Float(0x4350999a), SkBits2Float(0x42bce687), SkBits2Float(0x43514ccd), SkBits2Float(0x42bceb0d), SkBits2Float(0x43520000), SkBits2Float(0x42bceb9d)); // 208.6f, 94.4502f, 209.3f, 94.4591f, 210, 94.4602f +path.cubicTo(SkBits2Float(0x4352b333), SkBits2Float(0x42bcec2e), SkBits2Float(0x43536666), SkBits2Float(0x42bcea70), SkBits2Float(0x4354199a), SkBits2Float(0x42bce4d5)); // 210.7f, 94.4613f, 211.4f, 94.4579f, 212.1f, 94.4469f +path.cubicTo(SkBits2Float(0x4354cccd), SkBits2Float(0x42bcdf39), SkBits2Float(0x43558000), SkBits2Float(0x42bcd432), SkBits2Float(0x43563333), SkBits2Float(0x42bcc9f9)); // 212.8f, 94.436f, 213.5f, 94.4144f, 214.2f, 94.3945f +path.cubicTo(SkBits2Float(0x4356e666), SkBits2Float(0x42bcbfc1), SkBits2Float(0x4357999a), SkBits2Float(0x42bcb9bc), SkBits2Float(0x43584ccd), SkBits2Float(0x42bca782)); // 214.9f, 94.3745f, 215.6f, 94.3628f, 216.3f, 94.3272f +path.cubicTo(SkBits2Float(0x43590000), SkBits2Float(0x42bc9548), SkBits2Float(0x4359b333), SkBits2Float(0x42bc76fe), SkBits2Float(0x435a6666), SkBits2Float(0x42bc5c9e)); // 217, 94.2916f, 217.7f, 94.2324f, 218.4f, 94.1809f +path.cubicTo(SkBits2Float(0x435b199a), SkBits2Float(0x42bc423e), SkBits2Float(0x435bcccd), SkBits2Float(0x42bc19b0), SkBits2Float(0x435c8000), SkBits2Float(0x42bc0940)); // 219.1f, 94.1294f, 219.8f, 94.0502f, 220.5f, 94.0181f +path.cubicTo(SkBits2Float(0x435d3333), SkBits2Float(0x42bbf8cf), SkBits2Float(0x435de666), SkBits2Float(0x42bbfbf7), SkBits2Float(0x435e999a), SkBits2Float(0x42bbf9fc)); // 221.2f, 93.986f, 221.9f, 93.9921f, 222.6f, 93.9883f +path.cubicTo(SkBits2Float(0x435f4ccd), SkBits2Float(0x42bbf802), SkBits2Float(0x43600000), SkBits2Float(0x42bbfad6), SkBits2Float(0x4360b333), SkBits2Float(0x42bbfd61)); // 223.3f, 93.9844f, 224, 93.9899f, 224.7f, 93.9949f +path.cubicTo(SkBits2Float(0x43616666), SkBits2Float(0x42bbffec), SkBits2Float(0x4362199a), SkBits2Float(0x42bc06fd), SkBits2Float(0x4362cccd), SkBits2Float(0x42bc0940)); // 225.4f, 93.9998f, 226.1f, 94.0136f, 226.8f, 94.0181f +path.cubicTo(SkBits2Float(0x43638000), SkBits2Float(0x42bc0b82), SkBits2Float(0x43643333), SkBits2Float(0x42bc0c13), SkBits2Float(0x4364e666), SkBits2Float(0x42bc0af2)); // 227.5f, 94.0225f, 228.2f, 94.0236f, 228.9f, 94.0214f +path.cubicTo(SkBits2Float(0x4365999a), SkBits2Float(0x42bc09d0), SkBits2Float(0x43664ccd), SkBits2Float(0x42bc019e), SkBits2Float(0x43670000), SkBits2Float(0x42bc0277)); // 229.6f, 94.0192f, 230.3f, 94.0032f, 231, 94.0048f +path.cubicTo(SkBits2Float(0x4367b333), SkBits2Float(0x42bc0350), SkBits2Float(0x43686666), SkBits2Float(0x42bc0b3a), SkBits2Float(0x4369199a), SkBits2Float(0x42bc1008)); // 231.7f, 94.0065f, 232.4f, 94.0219f, 233.1f, 94.0313f +path.cubicTo(SkBits2Float(0x4369cccd), SkBits2Float(0x42bc14d6), SkBits2Float(0x436a8000), SkBits2Float(0x42bc1d08), SkBits2Float(0x436b3333), SkBits2Float(0x42bc1f4b)); // 233.8f, 94.0407f, 234.5f, 94.0567f, 235.2f, 94.0611f +path.cubicTo(SkBits2Float(0x436be666), SkBits2Float(0x42bc218e), SkBits2Float(0x436c999a), SkBits2Float(0x42bc1e2a), SkBits2Float(0x436d4ccd), SkBits2Float(0x42bc1d99)); // 235.9f, 94.0655f, 236.6f, 94.0589f, 237.3f, 94.0578f +path.cubicTo(SkBits2Float(0x436e0000), SkBits2Float(0x42bc1d08), SkBits2Float(0x436eb333), SkBits2Float(0x42bc1cc0), SkBits2Float(0x436f6666), SkBits2Float(0x42bc1be7)); // 238, 94.0567f, 238.7f, 94.0562f, 239.4f, 94.0545f +path.cubicTo(SkBits2Float(0x4370199a), SkBits2Float(0x42bc1b0e), SkBits2Float(0x4370cccd), SkBits2Float(0x42bc195c), SkBits2Float(0x43718000), SkBits2Float(0x42bc1883)); // 240.1f, 94.0528f, 240.8f, 94.0495f, 241.5f, 94.0479f +path.cubicTo(SkBits2Float(0x43723333), SkBits2Float(0x42bc17aa), SkBits2Float(0x4372e666), SkBits2Float(0x42bc1719), SkBits2Float(0x4373999a), SkBits2Float(0x42bc16d0)); // 242.2f, 94.0462f, 242.9f, 94.0451f, 243.6f, 94.0446f +path.cubicTo(SkBits2Float(0x43744ccd), SkBits2Float(0x42bc1688), SkBits2Float(0x43750000), SkBits2Float(0x42bc1719), SkBits2Float(0x4375b333), SkBits2Float(0x42bc16d0)); // 244.3f, 94.044f, 245, 94.0451f, 245.7f, 94.0446f +path.cubicTo(SkBits2Float(0x43766666), SkBits2Float(0x42bc1688), SkBits2Float(0x4377199a), SkBits2Float(0x42bc1567), SkBits2Float(0x4377cccd), SkBits2Float(0x42bc151e)); // 246.4f, 94.044f, 247.1f, 94.0418f, 247.8f, 94.0412f +path.cubicTo(SkBits2Float(0x43788000), SkBits2Float(0x42bc14d6), SkBits2Float(0x43793333), SkBits2Float(0x42bc148e), SkBits2Float(0x4379e666), SkBits2Float(0x42bc151e)); // 248.5f, 94.0407f, 249.2f, 94.0401f, 249.9f, 94.0412f +path.cubicTo(SkBits2Float(0x437a999a), SkBits2Float(0x42bc15af), SkBits2Float(0x437b4ccd), SkBits2Float(0x42bc1761), SkBits2Float(0x437c0000), SkBits2Float(0x42bc1883)); // 250.6f, 94.0424f, 251.3f, 94.0457f, 252, 94.0479f +path.cubicTo(SkBits2Float(0x437cb333), SkBits2Float(0x42bc19a4), SkBits2Float(0x437d6666), SkBits2Float(0x42bc1b0e), SkBits2Float(0x437e199a), SkBits2Float(0x42bc1be7)); // 252.7f, 94.0501f, 253.4f, 94.0528f, 254.1f, 94.0545f +path.cubicTo(SkBits2Float(0x437ecccd), SkBits2Float(0x42bc1cc0), SkBits2Float(0x437f8000), SkBits2Float(0x42bc1d99), SkBits2Float(0x4380199a), SkBits2Float(0x42bc1d99)); // 254.8f, 94.0562f, 255.5f, 94.0578f, 256.2f, 94.0578f +path.cubicTo(SkBits2Float(0x43807333), SkBits2Float(0x42bc1d99), SkBits2Float(0x4380cccd), SkBits2Float(0x42bc1d08), SkBits2Float(0x43812666), SkBits2Float(0x42bc1be7)); // 256.9f, 94.0578f, 257.6f, 94.0567f, 258.3f, 94.0545f +path.cubicTo(SkBits2Float(0x43818000), SkBits2Float(0x42bc1ac5), SkBits2Float(0x4381d99a), SkBits2Float(0x42bc183a), SkBits2Float(0x43823333), SkBits2Float(0x42bc16d0)); // 259, 94.0523f, 259.7f, 94.0473f, 260.4f, 94.0446f +path.cubicTo(SkBits2Float(0x43828ccd), SkBits2Float(0x42bc1567), SkBits2Float(0x4382e666), SkBits2Float(0x42bc13fd), SkBits2Float(0x43834000), SkBits2Float(0x42bc136c)); // 261.1f, 94.0418f, 261.8f, 94.039f, 262.5f, 94.0379f +path.cubicTo(SkBits2Float(0x4383999a), SkBits2Float(0x42bc12dc), SkBits2Float(0x4383f333), SkBits2Float(0x42bc1324), SkBits2Float(0x43844ccd), SkBits2Float(0x42bc136c)); // 263.2f, 94.0368f, 263.9f, 94.0374f, 264.6f, 94.0379f +path.cubicTo(SkBits2Float(0x4384a666), SkBits2Float(0x42bc13b5), SkBits2Float(0x43850000), SkBits2Float(0x42bc148e), SkBits2Float(0x4385599a), SkBits2Float(0x42bc151e)); // 265.3f, 94.0385f, 266, 94.0401f, 266.7f, 94.0412f +path.cubicTo(SkBits2Float(0x4385b333), SkBits2Float(0x42bc15af), SkBits2Float(0x43860ccd), SkBits2Float(0x42bc1688), SkBits2Float(0x43866666), SkBits2Float(0x42bc16d0)); // 267.4f, 94.0424f, 268.1f, 94.044f, 268.8f, 94.0446f +path.cubicTo(SkBits2Float(0x4386c000), SkBits2Float(0x42bc1719), SkBits2Float(0x4387199a), SkBits2Float(0x42bc1719), SkBits2Float(0x43877333), SkBits2Float(0x42bc16d0)); // 269.5f, 94.0451f, 270.2f, 94.0451f, 270.9f, 94.0446f +path.cubicTo(SkBits2Float(0x4387cccd), SkBits2Float(0x42bc1688), SkBits2Float(0x43882666), SkBits2Float(0x42bc1567), SkBits2Float(0x43888000), SkBits2Float(0x42bc151e)); // 271.6f, 94.044f, 272.3f, 94.0418f, 273, 94.0412f +path.cubicTo(SkBits2Float(0x4388d99a), SkBits2Float(0x42bc14d6), SkBits2Float(0x43893333), SkBits2Float(0x42bc151e), SkBits2Float(0x43898ccd), SkBits2Float(0x42bc151e)); // 273.7f, 94.0407f, 274.4f, 94.0412f, 275.1f, 94.0412f +path.cubicTo(SkBits2Float(0x4389e666), SkBits2Float(0x42bc151e), SkBits2Float(0x438a4000), SkBits2Float(0x42bc1567), SkBits2Float(0x438a999a), SkBits2Float(0x42bc151e)); // 275.8f, 94.0412f, 276.5f, 94.0418f, 277.2f, 94.0412f +path.cubicTo(SkBits2Float(0x438af333), SkBits2Float(0x42bc14d6), SkBits2Float(0x438b4ccd), SkBits2Float(0x42bc136c), SkBits2Float(0x438ba666), SkBits2Float(0x42bc136c)); // 277.9f, 94.0407f, 278.6f, 94.0379f, 279.3f, 94.0379f +path.cubicTo(SkBits2Float(0x438c0000), SkBits2Float(0x42bc136c), SkBits2Float(0x438c599a), SkBits2Float(0x42bc152a), SkBits2Float(0x438cb333), SkBits2Float(0x42bc151e)); // 280, 94.0379f, 280.7f, 94.0413f, 281.4f, 94.0412f +path.cubicTo(SkBits2Float(0x438d0ccd), SkBits2Float(0x42bc1513), SkBits2Float(0x438d6666), SkBits2Float(0x42bc14ef), SkBits2Float(0x438dc000), SkBits2Float(0x42bc1326)); // 282.1f, 94.0412f, 282.8f, 94.0409f, 283.5f, 94.0374f +path.cubicTo(SkBits2Float(0x438e199a), SkBits2Float(0x42bc115c), SkBits2Float(0x438e7333), SkBits2Float(0x42bc0c17), SkBits2Float(0x438ecccd), SkBits2Float(0x42bc0a64)); // 284.2f, 94.0339f, 284.9f, 94.0236f, 285.6f, 94.0203f +path.cubicTo(SkBits2Float(0x438f2666), SkBits2Float(0x42bc08b2), SkBits2Float(0x438f8000), SkBits2Float(0x42bc0413), SkBits2Float(0x438fd99a), SkBits2Float(0x42bc08f9)); // 286.3f, 94.017f, 287, 94.008f, 287.7f, 94.0175f +path.cubicTo(SkBits2Float(0x43903333), SkBits2Float(0x42bc0dde), SkBits2Float(0x43908ccd), SkBits2Float(0x42bc1476), SkBits2Float(0x4390e666), SkBits2Float(0x42bc27c6)); // 288.4f, 94.0271f, 289.1f, 94.04f, 289.8f, 94.0777f +path.cubicTo(SkBits2Float(0x43914000), SkBits2Float(0x42bc3b15), SkBits2Float(0x4391999a), SkBits2Float(0x42bc5916), SkBits2Float(0x4391f333), SkBits2Float(0x42bc7cd6)); // 290.5f, 94.1154f, 291.2f, 94.174f, 291.9f, 94.2438f +path.cubicTo(SkBits2Float(0x43924ccd), SkBits2Float(0x42bca096), SkBits2Float(0x4392a666), SkBits2Float(0x42bce868), SkBits2Float(0x43930000), SkBits2Float(0x42bcfe45)); // 292.6f, 94.3136f, 293.3f, 94.4539f, 294, 94.4966f +path.cubicTo(SkBits2Float(0x4393599a), SkBits2Float(0x42bd1421), SkBits2Float(0x4393b333), SkBits2Float(0x42bcffb6), SkBits2Float(0x43940ccd), SkBits2Float(0x42bd0000)); // 294.7f, 94.5393f, 295.4f, 94.4994f, 296.1f, 94.5f +path.lineTo(SkBits2Float(0x43940ccd), SkBits2Float(0x42bd0000)); // 296.1f, 94.5f +path.lineTo(SkBits2Float(0x43413333), SkBits2Float(0x42bd0000)); // 193.2f, 94.5f +path.close(); +path.moveTo(SkBits2Float(0x43ac3333), SkBits2Float(0x42bd0000)); // 344.4f, 94.5f +path.lineTo(SkBits2Float(0x43ac3333), SkBits2Float(0x42bd0000)); // 344.4f, 94.5f +path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42bd0000), SkBits2Float(0x43ace666), SkBits2Float(0x42bd03a2), SkBits2Float(0x43ad4000), SkBits2Float(0x42bd0000)); // 345.1f, 94.5f, 345.8f, 94.5071f, 346.5f, 94.5f +path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42bcfc5e), SkBits2Float(0x43adf333), SkBits2Float(0x42bd069a), SkBits2Float(0x43ae4ccd), SkBits2Float(0x42bcea32)); // 347.2f, 94.4929f, 347.9f, 94.5129f, 348.6f, 94.4574f +path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x42bccdca), SkBits2Float(0x43af0000), SkBits2Float(0x42bc7b18), SkBits2Float(0x43af599a), SkBits2Float(0x42bc558f)); // 349.3f, 94.4019f, 350, 94.2404f, 350.7f, 94.1671f +path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x42bc3005), SkBits2Float(0x43b00ccd), SkBits2Float(0x42bc16d2), SkBits2Float(0x43b06666), SkBits2Float(0x42bc08f9)); // 351.4f, 94.0938f, 352.1f, 94.0446f, 352.8f, 94.0175f +path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x42bbfb20), SkBits2Float(0x43b1199a), SkBits2Float(0x42bc03d5), SkBits2Float(0x43b17333), SkBits2Float(0x42bc0277)); // 353.5f, 93.9905f, 354.2f, 94.0075f, 354.9f, 94.0048f +path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42bc0119), SkBits2Float(0x43b22666), SkBits2Float(0x42bc019e), SkBits2Float(0x43b28000), SkBits2Float(0x42bc00c5)); // 355.6f, 94.0021f, 356.3f, 94.0032f, 357, 94.0015f +path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x42bbffec), SkBits2Float(0x43b33333), SkBits2Float(0x42bbfdf1), SkBits2Float(0x43b38ccd), SkBits2Float(0x42bbfd61)); // 357.7f, 93.9998f, 358.4f, 93.996f, 359.1f, 93.9949f +path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x42bbfcd0), SkBits2Float(0x43b44000), SkBits2Float(0x42bbfdf1), SkBits2Float(0x43b4999a), SkBits2Float(0x42bbfd61)); // 359.8f, 93.9938f, 360.5f, 93.996f, 361.2f, 93.9949f +path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x42bbfcd0), SkBits2Float(0x43b54ccd), SkBits2Float(0x42bbfad6), SkBits2Float(0x43b5a666), SkBits2Float(0x42bbf9fc)); // 361.9f, 93.9938f, 362.6f, 93.9899f, 363.3f, 93.9883f +path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x42bbf923), SkBits2Float(0x43b6599a), SkBits2Float(0x42bbf8db), SkBits2Float(0x43b6b333), SkBits2Float(0x42bbf84a)); // 364, 93.9866f, 364.7f, 93.986f, 365.4f, 93.9849f +path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x42bbf7ba), SkBits2Float(0x43b76666), SkBits2Float(0x42bbf729), SkBits2Float(0x43b7c000), SkBits2Float(0x42bbf698)); // 366.1f, 93.9838f, 366.8f, 93.9827f, 367.5f, 93.9816f +path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x42bbf608), SkBits2Float(0x43b87333), SkBits2Float(0x42bbf52e), SkBits2Float(0x43b8cccd), SkBits2Float(0x42bbf4e6)); // 368.2f, 93.9805f, 368.9f, 93.9789f, 369.6f, 93.9783f +path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x42bbf49e), SkBits2Float(0x43b98000), SkBits2Float(0x42bbf455), SkBits2Float(0x43b9d99a), SkBits2Float(0x42bbf4e6)); // 370.3f, 93.9778f, 371, 93.9772f, 371.7f, 93.9783f +path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x42bbf577), SkBits2Float(0x43ba8ccd), SkBits2Float(0x42bbf771), SkBits2Float(0x43bae666), SkBits2Float(0x42bbf84a)); // 372.4f, 93.9794f, 373.1f, 93.9833f, 373.8f, 93.9849f +path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x42bbf923), SkBits2Float(0x43bb999a), SkBits2Float(0x42bbf9b4), SkBits2Float(0x43bbf333), SkBits2Float(0x42bbf9fc)); // 374.5f, 93.9866f, 375.2f, 93.9877f, 375.9f, 93.9883f +path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x42bbfa45), SkBits2Float(0x43bca666), SkBits2Float(0x42bbf9fc), SkBits2Float(0x43bd0000), SkBits2Float(0x42bbf9fc)); // 376.6f, 93.9888f, 377.3f, 93.9883f, 378, 93.9883f +path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x42bbf9fc), SkBits2Float(0x43bdb333), SkBits2Float(0x42bbf9fc), SkBits2Float(0x43be0ccd), SkBits2Float(0x42bbf9fc)); // 378.7f, 93.9883f, 379.4f, 93.9883f, 380.1f, 93.9883f +path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x42bbf9fc), SkBits2Float(0x43bec000), SkBits2Float(0x42bbf8db), SkBits2Float(0x43bf199a), SkBits2Float(0x42bbf9fc)); // 380.8f, 93.9883f, 381.5f, 93.986f, 382.2f, 93.9883f +path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x42bbfb1e), SkBits2Float(0x43bfcccd), SkBits2Float(0x42bbfeca), SkBits2Float(0x43c02666), SkBits2Float(0x42bc00c5)); // 382.9f, 93.9905f, 383.6f, 93.9976f, 384.3f, 94.0015f +path.cubicTo(SkBits2Float(0x43c08000), SkBits2Float(0x42bc02bf), SkBits2Float(0x43c0d99a), SkBits2Float(0x42bc0593), SkBits2Float(0x43c13333), SkBits2Float(0x42bc05db)); // 385, 94.0054f, 385.7f, 94.0109f, 386.4f, 94.0114f +path.cubicTo(SkBits2Float(0x43c18ccd), SkBits2Float(0x42bc0624), SkBits2Float(0x43c1e666), SkBits2Float(0x42bc0308), SkBits2Float(0x43c24000), SkBits2Float(0x42bc0277)); // 387.1f, 94.012f, 387.8f, 94.0059f, 388.5f, 94.0048f +path.cubicTo(SkBits2Float(0x43c2999a), SkBits2Float(0x42bc01e6), SkBits2Float(0x43c2f333), SkBits2Float(0x42bc022f), SkBits2Float(0x43c34ccd), SkBits2Float(0x42bc0277)); // 389.2f, 94.0037f, 389.9f, 94.0043f, 390.6f, 94.0048f +path.cubicTo(SkBits2Float(0x43c3a666), SkBits2Float(0x42bc02bf), SkBits2Float(0x43c40000), SkBits2Float(0x42bc02bf), SkBits2Float(0x43c4599a), SkBits2Float(0x42bc0429)); // 391.3f, 94.0054f, 392, 94.0054f, 392.7f, 94.0081f +path.cubicTo(SkBits2Float(0x43c4b333), SkBits2Float(0x42bc0593), SkBits2Float(0x43c50ccd), SkBits2Float(0x42bc08f7), SkBits2Float(0x43c56666), SkBits2Float(0x42bc0af2)); // 393.4f, 94.0109f, 394.1f, 94.0175f, 394.8f, 94.0214f +path.cubicTo(SkBits2Float(0x43c5c000), SkBits2Float(0x42bc0cec), SkBits2Float(0x43c6199a), SkBits2Float(0x42bc0f2f), SkBits2Float(0x43c67333), SkBits2Float(0x42bc1008)); // 395.5f, 94.0252f, 396.2f, 94.0297f, 396.9f, 94.0313f +path.cubicTo(SkBits2Float(0x43c6cccd), SkBits2Float(0x42bc10e1), SkBits2Float(0x43c72666), SkBits2Float(0x42bc0fc0), SkBits2Float(0x43c78000), SkBits2Float(0x42bc1008)); // 397.6f, 94.033f, 398.3f, 94.0308f, 399, 94.0313f +path.cubicTo(SkBits2Float(0x43c7d99a), SkBits2Float(0x42bc1050), SkBits2Float(0x43c83333), SkBits2Float(0x42bc1172), SkBits2Float(0x43c88ccd), SkBits2Float(0x42bc11ba)); // 399.7f, 94.0319f, 400.4f, 94.0341f, 401.1f, 94.0346f +path.cubicTo(SkBits2Float(0x43c8e666), SkBits2Float(0x42bc1202), SkBits2Float(0x43c94000), SkBits2Float(0x42bc11ba), SkBits2Float(0x43c9999a), SkBits2Float(0x42bc11ba)); // 401.8f, 94.0352f, 402.5f, 94.0346f, 403.2f, 94.0346f +path.cubicTo(SkBits2Float(0x43c9f333), SkBits2Float(0x42bc11ba), SkBits2Float(0x43ca4ccd), SkBits2Float(0x42bc124b), SkBits2Float(0x43caa666), SkBits2Float(0x42bc11ba)); // 403.9f, 94.0346f, 404.6f, 94.0357f, 405.3f, 94.0346f +path.cubicTo(SkBits2Float(0x43cb0000), SkBits2Float(0x42bc1129), SkBits2Float(0x43cb599a), SkBits2Float(0x42bc0f77), SkBits2Float(0x43cbb333), SkBits2Float(0x42bc0e56)); // 406, 94.0335f, 406.7f, 94.0302f, 407.4f, 94.028f +path.cubicTo(SkBits2Float(0x43cc0ccd), SkBits2Float(0x42bc0d34), SkBits2Float(0x43cc6666), SkBits2Float(0x42bc0b82), SkBits2Float(0x43ccc000), SkBits2Float(0x42bc0af2)); // 408.1f, 94.0258f, 408.8f, 94.0225f, 409.5f, 94.0214f +path.cubicTo(SkBits2Float(0x43cd199a), SkBits2Float(0x42bc0a61), SkBits2Float(0x43cd7333), SkBits2Float(0x42bc0b3a), SkBits2Float(0x43cdcccd), SkBits2Float(0x42bc0af2)); // 410.2f, 94.0203f, 410.9f, 94.0219f, 411.6f, 94.0214f +path.cubicTo(SkBits2Float(0x43ce2666), SkBits2Float(0x42bc0aa9), SkBits2Float(0x43ce8000), SkBits2Float(0x42bc0aa9), SkBits2Float(0x43ced99a), SkBits2Float(0x42bc0940)); // 412.3f, 94.0208f, 413, 94.0208f, 413.7f, 94.0181f +path.cubicTo(SkBits2Float(0x43cf3333), SkBits2Float(0x42bc07d6), SkBits2Float(0x43cf8ccd), SkBits2Float(0x42bc0502), SkBits2Float(0x43cfe666), SkBits2Float(0x42bc0277)); // 414.4f, 94.0153f, 415.1f, 94.0098f, 415.8f, 94.0048f +path.cubicTo(SkBits2Float(0x43d04000), SkBits2Float(0x42bbffec), SkBits2Float(0x43d0999a), SkBits2Float(0x42bbfc88), SkBits2Float(0x43d0f333), SkBits2Float(0x42bbf9fc)); // 416.5f, 93.9998f, 417.2f, 93.9932f, 417.9f, 93.9883f +path.cubicTo(SkBits2Float(0x43d14ccd), SkBits2Float(0x42bbf771), SkBits2Float(0x43d1a666), SkBits2Float(0x42bbf4e6), SkBits2Float(0x43d20000), SkBits2Float(0x42bbf334)); // 418.6f, 93.9833f, 419.3f, 93.9783f, 420, 93.975f +path.cubicTo(SkBits2Float(0x43d2599a), SkBits2Float(0x42bbf182), SkBits2Float(0x43d2b333), SkBits2Float(0x42bbee66), SkBits2Float(0x43d30ccd), SkBits2Float(0x42bbefd0)); // 420.7f, 93.9717f, 421.4f, 93.9656f, 422.1f, 93.9684f +path.cubicTo(SkBits2Float(0x43d36666), SkBits2Float(0x42bbf13a), SkBits2Float(0x43d3c000), SkBits2Float(0x42bbf52e), SkBits2Float(0x43d4199a), SkBits2Float(0x42bbfbaf)); // 422.8f, 93.9711f, 423.5f, 93.9789f, 424.2f, 93.9916f +path.cubicTo(SkBits2Float(0x43d47333), SkBits2Float(0x42bc022f), SkBits2Float(0x43d4cccd), SkBits2Float(0x42bc1014), SkBits2Float(0x43d52666), SkBits2Float(0x42bc16d0)); // 424.9f, 94.0043f, 425.6f, 94.0314f, 426.3f, 94.0446f +path.cubicTo(SkBits2Float(0x43d58000), SkBits2Float(0x42bc1d8d), SkBits2Float(0x43d5d99a), SkBits2Float(0x42bc1de3), SkBits2Float(0x43d63333), SkBits2Float(0x42bc241b)); // 427, 94.0577f, 427.7f, 94.0584f, 428.4f, 94.0705f +path.cubicTo(SkBits2Float(0x43d68ccd), SkBits2Float(0x42bc2a53), SkBits2Float(0x43d6e666), SkBits2Float(0x42bc30c5), SkBits2Float(0x43d74000), SkBits2Float(0x42bc3c1f)); // 429.1f, 94.0827f, 429.8f, 94.0953f, 430.5f, 94.1174f +path.cubicTo(SkBits2Float(0x43d7999a), SkBits2Float(0x42bc4779), SkBits2Float(0x43d7f333), SkBits2Float(0x42bc6283), SkBits2Float(0x43d84ccd), SkBits2Float(0x42bc6836)); // 431.2f, 94.1396f, 431.9f, 94.1924f, 432.6f, 94.2035f +path.cubicTo(SkBits2Float(0x43d8a666), SkBits2Float(0x42bc6de9), SkBits2Float(0x43d90000), SkBits2Float(0x42bc67e0), SkBits2Float(0x43d9599a), SkBits2Float(0x42bc5e50)); // 433.3f, 94.2147f, 434, 94.2029f, 434.7f, 94.1842f +path.cubicTo(SkBits2Float(0x43d9b333), SkBits2Float(0x42bc54c0), SkBits2Float(0x43da0ccd), SkBits2Float(0x42bc3a23), SkBits2Float(0x43da6666), SkBits2Float(0x42bc2ed5)); // 435.4f, 94.1655f, 436.1f, 94.1135f, 436.8f, 94.0915f +path.cubicTo(SkBits2Float(0x43dac000), SkBits2Float(0x42bc2387), SkBits2Float(0x43db199a), SkBits2Float(0x42bc1e34), SkBits2Float(0x43db7333), SkBits2Float(0x42bc1a7b)); // 437.5f, 94.0694f, 438.2f, 94.059f, 438.9f, 94.0517f +path.cubicTo(SkBits2Float(0x43dbcccd), SkBits2Float(0x42bc16c3), SkBits2Float(0x43dc2666), SkBits2Float(0x42bc18a6), SkBits2Float(0x43dc8000), SkBits2Float(0x42bc1883)); // 439.6f, 94.0445f, 440.3f, 94.0481f, 441, 94.0479f +path.cubicTo(SkBits2Float(0x43dcd99a), SkBits2Float(0x42bc185f), SkBits2Float(0x43dd3333), SkBits2Float(0x42bc19f0), SkBits2Float(0x43dd8ccd), SkBits2Float(0x42bc19a8)); // 441.7f, 94.0476f, 442.4f, 94.0507f, 443.1f, 94.0501f +path.cubicTo(SkBits2Float(0x43dde666), SkBits2Float(0x42bc195f), SkBits2Float(0x43de4000), SkBits2Float(0x42bc15d4), SkBits2Float(0x43de999a), SkBits2Float(0x42bc16d0)); // 443.8f, 94.0496f, 444.5f, 94.0426f, 445.2f, 94.0446f +path.cubicTo(SkBits2Float(0x43def333), SkBits2Float(0x42bc17cd), SkBits2Float(0x43df4ccd), SkBits2Float(0x42bc11a1), SkBits2Float(0x43dfa666), SkBits2Float(0x42bc1f92)); // 445.9f, 94.0465f, 446.6f, 94.0344f, 447.3f, 94.0617f +path.cubicTo(SkBits2Float(0x43e00000), SkBits2Float(0x42bc2d83), SkBits2Float(0x43e0599a), SkBits2Float(0x42bc478f), SkBits2Float(0x43e0b333), SkBits2Float(0x42bc6a76)); // 448, 94.0889f, 448.7f, 94.1398f, 449.4f, 94.2079f +path.cubicTo(SkBits2Float(0x43e10ccd), SkBits2Float(0x42bc8d5c), SkBits2Float(0x43e16666), SkBits2Float(0x42bcd80e), SkBits2Float(0x43e1c000), SkBits2Float(0x42bcf0fa)); // 450.1f, 94.2761f, 450.8f, 94.422f, 451.5f, 94.4707f +path.cubicTo(SkBits2Float(0x43e2199a), SkBits2Float(0x42bd09e7), SkBits2Float(0x43e27333), SkBits2Float(0x42bd0449), SkBits2Float(0x43e2cccd), SkBits2Float(0x42bd0000)); // 452.2f, 94.5193f, 452.9f, 94.5084f, 453.6f, 94.5f +path.cubicTo(SkBits2Float(0x43e32666), SkBits2Float(0x42bcfbb7), SkBits2Float(0x43e38000), SkBits2Float(0x42bcebe7), SkBits2Float(0x43e3d99a), SkBits2Float(0x42bcd744)); // 454.3f, 94.4916f, 455, 94.4607f, 455.7f, 94.4204f +path.cubicTo(SkBits2Float(0x43e43333), SkBits2Float(0x42bcc2a0), SkBits2Float(0x43e48ccd), SkBits2Float(0x42bc8dc8), SkBits2Float(0x43e4e666), SkBits2Float(0x42bc842c)); // 456.4f, 94.3801f, 457.1f, 94.2769f, 457.8f, 94.2581f +path.cubicTo(SkBits2Float(0x43e54000), SkBits2Float(0x42bc7a90), SkBits2Float(0x43e5999a), SkBits2Float(0x42bc8f32), SkBits2Float(0x43e5f333), SkBits2Float(0x42bc9d9c)); // 458.5f, 94.2394f, 459.2f, 94.2797f, 459.9f, 94.3078f +path.cubicTo(SkBits2Float(0x43e64ccd), SkBits2Float(0x42bcac06), SkBits2Float(0x43e6a666), SkBits2Float(0x42bcca42), SkBits2Float(0x43e70000), SkBits2Float(0x42bcdaa8)); // 460.6f, 94.336f, 461.3f, 94.395f, 462, 94.4271f +path.cubicTo(SkBits2Float(0x43e7599a), SkBits2Float(0x42bceb0e), SkBits2Float(0x43e7b333), SkBits2Float(0x42bcf9c7), SkBits2Float(0x43e80ccd), SkBits2Float(0x42bd0000)); // 462.7f, 94.4591f, 463.4f, 94.4878f, 464.1f, 94.5f +path.cubicTo(SkBits2Float(0x43e86666), SkBits2Float(0x42bd0639), SkBits2Float(0x43e8c000), SkBits2Float(0x42bd010b), SkBits2Float(0x43e9199a), SkBits2Float(0x42bd0000)); // 464.8f, 94.5122f, 465.5f, 94.502f, 466.2f, 94.5f +path.cubicTo(SkBits2Float(0x43e97333), SkBits2Float(0x42bcfef5), SkBits2Float(0x43e9cccd), SkBits2Float(0x42bcf9bb), SkBits2Float(0x43ea2666), SkBits2Float(0x42bcf9bb)); // 466.9f, 94.498f, 467.6f, 94.4878f, 468.3f, 94.4878f +path.cubicTo(SkBits2Float(0x43ea8000), SkBits2Float(0x42bcf9bb), SkBits2Float(0x43ead99a), SkBits2Float(0x42bcfef5), SkBits2Float(0x43eb3333), SkBits2Float(0x42bd0000)); // 469, 94.4878f, 469.7f, 94.498f, 470.4f, 94.5f +path.lineTo(SkBits2Float(0x43eb3333), SkBits2Float(0x42bd0000)); // 470.4f, 94.5f +path.lineTo(SkBits2Float(0x43ac3333), SkBits2Float(0x42bd0000)); // 344.4f, 94.5f +path.close(); + return path; +} + +static SkPath path4() { + SkPath path; +path.moveTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000)); // 88.2f, 94.5f +path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000)); // 88.2f, 94.5f +path.cubicTo(SkBits2Float(0x42b1cccd), SkBits2Float(0x42bd0000), SkBits2Float(0x42b33333), SkBits2Float(0x42bcda8d), SkBits2Float(0x42b4999a), SkBits2Float(0x42bd0000)); // 88.9f, 94.5f, 89.6f, 94.4269f, 90.3f, 94.5f +path.cubicTo(SkBits2Float(0x42b60000), SkBits2Float(0x42bd2573), SkBits2Float(0x42b76666), SkBits2Float(0x42bdba68), SkBits2Float(0x42b8cccd), SkBits2Float(0x42bde0b5)); // 91, 94.5731f, 91.7f, 94.8641f, 92.4f, 94.9389f +path.cubicTo(SkBits2Float(0x42ba3333), SkBits2Float(0x42be0701), SkBits2Float(0x42bb999a), SkBits2Float(0x42bde462), SkBits2Float(0x42bd0000), SkBits2Float(0x42bde5cb)); // 93.1f, 95.0137f, 93.8f, 94.9461f, 94.5f, 94.9488f +path.cubicTo(SkBits2Float(0x42be6666), SkBits2Float(0x42bde735), SkBits2Float(0x42bfcccd), SkBits2Float(0x42bde80e), SkBits2Float(0x42c13333), SkBits2Float(0x42bde930)); // 95.2f, 94.9516f, 95.9f, 94.9532f, 96.6f, 94.9554f +path.cubicTo(SkBits2Float(0x42c2999a), SkBits2Float(0x42bdea51), SkBits2Float(0x42c40000), SkBits2Float(0x42bdec03), SkBits2Float(0x42c56666), SkBits2Float(0x42bdec94)); // 97.3f, 94.9576f, 98, 94.961f, 98.7f, 94.9621f +path.cubicTo(SkBits2Float(0x42c6cccd), SkBits2Float(0x42bded24), SkBits2Float(0x42c83333), SkBits2Float(0x42bded19), SkBits2Float(0x42c9999a), SkBits2Float(0x42bdec94)); // 99.4f, 94.9632f, 100.1f, 94.9631f, 100.8f, 94.9621f +path.cubicTo(SkBits2Float(0x42cb0000), SkBits2Float(0x42bdec0f), SkBits2Float(0x42cc6666), SkBits2Float(0x42bdea38), SkBits2Float(0x42cdcccd), SkBits2Float(0x42bde976)); // 101.5f, 94.9611f, 102.2f, 94.9575f, 102.9f, 94.956f +path.cubicTo(SkBits2Float(0x42cf3333), SkBits2Float(0x42bde8b5), SkBits2Float(0x42d0999a), SkBits2Float(0x42bde853), SkBits2Float(0x42d20000), SkBits2Float(0x42bde80b)); // 103.6f, 94.9545f, 104.3f, 94.9538f, 105, 94.9532f +path.cubicTo(SkBits2Float(0x42d36666), SkBits2Float(0x42bde7c2), SkBits2Float(0x42d4cccd), SkBits2Float(0x42bde9d6), SkBits2Float(0x42d63333), SkBits2Float(0x42bde7c4)); // 105.7f, 94.9527f, 106.4f, 94.9567f, 107.1f, 94.9527f +path.cubicTo(SkBits2Float(0x42d7999a), SkBits2Float(0x42bde5b2), SkBits2Float(0x42d90000), SkBits2Float(0x42bde19a), SkBits2Float(0x42da6666), SkBits2Float(0x42bddb9f)); // 107.8f, 94.9486f, 108.5f, 94.9406f, 109.2f, 94.9289f +path.cubicTo(SkBits2Float(0x42dbcccd), SkBits2Float(0x42bdd5a3), SkBits2Float(0x42dd3333), SkBits2Float(0x42bdcc07), SkBits2Float(0x42de999a), SkBits2Float(0x42bdc3e1)); // 109.9f, 94.9173f, 110.6f, 94.8985f, 111.3f, 94.8826f +path.cubicTo(SkBits2Float(0x42e00000), SkBits2Float(0x42bdbbba), SkBits2Float(0x42e16666), SkBits2Float(0x42bdb332), SkBits2Float(0x42e2cccd), SkBits2Float(0x42bdaab8)); // 112, 94.8667f, 112.7f, 94.85f, 113.4f, 94.8334f +path.cubicTo(SkBits2Float(0x42e43333), SkBits2Float(0x42bda23d), SkBits2Float(0x42e5999a), SkBits2Float(0x42bd9b8e), SkBits2Float(0x42e70000), SkBits2Float(0x42bd9101)); // 114.1f, 94.8169f, 114.8f, 94.8038f, 115.5f, 94.7832f +path.cubicTo(SkBits2Float(0x42e86666), SkBits2Float(0x42bd8675), SkBits2Float(0x42e9cccd), SkBits2Float(0x42bd79f9), SkBits2Float(0x42eb3333), SkBits2Float(0x42bd6b6c)); // 116.2f, 94.7626f, 116.9f, 94.7382f, 117.6f, 94.7098f +path.cubicTo(SkBits2Float(0x42ec999a), SkBits2Float(0x42bd5cdf), SkBits2Float(0x42ee0000), SkBits2Float(0x42bd4608), SkBits2Float(0x42ef6666), SkBits2Float(0x42bd39b1)); // 118.3f, 94.6814f, 119, 94.6368f, 119.7f, 94.6127f +path.cubicTo(SkBits2Float(0x42f0cccd), SkBits2Float(0x42bd2d5b), SkBits2Float(0x42f23333), SkBits2Float(0x42bd24cb), SkBits2Float(0x42f3999a), SkBits2Float(0x42bd2166)); // 120.4f, 94.5886f, 121.1f, 94.5719f, 121.8f, 94.5652f +path.cubicTo(SkBits2Float(0x42f50000), SkBits2Float(0x42bd1e02), SkBits2Float(0x42f66666), SkBits2Float(0x42bd22c1), SkBits2Float(0x42f7cccd), SkBits2Float(0x42bd2558)); // 122.5f, 94.5586f, 123.2f, 94.5679f, 123.9f, 94.5729f +path.cubicTo(SkBits2Float(0x42f93333), SkBits2Float(0x42bd27ef), SkBits2Float(0x42fa999a), SkBits2Float(0x42bd2da3), SkBits2Float(0x42fc0000), SkBits2Float(0x42bd30f0)); // 124.6f, 94.578f, 125.3f, 94.5891f, 126, 94.5956f +path.cubicTo(SkBits2Float(0x42fd6666), SkBits2Float(0x42bd343d), SkBits2Float(0x42fecccd), SkBits2Float(0x42bd36a5), SkBits2Float(0x4300199a), SkBits2Float(0x42bd3924)); // 126.7f, 94.602f, 127.4f, 94.6067f, 128.1f, 94.6116f +path.cubicTo(SkBits2Float(0x4300cccd), SkBits2Float(0x42bd3ba4), SkBits2Float(0x43018000), SkBits2Float(0x42bd3f14), SkBits2Float(0x43023333), SkBits2Float(0x42bd3fed)); // 128.8f, 94.6165f, 129.5f, 94.6232f, 130.2f, 94.6249f +path.cubicTo(SkBits2Float(0x4302e666), SkBits2Float(0x42bd40c6), SkBits2Float(0x4303999a), SkBits2Float(0x42bd3f50), SkBits2Float(0x43044ccd), SkBits2Float(0x42bd3e3b)); // 130.9f, 94.6265f, 131.6f, 94.6237f, 132.3f, 94.6215f +path.cubicTo(SkBits2Float(0x43050000), SkBits2Float(0x42bd3d25), SkBits2Float(0x4305b333), SkBits2Float(0x42bd3a44), SkBits2Float(0x43066666), SkBits2Float(0x42bd396b)); // 133, 94.6194f, 133.7f, 94.6138f, 134.4f, 94.6121f +path.cubicTo(SkBits2Float(0x4307199a), SkBits2Float(0x42bd3892), SkBits2Float(0x4307cccd), SkBits2Float(0x42bd3978), SkBits2Float(0x43088000), SkBits2Float(0x42bd3924)); // 135.1f, 94.6105f, 135.8f, 94.6122f, 136.5f, 94.6116f +path.cubicTo(SkBits2Float(0x43093333), SkBits2Float(0x42bd38d0), SkBits2Float(0x4309e666), SkBits2Float(0x42bd3766), SkBits2Float(0x430a999a), SkBits2Float(0x42bd3772)); // 137.2f, 94.611f, 137.9f, 94.6082f, 138.6f, 94.6083f +path.cubicTo(SkBits2Float(0x430b4ccd), SkBits2Float(0x42bd377e), SkBits2Float(0x430c0000), SkBits2Float(0x42bd3892), SkBits2Float(0x430cb333), SkBits2Float(0x42bd396b)); // 139.3f, 94.6084f, 140, 94.6105f, 140.7f, 94.6121f +path.cubicTo(SkBits2Float(0x430d6666), SkBits2Float(0x42bd3a44), SkBits2Float(0x430e199a), SkBits2Float(0x42bd3bbb), SkBits2Float(0x430ecccd), SkBits2Float(0x42bd3c88)); // 141.4f, 94.6138f, 142.1f, 94.6167f, 142.8f, 94.6182f +path.cubicTo(SkBits2Float(0x430f8000), SkBits2Float(0x42bd3d56), SkBits2Float(0x43103333), SkBits2Float(0x42bd3cd1), SkBits2Float(0x4310e666), SkBits2Float(0x42bd3e3b)); // 143.5f, 94.6198f, 144.2f, 94.6188f, 144.9f, 94.6215f +path.cubicTo(SkBits2Float(0x4311999a), SkBits2Float(0x42bd3fa4), SkBits2Float(0x43124ccd), SkBits2Float(0x42bd4278), SkBits2Float(0x43130000), SkBits2Float(0x42bd4503)); // 145.6f, 94.6243f, 146.3f, 94.6298f, 147, 94.6348f +path.cubicTo(SkBits2Float(0x4313b333), SkBits2Float(0x42bd478e), SkBits2Float(0x43146666), SkBits2Float(0x42bd4af2), SkBits2Float(0x4315199a), SkBits2Float(0x42bd4d7e)); // 147.7f, 94.6398f, 148.4f, 94.6464f, 149.1f, 94.6514f +path.cubicTo(SkBits2Float(0x4315cccd), SkBits2Float(0x42bd5009), SkBits2Float(0x43168000), SkBits2Float(0x42bd53aa), SkBits2Float(0x43173333), SkBits2Float(0x42bd5446)); // 149.8f, 94.6563f, 150.5f, 94.6634f, 151.2f, 94.6646f +path.cubicTo(SkBits2Float(0x4317e666), SkBits2Float(0x42bd54e3), SkBits2Float(0x4318999a), SkBits2Float(0x42bd5202), SkBits2Float(0x43194ccd), SkBits2Float(0x42bd5128)); // 151.9f, 94.6658f, 152.6f, 94.6602f, 153.3f, 94.6585f +path.cubicTo(SkBits2Float(0x431a0000), SkBits2Float(0x42bd504f), SkBits2Float(0x431ab333), SkBits2Float(0x42bd4fcc), SkBits2Float(0x431b6666), SkBits2Float(0x42bd4f30)); // 154, 94.6569f, 154.7f, 94.6559f, 155.4f, 94.6547f +path.cubicTo(SkBits2Float(0x431c199a), SkBits2Float(0x42bd4e93), SkBits2Float(0x431ccccd), SkBits2Float(0x42bd4ee7), SkBits2Float(0x431d8000), SkBits2Float(0x42bd4d7e)); // 156.1f, 94.6535f, 156.8f, 94.6541f, 157.5f, 94.6514f +path.cubicTo(SkBits2Float(0x431e3333), SkBits2Float(0x42bd4c14), SkBits2Float(0x431ee666), SkBits2Float(0x42bd48f8), SkBits2Float(0x431f999a), SkBits2Float(0x42bd46b5)); // 158.2f, 94.6486f, 158.9f, 94.6425f, 159.6f, 94.6381f +path.cubicTo(SkBits2Float(0x43204ccd), SkBits2Float(0x42bd4472), SkBits2Float(0x43210000), SkBits2Float(0x42bd410e), SkBits2Float(0x4321b333), SkBits2Float(0x42bd3fed)); // 160.3f, 94.6337f, 161, 94.6271f, 161.7f, 94.6249f +path.cubicTo(SkBits2Float(0x43226666), SkBits2Float(0x42bd3ecb), SkBits2Float(0x4323199a), SkBits2Float(0x42bd401d), SkBits2Float(0x4323cccd), SkBits2Float(0x42bd3fed)); // 162.4f, 94.6226f, 163.1f, 94.6252f, 163.8f, 94.6249f +path.cubicTo(SkBits2Float(0x43248000), SkBits2Float(0x42bd3fbc), SkBits2Float(0x43253333), SkBits2Float(0x42bd40e7), SkBits2Float(0x4325e666), SkBits2Float(0x42bd3ec8)); // 164.5f, 94.6245f, 165.2f, 94.6268f, 165.9f, 94.6226f +path.cubicTo(SkBits2Float(0x4326999a), SkBits2Float(0x42bd3ca8), SkBits2Float(0x43274ccd), SkBits2Float(0x42bd383a), SkBits2Float(0x43280000), SkBits2Float(0x42bd332f)); // 166.6f, 94.6185f, 167.3f, 94.6098f, 168, 94.6f +path.cubicTo(SkBits2Float(0x4328b333), SkBits2Float(0x42bd2e25), SkBits2Float(0x43296666), SkBits2Float(0x42bd2647), SkBits2Float(0x432a199a), SkBits2Float(0x42bd2088)); // 168.7f, 94.5901f, 169.4f, 94.5748f, 170.1f, 94.5635f +path.cubicTo(SkBits2Float(0x432acccd), SkBits2Float(0x42bd1aca), SkBits2Float(0x432b8000), SkBits2Float(0x42bd147c), SkBits2Float(0x432c3333), SkBits2Float(0x42bd10b8)); // 170.8f, 94.5523f, 171.5f, 94.54f, 172.2f, 94.5327f +path.cubicTo(SkBits2Float(0x432ce666), SkBits2Float(0x42bd0cf4), SkBits2Float(0x432d999a), SkBits2Float(0x42bd0c32), SkBits2Float(0x432e4ccd), SkBits2Float(0x42bd09ef)); // 172.9f, 94.5253f, 173.6f, 94.5238f, 174.3f, 94.5194f +path.cubicTo(SkBits2Float(0x432f0000), SkBits2Float(0x42bd07ad), SkBits2Float(0x432fb333), SkBits2Float(0x42bd04cf), SkBits2Float(0x43306666), SkBits2Float(0x42bd0327)); // 175, 94.515f, 175.7f, 94.5094f, 176.4f, 94.5062f +path.lineTo(SkBits2Float(0x43306666), SkBits2Float(0x42bd0000)); // 176.4f, 94.5f +path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000)); // 88.2f, 94.5f +path.close(); +path.moveTo(SkBits2Float(0x43413333), SkBits2Float(0x42bd0000)); // 193.2f, 94.5f +path.lineTo(SkBits2Float(0x43413333), SkBits2Float(0x42bd0000)); // 193.2f, 94.5f +path.cubicTo(SkBits2Float(0x4341e666), SkBits2Float(0x42bd0000), SkBits2Float(0x4342999a), SkBits2Float(0x42bcfab7), SkBits2Float(0x43434ccd), SkBits2Float(0x42bd0000)); // 193.9f, 94.5f, 194.6f, 94.4897f, 195.3f, 94.5f +path.cubicTo(SkBits2Float(0x43440000), SkBits2Float(0x42bd0549), SkBits2Float(0x4344b333), SkBits2Float(0x42bd13e0), SkBits2Float(0x43456666), SkBits2Float(0x42bd1fb4)); // 196, 94.5103f, 196.7f, 94.5388f, 197.4f, 94.5619f +path.cubicTo(SkBits2Float(0x4346199a), SkBits2Float(0x42bd2b89), SkBits2Float(0x4346cccd), SkBits2Float(0x42bd3eb2), SkBits2Float(0x43478000), SkBits2Float(0x42bd46fc)); // 198.1f, 94.585f, 198.8f, 94.6225f, 199.5f, 94.6386f +path.cubicTo(SkBits2Float(0x43483333), SkBits2Float(0x42bd4f46), SkBits2Float(0x4348e666), SkBits2Float(0x42bd5127), SkBits2Float(0x4349999a), SkBits2Float(0x42bd516f)); // 200.2f, 94.6548f, 200.9f, 94.6585f, 201.6f, 94.659f +path.cubicTo(SkBits2Float(0x434a4ccd), SkBits2Float(0x42bd51b7), SkBits2Float(0x434b0000), SkBits2Float(0x42bd4dc4), SkBits2Float(0x434bb333), SkBits2Float(0x42bd48ae)); // 202.3f, 94.6596f, 203, 94.6519f, 203.7f, 94.642f +path.cubicTo(SkBits2Float(0x434c6666), SkBits2Float(0x42bd4397), SkBits2Float(0x434d199a), SkBits2Float(0x42bd39ee), SkBits2Float(0x434dcccd), SkBits2Float(0x42bd32e9)); // 204.4f, 94.632f, 205.1f, 94.6131f, 205.8f, 94.5994f +path.cubicTo(SkBits2Float(0x434e8000), SkBits2Float(0x42bd2be4), SkBits2Float(0x434f3333), SkBits2Float(0x42bd23a6), SkBits2Float(0x434fe666), SkBits2Float(0x42bd1e8f)); // 206.5f, 94.5857f, 207.2f, 94.5696f, 207.9f, 94.5597f +path.cubicTo(SkBits2Float(0x4350999a), SkBits2Float(0x42bd1979), SkBits2Float(0x43514ccd), SkBits2Float(0x42bd14f3), SkBits2Float(0x43520000), SkBits2Float(0x42bd1463)); // 208.6f, 94.5498f, 209.3f, 94.5409f, 210, 94.5398f +path.cubicTo(SkBits2Float(0x4352b333), SkBits2Float(0x42bd13d2), SkBits2Float(0x43536666), SkBits2Float(0x42bd1590), SkBits2Float(0x4354199a), SkBits2Float(0x42bd1b2b)); // 210.7f, 94.5387f, 211.4f, 94.5421f, 212.1f, 94.5531f +path.cubicTo(SkBits2Float(0x4354cccd), SkBits2Float(0x42bd20c7), SkBits2Float(0x43558000), SkBits2Float(0x42bd2bce), SkBits2Float(0x43563333), SkBits2Float(0x42bd3607)); // 212.8f, 94.564f, 213.5f, 94.5856f, 214.2f, 94.6055f +path.cubicTo(SkBits2Float(0x4356e666), SkBits2Float(0x42bd403f), SkBits2Float(0x4357999a), SkBits2Float(0x42bd4644), SkBits2Float(0x43584ccd), SkBits2Float(0x42bd587e)); // 214.9f, 94.6255f, 215.6f, 94.6372f, 216.3f, 94.6728f +path.cubicTo(SkBits2Float(0x43590000), SkBits2Float(0x42bd6ab8), SkBits2Float(0x4359b333), SkBits2Float(0x42bd8902), SkBits2Float(0x435a6666), SkBits2Float(0x42bda362)); // 217, 94.7084f, 217.7f, 94.7676f, 218.4f, 94.8191f +path.cubicTo(SkBits2Float(0x435b199a), SkBits2Float(0x42bdbdc2), SkBits2Float(0x435bcccd), SkBits2Float(0x42bde650), SkBits2Float(0x435c8000), SkBits2Float(0x42bdf6c0)); // 219.1f, 94.8706f, 219.8f, 94.9498f, 220.5f, 94.9819f +path.cubicTo(SkBits2Float(0x435d3333), SkBits2Float(0x42be0731), SkBits2Float(0x435de666), SkBits2Float(0x42be0409), SkBits2Float(0x435e999a), SkBits2Float(0x42be0604)); // 221.2f, 95.014f, 221.9f, 95.0079f, 222.6f, 95.0117f +path.cubicTo(SkBits2Float(0x435f4ccd), SkBits2Float(0x42be07fe), SkBits2Float(0x43600000), SkBits2Float(0x42be052a), SkBits2Float(0x4360b333), SkBits2Float(0x42be029f)); // 223.3f, 95.0156f, 224, 95.0101f, 224.7f, 95.0051f +path.cubicTo(SkBits2Float(0x43616666), SkBits2Float(0x42be0014), SkBits2Float(0x4362199a), SkBits2Float(0x42bdf903), SkBits2Float(0x4362cccd), SkBits2Float(0x42bdf6c0)); // 225.4f, 95.0002f, 226.1f, 94.9864f, 226.8f, 94.9819f +path.cubicTo(SkBits2Float(0x43638000), SkBits2Float(0x42bdf47e), SkBits2Float(0x43643333), SkBits2Float(0x42bdf3ed), SkBits2Float(0x4364e666), SkBits2Float(0x42bdf50e)); // 227.5f, 94.9775f, 228.2f, 94.9764f, 228.9f, 94.9786f +path.cubicTo(SkBits2Float(0x4365999a), SkBits2Float(0x42bdf630), SkBits2Float(0x43664ccd), SkBits2Float(0x42bdfe62), SkBits2Float(0x43670000), SkBits2Float(0x42bdfd89)); // 229.6f, 94.9808f, 230.3f, 94.9968f, 231, 94.9952f +path.cubicTo(SkBits2Float(0x4367b333), SkBits2Float(0x42bdfcb0), SkBits2Float(0x43686666), SkBits2Float(0x42bdf4c6), SkBits2Float(0x4369199a), SkBits2Float(0x42bdeff8)); // 231.7f, 94.9935f, 232.4f, 94.9781f, 233.1f, 94.9687f +path.cubicTo(SkBits2Float(0x4369cccd), SkBits2Float(0x42bdeb2a), SkBits2Float(0x436a8000), SkBits2Float(0x42bde2f8), SkBits2Float(0x436b3333), SkBits2Float(0x42bde0b5)); // 233.8f, 94.9593f, 234.5f, 94.9433f, 235.2f, 94.9389f +path.cubicTo(SkBits2Float(0x436be666), SkBits2Float(0x42bdde72), SkBits2Float(0x436c999a), SkBits2Float(0x42bde1d6), SkBits2Float(0x436d4ccd), SkBits2Float(0x42bde267)); // 235.9f, 94.9345f, 236.6f, 94.9411f, 237.3f, 94.9422f +path.cubicTo(SkBits2Float(0x436e0000), SkBits2Float(0x42bde2f8), SkBits2Float(0x436eb333), SkBits2Float(0x42bde340), SkBits2Float(0x436f6666), SkBits2Float(0x42bde419)); // 238, 94.9433f, 238.7f, 94.9438f, 239.4f, 94.9455f +path.cubicTo(SkBits2Float(0x4370199a), SkBits2Float(0x42bde4f2), SkBits2Float(0x4370cccd), SkBits2Float(0x42bde6a4), SkBits2Float(0x43718000), SkBits2Float(0x42bde77d)); // 240.1f, 94.9472f, 240.8f, 94.9505f, 241.5f, 94.9521f +path.cubicTo(SkBits2Float(0x43723333), SkBits2Float(0x42bde856), SkBits2Float(0x4372e666), SkBits2Float(0x42bde8e7), SkBits2Float(0x4373999a), SkBits2Float(0x42bde930)); // 242.2f, 94.9538f, 242.9f, 94.9549f, 243.6f, 94.9554f +path.cubicTo(SkBits2Float(0x43744ccd), SkBits2Float(0x42bde978), SkBits2Float(0x43750000), SkBits2Float(0x42bde8e7), SkBits2Float(0x4375b333), SkBits2Float(0x42bde930)); // 244.3f, 94.956f, 245, 94.9549f, 245.7f, 94.9554f +path.cubicTo(SkBits2Float(0x43766666), SkBits2Float(0x42bde978), SkBits2Float(0x4377199a), SkBits2Float(0x42bdea99), SkBits2Float(0x4377cccd), SkBits2Float(0x42bdeae2)); // 246.4f, 94.956f, 247.1f, 94.9582f, 247.8f, 94.9588f +path.cubicTo(SkBits2Float(0x43788000), SkBits2Float(0x42bdeb2a), SkBits2Float(0x43793333), SkBits2Float(0x42bdeb72), SkBits2Float(0x4379e666), SkBits2Float(0x42bdeae2)); // 248.5f, 94.9593f, 249.2f, 94.9599f, 249.9f, 94.9588f +path.cubicTo(SkBits2Float(0x437a999a), SkBits2Float(0x42bdea51), SkBits2Float(0x437b4ccd), SkBits2Float(0x42bde89f), SkBits2Float(0x437c0000), SkBits2Float(0x42bde77d)); // 250.6f, 94.9576f, 251.3f, 94.9543f, 252, 94.9521f +path.cubicTo(SkBits2Float(0x437cb333), SkBits2Float(0x42bde65c), SkBits2Float(0x437d6666), SkBits2Float(0x42bde4f2), SkBits2Float(0x437e199a), SkBits2Float(0x42bde419)); // 252.7f, 94.9499f, 253.4f, 94.9472f, 254.1f, 94.9455f +path.cubicTo(SkBits2Float(0x437ecccd), SkBits2Float(0x42bde340), SkBits2Float(0x437f8000), SkBits2Float(0x42bde267), SkBits2Float(0x4380199a), SkBits2Float(0x42bde267)); // 254.8f, 94.9438f, 255.5f, 94.9422f, 256.2f, 94.9422f +path.cubicTo(SkBits2Float(0x43807333), SkBits2Float(0x42bde267), SkBits2Float(0x4380cccd), SkBits2Float(0x42bde2f8), SkBits2Float(0x43812666), SkBits2Float(0x42bde419)); // 256.9f, 94.9422f, 257.6f, 94.9433f, 258.3f, 94.9455f +path.cubicTo(SkBits2Float(0x43818000), SkBits2Float(0x42bde53b), SkBits2Float(0x4381d99a), SkBits2Float(0x42bde7c6), SkBits2Float(0x43823333), SkBits2Float(0x42bde930)); // 259, 94.9477f, 259.7f, 94.9527f, 260.4f, 94.9554f +path.cubicTo(SkBits2Float(0x43828ccd), SkBits2Float(0x42bdea99), SkBits2Float(0x4382e666), SkBits2Float(0x42bdec03), SkBits2Float(0x43834000), SkBits2Float(0x42bdec94)); // 261.1f, 94.9582f, 261.8f, 94.961f, 262.5f, 94.9621f +path.cubicTo(SkBits2Float(0x4383999a), SkBits2Float(0x42bded24), SkBits2Float(0x4383f333), SkBits2Float(0x42bdecdc), SkBits2Float(0x43844ccd), SkBits2Float(0x42bdec94)); // 263.2f, 94.9632f, 263.9f, 94.9626f, 264.6f, 94.9621f +path.cubicTo(SkBits2Float(0x4384a666), SkBits2Float(0x42bdec4b), SkBits2Float(0x43850000), SkBits2Float(0x42bdeb72), SkBits2Float(0x4385599a), SkBits2Float(0x42bdeae2)); // 265.3f, 94.9615f, 266, 94.9599f, 266.7f, 94.9588f +path.cubicTo(SkBits2Float(0x4385b333), SkBits2Float(0x42bdea51), SkBits2Float(0x43860ccd), SkBits2Float(0x42bde978), SkBits2Float(0x43866666), SkBits2Float(0x42bde930)); // 267.4f, 94.9576f, 268.1f, 94.956f, 268.8f, 94.9554f +path.cubicTo(SkBits2Float(0x4386c000), SkBits2Float(0x42bde8e7), SkBits2Float(0x4387199a), SkBits2Float(0x42bde8e7), SkBits2Float(0x43877333), SkBits2Float(0x42bde930)); // 269.5f, 94.9549f, 270.2f, 94.9549f, 270.9f, 94.9554f +path.cubicTo(SkBits2Float(0x4387cccd), SkBits2Float(0x42bde978), SkBits2Float(0x43882666), SkBits2Float(0x42bdea99), SkBits2Float(0x43888000), SkBits2Float(0x42bdeae2)); // 271.6f, 94.956f, 272.3f, 94.9582f, 273, 94.9588f +path.cubicTo(SkBits2Float(0x4388d99a), SkBits2Float(0x42bdeb2a), SkBits2Float(0x43893333), SkBits2Float(0x42bdeae2), SkBits2Float(0x43898ccd), SkBits2Float(0x42bdeae2)); // 273.7f, 94.9593f, 274.4f, 94.9588f, 275.1f, 94.9588f +path.cubicTo(SkBits2Float(0x4389e666), SkBits2Float(0x42bdeae2), SkBits2Float(0x438a4000), SkBits2Float(0x42bdea99), SkBits2Float(0x438a999a), SkBits2Float(0x42bdeae2)); // 275.8f, 94.9588f, 276.5f, 94.9582f, 277.2f, 94.9588f +path.cubicTo(SkBits2Float(0x438af333), SkBits2Float(0x42bdeb2a), SkBits2Float(0x438b4ccd), SkBits2Float(0x42bdec94), SkBits2Float(0x438ba666), SkBits2Float(0x42bdec94)); // 277.9f, 94.9593f, 278.6f, 94.9621f, 279.3f, 94.9621f +path.cubicTo(SkBits2Float(0x438c0000), SkBits2Float(0x42bdec94), SkBits2Float(0x438c599a), SkBits2Float(0x42bdead6), SkBits2Float(0x438cb333), SkBits2Float(0x42bdeae2)); // 280, 94.9621f, 280.7f, 94.9587f, 281.4f, 94.9588f +path.cubicTo(SkBits2Float(0x438d0ccd), SkBits2Float(0x42bdeaed), SkBits2Float(0x438d6666), SkBits2Float(0x42bdeb11), SkBits2Float(0x438dc000), SkBits2Float(0x42bdecda)); // 282.1f, 94.9588f, 282.8f, 94.9591f, 283.5f, 94.9626f +path.cubicTo(SkBits2Float(0x438e199a), SkBits2Float(0x42bdeea4), SkBits2Float(0x438e7333), SkBits2Float(0x42bdf3e9), SkBits2Float(0x438ecccd), SkBits2Float(0x42bdf59c)); // 284.2f, 94.9661f, 284.9f, 94.9764f, 285.6f, 94.9797f +path.cubicTo(SkBits2Float(0x438f2666), SkBits2Float(0x42bdf74e), SkBits2Float(0x438f8000), SkBits2Float(0x42bdfbed), SkBits2Float(0x438fd99a), SkBits2Float(0x42bdf707)); // 286.3f, 94.983f, 287, 94.992f, 287.7f, 94.9825f +path.cubicTo(SkBits2Float(0x43903333), SkBits2Float(0x42bdf222), SkBits2Float(0x43908ccd), SkBits2Float(0x42bdeb8a), SkBits2Float(0x4390e666), SkBits2Float(0x42bdd83a)); // 288.4f, 94.9729f, 289.1f, 94.96f, 289.8f, 94.9223f +path.cubicTo(SkBits2Float(0x43914000), SkBits2Float(0x42bdc4eb), SkBits2Float(0x4391999a), SkBits2Float(0x42bda6ea), SkBits2Float(0x4391f333), SkBits2Float(0x42bd832a)); // 290.5f, 94.8846f, 291.2f, 94.826f, 291.9f, 94.7562f +path.cubicTo(SkBits2Float(0x43924ccd), SkBits2Float(0x42bd5f6a), SkBits2Float(0x4392a666), SkBits2Float(0x42bd1798), SkBits2Float(0x43930000), SkBits2Float(0x42bd01bb)); // 292.6f, 94.6864f, 293.3f, 94.5461f, 294, 94.5034f +path.cubicTo(SkBits2Float(0x4393599a), SkBits2Float(0x42bcebdf), SkBits2Float(0x4393b333), SkBits2Float(0x42bd004a), SkBits2Float(0x43940ccd), SkBits2Float(0x42bd0000)); // 294.7f, 94.4607f, 295.4f, 94.5006f, 296.1f, 94.5f +path.lineTo(SkBits2Float(0x43940ccd), SkBits2Float(0x42bd0000)); // 296.1f, 94.5f +path.lineTo(SkBits2Float(0x43413333), SkBits2Float(0x42bd0000)); // 193.2f, 94.5f +path.close(); +path.moveTo(SkBits2Float(0x43ac3333), SkBits2Float(0x42bd0000)); // 344.4f, 94.5f +path.lineTo(SkBits2Float(0x43ac3333), SkBits2Float(0x42bd0000)); // 344.4f, 94.5f +path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42bd0000), SkBits2Float(0x43ace666), SkBits2Float(0x42bcfc5e), SkBits2Float(0x43ad4000), SkBits2Float(0x42bd0000)); // 345.1f, 94.5f, 345.8f, 94.4929f, 346.5f, 94.5f +path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42bd03a2), SkBits2Float(0x43adf333), SkBits2Float(0x42bcf966), SkBits2Float(0x43ae4ccd), SkBits2Float(0x42bd15ce)); // 347.2f, 94.5071f, 347.9f, 94.4871f, 348.6f, 94.5426f +path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x42bd3236), SkBits2Float(0x43af0000), SkBits2Float(0x42bd84e8), SkBits2Float(0x43af599a), SkBits2Float(0x42bdaa71)); // 349.3f, 94.5981f, 350, 94.7596f, 350.7f, 94.8329f +path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x42bdcffb), SkBits2Float(0x43b00ccd), SkBits2Float(0x42bde92e), SkBits2Float(0x43b06666), SkBits2Float(0x42bdf707)); // 351.4f, 94.9062f, 352.1f, 94.9554f, 352.8f, 94.9825f +path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x42be04e0), SkBits2Float(0x43b1199a), SkBits2Float(0x42bdfc2b), SkBits2Float(0x43b17333), SkBits2Float(0x42bdfd89)); // 353.5f, 95.0095f, 354.2f, 94.9925f, 354.9f, 94.9952f +path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42bdfee7), SkBits2Float(0x43b22666), SkBits2Float(0x42bdfe62), SkBits2Float(0x43b28000), SkBits2Float(0x42bdff3b)); // 355.6f, 94.9979f, 356.3f, 94.9968f, 357, 94.9985f +path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x42be0014), SkBits2Float(0x43b33333), SkBits2Float(0x42be020f), SkBits2Float(0x43b38ccd), SkBits2Float(0x42be029f)); // 357.7f, 95.0002f, 358.4f, 95.004f, 359.1f, 95.0051f +path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x42be0330), SkBits2Float(0x43b44000), SkBits2Float(0x42be020f), SkBits2Float(0x43b4999a), SkBits2Float(0x42be029f)); // 359.8f, 95.0062f, 360.5f, 95.004f, 361.2f, 95.0051f +path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x42be0330), SkBits2Float(0x43b54ccd), SkBits2Float(0x42be052a), SkBits2Float(0x43b5a666), SkBits2Float(0x42be0604)); // 361.9f, 95.0062f, 362.6f, 95.0101f, 363.3f, 95.0117f +path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x42be06dd), SkBits2Float(0x43b6599a), SkBits2Float(0x42be0725), SkBits2Float(0x43b6b333), SkBits2Float(0x42be07b6)); // 364, 95.0134f, 364.7f, 95.014f, 365.4f, 95.0151f +path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x42be0846), SkBits2Float(0x43b76666), SkBits2Float(0x42be08d7), SkBits2Float(0x43b7c000), SkBits2Float(0x42be0968)); // 366.1f, 95.0162f, 366.8f, 95.0173f, 367.5f, 95.0184f +path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x42be09f8), SkBits2Float(0x43b87333), SkBits2Float(0x42be0ad2), SkBits2Float(0x43b8cccd), SkBits2Float(0x42be0b1a)); // 368.2f, 95.0195f, 368.9f, 95.0211f, 369.6f, 95.0217f +path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x42be0b62), SkBits2Float(0x43b98000), SkBits2Float(0x42be0bab), SkBits2Float(0x43b9d99a), SkBits2Float(0x42be0b1a)); // 370.3f, 95.0222f, 371, 95.0228f, 371.7f, 95.0217f +path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x42be0a89), SkBits2Float(0x43ba8ccd), SkBits2Float(0x42be088f), SkBits2Float(0x43bae666), SkBits2Float(0x42be07b6)); // 372.4f, 95.0206f, 373.1f, 95.0167f, 373.8f, 95.0151f +path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x42be06dd), SkBits2Float(0x43bb999a), SkBits2Float(0x42be064c), SkBits2Float(0x43bbf333), SkBits2Float(0x42be0604)); // 374.5f, 95.0134f, 375.2f, 95.0123f, 375.9f, 95.0117f +path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x42be05bb), SkBits2Float(0x43bca666), SkBits2Float(0x42be0604), SkBits2Float(0x43bd0000), SkBits2Float(0x42be0604)); // 376.6f, 95.0112f, 377.3f, 95.0117f, 378, 95.0117f +path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x42be0604), SkBits2Float(0x43bdb333), SkBits2Float(0x42be0604), SkBits2Float(0x43be0ccd), SkBits2Float(0x42be0604)); // 378.7f, 95.0117f, 379.4f, 95.0117f, 380.1f, 95.0117f +path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x42be0604), SkBits2Float(0x43bec000), SkBits2Float(0x42be0725), SkBits2Float(0x43bf199a), SkBits2Float(0x42be0604)); // 380.8f, 95.0117f, 381.5f, 95.014f, 382.2f, 95.0117f +path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x42be04e2), SkBits2Float(0x43bfcccd), SkBits2Float(0x42be0136), SkBits2Float(0x43c02666), SkBits2Float(0x42bdff3b)); // 382.9f, 95.0095f, 383.6f, 95.0024f, 384.3f, 94.9985f +path.cubicTo(SkBits2Float(0x43c08000), SkBits2Float(0x42bdfd41), SkBits2Float(0x43c0d99a), SkBits2Float(0x42bdfa6d), SkBits2Float(0x43c13333), SkBits2Float(0x42bdfa25)); // 385, 94.9946f, 385.7f, 94.9891f, 386.4f, 94.9886f +path.cubicTo(SkBits2Float(0x43c18ccd), SkBits2Float(0x42bdf9dc), SkBits2Float(0x43c1e666), SkBits2Float(0x42bdfcf8), SkBits2Float(0x43c24000), SkBits2Float(0x42bdfd89)); // 387.1f, 94.988f, 387.8f, 94.9941f, 388.5f, 94.9952f +path.cubicTo(SkBits2Float(0x43c2999a), SkBits2Float(0x42bdfe1a), SkBits2Float(0x43c2f333), SkBits2Float(0x42bdfdd1), SkBits2Float(0x43c34ccd), SkBits2Float(0x42bdfd89)); // 389.2f, 94.9963f, 389.9f, 94.9957f, 390.6f, 94.9952f +path.cubicTo(SkBits2Float(0x43c3a666), SkBits2Float(0x42bdfd41), SkBits2Float(0x43c40000), SkBits2Float(0x42bdfd41), SkBits2Float(0x43c4599a), SkBits2Float(0x42bdfbd7)); // 391.3f, 94.9946f, 392, 94.9946f, 392.7f, 94.9919f +path.cubicTo(SkBits2Float(0x43c4b333), SkBits2Float(0x42bdfa6d), SkBits2Float(0x43c50ccd), SkBits2Float(0x42bdf709), SkBits2Float(0x43c56666), SkBits2Float(0x42bdf50e)); // 393.4f, 94.9891f, 394.1f, 94.9825f, 394.8f, 94.9786f +path.cubicTo(SkBits2Float(0x43c5c000), SkBits2Float(0x42bdf314), SkBits2Float(0x43c6199a), SkBits2Float(0x42bdf0d1), SkBits2Float(0x43c67333), SkBits2Float(0x42bdeff8)); // 395.5f, 94.9748f, 396.2f, 94.9703f, 396.9f, 94.9687f +path.cubicTo(SkBits2Float(0x43c6cccd), SkBits2Float(0x42bdef1f), SkBits2Float(0x43c72666), SkBits2Float(0x42bdf040), SkBits2Float(0x43c78000), SkBits2Float(0x42bdeff8)); // 397.6f, 94.967f, 398.3f, 94.9692f, 399, 94.9687f +path.cubicTo(SkBits2Float(0x43c7d99a), SkBits2Float(0x42bdefb0), SkBits2Float(0x43c83333), SkBits2Float(0x42bdee8e), SkBits2Float(0x43c88ccd), SkBits2Float(0x42bdee46)); // 399.7f, 94.9681f, 400.4f, 94.9659f, 401.1f, 94.9654f +path.cubicTo(SkBits2Float(0x43c8e666), SkBits2Float(0x42bdedfe), SkBits2Float(0x43c94000), SkBits2Float(0x42bdee46), SkBits2Float(0x43c9999a), SkBits2Float(0x42bdee46)); // 401.8f, 94.9648f, 402.5f, 94.9654f, 403.2f, 94.9654f +path.cubicTo(SkBits2Float(0x43c9f333), SkBits2Float(0x42bdee46), SkBits2Float(0x43ca4ccd), SkBits2Float(0x42bdedb5), SkBits2Float(0x43caa666), SkBits2Float(0x42bdee46)); // 403.9f, 94.9654f, 404.6f, 94.9643f, 405.3f, 94.9654f +path.cubicTo(SkBits2Float(0x43cb0000), SkBits2Float(0x42bdeed7), SkBits2Float(0x43cb599a), SkBits2Float(0x42bdf089), SkBits2Float(0x43cbb333), SkBits2Float(0x42bdf1aa)); // 406, 94.9665f, 406.7f, 94.9698f, 407.4f, 94.972f +path.cubicTo(SkBits2Float(0x43cc0ccd), SkBits2Float(0x42bdf2cc), SkBits2Float(0x43cc6666), SkBits2Float(0x42bdf47e), SkBits2Float(0x43ccc000), SkBits2Float(0x42bdf50e)); // 408.1f, 94.9742f, 408.8f, 94.9775f, 409.5f, 94.9786f +path.cubicTo(SkBits2Float(0x43cd199a), SkBits2Float(0x42bdf59f), SkBits2Float(0x43cd7333), SkBits2Float(0x42bdf4c6), SkBits2Float(0x43cdcccd), SkBits2Float(0x42bdf50e)); // 410.2f, 94.9797f, 410.9f, 94.9781f, 411.6f, 94.9786f +path.cubicTo(SkBits2Float(0x43ce2666), SkBits2Float(0x42bdf557), SkBits2Float(0x43ce8000), SkBits2Float(0x42bdf557), SkBits2Float(0x43ced99a), SkBits2Float(0x42bdf6c0)); // 412.3f, 94.9792f, 413, 94.9792f, 413.7f, 94.9819f +path.cubicTo(SkBits2Float(0x43cf3333), SkBits2Float(0x42bdf82a), SkBits2Float(0x43cf8ccd), SkBits2Float(0x42bdfafe), SkBits2Float(0x43cfe666), SkBits2Float(0x42bdfd89)); // 414.4f, 94.9847f, 415.1f, 94.9902f, 415.8f, 94.9952f +path.cubicTo(SkBits2Float(0x43d04000), SkBits2Float(0x42be0014), SkBits2Float(0x43d0999a), SkBits2Float(0x42be0378), SkBits2Float(0x43d0f333), SkBits2Float(0x42be0604)); // 416.5f, 95.0002f, 417.2f, 95.0068f, 417.9f, 95.0117f +path.cubicTo(SkBits2Float(0x43d14ccd), SkBits2Float(0x42be088f), SkBits2Float(0x43d1a666), SkBits2Float(0x42be0b1a), SkBits2Float(0x43d20000), SkBits2Float(0x42be0ccc)); // 418.6f, 95.0167f, 419.3f, 95.0217f, 420, 95.025f +path.cubicTo(SkBits2Float(0x43d2599a), SkBits2Float(0x42be0e7e), SkBits2Float(0x43d2b333), SkBits2Float(0x42be119a), SkBits2Float(0x43d30ccd), SkBits2Float(0x42be1030)); // 420.7f, 95.0283f, 421.4f, 95.0344f, 422.1f, 95.0316f +path.cubicTo(SkBits2Float(0x43d36666), SkBits2Float(0x42be0ec6), SkBits2Float(0x43d3c000), SkBits2Float(0x42be0ad2), SkBits2Float(0x43d4199a), SkBits2Float(0x42be0451)); // 422.8f, 95.0289f, 423.5f, 95.0211f, 424.2f, 95.0084f +path.cubicTo(SkBits2Float(0x43d47333), SkBits2Float(0x42bdfdd1), SkBits2Float(0x43d4cccd), SkBits2Float(0x42bdefec), SkBits2Float(0x43d52666), SkBits2Float(0x42bde930)); // 424.9f, 94.9957f, 425.6f, 94.9686f, 426.3f, 94.9554f +path.cubicTo(SkBits2Float(0x43d58000), SkBits2Float(0x42bde273), SkBits2Float(0x43d5d99a), SkBits2Float(0x42bde21d), SkBits2Float(0x43d63333), SkBits2Float(0x42bddbe5)); // 427, 94.9423f, 427.7f, 94.9416f, 428.4f, 94.9295f +path.cubicTo(SkBits2Float(0x43d68ccd), SkBits2Float(0x42bdd5ad), SkBits2Float(0x43d6e666), SkBits2Float(0x42bdcf3b), SkBits2Float(0x43d74000), SkBits2Float(0x42bdc3e1)); // 429.1f, 94.9173f, 429.8f, 94.9047f, 430.5f, 94.8826f +path.cubicTo(SkBits2Float(0x43d7999a), SkBits2Float(0x42bdb887), SkBits2Float(0x43d7f333), SkBits2Float(0x42bd9d7d), SkBits2Float(0x43d84ccd), SkBits2Float(0x42bd97ca)); // 431.2f, 94.8604f, 431.9f, 94.8076f, 432.6f, 94.7965f +path.cubicTo(SkBits2Float(0x43d8a666), SkBits2Float(0x42bd9217), SkBits2Float(0x43d90000), SkBits2Float(0x42bd9820), SkBits2Float(0x43d9599a), SkBits2Float(0x42bda1b0)); // 433.3f, 94.7853f, 434, 94.7971f, 434.7f, 94.8158f +path.cubicTo(SkBits2Float(0x43d9b333), SkBits2Float(0x42bdab40), SkBits2Float(0x43da0ccd), SkBits2Float(0x42bdc5dd), SkBits2Float(0x43da6666), SkBits2Float(0x42bdd12b)); // 435.4f, 94.8345f, 436.1f, 94.8865f, 436.8f, 94.9085f +path.cubicTo(SkBits2Float(0x43dac000), SkBits2Float(0x42bddc79), SkBits2Float(0x43db199a), SkBits2Float(0x42bde1cc), SkBits2Float(0x43db7333), SkBits2Float(0x42bde585)); // 437.5f, 94.9306f, 438.2f, 94.941f, 438.9f, 94.9483f +path.cubicTo(SkBits2Float(0x43dbcccd), SkBits2Float(0x42bde93d), SkBits2Float(0x43dc2666), SkBits2Float(0x42bde75a), SkBits2Float(0x43dc8000), SkBits2Float(0x42bde77d)); // 439.6f, 94.9555f, 440.3f, 94.9519f, 441, 94.9521f +path.cubicTo(SkBits2Float(0x43dcd99a), SkBits2Float(0x42bde7a1), SkBits2Float(0x43dd3333), SkBits2Float(0x42bde610), SkBits2Float(0x43dd8ccd), SkBits2Float(0x42bde658)); // 441.7f, 94.9524f, 442.4f, 94.9493f, 443.1f, 94.9499f +path.cubicTo(SkBits2Float(0x43dde666), SkBits2Float(0x42bde6a1), SkBits2Float(0x43de4000), SkBits2Float(0x42bdea2c), SkBits2Float(0x43de999a), SkBits2Float(0x42bde930)); // 443.8f, 94.9504f, 444.5f, 94.9574f, 445.2f, 94.9554f +path.cubicTo(SkBits2Float(0x43def333), SkBits2Float(0x42bde833), SkBits2Float(0x43df4ccd), SkBits2Float(0x42bdee5f), SkBits2Float(0x43dfa666), SkBits2Float(0x42bde06e)); // 445.9f, 94.9535f, 446.6f, 94.9656f, 447.3f, 94.9383f +path.cubicTo(SkBits2Float(0x43e00000), SkBits2Float(0x42bdd27d), SkBits2Float(0x43e0599a), SkBits2Float(0x42bdb871), SkBits2Float(0x43e0b333), SkBits2Float(0x42bd958a)); // 448, 94.9111f, 448.7f, 94.8602f, 449.4f, 94.7921f +path.cubicTo(SkBits2Float(0x43e10ccd), SkBits2Float(0x42bd72a4), SkBits2Float(0x43e16666), SkBits2Float(0x42bd27f2), SkBits2Float(0x43e1c000), SkBits2Float(0x42bd0f06)); // 450.1f, 94.7239f, 450.8f, 94.578f, 451.5f, 94.5293f +path.cubicTo(SkBits2Float(0x43e2199a), SkBits2Float(0x42bcf619), SkBits2Float(0x43e27333), SkBits2Float(0x42bcfbb7), SkBits2Float(0x43e2cccd), SkBits2Float(0x42bd0000)); // 452.2f, 94.4807f, 452.9f, 94.4916f, 453.6f, 94.5f +path.cubicTo(SkBits2Float(0x43e32666), SkBits2Float(0x42bd0449), SkBits2Float(0x43e38000), SkBits2Float(0x42bd1419), SkBits2Float(0x43e3d99a), SkBits2Float(0x42bd28bc)); // 454.3f, 94.5084f, 455, 94.5393f, 455.7f, 94.5796f +path.cubicTo(SkBits2Float(0x43e43333), SkBits2Float(0x42bd3d60), SkBits2Float(0x43e48ccd), SkBits2Float(0x42bd7238), SkBits2Float(0x43e4e666), SkBits2Float(0x42bd7bd4)); // 456.4f, 94.6199f, 457.1f, 94.7231f, 457.8f, 94.7419f +path.cubicTo(SkBits2Float(0x43e54000), SkBits2Float(0x42bd8570), SkBits2Float(0x43e5999a), SkBits2Float(0x42bd70ce), SkBits2Float(0x43e5f333), SkBits2Float(0x42bd6264)); // 458.5f, 94.7606f, 459.2f, 94.7203f, 459.9f, 94.6922f +path.cubicTo(SkBits2Float(0x43e64ccd), SkBits2Float(0x42bd53fa), SkBits2Float(0x43e6a666), SkBits2Float(0x42bd35be), SkBits2Float(0x43e70000), SkBits2Float(0x42bd2558)); // 460.6f, 94.664f, 461.3f, 94.605f, 462, 94.5729f +path.cubicTo(SkBits2Float(0x43e7599a), SkBits2Float(0x42bd14f2), SkBits2Float(0x43e7b333), SkBits2Float(0x42bd0639), SkBits2Float(0x43e80ccd), SkBits2Float(0x42bd0000)); // 462.7f, 94.5409f, 463.4f, 94.5122f, 464.1f, 94.5f +path.cubicTo(SkBits2Float(0x43e86666), SkBits2Float(0x42bcf9c7), SkBits2Float(0x43e8c000), SkBits2Float(0x42bcfef5), SkBits2Float(0x43e9199a), SkBits2Float(0x42bd0000)); // 464.8f, 94.4878f, 465.5f, 94.498f, 466.2f, 94.5f +path.cubicTo(SkBits2Float(0x43e97333), SkBits2Float(0x42bd010b), SkBits2Float(0x43e9cccd), SkBits2Float(0x42bd0645), SkBits2Float(0x43ea2666), SkBits2Float(0x42bd0645)); // 466.9f, 94.502f, 467.6f, 94.5122f, 468.3f, 94.5122f +path.cubicTo(SkBits2Float(0x43ea8000), SkBits2Float(0x42bd0645), SkBits2Float(0x43ead99a), SkBits2Float(0x42bd010b), SkBits2Float(0x43eb3333), SkBits2Float(0x42bd0000)); // 469, 94.5122f, 469.7f, 94.502f, 470.4f, 94.5f +path.lineTo(SkBits2Float(0x43eb3333), SkBits2Float(0x42bd0000)); // 470.4f, 94.5f +path.lineTo(SkBits2Float(0x43ac3333), SkBits2Float(0x42bd0000)); // 344.4f, 94.5f +path.close(); + return path; +} + +static SkPath path5() { + SkPath path; +path.moveTo(SkBits2Float(0x42b06666), SkBits2Float(0x42c9999a)); // 88.2f, 100.8f +path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42c9999a)); // 88.2f, 100.8f +path.cubicTo(SkBits2Float(0x42b1cccd), SkBits2Float(0x42c9999a), SkBits2Float(0x42b33333), SkBits2Float(0x42c9b407), SkBits2Float(0x42b4999a), SkBits2Float(0x42c9999a)); // 88.9f, 100.8f, 89.6f, 100.852f, 90.3f, 100.8f +path.cubicTo(SkBits2Float(0x42b60000), SkBits2Float(0x42c97f2c), SkBits2Float(0x42b76666), SkBits2Float(0x42c91521), SkBits2Float(0x42b8cccd), SkBits2Float(0x42c8fb07)); // 91, 100.748f, 91.7f, 100.541f, 92.4f, 100.49f +path.cubicTo(SkBits2Float(0x42ba3333), SkBits2Float(0x42c8e0ee), SkBits2Float(0x42bb999a), SkBits2Float(0x42c8fd00), SkBits2Float(0x42bd0000), SkBits2Float(0x42c8fd00)); // 93.1f, 100.439f, 93.8f, 100.494f, 94.5f, 100.494f +path.cubicTo(SkBits2Float(0x42be6666), SkBits2Float(0x42c8fd00), SkBits2Float(0x42bfcccd), SkBits2Float(0x42c8facb), SkBits2Float(0x42c13333), SkBits2Float(0x42c8fb07)); // 95.2f, 100.494f, 95.9f, 100.49f, 96.6f, 100.49f +path.cubicTo(SkBits2Float(0x42c2999a), SkBits2Float(0x42c8fb44), SkBits2Float(0x42c40000), SkBits2Float(0x42c8fd4a), SkBits2Float(0x42c56666), SkBits2Float(0x42c8fe6c)); // 97.3f, 100.491f, 98, 100.495f, 98.7f, 100.497f +path.cubicTo(SkBits2Float(0x42c6cccd), SkBits2Float(0x42c8ff8d), SkBits2Float(0x42c83333), SkBits2Float(0x42c90218), SkBits2Float(0x42c9999a), SkBits2Float(0x42c901d0)); // 99.4f, 100.499f, 100.1f, 100.504f, 100.8f, 100.504f +path.cubicTo(SkBits2Float(0x42cb0000), SkBits2Float(0x42c90187), SkBits2Float(0x42cc6666), SkBits2Float(0x42c8ff39), SkBits2Float(0x42cdcccd), SkBits2Float(0x42c8fcb9)); // 101.5f, 100.503f, 102.2f, 100.498f, 102.9f, 100.494f +path.cubicTo(SkBits2Float(0x42cf3333), SkBits2Float(0x42c8fa3a), SkBits2Float(0x42d0999a), SkBits2Float(0x42c8f364), SkBits2Float(0x42d20000), SkBits2Float(0x42c8f2d3)); // 103.6f, 100.489f, 104.3f, 100.475f, 105, 100.474f +path.cubicTo(SkBits2Float(0x42d36666), SkBits2Float(0x42c8f243), SkBits2Float(0x42d4cccd), SkBits2Float(0x42c8f402), SkBits2Float(0x42d63333), SkBits2Float(0x42c8f955)); // 105.7f, 100.473f, 106.4f, 100.477f, 107.1f, 100.487f +path.cubicTo(SkBits2Float(0x42d7999a), SkBits2Float(0x42c8fea8), SkBits2Float(0x42d90000), SkBits2Float(0x42c90daf), SkBits2Float(0x42da6666), SkBits2Float(0x42c912c5)); // 107.8f, 100.497f, 108.5f, 100.527f, 109.2f, 100.537f +path.cubicTo(SkBits2Float(0x42dbcccd), SkBits2Float(0x42c917db), SkBits2Float(0x42dd3333), SkBits2Float(0x42c918f1), SkBits2Float(0x42de999a), SkBits2Float(0x42c917db)); // 109.9f, 100.547f, 110.6f, 100.549f, 111.3f, 100.547f +path.cubicTo(SkBits2Float(0x42e00000), SkBits2Float(0x42c916c6), SkBits2Float(0x42e16666), SkBits2Float(0x42c90ac2), SkBits2Float(0x42e2cccd), SkBits2Float(0x42c90c43)); // 112, 100.544f, 112.7f, 100.521f, 113.4f, 100.524f +path.cubicTo(SkBits2Float(0x42e43333), SkBits2Float(0x42c90dc4), SkBits2Float(0x42e5999a), SkBits2Float(0x42c91420), SkBits2Float(0x42e70000), SkBits2Float(0x42c920e3)); // 114.1f, 100.527f, 114.8f, 100.539f, 115.5f, 100.564f +path.cubicTo(SkBits2Float(0x42e86666), SkBits2Float(0x42c92da7), SkBits2Float(0x42e9cccd), SkBits2Float(0x42c946ab), SkBits2Float(0x42eb3333), SkBits2Float(0x42c958d9)); // 116.2f, 100.589f, 116.9f, 100.638f, 117.6f, 100.674f +path.cubicTo(SkBits2Float(0x42ec999a), SkBits2Float(0x42c96b07), SkBits2Float(0x42ee0000), SkBits2Float(0x42c9832d), SkBits2Float(0x42ef6666), SkBits2Float(0x42c98df8)); // 118.3f, 100.709f, 119, 100.756f, 119.7f, 100.777f +path.cubicTo(SkBits2Float(0x42f0cccd), SkBits2Float(0x42c998c3), SkBits2Float(0x42f23333), SkBits2Float(0x42c997a9), SkBits2Float(0x42f3999a), SkBits2Float(0x42c9999a)); // 120.4f, 100.798f, 121.1f, 100.796f, 121.8f, 100.8f +path.lineTo(SkBits2Float(0x42f3999a), SkBits2Float(0x42c9999a)); // 121.8f, 100.8f +path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42c9999a)); // 88.2f, 100.8f +path.close(); +path.moveTo(SkBits2Float(0x4300199a), SkBits2Float(0x42c9999a)); // 128.1f, 100.8f +path.lineTo(SkBits2Float(0x4300199a), SkBits2Float(0x42c99825)); // 128.1f, 100.797f +path.cubicTo(SkBits2Float(0x4300cccd), SkBits2Float(0x42c99756), SkBits2Float(0x43018000), SkBits2Float(0x42c99482), SkBits2Float(0x43023333), SkBits2Float(0x42c994c1)); // 128.8f, 100.796f, 129.5f, 100.79f, 130.2f, 100.791f +path.cubicTo(SkBits2Float(0x4302e666), SkBits2Float(0x42c994ff), SkBits2Float(0x4303999a), SkBits2Float(0x42c998cb), SkBits2Float(0x43044ccd), SkBits2Float(0x42c9999a)); // 130.9f, 100.791f, 131.6f, 100.798f, 132.3f, 100.8f +path.lineTo(SkBits2Float(0x43044ccd), SkBits2Float(0x42c9999a)); // 132.3f, 100.8f +path.lineTo(SkBits2Float(0x4300199a), SkBits2Float(0x42c9999a)); // 128.1f, 100.8f +path.close(); +path.moveTo(SkBits2Float(0x431b6666), SkBits2Float(0x42c9999a)); // 155.4f, 100.8f +path.lineTo(SkBits2Float(0x431b6666), SkBits2Float(0x42c9999a)); // 155.4f, 100.8f +path.cubicTo(SkBits2Float(0x431c199a), SkBits2Float(0x42c9999a), SkBits2Float(0x431ccccd), SkBits2Float(0x42c99dcd), SkBits2Float(0x431d8000), SkBits2Float(0x42c9999a)); // 156.1f, 100.8f, 156.8f, 100.808f, 157.5f, 100.8f +path.cubicTo(SkBits2Float(0x431e3333), SkBits2Float(0x42c99567), SkBits2Float(0x431ee666), SkBits2Float(0x42c98d21), SkBits2Float(0x431f999a), SkBits2Float(0x42c98067)); // 158.2f, 100.792f, 158.9f, 100.776f, 159.6f, 100.751f +path.cubicTo(SkBits2Float(0x43204ccd), SkBits2Float(0x42c973ae), SkBits2Float(0x43210000), SkBits2Float(0x42c95aa1), SkBits2Float(0x4321b333), SkBits2Float(0x42c94d41)); // 160.3f, 100.726f, 161, 100.677f, 161.7f, 100.651f +path.cubicTo(SkBits2Float(0x43226666), SkBits2Float(0x42c93fe1), SkBits2Float(0x4323199a), SkBits2Float(0x42c935e5), SkBits2Float(0x4323cccd), SkBits2Float(0x42c93026)); // 162.4f, 100.625f, 163.1f, 100.605f, 163.8f, 100.594f +path.cubicTo(SkBits2Float(0x43248000), SkBits2Float(0x42c92a68), SkBits2Float(0x43253333), SkBits2Float(0x42c92b66), SkBits2Float(0x4325e666), SkBits2Float(0x42c92ac9)); // 164.5f, 100.583f, 165.2f, 100.585f, 165.9f, 100.584f +path.cubicTo(SkBits2Float(0x4326999a), SkBits2Float(0x42c92a2d), SkBits2Float(0x43274ccd), SkBits2Float(0x42c92759), SkBits2Float(0x43280000), SkBits2Float(0x42c92c7b)); // 166.6f, 100.582f, 167.3f, 100.577f, 168, 100.587f +path.cubicTo(SkBits2Float(0x4328b333), SkBits2Float(0x42c9319e), SkBits2Float(0x43296666), SkBits2Float(0x42c93d57), SkBits2Float(0x432a199a), SkBits2Float(0x42c94996)); // 168.7f, 100.597f, 169.4f, 100.62f, 170.1f, 100.644f +path.cubicTo(SkBits2Float(0x432acccd), SkBits2Float(0x42c955d5), SkBits2Float(0x432b8000), SkBits2Float(0x42c968e8), SkBits2Float(0x432c3333), SkBits2Float(0x42c975f4)); // 170.8f, 100.668f, 171.5f, 100.705f, 172.2f, 100.73f +path.cubicTo(SkBits2Float(0x432ce666), SkBits2Float(0x42c98300), SkBits2Float(0x432d999a), SkBits2Float(0x42c991ed), SkBits2Float(0x432e4ccd), SkBits2Float(0x42c997de)); // 172.9f, 100.756f, 173.6f, 100.785f, 174.3f, 100.797f +path.cubicTo(SkBits2Float(0x432f0000), SkBits2Float(0x42c99dcf), SkBits2Float(0x432fb333), SkBits2Float(0x42c99950), SkBits2Float(0x43306666), SkBits2Float(0x42c9999a)); // 175, 100.808f, 175.7f, 100.799f, 176.4f, 100.8f +path.lineTo(SkBits2Float(0x43306666), SkBits2Float(0x42c9999a)); // 176.4f, 100.8f +path.lineTo(SkBits2Float(0x431b6666), SkBits2Float(0x42c9999a)); // 155.4f, 100.8f +path.close(); +path.moveTo(SkBits2Float(0x43478000), SkBits2Float(0x42c9999a)); // 199.5f, 100.8f +path.lineTo(SkBits2Float(0x43478000), SkBits2Float(0x42c9999a)); // 199.5f, 100.8f +path.cubicTo(SkBits2Float(0x43483333), SkBits2Float(0x42c9999a), SkBits2Float(0x4348e666), SkBits2Float(0x42c99cf4), SkBits2Float(0x4349999a), SkBits2Float(0x42c9999a)); // 200.2f, 100.8f, 200.9f, 100.807f, 201.6f, 100.8f +path.cubicTo(SkBits2Float(0x434a4ccd), SkBits2Float(0x42c99640), SkBits2Float(0x434b0000), SkBits2Float(0x42c98bf3), SkBits2Float(0x434bb333), SkBits2Float(0x42c9857d)); // 202.3f, 100.793f, 203, 100.773f, 203.7f, 100.761f +path.cubicTo(SkBits2Float(0x434c6666), SkBits2Float(0x42c97f08), SkBits2Float(0x434d199a), SkBits2Float(0x42c9771f), SkBits2Float(0x434dcccd), SkBits2Float(0x42c972d6)); // 204.4f, 100.748f, 205.1f, 100.733f, 205.8f, 100.724f +path.cubicTo(SkBits2Float(0x434e8000), SkBits2Float(0x42c96e8d), SkBits2Float(0x434f3333), SkBits2Float(0x42c96dc2), SkBits2Float(0x434fe666), SkBits2Float(0x42c96bc7)); // 206.5f, 100.716f, 207.2f, 100.714f, 207.9f, 100.711f +path.cubicTo(SkBits2Float(0x4350999a), SkBits2Float(0x42c969cd), SkBits2Float(0x43514ccd), SkBits2Float(0x42c967c5), SkBits2Float(0x43520000), SkBits2Float(0x42c966f7)); // 208.6f, 100.707f, 209.3f, 100.703f, 210, 100.701f +path.cubicTo(SkBits2Float(0x4352b333), SkBits2Float(0x42c9662a), SkBits2Float(0x43536666), SkBits2Float(0x42c966af), SkBits2Float(0x4354199a), SkBits2Float(0x42c966f7)); // 210.7f, 100.7f, 211.4f, 100.701f, 212.1f, 100.701f +path.cubicTo(SkBits2Float(0x4354cccd), SkBits2Float(0x42c96740), SkBits2Float(0x43558000), SkBits2Float(0x42c96b40), SkBits2Float(0x43563333), SkBits2Float(0x42c968a9)); // 212.8f, 100.702f, 213.5f, 100.709f, 214.2f, 100.704f +path.cubicTo(SkBits2Float(0x4356e666), SkBits2Float(0x42c96612), SkBits2Float(0x4357999a), SkBits2Float(0x42c9624e), SkBits2Float(0x43584ccd), SkBits2Float(0x42c9576e)); // 214.9f, 100.699f, 215.6f, 100.692f, 216.3f, 100.671f +path.cubicTo(SkBits2Float(0x43590000), SkBits2Float(0x42c94c8d), SkBits2Float(0x4359b333), SkBits2Float(0x42c935e7), SkBits2Float(0x435a6666), SkBits2Float(0x42c92765)); // 217, 100.65f, 217.7f, 100.605f, 218.4f, 100.577f +path.cubicTo(SkBits2Float(0x435b199a), SkBits2Float(0x42c918e4), SkBits2Float(0x435bcccd), SkBits2Float(0x42c9097b), SkBits2Float(0x435c8000), SkBits2Float(0x42c90064)); // 219.1f, 100.549f, 219.8f, 100.519f, 220.5f, 100.501f +path.cubicTo(SkBits2Float(0x435d3333), SkBits2Float(0x42c8f74d), SkBits2Float(0x435de666), SkBits2Float(0x42c8f840), SkBits2Float(0x435e999a), SkBits2Float(0x42c8f0db)); // 221.2f, 100.483f, 221.9f, 100.485f, 222.6f, 100.47f +path.cubicTo(SkBits2Float(0x435f4ccd), SkBits2Float(0x42c8e976), SkBits2Float(0x43600000), SkBits2Float(0x42c8df18), SkBits2Float(0x4360b333), SkBits2Float(0x42c8d407)); // 223.3f, 100.456f, 224, 100.436f, 224.7f, 100.414f +path.cubicTo(SkBits2Float(0x43616666), SkBits2Float(0x42c8c8f5), SkBits2Float(0x4362199a), SkBits2Float(0x42c8b92f), SkBits2Float(0x4362cccd), SkBits2Float(0x42c8ae71)); // 225.4f, 100.392f, 226.1f, 100.362f, 226.8f, 100.341f +path.cubicTo(SkBits2Float(0x43638000), SkBits2Float(0x42c8a3b4), SkBits2Float(0x43643333), SkBits2Float(0x42c89926), SkBits2Float(0x4364e666), SkBits2Float(0x42c89396)); // 227.5f, 100.32f, 228.2f, 100.299f, 228.9f, 100.288f +path.cubicTo(SkBits2Float(0x4365999a), SkBits2Float(0x42c88e07), SkBits2Float(0x43664ccd), SkBits2Float(0x42c88baa), SkBits2Float(0x43670000), SkBits2Float(0x42c88d14)); // 229.6f, 100.277f, 230.3f, 100.273f, 231, 100.276f +path.cubicTo(SkBits2Float(0x4367b333), SkBits2Float(0x42c88e7e), SkBits2Float(0x43686666), SkBits2Float(0x42c89675), SkBits2Float(0x4369199a), SkBits2Float(0x42c89c11)); // 231.7f, 100.278f, 232.4f, 100.294f, 233.1f, 100.305f +path.cubicTo(SkBits2Float(0x4369cccd), SkBits2Float(0x42c8a1ac), SkBits2Float(0x436a8000), SkBits2Float(0x42c8a9ea), SkBits2Float(0x436b3333), SkBits2Float(0x42c8aeb8)); // 233.8f, 100.316f, 234.5f, 100.332f, 235.2f, 100.341f +path.cubicTo(SkBits2Float(0x436be666), SkBits2Float(0x42c8b386), SkBits2Float(0x436c999a), SkBits2Float(0x42c8b733), SkBits2Float(0x436d4ccd), SkBits2Float(0x42c8b8e5)); // 235.9f, 100.351f, 236.6f, 100.358f, 237.3f, 100.361f +path.cubicTo(SkBits2Float(0x436e0000), SkBits2Float(0x42c8ba97), SkBits2Float(0x436eb333), SkBits2Float(0x42c8b9be), SkBits2Float(0x436f6666), SkBits2Float(0x42c8b8e5)); // 238, 100.364f, 238.7f, 100.363f, 239.4f, 100.361f +path.cubicTo(SkBits2Float(0x4370199a), SkBits2Float(0x42c8b80c), SkBits2Float(0x4370cccd), SkBits2Float(0x42c8b45f), SkBits2Float(0x43718000), SkBits2Float(0x42c8b3ce)); // 240.1f, 100.359f, 240.8f, 100.352f, 241.5f, 100.351f +path.cubicTo(SkBits2Float(0x43723333), SkBits2Float(0x42c8b33e), SkBits2Float(0x4372e666), SkBits2Float(0x42c8b4f0), SkBits2Float(0x4373999a), SkBits2Float(0x42c8b580)); // 242.2f, 100.35f, 242.9f, 100.353f, 243.6f, 100.354f +path.cubicTo(SkBits2Float(0x43744ccd), SkBits2Float(0x42c8b611), SkBits2Float(0x43750000), SkBits2Float(0x42c8b6ea), SkBits2Float(0x4375b333), SkBits2Float(0x42c8b733)); // 244.3f, 100.356f, 245, 100.357f, 245.7f, 100.358f +path.cubicTo(SkBits2Float(0x43766666), SkBits2Float(0x42c8b77b), SkBits2Float(0x4377199a), SkBits2Float(0x42c8b77b), SkBits2Float(0x4377cccd), SkBits2Float(0x42c8b733)); // 246.4f, 100.358f, 247.1f, 100.358f, 247.8f, 100.358f +path.cubicTo(SkBits2Float(0x43788000), SkBits2Float(0x42c8b6ea), SkBits2Float(0x43793333), SkBits2Float(0x42c8b5c9), SkBits2Float(0x4379e666), SkBits2Float(0x42c8b580)); // 248.5f, 100.357f, 249.2f, 100.355f, 249.9f, 100.354f +path.cubicTo(SkBits2Float(0x437a999a), SkBits2Float(0x42c8b538), SkBits2Float(0x437b4ccd), SkBits2Float(0x42c8b538), SkBits2Float(0x437c0000), SkBits2Float(0x42c8b580)); // 250.6f, 100.354f, 251.3f, 100.354f, 252, 100.354f +path.cubicTo(SkBits2Float(0x437cb333), SkBits2Float(0x42c8b5c9), SkBits2Float(0x437d6666), SkBits2Float(0x42c8b6ea), SkBits2Float(0x437e199a), SkBits2Float(0x42c8b733)); // 252.7f, 100.355f, 253.4f, 100.357f, 254.1f, 100.358f +path.cubicTo(SkBits2Float(0x437ecccd), SkBits2Float(0x42c8b77b), SkBits2Float(0x437f8000), SkBits2Float(0x42c8b77b), SkBits2Float(0x4380199a), SkBits2Float(0x42c8b733)); // 254.8f, 100.358f, 255.5f, 100.358f, 256.2f, 100.358f +path.cubicTo(SkBits2Float(0x43807333), SkBits2Float(0x42c8b6ea), SkBits2Float(0x4380cccd), SkBits2Float(0x42c8b5c9), SkBits2Float(0x43812666), SkBits2Float(0x42c8b580)); // 256.9f, 100.357f, 257.6f, 100.355f, 258.3f, 100.354f +path.cubicTo(SkBits2Float(0x43818000), SkBits2Float(0x42c8b538), SkBits2Float(0x4381d99a), SkBits2Float(0x42c8b580), SkBits2Float(0x43823333), SkBits2Float(0x42c8b580)); // 259, 100.354f, 259.7f, 100.354f, 260.4f, 100.354f +path.cubicTo(SkBits2Float(0x43828ccd), SkBits2Float(0x42c8b580), SkBits2Float(0x4382e666), SkBits2Float(0x42c8b580), SkBits2Float(0x43834000), SkBits2Float(0x42c8b580)); // 261.1f, 100.354f, 261.8f, 100.354f, 262.5f, 100.354f +path.cubicTo(SkBits2Float(0x4383999a), SkBits2Float(0x42c8b580), SkBits2Float(0x4383f333), SkBits2Float(0x42c8b5c9), SkBits2Float(0x43844ccd), SkBits2Float(0x42c8b580)); // 263.2f, 100.354f, 263.9f, 100.355f, 264.6f, 100.354f +path.cubicTo(SkBits2Float(0x4384a666), SkBits2Float(0x42c8b538), SkBits2Float(0x43850000), SkBits2Float(0x42c8b417), SkBits2Float(0x4385599a), SkBits2Float(0x42c8b3ce)); // 265.3f, 100.354f, 266, 100.352f, 266.7f, 100.351f +path.cubicTo(SkBits2Float(0x4385b333), SkBits2Float(0x42c8b386), SkBits2Float(0x43860ccd), SkBits2Float(0x42c8b386), SkBits2Float(0x43866666), SkBits2Float(0x42c8b3ce)); // 267.4f, 100.351f, 268.1f, 100.351f, 268.8f, 100.351f +path.cubicTo(SkBits2Float(0x4386c000), SkBits2Float(0x42c8b417), SkBits2Float(0x4387199a), SkBits2Float(0x42c8b4f0), SkBits2Float(0x43877333), SkBits2Float(0x42c8b580)); // 269.5f, 100.352f, 270.2f, 100.353f, 270.9f, 100.354f +path.cubicTo(SkBits2Float(0x4387cccd), SkBits2Float(0x42c8b611), SkBits2Float(0x43882666), SkBits2Float(0x42c8b6ea), SkBits2Float(0x43888000), SkBits2Float(0x42c8b733)); // 271.6f, 100.356f, 272.3f, 100.357f, 273, 100.358f +path.cubicTo(SkBits2Float(0x4388d99a), SkBits2Float(0x42c8b77b), SkBits2Float(0x43893333), SkBits2Float(0x42c8b6a2), SkBits2Float(0x43898ccd), SkBits2Float(0x42c8b733)); // 273.7f, 100.358f, 274.4f, 100.357f, 275.1f, 100.358f +path.cubicTo(SkBits2Float(0x4389e666), SkBits2Float(0x42c8b7c3), SkBits2Float(0x438a4000), SkBits2Float(0x42c8ba97), SkBits2Float(0x438a999a), SkBits2Float(0x42c8ba97)); // 275.8f, 100.359f, 276.5f, 100.364f, 277.2f, 100.364f +path.cubicTo(SkBits2Float(0x438af333), SkBits2Float(0x42c8ba97), SkBits2Float(0x438b4ccd), SkBits2Float(0x42c8ba5a), SkBits2Float(0x438ba666), SkBits2Float(0x42c8b733)); // 277.9f, 100.364f, 278.6f, 100.364f, 279.3f, 100.358f +path.cubicTo(SkBits2Float(0x438c0000), SkBits2Float(0x42c8b40b), SkBits2Float(0x438c599a), SkBits2Float(0x42c8aad1), SkBits2Float(0x438cb333), SkBits2Float(0x42c8a7a9)); // 280, 100.352f, 280.7f, 100.334f, 281.4f, 100.327f +path.cubicTo(SkBits2Float(0x438d0ccd), SkBits2Float(0x42c8a481), SkBits2Float(0x438d6666), SkBits2Float(0x42c89f23), SkBits2Float(0x438dc000), SkBits2Float(0x42c8a445)); // 282.1f, 100.321f, 282.8f, 100.311f, 283.5f, 100.321f +path.cubicTo(SkBits2Float(0x438e199a), SkBits2Float(0x42c8a967), SkBits2Float(0x438e7333), SkBits2Float(0x42c8b67f), SkBits2Float(0x438ecccd), SkBits2Float(0x42c8c676)); // 284.2f, 100.331f, 284.9f, 100.356f, 285.6f, 100.388f +path.cubicTo(SkBits2Float(0x438f2666), SkBits2Float(0x42c8d66d), SkBits2Float(0x438f8000), SkBits2Float(0x42c8ecb3), SkBits2Float(0x438fd99a), SkBits2Float(0x42c9040f)); // 286.3f, 100.419f, 287, 100.462f, 287.7f, 100.508f +path.cubicTo(SkBits2Float(0x43903333), SkBits2Float(0x42c91b6b), SkBits2Float(0x43908ccd), SkBits2Float(0x42c939b1), SkBits2Float(0x4390e666), SkBits2Float(0x42c9529e)); // 288.4f, 100.554f, 289.1f, 100.613f, 289.8f, 100.661f +path.cubicTo(SkBits2Float(0x43914000), SkBits2Float(0x42c96b8a), SkBits2Float(0x4391999a), SkBits2Float(0x42c98dc5), SkBits2Float(0x4391f333), SkBits2Float(0x42c9999a)); // 290.5f, 100.71f, 291.2f, 100.777f, 291.9f, 100.8f +path.cubicTo(SkBits2Float(0x43924ccd), SkBits2Float(0x42c9a56e), SkBits2Float(0x4392a666), SkBits2Float(0x42c9999a), SkBits2Float(0x43930000), SkBits2Float(0x42c9999a)); // 292.6f, 100.823f, 293.3f, 100.8f, 294, 100.8f +path.lineTo(SkBits2Float(0x43930000), SkBits2Float(0x42c9999a)); // 294, 100.8f +path.lineTo(SkBits2Float(0x43478000), SkBits2Float(0x42c9999a)); // 199.5f, 100.8f +path.close(); +path.moveTo(SkBits2Float(0x43ab2666), SkBits2Float(0x42c9999a)); // 342.3f, 100.8f +path.lineTo(SkBits2Float(0x43ab2666), SkBits2Float(0x42c9999a)); // 342.3f, 100.8f +path.cubicTo(SkBits2Float(0x43ab8000), SkBits2Float(0x42c9999a), SkBits2Float(0x43abd99a), SkBits2Float(0x42c9a526), SkBits2Float(0x43ac3333), SkBits2Float(0x42c9999a)); // 343, 100.8f, 343.7f, 100.823f, 344.4f, 100.8f +path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42c98e0d), SkBits2Float(0x43ace666), SkBits2Float(0x42c9760b), SkBits2Float(0x43ad4000), SkBits2Float(0x42c95450)); // 345.1f, 100.777f, 345.8f, 100.731f, 346.5f, 100.665f +path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42c93295), SkBits2Float(0x43adf333), SkBits2Float(0x42c8ebfd), SkBits2Float(0x43ae4ccd), SkBits2Float(0x42c8cf37)); // 347.2f, 100.599f, 347.9f, 100.461f, 348.6f, 100.405f +path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x42c8b270), SkBits2Float(0x43af0000), SkBits2Float(0x42c8afe7), SkBits2Float(0x43af599a), SkBits2Float(0x42c8a7a9)); // 349.3f, 100.349f, 350, 100.344f, 350.7f, 100.327f +path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x42c89f6b), SkBits2Float(0x43b00ccd), SkBits2Float(0x42c8a08b), SkBits2Float(0x43b06666), SkBits2Float(0x42c89dc3)); // 351.4f, 100.311f, 352.1f, 100.314f, 352.8f, 100.308f +path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x42c89afb), SkBits2Float(0x43b1199a), SkBits2Float(0x42c89864), SkBits2Float(0x43b17333), SkBits2Float(0x42c896fa)); // 353.5f, 100.303f, 354.2f, 100.298f, 354.9f, 100.295f +path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42c89591), SkBits2Float(0x43b22666), SkBits2Float(0x42c89591), SkBits2Float(0x43b28000), SkBits2Float(0x42c89548)); // 355.6f, 100.292f, 356.3f, 100.292f, 357, 100.292f +path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x42c89500), SkBits2Float(0x43b33333), SkBits2Float(0x42c89500), SkBits2Float(0x43b38ccd), SkBits2Float(0x42c89548)); // 357.7f, 100.291f, 358.4f, 100.291f, 359.1f, 100.292f +path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x42c89591), SkBits2Float(0x43b44000), SkBits2Float(0x42c896b2), SkBits2Float(0x43b4999a), SkBits2Float(0x42c896fa)); // 359.8f, 100.292f, 360.5f, 100.294f, 361.2f, 100.295f +path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x42c89743), SkBits2Float(0x43b54ccd), SkBits2Float(0x42c896fa), SkBits2Float(0x43b5a666), SkBits2Float(0x42c896fa)); // 361.9f, 100.295f, 362.6f, 100.295f, 363.3f, 100.295f +path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x42c896fa), SkBits2Float(0x43b6599a), SkBits2Float(0x42c89743), SkBits2Float(0x43b6b333), SkBits2Float(0x42c896fa)); // 364, 100.295f, 364.7f, 100.295f, 365.4f, 100.295f +path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x42c896b2), SkBits2Float(0x43b76666), SkBits2Float(0x42c89591), SkBits2Float(0x43b7c000), SkBits2Float(0x42c89548)); // 366.1f, 100.294f, 366.8f, 100.292f, 367.5f, 100.292f +path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x42c89500), SkBits2Float(0x43b87333), SkBits2Float(0x42c89548), SkBits2Float(0x43b8cccd), SkBits2Float(0x42c89548)); // 368.2f, 100.291f, 368.9f, 100.292f, 369.6f, 100.292f +path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x42c89548), SkBits2Float(0x43b98000), SkBits2Float(0x42c89548), SkBits2Float(0x43b9d99a), SkBits2Float(0x42c89548)); // 370.3f, 100.292f, 371, 100.292f, 371.7f, 100.292f +path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x42c89548), SkBits2Float(0x43ba8ccd), SkBits2Float(0x42c894b7), SkBits2Float(0x43bae666), SkBits2Float(0x42c89548)); // 372.4f, 100.292f, 373.1f, 100.29f, 373.8f, 100.292f +path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x42c895d9), SkBits2Float(0x43bb999a), SkBits2Float(0x42c897d3), SkBits2Float(0x43bbf333), SkBits2Float(0x42c898ac)); // 374.5f, 100.293f, 375.2f, 100.297f, 375.9f, 100.298f +path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x42c89985), SkBits2Float(0x43bca666), SkBits2Float(0x42c89a16), SkBits2Float(0x43bd0000), SkBits2Float(0x42c89a5f)); // 376.6f, 100.3f, 377.3f, 100.301f, 378, 100.302f +path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x42c89aa7), SkBits2Float(0x43bdb333), SkBits2Float(0x42c89aa7), SkBits2Float(0x43be0ccd), SkBits2Float(0x42c89a5f)); // 378.7f, 100.302f, 379.4f, 100.302f, 380.1f, 100.302f +path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x42c89a16), SkBits2Float(0x43bec000), SkBits2Float(0x42c8993d), SkBits2Float(0x43bf199a), SkBits2Float(0x42c898ac)); // 380.8f, 100.301f, 381.5f, 100.299f, 382.2f, 100.298f +path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x42c8981c), SkBits2Float(0x43bfcccd), SkBits2Float(0x42c8978b), SkBits2Float(0x43c02666), SkBits2Float(0x42c896fa)); // 382.9f, 100.297f, 383.6f, 100.296f, 384.3f, 100.295f +path.cubicTo(SkBits2Float(0x43c08000), SkBits2Float(0x42c8966a), SkBits2Float(0x43c0d99a), SkBits2Float(0x42c8946f), SkBits2Float(0x43c13333), SkBits2Float(0x42c89548)); // 385, 100.294f, 385.7f, 100.29f, 386.4f, 100.292f +path.cubicTo(SkBits2Float(0x43c18ccd), SkBits2Float(0x42c89621), SkBits2Float(0x43c1e666), SkBits2Float(0x42c898f5), SkBits2Float(0x43c24000), SkBits2Float(0x42c89c11)); // 387.1f, 100.293f, 387.8f, 100.299f, 388.5f, 100.305f +path.cubicTo(SkBits2Float(0x43c2999a), SkBits2Float(0x42c89f2d), SkBits2Float(0x43c2f333), SkBits2Float(0x42c8a5ad), SkBits2Float(0x43c34ccd), SkBits2Float(0x42c8a7ef)); // 389.2f, 100.311f, 389.9f, 100.324f, 390.6f, 100.328f +path.cubicTo(SkBits2Float(0x43c3a666), SkBits2Float(0x42c8aa32), SkBits2Float(0x43c40000), SkBits2Float(0x42c8a9a2), SkBits2Float(0x43c4599a), SkBits2Float(0x42c8a9a2)); // 391.3f, 100.332f, 392, 100.331f, 392.7f, 100.331f +path.cubicTo(SkBits2Float(0x43c4b333), SkBits2Float(0x42c8a9a2), SkBits2Float(0x43c50ccd), SkBits2Float(0x42c8a8c9), SkBits2Float(0x43c56666), SkBits2Float(0x42c8a7ef)); // 393.4f, 100.331f, 394.1f, 100.33f, 394.8f, 100.328f +path.cubicTo(SkBits2Float(0x43c5c000), SkBits2Float(0x42c8a716), SkBits2Float(0x43c6199a), SkBits2Float(0x42c8a5ad), SkBits2Float(0x43c67333), SkBits2Float(0x42c8a48b)); // 395.5f, 100.326f, 396.2f, 100.324f, 396.9f, 100.321f +path.cubicTo(SkBits2Float(0x43c6cccd), SkBits2Float(0x42c8a36a), SkBits2Float(0x43c72666), SkBits2Float(0x42c8a291), SkBits2Float(0x43c78000), SkBits2Float(0x42c8a127)); // 397.6f, 100.319f, 398.3f, 100.318f, 399, 100.315f +path.cubicTo(SkBits2Float(0x43c7d99a), SkBits2Float(0x42c89fbd), SkBits2Float(0x43c83333), SkBits2Float(0x42c89dc3), SkBits2Float(0x43c88ccd), SkBits2Float(0x42c89c11)); // 399.7f, 100.312f, 400.4f, 100.308f, 401.1f, 100.305f +path.cubicTo(SkBits2Float(0x43c8e666), SkBits2Float(0x42c89a5f), SkBits2Float(0x43c94000), SkBits2Float(0x42c898ac), SkBits2Float(0x43c9999a), SkBits2Float(0x42c896fa)); // 401.8f, 100.302f, 402.5f, 100.298f, 403.2f, 100.295f +path.cubicTo(SkBits2Float(0x43c9f333), SkBits2Float(0x42c89548), SkBits2Float(0x43ca4ccd), SkBits2Float(0x42c89305), SkBits2Float(0x43caa666), SkBits2Float(0x42c891e4)); // 403.9f, 100.292f, 404.6f, 100.287f, 405.3f, 100.285f +path.cubicTo(SkBits2Float(0x43cb0000), SkBits2Float(0x42c890c3), SkBits2Float(0x43cb599a), SkBits2Float(0x42c88ec8), SkBits2Float(0x43cbb333), SkBits2Float(0x42c89032)); // 406, 100.283f, 406.7f, 100.279f, 407.4f, 100.282f +path.cubicTo(SkBits2Float(0x43cc0ccd), SkBits2Float(0x42c8919c), SkBits2Float(0x43cc6666), SkBits2Float(0x42c89864), SkBits2Float(0x43ccc000), SkBits2Float(0x42c89a5f)); // 408.1f, 100.284f, 408.8f, 100.298f, 409.5f, 100.302f +path.cubicTo(SkBits2Float(0x43cd199a), SkBits2Float(0x42c89c59), SkBits2Float(0x43cd7333), SkBits2Float(0x42c89dff), SkBits2Float(0x43cdcccd), SkBits2Float(0x42c89c11)); // 410.2f, 100.305f, 410.9f, 100.309f, 411.6f, 100.305f +path.cubicTo(SkBits2Float(0x43ce2666), SkBits2Float(0x42c89a22), SkBits2Float(0x43ce8000), SkBits2Float(0x42c8919a), SkBits2Float(0x43ced99a), SkBits2Float(0x42c88ec6)); // 412.3f, 100.301f, 413, 100.284f, 413.7f, 100.279f +path.cubicTo(SkBits2Float(0x43cf3333), SkBits2Float(0x42c88bf3), SkBits2Float(0x43cf8ccd), SkBits2Float(0x42c88b70), SkBits2Float(0x43cfe666), SkBits2Float(0x42c88b1b)); // 414.4f, 100.273f, 415.1f, 100.272f, 415.8f, 100.272f +path.cubicTo(SkBits2Float(0x43d04000), SkBits2Float(0x42c88ac7), SkBits2Float(0x43d0999a), SkBits2Float(0x42c88bac), SkBits2Float(0x43d0f333), SkBits2Float(0x42c88cce)); // 416.5f, 100.271f, 417.2f, 100.273f, 417.9f, 100.275f +path.cubicTo(SkBits2Float(0x43d14ccd), SkBits2Float(0x42c88def), SkBits2Float(0x43d1a666), SkBits2Float(0x42c89032), SkBits2Float(0x43d20000), SkBits2Float(0x42c891e4)); // 418.6f, 100.277f, 419.3f, 100.282f, 420, 100.285f +path.cubicTo(SkBits2Float(0x43d2599a), SkBits2Float(0x42c89396), SkBits2Float(0x43d2b333), SkBits2Float(0x42c89621), SkBits2Float(0x43d30ccd), SkBits2Float(0x42c896fa)); // 420.7f, 100.288f, 421.4f, 100.293f, 422.1f, 100.295f +path.cubicTo(SkBits2Float(0x43d36666), SkBits2Float(0x42c897d3), SkBits2Float(0x43d3c000), SkBits2Float(0x42c89810), SkBits2Float(0x43d4199a), SkBits2Float(0x42c896fa)); // 422.8f, 100.297f, 423.5f, 100.297f, 424.2f, 100.295f +path.cubicTo(SkBits2Float(0x43d47333), SkBits2Float(0x42c895e5), SkBits2Float(0x43d4cccd), SkBits2Float(0x42c88d99), SkBits2Float(0x43d52666), SkBits2Float(0x42c89078)); // 424.9f, 100.293f, 425.6f, 100.277f, 426.3f, 100.282f +path.cubicTo(SkBits2Float(0x43d58000), SkBits2Float(0x42c89358), SkBits2Float(0x43d5d99a), SkBits2Float(0x42c88f0f), SkBits2Float(0x43d63333), SkBits2Float(0x42c8a836)); // 427, 100.288f, 427.7f, 100.279f, 428.4f, 100.329f +path.cubicTo(SkBits2Float(0x43d68ccd), SkBits2Float(0x42c8c15e), SkBits2Float(0x43d6e666), SkBits2Float(0x42c8ff2a), SkBits2Float(0x43d74000), SkBits2Float(0x42c92765)); // 429.1f, 100.378f, 429.8f, 100.498f, 430.5f, 100.577f +path.cubicTo(SkBits2Float(0x43d7999a), SkBits2Float(0x42c94fa0), SkBits2Float(0x43d7f333), SkBits2Float(0x42c98691), SkBits2Float(0x43d84ccd), SkBits2Float(0x42c9999a)); // 431.2f, 100.656f, 431.9f, 100.763f, 432.6f, 100.8f +path.cubicTo(SkBits2Float(0x43d8a666), SkBits2Float(0x42c9aca2), SkBits2Float(0x43d90000), SkBits2Float(0x42c9999a), SkBits2Float(0x43d9599a), SkBits2Float(0x42c9999a)); // 433.3f, 100.837f, 434, 100.8f, 434.7f, 100.8f +path.lineTo(SkBits2Float(0x43d9599a), SkBits2Float(0x42c9999a)); // 434.7f, 100.8f +path.lineTo(SkBits2Float(0x43ab2666), SkBits2Float(0x42c9999a)); // 342.3f, 100.8f +path.close(); +path.moveTo(SkBits2Float(0x43dfa666), SkBits2Float(0x42c9999a)); // 447.3f, 100.8f +path.lineTo(SkBits2Float(0x43dfa666), SkBits2Float(0x42c9999a)); // 447.3f, 100.8f +path.cubicTo(SkBits2Float(0x43e00000), SkBits2Float(0x42c99877), SkBits2Float(0x43e0599a), SkBits2Float(0x42c99312), SkBits2Float(0x43e0b333), SkBits2Float(0x42c992c8)); // 448, 100.798f, 448.7f, 100.787f, 449.4f, 100.787f +path.cubicTo(SkBits2Float(0x43e10ccd), SkBits2Float(0x42c9927e), SkBits2Float(0x43e16666), SkBits2Float(0x42c996bb), SkBits2Float(0x43e1c000), SkBits2Float(0x42c997de)); // 450.1f, 100.786f, 450.8f, 100.794f, 451.5f, 100.797f +path.cubicTo(SkBits2Float(0x43e2199a), SkBits2Float(0x42c99901), SkBits2Float(0x43e27333), SkBits2Float(0x42c9a0aa), SkBits2Float(0x43e2cccd), SkBits2Float(0x42c9999a)); // 452.2f, 100.799f, 452.9f, 100.814f, 453.6f, 100.8f +path.cubicTo(SkBits2Float(0x43e32666), SkBits2Float(0x42c99289), SkBits2Float(0x43e38000), SkBits2Float(0x42c97cb2), SkBits2Float(0x43e3d99a), SkBits2Float(0x42c96d79)); // 454.3f, 100.786f, 455, 100.744f, 455.7f, 100.714f +path.cubicTo(SkBits2Float(0x43e43333), SkBits2Float(0x42c95e40), SkBits2Float(0x43e48ccd), SkBits2Float(0x42c943a3), SkBits2Float(0x43e4e666), SkBits2Float(0x42c93e44)); // 456.4f, 100.684f, 457.1f, 100.632f, 457.8f, 100.622f +path.cubicTo(SkBits2Float(0x43e54000), SkBits2Float(0x42c938e6), SkBits2Float(0x43e5999a), SkBits2Float(0x42c949f4), SkBits2Float(0x43e5f333), SkBits2Float(0x42c94d41)); // 458.5f, 100.611f, 459.2f, 100.644f, 459.9f, 100.651f +path.cubicTo(SkBits2Float(0x43e64ccd), SkBits2Float(0x42c9508e), SkBits2Float(0x43e6a666), SkBits2Float(0x42c94fce), SkBits2Float(0x43e70000), SkBits2Float(0x42c95211)); // 460.6f, 100.657f, 461.3f, 100.656f, 462, 100.66f +path.cubicTo(SkBits2Float(0x43e7599a), SkBits2Float(0x42c95454), SkBits2Float(0x43e7b333), SkBits2Float(0x42c9595c), SkBits2Float(0x43e80ccd), SkBits2Float(0x42c95ad2)); // 462.7f, 100.665f, 463.4f, 100.675f, 464.1f, 100.677f +path.cubicTo(SkBits2Float(0x43e86666), SkBits2Float(0x42c95c47), SkBits2Float(0x43e8c000), SkBits2Float(0x42c959f9), SkBits2Float(0x43e9199a), SkBits2Float(0x42c95ad2)); // 464.8f, 100.68f, 465.5f, 100.676f, 466.2f, 100.677f +path.cubicTo(SkBits2Float(0x43e97333), SkBits2Float(0x42c95bab), SkBits2Float(0x43e9cccd), SkBits2Float(0x42c95d20), SkBits2Float(0x43ea2666), SkBits2Float(0x42c95fe8)); // 466.9f, 100.679f, 467.6f, 100.682f, 468.3f, 100.687f +path.cubicTo(SkBits2Float(0x43ea8000), SkBits2Float(0x42c962b0), SkBits2Float(0x43ead99a), SkBits2Float(0x42c96743), SkBits2Float(0x43eb3333), SkBits2Float(0x42c96b80)); // 469, 100.693f, 469.7f, 100.702f, 470.4f, 100.71f +path.cubicTo(SkBits2Float(0x43eb8ccd), SkBits2Float(0x42c96fbe), SkBits2Float(0x43ebe666), SkBits2Float(0x42c9754c), SkBits2Float(0x43ec4000), SkBits2Float(0x42c97958)); // 471.1f, 100.718f, 471.8f, 100.729f, 472.5f, 100.737f +path.cubicTo(SkBits2Float(0x43ec999a), SkBits2Float(0x42c97d65), SkBits2Float(0x43ecf333), SkBits2Float(0x42c97ef2), SkBits2Float(0x43ed4ccd), SkBits2Float(0x42c983cb)); // 473.2f, 100.745f, 473.9f, 100.748f, 474.6f, 100.757f +path.cubicTo(SkBits2Float(0x43eda666), SkBits2Float(0x42c988a5), SkBits2Float(0x43ee0000), SkBits2Float(0x42c992d0), SkBits2Float(0x43ee599a), SkBits2Float(0x42c99673)); // 475.3f, 100.767f, 476, 100.787f, 476.7f, 100.794f +path.cubicTo(SkBits2Float(0x43eeb333), SkBits2Float(0x42c99a15), SkBits2Float(0x43ef0ccd), SkBits2Float(0x42c99bdc), SkBits2Float(0x43ef6666), SkBits2Float(0x42c9999a)); // 477.4f, 100.801f, 478.1f, 100.804f, 478.8f, 100.8f +path.cubicTo(SkBits2Float(0x43efc000), SkBits2Float(0x42c99757), SkBits2Float(0x43f0199a), SkBits2Float(0x42c99426), SkBits2Float(0x43f07333), SkBits2Float(0x42c988e2)); // 479.5f, 100.796f, 480.2f, 100.789f, 480.9f, 100.767f +path.cubicTo(SkBits2Float(0x43f0cccd), SkBits2Float(0x42c97d9e), SkBits2Float(0x43f12666), SkBits2Float(0x42c96356), SkBits2Float(0x43f18000), SkBits2Float(0x42c95602)); // 481.6f, 100.745f, 482.3f, 100.694f, 483, 100.668f +path.cubicTo(SkBits2Float(0x43f1d99a), SkBits2Float(0x42c948ae), SkBits2Float(0x43f23333), SkBits2Float(0x42c93ee3), SkBits2Float(0x43f28ccd), SkBits2Float(0x42c938e7)); // 483.7f, 100.642f, 484.4f, 100.623f, 485.1f, 100.611f +path.cubicTo(SkBits2Float(0x43f2e666), SkBits2Float(0x42c932ec), SkBits2Float(0x43f34000), SkBits2Float(0x42c93741), SkBits2Float(0x43f3999a), SkBits2Float(0x42c9321f)); // 485.8f, 100.599f, 486.5f, 100.608f, 487.2f, 100.598f +path.cubicTo(SkBits2Float(0x43f3f333), SkBits2Float(0x42c92cfd), SkBits2Float(0x43f44ccd), SkBits2Float(0x42c922f5), SkBits2Float(0x43f4a666), SkBits2Float(0x42c91a1b)); // 487.9f, 100.588f, 488.6f, 100.568f, 489.3f, 100.551f +path.cubicTo(SkBits2Float(0x43f50000), SkBits2Float(0x42c91140), SkBits2Float(0x43f5599a), SkBits2Float(0x42c904ad), SkBits2Float(0x43f5b333), SkBits2Float(0x42c8fd00)); // 490, 100.534f, 490.7f, 100.509f, 491.4f, 100.494f +path.cubicTo(SkBits2Float(0x43f60ccd), SkBits2Float(0x42c8f553), SkBits2Float(0x43f66666), SkBits2Float(0x42c8ef7b), SkBits2Float(0x43f6c000), SkBits2Float(0x42c8ec0b)); // 492.1f, 100.479f, 492.8f, 100.468f, 493.5f, 100.461f +path.cubicTo(SkBits2Float(0x43f7199a), SkBits2Float(0x42c8e89b), SkBits2Float(0x43f77333), SkBits2Float(0x42c8e981), SkBits2Float(0x43f7cccd), SkBits2Float(0x42c8e860)); // 494.2f, 100.454f, 494.9f, 100.456f, 495.6f, 100.454f +path.cubicTo(SkBits2Float(0x43f82666), SkBits2Float(0x42c8e73f), SkBits2Float(0x43f88000), SkBits2Float(0x42c8e658), SkBits2Float(0x43f8d99a), SkBits2Float(0x42c8e542)); // 496.3f, 100.452f, 497, 100.45f, 497.7f, 100.448f +path.cubicTo(SkBits2Float(0x43f93333), SkBits2Float(0x42c8e42d), SkBits2Float(0x43f98ccd), SkBits2Float(0x42c8e348), SkBits2Float(0x43f9e666), SkBits2Float(0x42c8e1de)); // 498.4f, 100.446f, 499.1f, 100.444f, 499.8f, 100.441f +path.cubicTo(SkBits2Float(0x43fa4000), SkBits2Float(0x42c8e074), SkBits2Float(0x43fa999a), SkBits2Float(0x42c8df53), SkBits2Float(0x43faf333), SkBits2Float(0x42c8dcc8)); // 500.5f, 100.438f, 501.2f, 100.436f, 501.9f, 100.431f +path.cubicTo(SkBits2Float(0x43fb4ccd), SkBits2Float(0x42c8da3d), SkBits2Float(0x43fba666), SkBits2Float(0x42c8d5c3), SkBits2Float(0x43fc0000), SkBits2Float(0x42c8d29b)); // 502.6f, 100.426f, 503.3f, 100.418f, 504, 100.411f +path.cubicTo(SkBits2Float(0x43fc599a), SkBits2Float(0x42c8cf73), SkBits2Float(0x43fcb333), SkBits2Float(0x42c8ccf6), SkBits2Float(0x43fd0ccd), SkBits2Float(0x42c8c9da)); // 504.7f, 100.405f, 505.4f, 100.4f, 506.1f, 100.394f +path.cubicTo(SkBits2Float(0x43fd6666), SkBits2Float(0x42c8c6be), SkBits2Float(0x43fdc000), SkBits2Float(0x42c8c310), SkBits2Float(0x43fe199a), SkBits2Float(0x42c8bff4)); // 506.8f, 100.388f, 507.5f, 100.381f, 508.2f, 100.375f +path.cubicTo(SkBits2Float(0x43fe7333), SkBits2Float(0x42c8bcd8), SkBits2Float(0x43fecccd), SkBits2Float(0x42c8b8f0), SkBits2Float(0x43ff2666), SkBits2Float(0x42c8b733)); // 508.9f, 100.369f, 509.6f, 100.361f, 510.3f, 100.358f +path.cubicTo(SkBits2Float(0x43ff8000), SkBits2Float(0x42c8b575), SkBits2Float(0x43ffd99a), SkBits2Float(0x42c8b45f), SkBits2Float(0x4400199a), SkBits2Float(0x42c8b580)); // 511, 100.354f, 511.7f, 100.352f, 512.4f, 100.354f +path.cubicTo(SkBits2Float(0x44004666), SkBits2Float(0x42c8b6a2), SkBits2Float(0x44007333), SkBits2Float(0x42c8bb28), SkBits2Float(0x4400a000), SkBits2Float(0x42c8bdfb)); // 513.1f, 100.357f, 513.8f, 100.366f, 514.5f, 100.371f +path.cubicTo(SkBits2Float(0x4400cccd), SkBits2Float(0x42c8c0cf), SkBits2Float(0x4400f99a), SkBits2Float(0x42c8c31d), SkBits2Float(0x44012666), SkBits2Float(0x42c8c676)); // 515.2f, 100.377f, 515.9f, 100.381f, 516.6f, 100.388f +path.cubicTo(SkBits2Float(0x44015333), SkBits2Float(0x42c8c9ce), SkBits2Float(0x44018000), SkBits2Float(0x42c8caa9), SkBits2Float(0x4401accd), SkBits2Float(0x42c8d20e)); // 517.3f, 100.394f, 518, 100.396f, 518.7f, 100.41f +path.cubicTo(SkBits2Float(0x4401d99a), SkBits2Float(0x42c8d973), SkBits2Float(0x44020666), SkBits2Float(0x42c8e446), SkBits2Float(0x44023333), SkBits2Float(0x42c8f2d3)); // 519.4f, 100.425f, 520.1f, 100.446f, 520.8f, 100.474f +path.cubicTo(SkBits2Float(0x44026000), SkBits2Float(0x42c90161), SkBits2Float(0x44028ccd), SkBits2Float(0x42c917c0), SkBits2Float(0x4402b99a), SkBits2Float(0x42c9295e)); // 521.5f, 100.503f, 522.2f, 100.546f, 522.9f, 100.581f +path.cubicTo(SkBits2Float(0x4402e666), SkBits2Float(0x42c93afb), SkBits2Float(0x44031333), SkBits2Float(0x42c94c14), SkBits2Float(0x44034000), SkBits2Float(0x42c95c84)); // 523.6f, 100.615f, 524.3f, 100.649f, 525, 100.681f +path.cubicTo(SkBits2Float(0x44036ccd), SkBits2Float(0x42c96cf4), SkBits2Float(0x4403999a), SkBits2Float(0x42c981d1), SkBits2Float(0x4403c666), SkBits2Float(0x42c98bff)); // 525.7f, 100.713f, 526.4f, 100.754f, 527.1f, 100.773f +path.cubicTo(SkBits2Float(0x4403f333), SkBits2Float(0x42c9962e), SkBits2Float(0x44042000), SkBits2Float(0x42c99755), SkBits2Float(0x44044ccd), SkBits2Float(0x42c9999a)); // 527.8f, 100.793f, 528.5f, 100.796f, 529.2f, 100.8f +path.lineTo(SkBits2Float(0x44044ccd), SkBits2Float(0x42c9999a)); // 529.2f, 100.8f +path.lineTo(SkBits2Float(0x43dfa666), SkBits2Float(0x42c9999a)); // 447.3f, 100.8f +path.close(); + return path; +} + +static SkPath path6() { + SkPath path; +path.moveTo(SkBits2Float(0x42b06666), SkBits2Float(0x42c9999a)); // 88.2f, 100.8f +path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42c9999a)); // 88.2f, 100.8f +path.cubicTo(SkBits2Float(0x42b1cccd), SkBits2Float(0x42c9999a), SkBits2Float(0x42b33333), SkBits2Float(0x42c97f2d), SkBits2Float(0x42b4999a), SkBits2Float(0x42c9999a)); // 88.9f, 100.8f, 89.6f, 100.748f, 90.3f, 100.8f +path.cubicTo(SkBits2Float(0x42b60000), SkBits2Float(0x42c9b408), SkBits2Float(0x42b76666), SkBits2Float(0x42ca1e13), SkBits2Float(0x42b8cccd), SkBits2Float(0x42ca382d)); // 91, 100.852f, 91.7f, 101.059f, 92.4f, 101.11f +path.cubicTo(SkBits2Float(0x42ba3333), SkBits2Float(0x42ca5246), SkBits2Float(0x42bb999a), SkBits2Float(0x42ca3634), SkBits2Float(0x42bd0000), SkBits2Float(0x42ca3634)); // 93.1f, 101.161f, 93.8f, 101.106f, 94.5f, 101.106f +path.cubicTo(SkBits2Float(0x42be6666), SkBits2Float(0x42ca3634), SkBits2Float(0x42bfcccd), SkBits2Float(0x42ca3869), SkBits2Float(0x42c13333), SkBits2Float(0x42ca382d)); // 95.2f, 101.106f, 95.9f, 101.11f, 96.6f, 101.11f +path.cubicTo(SkBits2Float(0x42c2999a), SkBits2Float(0x42ca37f0), SkBits2Float(0x42c40000), SkBits2Float(0x42ca35ea), SkBits2Float(0x42c56666), SkBits2Float(0x42ca34c8)); // 97.3f, 101.109f, 98, 101.105f, 98.7f, 101.103f +path.cubicTo(SkBits2Float(0x42c6cccd), SkBits2Float(0x42ca33a7), SkBits2Float(0x42c83333), SkBits2Float(0x42ca311c), SkBits2Float(0x42c9999a), SkBits2Float(0x42ca3164)); // 99.4f, 101.101f, 100.1f, 101.096f, 100.8f, 101.096f +path.cubicTo(SkBits2Float(0x42cb0000), SkBits2Float(0x42ca31ad), SkBits2Float(0x42cc6666), SkBits2Float(0x42ca33fb), SkBits2Float(0x42cdcccd), SkBits2Float(0x42ca367b)); // 101.5f, 101.097f, 102.2f, 101.102f, 102.9f, 101.106f +path.cubicTo(SkBits2Float(0x42cf3333), SkBits2Float(0x42ca38fa), SkBits2Float(0x42d0999a), SkBits2Float(0x42ca3fd0), SkBits2Float(0x42d20000), SkBits2Float(0x42ca4061)); // 103.6f, 101.111f, 104.3f, 101.125f, 105, 101.126f +path.cubicTo(SkBits2Float(0x42d36666), SkBits2Float(0x42ca40f1), SkBits2Float(0x42d4cccd), SkBits2Float(0x42ca3f32), SkBits2Float(0x42d63333), SkBits2Float(0x42ca39df)); // 105.7f, 101.127f, 106.4f, 101.123f, 107.1f, 101.113f +path.cubicTo(SkBits2Float(0x42d7999a), SkBits2Float(0x42ca348c), SkBits2Float(0x42d90000), SkBits2Float(0x42ca2585), SkBits2Float(0x42da6666), SkBits2Float(0x42ca206f)); // 107.8f, 101.103f, 108.5f, 101.073f, 109.2f, 101.063f +path.cubicTo(SkBits2Float(0x42dbcccd), SkBits2Float(0x42ca1b59), SkBits2Float(0x42dd3333), SkBits2Float(0x42ca1a43), SkBits2Float(0x42de999a), SkBits2Float(0x42ca1b59)); // 109.9f, 101.053f, 110.6f, 101.051f, 111.3f, 101.053f +path.cubicTo(SkBits2Float(0x42e00000), SkBits2Float(0x42ca1c6e), SkBits2Float(0x42e16666), SkBits2Float(0x42ca2872), SkBits2Float(0x42e2cccd), SkBits2Float(0x42ca26f1)); // 112, 101.056f, 112.7f, 101.079f, 113.4f, 101.076f +path.cubicTo(SkBits2Float(0x42e43333), SkBits2Float(0x42ca2570), SkBits2Float(0x42e5999a), SkBits2Float(0x42ca1f14), SkBits2Float(0x42e70000), SkBits2Float(0x42ca1251)); // 114.1f, 101.073f, 114.8f, 101.061f, 115.5f, 101.036f +path.cubicTo(SkBits2Float(0x42e86666), SkBits2Float(0x42ca058d), SkBits2Float(0x42e9cccd), SkBits2Float(0x42c9ec89), SkBits2Float(0x42eb3333), SkBits2Float(0x42c9da5b)); // 116.2f, 101.011f, 116.9f, 100.962f, 117.6f, 100.926f +path.cubicTo(SkBits2Float(0x42ec999a), SkBits2Float(0x42c9c82d), SkBits2Float(0x42ee0000), SkBits2Float(0x42c9b007), SkBits2Float(0x42ef6666), SkBits2Float(0x42c9a53c)); // 118.3f, 100.891f, 119, 100.844f, 119.7f, 100.823f +path.cubicTo(SkBits2Float(0x42f0cccd), SkBits2Float(0x42c99a71), SkBits2Float(0x42f23333), SkBits2Float(0x42c99b8b), SkBits2Float(0x42f3999a), SkBits2Float(0x42c9999a)); // 120.4f, 100.802f, 121.1f, 100.804f, 121.8f, 100.8f +path.lineTo(SkBits2Float(0x42f3999a), SkBits2Float(0x42c9999a)); // 121.8f, 100.8f +path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42c9999a)); // 88.2f, 100.8f +path.close(); +path.moveTo(SkBits2Float(0x4300199a), SkBits2Float(0x42c9999a)); // 128.1f, 100.8f +path.lineTo(SkBits2Float(0x4300199a), SkBits2Float(0x42c99b0f)); // 128.1f, 100.803f +path.cubicTo(SkBits2Float(0x4300cccd), SkBits2Float(0x42c99bde), SkBits2Float(0x43018000), SkBits2Float(0x42c99eb2), SkBits2Float(0x43023333), SkBits2Float(0x42c99e73)); // 128.8f, 100.804f, 129.5f, 100.81f, 130.2f, 100.809f +path.cubicTo(SkBits2Float(0x4302e666), SkBits2Float(0x42c99e35), SkBits2Float(0x4303999a), SkBits2Float(0x42c99a69), SkBits2Float(0x43044ccd), SkBits2Float(0x42c9999a)); // 130.9f, 100.809f, 131.6f, 100.802f, 132.3f, 100.8f +path.lineTo(SkBits2Float(0x43044ccd), SkBits2Float(0x42c9999a)); // 132.3f, 100.8f +path.lineTo(SkBits2Float(0x4300199a), SkBits2Float(0x42c9999a)); // 128.1f, 100.8f +path.close(); +path.moveTo(SkBits2Float(0x431b6666), SkBits2Float(0x42c9999a)); // 155.4f, 100.8f +path.lineTo(SkBits2Float(0x431b6666), SkBits2Float(0x42c9999a)); // 155.4f, 100.8f +path.cubicTo(SkBits2Float(0x431c199a), SkBits2Float(0x42c9999a), SkBits2Float(0x431ccccd), SkBits2Float(0x42c99567), SkBits2Float(0x431d8000), SkBits2Float(0x42c9999a)); // 156.1f, 100.8f, 156.8f, 100.792f, 157.5f, 100.8f +path.cubicTo(SkBits2Float(0x431e3333), SkBits2Float(0x42c99dcd), SkBits2Float(0x431ee666), SkBits2Float(0x42c9a613), SkBits2Float(0x431f999a), SkBits2Float(0x42c9b2cd)); // 158.2f, 100.808f, 158.9f, 100.824f, 159.6f, 100.849f +path.cubicTo(SkBits2Float(0x43204ccd), SkBits2Float(0x42c9bf86), SkBits2Float(0x43210000), SkBits2Float(0x42c9d893), SkBits2Float(0x4321b333), SkBits2Float(0x42c9e5f3)); // 160.3f, 100.874f, 161, 100.923f, 161.7f, 100.949f +path.cubicTo(SkBits2Float(0x43226666), SkBits2Float(0x42c9f353), SkBits2Float(0x4323199a), SkBits2Float(0x42c9fd4f), SkBits2Float(0x4323cccd), SkBits2Float(0x42ca030e)); // 162.4f, 100.975f, 163.1f, 100.995f, 163.8f, 101.006f +path.cubicTo(SkBits2Float(0x43248000), SkBits2Float(0x42ca08cc), SkBits2Float(0x43253333), SkBits2Float(0x42ca07ce), SkBits2Float(0x4325e666), SkBits2Float(0x42ca086b)); // 164.5f, 101.017f, 165.2f, 101.015f, 165.9f, 101.016f +path.cubicTo(SkBits2Float(0x4326999a), SkBits2Float(0x42ca0907), SkBits2Float(0x43274ccd), SkBits2Float(0x42ca0bdb), SkBits2Float(0x43280000), SkBits2Float(0x42ca06b9)); // 166.6f, 101.018f, 167.3f, 101.023f, 168, 101.013f +path.cubicTo(SkBits2Float(0x4328b333), SkBits2Float(0x42ca0196), SkBits2Float(0x43296666), SkBits2Float(0x42c9f5dd), SkBits2Float(0x432a199a), SkBits2Float(0x42c9e99e)); // 168.7f, 101.003f, 169.4f, 100.98f, 170.1f, 100.956f +path.cubicTo(SkBits2Float(0x432acccd), SkBits2Float(0x42c9dd5f), SkBits2Float(0x432b8000), SkBits2Float(0x42c9ca4c), SkBits2Float(0x432c3333), SkBits2Float(0x42c9bd40)); // 170.8f, 100.932f, 171.5f, 100.895f, 172.2f, 100.87f +path.cubicTo(SkBits2Float(0x432ce666), SkBits2Float(0x42c9b034), SkBits2Float(0x432d999a), SkBits2Float(0x42c9a147), SkBits2Float(0x432e4ccd), SkBits2Float(0x42c99b56)); // 172.9f, 100.844f, 173.6f, 100.815f, 174.3f, 100.803f +path.cubicTo(SkBits2Float(0x432f0000), SkBits2Float(0x42c99565), SkBits2Float(0x432fb333), SkBits2Float(0x42c999e4), SkBits2Float(0x43306666), SkBits2Float(0x42c9999a)); // 175, 100.792f, 175.7f, 100.801f, 176.4f, 100.8f +path.lineTo(SkBits2Float(0x43306666), SkBits2Float(0x42c9999a)); // 176.4f, 100.8f +path.lineTo(SkBits2Float(0x431b6666), SkBits2Float(0x42c9999a)); // 155.4f, 100.8f +path.close(); +path.moveTo(SkBits2Float(0x43478000), SkBits2Float(0x42c9999a)); // 199.5f, 100.8f +path.lineTo(SkBits2Float(0x43478000), SkBits2Float(0x42c9999a)); // 199.5f, 100.8f +path.cubicTo(SkBits2Float(0x43483333), SkBits2Float(0x42c9999a), SkBits2Float(0x4348e666), SkBits2Float(0x42c99640), SkBits2Float(0x4349999a), SkBits2Float(0x42c9999a)); // 200.2f, 100.8f, 200.9f, 100.793f, 201.6f, 100.8f +path.cubicTo(SkBits2Float(0x434a4ccd), SkBits2Float(0x42c99cf4), SkBits2Float(0x434b0000), SkBits2Float(0x42c9a741), SkBits2Float(0x434bb333), SkBits2Float(0x42c9adb7)); // 202.3f, 100.807f, 203, 100.827f, 203.7f, 100.839f +path.cubicTo(SkBits2Float(0x434c6666), SkBits2Float(0x42c9b42c), SkBits2Float(0x434d199a), SkBits2Float(0x42c9bc15), SkBits2Float(0x434dcccd), SkBits2Float(0x42c9c05e)); // 204.4f, 100.852f, 205.1f, 100.867f, 205.8f, 100.876f +path.cubicTo(SkBits2Float(0x434e8000), SkBits2Float(0x42c9c4a7), SkBits2Float(0x434f3333), SkBits2Float(0x42c9c572), SkBits2Float(0x434fe666), SkBits2Float(0x42c9c76d)); // 206.5f, 100.884f, 207.2f, 100.886f, 207.9f, 100.89f +path.cubicTo(SkBits2Float(0x4350999a), SkBits2Float(0x42c9c967), SkBits2Float(0x43514ccd), SkBits2Float(0x42c9cb6f), SkBits2Float(0x43520000), SkBits2Float(0x42c9cc3d)); // 208.6f, 100.893f, 209.3f, 100.897f, 210, 100.899f +path.cubicTo(SkBits2Float(0x4352b333), SkBits2Float(0x42c9cd0a), SkBits2Float(0x43536666), SkBits2Float(0x42c9cc85), SkBits2Float(0x4354199a), SkBits2Float(0x42c9cc3d)); // 210.7f, 100.9f, 211.4f, 100.899f, 212.1f, 100.899f +path.cubicTo(SkBits2Float(0x4354cccd), SkBits2Float(0x42c9cbf4), SkBits2Float(0x43558000), SkBits2Float(0x42c9c7f4), SkBits2Float(0x43563333), SkBits2Float(0x42c9ca8b)); // 212.8f, 100.898f, 213.5f, 100.891f, 214.2f, 100.896f +path.cubicTo(SkBits2Float(0x4356e666), SkBits2Float(0x42c9cd22), SkBits2Float(0x4357999a), SkBits2Float(0x42c9d0e6), SkBits2Float(0x43584ccd), SkBits2Float(0x42c9dbc6)); // 214.9f, 100.901f, 215.6f, 100.908f, 216.3f, 100.929f +path.cubicTo(SkBits2Float(0x43590000), SkBits2Float(0x42c9e6a7), SkBits2Float(0x4359b333), SkBits2Float(0x42c9fd4d), SkBits2Float(0x435a6666), SkBits2Float(0x42ca0bcf)); // 217, 100.95f, 217.7f, 100.995f, 218.4f, 101.023f +path.cubicTo(SkBits2Float(0x435b199a), SkBits2Float(0x42ca1a50), SkBits2Float(0x435bcccd), SkBits2Float(0x42ca29b9), SkBits2Float(0x435c8000), SkBits2Float(0x42ca32d0)); // 219.1f, 101.051f, 219.8f, 101.081f, 220.5f, 101.099f +path.cubicTo(SkBits2Float(0x435d3333), SkBits2Float(0x42ca3be7), SkBits2Float(0x435de666), SkBits2Float(0x42ca3af4), SkBits2Float(0x435e999a), SkBits2Float(0x42ca4259)); // 221.2f, 101.117f, 221.9f, 101.115f, 222.6f, 101.13f +path.cubicTo(SkBits2Float(0x435f4ccd), SkBits2Float(0x42ca49be), SkBits2Float(0x43600000), SkBits2Float(0x42ca541c), SkBits2Float(0x4360b333), SkBits2Float(0x42ca5f2d)); // 223.3f, 101.144f, 224, 101.164f, 224.7f, 101.186f +path.cubicTo(SkBits2Float(0x43616666), SkBits2Float(0x42ca6a3f), SkBits2Float(0x4362199a), SkBits2Float(0x42ca7a05), SkBits2Float(0x4362cccd), SkBits2Float(0x42ca84c3)); // 225.4f, 101.208f, 226.1f, 101.238f, 226.8f, 101.259f +path.cubicTo(SkBits2Float(0x43638000), SkBits2Float(0x42ca8f80), SkBits2Float(0x43643333), SkBits2Float(0x42ca9a0e), SkBits2Float(0x4364e666), SkBits2Float(0x42ca9f9e)); // 227.5f, 101.28f, 228.2f, 101.301f, 228.9f, 101.312f +path.cubicTo(SkBits2Float(0x4365999a), SkBits2Float(0x42caa52d), SkBits2Float(0x43664ccd), SkBits2Float(0x42caa78a), SkBits2Float(0x43670000), SkBits2Float(0x42caa620)); // 229.6f, 101.323f, 230.3f, 101.327f, 231, 101.324f +path.cubicTo(SkBits2Float(0x4367b333), SkBits2Float(0x42caa4b6), SkBits2Float(0x43686666), SkBits2Float(0x42ca9cbf), SkBits2Float(0x4369199a), SkBits2Float(0x42ca9723)); // 231.7f, 101.322f, 232.4f, 101.306f, 233.1f, 101.295f +path.cubicTo(SkBits2Float(0x4369cccd), SkBits2Float(0x42ca9188), SkBits2Float(0x436a8000), SkBits2Float(0x42ca894a), SkBits2Float(0x436b3333), SkBits2Float(0x42ca847c)); // 233.8f, 101.284f, 234.5f, 101.268f, 235.2f, 101.259f +path.cubicTo(SkBits2Float(0x436be666), SkBits2Float(0x42ca7fae), SkBits2Float(0x436c999a), SkBits2Float(0x42ca7c01), SkBits2Float(0x436d4ccd), SkBits2Float(0x42ca7a4f)); // 235.9f, 101.249f, 236.6f, 101.242f, 237.3f, 101.239f +path.cubicTo(SkBits2Float(0x436e0000), SkBits2Float(0x42ca789d), SkBits2Float(0x436eb333), SkBits2Float(0x42ca7976), SkBits2Float(0x436f6666), SkBits2Float(0x42ca7a4f)); // 238, 101.236f, 238.7f, 101.237f, 239.4f, 101.239f +path.cubicTo(SkBits2Float(0x4370199a), SkBits2Float(0x42ca7b28), SkBits2Float(0x4370cccd), SkBits2Float(0x42ca7ed5), SkBits2Float(0x43718000), SkBits2Float(0x42ca7f66)); // 240.1f, 101.241f, 240.8f, 101.248f, 241.5f, 101.249f +path.cubicTo(SkBits2Float(0x43723333), SkBits2Float(0x42ca7ff6), SkBits2Float(0x4372e666), SkBits2Float(0x42ca7e44), SkBits2Float(0x4373999a), SkBits2Float(0x42ca7db4)); // 242.2f, 101.25f, 242.9f, 101.247f, 243.6f, 101.246f +path.cubicTo(SkBits2Float(0x43744ccd), SkBits2Float(0x42ca7d23), SkBits2Float(0x43750000), SkBits2Float(0x42ca7c4a), SkBits2Float(0x4375b333), SkBits2Float(0x42ca7c01)); // 244.3f, 101.244f, 245, 101.243f, 245.7f, 101.242f +path.cubicTo(SkBits2Float(0x43766666), SkBits2Float(0x42ca7bb9), SkBits2Float(0x4377199a), SkBits2Float(0x42ca7bb9), SkBits2Float(0x4377cccd), SkBits2Float(0x42ca7c01)); // 246.4f, 101.242f, 247.1f, 101.242f, 247.8f, 101.242f +path.cubicTo(SkBits2Float(0x43788000), SkBits2Float(0x42ca7c4a), SkBits2Float(0x43793333), SkBits2Float(0x42ca7d6b), SkBits2Float(0x4379e666), SkBits2Float(0x42ca7db4)); // 248.5f, 101.243f, 249.2f, 101.245f, 249.9f, 101.246f +path.cubicTo(SkBits2Float(0x437a999a), SkBits2Float(0x42ca7dfc), SkBits2Float(0x437b4ccd), SkBits2Float(0x42ca7dfc), SkBits2Float(0x437c0000), SkBits2Float(0x42ca7db4)); // 250.6f, 101.246f, 251.3f, 101.246f, 252, 101.246f +path.cubicTo(SkBits2Float(0x437cb333), SkBits2Float(0x42ca7d6b), SkBits2Float(0x437d6666), SkBits2Float(0x42ca7c4a), SkBits2Float(0x437e199a), SkBits2Float(0x42ca7c01)); // 252.7f, 101.245f, 253.4f, 101.243f, 254.1f, 101.242f +path.cubicTo(SkBits2Float(0x437ecccd), SkBits2Float(0x42ca7bb9), SkBits2Float(0x437f8000), SkBits2Float(0x42ca7bb9), SkBits2Float(0x4380199a), SkBits2Float(0x42ca7c01)); // 254.8f, 101.242f, 255.5f, 101.242f, 256.2f, 101.242f +path.cubicTo(SkBits2Float(0x43807333), SkBits2Float(0x42ca7c4a), SkBits2Float(0x4380cccd), SkBits2Float(0x42ca7d6b), SkBits2Float(0x43812666), SkBits2Float(0x42ca7db4)); // 256.9f, 101.243f, 257.6f, 101.245f, 258.3f, 101.246f +path.cubicTo(SkBits2Float(0x43818000), SkBits2Float(0x42ca7dfc), SkBits2Float(0x4381d99a), SkBits2Float(0x42ca7db4), SkBits2Float(0x43823333), SkBits2Float(0x42ca7db4)); // 259, 101.246f, 259.7f, 101.246f, 260.4f, 101.246f +path.cubicTo(SkBits2Float(0x43828ccd), SkBits2Float(0x42ca7db4), SkBits2Float(0x4382e666), SkBits2Float(0x42ca7db4), SkBits2Float(0x43834000), SkBits2Float(0x42ca7db4)); // 261.1f, 101.246f, 261.8f, 101.246f, 262.5f, 101.246f +path.cubicTo(SkBits2Float(0x4383999a), SkBits2Float(0x42ca7db4), SkBits2Float(0x4383f333), SkBits2Float(0x42ca7d6b), SkBits2Float(0x43844ccd), SkBits2Float(0x42ca7db4)); // 263.2f, 101.246f, 263.9f, 101.245f, 264.6f, 101.246f +path.cubicTo(SkBits2Float(0x4384a666), SkBits2Float(0x42ca7dfc), SkBits2Float(0x43850000), SkBits2Float(0x42ca7f1d), SkBits2Float(0x4385599a), SkBits2Float(0x42ca7f66)); // 265.3f, 101.246f, 266, 101.248f, 266.7f, 101.249f +path.cubicTo(SkBits2Float(0x4385b333), SkBits2Float(0x42ca7fae), SkBits2Float(0x43860ccd), SkBits2Float(0x42ca7fae), SkBits2Float(0x43866666), SkBits2Float(0x42ca7f66)); // 267.4f, 101.249f, 268.1f, 101.249f, 268.8f, 101.249f +path.cubicTo(SkBits2Float(0x4386c000), SkBits2Float(0x42ca7f1d), SkBits2Float(0x4387199a), SkBits2Float(0x42ca7e44), SkBits2Float(0x43877333), SkBits2Float(0x42ca7db4)); // 269.5f, 101.248f, 270.2f, 101.247f, 270.9f, 101.246f +path.cubicTo(SkBits2Float(0x4387cccd), SkBits2Float(0x42ca7d23), SkBits2Float(0x43882666), SkBits2Float(0x42ca7c4a), SkBits2Float(0x43888000), SkBits2Float(0x42ca7c01)); // 271.6f, 101.244f, 272.3f, 101.243f, 273, 101.242f +path.cubicTo(SkBits2Float(0x4388d99a), SkBits2Float(0x42ca7bb9), SkBits2Float(0x43893333), SkBits2Float(0x42ca7c92), SkBits2Float(0x43898ccd), SkBits2Float(0x42ca7c01)); // 273.7f, 101.242f, 274.4f, 101.243f, 275.1f, 101.242f +path.cubicTo(SkBits2Float(0x4389e666), SkBits2Float(0x42ca7b71), SkBits2Float(0x438a4000), SkBits2Float(0x42ca789d), SkBits2Float(0x438a999a), SkBits2Float(0x42ca789d)); // 275.8f, 101.241f, 276.5f, 101.236f, 277.2f, 101.236f +path.cubicTo(SkBits2Float(0x438af333), SkBits2Float(0x42ca789d), SkBits2Float(0x438b4ccd), SkBits2Float(0x42ca78da), SkBits2Float(0x438ba666), SkBits2Float(0x42ca7c01)); // 277.9f, 101.236f, 278.6f, 101.236f, 279.3f, 101.242f +path.cubicTo(SkBits2Float(0x438c0000), SkBits2Float(0x42ca7f29), SkBits2Float(0x438c599a), SkBits2Float(0x42ca8863), SkBits2Float(0x438cb333), SkBits2Float(0x42ca8b8b)); // 280, 101.248f, 280.7f, 101.266f, 281.4f, 101.273f +path.cubicTo(SkBits2Float(0x438d0ccd), SkBits2Float(0x42ca8eb3), SkBits2Float(0x438d6666), SkBits2Float(0x42ca9411), SkBits2Float(0x438dc000), SkBits2Float(0x42ca8eef)); // 282.1f, 101.279f, 282.8f, 101.289f, 283.5f, 101.279f +path.cubicTo(SkBits2Float(0x438e199a), SkBits2Float(0x42ca89cd), SkBits2Float(0x438e7333), SkBits2Float(0x42ca7cb5), SkBits2Float(0x438ecccd), SkBits2Float(0x42ca6cbe)); // 284.2f, 101.269f, 284.9f, 101.244f, 285.6f, 101.212f +path.cubicTo(SkBits2Float(0x438f2666), SkBits2Float(0x42ca5cc7), SkBits2Float(0x438f8000), SkBits2Float(0x42ca4681), SkBits2Float(0x438fd99a), SkBits2Float(0x42ca2f25)); // 286.3f, 101.181f, 287, 101.138f, 287.7f, 101.092f +path.cubicTo(SkBits2Float(0x43903333), SkBits2Float(0x42ca17c9), SkBits2Float(0x43908ccd), SkBits2Float(0x42c9f983), SkBits2Float(0x4390e666), SkBits2Float(0x42c9e096)); // 288.4f, 101.046f, 289.1f, 100.987f, 289.8f, 100.939f +path.cubicTo(SkBits2Float(0x43914000), SkBits2Float(0x42c9c7aa), SkBits2Float(0x4391999a), SkBits2Float(0x42c9a56f), SkBits2Float(0x4391f333), SkBits2Float(0x42c9999a)); // 290.5f, 100.89f, 291.2f, 100.823f, 291.9f, 100.8f +path.cubicTo(SkBits2Float(0x43924ccd), SkBits2Float(0x42c98dc6), SkBits2Float(0x4392a666), SkBits2Float(0x42c9999a), SkBits2Float(0x43930000), SkBits2Float(0x42c9999a)); // 292.6f, 100.777f, 293.3f, 100.8f, 294, 100.8f +path.lineTo(SkBits2Float(0x43930000), SkBits2Float(0x42c9999a)); // 294, 100.8f +path.lineTo(SkBits2Float(0x43478000), SkBits2Float(0x42c9999a)); // 199.5f, 100.8f +path.close(); +path.moveTo(SkBits2Float(0x43ab2666), SkBits2Float(0x42c9999a)); // 342.3f, 100.8f +path.lineTo(SkBits2Float(0x43ab2666), SkBits2Float(0x42c9999a)); // 342.3f, 100.8f +path.cubicTo(SkBits2Float(0x43ab8000), SkBits2Float(0x42c9999a), SkBits2Float(0x43abd99a), SkBits2Float(0x42c98e0e), SkBits2Float(0x43ac3333), SkBits2Float(0x42c9999a)); // 343, 100.8f, 343.7f, 100.777f, 344.4f, 100.8f +path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42c9a527), SkBits2Float(0x43ace666), SkBits2Float(0x42c9bd29), SkBits2Float(0x43ad4000), SkBits2Float(0x42c9dee4)); // 345.1f, 100.823f, 345.8f, 100.869f, 346.5f, 100.935f +path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42ca009f), SkBits2Float(0x43adf333), SkBits2Float(0x42ca4737), SkBits2Float(0x43ae4ccd), SkBits2Float(0x42ca63fd)); // 347.2f, 101.001f, 347.9f, 101.139f, 348.6f, 101.195f +path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x42ca80c4), SkBits2Float(0x43af0000), SkBits2Float(0x42ca834d), SkBits2Float(0x43af599a), SkBits2Float(0x42ca8b8b)); // 349.3f, 101.251f, 350, 101.256f, 350.7f, 101.273f +path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x42ca93c9), SkBits2Float(0x43b00ccd), SkBits2Float(0x42ca92a9), SkBits2Float(0x43b06666), SkBits2Float(0x42ca9571)); // 351.4f, 101.289f, 352.1f, 101.286f, 352.8f, 101.292f +path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x42ca9839), SkBits2Float(0x43b1199a), SkBits2Float(0x42ca9ad0), SkBits2Float(0x43b17333), SkBits2Float(0x42ca9c3a)); // 353.5f, 101.297f, 354.2f, 101.302f, 354.9f, 101.305f +path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42ca9da3), SkBits2Float(0x43b22666), SkBits2Float(0x42ca9da3), SkBits2Float(0x43b28000), SkBits2Float(0x42ca9dec)); // 355.6f, 101.308f, 356.3f, 101.308f, 357, 101.308f +path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x42ca9e34), SkBits2Float(0x43b33333), SkBits2Float(0x42ca9e34), SkBits2Float(0x43b38ccd), SkBits2Float(0x42ca9dec)); // 357.7f, 101.309f, 358.4f, 101.309f, 359.1f, 101.308f +path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x42ca9da3), SkBits2Float(0x43b44000), SkBits2Float(0x42ca9c82), SkBits2Float(0x43b4999a), SkBits2Float(0x42ca9c3a)); // 359.8f, 101.308f, 360.5f, 101.306f, 361.2f, 101.305f +path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x42ca9bf1), SkBits2Float(0x43b54ccd), SkBits2Float(0x42ca9c3a), SkBits2Float(0x43b5a666), SkBits2Float(0x42ca9c3a)); // 361.9f, 101.305f, 362.6f, 101.305f, 363.3f, 101.305f +path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x42ca9c3a), SkBits2Float(0x43b6599a), SkBits2Float(0x42ca9bf1), SkBits2Float(0x43b6b333), SkBits2Float(0x42ca9c3a)); // 364, 101.305f, 364.7f, 101.305f, 365.4f, 101.305f +path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x42ca9c82), SkBits2Float(0x43b76666), SkBits2Float(0x42ca9da3), SkBits2Float(0x43b7c000), SkBits2Float(0x42ca9dec)); // 366.1f, 101.306f, 366.8f, 101.308f, 367.5f, 101.308f +path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x42ca9e34), SkBits2Float(0x43b87333), SkBits2Float(0x42ca9dec), SkBits2Float(0x43b8cccd), SkBits2Float(0x42ca9dec)); // 368.2f, 101.309f, 368.9f, 101.308f, 369.6f, 101.308f +path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x42ca9dec), SkBits2Float(0x43b98000), SkBits2Float(0x42ca9dec), SkBits2Float(0x43b9d99a), SkBits2Float(0x42ca9dec)); // 370.3f, 101.308f, 371, 101.308f, 371.7f, 101.308f +path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x42ca9dec), SkBits2Float(0x43ba8ccd), SkBits2Float(0x42ca9e7d), SkBits2Float(0x43bae666), SkBits2Float(0x42ca9dec)); // 372.4f, 101.308f, 373.1f, 101.31f, 373.8f, 101.308f +path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x42ca9d5b), SkBits2Float(0x43bb999a), SkBits2Float(0x42ca9b61), SkBits2Float(0x43bbf333), SkBits2Float(0x42ca9a88)); // 374.5f, 101.307f, 375.2f, 101.303f, 375.9f, 101.302f +path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x42ca99af), SkBits2Float(0x43bca666), SkBits2Float(0x42ca991e), SkBits2Float(0x43bd0000), SkBits2Float(0x42ca98d5)); // 376.6f, 101.3f, 377.3f, 101.299f, 378, 101.299f +path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x42ca988d), SkBits2Float(0x43bdb333), SkBits2Float(0x42ca988d), SkBits2Float(0x43be0ccd), SkBits2Float(0x42ca98d5)); // 378.7f, 101.298f, 379.4f, 101.298f, 380.1f, 101.299f +path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x42ca991e), SkBits2Float(0x43bec000), SkBits2Float(0x42ca99f7), SkBits2Float(0x43bf199a), SkBits2Float(0x42ca9a88)); // 380.8f, 101.299f, 381.5f, 101.301f, 382.2f, 101.302f +path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x42ca9b18), SkBits2Float(0x43bfcccd), SkBits2Float(0x42ca9ba9), SkBits2Float(0x43c02666), SkBits2Float(0x42ca9c3a)); // 382.9f, 101.303f, 383.6f, 101.304f, 384.3f, 101.305f +path.cubicTo(SkBits2Float(0x43c08000), SkBits2Float(0x42ca9cca), SkBits2Float(0x43c0d99a), SkBits2Float(0x42ca9ec5), SkBits2Float(0x43c13333), SkBits2Float(0x42ca9dec)); // 385, 101.306f, 385.7f, 101.31f, 386.4f, 101.308f +path.cubicTo(SkBits2Float(0x43c18ccd), SkBits2Float(0x42ca9d13), SkBits2Float(0x43c1e666), SkBits2Float(0x42ca9a3f), SkBits2Float(0x43c24000), SkBits2Float(0x42ca9723)); // 387.1f, 101.307f, 387.8f, 101.301f, 388.5f, 101.295f +path.cubicTo(SkBits2Float(0x43c2999a), SkBits2Float(0x42ca9407), SkBits2Float(0x43c2f333), SkBits2Float(0x42ca8d87), SkBits2Float(0x43c34ccd), SkBits2Float(0x42ca8b45)); // 389.2f, 101.289f, 389.9f, 101.276f, 390.6f, 101.272f +path.cubicTo(SkBits2Float(0x43c3a666), SkBits2Float(0x42ca8902), SkBits2Float(0x43c40000), SkBits2Float(0x42ca8992), SkBits2Float(0x43c4599a), SkBits2Float(0x42ca8992)); // 391.3f, 101.268f, 392, 101.269f, 392.7f, 101.269f +path.cubicTo(SkBits2Float(0x43c4b333), SkBits2Float(0x42ca8992), SkBits2Float(0x43c50ccd), SkBits2Float(0x42ca8a6b), SkBits2Float(0x43c56666), SkBits2Float(0x42ca8b45)); // 393.4f, 101.269f, 394.1f, 101.27f, 394.8f, 101.272f +path.cubicTo(SkBits2Float(0x43c5c000), SkBits2Float(0x42ca8c1e), SkBits2Float(0x43c6199a), SkBits2Float(0x42ca8d87), SkBits2Float(0x43c67333), SkBits2Float(0x42ca8ea9)); // 395.5f, 101.274f, 396.2f, 101.276f, 396.9f, 101.279f +path.cubicTo(SkBits2Float(0x43c6cccd), SkBits2Float(0x42ca8fca), SkBits2Float(0x43c72666), SkBits2Float(0x42ca90a3), SkBits2Float(0x43c78000), SkBits2Float(0x42ca920d)); // 397.6f, 101.281f, 398.3f, 101.282f, 399, 101.285f +path.cubicTo(SkBits2Float(0x43c7d99a), SkBits2Float(0x42ca9377), SkBits2Float(0x43c83333), SkBits2Float(0x42ca9571), SkBits2Float(0x43c88ccd), SkBits2Float(0x42ca9723)); // 399.7f, 101.288f, 400.4f, 101.292f, 401.1f, 101.295f +path.cubicTo(SkBits2Float(0x43c8e666), SkBits2Float(0x42ca98d5), SkBits2Float(0x43c94000), SkBits2Float(0x42ca9a88), SkBits2Float(0x43c9999a), SkBits2Float(0x42ca9c3a)); // 401.8f, 101.299f, 402.5f, 101.302f, 403.2f, 101.305f +path.cubicTo(SkBits2Float(0x43c9f333), SkBits2Float(0x42ca9dec), SkBits2Float(0x43ca4ccd), SkBits2Float(0x42caa02f), SkBits2Float(0x43caa666), SkBits2Float(0x42caa150)); // 403.9f, 101.308f, 404.6f, 101.313f, 405.3f, 101.315f +path.cubicTo(SkBits2Float(0x43cb0000), SkBits2Float(0x42caa271), SkBits2Float(0x43cb599a), SkBits2Float(0x42caa46c), SkBits2Float(0x43cbb333), SkBits2Float(0x42caa302)); // 406, 101.317f, 406.7f, 101.321f, 407.4f, 101.318f +path.cubicTo(SkBits2Float(0x43cc0ccd), SkBits2Float(0x42caa198), SkBits2Float(0x43cc6666), SkBits2Float(0x42ca9ad0), SkBits2Float(0x43ccc000), SkBits2Float(0x42ca98d5)); // 408.1f, 101.316f, 408.8f, 101.302f, 409.5f, 101.299f +path.cubicTo(SkBits2Float(0x43cd199a), SkBits2Float(0x42ca96db), SkBits2Float(0x43cd7333), SkBits2Float(0x42ca9535), SkBits2Float(0x43cdcccd), SkBits2Float(0x42ca9723)); // 410.2f, 101.295f, 410.9f, 101.291f, 411.6f, 101.295f +path.cubicTo(SkBits2Float(0x43ce2666), SkBits2Float(0x42ca9912), SkBits2Float(0x43ce8000), SkBits2Float(0x42caa19a), SkBits2Float(0x43ced99a), SkBits2Float(0x42caa46e)); // 412.3f, 101.299f, 413, 101.316f, 413.7f, 101.321f +path.cubicTo(SkBits2Float(0x43cf3333), SkBits2Float(0x42caa741), SkBits2Float(0x43cf8ccd), SkBits2Float(0x42caa7c4), SkBits2Float(0x43cfe666), SkBits2Float(0x42caa819)); // 414.4f, 101.327f, 415.1f, 101.328f, 415.8f, 101.328f +path.cubicTo(SkBits2Float(0x43d04000), SkBits2Float(0x42caa86d), SkBits2Float(0x43d0999a), SkBits2Float(0x42caa788), SkBits2Float(0x43d0f333), SkBits2Float(0x42caa666)); // 416.5f, 101.329f, 417.2f, 101.327f, 417.9f, 101.325f +path.cubicTo(SkBits2Float(0x43d14ccd), SkBits2Float(0x42caa545), SkBits2Float(0x43d1a666), SkBits2Float(0x42caa302), SkBits2Float(0x43d20000), SkBits2Float(0x42caa150)); // 418.6f, 101.323f, 419.3f, 101.318f, 420, 101.315f +path.cubicTo(SkBits2Float(0x43d2599a), SkBits2Float(0x42ca9f9e), SkBits2Float(0x43d2b333), SkBits2Float(0x42ca9d13), SkBits2Float(0x43d30ccd), SkBits2Float(0x42ca9c3a)); // 420.7f, 101.312f, 421.4f, 101.307f, 422.1f, 101.305f +path.cubicTo(SkBits2Float(0x43d36666), SkBits2Float(0x42ca9b61), SkBits2Float(0x43d3c000), SkBits2Float(0x42ca9b24), SkBits2Float(0x43d4199a), SkBits2Float(0x42ca9c3a)); // 422.8f, 101.303f, 423.5f, 101.303f, 424.2f, 101.305f +path.cubicTo(SkBits2Float(0x43d47333), SkBits2Float(0x42ca9d4f), SkBits2Float(0x43d4cccd), SkBits2Float(0x42caa59b), SkBits2Float(0x43d52666), SkBits2Float(0x42caa2bc)); // 424.9f, 101.307f, 425.6f, 101.323f, 426.3f, 101.318f +path.cubicTo(SkBits2Float(0x43d58000), SkBits2Float(0x42ca9fdc), SkBits2Float(0x43d5d99a), SkBits2Float(0x42caa425), SkBits2Float(0x43d63333), SkBits2Float(0x42ca8afe)); // 427, 101.312f, 427.7f, 101.321f, 428.4f, 101.271f +path.cubicTo(SkBits2Float(0x43d68ccd), SkBits2Float(0x42ca71d6), SkBits2Float(0x43d6e666), SkBits2Float(0x42ca340a), SkBits2Float(0x43d74000), SkBits2Float(0x42ca0bcf)); // 429.1f, 101.222f, 429.8f, 101.102f, 430.5f, 101.023f +path.cubicTo(SkBits2Float(0x43d7999a), SkBits2Float(0x42c9e394), SkBits2Float(0x43d7f333), SkBits2Float(0x42c9aca3), SkBits2Float(0x43d84ccd), SkBits2Float(0x42c9999a)); // 431.2f, 100.944f, 431.9f, 100.837f, 432.6f, 100.8f +path.cubicTo(SkBits2Float(0x43d8a666), SkBits2Float(0x42c98692), SkBits2Float(0x43d90000), SkBits2Float(0x42c9999a), SkBits2Float(0x43d9599a), SkBits2Float(0x42c9999a)); // 433.3f, 100.763f, 434, 100.8f, 434.7f, 100.8f +path.lineTo(SkBits2Float(0x43d9599a), SkBits2Float(0x42c9999a)); // 434.7f, 100.8f +path.lineTo(SkBits2Float(0x43ab2666), SkBits2Float(0x42c9999a)); // 342.3f, 100.8f +path.close(); +path.moveTo(SkBits2Float(0x43dfa666), SkBits2Float(0x42c9999a)); // 447.3f, 100.8f +path.lineTo(SkBits2Float(0x43dfa666), SkBits2Float(0x42c9999a)); // 447.3f, 100.8f +path.cubicTo(SkBits2Float(0x43e00000), SkBits2Float(0x42c99abd), SkBits2Float(0x43e0599a), SkBits2Float(0x42c9a022), SkBits2Float(0x43e0b333), SkBits2Float(0x42c9a06c)); // 448, 100.802f, 448.7f, 100.813f, 449.4f, 100.813f +path.cubicTo(SkBits2Float(0x43e10ccd), SkBits2Float(0x42c9a0b6), SkBits2Float(0x43e16666), SkBits2Float(0x42c99c79), SkBits2Float(0x43e1c000), SkBits2Float(0x42c99b56)); // 450.1f, 100.814f, 450.8f, 100.806f, 451.5f, 100.803f +path.cubicTo(SkBits2Float(0x43e2199a), SkBits2Float(0x42c99a33), SkBits2Float(0x43e27333), SkBits2Float(0x42c9928a), SkBits2Float(0x43e2cccd), SkBits2Float(0x42c9999a)); // 452.2f, 100.801f, 452.9f, 100.786f, 453.6f, 100.8f +path.cubicTo(SkBits2Float(0x43e32666), SkBits2Float(0x42c9a0ab), SkBits2Float(0x43e38000), SkBits2Float(0x42c9b682), SkBits2Float(0x43e3d99a), SkBits2Float(0x42c9c5bb)); // 454.3f, 100.814f, 455, 100.856f, 455.7f, 100.886f +path.cubicTo(SkBits2Float(0x43e43333), SkBits2Float(0x42c9d4f4), SkBits2Float(0x43e48ccd), SkBits2Float(0x42c9ef91), SkBits2Float(0x43e4e666), SkBits2Float(0x42c9f4f0)); // 456.4f, 100.916f, 457.1f, 100.968f, 457.8f, 100.978f +path.cubicTo(SkBits2Float(0x43e54000), SkBits2Float(0x42c9fa4e), SkBits2Float(0x43e5999a), SkBits2Float(0x42c9e940), SkBits2Float(0x43e5f333), SkBits2Float(0x42c9e5f3)); // 458.5f, 100.989f, 459.2f, 100.956f, 459.9f, 100.949f +path.cubicTo(SkBits2Float(0x43e64ccd), SkBits2Float(0x42c9e2a6), SkBits2Float(0x43e6a666), SkBits2Float(0x42c9e366), SkBits2Float(0x43e70000), SkBits2Float(0x42c9e123)); // 460.6f, 100.943f, 461.3f, 100.944f, 462, 100.94f +path.cubicTo(SkBits2Float(0x43e7599a), SkBits2Float(0x42c9dee0), SkBits2Float(0x43e7b333), SkBits2Float(0x42c9d9d8), SkBits2Float(0x43e80ccd), SkBits2Float(0x42c9d862)); // 462.7f, 100.935f, 463.4f, 100.925f, 464.1f, 100.923f +path.cubicTo(SkBits2Float(0x43e86666), SkBits2Float(0x42c9d6ed), SkBits2Float(0x43e8c000), SkBits2Float(0x42c9d93b), SkBits2Float(0x43e9199a), SkBits2Float(0x42c9d862)); // 464.8f, 100.92f, 465.5f, 100.924f, 466.2f, 100.923f +path.cubicTo(SkBits2Float(0x43e97333), SkBits2Float(0x42c9d789), SkBits2Float(0x43e9cccd), SkBits2Float(0x42c9d614), SkBits2Float(0x43ea2666), SkBits2Float(0x42c9d34c)); // 466.9f, 100.921f, 467.6f, 100.918f, 468.3f, 100.913f +path.cubicTo(SkBits2Float(0x43ea8000), SkBits2Float(0x42c9d084), SkBits2Float(0x43ead99a), SkBits2Float(0x42c9cbf1), SkBits2Float(0x43eb3333), SkBits2Float(0x42c9c7b4)); // 469, 100.907f, 469.7f, 100.898f, 470.4f, 100.89f +path.cubicTo(SkBits2Float(0x43eb8ccd), SkBits2Float(0x42c9c376), SkBits2Float(0x43ebe666), SkBits2Float(0x42c9bde8), SkBits2Float(0x43ec4000), SkBits2Float(0x42c9b9dc)); // 471.1f, 100.882f, 471.8f, 100.871f, 472.5f, 100.863f +path.cubicTo(SkBits2Float(0x43ec999a), SkBits2Float(0x42c9b5cf), SkBits2Float(0x43ecf333), SkBits2Float(0x42c9b442), SkBits2Float(0x43ed4ccd), SkBits2Float(0x42c9af69)); // 473.2f, 100.855f, 473.9f, 100.852f, 474.6f, 100.843f +path.cubicTo(SkBits2Float(0x43eda666), SkBits2Float(0x42c9aa8f), SkBits2Float(0x43ee0000), SkBits2Float(0x42c9a064), SkBits2Float(0x43ee599a), SkBits2Float(0x42c99cc1)); // 475.3f, 100.833f, 476, 100.813f, 476.7f, 100.806f +path.cubicTo(SkBits2Float(0x43eeb333), SkBits2Float(0x42c9991f), SkBits2Float(0x43ef0ccd), SkBits2Float(0x42c99758), SkBits2Float(0x43ef6666), SkBits2Float(0x42c9999a)); // 477.4f, 100.799f, 478.1f, 100.796f, 478.8f, 100.8f +path.cubicTo(SkBits2Float(0x43efc000), SkBits2Float(0x42c99bdd), SkBits2Float(0x43f0199a), SkBits2Float(0x42c99f0e), SkBits2Float(0x43f07333), SkBits2Float(0x42c9aa52)); // 479.5f, 100.804f, 480.2f, 100.811f, 480.9f, 100.833f +path.cubicTo(SkBits2Float(0x43f0cccd), SkBits2Float(0x42c9b596), SkBits2Float(0x43f12666), SkBits2Float(0x42c9cfde), SkBits2Float(0x43f18000), SkBits2Float(0x42c9dd32)); // 481.6f, 100.855f, 482.3f, 100.906f, 483, 100.932f +path.cubicTo(SkBits2Float(0x43f1d99a), SkBits2Float(0x42c9ea86), SkBits2Float(0x43f23333), SkBits2Float(0x42c9f451), SkBits2Float(0x43f28ccd), SkBits2Float(0x42c9fa4d)); // 483.7f, 100.958f, 484.4f, 100.977f, 485.1f, 100.989f +path.cubicTo(SkBits2Float(0x43f2e666), SkBits2Float(0x42ca0048), SkBits2Float(0x43f34000), SkBits2Float(0x42c9fbf3), SkBits2Float(0x43f3999a), SkBits2Float(0x42ca0115)); // 485.8f, 101.001f, 486.5f, 100.992f, 487.2f, 101.002f +path.cubicTo(SkBits2Float(0x43f3f333), SkBits2Float(0x42ca0637), SkBits2Float(0x43f44ccd), SkBits2Float(0x42ca103f), SkBits2Float(0x43f4a666), SkBits2Float(0x42ca1919)); // 487.9f, 101.012f, 488.6f, 101.032f, 489.3f, 101.049f +path.cubicTo(SkBits2Float(0x43f50000), SkBits2Float(0x42ca21f4), SkBits2Float(0x43f5599a), SkBits2Float(0x42ca2e87), SkBits2Float(0x43f5b333), SkBits2Float(0x42ca3634)); // 490, 101.066f, 490.7f, 101.091f, 491.4f, 101.106f +path.cubicTo(SkBits2Float(0x43f60ccd), SkBits2Float(0x42ca3de1), SkBits2Float(0x43f66666), SkBits2Float(0x42ca43b9), SkBits2Float(0x43f6c000), SkBits2Float(0x42ca4729)); // 492.1f, 101.121f, 492.8f, 101.132f, 493.5f, 101.139f +path.cubicTo(SkBits2Float(0x43f7199a), SkBits2Float(0x42ca4a99), SkBits2Float(0x43f77333), SkBits2Float(0x42ca49b3), SkBits2Float(0x43f7cccd), SkBits2Float(0x42ca4ad4)); // 494.2f, 101.146f, 494.9f, 101.144f, 495.6f, 101.146f +path.cubicTo(SkBits2Float(0x43f82666), SkBits2Float(0x42ca4bf5), SkBits2Float(0x43f88000), SkBits2Float(0x42ca4cdc), SkBits2Float(0x43f8d99a), SkBits2Float(0x42ca4df2)); // 496.3f, 101.148f, 497, 101.15f, 497.7f, 101.152f +path.cubicTo(SkBits2Float(0x43f93333), SkBits2Float(0x42ca4f07), SkBits2Float(0x43f98ccd), SkBits2Float(0x42ca4fec), SkBits2Float(0x43f9e666), SkBits2Float(0x42ca5156)); // 498.4f, 101.154f, 499.1f, 101.156f, 499.8f, 101.159f +path.cubicTo(SkBits2Float(0x43fa4000), SkBits2Float(0x42ca52c0), SkBits2Float(0x43fa999a), SkBits2Float(0x42ca53e1), SkBits2Float(0x43faf333), SkBits2Float(0x42ca566c)); // 500.5f, 101.162f, 501.2f, 101.164f, 501.9f, 101.169f +path.cubicTo(SkBits2Float(0x43fb4ccd), SkBits2Float(0x42ca58f7), SkBits2Float(0x43fba666), SkBits2Float(0x42ca5d71), SkBits2Float(0x43fc0000), SkBits2Float(0x42ca6099)); // 502.6f, 101.174f, 503.3f, 101.183f, 504, 101.189f +path.cubicTo(SkBits2Float(0x43fc599a), SkBits2Float(0x42ca63c1), SkBits2Float(0x43fcb333), SkBits2Float(0x42ca663e), SkBits2Float(0x43fd0ccd), SkBits2Float(0x42ca695a)); // 504.7f, 101.195f, 505.4f, 101.2f, 506.1f, 101.206f +path.cubicTo(SkBits2Float(0x43fd6666), SkBits2Float(0x42ca6c76), SkBits2Float(0x43fdc000), SkBits2Float(0x42ca7024), SkBits2Float(0x43fe199a), SkBits2Float(0x42ca7340)); // 506.8f, 101.212f, 507.5f, 101.219f, 508.2f, 101.225f +path.cubicTo(SkBits2Float(0x43fe7333), SkBits2Float(0x42ca765c), SkBits2Float(0x43fecccd), SkBits2Float(0x42ca7a44), SkBits2Float(0x43ff2666), SkBits2Float(0x42ca7c01)); // 508.9f, 101.231f, 509.6f, 101.239f, 510.3f, 101.242f +path.cubicTo(SkBits2Float(0x43ff8000), SkBits2Float(0x42ca7dbf), SkBits2Float(0x43ffd99a), SkBits2Float(0x42ca7ed5), SkBits2Float(0x4400199a), SkBits2Float(0x42ca7db4)); // 511, 101.246f, 511.7f, 101.248f, 512.4f, 101.246f +path.cubicTo(SkBits2Float(0x44004666), SkBits2Float(0x42ca7c92), SkBits2Float(0x44007333), SkBits2Float(0x42ca780c), SkBits2Float(0x4400a000), SkBits2Float(0x42ca7539)); // 513.1f, 101.243f, 513.8f, 101.234f, 514.5f, 101.229f +path.cubicTo(SkBits2Float(0x4400cccd), SkBits2Float(0x42ca7265), SkBits2Float(0x4400f99a), SkBits2Float(0x42ca7017), SkBits2Float(0x44012666), SkBits2Float(0x42ca6cbe)); // 515.2f, 101.223f, 515.9f, 101.219f, 516.6f, 101.212f +path.cubicTo(SkBits2Float(0x44015333), SkBits2Float(0x42ca6966), SkBits2Float(0x44018000), SkBits2Float(0x42ca688b), SkBits2Float(0x4401accd), SkBits2Float(0x42ca6126)); // 517.3f, 101.206f, 518, 101.204f, 518.7f, 101.19f +path.cubicTo(SkBits2Float(0x4401d99a), SkBits2Float(0x42ca59c1), SkBits2Float(0x44020666), SkBits2Float(0x42ca4eee), SkBits2Float(0x44023333), SkBits2Float(0x42ca4061)); // 519.4f, 101.175f, 520.1f, 101.154f, 520.8f, 101.126f +path.cubicTo(SkBits2Float(0x44026000), SkBits2Float(0x42ca31d3), SkBits2Float(0x44028ccd), SkBits2Float(0x42ca1b74), SkBits2Float(0x4402b99a), SkBits2Float(0x42ca09d6)); // 521.5f, 101.097f, 522.2f, 101.054f, 522.9f, 101.019f +path.cubicTo(SkBits2Float(0x4402e666), SkBits2Float(0x42c9f839), SkBits2Float(0x44031333), SkBits2Float(0x42c9e720), SkBits2Float(0x44034000), SkBits2Float(0x42c9d6b0)); // 523.6f, 100.985f, 524.3f, 100.951f, 525, 100.919f +path.cubicTo(SkBits2Float(0x44036ccd), SkBits2Float(0x42c9c640), SkBits2Float(0x4403999a), SkBits2Float(0x42c9b163), SkBits2Float(0x4403c666), SkBits2Float(0x42c9a735)); // 525.7f, 100.887f, 526.4f, 100.846f, 527.1f, 100.827f +path.cubicTo(SkBits2Float(0x4403f333), SkBits2Float(0x42c99d06), SkBits2Float(0x44042000), SkBits2Float(0x42c99bdf), SkBits2Float(0x44044ccd), SkBits2Float(0x42c9999a)); // 527.8f, 100.807f, 528.5f, 100.804f, 529.2f, 100.8f +path.lineTo(SkBits2Float(0x44044ccd), SkBits2Float(0x42c9999a)); // 529.2f, 100.8f +path.lineTo(SkBits2Float(0x43dfa666), SkBits2Float(0x42c9999a)); // 447.3f, 100.8f +path.close(); + return path; +} + +static void issue3651_1(skiatest::Reporter* reporter, const char* filename) { + SkPath path = path1(); + SkPath pathB = path2(); + // DEBUG_UNDER_DEVELOPMENT issue3651_1 disable expectation check for now + testPathOpCheck(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename, + !SkOpGlobalState::DebugRunFail()); +} + +static void issue3651_2(skiatest::Reporter* reporter, const char* filename) { + SkPath path = path3(); + SkPath pathB = path4(); + testPathOp(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename); +} + +static void issue3651_3(skiatest::Reporter* reporter, const char* filename) { + SkPath path = path5(); + SkPath pathB = path6(); + testPathOp(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename); +} + +static void issue3651_4(skiatest::Reporter* reporter, const char* filename) { +SkPath path; +path.moveTo(SkBits2Float(0x42033333), SkBits2Float(0x43346666)); // 32.8f, 180.4f +path.lineTo(SkBits2Float(0x42033333), SkBits2Float(0x43346666)); // 32.8f, 180.4f +path.cubicTo(SkBits2Float(0x4205eeef), SkBits2Float(0x43346666), SkBits2Float(0x4208aaab), SkBits2Float(0x4334780f), SkBits2Float(0x420b6666), SkBits2Float(0x43346666)); // 33.4833f, 180.4f, 34.1667f, 180.469f, 34.85f, 180.4f +path.cubicTo(SkBits2Float(0x420e2222), SkBits2Float(0x433454be), SkBits2Float(0x4210ddde), SkBits2Float(0x43340d56), SkBits2Float(0x4213999a), SkBits2Float(0x4333fc72)); // 35.5333f, 180.331f, 36.2167f, 180.052f, 36.9f, 179.986f +path.cubicTo(SkBits2Float(0x42165555), SkBits2Float(0x4333eb8e), SkBits2Float(0x42191111), SkBits2Float(0x4333fbf4), SkBits2Float(0x421bcccd), SkBits2Float(0x4334010f)); // 37.5833f, 179.92f, 38.2667f, 179.984f, 38.95f, 180.004f +path.cubicTo(SkBits2Float(0x421e8889), SkBits2Float(0x4334062b), SkBits2Float(0x42214444), SkBits2Float(0x43341213), SkBits2Float(0x42240000), SkBits2Float(0x43341b17)); // 39.6333f, 180.024f, 40.3167f, 180.071f, 41, 180.106f +path.cubicTo(SkBits2Float(0x4226bbbc), SkBits2Float(0x4334241b), SkBits2Float(0x42297777), SkBits2Float(0x4334339e), SkBits2Float(0x422c3333), SkBits2Float(0x43343728)); // 41.6833f, 180.141f, 42.3667f, 180.202f, 43.05f, 180.215f +path.cubicTo(SkBits2Float(0x422eeeef), SkBits2Float(0x43343ab2), SkBits2Float(0x4231aaab), SkBits2Float(0x4334337f), SkBits2Float(0x42346666), SkBits2Float(0x43343054)); // 43.7333f, 180.229f, 44.4167f, 180.201f, 45.1f, 180.189f +path.cubicTo(SkBits2Float(0x42372222), SkBits2Float(0x43342d28), SkBits2Float(0x4239ddde), SkBits2Float(0x43342281), SkBits2Float(0x423c999a), SkBits2Float(0x43342423)); // 45.7833f, 180.176f, 46.4667f, 180.135f, 47.15f, 180.141f +path.cubicTo(SkBits2Float(0x423f5555), SkBits2Float(0x433425c5), SkBits2Float(0x42421111), SkBits2Float(0x43343381), SkBits2Float(0x4244cccd), SkBits2Float(0x43343a1f)); // 47.8333f, 180.148f, 48.5167f, 180.201f, 49.2f, 180.227f +path.cubicTo(SkBits2Float(0x42478889), SkBits2Float(0x433440be), SkBits2Float(0x424a4444), SkBits2Float(0x43344d5d), SkBits2Float(0x424d0000), SkBits2Float(0x43344bdb)); // 49.8833f, 180.253f, 50.5667f, 180.302f, 51.25f, 180.296f +path.cubicTo(SkBits2Float(0x424fbbbc), SkBits2Float(0x43344a59), SkBits2Float(0x42527777), SkBits2Float(0x43342ca7), SkBits2Float(0x42553333), SkBits2Float(0x43343113)); // 51.9333f, 180.29f, 52.6167f, 180.174f, 53.3f, 180.192f +path.cubicTo(SkBits2Float(0x4257eeef), SkBits2Float(0x43343580), SkBits2Float(0x425aaaab), SkBits2Float(0x4334654b), SkBits2Float(0x425d6666), SkBits2Float(0x43346666)); // 53.9833f, 180.209f, 54.6667f, 180.396f, 55.35f, 180.4f +path.cubicTo(SkBits2Float(0x42602222), SkBits2Float(0x43346782), SkBits2Float(0x4262ddde), SkBits2Float(0x43343ee4), SkBits2Float(0x4265999a), SkBits2Float(0x433437ba)); // 56.0333f, 180.404f, 56.7167f, 180.246f, 57.4f, 180.218f +path.cubicTo(SkBits2Float(0x42685555), SkBits2Float(0x4334308f), SkBits2Float(0x426b1111), SkBits2Float(0x43343ce3), SkBits2Float(0x426dcccd), SkBits2Float(0x43343b69)); // 58.0833f, 180.19f, 58.7667f, 180.238f, 59.45f, 180.232f +path.cubicTo(SkBits2Float(0x42708889), SkBits2Float(0x433439ef), SkBits2Float(0x42734444), SkBits2Float(0x433437d8), SkBits2Float(0x42760000), SkBits2Float(0x43342edc)); // 60.1333f, 180.226f, 60.8167f, 180.218f, 61.5f, 180.183f +path.cubicTo(SkBits2Float(0x4278bbbc), SkBits2Float(0x433425df), SkBits2Float(0x427b7777), SkBits2Float(0x43340d4f), SkBits2Float(0x427e3333), SkBits2Float(0x4334057e)); // 62.1833f, 180.148f, 62.8667f, 180.052f, 63.55f, 180.021f +path.cubicTo(SkBits2Float(0x42807777), SkBits2Float(0x4333fdad), SkBits2Float(0x4281d555), SkBits2Float(0x4333fb55), SkBits2Float(0x42833333), SkBits2Float(0x4333fff3)); // 64.2333f, 179.991f, 64.9167f, 179.982f, 65.6f, 180 +path.cubicTo(SkBits2Float(0x42849111), SkBits2Float(0x43340492), SkBits2Float(0x4285eeef), SkBits2Float(0x43341020), SkBits2Float(0x42874ccd), SkBits2Float(0x43342133)); // 66.2833f, 180.018f, 66.9667f, 180.063f, 67.65f, 180.13f +path.cubicTo(SkBits2Float(0x4288aaab), SkBits2Float(0x43343246), SkBits2Float(0x428a0889), SkBits2Float(0x43345ade), SkBits2Float(0x428b6666), SkBits2Float(0x43346666)); // 68.3333f, 180.196f, 69.0167f, 180.355f, 69.7f, 180.4f +path.cubicTo(SkBits2Float(0x428cc444), SkBits2Float(0x433471ef), SkBits2Float(0x428e2222), SkBits2Float(0x433467af), SkBits2Float(0x428f8000), SkBits2Float(0x43346666)); // 70.3833f, 180.445f, 71.0667f, 180.405f, 71.75f, 180.4f +path.cubicTo(SkBits2Float(0x4290ddde), SkBits2Float(0x4334651e), SkBits2Float(0x42923bbc), SkBits2Float(0x43346442), SkBits2Float(0x4293999a), SkBits2Float(0x43345eb2)); // 72.4333f, 180.395f, 73.1167f, 180.392f, 73.8f, 180.37f +path.cubicTo(SkBits2Float(0x4294f777), SkBits2Float(0x43345922), SkBits2Float(0x42965555), SkBits2Float(0x43344bb4), SkBits2Float(0x4297b333), SkBits2Float(0x43344506)); // 74.4833f, 180.348f, 75.1667f, 180.296f, 75.85f, 180.27f +path.cubicTo(SkBits2Float(0x42991111), SkBits2Float(0x43343e58), SkBits2Float(0x429a6eef), SkBits2Float(0x433438a6), SkBits2Float(0x429bcccd), SkBits2Float(0x4334369e)); // 76.5333f, 180.244f, 77.2167f, 180.221f, 77.9f, 180.213f +path.cubicTo(SkBits2Float(0x429d2aab), SkBits2Float(0x43343496), SkBits2Float(0x429e8889), SkBits2Float(0x43343fb3), SkBits2Float(0x429fe666), SkBits2Float(0x433438d5)); // 78.5833f, 180.205f, 79.2667f, 180.249f, 79.95f, 180.222f +path.cubicTo(SkBits2Float(0x42a14444), SkBits2Float(0x433431f8), SkBits2Float(0x42a2a222), SkBits2Float(0x433411e5), SkBits2Float(0x42a40000), SkBits2Float(0x43340d6e)); // 80.6333f, 180.195f, 81.3167f, 180.07f, 82, 180.052f +path.cubicTo(SkBits2Float(0x42a55dde), SkBits2Float(0x433408f8), SkBits2Float(0x42a6bbbc), SkBits2Float(0x43341ae2), SkBits2Float(0x42a8199a), SkBits2Float(0x43341e0e)); // 82.6833f, 180.035f, 83.3667f, 180.105f, 84.05f, 180.117f +path.cubicTo(SkBits2Float(0x42a97777), SkBits2Float(0x43342139), SkBits2Float(0x42aad555), SkBits2Float(0x433427a8), SkBits2Float(0x42ac3333), SkBits2Float(0x43342073)); // 84.7333f, 180.13f, 85.4167f, 180.155f, 86.1f, 180.127f +path.cubicTo(SkBits2Float(0x42ad9111), SkBits2Float(0x4334193f), SkBits2Float(0x42aeeeef), SkBits2Float(0x4333fa48), SkBits2Float(0x42b04ccd), SkBits2Float(0x4333f2d5)); // 86.7833f, 180.099f, 87.4667f, 179.978f, 88.15f, 179.949f +path.cubicTo(SkBits2Float(0x42b1aaab), SkBits2Float(0x4333eb62), SkBits2Float(0x42b30889), SkBits2Float(0x4333f0fd), SkBits2Float(0x42b46666), SkBits2Float(0x4333f3c3)); // 88.8333f, 179.919f, 89.5167f, 179.941f, 90.2f, 179.952f +path.cubicTo(SkBits2Float(0x42b5c444), SkBits2Float(0x4333f688), SkBits2Float(0x42b72222), SkBits2Float(0x4333f7ca), SkBits2Float(0x42b88000), SkBits2Float(0x43340375)); // 90.8833f, 179.963f, 91.5667f, 179.968f, 92.25f, 180.014f +path.cubicTo(SkBits2Float(0x42b9ddde), SkBits2Float(0x43340f1f), SkBits2Float(0x42bb3bbc), SkBits2Float(0x43342b53), SkBits2Float(0x42bc999a), SkBits2Float(0x433439c3)); // 92.9333f, 180.059f, 93.6167f, 180.169f, 94.3f, 180.226f +path.cubicTo(SkBits2Float(0x42bdf777), SkBits2Float(0x43344833), SkBits2Float(0x42bf5555), SkBits2Float(0x43345473), SkBits2Float(0x42c0b333), SkBits2Float(0x43345a15)); // 94.9833f, 180.282f, 95.6667f, 180.33f, 96.35f, 180.352f +path.cubicTo(SkBits2Float(0x42c21111), SkBits2Float(0x43345fb6), SkBits2Float(0x42c36eef), SkBits2Float(0x433467f5), SkBits2Float(0x42c4cccd), SkBits2Float(0x43345b8d)); // 97.0333f, 180.374f, 97.7167f, 180.406f, 98.4f, 180.358f +path.cubicTo(SkBits2Float(0x42c62aab), SkBits2Float(0x43344f25), SkBits2Float(0x42c78889), SkBits2Float(0x43342bb0), SkBits2Float(0x42c8e666), SkBits2Float(0x43340fa6)); // 99.0833f, 180.309f, 99.7667f, 180.171f, 100.45f, 180.061f +path.cubicTo(SkBits2Float(0x42ca4444), SkBits2Float(0x4333f39b), SkBits2Float(0x42cba222), SkBits2Float(0x4333c2e0), SkBits2Float(0x42cd0000), SkBits2Float(0x4333b34d)); // 101.133f, 179.952f, 101.817f, 179.761f, 102.5f, 179.7f +path.cubicTo(SkBits2Float(0x42ce5dde), SkBits2Float(0x4333a3b9), SkBits2Float(0x42cfbbbc), SkBits2Float(0x4333b115), SkBits2Float(0x42d1199a), SkBits2Float(0x4333b231)); // 103.183f, 179.64f, 103.867f, 179.692f, 104.55f, 179.696f +path.cubicTo(SkBits2Float(0x42d27777), SkBits2Float(0x4333b34d), SkBits2Float(0x42d3d555), SkBits2Float(0x4333b6a0), SkBits2Float(0x42d53333), SkBits2Float(0x4333b9f3)); // 105.233f, 179.7f, 105.917f, 179.713f, 106.6f, 179.726f +path.cubicTo(SkBits2Float(0x42d69111), SkBits2Float(0x4333bd46), SkBits2Float(0x42d7eeef), SkBits2Float(0x4333c308), SkBits2Float(0x42d94ccd), SkBits2Float(0x4333c624)); // 107.283f, 179.739f, 107.967f, 179.762f, 108.65f, 179.774f +path.cubicTo(SkBits2Float(0x42daaaab), SkBits2Float(0x4333c940), SkBits2Float(0x42dc0889), SkBits2Float(0x4333b41c), SkBits2Float(0x42dd6666), SkBits2Float(0x4333cc9c)); // 109.333f, 179.786f, 110.017f, 179.704f, 110.7f, 179.799f +path.cubicTo(SkBits2Float(0x42dec444), SkBits2Float(0x4333e51d), SkBits2Float(0x42e02222), SkBits2Float(0x43343f85), SkBits2Float(0x42e18000), SkBits2Float(0x43345927)); // 111.383f, 179.895f, 112.067f, 180.248f, 112.75f, 180.348f +path.cubicTo(SkBits2Float(0x42e2ddde), SkBits2Float(0x433472c9), SkBits2Float(0x42e43bbc), SkBits2Float(0x43346431), SkBits2Float(0x42e5999a), SkBits2Float(0x43346666)); // 113.433f, 180.448f, 114.117f, 180.391f, 114.8f, 180.4f +path.cubicTo(SkBits2Float(0x42e6f777), SkBits2Float(0x4334689c), SkBits2Float(0x42e85555), SkBits2Float(0x43346666), SkBits2Float(0x42e9b333), SkBits2Float(0x43346666)); // 115.483f, 180.409f, 116.167f, 180.4f, 116.85f, 180.4f +path.lineTo(SkBits2Float(0x42e9b333), SkBits2Float(0x43346666)); // 116.85f, 180.4f +path.lineTo(SkBits2Float(0x42033333), SkBits2Float(0x43346666)); // 32.8f, 180.4f +path.close(); +path.moveTo(SkBits2Float(0x43054000), SkBits2Float(0x43346666)); // 133.25f, 180.4f +path.lineTo(SkBits2Float(0x43054000), SkBits2Float(0x43346666)); // 133.25f, 180.4f +path.cubicTo(SkBits2Float(0x4305eeef), SkBits2Float(0x43346666), SkBits2Float(0x43069dde), SkBits2Float(0x43347a6e), SkBits2Float(0x43074ccd), SkBits2Float(0x43346666)); // 133.933f, 180.4f, 134.617f, 180.478f, 135.3f, 180.4f +path.cubicTo(SkBits2Float(0x4307fbbc), SkBits2Float(0x4334525f), SkBits2Float(0x4308aaab), SkBits2Float(0x43340a40), SkBits2Float(0x4309599a), SkBits2Float(0x4333ee38)); // 135.983f, 180.322f, 136.667f, 180.04f, 137.35f, 179.931f +path.cubicTo(SkBits2Float(0x430a0889), SkBits2Float(0x4333d230), SkBits2Float(0x430ab777), SkBits2Float(0x4333c68b), SkBits2Float(0x430b6666), SkBits2Float(0x4333be34)); // 138.033f, 179.821f, 138.717f, 179.776f, 139.4f, 179.743f +path.cubicTo(SkBits2Float(0x430c1555), SkBits2Float(0x4333b5dc), SkBits2Float(0x430cc444), SkBits2Float(0x4333bc82), SkBits2Float(0x430d7333), SkBits2Float(0x4333bc2b)); // 140.083f, 179.71f, 140.767f, 179.736f, 141.45f, 179.735f +path.cubicTo(SkBits2Float(0x430e2222), SkBits2Float(0x4333bbd4), SkBits2Float(0x430ed111), SkBits2Float(0x4333bd76), SkBits2Float(0x430f8000), SkBits2Float(0x4333bc2b)); // 142.133f, 179.734f, 142.817f, 179.74f, 143.5f, 179.735f +path.cubicTo(SkBits2Float(0x43102eef), SkBits2Float(0x4333bae0), SkBits2Float(0x4310ddde), SkBits2Float(0x4333b72e), SkBits2Float(0x43118ccd), SkBits2Float(0x4333b469)); // 144.183f, 179.73f, 144.867f, 179.716f, 145.55f, 179.705f +path.cubicTo(SkBits2Float(0x43123bbc), SkBits2Float(0x4333b1a3), SkBits2Float(0x4312eaab), SkBits2Float(0x4333ad34), SkBits2Float(0x4313999a), SkBits2Float(0x4333ab8b)); // 146.233f, 179.694f, 146.917f, 179.677f, 147.6f, 179.67f +path.cubicTo(SkBits2Float(0x43144889), SkBits2Float(0x4333a9e1), SkBits2Float(0x4314f777), SkBits2Float(0x4333aa97), SkBits2Float(0x4315a666), SkBits2Float(0x4333aa6f)); // 148.283f, 179.664f, 148.967f, 179.666f, 149.65f, 179.666f +path.cubicTo(SkBits2Float(0x43165555), SkBits2Float(0x4333aa48), SkBits2Float(0x43170444), SkBits2Float(0x4333aac5), SkBits2Float(0x4317b333), SkBits2Float(0x4333aa9d)); // 150.333f, 179.665f, 151.017f, 179.667f, 151.7f, 179.666f +path.cubicTo(SkBits2Float(0x43186222), SkBits2Float(0x4333aa76), SkBits2Float(0x43191111), SkBits2Float(0x4333a962), SkBits2Float(0x4319c000), SkBits2Float(0x4333a982)); // 152.383f, 179.666f, 153.067f, 179.662f, 153.75f, 179.662f +path.cubicTo(SkBits2Float(0x431a6eef), SkBits2Float(0x4333a9a2), SkBits2Float(0x431b1dde), SkBits2Float(0x4333ab0e), SkBits2Float(0x431bcccd), SkBits2Float(0x4333ab5d)); // 154.433f, 179.663f, 155.117f, 179.668f, 155.8f, 179.669f +path.cubicTo(SkBits2Float(0x431c7bbc), SkBits2Float(0x4333abac), SkBits2Float(0x431d2aab), SkBits2Float(0x4333ab84), SkBits2Float(0x431dd99a), SkBits2Float(0x4333ab5d)); // 156.483f, 179.671f, 157.167f, 179.67f, 157.85f, 179.669f +path.cubicTo(SkBits2Float(0x431e8889), SkBits2Float(0x4333ab35), SkBits2Float(0x431f3777), SkBits2Float(0x4333aa8f), SkBits2Float(0x431fe666), SkBits2Float(0x4333aa6f)); // 158.533f, 179.669f, 159.217f, 179.666f, 159.9f, 179.666f +path.cubicTo(SkBits2Float(0x43209555), SkBits2Float(0x4333aa4f), SkBits2Float(0x43214444), SkBits2Float(0x4333a9b1), SkBits2Float(0x4321f333), SkBits2Float(0x4333aa9d)); // 160.583f, 179.665f, 161.267f, 179.663f, 161.95f, 179.666f +path.cubicTo(SkBits2Float(0x4322a222), SkBits2Float(0x4333ab8a), SkBits2Float(0x43235111), SkBits2Float(0x4333aeb6), SkBits2Float(0x43240000), SkBits2Float(0x4333affa)); // 162.633f, 179.67f, 163.317f, 179.682f, 164, 179.687f +path.cubicTo(SkBits2Float(0x4324aeef), SkBits2Float(0x4333b13d), SkBits2Float(0x43255dde), SkBits2Float(0x4333b1a3), SkBits2Float(0x43260ccd), SkBits2Float(0x4333b231)); // 164.683f, 179.692f, 165.367f, 179.694f, 166.05f, 179.696f +path.cubicTo(SkBits2Float(0x4326bbbc), SkBits2Float(0x4333b2bf), SkBits2Float(0x43276aab), SkBits2Float(0x4333b439), SkBits2Float(0x4328199a), SkBits2Float(0x4333b34d)); // 166.733f, 179.698f, 167.417f, 179.704f, 168.1f, 179.7f +path.cubicTo(SkBits2Float(0x4328c889), SkBits2Float(0x4333b260), SkBits2Float(0x43297777), SkBits2Float(0x4333ae48), SkBits2Float(0x432a2666), SkBits2Float(0x4333aca7)); // 168.783f, 179.697f, 169.467f, 179.681f, 170.15f, 179.674f +path.cubicTo(SkBits2Float(0x432ad555), SkBits2Float(0x4333ab05), SkBits2Float(0x432b8444), SkBits2Float(0x4333a9d8), SkBits2Float(0x432c3333), SkBits2Float(0x4333a982)); // 170.833f, 179.668f, 171.517f, 179.663f, 172.2f, 179.662f +path.cubicTo(SkBits2Float(0x432ce222), SkBits2Float(0x4333a92b), SkBits2Float(0x432d9111), SkBits2Float(0x4333a63e), SkBits2Float(0x432e4000), SkBits2Float(0x4333aa9d)); // 172.883f, 179.661f, 173.567f, 179.649f, 174.25f, 179.666f +path.cubicTo(SkBits2Float(0x432eeeef), SkBits2Float(0x4333aefd), SkBits2Float(0x432f9dde), SkBits2Float(0x4333aacf), SkBits2Float(0x43304ccd), SkBits2Float(0x4333c3bf)); // 174.933f, 179.684f, 175.617f, 179.667f, 176.3f, 179.765f +path.cubicTo(SkBits2Float(0x4330fbbc), SkBits2Float(0x4333dcae), SkBits2Float(0x4331aaab), SkBits2Float(0x433427ba), SkBits2Float(0x4332599a), SkBits2Float(0x4334403b)); // 176.983f, 179.862f, 177.667f, 180.155f, 178.35f, 180.251f +path.cubicTo(SkBits2Float(0x43330889), SkBits2Float(0x433458bc), SkBits2Float(0x4333b777), SkBits2Float(0x43345065), SkBits2Float(0x43346666), SkBits2Float(0x433456c2)); // 179.033f, 180.347f, 179.717f, 180.314f, 180.4f, 180.339f +path.cubicTo(SkBits2Float(0x43351555), SkBits2Float(0x43345d1e), SkBits2Float(0x4335c444), SkBits2Float(0x433463cb), SkBits2Float(0x43367333), SkBits2Float(0x43346666)); // 181.083f, 180.364f, 181.767f, 180.39f, 182.45f, 180.4f +path.cubicTo(SkBits2Float(0x43372222), SkBits2Float(0x43346902), SkBits2Float(0x4337d111), SkBits2Float(0x43346666), SkBits2Float(0x43388000), SkBits2Float(0x43346666)); // 183.133f, 180.41f, 183.817f, 180.4f, 184.5f, 180.4f +path.lineTo(SkBits2Float(0x43388000), SkBits2Float(0x43346666)); // 184.5f, 180.4f +path.lineTo(SkBits2Float(0x43054000), SkBits2Float(0x43346666)); // 133.25f, 180.4f +path.close(); +path.moveTo(SkBits2Float(0x433a8ccd), SkBits2Float(0x43346666)); // 186.55f, 180.4f +path.lineTo(SkBits2Float(0x433a8ccd), SkBits2Float(0x43346666)); // 186.55f, 180.4f +path.cubicTo(SkBits2Float(0x433b3bbc), SkBits2Float(0x433465db), SkBits2Float(0x433beaab), SkBits2Float(0x433463ac), SkBits2Float(0x433c999a), SkBits2Float(0x43346321)); // 187.233f, 180.398f, 187.917f, 180.389f, 188.6f, 180.387f +path.cubicTo(SkBits2Float(0x433d4889), SkBits2Float(0x43346295), SkBits2Float(0x433df777), SkBits2Float(0x43346295), SkBits2Float(0x433ea666), SkBits2Float(0x43346321)); // 189.283f, 180.385f, 189.967f, 180.385f, 190.65f, 180.387f +path.cubicTo(SkBits2Float(0x433f5555), SkBits2Float(0x433463ac), SkBits2Float(0x43400444), SkBits2Float(0x433465db), SkBits2Float(0x4340b333), SkBits2Float(0x43346666)); // 191.333f, 180.389f, 192.017f, 180.398f, 192.7f, 180.4f +path.lineTo(SkBits2Float(0x4340b333), SkBits2Float(0x43346666)); // 192.7f, 180.4f +path.lineTo(SkBits2Float(0x433a8ccd), SkBits2Float(0x43346666)); // 186.55f, 180.4f +path.close(); +SkPath pathA = path; +path.reset(); +path.moveTo(SkBits2Float(0x42033333), SkBits2Float(0x43346666)); // 32.8f, 180.4f +path.lineTo(SkBits2Float(0x42033333), SkBits2Float(0x43346666)); // 32.8f, 180.4f +path.cubicTo(SkBits2Float(0x4205eeef), SkBits2Float(0x43346666), SkBits2Float(0x4208aaab), SkBits2Float(0x433454bd), SkBits2Float(0x420b6666), SkBits2Float(0x43346666)); // 33.4833f, 180.4f, 34.1667f, 180.331f, 34.85f, 180.4f +path.cubicTo(SkBits2Float(0x420e2222), SkBits2Float(0x4334780e), SkBits2Float(0x4210ddde), SkBits2Float(0x4334bf76), SkBits2Float(0x4213999a), SkBits2Float(0x4334d05a)); // 35.5333f, 180.469f, 36.2167f, 180.748f, 36.9f, 180.814f +path.cubicTo(SkBits2Float(0x42165555), SkBits2Float(0x4334e13e), SkBits2Float(0x42191111), SkBits2Float(0x4334d0d8), SkBits2Float(0x421bcccd), SkBits2Float(0x4334cbbd)); // 37.5833f, 180.88f, 38.2667f, 180.816f, 38.95f, 180.796f +path.cubicTo(SkBits2Float(0x421e8889), SkBits2Float(0x4334c6a1), SkBits2Float(0x42214444), SkBits2Float(0x4334bab9), SkBits2Float(0x42240000), SkBits2Float(0x4334b1b5)); // 39.6333f, 180.776f, 40.3167f, 180.729f, 41, 180.694f +path.cubicTo(SkBits2Float(0x4226bbbc), SkBits2Float(0x4334a8b1), SkBits2Float(0x42297777), SkBits2Float(0x4334992e), SkBits2Float(0x422c3333), SkBits2Float(0x433495a4)); // 41.6833f, 180.659f, 42.3667f, 180.598f, 43.05f, 180.585f +path.cubicTo(SkBits2Float(0x422eeeef), SkBits2Float(0x4334921a), SkBits2Float(0x4231aaab), SkBits2Float(0x4334994d), SkBits2Float(0x42346666), SkBits2Float(0x43349c78)); // 43.7333f, 180.571f, 44.4167f, 180.599f, 45.1f, 180.611f +path.cubicTo(SkBits2Float(0x42372222), SkBits2Float(0x43349fa4), SkBits2Float(0x4239ddde), SkBits2Float(0x4334aa4b), SkBits2Float(0x423c999a), SkBits2Float(0x4334a8a9)); // 45.7833f, 180.624f, 46.4667f, 180.665f, 47.15f, 180.659f +path.cubicTo(SkBits2Float(0x423f5555), SkBits2Float(0x4334a707), SkBits2Float(0x42421111), SkBits2Float(0x4334994b), SkBits2Float(0x4244cccd), SkBits2Float(0x433492ad)); // 47.8333f, 180.652f, 48.5167f, 180.599f, 49.2f, 180.573f +path.cubicTo(SkBits2Float(0x42478889), SkBits2Float(0x43348c0e), SkBits2Float(0x424a4444), SkBits2Float(0x43347f6f), SkBits2Float(0x424d0000), SkBits2Float(0x433480f1)); // 49.8833f, 180.547f, 50.5667f, 180.498f, 51.25f, 180.504f +path.cubicTo(SkBits2Float(0x424fbbbc), SkBits2Float(0x43348273), SkBits2Float(0x42527777), SkBits2Float(0x4334a025), SkBits2Float(0x42553333), SkBits2Float(0x43349bb9)); // 51.9333f, 180.51f, 52.6167f, 180.626f, 53.3f, 180.608f +path.cubicTo(SkBits2Float(0x4257eeef), SkBits2Float(0x4334974c), SkBits2Float(0x425aaaab), SkBits2Float(0x43346781), SkBits2Float(0x425d6666), SkBits2Float(0x43346666)); // 53.9833f, 180.591f, 54.6667f, 180.404f, 55.35f, 180.4f +path.cubicTo(SkBits2Float(0x42602222), SkBits2Float(0x4334654a), SkBits2Float(0x4262ddde), SkBits2Float(0x43348de8), SkBits2Float(0x4265999a), SkBits2Float(0x43349512)); // 56.0333f, 180.396f, 56.7167f, 180.554f, 57.4f, 180.582f +path.cubicTo(SkBits2Float(0x42685555), SkBits2Float(0x43349c3d), SkBits2Float(0x426b1111), SkBits2Float(0x43348fe9), SkBits2Float(0x426dcccd), SkBits2Float(0x43349163)); // 58.0833f, 180.61f, 58.7667f, 180.562f, 59.45f, 180.568f +path.cubicTo(SkBits2Float(0x42708889), SkBits2Float(0x433492dd), SkBits2Float(0x42734444), SkBits2Float(0x433494f4), SkBits2Float(0x42760000), SkBits2Float(0x43349df0)); // 60.1333f, 180.574f, 60.8167f, 180.582f, 61.5f, 180.617f +path.cubicTo(SkBits2Float(0x4278bbbc), SkBits2Float(0x4334a6ed), SkBits2Float(0x427b7777), SkBits2Float(0x4334bf7d), SkBits2Float(0x427e3333), SkBits2Float(0x4334c74e)); // 62.1833f, 180.652f, 62.8667f, 180.748f, 63.55f, 180.779f +path.cubicTo(SkBits2Float(0x42807777), SkBits2Float(0x4334cf1f), SkBits2Float(0x4281d555), SkBits2Float(0x4334d177), SkBits2Float(0x42833333), SkBits2Float(0x4334ccd9)); // 64.2333f, 180.809f, 64.9167f, 180.818f, 65.6f, 180.8f +path.cubicTo(SkBits2Float(0x42849111), SkBits2Float(0x4334c83a), SkBits2Float(0x4285eeef), SkBits2Float(0x4334bcac), SkBits2Float(0x42874ccd), SkBits2Float(0x4334ab99)); // 66.2833f, 180.782f, 66.9667f, 180.737f, 67.65f, 180.67f +path.cubicTo(SkBits2Float(0x4288aaab), SkBits2Float(0x43349a86), SkBits2Float(0x428a0889), SkBits2Float(0x433471ee), SkBits2Float(0x428b6666), SkBits2Float(0x43346666)); // 68.3333f, 180.604f, 69.0167f, 180.445f, 69.7f, 180.4f +path.cubicTo(SkBits2Float(0x428cc444), SkBits2Float(0x43345add), SkBits2Float(0x428e2222), SkBits2Float(0x4334651d), SkBits2Float(0x428f8000), SkBits2Float(0x43346666)); // 70.3833f, 180.355f, 71.0667f, 180.395f, 71.75f, 180.4f +path.cubicTo(SkBits2Float(0x4290ddde), SkBits2Float(0x433467ae), SkBits2Float(0x42923bbc), SkBits2Float(0x4334688a), SkBits2Float(0x4293999a), SkBits2Float(0x43346e1a)); // 72.4333f, 180.405f, 73.1167f, 180.408f, 73.8f, 180.43f +path.cubicTo(SkBits2Float(0x4294f777), SkBits2Float(0x433473aa), SkBits2Float(0x42965555), SkBits2Float(0x43348118), SkBits2Float(0x4297b333), SkBits2Float(0x433487c6)); // 74.4833f, 180.452f, 75.1667f, 180.504f, 75.85f, 180.53f +path.cubicTo(SkBits2Float(0x42991111), SkBits2Float(0x43348e74), SkBits2Float(0x429a6eef), SkBits2Float(0x43349426), SkBits2Float(0x429bcccd), SkBits2Float(0x4334962e)); // 76.5333f, 180.556f, 77.2167f, 180.579f, 77.9f, 180.587f +path.cubicTo(SkBits2Float(0x429d2aab), SkBits2Float(0x43349836), SkBits2Float(0x429e8889), SkBits2Float(0x43348d19), SkBits2Float(0x429fe666), SkBits2Float(0x433493f7)); // 78.5833f, 180.595f, 79.2667f, 180.551f, 79.95f, 180.578f +path.cubicTo(SkBits2Float(0x42a14444), SkBits2Float(0x43349ad4), SkBits2Float(0x42a2a222), SkBits2Float(0x4334bae7), SkBits2Float(0x42a40000), SkBits2Float(0x4334bf5e)); // 80.6333f, 180.605f, 81.3167f, 180.73f, 82, 180.748f +path.cubicTo(SkBits2Float(0x42a55dde), SkBits2Float(0x4334c3d4), SkBits2Float(0x42a6bbbc), SkBits2Float(0x4334b1ea), SkBits2Float(0x42a8199a), SkBits2Float(0x4334aebe)); // 82.6833f, 180.765f, 83.3667f, 180.695f, 84.05f, 180.683f +path.cubicTo(SkBits2Float(0x42a97777), SkBits2Float(0x4334ab93), SkBits2Float(0x42aad555), SkBits2Float(0x4334a524), SkBits2Float(0x42ac3333), SkBits2Float(0x4334ac59)); // 84.7333f, 180.67f, 85.4167f, 180.645f, 86.1f, 180.673f +path.cubicTo(SkBits2Float(0x42ad9111), SkBits2Float(0x4334b38d), SkBits2Float(0x42aeeeef), SkBits2Float(0x4334d284), SkBits2Float(0x42b04ccd), SkBits2Float(0x4334d9f7)); // 86.7833f, 180.701f, 87.4667f, 180.822f, 88.15f, 180.851f +path.cubicTo(SkBits2Float(0x42b1aaab), SkBits2Float(0x4334e16a), SkBits2Float(0x42b30889), SkBits2Float(0x4334dbcf), SkBits2Float(0x42b46666), SkBits2Float(0x4334d909)); // 88.8333f, 180.881f, 89.5167f, 180.859f, 90.2f, 180.848f +path.cubicTo(SkBits2Float(0x42b5c444), SkBits2Float(0x4334d644), SkBits2Float(0x42b72222), SkBits2Float(0x4334d502), SkBits2Float(0x42b88000), SkBits2Float(0x4334c957)); // 90.8833f, 180.837f, 91.5667f, 180.832f, 92.25f, 180.786f +path.cubicTo(SkBits2Float(0x42b9ddde), SkBits2Float(0x4334bdad), SkBits2Float(0x42bb3bbc), SkBits2Float(0x4334a179), SkBits2Float(0x42bc999a), SkBits2Float(0x43349309)); // 92.9333f, 180.741f, 93.6167f, 180.631f, 94.3f, 180.574f +path.cubicTo(SkBits2Float(0x42bdf777), SkBits2Float(0x43348499), SkBits2Float(0x42bf5555), SkBits2Float(0x43347859), SkBits2Float(0x42c0b333), SkBits2Float(0x433472b7)); // 94.9833f, 180.518f, 95.6667f, 180.47f, 96.35f, 180.448f +path.cubicTo(SkBits2Float(0x42c21111), SkBits2Float(0x43346d16), SkBits2Float(0x42c36eef), SkBits2Float(0x433464d7), SkBits2Float(0x42c4cccd), SkBits2Float(0x4334713f)); // 97.0333f, 180.426f, 97.7167f, 180.394f, 98.4f, 180.442f +path.cubicTo(SkBits2Float(0x42c62aab), SkBits2Float(0x43347da7), SkBits2Float(0x42c78889), SkBits2Float(0x4334a11c), SkBits2Float(0x42c8e666), SkBits2Float(0x4334bd26)); // 99.0833f, 180.491f, 99.7667f, 180.629f, 100.45f, 180.739f +path.cubicTo(SkBits2Float(0x42ca4444), SkBits2Float(0x4334d931), SkBits2Float(0x42cba222), SkBits2Float(0x433509ec), SkBits2Float(0x42cd0000), SkBits2Float(0x4335197f)); // 101.133f, 180.848f, 101.817f, 181.039f, 102.5f, 181.1f +path.cubicTo(SkBits2Float(0x42ce5dde), SkBits2Float(0x43352913), SkBits2Float(0x42cfbbbc), SkBits2Float(0x43351bb7), SkBits2Float(0x42d1199a), SkBits2Float(0x43351a9b)); // 103.183f, 181.16f, 103.867f, 181.108f, 104.55f, 181.104f +path.cubicTo(SkBits2Float(0x42d27777), SkBits2Float(0x4335197f), SkBits2Float(0x42d3d555), SkBits2Float(0x4335162c), SkBits2Float(0x42d53333), SkBits2Float(0x433512d9)); // 105.233f, 181.1f, 105.917f, 181.087f, 106.6f, 181.074f +path.cubicTo(SkBits2Float(0x42d69111), SkBits2Float(0x43350f86), SkBits2Float(0x42d7eeef), SkBits2Float(0x433509c4), SkBits2Float(0x42d94ccd), SkBits2Float(0x433506a8)); // 107.283f, 181.061f, 107.967f, 181.038f, 108.65f, 181.026f +path.cubicTo(SkBits2Float(0x42daaaab), SkBits2Float(0x4335038c), SkBits2Float(0x42dc0889), SkBits2Float(0x433518b0), SkBits2Float(0x42dd6666), SkBits2Float(0x43350030)); // 109.333f, 181.014f, 110.017f, 181.096f, 110.7f, 181.001f +path.cubicTo(SkBits2Float(0x42dec444), SkBits2Float(0x4334e7af), SkBits2Float(0x42e02222), SkBits2Float(0x43348d47), SkBits2Float(0x42e18000), SkBits2Float(0x433473a5)); // 111.383f, 180.905f, 112.067f, 180.552f, 112.75f, 180.452f +path.cubicTo(SkBits2Float(0x42e2ddde), SkBits2Float(0x43345a03), SkBits2Float(0x42e43bbc), SkBits2Float(0x4334689b), SkBits2Float(0x42e5999a), SkBits2Float(0x43346666)); // 113.433f, 180.352f, 114.117f, 180.409f, 114.8f, 180.4f +path.cubicTo(SkBits2Float(0x42e6f777), SkBits2Float(0x43346430), SkBits2Float(0x42e85555), SkBits2Float(0x43346666), SkBits2Float(0x42e9b333), SkBits2Float(0x43346666)); // 115.483f, 180.391f, 116.167f, 180.4f, 116.85f, 180.4f +path.lineTo(SkBits2Float(0x42e9b333), SkBits2Float(0x43346666)); // 116.85f, 180.4f +path.lineTo(SkBits2Float(0x42033333), SkBits2Float(0x43346666)); // 32.8f, 180.4f +path.close(); +path.moveTo(SkBits2Float(0x43054000), SkBits2Float(0x43346666)); // 133.25f, 180.4f +path.lineTo(SkBits2Float(0x43054000), SkBits2Float(0x43346666)); // 133.25f, 180.4f +path.cubicTo(SkBits2Float(0x4305eeef), SkBits2Float(0x43346666), SkBits2Float(0x43069dde), SkBits2Float(0x4334525e), SkBits2Float(0x43074ccd), SkBits2Float(0x43346666)); // 133.933f, 180.4f, 134.617f, 180.322f, 135.3f, 180.4f +path.cubicTo(SkBits2Float(0x4307fbbc), SkBits2Float(0x43347a6d), SkBits2Float(0x4308aaab), SkBits2Float(0x4334c28c), SkBits2Float(0x4309599a), SkBits2Float(0x4334de94)); // 135.983f, 180.478f, 136.667f, 180.76f, 137.35f, 180.869f +path.cubicTo(SkBits2Float(0x430a0889), SkBits2Float(0x4334fa9c), SkBits2Float(0x430ab777), SkBits2Float(0x43350641), SkBits2Float(0x430b6666), SkBits2Float(0x43350e98)); // 138.033f, 180.979f, 138.717f, 181.024f, 139.4f, 181.057f +path.cubicTo(SkBits2Float(0x430c1555), SkBits2Float(0x433516f0), SkBits2Float(0x430cc444), SkBits2Float(0x4335104a), SkBits2Float(0x430d7333), SkBits2Float(0x433510a1)); // 140.083f, 181.09f, 140.767f, 181.064f, 141.45f, 181.065f +path.cubicTo(SkBits2Float(0x430e2222), SkBits2Float(0x433510f8), SkBits2Float(0x430ed111), SkBits2Float(0x43350f56), SkBits2Float(0x430f8000), SkBits2Float(0x433510a1)); // 142.133f, 181.066f, 142.817f, 181.06f, 143.5f, 181.065f +path.cubicTo(SkBits2Float(0x43102eef), SkBits2Float(0x433511ec), SkBits2Float(0x4310ddde), SkBits2Float(0x4335159e), SkBits2Float(0x43118ccd), SkBits2Float(0x43351863)); // 144.183f, 181.07f, 144.867f, 181.084f, 145.55f, 181.095f +path.cubicTo(SkBits2Float(0x43123bbc), SkBits2Float(0x43351b29), SkBits2Float(0x4312eaab), SkBits2Float(0x43351f98), SkBits2Float(0x4313999a), SkBits2Float(0x43352141)); // 146.233f, 181.106f, 146.917f, 181.123f, 147.6f, 181.13f +path.cubicTo(SkBits2Float(0x43144889), SkBits2Float(0x433522eb), SkBits2Float(0x4314f777), SkBits2Float(0x43352235), SkBits2Float(0x4315a666), SkBits2Float(0x4335225d)); // 148.283f, 181.136f, 148.967f, 181.134f, 149.65f, 181.134f +path.cubicTo(SkBits2Float(0x43165555), SkBits2Float(0x43352284), SkBits2Float(0x43170444), SkBits2Float(0x43352207), SkBits2Float(0x4317b333), SkBits2Float(0x4335222f)); // 150.333f, 181.135f, 151.017f, 181.133f, 151.7f, 181.134f +path.cubicTo(SkBits2Float(0x43186222), SkBits2Float(0x43352256), SkBits2Float(0x43191111), SkBits2Float(0x4335236a), SkBits2Float(0x4319c000), SkBits2Float(0x4335234a)); // 152.383f, 181.134f, 153.067f, 181.138f, 153.75f, 181.138f +path.cubicTo(SkBits2Float(0x431a6eef), SkBits2Float(0x4335232a), SkBits2Float(0x431b1dde), SkBits2Float(0x433521be), SkBits2Float(0x431bcccd), SkBits2Float(0x4335216f)); // 154.433f, 181.137f, 155.117f, 181.132f, 155.8f, 181.131f +path.cubicTo(SkBits2Float(0x431c7bbc), SkBits2Float(0x43352120), SkBits2Float(0x431d2aab), SkBits2Float(0x43352148), SkBits2Float(0x431dd99a), SkBits2Float(0x4335216f)); // 156.483f, 181.129f, 157.167f, 181.13f, 157.85f, 181.131f +path.cubicTo(SkBits2Float(0x431e8889), SkBits2Float(0x43352197), SkBits2Float(0x431f3777), SkBits2Float(0x4335223d), SkBits2Float(0x431fe666), SkBits2Float(0x4335225d)); // 158.533f, 181.131f, 159.217f, 181.134f, 159.9f, 181.134f +path.cubicTo(SkBits2Float(0x43209555), SkBits2Float(0x4335227d), SkBits2Float(0x43214444), SkBits2Float(0x4335231b), SkBits2Float(0x4321f333), SkBits2Float(0x4335222f)); // 160.583f, 181.135f, 161.267f, 181.137f, 161.95f, 181.134f +path.cubicTo(SkBits2Float(0x4322a222), SkBits2Float(0x43352142), SkBits2Float(0x43235111), SkBits2Float(0x43351e16), SkBits2Float(0x43240000), SkBits2Float(0x43351cd2)); // 162.633f, 181.13f, 163.317f, 181.118f, 164, 181.113f +path.cubicTo(SkBits2Float(0x4324aeef), SkBits2Float(0x43351b8f), SkBits2Float(0x43255dde), SkBits2Float(0x43351b29), SkBits2Float(0x43260ccd), SkBits2Float(0x43351a9b)); // 164.683f, 181.108f, 165.367f, 181.106f, 166.05f, 181.104f +path.cubicTo(SkBits2Float(0x4326bbbc), SkBits2Float(0x43351a0d), SkBits2Float(0x43276aab), SkBits2Float(0x43351893), SkBits2Float(0x4328199a), SkBits2Float(0x4335197f)); // 166.733f, 181.102f, 167.417f, 181.096f, 168.1f, 181.1f +path.cubicTo(SkBits2Float(0x4328c889), SkBits2Float(0x43351a6c), SkBits2Float(0x43297777), SkBits2Float(0x43351e84), SkBits2Float(0x432a2666), SkBits2Float(0x43352025)); // 168.783f, 181.103f, 169.467f, 181.119f, 170.15f, 181.126f +path.cubicTo(SkBits2Float(0x432ad555), SkBits2Float(0x433521c7), SkBits2Float(0x432b8444), SkBits2Float(0x433522f4), SkBits2Float(0x432c3333), SkBits2Float(0x4335234a)); // 170.833f, 181.132f, 171.517f, 181.137f, 172.2f, 181.138f +path.cubicTo(SkBits2Float(0x432ce222), SkBits2Float(0x433523a1), SkBits2Float(0x432d9111), SkBits2Float(0x4335268e), SkBits2Float(0x432e4000), SkBits2Float(0x4335222f)); // 172.883f, 181.139f, 173.567f, 181.151f, 174.25f, 181.134f +path.cubicTo(SkBits2Float(0x432eeeef), SkBits2Float(0x43351dcf), SkBits2Float(0x432f9dde), SkBits2Float(0x433521fd), SkBits2Float(0x43304ccd), SkBits2Float(0x4335090d)); // 174.933f, 181.116f, 175.617f, 181.133f, 176.3f, 181.035f +path.cubicTo(SkBits2Float(0x4330fbbc), SkBits2Float(0x4334f01e), SkBits2Float(0x4331aaab), SkBits2Float(0x4334a512), SkBits2Float(0x4332599a), SkBits2Float(0x43348c91)); // 176.983f, 180.938f, 177.667f, 180.645f, 178.35f, 180.549f +path.cubicTo(SkBits2Float(0x43330889), SkBits2Float(0x43347410), SkBits2Float(0x4333b777), SkBits2Float(0x43347c67), SkBits2Float(0x43346666), SkBits2Float(0x4334760a)); // 179.033f, 180.453f, 179.717f, 180.486f, 180.4f, 180.461f +path.cubicTo(SkBits2Float(0x43351555), SkBits2Float(0x43346fae), SkBits2Float(0x4335c444), SkBits2Float(0x43346901), SkBits2Float(0x43367333), SkBits2Float(0x43346666)); // 181.083f, 180.436f, 181.767f, 180.41f, 182.45f, 180.4f +path.cubicTo(SkBits2Float(0x43372222), SkBits2Float(0x433463ca), SkBits2Float(0x4337d111), SkBits2Float(0x43346666), SkBits2Float(0x43388000), SkBits2Float(0x43346666)); // 183.133f, 180.39f, 183.817f, 180.4f, 184.5f, 180.4f +path.lineTo(SkBits2Float(0x43388000), SkBits2Float(0x43346666)); // 184.5f, 180.4f +path.lineTo(SkBits2Float(0x43054000), SkBits2Float(0x43346666)); // 133.25f, 180.4f +path.close(); +path.moveTo(SkBits2Float(0x433a8ccd), SkBits2Float(0x43346666)); // 186.55f, 180.4f +path.lineTo(SkBits2Float(0x433a8ccd), SkBits2Float(0x43346666)); // 186.55f, 180.4f +path.cubicTo(SkBits2Float(0x433b3bbc), SkBits2Float(0x433466f1), SkBits2Float(0x433beaab), SkBits2Float(0x43346920), SkBits2Float(0x433c999a), SkBits2Float(0x433469ab)); // 187.233f, 180.402f, 187.917f, 180.411f, 188.6f, 180.413f +path.cubicTo(SkBits2Float(0x433d4889), SkBits2Float(0x43346a37), SkBits2Float(0x433df777), SkBits2Float(0x43346a37), SkBits2Float(0x433ea666), SkBits2Float(0x433469ab)); // 189.283f, 180.415f, 189.967f, 180.415f, 190.65f, 180.413f +path.cubicTo(SkBits2Float(0x433f5555), SkBits2Float(0x43346920), SkBits2Float(0x43400444), SkBits2Float(0x433466f1), SkBits2Float(0x4340b333), SkBits2Float(0x43346666)); // 191.333f, 180.411f, 192.017f, 180.402f, 192.7f, 180.4f +path.lineTo(SkBits2Float(0x4340b333), SkBits2Float(0x43346666)); // 192.7f, 180.4f +path.lineTo(SkBits2Float(0x433a8ccd), SkBits2Float(0x43346666)); // 186.55f, 180.4f +path.close(); + testPathOp(reporter, pathA, path, SkPathOp::kUnion_SkPathOp, filename); +} + +static void issue3651_5(skiatest::Reporter* reporter, const char* filename) { +SkPath path; +path.moveTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333)); // 9.9f, 257.4f +path.lineTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333)); // 9.9f, 257.4f +path.cubicTo(SkBits2Float(0x41300000), SkBits2Float(0x4380b333), SkBits2Float(0x4141999a), SkBits2Float(0x4380ba9e), SkBits2Float(0x41533333), SkBits2Float(0x4380b333)); // 11, 257.4f, 12.1f, 257.458f, 13.2f, 257.4f +path.cubicTo(SkBits2Float(0x4164cccd), SkBits2Float(0x4380abc8), SkBits2Float(0x41766666), SkBits2Float(0x43809a93), SkBits2Float(0x41840000), SkBits2Float(0x438086b0)); // 14.3f, 257.342f, 15.4f, 257.208f, 16.5f, 257.052f +path.cubicTo(SkBits2Float(0x418ccccd), SkBits2Float(0x438072cc), SkBits2Float(0x4195999a), SkBits2Float(0x4380498b), SkBits2Float(0x419e6666), SkBits2Float(0x43803bdc)); // 17.6f, 256.897f, 18.7f, 256.575f, 19.8f, 256.468f +path.cubicTo(SkBits2Float(0x41a73333), SkBits2Float(0x43802e2d), SkBits2Float(0x41b00000), SkBits2Float(0x438026f3), SkBits2Float(0x41b8cccd), SkBits2Float(0x43803498)); // 20.9f, 256.361f, 22, 256.304f, 23.1f, 256.411f +path.cubicTo(SkBits2Float(0x41c1999a), SkBits2Float(0x4380423d), SkBits2Float(0x41ca6666), SkBits2Float(0x4380789f), SkBits2Float(0x41d33333), SkBits2Float(0x43808db9)); // 24.2f, 256.517f, 25.3f, 256.942f, 26.4f, 257.107f +path.cubicTo(SkBits2Float(0x41dc0000), SkBits2Float(0x4380a2d3), SkBits2Float(0x41e4cccd), SkBits2Float(0x4380b36f), SkBits2Float(0x41ed999a), SkBits2Float(0x4380b333)); // 27.5f, 257.272f, 28.6f, 257.402f, 29.7f, 257.4f +path.cubicTo(SkBits2Float(0x41f66666), SkBits2Float(0x4380b2f7), SkBits2Float(0x41ff3333), SkBits2Float(0x4380a1a6), SkBits2Float(0x42040000), SkBits2Float(0x43808c51)); // 30.8f, 257.398f, 31.9f, 257.263f, 33, 257.096f +path.cubicTo(SkBits2Float(0x42086666), SkBits2Float(0x438076fc), SkBits2Float(0x420ccccd), SkBits2Float(0x43803f7a), SkBits2Float(0x42113333), SkBits2Float(0x43803333)); // 34.1f, 256.93f, 35.2f, 256.496f, 36.3f, 256.4f +path.cubicTo(SkBits2Float(0x4215999a), SkBits2Float(0x438026ed), SkBits2Float(0x421a0000), SkBits2Float(0x43802d56), SkBits2Float(0x421e6666), SkBits2Float(0x438042ab)); // 37.4f, 256.304f, 38.5f, 256.354f, 39.6f, 256.521f +path.cubicTo(SkBits2Float(0x4222cccd), SkBits2Float(0x43805800), SkBits2Float(0x42273333), SkBits2Float(0x4380a072), SkBits2Float(0x422b999a), SkBits2Float(0x4380b333)); // 40.7f, 256.688f, 41.8f, 257.253f, 42.9f, 257.4f +path.cubicTo(SkBits2Float(0x42300000), SkBits2Float(0x4380c5f5), SkBits2Float(0x42346666), SkBits2Float(0x4380b333), SkBits2Float(0x4238cccd), SkBits2Float(0x4380b333)); // 44, 257.547f, 45.1f, 257.4f, 46.2f, 257.4f +path.cubicTo(SkBits2Float(0x423d3333), SkBits2Float(0x4380b333), SkBits2Float(0x4241999a), SkBits2Float(0x4380c1e4), SkBits2Float(0x42460000), SkBits2Float(0x4380b333)); // 47.3f, 257.4f, 48.4f, 257.515f, 49.5f, 257.4f +path.cubicTo(SkBits2Float(0x424a6666), SkBits2Float(0x4380a482), SkBits2Float(0x424ecccd), SkBits2Float(0x43807063), SkBits2Float(0x42533333), SkBits2Float(0x43805b0e)); // 50.6f, 257.285f, 51.7f, 256.878f, 52.8f, 256.711f +path.cubicTo(SkBits2Float(0x4257999a), SkBits2Float(0x438045b8), SkBits2Float(0x425c0000), SkBits2Float(0x438039d8), SkBits2Float(0x42606666), SkBits2Float(0x43803333)); // 53.9f, 256.545f, 55, 256.452f, 56.1f, 256.4f +path.cubicTo(SkBits2Float(0x4264cccd), SkBits2Float(0x43802c8f), SkBits2Float(0x42693333), SkBits2Float(0x43803333), SkBits2Float(0x426d999a), SkBits2Float(0x43803333)); // 57.2f, 256.348f, 58.3f, 256.4f, 59.4f, 256.4f +path.cubicTo(SkBits2Float(0x42720000), SkBits2Float(0x43803333), SkBits2Float(0x42766666), SkBits2Float(0x43802bdb), SkBits2Float(0x427acccd), SkBits2Float(0x43803333)); // 60.5f, 256.4f, 61.6f, 256.343f, 62.7f, 256.4f +path.cubicTo(SkBits2Float(0x427f3333), SkBits2Float(0x43803a8c), SkBits2Float(0x4281cccd), SkBits2Float(0x438049f1), SkBits2Float(0x42840000), SkBits2Float(0x43805f47)); // 63.8f, 256.457f, 64.9f, 256.578f, 66, 256.744f +path.cubicTo(SkBits2Float(0x42863333), SkBits2Float(0x4380749c), SkBits2Float(0x42886666), SkBits2Float(0x4380a536), SkBits2Float(0x428a999a), SkBits2Float(0x4380b333)); // 67.1f, 256.911f, 68.2f, 257.291f, 69.3f, 257.4f +path.cubicTo(SkBits2Float(0x428ccccd), SkBits2Float(0x4380c130), SkBits2Float(0x428f0000), SkBits2Float(0x4380b333), SkBits2Float(0x42913333), SkBits2Float(0x4380b333)); // 70.4f, 257.509f, 71.5f, 257.4f, 72.6f, 257.4f +path.cubicTo(SkBits2Float(0x42936666), SkBits2Float(0x4380b333), SkBits2Float(0x4295999a), SkBits2Float(0x4380bb17), SkBits2Float(0x4297cccd), SkBits2Float(0x4380b333)); // 73.7f, 257.4f, 74.8f, 257.462f, 75.9f, 257.4f +path.cubicTo(SkBits2Float(0x429a0000), SkBits2Float(0x4380ab50), SkBits2Float(0x429c3333), SkBits2Float(0x438083df), SkBits2Float(0x429e6666), SkBits2Float(0x438083df)); // 77, 257.338f, 78.1f, 257.03f, 79.2f, 257.03f +path.cubicTo(SkBits2Float(0x42a0999a), SkBits2Float(0x438083df), SkBits2Float(0x42a2cccd), SkBits2Float(0x4380ab50), SkBits2Float(0x42a50000), SkBits2Float(0x4380b333)); // 80.3f, 257.03f, 81.4f, 257.338f, 82.5f, 257.4f +path.cubicTo(SkBits2Float(0x42a73333), SkBits2Float(0x4380bb17), SkBits2Float(0x42a96666), SkBits2Float(0x4380b333), SkBits2Float(0x42ab999a), SkBits2Float(0x4380b333)); // 83.6f, 257.462f, 84.7f, 257.4f, 85.8f, 257.4f +path.lineTo(SkBits2Float(0x42ab999a), SkBits2Float(0x4380b333)); // 85.8f, 257.4f +path.lineTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333)); // 9.9f, 257.4f +path.close(); +SkPath pathA = path; +path.reset(); +path.moveTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333)); // 9.9f, 257.4f +path.lineTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333)); // 9.9f, 257.4f +path.cubicTo(SkBits2Float(0x41300000), SkBits2Float(0x4380b333), SkBits2Float(0x4141999a), SkBits2Float(0x4380abc8), SkBits2Float(0x41533333), SkBits2Float(0x4380b333)); // 11, 257.4f, 12.1f, 257.342f, 13.2f, 257.4f +path.cubicTo(SkBits2Float(0x4164cccd), SkBits2Float(0x4380ba9e), SkBits2Float(0x41766666), SkBits2Float(0x4380cbd3), SkBits2Float(0x41840000), SkBits2Float(0x4380dfb6)); // 14.3f, 257.458f, 15.4f, 257.592f, 16.5f, 257.748f +path.cubicTo(SkBits2Float(0x418ccccd), SkBits2Float(0x4380f39a), SkBits2Float(0x4195999a), SkBits2Float(0x43811cdb), SkBits2Float(0x419e6666), SkBits2Float(0x43812a8a)); // 17.6f, 257.903f, 18.7f, 258.225f, 19.8f, 258.332f +path.cubicTo(SkBits2Float(0x41a73333), SkBits2Float(0x43813839), SkBits2Float(0x41b00000), SkBits2Float(0x43813f73), SkBits2Float(0x41b8cccd), SkBits2Float(0x438131ce)); // 20.9f, 258.439f, 22, 258.496f, 23.1f, 258.389f +path.cubicTo(SkBits2Float(0x41c1999a), SkBits2Float(0x43812429), SkBits2Float(0x41ca6666), SkBits2Float(0x4380edc7), SkBits2Float(0x41d33333), SkBits2Float(0x4380d8ad)); // 24.2f, 258.283f, 25.3f, 257.858f, 26.4f, 257.693f +path.cubicTo(SkBits2Float(0x41dc0000), SkBits2Float(0x4380c393), SkBits2Float(0x41e4cccd), SkBits2Float(0x4380b2f7), SkBits2Float(0x41ed999a), SkBits2Float(0x4380b333)); // 27.5f, 257.528f, 28.6f, 257.398f, 29.7f, 257.4f +path.cubicTo(SkBits2Float(0x41f66666), SkBits2Float(0x4380b36f), SkBits2Float(0x41ff3333), SkBits2Float(0x4380c4c0), SkBits2Float(0x42040000), SkBits2Float(0x4380da15)); // 30.8f, 257.402f, 31.9f, 257.537f, 33, 257.704f +path.cubicTo(SkBits2Float(0x42086666), SkBits2Float(0x4380ef6a), SkBits2Float(0x420ccccd), SkBits2Float(0x438126ec), SkBits2Float(0x42113333), SkBits2Float(0x43813333)); // 34.1f, 257.87f, 35.2f, 258.304f, 36.3f, 258.4f +path.cubicTo(SkBits2Float(0x4215999a), SkBits2Float(0x43813f79), SkBits2Float(0x421a0000), SkBits2Float(0x43813910), SkBits2Float(0x421e6666), SkBits2Float(0x438123bb)); // 37.4f, 258.496f, 38.5f, 258.446f, 39.6f, 258.279f +path.cubicTo(SkBits2Float(0x4222cccd), SkBits2Float(0x43810e66), SkBits2Float(0x42273333), SkBits2Float(0x4380c5f4), SkBits2Float(0x422b999a), SkBits2Float(0x4380b333)); // 40.7f, 258.112f, 41.8f, 257.547f, 42.9f, 257.4f +path.cubicTo(SkBits2Float(0x42300000), SkBits2Float(0x4380a071), SkBits2Float(0x42346666), SkBits2Float(0x4380b333), SkBits2Float(0x4238cccd), SkBits2Float(0x4380b333)); // 44, 257.253f, 45.1f, 257.4f, 46.2f, 257.4f +path.cubicTo(SkBits2Float(0x423d3333), SkBits2Float(0x4380b333), SkBits2Float(0x4241999a), SkBits2Float(0x4380a482), SkBits2Float(0x42460000), SkBits2Float(0x4380b333)); // 47.3f, 257.4f, 48.4f, 257.285f, 49.5f, 257.4f +path.cubicTo(SkBits2Float(0x424a6666), SkBits2Float(0x4380c1e4), SkBits2Float(0x424ecccd), SkBits2Float(0x4380f603), SkBits2Float(0x42533333), SkBits2Float(0x43810b58)); // 50.6f, 257.515f, 51.7f, 257.922f, 52.8f, 258.089f +path.cubicTo(SkBits2Float(0x4257999a), SkBits2Float(0x438120ae), SkBits2Float(0x425c0000), SkBits2Float(0x43812c8e), SkBits2Float(0x42606666), SkBits2Float(0x43813333)); // 53.9f, 258.255f, 55, 258.348f, 56.1f, 258.4f +path.cubicTo(SkBits2Float(0x4264cccd), SkBits2Float(0x438139d7), SkBits2Float(0x42693333), SkBits2Float(0x43813333), SkBits2Float(0x426d999a), SkBits2Float(0x43813333)); // 57.2f, 258.452f, 58.3f, 258.4f, 59.4f, 258.4f +path.cubicTo(SkBits2Float(0x42720000), SkBits2Float(0x43813333), SkBits2Float(0x42766666), SkBits2Float(0x43813a8b), SkBits2Float(0x427acccd), SkBits2Float(0x43813333)); // 60.5f, 258.4f, 61.6f, 258.457f, 62.7f, 258.4f +path.cubicTo(SkBits2Float(0x427f3333), SkBits2Float(0x43812bda), SkBits2Float(0x4281cccd), SkBits2Float(0x43811c75), SkBits2Float(0x42840000), SkBits2Float(0x4381071f)); // 63.8f, 258.343f, 64.9f, 258.222f, 66, 258.056f +path.cubicTo(SkBits2Float(0x42863333), SkBits2Float(0x4380f1ca), SkBits2Float(0x42886666), SkBits2Float(0x4380c130), SkBits2Float(0x428a999a), SkBits2Float(0x4380b333)); // 67.1f, 257.889f, 68.2f, 257.509f, 69.3f, 257.4f +path.cubicTo(SkBits2Float(0x428ccccd), SkBits2Float(0x4380a536), SkBits2Float(0x428f0000), SkBits2Float(0x4380b333), SkBits2Float(0x42913333), SkBits2Float(0x4380b333)); // 70.4f, 257.291f, 71.5f, 257.4f, 72.6f, 257.4f +path.cubicTo(SkBits2Float(0x42936666), SkBits2Float(0x4380b333), SkBits2Float(0x4295999a), SkBits2Float(0x4380ab4f), SkBits2Float(0x4297cccd), SkBits2Float(0x4380b333)); // 73.7f, 257.4f, 74.8f, 257.338f, 75.9f, 257.4f +path.cubicTo(SkBits2Float(0x429a0000), SkBits2Float(0x4380bb16), SkBits2Float(0x429c3333), SkBits2Float(0x4380e287), SkBits2Float(0x429e6666), SkBits2Float(0x4380e287)); // 77, 257.462f, 78.1f, 257.77f, 79.2f, 257.77f +path.cubicTo(SkBits2Float(0x42a0999a), SkBits2Float(0x4380e287), SkBits2Float(0x42a2cccd), SkBits2Float(0x4380bb16), SkBits2Float(0x42a50000), SkBits2Float(0x4380b333)); // 80.3f, 257.77f, 81.4f, 257.462f, 82.5f, 257.4f +path.cubicTo(SkBits2Float(0x42a73333), SkBits2Float(0x4380ab4f), SkBits2Float(0x42a96666), SkBits2Float(0x4380b333), SkBits2Float(0x42ab999a), SkBits2Float(0x4380b333)); // 83.6f, 257.338f, 84.7f, 257.4f, 85.8f, 257.4f +path.lineTo(SkBits2Float(0x42ab999a), SkBits2Float(0x4380b333)); // 85.8f, 257.4f +path.lineTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333)); // 9.9f, 257.4f +path.close(); + testPathOp(reporter, pathA, path, SkPathOp::kUnion_SkPathOp, filename); +} + +static void issue3651_6(skiatest::Reporter* reporter, const char* filename) { + SkPath path; +path.cubicTo(SkBits2Float(0x41c1999a), SkBits2Float(0x4380423d), SkBits2Float(0x41ca6666), SkBits2Float(0x4380789f), SkBits2Float(0x41d33333), SkBits2Float(0x43808db9)); // 24.2f, 256.517f, 25.3f, 256.942f, 26.4f, 257.107f +path.cubicTo(SkBits2Float(0x41dc0000), SkBits2Float(0x4380a2d3), SkBits2Float(0x41e4cccd), SkBits2Float(0x4380b36f), SkBits2Float(0x41ed999a), SkBits2Float(0x4380b333)); // 27.5f, 257.272f, 28.6f, 257.402f, 29.7f, 257.4f +path.lineTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333)); // 9.9f, 257.4f +path.close(); +SkPath pathA = path; +path.reset(); +path.cubicTo(SkBits2Float(0x41c1999a), SkBits2Float(0x43812429), SkBits2Float(0x41ca6666), SkBits2Float(0x4380edc7), SkBits2Float(0x41d33333), SkBits2Float(0x4380d8ad)); // 24.2f, 258.283f, 25.3f, 257.858f, 26.4f, 257.693f +path.cubicTo(SkBits2Float(0x41dc0000), SkBits2Float(0x4380c393), SkBits2Float(0x41e4cccd), SkBits2Float(0x4380b2f7), SkBits2Float(0x41ed999a), SkBits2Float(0x4380b333)); // 27.5f, 257.528f, 28.6f, 257.398f, 29.7f, 257.4f + testPathOp(reporter, pathA, path, SkPathOp::kUnion_SkPathOp, filename); +} + +static void issue3651_7(skiatest::Reporter* reporter, const char* filename) { + SkPath path; +path.moveTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a)); // 68.2f, 74.8f +path.lineTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a)); // 68.2f, 74.8f +path.cubicTo(SkBits2Float(0x4289ddde), SkBits2Float(0x4295999a), SkBits2Float(0x428b5555), SkBits2Float(0x4295ab91), SkBits2Float(0x428ccccd), SkBits2Float(0x4295999a)); // 68.9333f, 74.8f, 69.6667f, 74.8351f, 70.4f, 74.8f +path.cubicTo(SkBits2Float(0x428e4444), SkBits2Float(0x429587a2), SkBits2Float(0x428fbbbc), SkBits2Float(0x42954339), SkBits2Float(0x42913333), SkBits2Float(0x42952dcf)); // 71.1333f, 74.7649f, 71.8667f, 74.6313f, 72.6f, 74.5895f +path.cubicTo(SkBits2Float(0x4292aaab), SkBits2Float(0x42951865), SkBits2Float(0x42942222), SkBits2Float(0x42951dec), SkBits2Float(0x4295999a), SkBits2Float(0x4295191f)); // 73.3333f, 74.5476f, 74.0667f, 74.5584f, 74.8f, 74.5491f +path.cubicTo(SkBits2Float(0x42971111), SkBits2Float(0x42951452), SkBits2Float(0x42988889), SkBits2Float(0x4294fb9a), SkBits2Float(0x429a0000), SkBits2Float(0x42951101)); // 75.5333f, 74.5397f, 76.2667f, 74.4914f, 77, 74.5332f +path.cubicTo(SkBits2Float(0x429b7777), SkBits2Float(0x42952667), SkBits2Float(0x429ceeef), SkBits2Float(0x429582c2), SkBits2Float(0x429e6666), SkBits2Float(0x42959986)); // 77.7333f, 74.575f, 78.4667f, 74.7554f, 79.2f, 74.7999f +path.cubicTo(SkBits2Float(0x429fddde), SkBits2Float(0x4295b04b), SkBits2Float(0x42a15555), SkBits2Float(0x42959996), SkBits2Float(0x42a2cccd), SkBits2Float(0x4295999a)); // 79.9333f, 74.8443f, 80.6667f, 74.8f, 81.4f, 74.8f +path.cubicTo(SkBits2Float(0x42a44444), SkBits2Float(0x4295999d), SkBits2Float(0x42a5bbbc), SkBits2Float(0x42959e78), SkBits2Float(0x42a73333), SkBits2Float(0x4295999a)); // 82.1333f, 74.8f, 82.8667f, 74.8095f, 83.6f, 74.8f +path.cubicTo(SkBits2Float(0x42a8aaab), SkBits2Float(0x429594bb), SkBits2Float(0x42aa2222), SkBits2Float(0x42957c62), SkBits2Float(0x42ab999a), SkBits2Float(0x42957c62)); // 84.3333f, 74.7905f, 85.0667f, 74.7429f, 85.8f, 74.7429f +path.cubicTo(SkBits2Float(0x42ad1111), SkBits2Float(0x42957c62), SkBits2Float(0x42ae8889), SkBits2Float(0x429594bb), SkBits2Float(0x42b00000), SkBits2Float(0x4295999a)); // 86.5333f, 74.7429f, 87.2667f, 74.7905f, 88, 74.8f +path.cubicTo(SkBits2Float(0x42b17777), SkBits2Float(0x42959e78), SkBits2Float(0x42b2eeef), SkBits2Float(0x4295ac2d), SkBits2Float(0x42b46666), SkBits2Float(0x4295999a)); // 88.7333f, 74.8095f, 89.4667f, 74.8363f, 90.2f, 74.8f +path.cubicTo(SkBits2Float(0x42b5ddde), SkBits2Float(0x42958706), SkBits2Float(0x42b75555), SkBits2Float(0x42953b44), SkBits2Float(0x42b8cccd), SkBits2Float(0x42952a25)); // 90.9333f, 74.7637f, 91.6667f, 74.6158f, 92.4f, 74.5823f +path.cubicTo(SkBits2Float(0x42ba4444), SkBits2Float(0x42951906), SkBits2Float(0x42bbbbbc), SkBits2Float(0x42952e4b), SkBits2Float(0x42bd3333), SkBits2Float(0x429532e0)); // 93.1333f, 74.5489f, 93.8667f, 74.5904f, 94.6f, 74.5994f +path.cubicTo(SkBits2Float(0x42beaaab), SkBits2Float(0x42953775), SkBits2Float(0x42c02222), SkBits2Float(0x42954fda), SkBits2Float(0x42c1999a), SkBits2Float(0x429545a4)); // 95.3333f, 74.6083f, 96.0667f, 74.656f, 96.8f, 74.636f +path.cubicTo(SkBits2Float(0x42c31111), SkBits2Float(0x42953b6d), SkBits2Float(0x42c48889), SkBits2Float(0x42950c1f), SkBits2Float(0x42c60000), SkBits2Float(0x4294f599)); // 97.5333f, 74.6161f, 98.2667f, 74.5237f, 99, 74.4797f +path.cubicTo(SkBits2Float(0x42c77777), SkBits2Float(0x4294df14), SkBits2Float(0x42c8eeef), SkBits2Float(0x4294c8f4), SkBits2Float(0x42ca6666), SkBits2Float(0x4294be85)); // 99.7333f, 74.4357f, 100.467f, 74.3925f, 101.2f, 74.3721f +path.cubicTo(SkBits2Float(0x42cbddde), SkBits2Float(0x4294b417), SkBits2Float(0x42cd5555), SkBits2Float(0x4294ba72), SkBits2Float(0x42cecccd), SkBits2Float(0x4294b703)); // 101.933f, 74.3517f, 102.667f, 74.3642f, 103.4f, 74.3574f +path.cubicTo(SkBits2Float(0x42d04444), SkBits2Float(0x4294b395), SkBits2Float(0x42d1bbbc), SkBits2Float(0x4294b0ca), SkBits2Float(0x42d33333), SkBits2Float(0x4294a9ec)); // 104.133f, 74.3507f, 104.867f, 74.3453f, 105.6f, 74.3319f +path.cubicTo(SkBits2Float(0x42d4aaab), SkBits2Float(0x4294a30e), SkBits2Float(0x42d62222), SkBits2Float(0x429493f5), SkBits2Float(0x42d7999a), SkBits2Float(0x42948dd1)); // 106.333f, 74.3185f, 107.067f, 74.289f, 107.8f, 74.277f +path.cubicTo(SkBits2Float(0x42d91111), SkBits2Float(0x429487ae), SkBits2Float(0x42da8889), SkBits2Float(0x42947f24), SkBits2Float(0x42dc0000), SkBits2Float(0x42948517)); // 108.533f, 74.265f, 109.267f, 74.2483f, 110, 74.2599f +path.cubicTo(SkBits2Float(0x42dd7777), SkBits2Float(0x42948b0a), SkBits2Float(0x42deeeef), SkBits2Float(0x42949db5), SkBits2Float(0x42e06666), SkBits2Float(0x4294b185)); // 110.733f, 74.2716f, 111.467f, 74.308f, 112.2f, 74.3467f +path.cubicTo(SkBits2Float(0x42e1ddde), SkBits2Float(0x4294c556), SkBits2Float(0x42e35555), SkBits2Float(0x4294ed7d), SkBits2Float(0x42e4cccd), SkBits2Float(0x4294fbfa)); // 112.933f, 74.3854f, 113.667f, 74.4638f, 114.4f, 74.4921f +path.cubicTo(SkBits2Float(0x42e64444), SkBits2Float(0x42950a77), SkBits2Float(0x42e7bbbc), SkBits2Float(0x42950d94), SkBits2Float(0x42e93333), SkBits2Float(0x42950875)); // 115.133f, 74.5204f, 115.867f, 74.5265f, 116.6f, 74.5165f +path.cubicTo(SkBits2Float(0x42eaaaab), SkBits2Float(0x42950356), SkBits2Float(0x42ec2222), SkBits2Float(0x4294e77a), SkBits2Float(0x42ed999a), SkBits2Float(0x4294dd40)); // 117.333f, 74.5065f, 118.067f, 74.4521f, 118.8f, 74.4321f +path.cubicTo(SkBits2Float(0x42ef1111), SkBits2Float(0x4294d305), SkBits2Float(0x42f08889), SkBits2Float(0x4294c50a), SkBits2Float(0x42f20000), SkBits2Float(0x4294cb18)); // 119.533f, 74.4121f, 120.267f, 74.3848f, 121, 74.3967f +path.cubicTo(SkBits2Float(0x42f37777), SkBits2Float(0x4294d125), SkBits2Float(0x42f4eeef), SkBits2Float(0x4294ece7), SkBits2Float(0x42f66666), SkBits2Float(0x42950190)); // 121.733f, 74.4085f, 122.467f, 74.4627f, 123.2f, 74.5031f +path.cubicTo(SkBits2Float(0x42f7ddde), SkBits2Float(0x42951638), SkBits2Float(0x42f95555), SkBits2Float(0x42953e63), SkBits2Float(0x42facccd), SkBits2Float(0x4295470b)); // 123.933f, 74.5434f, 124.667f, 74.6218f, 125.4f, 74.6388f +path.cubicTo(SkBits2Float(0x42fc4444), SkBits2Float(0x42954fb3), SkBits2Float(0x42fdbbbc), SkBits2Float(0x4295478d), SkBits2Float(0x42ff3333), SkBits2Float(0x4295357f)); // 126.133f, 74.6557f, 126.867f, 74.6397f, 127.6f, 74.6045f +path.cubicTo(SkBits2Float(0x43005555), SkBits2Float(0x42952371), SkBits2Float(0x43011111), SkBits2Float(0x4294fc35), SkBits2Float(0x4301cccd), SkBits2Float(0x4294dab7)); // 128.333f, 74.5692f, 129.067f, 74.4926f, 129.8f, 74.4272f +path.cubicTo(SkBits2Float(0x43028889), SkBits2Float(0x4294b93a), SkBits2Float(0x43034444), SkBits2Float(0x429480ff), SkBits2Float(0x43040000), SkBits2Float(0x42946c8f)); // 130.533f, 74.3618f, 131.267f, 74.2519f, 132, 74.212f +path.cubicTo(SkBits2Float(0x4304bbbc), SkBits2Float(0x4294581e), SkBits2Float(0x43057777), SkBits2Float(0x42945d0d), SkBits2Float(0x43063333), SkBits2Float(0x42946014)); // 132.733f, 74.1721f, 133.467f, 74.1817f, 134.2f, 74.1877f +path.cubicTo(SkBits2Float(0x4306eeef), SkBits2Float(0x4294631a), SkBits2Float(0x4307aaab), SkBits2Float(0x42947340), SkBits2Float(0x43086666), SkBits2Float(0x42947eb7)); // 134.933f, 74.1936f, 135.667f, 74.2251f, 136.4f, 74.2475f +path.cubicTo(SkBits2Float(0x43092222), SkBits2Float(0x42948a2d), SkBits2Float(0x4309ddde), SkBits2Float(0x42949abf), SkBits2Float(0x430a999a), SkBits2Float(0x4294a4db)); // 137.133f, 74.2699f, 137.867f, 74.3022f, 138.6f, 74.322f +path.cubicTo(SkBits2Float(0x430b5555), SkBits2Float(0x4294aef8), SkBits2Float(0x430c1111), SkBits2Float(0x4294bfa8), SkBits2Float(0x430ccccd), SkBits2Float(0x4294bb61)); // 139.333f, 74.3417f, 140.067f, 74.3743f, 140.8f, 74.366f +path.cubicTo(SkBits2Float(0x430d8889), SkBits2Float(0x4294b71a), SkBits2Float(0x430e4444), SkBits2Float(0x429490b9), SkBits2Float(0x430f0000), SkBits2Float(0x42948b32)); // 141.533f, 74.3576f, 142.267f, 74.2827f, 143, 74.2719f +path.cubicTo(SkBits2Float(0x430fbbbc), SkBits2Float(0x429485ab), SkBits2Float(0x43107777), SkBits2Float(0x429483e4), SkBits2Float(0x43113333), SkBits2Float(0x42949a35)); // 143.733f, 74.2611f, 144.467f, 74.2576f, 145.2f, 74.3012f +path.cubicTo(SkBits2Float(0x4311eeef), SkBits2Float(0x4294b086), SkBits2Float(0x4312aaab), SkBits2Float(0x4294f894), SkBits2Float(0x43136666), SkBits2Float(0x42951118)); // 145.933f, 74.3448f, 146.667f, 74.4855f, 147.4f, 74.5334f +path.cubicTo(SkBits2Float(0x43142222), SkBits2Float(0x4295299b), SkBits2Float(0x4314ddde), SkBits2Float(0x4295342c), SkBits2Float(0x4315999a), SkBits2Float(0x42952d4a)); // 148.133f, 74.5813f, 148.867f, 74.6019f, 149.6f, 74.5885f +path.cubicTo(SkBits2Float(0x43165555), SkBits2Float(0x42952668), SkBits2Float(0x43171111), SkBits2Float(0x4294f257), SkBits2Float(0x4317cccd), SkBits2Float(0x4294e7cf)); // 150.333f, 74.575f, 151.067f, 74.4733f, 151.8f, 74.4528f +path.cubicTo(SkBits2Float(0x43188889), SkBits2Float(0x4294dd46), SkBits2Float(0x43194444), SkBits2Float(0x4294e9b3), SkBits2Float(0x431a0000), SkBits2Float(0x4294ee18)); // 152.533f, 74.4322f, 153.267f, 74.4564f, 154, 74.465f +path.cubicTo(SkBits2Float(0x431abbbc), SkBits2Float(0x4294f27d), SkBits2Float(0x431b7777), SkBits2Float(0x42950422), SkBits2Float(0x431c3333), SkBits2Float(0x4295022c)); // 154.733f, 74.4736f, 155.467f, 74.5081f, 156.2f, 74.5042f +path.cubicTo(SkBits2Float(0x431ceeef), SkBits2Float(0x42950035), SkBits2Float(0x431daaab), SkBits2Float(0x4294e324), SkBits2Float(0x431e6666), SkBits2Float(0x4294e250)); // 156.933f, 74.5004f, 157.667f, 74.4436f, 158.4f, 74.442f +path.cubicTo(SkBits2Float(0x431f2222), SkBits2Float(0x4294e17c), SkBits2Float(0x431fddde), SkBits2Float(0x4294e5a1), SkBits2Float(0x4320999a), SkBits2Float(0x4294fd32)); // 159.133f, 74.4404f, 159.867f, 74.4485f, 160.6f, 74.4945f +path.cubicTo(SkBits2Float(0x43215555), SkBits2Float(0x429514c4), SkBits2Float(0x43221111), SkBits2Float(0x42955c02), SkBits2Float(0x4322cccd), SkBits2Float(0x42956fb8)); // 161.333f, 74.5406f, 162.067f, 74.6797f, 162.8f, 74.7182f +path.cubicTo(SkBits2Float(0x43238889), SkBits2Float(0x4295836e), SkBits2Float(0x43244444), SkBits2Float(0x429586fb), SkBits2Float(0x43250000), SkBits2Float(0x42957379)); // 163.533f, 74.7567f, 164.267f, 74.7636f, 165, 74.7255f +path.cubicTo(SkBits2Float(0x4325bbbc), SkBits2Float(0x42955ff7), SkBits2Float(0x43267777), SkBits2Float(0x42950a52), SkBits2Float(0x43273333), SkBits2Float(0x4294faaa)); // 165.733f, 74.6874f, 166.467f, 74.5202f, 167.2f, 74.4896f +path.cubicTo(SkBits2Float(0x4327eeef), SkBits2Float(0x4294eb03), SkBits2Float(0x4328aaab), SkBits2Float(0x4294fb0f), SkBits2Float(0x43296666), SkBits2Float(0x4295158c)); // 167.933f, 74.459f, 168.667f, 74.4903f, 169.4f, 74.5421f +path.cubicTo(SkBits2Float(0x432a2222), SkBits2Float(0x4295300a), SkBits2Float(0x432addde), SkBits2Float(0x42958397), SkBits2Float(0x432b999a), SkBits2Float(0x4295999a)); // 170.133f, 74.5938f, 170.867f, 74.757f, 171.6f, 74.8f +path.cubicTo(SkBits2Float(0x432c5555), SkBits2Float(0x4295af9c), SkBits2Float(0x432d1111), SkBits2Float(0x4295ace7), SkBits2Float(0x432dcccd), SkBits2Float(0x4295999a)); // 172.333f, 74.843f, 173.067f, 74.8377f, 173.8f, 74.8f +path.cubicTo(SkBits2Float(0x432e8889), SkBits2Float(0x4295864c), SkBits2Float(0x432f4444), SkBits2Float(0x4295555a), SkBits2Float(0x43300000), SkBits2Float(0x429525c8)); // 174.533f, 74.7623f, 175.267f, 74.6667f, 176, 74.5738f +path.cubicTo(SkBits2Float(0x4330bbbc), SkBits2Float(0x4294f636), SkBits2Float(0x43317777), SkBits2Float(0x42949a53), SkBits2Float(0x43323333), SkBits2Float(0x42947c2e)); // 176.733f, 74.4809f, 177.467f, 74.3014f, 178.2f, 74.2425f +path.cubicTo(SkBits2Float(0x4332eeef), SkBits2Float(0x42945e0a), SkBits2Float(0x4333aaab), SkBits2Float(0x42946377), SkBits2Float(0x43346666), SkBits2Float(0x429470ec)); // 178.933f, 74.1837f, 179.667f, 74.1943f, 180.4f, 74.2206f +path.cubicTo(SkBits2Float(0x43352222), SkBits2Float(0x42947e61), SkBits2Float(0x4335ddde), SkBits2Float(0x4294b4b7), SkBits2Float(0x4336999a), SkBits2Float(0x4294ccec)); // 181.133f, 74.2468f, 181.867f, 74.353f, 182.6f, 74.4002f +path.cubicTo(SkBits2Float(0x43375555), SkBits2Float(0x4294e522), SkBits2Float(0x43381111), SkBits2Float(0x4294f1c6), SkBits2Float(0x4338cccd), SkBits2Float(0x4295022c)); // 183.333f, 74.4475f, 184.067f, 74.4722f, 184.8f, 74.5042f +path.cubicTo(SkBits2Float(0x43398889), SkBits2Float(0x42951291), SkBits2Float(0x433a4444), SkBits2Float(0x42952241), SkBits2Float(0x433b0000), SkBits2Float(0x42952f4d)); // 185.533f, 74.5363f, 186.267f, 74.5669f, 187, 74.5924f +path.cubicTo(SkBits2Float(0x433bbbbc), SkBits2Float(0x42953c5a), SkBits2Float(0x433c7777), SkBits2Float(0x42955a4b), SkBits2Float(0x433d3333), SkBits2Float(0x42955079)); // 187.733f, 74.6179f, 188.467f, 74.6764f, 189.2f, 74.6572f +path.cubicTo(SkBits2Float(0x433deeef), SkBits2Float(0x429546a7), SkBits2Float(0x433eaaab), SkBits2Float(0x4294fc01), SkBits2Float(0x433f6666), SkBits2Float(0x4294f461)); // 189.933f, 74.638f, 190.667f, 74.4922f, 191.4f, 74.4773f +path.cubicTo(SkBits2Float(0x43402222), SkBits2Float(0x4294ecc1), SkBits2Float(0x4340ddde), SkBits2Float(0x42950f1f), SkBits2Float(0x4341999a), SkBits2Float(0x429522bb)); // 192.133f, 74.4624f, 192.867f, 74.5295f, 193.6f, 74.5678f +path.cubicTo(SkBits2Float(0x43425555), SkBits2Float(0x42953657), SkBits2Float(0x43431111), SkBits2Float(0x4295616b), SkBits2Float(0x4343cccd), SkBits2Float(0x42956a0b)); // 194.333f, 74.6061f, 195.067f, 74.6903f, 195.8f, 74.7071f +path.cubicTo(SkBits2Float(0x43448889), SkBits2Float(0x429572ab), SkBits2Float(0x43454444), SkBits2Float(0x4295608a), SkBits2Float(0x43460000), SkBits2Float(0x4295567c)); // 196.533f, 74.724f, 197.267f, 74.6886f, 198, 74.6689f +path.cubicTo(SkBits2Float(0x4346bbbc), SkBits2Float(0x42954c6e), SkBits2Float(0x43477777), SkBits2Float(0x42953beb), SkBits2Float(0x43483333), SkBits2Float(0x42952db8)); // 198.733f, 74.6493f, 199.467f, 74.617f, 200.2f, 74.5893f +path.cubicTo(SkBits2Float(0x4348eeef), SkBits2Float(0x42951f85), SkBits2Float(0x4349aaab), SkBits2Float(0x42950a92), SkBits2Float(0x434a6666), SkBits2Float(0x4295014a)); // 200.933f, 74.5616f, 201.667f, 74.5206f, 202.4f, 74.5025f +path.cubicTo(SkBits2Float(0x434b2222), SkBits2Float(0x4294f802), SkBits2Float(0x434bddde), SkBits2Float(0x4294f9c8), SkBits2Float(0x434c999a), SkBits2Float(0x4294f607)); // 203.133f, 74.4844f, 203.867f, 74.4879f, 204.6f, 74.4805f +path.cubicTo(SkBits2Float(0x434d5555), SkBits2Float(0x4294f246), SkBits2Float(0x434e1111), SkBits2Float(0x4294f080), SkBits2Float(0x434ecccd), SkBits2Float(0x4294eac5)); // 205.333f, 74.4732f, 206.067f, 74.4697f, 206.8f, 74.4585f +path.cubicTo(SkBits2Float(0x434f8889), SkBits2Float(0x4294e509), SkBits2Float(0x43504444), SkBits2Float(0x4294dd6d), SkBits2Float(0x43510000), SkBits2Float(0x4294d3a3)); // 207.533f, 74.4473f, 208.267f, 74.4325f, 209, 74.4134f +path.cubicTo(SkBits2Float(0x4351bbbc), SkBits2Float(0x4294c9d9), SkBits2Float(0x43527777), SkBits2Float(0x4294b931), SkBits2Float(0x43533333), SkBits2Float(0x4294b007)); // 209.733f, 74.3942f, 210.467f, 74.3617f, 211.2f, 74.3438f +path.cubicTo(SkBits2Float(0x4353eeef), SkBits2Float(0x4294a6dd), SkBits2Float(0x4354aaab), SkBits2Float(0x4294a02f), SkBits2Float(0x43556666), SkBits2Float(0x42949ca6)); // 211.933f, 74.3259f, 212.667f, 74.3129f, 213.4f, 74.306f +path.cubicTo(SkBits2Float(0x43562222), SkBits2Float(0x4294991d), SkBits2Float(0x4356ddde), SkBits2Float(0x42949775), SkBits2Float(0x4357999a), SkBits2Float(0x42949ad1)); // 214.133f, 74.299f, 214.867f, 74.2958f, 215.6f, 74.3024f +path.cubicTo(SkBits2Float(0x43585555), SkBits2Float(0x42949e2e), SkBits2Float(0x43591111), SkBits2Float(0x4294a9b4), SkBits2Float(0x4359cccd), SkBits2Float(0x4294b0d2)); // 216.333f, 74.3089f, 217.067f, 74.3315f, 217.8f, 74.3454f +path.cubicTo(SkBits2Float(0x435a8889), SkBits2Float(0x4294b7ef), SkBits2Float(0x435b4444), SkBits2Float(0x4294d2c3), SkBits2Float(0x435c0000), SkBits2Float(0x4294c582)); // 218.533f, 74.3592f, 219.267f, 74.4116f, 220, 74.3858f +path.cubicTo(SkBits2Float(0x435cbbbc), SkBits2Float(0x4294b841), SkBits2Float(0x435d7777), SkBits2Float(0x42947919), SkBits2Float(0x435e3333), SkBits2Float(0x4294614c)); // 220.733f, 74.3599f, 221.467f, 74.2365f, 222.2f, 74.19f +path.cubicTo(SkBits2Float(0x435eeeef), SkBits2Float(0x4294497f), SkBits2Float(0x435faaab), SkBits2Float(0x42943521), SkBits2Float(0x43606666), SkBits2Float(0x429436b3)); // 222.933f, 74.1435f, 223.667f, 74.1038f, 224.4f, 74.1068f +path.cubicTo(SkBits2Float(0x43612222), SkBits2Float(0x42943845), SkBits2Float(0x4361ddde), SkBits2Float(0x42945996), SkBits2Float(0x4362999a), SkBits2Float(0x42946aba)); // 225.133f, 74.1099f, 225.867f, 74.175f, 226.6f, 74.2085f +path.cubicTo(SkBits2Float(0x43635555), SkBits2Float(0x42947bdd), SkBits2Float(0x43641111), SkBits2Float(0x4294973f), SkBits2Float(0x4364cccd), SkBits2Float(0x42949d88)); // 227.333f, 74.2419f, 228.067f, 74.2954f, 228.8f, 74.3077f +path.cubicTo(SkBits2Float(0x43658889), SkBits2Float(0x4294a3d2), SkBits2Float(0x43664444), SkBits2Float(0x429495a6), SkBits2Float(0x43670000), SkBits2Float(0x42949071)); // 229.533f, 74.32f, 230.267f, 74.2923f, 231, 74.2821f +path.cubicTo(SkBits2Float(0x4367bbbc), SkBits2Float(0x42948b3c), SkBits2Float(0x43687777), SkBits2Float(0x429480b0), SkBits2Float(0x43693333), SkBits2Float(0x42947e49)); // 231.733f, 74.2719f, 232.467f, 74.2513f, 233.2f, 74.2467f +path.cubicTo(SkBits2Float(0x4369eeef), SkBits2Float(0x42947be3), SkBits2Float(0x436aaaab), SkBits2Float(0x42947a88), SkBits2Float(0x436b6666), SkBits2Float(0x4294820a)); // 233.933f, 74.242f, 234.667f, 74.2393f, 235.4f, 74.254f +path.cubicTo(SkBits2Float(0x436c2222), SkBits2Float(0x4294898c), SkBits2Float(0x436cddde), SkBits2Float(0x42949eee), SkBits2Float(0x436d999a), SkBits2Float(0x4294ab53)); // 236.133f, 74.2686f, 236.867f, 74.3104f, 237.6f, 74.3346f +path.cubicTo(SkBits2Float(0x436e5555), SkBits2Float(0x4294b7b8), SkBits2Float(0x436f1111), SkBits2Float(0x4294c2bb), SkBits2Float(0x436fcccd), SkBits2Float(0x4294cc67)); // 238.333f, 74.3588f, 239.067f, 74.3803f, 239.8f, 74.3992f +path.cubicTo(SkBits2Float(0x43708889), SkBits2Float(0x4294d614), SkBits2Float(0x43714444), SkBits2Float(0x4294d5a0), SkBits2Float(0x43720000), SkBits2Float(0x4294e55e)); // 240.533f, 74.4181f, 241.267f, 74.4172f, 242, 74.448f +path.cubicTo(SkBits2Float(0x4372bbbc), SkBits2Float(0x4294f51b), SkBits2Float(0x43737777), SkBits2Float(0x429516d0), SkBits2Float(0x43743333), SkBits2Float(0x42952ad9)); // 242.733f, 74.4787f, 243.467f, 74.5446f, 244.2f, 74.5837f +path.cubicTo(SkBits2Float(0x4374eeef), SkBits2Float(0x42953ee1), SkBits2Float(0x4375aaab), SkBits2Float(0x42954f2d), SkBits2Float(0x43766666), SkBits2Float(0x42955d90)); // 244.933f, 74.6228f, 245.667f, 74.6546f, 246.4f, 74.6827f +path.cubicTo(SkBits2Float(0x43772222), SkBits2Float(0x42956bf3), SkBits2Float(0x4377ddde), SkBits2Float(0x4295772b), SkBits2Float(0x4378999a), SkBits2Float(0x4295812d)); // 247.133f, 74.7108f, 247.867f, 74.7327f, 248.6f, 74.7523f +path.cubicTo(SkBits2Float(0x43795555), SkBits2Float(0x42958b2e), SkBits2Float(0x437a1111), SkBits2Float(0x42959587), SkBits2Float(0x437acccd), SkBits2Float(0x4295999a)); // 249.333f, 74.7718f, 250.067f, 74.792f, 250.8f, 74.8f +path.cubicTo(SkBits2Float(0x437b8889), SkBits2Float(0x42959dac), SkBits2Float(0x437c4444), SkBits2Float(0x4295999a), SkBits2Float(0x437d0000), SkBits2Float(0x4295999a)); // 251.533f, 74.808f, 252.267f, 74.8f, 253, 74.8f +path.lineTo(SkBits2Float(0x437d0000), SkBits2Float(0x4295999a)); // 253, 74.8f +path.lineTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a)); // 68.2f, 74.8f +path.close(); +SkPath pathA = path; +path.reset(); + +path.moveTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a)); // 68.2f, 74.8f +path.lineTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a)); // 68.2f, 74.8f +path.cubicTo(SkBits2Float(0x4289ddde), SkBits2Float(0x4295999a), SkBits2Float(0x428b5555), SkBits2Float(0x429587a3), SkBits2Float(0x428ccccd), SkBits2Float(0x4295999a)); // 68.9333f, 74.8f, 69.6667f, 74.7649f, 70.4f, 74.8f +path.cubicTo(SkBits2Float(0x428e4444), SkBits2Float(0x4295ab92), SkBits2Float(0x428fbbbc), SkBits2Float(0x4295effb), SkBits2Float(0x42913333), SkBits2Float(0x42960565)); // 71.1333f, 74.8351f, 71.8667f, 74.9687f, 72.6f, 75.0105f +path.cubicTo(SkBits2Float(0x4292aaab), SkBits2Float(0x42961acf), SkBits2Float(0x42942222), SkBits2Float(0x42961548), SkBits2Float(0x4295999a), SkBits2Float(0x42961a15)); // 73.3333f, 75.0524f, 74.0667f, 75.0416f, 74.8f, 75.0509f +path.cubicTo(SkBits2Float(0x42971111), SkBits2Float(0x42961ee2), SkBits2Float(0x42988889), SkBits2Float(0x4296379a), SkBits2Float(0x429a0000), SkBits2Float(0x42962233)); // 75.5333f, 75.0603f, 76.2667f, 75.1086f, 77, 75.0668f +path.cubicTo(SkBits2Float(0x429b7777), SkBits2Float(0x42960ccd), SkBits2Float(0x429ceeef), SkBits2Float(0x4295b072), SkBits2Float(0x429e6666), SkBits2Float(0x429599ae)); // 77.7333f, 75.025f, 78.4667f, 74.8446f, 79.2f, 74.8002f +path.cubicTo(SkBits2Float(0x429fddde), SkBits2Float(0x429582e9), SkBits2Float(0x42a15555), SkBits2Float(0x4295999e), SkBits2Float(0x42a2cccd), SkBits2Float(0x4295999a)); // 79.9333f, 74.7557f, 80.6667f, 74.8f, 81.4f, 74.8f +path.cubicTo(SkBits2Float(0x42a44444), SkBits2Float(0x42959997), SkBits2Float(0x42a5bbbc), SkBits2Float(0x429594bc), SkBits2Float(0x42a73333), SkBits2Float(0x4295999a)); // 82.1333f, 74.8f, 82.8667f, 74.7905f, 83.6f, 74.8f +path.cubicTo(SkBits2Float(0x42a8aaab), SkBits2Float(0x42959e79), SkBits2Float(0x42aa2222), SkBits2Float(0x4295b6d2), SkBits2Float(0x42ab999a), SkBits2Float(0x4295b6d2)); // 84.3333f, 74.8095f, 85.0667f, 74.8571f, 85.8f, 74.8571f +path.cubicTo(SkBits2Float(0x42ad1111), SkBits2Float(0x4295b6d2), SkBits2Float(0x42ae8889), SkBits2Float(0x42959e79), SkBits2Float(0x42b00000), SkBits2Float(0x4295999a)); // 86.5333f, 74.8571f, 87.2667f, 74.8095f, 88, 74.8f +path.cubicTo(SkBits2Float(0x42b17777), SkBits2Float(0x429594bc), SkBits2Float(0x42b2eeef), SkBits2Float(0x42958707), SkBits2Float(0x42b46666), SkBits2Float(0x4295999a)); // 88.7333f, 74.7905f, 89.4667f, 74.7637f, 90.2f, 74.8f +path.cubicTo(SkBits2Float(0x42b5ddde), SkBits2Float(0x4295ac2e), SkBits2Float(0x42b75555), SkBits2Float(0x4295f7f0), SkBits2Float(0x42b8cccd), SkBits2Float(0x4296090f)); // 90.9333f, 74.8363f, 91.6667f, 74.9843f, 92.4f, 75.0177f +path.cubicTo(SkBits2Float(0x42ba4444), SkBits2Float(0x42961a2e), SkBits2Float(0x42bbbbbc), SkBits2Float(0x429604e9), SkBits2Float(0x42bd3333), SkBits2Float(0x42960054)); // 93.1333f, 75.0511f, 93.8667f, 75.0096f, 94.6f, 75.0006f +path.cubicTo(SkBits2Float(0x42beaaab), SkBits2Float(0x4295fbbf), SkBits2Float(0x42c02222), SkBits2Float(0x4295e35a), SkBits2Float(0x42c1999a), SkBits2Float(0x4295ed90)); // 95.3333f, 74.9917f, 96.0667f, 74.944f, 96.8f, 74.964f +path.cubicTo(SkBits2Float(0x42c31111), SkBits2Float(0x4295f7c7), SkBits2Float(0x42c48889), SkBits2Float(0x42962715), SkBits2Float(0x42c60000), SkBits2Float(0x42963d9b)); // 97.5333f, 74.9839f, 98.2667f, 75.0763f, 99, 75.1203f +path.cubicTo(SkBits2Float(0x42c77777), SkBits2Float(0x42965420), SkBits2Float(0x42c8eeef), SkBits2Float(0x42966a40), SkBits2Float(0x42ca6666), SkBits2Float(0x429674af)); // 99.7333f, 75.1643f, 100.467f, 75.2075f, 101.2f, 75.2279f +path.cubicTo(SkBits2Float(0x42cbddde), SkBits2Float(0x42967f1d), SkBits2Float(0x42cd5555), SkBits2Float(0x429678c2), SkBits2Float(0x42cecccd), SkBits2Float(0x42967c31)); // 101.933f, 75.2483f, 102.667f, 75.2359f, 103.4f, 75.2426f +path.cubicTo(SkBits2Float(0x42d04444), SkBits2Float(0x42967f9f), SkBits2Float(0x42d1bbbc), SkBits2Float(0x4296826a), SkBits2Float(0x42d33333), SkBits2Float(0x42968948)); // 104.133f, 75.2493f, 104.867f, 75.2547f, 105.6f, 75.2681f +path.cubicTo(SkBits2Float(0x42d4aaab), SkBits2Float(0x42969026), SkBits2Float(0x42d62222), SkBits2Float(0x42969f3f), SkBits2Float(0x42d7999a), SkBits2Float(0x4296a563)); // 106.333f, 75.2815f, 107.067f, 75.311f, 107.8f, 75.323f +path.cubicTo(SkBits2Float(0x42d91111), SkBits2Float(0x4296ab86), SkBits2Float(0x42da8889), SkBits2Float(0x4296b410), SkBits2Float(0x42dc0000), SkBits2Float(0x4296ae1d)); // 108.533f, 75.335f, 109.267f, 75.3517f, 110, 75.3401f +path.cubicTo(SkBits2Float(0x42dd7777), SkBits2Float(0x4296a82a), SkBits2Float(0x42deeeef), SkBits2Float(0x4296957f), SkBits2Float(0x42e06666), SkBits2Float(0x429681af)); // 110.733f, 75.3284f, 111.467f, 75.292f, 112.2f, 75.2533f +path.cubicTo(SkBits2Float(0x42e1ddde), SkBits2Float(0x42966dde), SkBits2Float(0x42e35555), SkBits2Float(0x429645b7), SkBits2Float(0x42e4cccd), SkBits2Float(0x4296373a)); // 112.933f, 75.2146f, 113.667f, 75.1362f, 114.4f, 75.1079f +path.cubicTo(SkBits2Float(0x42e64444), SkBits2Float(0x429628bd), SkBits2Float(0x42e7bbbc), SkBits2Float(0x429625a0), SkBits2Float(0x42e93333), SkBits2Float(0x42962abf)); // 115.133f, 75.0796f, 115.867f, 75.0735f, 116.6f, 75.0835f +path.cubicTo(SkBits2Float(0x42eaaaab), SkBits2Float(0x42962fde), SkBits2Float(0x42ec2222), SkBits2Float(0x42964bba), SkBits2Float(0x42ed999a), SkBits2Float(0x429655f4)); // 117.333f, 75.0935f, 118.067f, 75.1479f, 118.8f, 75.1679f +path.cubicTo(SkBits2Float(0x42ef1111), SkBits2Float(0x4296602f), SkBits2Float(0x42f08889), SkBits2Float(0x42966e2a), SkBits2Float(0x42f20000), SkBits2Float(0x4296681c)); // 119.533f, 75.1879f, 120.267f, 75.2152f, 121, 75.2033f +path.cubicTo(SkBits2Float(0x42f37777), SkBits2Float(0x4296620f), SkBits2Float(0x42f4eeef), SkBits2Float(0x4296464d), SkBits2Float(0x42f66666), SkBits2Float(0x429631a4)); // 121.733f, 75.1915f, 122.467f, 75.1373f, 123.2f, 75.097f +path.cubicTo(SkBits2Float(0x42f7ddde), SkBits2Float(0x42961cfc), SkBits2Float(0x42f95555), SkBits2Float(0x4295f4d1), SkBits2Float(0x42facccd), SkBits2Float(0x4295ec29)); // 123.933f, 75.0566f, 124.667f, 74.9782f, 125.4f, 74.9613f +path.cubicTo(SkBits2Float(0x42fc4444), SkBits2Float(0x4295e381), SkBits2Float(0x42fdbbbc), SkBits2Float(0x4295eba7), SkBits2Float(0x42ff3333), SkBits2Float(0x4295fdb5)); // 126.133f, 74.9443f, 126.867f, 74.9603f, 127.6f, 74.9955f +path.cubicTo(SkBits2Float(0x43005555), SkBits2Float(0x42960fc3), SkBits2Float(0x43011111), SkBits2Float(0x429636ff), SkBits2Float(0x4301cccd), SkBits2Float(0x4296587d)); // 128.333f, 75.0308f, 129.067f, 75.1074f, 129.8f, 75.1728f +path.cubicTo(SkBits2Float(0x43028889), SkBits2Float(0x429679fa), SkBits2Float(0x43034444), SkBits2Float(0x4296b235), SkBits2Float(0x43040000), SkBits2Float(0x4296c6a5)); // 130.533f, 75.2382f, 131.267f, 75.3481f, 132, 75.388f +path.cubicTo(SkBits2Float(0x4304bbbc), SkBits2Float(0x4296db16), SkBits2Float(0x43057777), SkBits2Float(0x4296d627), SkBits2Float(0x43063333), SkBits2Float(0x4296d320)); // 132.733f, 75.4279f, 133.467f, 75.4183f, 134.2f, 75.4124f +path.cubicTo(SkBits2Float(0x4306eeef), SkBits2Float(0x4296d01a), SkBits2Float(0x4307aaab), SkBits2Float(0x4296bff4), SkBits2Float(0x43086666), SkBits2Float(0x4296b47d)); // 134.933f, 75.4064f, 135.667f, 75.3749f, 136.4f, 75.3525f +path.cubicTo(SkBits2Float(0x43092222), SkBits2Float(0x4296a907), SkBits2Float(0x4309ddde), SkBits2Float(0x42969875), SkBits2Float(0x430a999a), SkBits2Float(0x42968e59)); // 137.133f, 75.3301f, 137.867f, 75.2978f, 138.6f, 75.278f +path.cubicTo(SkBits2Float(0x430b5555), SkBits2Float(0x4296843c), SkBits2Float(0x430c1111), SkBits2Float(0x4296738c), SkBits2Float(0x430ccccd), SkBits2Float(0x429677d3)); // 139.333f, 75.2583f, 140.067f, 75.2257f, 140.8f, 75.234f +path.cubicTo(SkBits2Float(0x430d8889), SkBits2Float(0x42967c1a), SkBits2Float(0x430e4444), SkBits2Float(0x4296a27b), SkBits2Float(0x430f0000), SkBits2Float(0x4296a802)); // 141.533f, 75.2424f, 142.267f, 75.3173f, 143, 75.3281f +path.cubicTo(SkBits2Float(0x430fbbbc), SkBits2Float(0x4296ad89), SkBits2Float(0x43107777), SkBits2Float(0x4296af50), SkBits2Float(0x43113333), SkBits2Float(0x429698ff)); // 143.733f, 75.3389f, 144.467f, 75.3424f, 145.2f, 75.2988f +path.cubicTo(SkBits2Float(0x4311eeef), SkBits2Float(0x429682ae), SkBits2Float(0x4312aaab), SkBits2Float(0x42963aa0), SkBits2Float(0x43136666), SkBits2Float(0x4296221c)); // 145.933f, 75.2552f, 146.667f, 75.1145f, 147.4f, 75.0666f +path.cubicTo(SkBits2Float(0x43142222), SkBits2Float(0x42960999), SkBits2Float(0x4314ddde), SkBits2Float(0x4295ff08), SkBits2Float(0x4315999a), SkBits2Float(0x429605ea)); // 148.133f, 75.0187f, 148.867f, 74.9981f, 149.6f, 75.0116f +path.cubicTo(SkBits2Float(0x43165555), SkBits2Float(0x42960ccc), SkBits2Float(0x43171111), SkBits2Float(0x429640dd), SkBits2Float(0x4317cccd), SkBits2Float(0x42964b65)); // 150.333f, 75.025f, 151.067f, 75.1267f, 151.8f, 75.1473f +path.cubicTo(SkBits2Float(0x43188889), SkBits2Float(0x429655ee), SkBits2Float(0x43194444), SkBits2Float(0x42964981), SkBits2Float(0x431a0000), SkBits2Float(0x4296451c)); // 152.533f, 75.1678f, 153.267f, 75.1436f, 154, 75.135f +path.cubicTo(SkBits2Float(0x431abbbc), SkBits2Float(0x429640b7), SkBits2Float(0x431b7777), SkBits2Float(0x42962f12), SkBits2Float(0x431c3333), SkBits2Float(0x42963108)); // 154.733f, 75.1264f, 155.467f, 75.0919f, 156.2f, 75.0958f +path.cubicTo(SkBits2Float(0x431ceeef), SkBits2Float(0x429632ff), SkBits2Float(0x431daaab), SkBits2Float(0x42965010), SkBits2Float(0x431e6666), SkBits2Float(0x429650e4)); // 156.933f, 75.0996f, 157.667f, 75.1564f, 158.4f, 75.158f +path.cubicTo(SkBits2Float(0x431f2222), SkBits2Float(0x429651b8), SkBits2Float(0x431fddde), SkBits2Float(0x42964d93), SkBits2Float(0x4320999a), SkBits2Float(0x42963602)); // 159.133f, 75.1596f, 159.867f, 75.1515f, 160.6f, 75.1055f +path.cubicTo(SkBits2Float(0x43215555), SkBits2Float(0x42961e70), SkBits2Float(0x43221111), SkBits2Float(0x4295d732), SkBits2Float(0x4322cccd), SkBits2Float(0x4295c37c)); // 161.333f, 75.0594f, 162.067f, 74.9203f, 162.8f, 74.8818f +path.cubicTo(SkBits2Float(0x43238889), SkBits2Float(0x4295afc6), SkBits2Float(0x43244444), SkBits2Float(0x4295ac39), SkBits2Float(0x43250000), SkBits2Float(0x4295bfbb)); // 163.533f, 74.8433f, 164.267f, 74.8364f, 165, 74.8745f +path.cubicTo(SkBits2Float(0x4325bbbc), SkBits2Float(0x4295d33d), SkBits2Float(0x43267777), SkBits2Float(0x429628e2), SkBits2Float(0x43273333), SkBits2Float(0x4296388a)); // 165.733f, 74.9126f, 166.467f, 75.0798f, 167.2f, 75.1104f +path.cubicTo(SkBits2Float(0x4327eeef), SkBits2Float(0x42964831), SkBits2Float(0x4328aaab), SkBits2Float(0x42963825), SkBits2Float(0x43296666), SkBits2Float(0x42961da8)); // 167.933f, 75.141f, 168.667f, 75.1097f, 169.4f, 75.0579f +path.cubicTo(SkBits2Float(0x432a2222), SkBits2Float(0x4296032a), SkBits2Float(0x432addde), SkBits2Float(0x4295af9d), SkBits2Float(0x432b999a), SkBits2Float(0x4295999a)); // 170.133f, 75.0062f, 170.867f, 74.843f, 171.6f, 74.8f +path.cubicTo(SkBits2Float(0x432c5555), SkBits2Float(0x42958398), SkBits2Float(0x432d1111), SkBits2Float(0x4295864d), SkBits2Float(0x432dcccd), SkBits2Float(0x4295999a)); // 172.333f, 74.757f, 173.067f, 74.7623f, 173.8f, 74.8f +path.cubicTo(SkBits2Float(0x432e8889), SkBits2Float(0x4295ace8), SkBits2Float(0x432f4444), SkBits2Float(0x4295ddda), SkBits2Float(0x43300000), SkBits2Float(0x42960d6c)); // 174.533f, 74.8377f, 175.267f, 74.9333f, 176, 75.0262f +path.cubicTo(SkBits2Float(0x4330bbbc), SkBits2Float(0x42963cfe), SkBits2Float(0x43317777), SkBits2Float(0x429698e1), SkBits2Float(0x43323333), SkBits2Float(0x4296b706)); // 176.733f, 75.1191f, 177.467f, 75.2986f, 178.2f, 75.3575f +path.cubicTo(SkBits2Float(0x4332eeef), SkBits2Float(0x4296d52a), SkBits2Float(0x4333aaab), SkBits2Float(0x4296cfbd), SkBits2Float(0x43346666), SkBits2Float(0x4296c248)); // 178.933f, 75.4163f, 179.667f, 75.4057f, 180.4f, 75.3795f +path.cubicTo(SkBits2Float(0x43352222), SkBits2Float(0x4296b4d3), SkBits2Float(0x4335ddde), SkBits2Float(0x42967e7d), SkBits2Float(0x4336999a), SkBits2Float(0x42966648)); // 181.133f, 75.3532f, 181.867f, 75.247f, 182.6f, 75.1998f +path.cubicTo(SkBits2Float(0x43375555), SkBits2Float(0x42964e12), SkBits2Float(0x43381111), SkBits2Float(0x4296416e), SkBits2Float(0x4338cccd), SkBits2Float(0x42963108)); // 183.333f, 75.1525f, 184.067f, 75.1278f, 184.8f, 75.0958f +path.cubicTo(SkBits2Float(0x43398889), SkBits2Float(0x429620a3), SkBits2Float(0x433a4444), SkBits2Float(0x429610f3), SkBits2Float(0x433b0000), SkBits2Float(0x429603e7)); // 185.533f, 75.0637f, 186.267f, 75.0331f, 187, 75.0076f +path.cubicTo(SkBits2Float(0x433bbbbc), SkBits2Float(0x4295f6da), SkBits2Float(0x433c7777), SkBits2Float(0x4295d8e9), SkBits2Float(0x433d3333), SkBits2Float(0x4295e2bb)); // 187.733f, 74.9821f, 188.467f, 74.9237f, 189.2f, 74.9428f +path.cubicTo(SkBits2Float(0x433deeef), SkBits2Float(0x4295ec8d), SkBits2Float(0x433eaaab), SkBits2Float(0x42963733), SkBits2Float(0x433f6666), SkBits2Float(0x42963ed3)); // 189.933f, 74.962f, 190.667f, 75.1078f, 191.4f, 75.1227f +path.cubicTo(SkBits2Float(0x43402222), SkBits2Float(0x42964673), SkBits2Float(0x4340ddde), SkBits2Float(0x42962415), SkBits2Float(0x4341999a), SkBits2Float(0x42961079)); // 192.133f, 75.1376f, 192.867f, 75.0705f, 193.6f, 75.0322f +path.cubicTo(SkBits2Float(0x43425555), SkBits2Float(0x4295fcdd), SkBits2Float(0x43431111), SkBits2Float(0x4295d1c9), SkBits2Float(0x4343cccd), SkBits2Float(0x4295c929)); // 194.333f, 74.9939f, 195.067f, 74.9097f, 195.8f, 74.8929f +path.cubicTo(SkBits2Float(0x43448889), SkBits2Float(0x4295c089), SkBits2Float(0x43454444), SkBits2Float(0x4295d2aa), SkBits2Float(0x43460000), SkBits2Float(0x4295dcb8)); // 196.533f, 74.876f, 197.267f, 74.9115f, 198, 74.9311f +path.cubicTo(SkBits2Float(0x4346bbbc), SkBits2Float(0x4295e6c6), SkBits2Float(0x43477777), SkBits2Float(0x4295f749), SkBits2Float(0x43483333), SkBits2Float(0x4296057c)); // 198.733f, 74.9507f, 199.467f, 74.983f, 200.2f, 75.0107f +path.cubicTo(SkBits2Float(0x4348eeef), SkBits2Float(0x429613af), SkBits2Float(0x4349aaab), SkBits2Float(0x429628a2), SkBits2Float(0x434a6666), SkBits2Float(0x429631ea)); // 200.933f, 75.0384f, 201.667f, 75.0794f, 202.4f, 75.0975f +path.cubicTo(SkBits2Float(0x434b2222), SkBits2Float(0x42963b32), SkBits2Float(0x434bddde), SkBits2Float(0x4296396c), SkBits2Float(0x434c999a), SkBits2Float(0x42963d2d)); // 203.133f, 75.1156f, 203.867f, 75.1122f, 204.6f, 75.1195f +path.cubicTo(SkBits2Float(0x434d5555), SkBits2Float(0x429640ee), SkBits2Float(0x434e1111), SkBits2Float(0x429642b4), SkBits2Float(0x434ecccd), SkBits2Float(0x4296486f)); // 205.333f, 75.1268f, 206.067f, 75.1303f, 206.8f, 75.1415f +path.cubicTo(SkBits2Float(0x434f8889), SkBits2Float(0x42964e2b), SkBits2Float(0x43504444), SkBits2Float(0x429655c7), SkBits2Float(0x43510000), SkBits2Float(0x42965f91)); // 207.533f, 75.1527f, 208.267f, 75.1675f, 209, 75.1867f +path.cubicTo(SkBits2Float(0x4351bbbc), SkBits2Float(0x4296695b), SkBits2Float(0x43527777), SkBits2Float(0x42967a03), SkBits2Float(0x43533333), SkBits2Float(0x4296832d)); // 209.733f, 75.2058f, 210.467f, 75.2383f, 211.2f, 75.2562f +path.cubicTo(SkBits2Float(0x4353eeef), SkBits2Float(0x42968c57), SkBits2Float(0x4354aaab), SkBits2Float(0x42969305), SkBits2Float(0x43556666), SkBits2Float(0x4296968e)); // 211.933f, 75.2741f, 212.667f, 75.2871f, 213.4f, 75.2941f +path.cubicTo(SkBits2Float(0x43562222), SkBits2Float(0x42969a17), SkBits2Float(0x4356ddde), SkBits2Float(0x42969bbf), SkBits2Float(0x4357999a), SkBits2Float(0x42969863)); // 214.133f, 75.301f, 214.867f, 75.3042f, 215.6f, 75.2976f +path.cubicTo(SkBits2Float(0x43585555), SkBits2Float(0x42969506), SkBits2Float(0x43591111), SkBits2Float(0x42968980), SkBits2Float(0x4359cccd), SkBits2Float(0x42968262)); // 216.333f, 75.2911f, 217.067f, 75.2686f, 217.8f, 75.2547f +path.cubicTo(SkBits2Float(0x435a8889), SkBits2Float(0x42967b45), SkBits2Float(0x435b4444), SkBits2Float(0x42966071), SkBits2Float(0x435c0000), SkBits2Float(0x42966db2)); // 218.533f, 75.2408f, 219.267f, 75.1884f, 220, 75.2142f +path.cubicTo(SkBits2Float(0x435cbbbc), SkBits2Float(0x42967af3), SkBits2Float(0x435d7777), SkBits2Float(0x4296ba1b), SkBits2Float(0x435e3333), SkBits2Float(0x4296d1e8)); // 220.733f, 75.2401f, 221.467f, 75.3635f, 222.2f, 75.41f +path.cubicTo(SkBits2Float(0x435eeeef), SkBits2Float(0x4296e9b5), SkBits2Float(0x435faaab), SkBits2Float(0x4296fe13), SkBits2Float(0x43606666), SkBits2Float(0x4296fc81)); // 222.933f, 75.4565f, 223.667f, 75.4962f, 224.4f, 75.4932f +path.cubicTo(SkBits2Float(0x43612222), SkBits2Float(0x4296faef), SkBits2Float(0x4361ddde), SkBits2Float(0x4296d99e), SkBits2Float(0x4362999a), SkBits2Float(0x4296c87a)); // 225.133f, 75.4901f, 225.867f, 75.425f, 226.6f, 75.3916f +path.cubicTo(SkBits2Float(0x43635555), SkBits2Float(0x4296b757), SkBits2Float(0x43641111), SkBits2Float(0x42969bf5), SkBits2Float(0x4364cccd), SkBits2Float(0x429695ac)); // 227.333f, 75.3581f, 228.067f, 75.3046f, 228.8f, 75.2923f +path.cubicTo(SkBits2Float(0x43658889), SkBits2Float(0x42968f62), SkBits2Float(0x43664444), SkBits2Float(0x42969d8e), SkBits2Float(0x43670000), SkBits2Float(0x4296a2c3)); // 229.533f, 75.28f, 230.267f, 75.3077f, 231, 75.3179f +path.cubicTo(SkBits2Float(0x4367bbbc), SkBits2Float(0x4296a7f8), SkBits2Float(0x43687777), SkBits2Float(0x4296b284), SkBits2Float(0x43693333), SkBits2Float(0x4296b4eb)); // 231.733f, 75.3281f, 232.467f, 75.3487f, 233.2f, 75.3534f +path.cubicTo(SkBits2Float(0x4369eeef), SkBits2Float(0x4296b751), SkBits2Float(0x436aaaab), SkBits2Float(0x4296b8ac), SkBits2Float(0x436b6666), SkBits2Float(0x4296b12a)); // 233.933f, 75.358f, 234.667f, 75.3607f, 235.4f, 75.346f +path.cubicTo(SkBits2Float(0x436c2222), SkBits2Float(0x4296a9a8), SkBits2Float(0x436cddde), SkBits2Float(0x42969446), SkBits2Float(0x436d999a), SkBits2Float(0x429687e1)); // 236.133f, 75.3314f, 236.867f, 75.2896f, 237.6f, 75.2654f +path.cubicTo(SkBits2Float(0x436e5555), SkBits2Float(0x42967b7c), SkBits2Float(0x436f1111), SkBits2Float(0x42967079), SkBits2Float(0x436fcccd), SkBits2Float(0x429666cd)); // 238.333f, 75.2412f, 239.067f, 75.2197f, 239.8f, 75.2008f +path.cubicTo(SkBits2Float(0x43708889), SkBits2Float(0x42965d20), SkBits2Float(0x43714444), SkBits2Float(0x42965d94), SkBits2Float(0x43720000), SkBits2Float(0x42964dd6)); // 240.533f, 75.1819f, 241.267f, 75.1828f, 242, 75.152f +path.cubicTo(SkBits2Float(0x4372bbbc), SkBits2Float(0x42963e19), SkBits2Float(0x43737777), SkBits2Float(0x42961c64), SkBits2Float(0x43743333), SkBits2Float(0x4296085b)); // 242.733f, 75.1213f, 243.467f, 75.0555f, 244.2f, 75.0163f +path.cubicTo(SkBits2Float(0x4374eeef), SkBits2Float(0x4295f453), SkBits2Float(0x4375aaab), SkBits2Float(0x4295e407), SkBits2Float(0x43766666), SkBits2Float(0x4295d5a4)); // 244.933f, 74.9772f, 245.667f, 74.9454f, 246.4f, 74.9173f +path.cubicTo(SkBits2Float(0x43772222), SkBits2Float(0x4295c741), SkBits2Float(0x4377ddde), SkBits2Float(0x4295bc09), SkBits2Float(0x4378999a), SkBits2Float(0x4295b207)); // 247.133f, 74.8892f, 247.867f, 74.8673f, 248.6f, 74.8477f +path.cubicTo(SkBits2Float(0x43795555), SkBits2Float(0x4295a806), SkBits2Float(0x437a1111), SkBits2Float(0x42959dad), SkBits2Float(0x437acccd), SkBits2Float(0x4295999a)); // 249.333f, 74.8282f, 250.067f, 74.808f, 250.8f, 74.8f +path.cubicTo(SkBits2Float(0x437b8889), SkBits2Float(0x42959588), SkBits2Float(0x437c4444), SkBits2Float(0x4295999a), SkBits2Float(0x437d0000), SkBits2Float(0x4295999a)); // 251.533f, 74.7921f, 252.267f, 74.8f, 253, 74.8f +path.lineTo(SkBits2Float(0x437d0000), SkBits2Float(0x4295999a)); // 253, 74.8f +path.lineTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a)); // 68.2f, 74.8f +path.close(); + testPathOp(reporter, pathA, path, SkPathOp::kUnion_SkPathOp, filename); +} + + +static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0; +static void (*firstTest)(skiatest::Reporter* , const char* filename) = issue3651_1; +static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0; + +static struct TestDesc tests[] = { + TEST(issue3651_6), + TEST(issue3651_1a), + TEST(issue3651_1), + TEST(issue3651_7), + TEST(issue3651_5), + TEST(issue3651_4), + TEST(issue3651_2), + TEST(issue3651_3), +}; + +static const size_t testCount = SK_ARRAY_COUNT(tests); + +static bool runReverse = false; + +DEF_TEST(PathOpsIssue3651, reporter) { +#if DEBUG_SHOW_TEST_NAME + strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH); +#endif + RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse); +}
diff --git a/src/third_party/skia/tests/PathOpsLineIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsLineIntersectionTest.cpp index 105187b..bc0259c 100644 --- a/src/third_party/skia/tests/PathOpsLineIntersectionTest.cpp +++ b/src/third_party/skia/tests/PathOpsLineIntersectionTest.cpp
@@ -11,6 +11,9 @@ // FIXME: add tests for intersecting, non-intersecting, degenerate, coincident static const SkDLine tests[][2] = { +{{{{0.00010360032320022583, 1.0172703415155411}, {0.00014114845544099808, 1.0200891587883234}}}, + {{{0.00010259449481964111, 1.017270140349865}, {0.00018215179443359375, 1.022890567779541}}}}, + #if 0 // these do intersect at a pair of points, but not close enough for check results liking {{{{365.848175,5081.15186}, {368,5103}}}, {{{367.967712,5102.61084}, {368.278717,5105.71045}}}}, @@ -82,10 +85,13 @@ static const size_t coincidentTests_count = SK_ARRAY_COUNT(coincidentTests); static void check_results(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2, - const SkIntersections& ts) { + const SkIntersections& ts, bool nearAllowed) { for (int i = 0; i < ts.used(); ++i) { SkDPoint result1 = line1.ptAtT(ts[0][i]); SkDPoint result2 = line2.ptAtT(ts[1][i]); + if (nearAllowed && result1.roughlyEqual(result2)) { + continue; + } if (!result1.approximatelyEqual(result2) && !ts.nearlySame(i)) { REPORTER_ASSERT(reporter, ts.used() != 1); result2 = line2.ptAtT(ts[1][i ^ 1]); @@ -98,14 +104,16 @@ } } -static void testOne(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2) { +static void testOne(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2, + bool nearAllowed) { SkASSERT(ValidLine(line1)); SkASSERT(ValidLine(line2)); SkIntersections i; + i.allowNear(nearAllowed); int pts = i.intersect(line1, line2); REPORTER_ASSERT(reporter, pts); REPORTER_ASSERT(reporter, pts == i.used()); - check_results(reporter, line1, line2, i); + check_results(reporter, line1, line2, i, nearAllowed); if (line1[0] == line1[1] || line2[0] == line2[1]) { return; } @@ -114,28 +122,28 @@ double right = SkTMax(line1[0].fX, line1[1].fX); SkIntersections ts; ts.horizontal(line2, left, right, line1[0].fY, line1[0].fX != left); - check_results(reporter, line2, line1, ts); + check_results(reporter, line2, line1, ts, nearAllowed); } if (line2[0].fY == line2[1].fY) { double left = SkTMin(line2[0].fX, line2[1].fX); double right = SkTMax(line2[0].fX, line2[1].fX); SkIntersections ts; ts.horizontal(line1, left, right, line2[0].fY, line2[0].fX != left); - check_results(reporter, line1, line2, ts); + check_results(reporter, line1, line2, ts, nearAllowed); } if (line1[0].fX == line1[1].fX) { double top = SkTMin(line1[0].fY, line1[1].fY); double bottom = SkTMax(line1[0].fY, line1[1].fY); SkIntersections ts; ts.vertical(line2, top, bottom, line1[0].fX, line1[0].fY != top); - check_results(reporter, line2, line1, ts); + check_results(reporter, line2, line1, ts, nearAllowed); } if (line2[0].fX == line2[1].fX) { double top = SkTMin(line2[0].fY, line2[1].fY); double bottom = SkTMax(line2[0].fY, line2[1].fY); SkIntersections ts; ts.vertical(line1, top, bottom, line2[0].fX, line2[0].fY != top); - check_results(reporter, line1, line2, ts); + check_results(reporter, line1, line2, ts, nearAllowed); } reporter->bumpTestCount(); } @@ -148,7 +156,7 @@ int pts = ts.intersect(line1, line2); REPORTER_ASSERT(reporter, pts == 2); REPORTER_ASSERT(reporter, pts == ts.used()); - check_results(reporter, line1, line2, ts); + check_results(reporter, line1, line2, ts, false); if (line1[0] == line1[1] || line2[0] == line2[1]) { return; } @@ -159,7 +167,7 @@ ts.horizontal(line2, left, right, line1[0].fY, line1[0].fX != left); REPORTER_ASSERT(reporter, pts == 2); REPORTER_ASSERT(reporter, pts == ts.used()); - check_results(reporter, line2, line1, ts); + check_results(reporter, line2, line1, ts, false); } if (line2[0].fY == line2[1].fY) { double left = SkTMin(line2[0].fX, line2[1].fX); @@ -168,7 +176,7 @@ ts.horizontal(line1, left, right, line2[0].fY, line2[0].fX != left); REPORTER_ASSERT(reporter, pts == 2); REPORTER_ASSERT(reporter, pts == ts.used()); - check_results(reporter, line1, line2, ts); + check_results(reporter, line1, line2, ts, false); } if (line1[0].fX == line1[1].fX) { double top = SkTMin(line1[0].fY, line1[1].fY); @@ -177,7 +185,7 @@ ts.vertical(line2, top, bottom, line1[0].fX, line1[0].fY != top); REPORTER_ASSERT(reporter, pts == 2); REPORTER_ASSERT(reporter, pts == ts.used()); - check_results(reporter, line2, line1, ts); + check_results(reporter, line2, line1, ts, false); } if (line2[0].fX == line2[1].fX) { double top = SkTMin(line2[0].fY, line2[1].fY); @@ -186,7 +194,7 @@ ts.vertical(line1, top, bottom, line2[0].fX, line2[0].fY != top); REPORTER_ASSERT(reporter, pts == 2); REPORTER_ASSERT(reporter, pts == ts.used()); - check_results(reporter, line1, line2, ts); + check_results(reporter, line1, line2, ts, false); } reporter->bumpTestCount(); } @@ -201,7 +209,7 @@ for (index = 0; index < tests_count; ++index) { const SkDLine& line1 = tests[index][0]; const SkDLine& line2 = tests[index][1]; - testOne(reporter, line1, line2); + testOne(reporter, line1, line2, true); } for (index = 0; index < noIntersect_count; ++index) { const SkDLine& line1 = noIntersect[index][0]; @@ -217,8 +225,13 @@ DEF_TEST(PathOpsLineIntersectionOneOff, reporter) { int index = 0; SkASSERT(index < (int) tests_count); - testOne(reporter, tests[index][0], tests[index][1]); - testOne(reporter, tests[1][0], tests[1][1]); + testOne(reporter, tests[index][0], tests[index][1], true); +} + +DEF_TEST(PathOpsLineIntersectionExactOneOff, reporter) { + int index = 0; + SkASSERT(index < (int) tests_count); + testOne(reporter, tests[index][0], tests[index][1], false); } DEF_TEST(PathOpsLineIntersectionOneCoincident, reporter) {
diff --git a/src/third_party/skia/tests/PathOpsLineParametetersTest.cpp b/src/third_party/skia/tests/PathOpsLineParametetersTest.cpp index aab1f7a..66a4be2 100644 --- a/src/third_party/skia/tests/PathOpsLineParametetersTest.cpp +++ b/src/third_party/skia/tests/PathOpsLineParametetersTest.cpp
@@ -9,7 +9,7 @@ #include "Test.h" // tests to verify that distance calculations are coded correctly -static const SkDCubic tests[] = { +static const CubicPts tests[] = { {{{0, 0}, {1, 1}, {2, 2}, {0, 3}}}, {{{0, 0}, {1, 1}, {2, 2}, {3, 0}}}, {{{0, 0}, {5, 0}, {-2, 4}, {3, 4}}}, @@ -40,7 +40,9 @@ DEF_TEST(PathOpsLineParameters, reporter) { for (size_t index = 0; index < tests_count; ++index) { SkLineParameters lineParameters; - const SkDCubic& cubic = tests[index]; + const CubicPts& c = tests[index]; + SkDCubic cubic; + cubic.debugSet(c.fPts); SkASSERT(ValidCubic(cubic)); lineParameters.cubicEndPoints(cubic, 0, 3); double denormalizedDistance[2];
diff --git a/src/third_party/skia/tests/PathOpsOpCircleThreadedTest.cpp b/src/third_party/skia/tests/PathOpsOpCircleThreadedTest.cpp new file mode 100644 index 0000000..7711dd0 --- /dev/null +++ b/src/third_party/skia/tests/PathOpsOpCircleThreadedTest.cpp
@@ -0,0 +1,82 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "PathOpsExtendedTest.h" +#include "PathOpsThreadedCommon.h" +#include "SkString.h" + +static int loopNo = 4; + +static void testOpCirclesMain(PathOpsThreadState* data) { + SkASSERT(data); + PathOpsThreadState& state = *data; + SkString pathStr; + for (int a = 0 ; a < 6; ++a) { + for (int b = a + 1 ; b < 7; ++b) { + for (int c = 0 ; c < 6; ++c) { + for (int d = c + 1 ; d < 7; ++d) { + for (int e = SkPath::kWinding_FillType ; e <= SkPath::kEvenOdd_FillType; ++e) { + for (int f = SkPath::kWinding_FillType ; f <= SkPath::kEvenOdd_FillType; ++f) { + SkPath pathA, pathB; + pathA.setFillType((SkPath::FillType) e); + pathA.addCircle(SkIntToScalar(state.fA), SkIntToScalar(state.fB), SkIntToScalar(state.fC), + state.fD ? SkPath::kCW_Direction : SkPath::kCCW_Direction); + pathB.setFillType((SkPath::FillType) f); + pathB.addCircle(SkIntToScalar(a), SkIntToScalar(b), SkIntToScalar(c), + d ? SkPath::kCW_Direction : SkPath::kCCW_Direction); + for (int op = 0 ; op <= kXOR_SkPathOp; ++op) { + if (state.fReporter->verbose()) { + pathStr.printf("static void circlesOp%d(skiatest::Reporter* reporter," + " const char* filename) {\n", loopNo); + pathStr.appendf(" SkPath path, pathB;\n"); + pathStr.appendf(" path.setFillType(SkPath::k%s_FillType);\n", + e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType + ? "EvenOdd" : "?UNDEFINED"); + pathStr.appendf(" path.addCircle(%d, %d, %d, %s);\n", state.fA, state.fB, + state.fC, state.fD ? "SkPath::kCW_Direction" : "SkPath::kCCW_Direction"); + pathStr.appendf(" pathB.setFillType(SkPath::k%s_FillType);\n", + f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType + ? "EvenOdd" : "?UNDEFINED"); + pathStr.appendf(" pathB.addCircle(%d, %d, %d, %s);\n", a, b, + c, d ? "SkPath::kCW_Direction" : "SkPath::kCCW_Direction"); + pathStr.appendf(" testPathOp(reporter, path, pathB, %s, filename);\n", + SkPathOpsDebug::OpStr((SkPathOp) op)); + pathStr.appendf("}\n"); + state.outputProgress(pathStr.c_str(), (SkPathOp) op); + } + if (!testPathOp(state.fReporter, pathA, pathB, (SkPathOp) op, "circles")) { + if (state.fReporter->verbose()) { + ++loopNo; + goto skipToNext; + } + } + } + } + } +skipToNext: ; + } + } + } + } +} + +DEF_TEST(PathOpsOpCircleThreaded, reporter) { + initializeTests(reporter, "circleOp"); + PathOpsThreadedTestRunner testRunner(reporter); + for (int a = 0; a < 6; ++a) { // outermost + for (int b = a + 1; b < 7; ++b) { + for (int c = 0 ; c < 6; ++c) { + for (int d = 0; d < 2; ++d) { + *testRunner.fRunnables.append() = new PathOpsThreadedRunnable( + &testOpCirclesMain, a, b, c, d, &testRunner); + } + } + if (!reporter->allowExtendedTest()) goto finish; + } + } +finish: + testRunner.render(); +}
diff --git a/src/third_party/skia/tests/PathOpsOpCubicThreadedTest.cpp b/src/third_party/skia/tests/PathOpsOpCubicThreadedTest.cpp index 751ccc5..0bb8c19 100644 --- a/src/third_party/skia/tests/PathOpsOpCubicThreadedTest.cpp +++ b/src/third_party/skia/tests/PathOpsOpCubicThreadedTest.cpp
@@ -6,6 +6,9 @@ */ #include "PathOpsExtendedTest.h" #include "PathOpsThreadedCommon.h" +#include "SkString.h" + +static int loopNo = 158; static void testOpCubicsMain(PathOpsThreadState* data) { #if DEBUG_SHOW_TEST_NAME @@ -13,11 +16,7 @@ #endif SkASSERT(data); PathOpsThreadState& state = *data; - char pathStr[1024]; // gdb: set print elements 400 - bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter? - if (progress) { - sk_bzero(pathStr, sizeof(pathStr)); - } + SkString pathStr; for (int a = 0 ; a < 6; ++a) { for (int b = a + 1 ; b < 7; ++b) { for (int c = 0 ; c < 6; ++c) { @@ -25,23 +24,6 @@ for (int e = SkPath::kWinding_FillType ; e <= SkPath::kEvenOdd_FillType; ++e) { for (int f = SkPath::kWinding_FillType ; f <= SkPath::kEvenOdd_FillType; ++f) { SkPath pathA, pathB; - if (progress) { - char* str = pathStr; - str += sprintf(str, " path.setFillType(SkPath::k%s_FillType);\n", - e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType - ? "EvenOdd" : "?UNDEFINED"); - str += sprintf(str, " path.moveTo(%d,%d);\n", state.fA, state.fB); - str += sprintf(str, " path.cubicTo(%d,%d, %d,%d, %d,%d);\n", state.fC, state.fD, - b, a, d, c); - str += sprintf(str, " path.close();\n"); - str += sprintf(str, " pathB.setFillType(SkPath::k%s_FillType);\n", - f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType - ? "EvenOdd" : "?UNDEFINED"); - str += sprintf(str, " pathB.moveTo(%d,%d);\n", a, b); - str += sprintf(str, " pathB.cubicTo(%d,%d, %d,%d, %d,%d);\n", c, d, - state.fB, state.fA, state.fD, state.fC); - str += sprintf(str, " pathB.close();\n"); - } pathA.setFillType((SkPath::FillType) e); pathA.moveTo(SkIntToScalar(state.fA), SkIntToScalar(state.fB)); pathA.cubicTo(SkIntToScalar(state.fC), SkIntToScalar(state.fD), SkIntToScalar(b), @@ -52,14 +34,40 @@ pathB.cubicTo(SkIntToScalar(c), SkIntToScalar(d), SkIntToScalar(state.fB), SkIntToScalar(state.fA), SkIntToScalar(state.fD), SkIntToScalar(state.fC)); pathB.close(); - for (int op = 0 ; op <= kXOR_PathOp; ++op) { - if (progress) { - outputProgress(state.fPathStr, pathStr, (SkPathOp) op); + for (int op = 0 ; op <= kXOR_SkPathOp; ++op) { + if (state.fReporter->verbose()) { + pathStr.printf("static void cubicOp%d(skiatest::Reporter* reporter," + " const char* filename) {\n", loopNo); + pathStr.appendf(" SkPath path, pathB;\n"); + pathStr.appendf(" path.setFillType(SkPath::k%s_FillType);\n", + e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType + ? "EvenOdd" : "?UNDEFINED"); + pathStr.appendf(" path.moveTo(%d,%d);\n", state.fA, state.fB); + pathStr.appendf(" path.cubicTo(%d,%d, %d,%d, %d,%d);\n", state.fC, state.fD, + b, a, d, c); + pathStr.appendf(" path.close();\n"); + pathStr.appendf(" pathB.setFillType(SkPath::k%s_FillType);\n", + f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType + ? "EvenOdd" : "?UNDEFINED"); + pathStr.appendf(" pathB.moveTo(%d,%d);\n", a, b); + pathStr.appendf(" pathB.cubicTo(%d,%d, %d,%d, %d,%d);\n", c, d, + state.fB, state.fA, state.fD, state.fC); + pathStr.appendf(" pathB.close();\n"); + pathStr.appendf(" testPathOp(reporter, path, pathB, %s, filename);\n", + SkPathOpsDebug::OpStr((SkPathOp) op)); + pathStr.appendf("}\n"); + state.outputProgress(pathStr.c_str(), (SkPathOp) op); } - testThreadedPathOp(state.fReporter, pathA, pathB, (SkPathOp) op, "cubics"); + if (!testPathOp(state.fReporter, pathA, pathB, (SkPathOp) op, "cubics")) { + if (state.fReporter->verbose()) { + ++loopNo; + goto skipToNext; + } + } } } } +skipToNext: ; } } } @@ -73,8 +81,8 @@ for (int b = a + 1; b < 7; ++b) { for (int c = 0 ; c < 6; ++c) { for (int d = c + 1; d < 7; ++d) { - *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable, - (&testOpCubicsMain, a, b, c, d, &testRunner)); + *testRunner.fRunnables.append() = + new PathOpsThreadedRunnable(&testOpCubicsMain, a, b, c, d, &testRunner); } } if (!reporter->allowExtendedTest()) goto finish; @@ -82,5 +90,4 @@ } finish: testRunner.render(); - ShowTestArray(); }
diff --git a/src/third_party/skia/tests/PathOpsOpLoopThreadedTest.cpp b/src/third_party/skia/tests/PathOpsOpLoopThreadedTest.cpp old mode 100755 new mode 100644 index c50e23b..04caac9 --- a/src/third_party/skia/tests/PathOpsOpLoopThreadedTest.cpp +++ b/src/third_party/skia/tests/PathOpsOpLoopThreadedTest.cpp
@@ -6,6 +6,25 @@ */ #include "PathOpsExtendedTest.h" #include "PathOpsThreadedCommon.h" +#include "SkString.h" + +static int loopNo = 17; + +static void add_point(SkString* str, SkScalar x, SkScalar y) { + int asInt = SkScalarRoundToInt(x); + if (SkIntToScalar(asInt) == x) { + str->appendf("%d", asInt); + } else { + str->appendf("%1.9gf", x); + } + str->appendf(","); + asInt = SkScalarRoundToInt(y); + if (SkIntToScalar(asInt) == y) { + str->appendf("%d", asInt); + } else { + str->appendf("%1.9gf", y); + } +} static void testOpLoopsMain(PathOpsThreadState* data) { #if DEBUG_SHOW_TEST_NAME @@ -13,11 +32,7 @@ #endif SkASSERT(data); PathOpsThreadState& state = *data; - char pathStr[1024]; // gdb: set print elements 400 - bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter? - if (progress) { - sk_bzero(pathStr, sizeof(pathStr)); - } + SkString pathStr; for (int a = 0 ; a < 6; ++a) { for (int b = a + 1 ; b < 7; ++b) { for (int c = 0 ; c < 6; ++c) { @@ -33,17 +48,6 @@ SkPoint endD = { midB.fX - v.fY * state.fD / 3, midB.fY + v.fX * state.fD / 3 }; SkPath pathA, pathB; - if (progress) { - char* str = pathStr; - str += sprintf(str, " path.moveTo(%d,%d);\n", a, b); - str += sprintf(str, " path.cubicTo(%d,%d, %1.9gf,%1.9gf, %1.9gf,%1.9gf);\n", - c, d, endC.fX, endC.fY, endD.fX, endD.fY); - str += sprintf(str, " path.close();\n"); - str += sprintf(str, " pathB.moveTo(%d,%d);\n", c, d); - str += sprintf(str, " pathB.cubicTo(%1.9gf,%1.9gf, %1.9gf,%1.9gf, %d,%d);\n", - endC.fX, endC.fY, endD.fX, endD.fY, a, b); - str += sprintf(str, " pathB.close();\n"); - } pathA.moveTo(SkIntToScalar(a), SkIntToScalar(b)); pathA.cubicTo(SkIntToScalar(c), SkIntToScalar(d), endC.fX, endC.fY, endD.fX, endD.fY); pathA.close(); @@ -51,10 +55,30 @@ pathB.cubicTo(endC.fX, endC.fY, endD.fX, endD.fY, SkIntToScalar(a), SkIntToScalar(b)); pathB.close(); // SkDebugf("%s\n", pathStr); - if (progress) { - outputProgress(state.fPathStr, pathStr, kIntersect_PathOp); + if (state.fReporter->verbose()) { + pathStr.printf("static void loop%d(skiatest::Reporter* reporter," + " const char* filename) {\n", loopNo); + pathStr.appendf(" SkPath path, pathB;\n"); + pathStr.appendf(" path.moveTo(%d,%d);\n", a, b); + pathStr.appendf(" path.cubicTo(%d,%d, ", c, d); + add_point(&pathStr, endC.fX, endC.fY); + pathStr.appendf(", "); + add_point(&pathStr, endD.fX, endD.fY); + pathStr.appendf(");\n"); + pathStr.appendf(" path.close();\n"); + pathStr.appendf(" pathB.moveTo(%d,%d);\n", c, d); + pathStr.appendf(" pathB.cubicTo("); + add_point(&pathStr, endC.fX, endC.fY); + pathStr.appendf(", "); + add_point(&pathStr, endD.fX, endD.fY); + pathStr.appendf(", %d,%d);\n", a, b); + pathStr.appendf(" pathB.close();\n"); + pathStr.appendf(" testPathOp(reporter, path, pathB, kIntersect_SkPathOp," + " filename);\n"); + pathStr.appendf("}\n"); + state.outputProgress(pathStr.c_str(), kIntersect_SkPathOp); } - testThreadedPathOp(state.fReporter, pathA, pathB, kIntersect_PathOp, "loops"); + testPathOp(state.fReporter, pathA, pathB, kIntersect_SkPathOp, "loops"); } } } @@ -62,17 +86,14 @@ } DEF_TEST(PathOpsOpLoopsThreaded, reporter) { - if (!FLAGS_runFail) { - return; - } - initializeTests(reporter, "cubicOp"); + initializeTests(reporter, "loopOp"); PathOpsThreadedTestRunner testRunner(reporter); for (int a = 0; a < 6; ++a) { // outermost for (int b = a + 1; b < 7; ++b) { for (int c = 0 ; c < 6; ++c) { for (int d = c + 1; d < 7; ++d) { - *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable, - (&testOpLoopsMain, a, b, c, d, &testRunner)); + *testRunner.fRunnables.append() = + new PathOpsThreadedRunnable(&testOpLoopsMain, a, b, c, d, &testRunner); } } if (!reporter->allowExtendedTest()) goto finish; @@ -80,30 +101,4 @@ } finish: testRunner.render(); - ShowTestArray(); -} - -DEF_TEST(PathOpsOpLoops, reporter) { - if (!FLAGS_runFail) { - return; - } - initializeTests(reporter, "cubicOp"); - PathOpsThreadState state; - state.fReporter = reporter; - SkBitmap bitmap; - state.fBitmap = &bitmap; - char pathStr[PATH_STR_SIZE]; - state.fPathStr = pathStr; - for (state.fA = 0; state.fA < 6; ++state.fA) { // outermost - for (state.fB = state.fA + 1; state.fB < 7; ++state.fB) { - for (state.fC = 0 ; state.fC < 6; ++state.fC) { - for (state.fD = state.fC + 1; state.fD < 7; ++state.fD) { - testOpLoopsMain(&state); - } - } - if (!reporter->allowExtendedTest()) goto finish; - } - } -finish: - ShowTestArray(); }
diff --git a/src/third_party/skia/tests/PathOpsOpRectThreadedTest.cpp b/src/third_party/skia/tests/PathOpsOpRectThreadedTest.cpp index 1b6e4e8..e904a8d 100644 --- a/src/third_party/skia/tests/PathOpsOpRectThreadedTest.cpp +++ b/src/third_party/skia/tests/PathOpsOpRectThreadedTest.cpp
@@ -6,6 +6,7 @@ */ #include "PathOpsExtendedTest.h" #include "PathOpsThreadedCommon.h" +#include "SkString.h" // four rects, of four sizes // for 3 smaller sizes, tall, wide @@ -14,38 +15,19 @@ // not included, square, tall, wide (2 bits) // cw or ccw (1 bit) +static int loopNo = 6; + static void testPathOpsRectsMain(PathOpsThreadState* data) { SkASSERT(data); PathOpsThreadState& state = *data; - char pathStr[1024]; // gdb: set print elements 400 - bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter? - if (progress) { - sk_bzero(pathStr, sizeof(pathStr)); - } + SkString pathStr; for (int a = 0 ; a < 6; ++a) { for (int b = a + 1 ; b < 7; ++b) { for (int c = 0 ; c < 6; ++c) { for (int d = c + 1 ; d < 7; ++d) { for (int e = SkPath::kWinding_FillType ; e <= SkPath::kEvenOdd_FillType; ++e) { for (int f = SkPath::kWinding_FillType ; f <= SkPath::kEvenOdd_FillType; ++f) { - if (progress) { - char* str = pathStr; - str += sprintf(str, " path.setFillType(SkPath::k%s_FillType);\n", - e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType - ? "EvenOdd" : "?UNDEFINED"); - str += sprintf(str, " path.addRect(%d, %d, %d, %d," - " SkPath::kCW_Direction);\n", state.fA, state.fA, state.fB, state.fB); - str += sprintf(str, " path.addRect(%d, %d, %d, %d," - " SkPath::kCW_Direction);\n", state.fC, state.fC, state.fD, state.fD); - str += sprintf(str, " pathB.setFillType(SkPath::k%s_FillType);\n", - f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType - ? "EvenOdd" : "?UNDEFINED"); - str += sprintf(str, " pathB.addRect(%d, %d, %d, %d," - " SkPath::kCW_Direction);\n", a, a, b, b); - str += sprintf(str, " pathB.addRect(%d, %d, %d, %d," - " SkPath::kCW_Direction);\n", c, c, d, d); - } SkPath pathA, pathB; pathA.setFillType((SkPath::FillType) e); pathA.addRect(SkIntToScalar(state.fA), SkIntToScalar(state.fA), SkIntToScalar(state.fB), @@ -59,14 +41,41 @@ pathB.addRect(SkIntToScalar(c), SkIntToScalar(c), SkIntToScalar(d), SkIntToScalar(d), SkPath::kCW_Direction); pathB.close(); - for (int op = 0 ; op <= kXOR_PathOp; ++op) { - if (progress) { - outputProgress(state.fPathStr, pathStr, (SkPathOp) op); + for (int op = 0 ; op <= kXOR_SkPathOp; ++op) { + if (state.fReporter->verbose()) { + pathStr.printf( + "static void rects%d(skiatest::Reporter* reporter," + "const char* filename) {\n", loopNo); + pathStr.appendf(" SkPath path, pathB;"); + pathStr.appendf(" path.setFillType(SkPath::k%s_FillType);\n", + e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType + ? "EvenOdd" : "?UNDEFINED"); + pathStr.appendf(" path.addRect(%d, %d, %d, %d," + " SkPath::kCW_Direction);\n", state.fA, state.fA, state.fB, state.fB); + pathStr.appendf(" path.addRect(%d, %d, %d, %d," + " SkPath::kCW_Direction);\n", state.fC, state.fC, state.fD, state.fD); + pathStr.appendf(" pathB.setFillType(SkPath::k%s_FillType);\n", + f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType + ? "EvenOdd" : "?UNDEFINED"); + pathStr.appendf(" pathB.addRect(%d, %d, %d, %d," + " SkPath::kCW_Direction);\n", a, a, b, b); + pathStr.appendf(" pathB.addRect(%d, %d, %d, %d," + " SkPath::kCW_Direction);\n", c, c, d, d); + pathStr.appendf(" testPathOp(reporter, path, pathB, %s, filename);\n", + SkPathOpsDebug::OpStr((SkPathOp) op)); + pathStr.appendf("}\n\n"); + state.outputProgress(pathStr.c_str(), (SkPathOp) op); } - testThreadedPathOp(state.fReporter, pathA, pathB, (SkPathOp) op, "rects"); + if (!testPathOp(state.fReporter, pathA, pathB, (SkPathOp) op, "rects")) { + if (state.fReporter->verbose()) { + ++loopNo; + goto skipToNext; + } + } } } } +skipToNext: ; } } } @@ -80,8 +89,8 @@ for (int b = a + 1; b < 7; ++b) { for (int c = 0 ; c < 6; ++c) { for (int d = c + 1; d < 7; ++d) { - *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable, - (&testPathOpsRectsMain, a, b, c, d, &testRunner)); + *testRunner.fRunnables.append() = new PathOpsThreadedRunnable( + &testPathOpsRectsMain, a, b, c, d, &testRunner); } } if (!reporter->allowExtendedTest()) goto finish;
diff --git a/src/third_party/skia/tests/PathOpsOpTest.cpp b/src/third_party/skia/tests/PathOpsOpTest.cpp index 7ca15e1..6cc9dc7 100644 --- a/src/third_party/skia/tests/PathOpsOpTest.cpp +++ b/src/third_party/skia/tests/PathOpsOpTest.cpp
@@ -7,7 +7,27 @@ #include "PathOpsExtendedTest.h" #include "PathOpsTestCommon.h" -#define TEST(name) { name, #name } +class PathTest_Private { +public: + PathTest_Private(SkPath* path) + : fPath(path) {} + + void setPt(int index, SkScalar x, SkScalar y) { + fPath->setPt(index, x, y); + } + + SkPath* fPath; +}; + +static void path_edit(const SkPoint& from, const SkPoint& to, SkPath* path) { + PathTest_Private testPath(path); + for (int index = 0; index < path->countPoints(); ++index) { + if (SkDPoint::ApproximatelyEqual(path->getPoint(index), from)) { + testPath.setPt(index, to.fX, to.fY); + return; + } + } +} static void cubicOp1d(skiatest::Reporter* reporter, const char* filename) { SkPath path, pathB; @@ -19,7 +39,7 @@ pathB.moveTo(0,1); pathB.cubicTo(0,1, 1,0, 2,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp2d(skiatest::Reporter* reporter, const char* filename) { @@ -32,7 +52,7 @@ pathB.moveTo(0,1); pathB.cubicTo(0,1, 2,0, 1,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp3d(skiatest::Reporter* reporter, const char* filename) { @@ -45,7 +65,7 @@ pathB.moveTo(0,1); pathB.cubicTo(0,1, 1,0, 3,2); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp5d(skiatest::Reporter* reporter, const char* filename) { @@ -58,7 +78,7 @@ pathB.moveTo(0,1); pathB.cubicTo(0,2, 1,0, 2,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp6d(skiatest::Reporter* reporter, const char* filename) { @@ -71,7 +91,7 @@ pathB.moveTo(0,1); pathB.cubicTo(0,3, 1,0, 6,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp7d(skiatest::Reporter* reporter, const char* filename) { @@ -84,7 +104,7 @@ pathB.moveTo(0,1); pathB.cubicTo(0,3, 1,0, 4,3); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp8d(skiatest::Reporter* reporter, const char* filename) { @@ -97,7 +117,7 @@ pathB.moveTo(0,1); pathB.cubicTo(0,4, 1,0, 5,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp9d(skiatest::Reporter* reporter, const char* filename) { @@ -110,7 +130,7 @@ pathB.moveTo(0,1); pathB.cubicTo(1,2, 1,0, 6,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void quadOp9d(skiatest::Reporter* reporter, const char* filename) { @@ -125,7 +145,7 @@ pathB.quadTo(1,2, 1.4f,1); pathB.quadTo(3,0.4f, 6,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void lineOp9d(skiatest::Reporter* reporter, const char* filename) { @@ -144,7 +164,7 @@ pathB.lineTo(3,0.4f); pathB.lineTo(6,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp1i(skiatest::Reporter* reporter, const char* filename) { @@ -157,7 +177,7 @@ pathB.moveTo(0,1); pathB.cubicTo(1,2, 1,0, 2,1); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp10d(skiatest::Reporter* reporter, const char* filename) { @@ -170,7 +190,7 @@ pathB.moveTo(0,1); pathB.cubicTo(1,4, 1,0, 3,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp11d(skiatest::Reporter* reporter, const char* filename) { @@ -183,7 +203,7 @@ pathB.moveTo(0,1); pathB.cubicTo(1,5, 1,0, 4,3); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp12d(skiatest::Reporter* reporter, const char* filename) { @@ -196,7 +216,7 @@ pathB.moveTo(0,1); pathB.cubicTo(0,1, 1,0, 6,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp13d(skiatest::Reporter* reporter, const char* filename) { @@ -209,7 +229,7 @@ pathB.moveTo(0,1); pathB.cubicTo(3,5, 1,0, 5,4); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp14d(skiatest::Reporter* reporter, const char* filename) { @@ -222,7 +242,7 @@ pathB.moveTo(0,2); pathB.cubicTo(1,2, 1,0, 2,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp15d(skiatest::Reporter* reporter, const char* filename) { @@ -235,7 +255,7 @@ pathB.moveTo(0,2); pathB.cubicTo(1,2, 1,0, 6,3); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp16d(skiatest::Reporter* reporter, const char* filename) { @@ -248,7 +268,7 @@ pathB.moveTo(0,3); pathB.cubicTo(0,1, 2,0, 1,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp17d(skiatest::Reporter* reporter, const char* filename) { @@ -261,7 +281,7 @@ pathB.moveTo(0,4); pathB.cubicTo(1,2, 2,0, 2,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp18d(skiatest::Reporter* reporter, const char* filename) { @@ -274,7 +294,7 @@ pathB.moveTo(0,2); pathB.cubicTo(1,2, 1,0, 5,3); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp19i(skiatest::Reporter* reporter, const char* filename) { @@ -287,7 +307,7 @@ pathB.moveTo(1,2); pathB.cubicTo(2,6, 2,0, 1,0); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp20d(skiatest::Reporter* reporter, const char* filename) { @@ -300,7 +320,7 @@ pathB.moveTo(0,6); pathB.cubicTo(1,2, 1,0, 1,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp21d(skiatest::Reporter* reporter, const char* filename) { @@ -313,7 +333,7 @@ pathB.moveTo(1,2); pathB.cubicTo(5,6, 1,0, 1,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp22d(skiatest::Reporter* reporter, const char* filename) { @@ -326,7 +346,7 @@ pathB.moveTo(0,3); pathB.cubicTo(1,2, 1,0, 3,2); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp23d(skiatest::Reporter* reporter, const char* filename) { @@ -339,7 +359,7 @@ pathB.moveTo(0,4); pathB.cubicTo(1,2, 1,0, 2,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp24d(skiatest::Reporter* reporter, const char* filename) { @@ -352,63 +372,63 @@ pathB.moveTo(0,2); pathB.cubicTo(2,3, 1,0, 2,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void testIntersect1(skiatest::Reporter* reporter, const char* filename) { SkPath one, two; one.addRect(0, 0, 6, 6, SkPath::kCW_Direction); two.addRect(3, 3, 9, 9, SkPath::kCW_Direction); - testPathOp(reporter, one, two, kIntersect_PathOp, filename); + testPathOp(reporter, one, two, kIntersect_SkPathOp, filename); } static void testUnion1(skiatest::Reporter* reporter, const char* filename) { SkPath one, two; one.addRect(0, 0, 6, 6, SkPath::kCW_Direction); two.addRect(3, 3, 9, 9, SkPath::kCW_Direction); - testPathOp(reporter, one, two, kUnion_PathOp, filename); + testPathOp(reporter, one, two, kUnion_SkPathOp, filename); } static void testDiff1(skiatest::Reporter* reporter, const char* filename) { SkPath one, two; one.addRect(0, 0, 6, 6, SkPath::kCW_Direction); two.addRect(3, 3, 9, 9, SkPath::kCW_Direction); - testPathOp(reporter, one, two, kDifference_PathOp, filename); + testPathOp(reporter, one, two, kDifference_SkPathOp, filename); } static void testXor1(skiatest::Reporter* reporter, const char* filename) { SkPath one, two; one.addRect(0, 0, 6, 6, SkPath::kCW_Direction); two.addRect(3, 3, 9, 9, SkPath::kCW_Direction); - testPathOp(reporter, one, two, kXOR_PathOp, filename); + testPathOp(reporter, one, two, kXOR_SkPathOp, filename); } static void testIntersect2(skiatest::Reporter* reporter, const char* filename) { SkPath one, two; one.addRect(0, 0, 6, 6, SkPath::kCW_Direction); two.addRect(0, 3, 9, 9, SkPath::kCW_Direction); - testPathOp(reporter, one, two, kIntersect_PathOp, filename); + testPathOp(reporter, one, two, kIntersect_SkPathOp, filename); } static void testUnion2(skiatest::Reporter* reporter, const char* filename) { SkPath one, two; one.addRect(0, 0, 6, 6, SkPath::kCW_Direction); two.addRect(0, 3, 9, 9, SkPath::kCW_Direction); - testPathOp(reporter, one, two, kUnion_PathOp, filename); + testPathOp(reporter, one, two, kUnion_SkPathOp, filename); } static void testDiff2(skiatest::Reporter* reporter, const char* filename) { SkPath one, two; one.addRect(0, 0, 6, 6, SkPath::kCW_Direction); two.addRect(0, 3, 9, 9, SkPath::kCW_Direction); - testPathOp(reporter, one, two, kDifference_PathOp, filename); + testPathOp(reporter, one, two, kDifference_SkPathOp, filename); } static void testXor2(skiatest::Reporter* reporter, const char* filename) { SkPath one, two; one.addRect(0, 0, 6, 6, SkPath::kCW_Direction); two.addRect(0, 3, 9, 9, SkPath::kCW_Direction); - testPathOp(reporter, one, two, kXOR_PathOp, filename); + testPathOp(reporter, one, two, kXOR_SkPathOp, filename); } static void testOp1d(skiatest::Reporter* reporter, const char* filename) { @@ -419,7 +439,7 @@ pathB.setFillType(SkPath::kWinding_FillType); pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction); pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void testOp2d(skiatest::Reporter* reporter, const char* filename) { @@ -430,7 +450,7 @@ pathB.setFillType(SkPath::kEvenOdd_FillType); pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction); pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void testOp3d(skiatest::Reporter* reporter, const char* filename) { @@ -441,7 +461,7 @@ pathB.setFillType(SkPath::kWinding_FillType); pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction); pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void testOp1u(skiatest::Reporter* reporter, const char* filename) { @@ -452,7 +472,7 @@ pathB.setFillType(SkPath::kWinding_FillType); pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction); pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void testOp4d(skiatest::Reporter* reporter, const char* filename) { @@ -463,7 +483,7 @@ pathB.setFillType(SkPath::kWinding_FillType); pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction); pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void testOp5d(skiatest::Reporter* reporter, const char* filename) { @@ -474,7 +494,7 @@ pathB.setFillType(SkPath::kEvenOdd_FillType); pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction); pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void testOp6d(skiatest::Reporter* reporter, const char* filename) { @@ -485,7 +505,7 @@ pathB.setFillType(SkPath::kWinding_FillType); pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction); pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void testOp7d(skiatest::Reporter* reporter, const char* filename) { @@ -496,7 +516,7 @@ pathB.setFillType(SkPath::kEvenOdd_FillType); pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction); pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void testOp2u(skiatest::Reporter* reporter, const char* filename) { @@ -507,7 +527,7 @@ pathB.setFillType(SkPath::kWinding_FillType); pathB.addRect(0, 0, 3, 3, SkPath::kCW_Direction); pathB.addRect(1, 1, 2, 2, SkPath::kCW_Direction); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void testOp8d(skiatest::Reporter* reporter, const char* filename) { @@ -516,7 +536,7 @@ pathB.moveTo(577330, 1971.72f); pathB.cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp25i(skiatest::Reporter* reporter, const char* filename) { SkPath path, pathB; @@ -528,7 +548,7 @@ pathB.moveTo(0,5); pathB.cubicTo(2,3, 1,0, 4,2); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp26d(skiatest::Reporter* reporter, const char* filename) { @@ -541,7 +561,7 @@ pathB.moveTo(0,4); pathB.cubicTo(2,3, 1,0, 4,3); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp27d(skiatest::Reporter* reporter, const char* filename) { @@ -554,7 +574,7 @@ pathB.moveTo(0,1); pathB.cubicTo(2,5, 1,0, 6,3); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp28u(skiatest::Reporter* reporter, const char* filename) { @@ -567,7 +587,7 @@ pathB.moveTo(0,6); pathB.cubicTo(2,3, 1,0, 4,1); pathB.close(); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void cubicOp29d(skiatest::Reporter* reporter, const char* filename) { @@ -580,7 +600,7 @@ pathB.moveTo(0,6); pathB.cubicTo(2,4, 1,0, 5,2); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp30d(skiatest::Reporter* reporter, const char* filename) { @@ -593,7 +613,7 @@ pathB.moveTo(0,6); pathB.cubicTo(3,5, 1,0, 5,2); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp31d(skiatest::Reporter* reporter, const char* filename) { @@ -606,7 +626,7 @@ pathB.moveTo(1,2); pathB.cubicTo(0,4, 2,0, 3,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp31u(skiatest::Reporter* reporter, const char* filename) { @@ -619,7 +639,7 @@ pathB.moveTo(1,2); pathB.cubicTo(0,4, 2,0, 3,0); pathB.close(); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void cubicOp31x(skiatest::Reporter* reporter, const char* filename) { @@ -632,7 +652,7 @@ pathB.moveTo(1,2); pathB.cubicTo(0,4, 2,0, 3,0); pathB.close(); - testPathOp(reporter, path, pathB, kXOR_PathOp, filename); + testPathOp(reporter, path, pathB, kXOR_SkPathOp, filename); } static void cubicOp32d(skiatest::Reporter* reporter, const char* filename) { @@ -645,7 +665,7 @@ pathB.moveTo(0,6); pathB.cubicTo(1,3, 1,0, 2,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp33i(skiatest::Reporter* reporter, const char* filename) { @@ -658,7 +678,7 @@ pathB.moveTo(0,6); pathB.cubicTo(1,3, 1,0, 2,1); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp34d(skiatest::Reporter* reporter, const char* filename) { @@ -671,7 +691,7 @@ pathB.moveTo(1,2); pathB.cubicTo(1,3, 1,0, 5,3); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp35d(skiatest::Reporter* reporter, const char* filename) { @@ -684,7 +704,7 @@ pathB.moveTo(1,2); pathB.cubicTo(0,4, 1,0, 5,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp36u(skiatest::Reporter* reporter, const char* filename) { @@ -697,7 +717,7 @@ pathB.moveTo(0,2); pathB.cubicTo(1,5, 1,0, 6,1); pathB.close(); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void cubicOp37d(skiatest::Reporter* reporter, const char* filename) { @@ -710,14 +730,9 @@ pathB.moveTo(1,6); pathB.cubicTo(3,4, 1,0, 6,2); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } -// this fails to detect a cubic/cubic intersection -// the slight overlap is missed when the cubics are approximated by quadratics -// and the subsequent line/cubic intersection also (correctly) misses the intersection -// if the line/cubic was a matching line/approx.quadratic then the missing intersection -// could have been detected static void cubicOp38d(skiatest::Reporter* reporter, const char* filename) { SkPath path, pathB; path.setFillType(SkPath::kWinding_FillType); @@ -728,7 +743,7 @@ pathB.moveTo(2,3); pathB.cubicTo(1,4, 1,0, 6,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp39d(skiatest::Reporter* reporter, const char* filename) { @@ -741,7 +756,7 @@ pathB.moveTo(1,5); pathB.cubicTo(3,4, 1,0, 3,2); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp40d(skiatest::Reporter* reporter, const char* filename) { @@ -754,7 +769,7 @@ pathB.moveTo(2,3); pathB.cubicTo(2,4, 1,0, 5,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp41i(skiatest::Reporter* reporter, const char* filename) { @@ -767,7 +782,7 @@ pathB.moveTo(3,4); pathB.cubicTo(4,6, 1,0, 6,2); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp42d(skiatest::Reporter* reporter, const char* filename) { @@ -780,7 +795,7 @@ pathB.moveTo(5,6); pathB.cubicTo(4,5, 1,0, 2,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp43d(skiatest::Reporter* reporter, const char* filename) { @@ -793,7 +808,7 @@ pathB.moveTo(0,4); pathB.cubicTo(1,3, 2,0, 2,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp44d(skiatest::Reporter* reporter, const char* filename) { @@ -806,7 +821,7 @@ pathB.moveTo(0,4); pathB.cubicTo(2,3, 2,0, 6,3); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp45d(skiatest::Reporter* reporter, const char* filename) { @@ -819,7 +834,7 @@ pathB.moveTo(0,4); pathB.cubicTo(2,3, 2,0, 4,2); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp46d(skiatest::Reporter* reporter, const char* filename) { @@ -832,7 +847,7 @@ pathB.moveTo(0,5); pathB.cubicTo(2,4, 2,0, 5,3); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp47d(skiatest::Reporter* reporter, const char* filename) { @@ -845,7 +860,7 @@ pathB.moveTo(2,6); pathB.cubicTo(4,5, 1,0, 6,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp48d(skiatest::Reporter* reporter, const char* filename) { @@ -858,7 +873,7 @@ pathB.moveTo(1,5); pathB.cubicTo(2,3, 2,0, 3,2); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp49d(skiatest::Reporter* reporter, const char* filename) { @@ -871,7 +886,7 @@ pathB.moveTo(2,3); pathB.cubicTo(1,4, 2,0, 5,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp50d(skiatest::Reporter* reporter, const char* filename) { @@ -884,7 +899,7 @@ pathB.moveTo(0,5); pathB.cubicTo(1,5, 3,0, 6,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp51d(skiatest::Reporter* reporter, const char* filename) { @@ -897,7 +912,7 @@ pathB.moveTo(1,4); pathB.cubicTo(0,6, 3,0, 2,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp52d(skiatest::Reporter* reporter, const char* filename) { @@ -910,7 +925,7 @@ pathB.moveTo(4,5); pathB.cubicTo(3,4, 2,0, 2,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp53d(skiatest::Reporter* reporter, const char* filename) { @@ -923,7 +938,7 @@ pathB.moveTo(3,5); pathB.cubicTo(1,2, 3,0, 2,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp54d(skiatest::Reporter* reporter, const char* filename) { @@ -936,7 +951,7 @@ pathB.moveTo(4,5); pathB.cubicTo(2,4, 4,0, 3,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp55d(skiatest::Reporter* reporter, const char* filename) { @@ -949,7 +964,7 @@ pathB.moveTo(2,3); pathB.cubicTo(0,5, 5,0, 3,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp56d(skiatest::Reporter* reporter, const char* filename) { @@ -962,7 +977,7 @@ pathB.moveTo(0,5); pathB.cubicTo(1,2, 1,0, 6,2); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp57d(skiatest::Reporter* reporter, const char* filename) { @@ -975,7 +990,7 @@ pathB.moveTo(4,5); pathB.cubicTo(4,6, 5,0, 5,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp58d(skiatest::Reporter* reporter, const char* filename) { @@ -988,7 +1003,7 @@ pathB.moveTo(5,6); pathB.cubicTo(3,5, 5,0, 4,3); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp59d(skiatest::Reporter* reporter, const char* filename) { @@ -1001,7 +1016,7 @@ pathB.moveTo(0,4); pathB.cubicTo(1,4, 1,0, 6,5); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp60d(skiatest::Reporter* reporter, const char* filename) { @@ -1014,7 +1029,7 @@ pathB.moveTo(0,6); pathB.cubicTo(2,5, 2,0, 6,4); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp61d(skiatest::Reporter* reporter, const char* filename) { @@ -1027,7 +1042,7 @@ pathB.moveTo(2,3); pathB.cubicTo(1,6, 2,1, 5,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp62d(skiatest::Reporter* reporter, const char* filename) { @@ -1040,7 +1055,7 @@ pathB.moveTo(3,5); pathB.cubicTo(4,5, 3,1, 6,5); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp63d(skiatest::Reporter* reporter, const char* filename) { @@ -1053,7 +1068,7 @@ pathB.moveTo(2,3); pathB.cubicTo(3,5, 3,2, 4,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp64d(skiatest::Reporter* reporter, const char* filename) { @@ -1066,7 +1081,7 @@ pathB.cubicTo(0,3, 1,0, 1,0); pathB.lineTo(0,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp65d(skiatest::Reporter* reporter, const char* filename) { @@ -1079,7 +1094,7 @@ pathB.cubicTo(0,1, 1,0, 5,1); pathB.lineTo(0,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void rectOp1d(skiatest::Reporter* reporter, const char* filename) { @@ -1092,7 +1107,7 @@ pathB.cubicTo(0,3, 1,0, 1,0); pathB.lineTo(0,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp66u(skiatest::Reporter* reporter, const char* filename) { @@ -1105,7 +1120,7 @@ pathB.moveTo(2,4); pathB.cubicTo(3,5, 1,0, 6,2); pathB.close(); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void cubicOp67u(skiatest::Reporter* reporter, const char* filename) { @@ -1118,7 +1133,7 @@ pathB.cubicTo(1,3, 5,3, 6,1); pathB.lineTo(0,5); pathB.close(); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void cubicOp68u(skiatest::Reporter* reporter, const char* filename) { @@ -1129,7 +1144,7 @@ pathB.moveTo(1,4); pathB.cubicTo(0,5, 5,0, 5,4); pathB.close(); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void cubicOp69d(skiatest::Reporter* reporter, const char* filename) { @@ -1140,15 +1155,15 @@ pathB.moveTo(1,3); pathB.cubicTo(0,2, 3,1, 1,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } SkPathOp ops[] = { - kUnion_PathOp, - kXOR_PathOp, - kReverseDifference_PathOp, - kXOR_PathOp, - kReverseDifference_PathOp, + kUnion_SkPathOp, + kXOR_SkPathOp, + kReverseDifference_SkPathOp, + kXOR_SkPathOp, + kReverseDifference_SkPathOp, }; static void rRect1(skiatest::Reporter* reporter, const char* filename) { @@ -1181,7 +1196,7 @@ path.setFillType(SkPath::kInverseEvenOdd_FillType); for (int index = 0; index < 5; ++index) { testPathOp(reporter, path, paths[index], ops[index], filename); - Op(path, paths[index], ops[index], &path); + REPORTER_ASSERT(reporter, Op(path, paths[index], ops[index], &path)); } } @@ -1216,7 +1231,7 @@ pathB.lineTo(246,4); pathB.lineTo(189,4); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skp2(skiatest::Reporter* reporter, const char* filename) { @@ -1236,7 +1251,7 @@ pathB.lineTo(823.000000f, 1028.00000f); pathB.lineTo(258.000000f, 1028.00000f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skp3(skiatest::Reporter* reporter, const char* filename) { @@ -1268,7 +1283,7 @@ pathB.lineTo(973.000000f, 510.000000f); pathB.lineTo(717.000000f, 510.000000f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skp4(skiatest::Reporter* reporter, const char* filename) { @@ -1300,7 +1315,7 @@ pathB.lineTo(306.000000f, 617.000000f); pathB.lineTo(306.000000f, 590.000000f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skp5(skiatest::Reporter* reporter, const char* filename) { @@ -1326,7 +1341,7 @@ pathB.lineTo(10.0000000f, 234.000000f); pathB.cubicTo(10.0000000f, 229.581726f, 13.5817204f, 226.000000f, 18.0000000f, 226.000000f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp70d(skiatest::Reporter* reporter, const char* filename) { @@ -1339,7 +1354,7 @@ pathB.moveTo(0,4); pathB.cubicTo(0,5, 1,0, 5,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp71d(skiatest::Reporter* reporter, const char* filename) { @@ -1352,7 +1367,7 @@ pathB.moveTo(1,4); pathB.cubicTo(4,6, 1,0, 5,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp72i(skiatest::Reporter* reporter, const char* filename) { @@ -1365,7 +1380,7 @@ pathB.moveTo(2,5); pathB.cubicTo(4,5, 1,0, 5,0); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp73d(skiatest::Reporter* reporter, const char* filename) { @@ -1380,7 +1395,7 @@ pathB.cubicTo(4,6, 1,0, 4,3); pathB.lineTo(0,4); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp74d(skiatest::Reporter* reporter, const char* filename) { @@ -1395,7 +1410,7 @@ pathB.cubicTo(1,5, 1,0, 5,1); pathB.lineTo(1,5); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp75d(skiatest::Reporter* reporter, const char* filename) { @@ -1410,7 +1425,7 @@ pathB.cubicTo(4,6, 1,0, 4,0); pathB.lineTo(1,5); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp76u(skiatest::Reporter* reporter, const char* filename) { @@ -1423,7 +1438,7 @@ pathB.moveTo(0,2); pathB.cubicTo(3,5, 1,0, 2,0); pathB.close(); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void cubicOp77i(skiatest::Reporter* reporter, const char* filename) { @@ -1438,7 +1453,7 @@ pathB.cubicTo(2,3, 1,0, 3,1); pathB.lineTo(0,2); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp78u(skiatest::Reporter* reporter, const char* filename) { @@ -1453,7 +1468,7 @@ pathB.cubicTo(1,6, 6,1, 6,1); pathB.lineTo(0,5); pathB.close(); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void cubicOp79u(skiatest::Reporter* reporter, const char* filename) { @@ -1466,7 +1481,7 @@ pathB.moveTo(0,1); pathB.cubicTo(4,6, 1,0, 3,1); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp80i(skiatest::Reporter* reporter, const char* filename) { @@ -1481,7 +1496,7 @@ pathB.cubicTo(3,4, 1,0, 3,2); pathB.lineTo(1,2); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp81d(skiatest::Reporter* reporter, const char* filename) { @@ -1494,7 +1509,7 @@ pathB.moveTo(3,4); pathB.cubicTo(4,5, 1,0, 6,4); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp82i(skiatest::Reporter* reporter, const char* filename) { @@ -1509,7 +1524,7 @@ pathB.cubicTo(0,3, 1,0, 3,2); pathB.lineTo(2,5); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp83i(skiatest::Reporter* reporter, const char* filename) { @@ -1524,7 +1539,7 @@ pathB.cubicTo(1,4, 1,0, 3,0); pathB.lineTo(1,2); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp84d(skiatest::Reporter* reporter, const char* filename) { @@ -1537,7 +1552,7 @@ pathB.moveTo(3,6); pathB.cubicTo(2,3, 4,0, 3,2); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void skpClip1(skiatest::Reporter* reporter, const char* filename) { @@ -1567,7 +1582,7 @@ pathB.lineTo(1247.00000f, 907.000000f); pathB.lineTo(1247.00000f, 876.000000f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpClip2(skiatest::Reporter* reporter, const char* filename) { @@ -1593,7 +1608,7 @@ pathB.lineTo(130.000000f, 11416.0000f); pathB.cubicTo(130.000000f, 11415.4473f, 130.895432f, 11415.0000f, 132.000000f, 11415.0000f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skp96prezzi1(skiatest::Reporter* reporter, const char* filename) { @@ -1623,7 +1638,7 @@ pathB.lineTo(253.000000f, 669.000000f); pathB.lineTo(156.000000f, 669.000000f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpancestry_com1(skiatest::Reporter* reporter, const char* filename) { @@ -1649,7 +1664,7 @@ pathB.lineTo(157.000000f, 930.000000f); pathB.cubicTo(157.000000f, 927.790833f, 158.790863f, 926.000000f, 161.000000f, 926.000000f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpeldorado_com_ua1(skiatest::Reporter* reporter, const char* filename) { @@ -1673,7 +1688,7 @@ pathB.cubicTo(283.840179f, 304.431458f, 300.126587f, 291.000000f, 316.695129f, 291.000000f); pathB.lineTo(1006.69513f, 291.000000f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpbyte_com1(skiatest::Reporter* reporter, const char* filename) { @@ -1701,7 +1716,7 @@ pathB.lineTo(963.000000f, 19.0000000f); pathB.cubicTo(963.000000f, 16.2385750f, 965.238586f, 14.0000000f, 968.000000f, 14.0000000f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skphealth_com76(skiatest::Reporter* reporter, const char* filename) { @@ -1719,17 +1734,10 @@ SkPath pathB; pathB.setFillType(SkPath::kWinding_FillType); pathB.moveTo(704.000000f, 3.00000000f); -#if 0 - pathB.lineTo(719.500000f, 3.00000000f); - pathB.lineTo(705.000000f, 33.0000000f); - pathB.lineTo(704.000000f, 33.0000000f); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); -#else pathB.lineTo(704.000000f, 33.0000000f); pathB.lineTo(705.000000f, 33.0000000f); pathB.lineTo(719.500000f, 3.00000000f); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); -#endif + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpahrefs_com88(skiatest::Reporter* reporter, const char* filename) { @@ -1755,7 +1763,7 @@ pathB.lineTo(1088.00000f, 6.00000000f); pathB.lineTo(1088.00000f, 19.0000000f); pathB.lineTo(1101.00000f, 32.0000000f); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpahrefs_com29(skiatest::Reporter* reporter, const char* filename) { @@ -1785,7 +1793,7 @@ pathB.lineTo(1049.00000f, 19.0000000f); pathB.lineTo(1073.00000f, 31.0000000f); pathB.lineTo(1074.00000f, 32.0000000f); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp85d(skiatest::Reporter* reporter, const char* filename) { @@ -1799,12 +1807,9 @@ pathB.moveTo(0,1); pathB.cubicTo(2,6, 1,0, 6,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } -// this fails because the pair of nearly coincident cubics intersect at the ends -// but the line connected to one of the cubics at the same point does not intersect -// the other static void skpkkiste_to98(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1830,7 +1835,7 @@ pathB.cubicTo(91, 124.238579f, 93.2385788f, 122, 96, 122); pathB.lineTo(258, 122); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void issue1417(skiatest::Reporter* reporter, const char* filename) { @@ -1941,8 +1946,8 @@ path2.lineTo(113.232177734375f, 173.5789947509765625f); path2.lineTo(113.232177734375f, 173.5789947509765625f); path2.close(); - - testPathOp(reporter, path1, path2, kUnion_PathOp, filename); + // FIXME : difficult data, circle back later + testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename); } static void issue1418(skiatest::Reporter* reporter, const char* filename) { @@ -1968,7 +1973,7 @@ path2.quadTo(0.79289329051971435547f, 0.50000005960464477539f, 0.64644664525985717773f, 0.35355341434478759766f); path2.quadTo(0.50000005960464477539f, 0.20710679888725280762f, 0.50000005960464477539f, 0); path2.quadTo(0.50000005960464477539f, -0.20710679888725280762f, 0.64644664525985717773f, -0.35355341434478759766f); - testPathOp(reporter, path1, path2, kIntersect_PathOp, filename); + testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename); } static void cubicOp85i(skiatest::Reporter* reporter, const char* filename) { @@ -1981,7 +1986,7 @@ pathB.moveTo(3, 4); pathB.cubicTo(4, 6, 4, 3, 5, 1); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void issue1418b(skiatest::Reporter* reporter, const char* filename) { @@ -2011,7 +2016,7 @@ path2.quadTo(0.792893291f, 0.50000006f, 1.00000012f, 0.50000006f); path2.close(); path2.setFillType(SkPath::kEvenOdd_FillType); - testPathOp(reporter, path1, path2, kIntersect_PathOp, filename); + testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename); } static void rectOp1i(skiatest::Reporter* reporter, const char* filename) { @@ -2022,7 +2027,7 @@ pathB.setFillType(SkPath::kWinding_FillType); pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction); pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void rectOp2i(skiatest::Reporter* reporter, const char* filename) { @@ -2033,7 +2038,7 @@ pathB.setFillType(SkPath::kWinding_FillType); pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction); pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void rectOp3x(skiatest::Reporter* reporter, const char* filename) { @@ -2060,12 +2065,9 @@ pathB.lineTo(3, 3); pathB.lineTo(2, 3); pathB.close(); - testPathOp(reporter, path, pathB, kXOR_PathOp, filename); + testPathOp(reporter, path, pathB, kXOR_SkPathOp, filename); } -// this fails to generate two interior line segments -// an earlier pathops succeeded, but still failed to generate one interior line segment -// (but was saved by assemble, which works around a single line missing segment) static void issue1435(skiatest::Reporter* reporter, const char* filename) { SkPath path1; path1.moveTo(160, 60); @@ -2114,7 +2116,7 @@ path2.lineTo(195.830978f, 161.521133f); path2.close(); path2.setFillType(SkPath::kEvenOdd_FillType); - testPathOp(reporter, path1, path2, kIntersect_PathOp, filename); + testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename); } static void skpkkiste_to716(skiatest::Reporter* reporter, const char* filename) { @@ -2138,7 +2140,7 @@ pathB.cubicTo(1173, 124.238579f, 1175.23853f, 122, 1178, 122); pathB.lineTo(1340, 122); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void loopEdge1(skiatest::Reporter* reporter, const char* filename) { @@ -2160,7 +2162,7 @@ pathB.lineTo(2,4); pathB.lineTo(1,4); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void loopEdge2(skiatest::Reporter* reporter, const char* filename) { @@ -2182,7 +2184,7 @@ pathB.lineTo(2 - 1e-6f,4); pathB.lineTo(1 - 1e-6f,4); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp86i(skiatest::Reporter* reporter, const char* filename) { @@ -2195,7 +2197,7 @@ pathB.moveTo(2, 6); pathB.cubicTo(2, 5, 4, 0, 4, 3); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp87u(skiatest::Reporter* reporter, const char* filename) { @@ -2208,7 +2210,7 @@ pathB.moveTo(0,2); pathB.cubicTo(4,6, 1,0, 2,0); pathB.close(); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void cubicOp88u(skiatest::Reporter* reporter, const char* filename) { @@ -2221,7 +2223,7 @@ pathB.moveTo(0,5); pathB.cubicTo(4,6, 1,0, 5,2); pathB.close(); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void cubicOp89u(skiatest::Reporter* reporter, const char* filename) { @@ -2234,7 +2236,7 @@ pathB.moveTo(0, 5); pathB.cubicTo(3, 6, 3, 0, 6, 1); pathB.close(); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void cubicOp90u(skiatest::Reporter* reporter, const char* filename) { @@ -2247,7 +2249,7 @@ pathB.moveTo(2, 5); pathB.cubicTo(1, 4, 5, 0, 2, 1); pathB.close(); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void cubicOp91u(skiatest::Reporter* reporter, const char* filename) { @@ -2260,10 +2262,10 @@ pathB.moveTo(3, 6); pathB.cubicTo(0, 5, 6, 1, 3, 0); pathB.close(); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } -static void skpaaalgarve_org53(skiatest::Reporter* reporter, const char* filename) { // add t cancel +static void skpaaalgarve_org53(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); path.moveTo(-1.24344979e-014f, 348); @@ -2281,10 +2283,10 @@ pathB.lineTo(258, 348); pathB.lineTo(0, 348); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -static void skpabcspark_ca103(skiatest::Reporter* reporter, const char* filename) { // add t cancel +static void skpabcspark_ca103(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); path.moveTo(1.99840144e-015f, 494); @@ -2304,10 +2306,10 @@ pathB.lineTo(105, 494); pathB.lineTo(0, 494); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -static void skpacesoftech_com47(skiatest::Reporter* reporter, const char* filename) { // partial coincidence +static void skpacesoftech_com47(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); path.moveTo(670.537415f, 285); @@ -2330,10 +2332,10 @@ pathB.quadTo(691.660889f, 337.416199f, 685.173523f, 352.661896f); pathB.quadTo(678.686157f, 367.907562f, 663.318542f, 374.100616f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -static void skpact_com43(skiatest::Reporter* reporter, const char* filename) { // bridge op +static void skpact_com43(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); path.moveTo(1.45716772e-016f, 924.336121f); @@ -2355,10 +2357,10 @@ pathB.lineTo(0, 920); pathB.lineTo(3, 927); pathB.lineTo(-1, 927); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -static void skpadbox_lt8(skiatest::Reporter* reporter, const char* filename) { // zero span +static void skpadbox_lt8(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); path.moveTo(320.097229f, 628.573669f); @@ -2379,10 +2381,10 @@ pathB.lineTo(334.366943f, 625.145508f); pathB.cubicTo(333.773315f, 624.828247f, 333.549286f, 624.089783f, 333.866608f, 623.496155f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -static void skpadindex_de4(skiatest::Reporter* reporter, const char* filename) { // find chase op +static void skpadindex_de4(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); path.moveTo(0, 926); @@ -2398,10 +2400,10 @@ pathB.lineTo(49, 178); pathB.lineTo(49, 312); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -static void skpadithya_putr4_blogspot_com551(skiatest::Reporter* reporter, const char* filename) { // calc common +static void skpadithya_putr4_blogspot_com551(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); path.moveTo(205.605804f, 142.334625f); @@ -2422,10 +2424,10 @@ pathB.cubicTo(243.829437f, 98.1356659f, 267.195221f, 96.4417267f, 282.651581f, 109.808517f); pathB.lineTo(283.407959f, 110.462646f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -static void skpadspert_de11(skiatest::Reporter* reporter, const char* filename) { // mark and chase winding +static void skpadspert_de11(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); path.moveTo(-4.4408921e-016f, 682.5f); @@ -2443,10 +2445,10 @@ pathB.lineTo(35, 683); pathB.lineTo(0, 683); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -static void skpaiaigames_com870(skiatest::Reporter* reporter, const char* filename) { // cubic/cubic intersect +static void skpaiaigames_com870(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); path.moveTo(324.071075f, 845.071045f); @@ -2473,7 +2475,7 @@ pathB.cubicTo(145, 715.477173f, 149.477158f, 711, 155, 711); pathB.lineTo(317, 711); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp92i(skiatest::Reporter* reporter, const char* filename) { @@ -2486,7 +2488,7 @@ pathB.moveTo(1, 4); pathB.cubicTo(4, 5, 1, 0, 6, 2); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp93d(skiatest::Reporter* reporter, const char* filename) { @@ -2499,7 +2501,7 @@ pathB.moveTo(1, 4); pathB.cubicTo(3, 4, 1, 0, 6, 1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp94u(skiatest::Reporter* reporter, const char* filename) { @@ -2512,7 +2514,7 @@ pathB.moveTo(0, 5); pathB.cubicTo(3, 5, 3, 0, 3, 2); pathB.close(); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void skpadbox_lt15(skiatest::Reporter* reporter, const char* filename) { @@ -2536,7 +2538,7 @@ pathB.cubicTo(333.773315f, 624.828247f, 333.549286f, 624.089783f, 333.866608f, 623.496155f); pathB.lineTo(613.368042f, 100.585754f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpadoption_org196(skiatest::Reporter* reporter, const char* filename) { @@ -2561,7 +2563,7 @@ pathB.cubicTo(805.238586f, 375, 803, 372.761414f, 803, 370); pathB.lineTo(803, 326); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpadspert_net23(skiatest::Reporter* reporter, const char* filename) { @@ -2588,7 +2590,7 @@ pathB.lineTo(35, 683); pathB.lineTo(0, 683); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpadventistmission_org572(skiatest::Reporter* reporter, const char* filename) { @@ -2607,7 +2609,7 @@ pathB.lineTo(1182, 926); pathB.lineTo(934, 926); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpagentxsites_com55(skiatest::Reporter* reporter, const char* filename) { @@ -2635,7 +2637,7 @@ pathB.cubicTo(924, 27.8954315f, 924.895447f, 27, 926, 27); pathB.lineTo(1103, 27); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpbakosoft_com10(skiatest::Reporter* reporter, const char* filename) { @@ -2661,7 +2663,7 @@ pathB.quadTo(198.284271f, 170, 204.142136f, 175.857864f); pathB.quadTo(210, 181.715729f, 210, 190); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpbambootheme_com12(skiatest::Reporter* reporter, const char* filename) { @@ -2685,7 +2687,7 @@ pathB.lineTo(-51, 47); pathB.cubicTo(-51, 19.3857498f, -28.6142502f, -3, -1, -3); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpakmmos_ru100(skiatest::Reporter* reporter, const char* filename) { @@ -2704,7 +2706,7 @@ pathB.lineTo(693, 926); pathB.lineTo(575, 926); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpcarpetplanet_ru22(skiatest::Reporter* reporter, const char* filename) { @@ -2728,14 +2730,10 @@ pathB.cubicTo(67, 842.307556f, 123.85984f, 785, 194, 785); pathB.lineTo(195, 785); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// this fails because cubic/quad misses an intersection (failure is isolated in c/q int test) static void skpcarrot_is24(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; - } SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); path.moveTo(945, 597); @@ -2758,7 +2756,7 @@ pathB.cubicTo(1019.77502f, 679.955017f, 1020.08099f, 676.094971f, 1020.08099f, 672.161987f); pathB.cubicTo(1020.08002f, 630.73999f, 986.502014f, 597.161987f, 945.080994f, 597.161987f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpbangalorenest_com4(skiatest::Reporter* reporter, const char* filename) { @@ -2777,7 +2775,7 @@ pathB.lineTo(30, 146); pathB.lineTo(30, 290); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpbenzoteh_ru152(skiatest::Reporter* reporter, const char* filename) { @@ -2804,7 +2802,7 @@ pathB.quadTo(885.928955f, 28, 884.463989f, 26.5359993f); pathB.quadTo(883, 25.0710678f, 883, 23); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpbestred_ru37(skiatest::Reporter* reporter, const char* filename) { @@ -2831,7 +2829,7 @@ pathB.quadTo(885.928955f, 28, 884.463989f, 26.5359993f); pathB.quadTo(883, 25.0710678f, 883, 23); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpbingoentertainment_net189(skiatest::Reporter* reporter, const char* filename) { @@ -2855,7 +2853,7 @@ pathB.cubicTo(922.567993f, 755.399414f, 920.880615f, 748.474304f, 918.799133f, 748.216003f); pathB.lineTo(899.200928f, 745.783997f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpcarrefour_ro62(skiatest::Reporter* reporter, const char* filename) { @@ -2878,7 +2876,7 @@ pathB.cubicTo(402.686279f, 666, 400, 663.313721f, 400, 660); pathB.lineTo(400, 453); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpcaffelavazzait_com_ua21(skiatest::Reporter* reporter, const char* filename) { @@ -2905,7 +2903,7 @@ pathB.quadTo(885.928955f, 28, 884.463989f, 26.5359993f); pathB.quadTo(883, 25.0710678f, 883, 23); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpcamcorder_kz21(skiatest::Reporter* reporter, const char* filename) { @@ -2932,7 +2930,7 @@ pathB.quadTo(885.928955f, 28, 884.463989f, 26.5359993f); pathB.quadTo(883, 25.0710678f, 883, 23); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpcavablar_net563(skiatest::Reporter* reporter, const char* filename) { @@ -2951,7 +2949,7 @@ pathB.lineTo(160, 918); pathB.lineTo(91, 918); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpinsomnia_gr72(skiatest::Reporter* reporter, const char* filename) { @@ -2970,7 +2968,7 @@ pathB.lineTo(1138, 231); pathB.lineTo(633, 6101); pathB.lineTo(1139, 6607); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp95u(skiatest::Reporter* reporter, const char* filename) { @@ -2983,7 +2981,7 @@ pathB.moveTo(1, 5); pathB.cubicTo(2, 3, 2, 0, 3, 2); pathB.close(); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void cubicOp96d(skiatest::Reporter* reporter, const char* filename) { @@ -2996,7 +2994,7 @@ pathB.moveTo(3, 6); pathB.cubicTo(0, 5, 6, 1, 3, 0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp97x(skiatest::Reporter* reporter, const char* filename) { @@ -3009,7 +3007,7 @@ pathB.moveTo(1, 2); pathB.cubicTo(1, 2, 2, 0, 6, 0); pathB.close(); - testPathOp(reporter, path, pathB, kXOR_PathOp, filename); + testPathOp(reporter, path, pathB, kXOR_SkPathOp, filename); } static void cubicOp98x(skiatest::Reporter* reporter, const char* filename) { @@ -3022,7 +3020,7 @@ pathB.moveTo(1, 4); pathB.cubicTo(3, 6, 3, 0, 6, 3); pathB.close(); - testPathOp(reporter, path, pathB, kXOR_PathOp, filename); + testPathOp(reporter, path, pathB, kXOR_SkPathOp, filename); } static void cubicOp99(skiatest::Reporter* reporter, const char* filename) { @@ -3035,7 +3033,7 @@ pathB.moveTo(5,6); pathB.cubicTo(4,5, 6,3, 3,0); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp100(skiatest::Reporter* reporter, const char* filename) { @@ -3048,7 +3046,7 @@ pathB.moveTo(1,2); pathB.cubicTo(2,4, 1,0, 2,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp101(skiatest::Reporter* reporter, const char* filename) { @@ -3061,7 +3059,7 @@ pathB.moveTo(1, 2); pathB.cubicTo(3, 5, 1, 0, 3, 2); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp102(skiatest::Reporter* reporter, const char* filename) { @@ -3074,7 +3072,7 @@ pathB.moveTo(0,1); pathB.cubicTo(0,3, 1,0, 2,1); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp103(skiatest::Reporter* reporter, const char* filename) { @@ -3087,7 +3085,7 @@ pathB.moveTo(0,2); pathB.cubicTo(1,2, 1,0, 5,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp104(skiatest::Reporter* reporter, const char* filename) { @@ -3100,7 +3098,7 @@ pathB.moveTo(0,4); pathB.cubicTo(1,6, 1,0, 6,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp105(skiatest::Reporter* reporter, const char* filename) { @@ -3113,7 +3111,7 @@ pathB.moveTo(5,6); pathB.cubicTo(0,2, 1,0, 4,0); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp106(skiatest::Reporter* reporter, const char* filename) { @@ -3126,7 +3124,7 @@ pathB.moveTo(1, 2); pathB.cubicTo(0, 2, 1, 0, 6, 4); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp107(skiatest::Reporter* reporter, const char* filename) { @@ -3139,7 +3137,7 @@ pathB.moveTo(1, 2); pathB.cubicTo(0, 2, 1, 0, 6, 4); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp108(skiatest::Reporter* reporter, const char* filename) { @@ -3152,7 +3150,7 @@ pathB.moveTo(1, 2); pathB.cubicTo(0, 2, 1, 0, 6, 4); pathB.close(); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void cubicOp109(skiatest::Reporter* reporter, const char* filename) { @@ -3165,7 +3163,7 @@ pathB.moveTo(3,6); pathB.cubicTo(4,5, 1,0, 5,4); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp110(skiatest::Reporter* reporter, const char* filename) { @@ -3176,7 +3174,7 @@ pathB.setFillType(SkPath::kEvenOdd_FillType); pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction); pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp111(skiatest::Reporter* reporter, const char* filename) { @@ -3189,7 +3187,7 @@ pathB.moveTo(1,4); pathB.cubicTo(1,3, 4,1, 5,0); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void xOp1u(skiatest::Reporter* reporter, const char* filename) { @@ -3202,7 +3200,7 @@ pathB.moveTo(2, 3); pathB.cubicTo(3, 6, 4, 1, 5, 4); pathB.close(); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void xOp1i(skiatest::Reporter* reporter, const char* filename) { @@ -3215,7 +3213,7 @@ pathB.moveTo(0, 6); pathB.cubicTo(1, 5, 4, 1, 5, 1); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void xOp2i(skiatest::Reporter* reporter, const char* filename) { @@ -3228,7 +3226,7 @@ pathB.moveTo(2, 3); pathB.cubicTo(1, 6, 5, 1, 4, 0); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void xOp3i(skiatest::Reporter* reporter, const char* filename) { @@ -3241,7 +3239,7 @@ pathB.moveTo(1,4); pathB.cubicTo(1,3, 4,1, 5,0); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void findFirst1(skiatest::Reporter* reporter, const char* filename) { @@ -3254,10 +3252,9 @@ pathB.moveTo(0,5); pathB.cubicTo(1,2, 1,0, 6,1); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } -// triggers addSimpleAngle with non-zero argument static void cubicOp112(skiatest::Reporter* reporter, const char* filename) { SkPath path, pathB; path.setFillType(SkPath::kWinding_FillType); @@ -3268,7 +3265,7 @@ pathB.moveTo(4,6); pathB.cubicTo(0,1, 4,2, 3,2); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp113(skiatest::Reporter* reporter, const char* filename) { @@ -3279,13 +3276,10 @@ pathB.moveTo(3,5); pathB.cubicTo(2.33333325f,4.33333349f, 3.83333325f,3.83333349f, 2,4); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void cubicOp114(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; - } SkPath path, pathB; path.setFillType(SkPath::kWinding_FillType); path.moveTo(0, 1); @@ -3295,7 +3289,7 @@ pathB.moveTo(1, 3); pathB.cubicTo(-1, 2, 3.5f, 1.33333337f, 0, 1); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void cubicOp114asQuad(skiatest::Reporter* reporter, const char* filename) { @@ -3311,7 +3305,7 @@ SkPath qPath, qPathB; CubicPathToQuads(path, &qPath); CubicPathToQuads(pathB, &qPathB); - testPathOp(reporter, qPath, qPathB, kIntersect_PathOp, filename); + testPathOp(reporter, qPath, qPathB, kIntersect_SkPathOp, filename); } static void quadOp10i(skiatest::Reporter* reporter, const char* filename) { @@ -3323,7 +3317,7 @@ pathB.moveTo(0, 0); pathB.quadTo(8, 1, 4, 8); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void kari1(skiatest::Reporter* reporter, const char* filename) { @@ -3342,7 +3336,7 @@ path2.cubicTo(38.0664443969727f, -0.154893040657043f, 37.1809883117676f, -1.18359375f, 37.52734375, -1.44140625f); path2.close(); - testPathOp(reporter, path1, path2, kDifference_PathOp, filename); + testPathOp(reporter, path1, path2, kDifference_SkPathOp, filename); } static void issue2504(skiatest::Reporter* reporter, const char* filename) { @@ -3361,7 +3355,7 @@ 34.53484344482421875, -5.6777553558349609375, 34.53484344482421875, -5.6777553558349609375); path2.close(); - testPathOp(reporter, path1, path2, kUnion_PathOp, filename); + testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename); } static void issue2540(skiatest::Reporter* reporter, const char* filename) { @@ -3381,7 +3375,7 @@ path2.cubicTo(-16.06999969482421875, 124.66899871826171875, 1.2680000066757202148437500, 91.23999786376953125, 37.264003753662109375, 95.35400390625); path2.cubicTo(37.264003753662109375, 95.35400390625, 11.3710002899169921875, 83.7339935302734375, -25.077999114990234375, 124.9120025634765625); path2.close(); - testPathOp(reporter, path1, path2, kUnion_PathOp, filename); + testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename); } static void rects1(skiatest::Reporter* reporter, const char* filename) { @@ -3408,7 +3402,7 @@ pathB.lineTo(2, 2); pathB.lineTo(0, 2); pathB.close(); - testPathOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename); } static void rects2(skiatest::Reporter* reporter, const char* filename) { @@ -3435,7 +3429,7 @@ pathB.lineTo(4, 4); pathB.lineTo(3, 4); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void rects3(skiatest::Reporter* reporter, const char* filename) { @@ -3446,7 +3440,7 @@ pathB.setFillType(SkPath::kWinding_FillType); pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction); pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void rects4(skiatest::Reporter* reporter, const char* filename) { @@ -3457,13 +3451,10 @@ pathB.setFillType(SkPath::kWinding_FillType); pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction); pathB.addRect(0, 0, 3, 3, SkPath::kCW_Direction); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void issue2753(skiatest::Reporter* reporter, const char* filename) { - if (!FLAGS_runFail) { - return; - } SkPath path1; path1.moveTo(142.701f, 110.568f); path1.lineTo(142.957f, 100); @@ -3480,255 +3471,83 @@ path2.cubicTo(188.201f, 117.601f, 174.801f, 93, 39, 124.001f); path2.close(); - testPathOp(reporter, path1, path2, kUnion_PathOp, filename); + testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename); } static void issue2808(skiatest::Reporter* reporter, const char* filename) { SkPath path1, path2; - path1.moveTo(509.20300293f, 385.601989746f); - path1.quadTo(509.20300293f, 415.68838501f, 487.928710938f, 436.96270752f); - path1.quadTo(466.654388428f, 458.236999512f, 436.567993164f, 458.236999512f); - path1.quadTo(406.4815979f, 458.236999512f, 385.207275391f, 436.96270752f); - path1.quadTo(363.932983398f, 415.68838501f, 363.932983398f, 385.601989746f); - path1.quadTo(363.932983398f, 355.515594482f, 385.207275391f, 334.241271973f); - path1.quadTo(406.4815979f, 312.96697998f, 436.567993164f, 312.96697998f); - path1.quadTo(466.654388428f, 312.96697998f, 487.928710938f, 334.241271973f); - path1.quadTo(509.20300293f, 355.515594482f, 509.20300293f, 385.601989746f); - path1.close(); + path1.moveTo(509.20300293f, 385.601989746f); + path1.quadTo(509.20300293f, 415.68838501f, 487.928710938f, 436.96270752f); + path1.quadTo(466.654388428f, 458.236999512f, 436.567993164f, 458.236999512f); + path1.quadTo(406.4815979f, 458.236999512f, 385.207275391f, 436.96270752f); + path1.quadTo(363.932983398f, 415.68838501f, 363.932983398f, 385.601989746f); + path1.quadTo(363.932983398f, 355.515594482f, 385.207275391f, 334.241271973f); + path1.quadTo(406.4815979f, 312.96697998f, 436.567993164f, 312.96697998f); + path1.quadTo(466.654388428f, 312.96697998f, 487.928710938f, 334.241271973f); + path1.quadTo(509.20300293f, 355.515594482f, 509.20300293f, 385.601989746f); + path1.close(); - path2.moveTo(449.033996582f, 290.87298584f); - path2.quadTo(449.033996582f, 301.028259277f, 441.853149414f, 308.209106445f); - path2.quadTo(434.672271729f, 315.389984131f, 424.516998291f, 315.389984131f); - path2.quadTo(414.361724854f, 315.389984131f, 407.180847168f, 308.209106445f); - path2.quadTo(400, 301.028259277f, 400, 290.87298584f); - path2.quadTo(400, 280.717712402f, 407.180847168f, 273.536865234f); - path2.quadTo(414.361724854f, 266.355987549f, 424.516998291f, 266.355987549f); - path2.quadTo(434.672271729f, 266.355987549f, 441.853149414f, 273.536865234f); - path2.quadTo(449.033996582f, 280.717712402f, 449.033996582f, 290.87298584f); - path2.close(); + path2.moveTo(449.033996582f, 290.87298584f); + path2.quadTo(449.033996582f, 301.028259277f, 441.853149414f, 308.209106445f); + path2.quadTo(434.672271729f, 315.389984131f, 424.516998291f, 315.389984131f); + path2.quadTo(414.361724854f, 315.389984131f, 407.180847168f, 308.209106445f); + path2.quadTo(400, 301.028259277f, 400, 290.87298584f); + path2.quadTo(400, 280.717712402f, 407.180847168f, 273.536865234f); + path2.quadTo(414.361724854f, 266.355987549f, 424.516998291f, 266.355987549f); + path2.quadTo(434.672271729f, 266.355987549f, 441.853149414f, 273.536865234f); + path2.quadTo(449.033996582f, 280.717712402f, 449.033996582f, 290.87298584f); + path2.close(); - testPathOp(reporter, path1, path2, kUnion_PathOp, filename); + testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename); } -static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0; -static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0; +static void cubicOp115(skiatest::Reporter* reporter, const char* filename) { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(0,1); + path.cubicTo(3,4, 2,1, 5,3); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(1,2); + pathB.cubicTo(3,5, 1,0, 4,3); + pathB.close(); + SkPath path2(path); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); +} -static struct TestDesc tests[] = { - TEST(issue2753), // FIXME: pair of cubics miss intersection - TEST(cubicOp114), // FIXME: curve with inflection is ordered the wrong way - TEST(issue2808), - TEST(cubicOp114asQuad), - TEST(rects4), - TEST(rects3), - TEST(rects2), - TEST(rects1), - TEST(issue2540), - TEST(issue2504), - TEST(kari1), - TEST(quadOp10i), - TEST(cubicOp113), - // fails because a cubic/quadratic intersection is missed - // the internal quad/quad is far enough away from the real cubic/quad that it is rejected - TEST(skpcarrot_is24), - TEST(issue1417), - TEST(cubicOp112), - TEST(skpadspert_net23), - TEST(skpadspert_de11), - TEST(findFirst1), - TEST(xOp2i), - TEST(xOp3i), - TEST(xOp1u), - TEST(xOp1i), - TEST(cubicOp111), - TEST(cubicOp110), - TEST(cubicOp109), - TEST(cubicOp108), - TEST(cubicOp107), - TEST(cubicOp106), - TEST(cubicOp105), - TEST(cubicOp104), - TEST(cubicOp103), - TEST(cubicOp102), - TEST(cubicOp101), - TEST(cubicOp100), - TEST(cubicOp99), - TEST(issue1435), - TEST(cubicOp98x), - TEST(cubicOp97x), - TEST(skpcarpetplanet_ru22), // cubic/cubic intersect detects unwanted coincidence - TEST(cubicOp96d), - TEST(cubicOp95u), - TEST(skpadbox_lt15), - TEST(skpagentxsites_com55), - TEST(skpadventistmission_org572), - TEST(skpadoption_org196), - TEST(skpbambootheme_com12), - TEST(skpbakosoft_com10), - TEST(skpakmmos_ru100), - TEST(skpbangalorenest_com4), - TEST(skpbingoentertainment_net189), - TEST(skpbestred_ru37), - TEST(skpbenzoteh_ru152), - TEST(skpcamcorder_kz21), - TEST(skpcaffelavazzait_com_ua21), - TEST(skpcarrefour_ro62), - TEST(skpcavablar_net563), - TEST(skpinsomnia_gr72), - TEST(skpadbox_lt8), - TEST(skpact_com43), - TEST(skpacesoftech_com47), - TEST(skpabcspark_ca103), - TEST(cubicOp94u), - TEST(cubicOp93d), - TEST(cubicOp92i), - TEST(skpadithya_putr4_blogspot_com551), - TEST(skpadindex_de4), - TEST(skpaiaigames_com870), - TEST(skpaaalgarve_org53), - TEST(skpkkiste_to716), - TEST(cubicOp91u), - TEST(cubicOp90u), - TEST(cubicOp89u), - TEST(cubicOp88u), - TEST(cubicOp87u), - TEST(cubicOp86i), - TEST(loopEdge2), - TEST(loopEdge1), - TEST(rectOp3x), - TEST(rectOp2i), - TEST(rectOp1i), - TEST(issue1418b), - TEST(cubicOp85i), - TEST(issue1418), - TEST(skpkkiste_to98), - TEST(skpahrefs_com29), - TEST(cubicOp85d), - TEST(skpahrefs_com88), - TEST(skphealth_com76), - TEST(skpancestry_com1), - TEST(skpbyte_com1), - TEST(skpeldorado_com_ua1), - TEST(skp96prezzi1), - TEST(skpClip2), - TEST(skpClip1), - TEST(cubicOp84d), - TEST(cubicOp83i), - TEST(cubicOp82i), - TEST(cubicOp81d), - TEST(cubicOp80i), - TEST(cubicOp79u), - TEST(cubicOp78u), - TEST(cubicOp77i), - TEST(cubicOp76u), - TEST(cubicOp75d), - TEST(cubicOp74d), - TEST(cubicOp73d), - TEST(cubicOp72i), - TEST(cubicOp71d), - TEST(skp5), - TEST(skp4), - TEST(skp3), - TEST(skp2), - TEST(skp1), - TEST(rRect1), - TEST(cubicOp70d), - TEST(cubicOp69d), - TEST(cubicOp68u), - TEST(cubicOp67u), - TEST(cubicOp66u), - TEST(rectOp1d), - TEST(cubicOp65d), - TEST(cubicOp64d), - TEST(cubicOp63d), - TEST(cubicOp62d), - TEST(cubicOp61d), - TEST(cubicOp60d), - TEST(cubicOp59d), - TEST(cubicOp58d), - TEST(cubicOp57d), - TEST(cubicOp56d), - TEST(cubicOp55d), - TEST(cubicOp54d), - TEST(cubicOp53d), - TEST(cubicOp52d), - TEST(cubicOp51d), - TEST(cubicOp50d), - TEST(cubicOp49d), - TEST(cubicOp48d), - TEST(cubicOp47d), - TEST(cubicOp46d), - TEST(cubicOp45d), - TEST(cubicOp44d), - TEST(cubicOp43d), - TEST(cubicOp42d), - TEST(cubicOp41i), - TEST(cubicOp40d), - TEST(cubicOp39d), - TEST(cubicOp38d), - TEST(cubicOp37d), - TEST(cubicOp36u), - TEST(cubicOp35d), - TEST(cubicOp34d), - TEST(cubicOp33i), - TEST(cubicOp32d), - TEST(cubicOp31d), - TEST(cubicOp31x), - TEST(cubicOp31u), - TEST(cubicOp30d), - TEST(cubicOp29d), - TEST(cubicOp28u), - TEST(cubicOp27d), - TEST(cubicOp26d), - TEST(cubicOp25i), - TEST(testOp8d), - TEST(testDiff1), - TEST(testIntersect1), - TEST(testUnion1), - TEST(testXor1), - TEST(testDiff2), - TEST(testIntersect2), - TEST(testUnion2), - TEST(testXor2), - TEST(testOp1d), - TEST(testOp2d), - TEST(testOp3d), - TEST(testOp1u), - TEST(testOp4d), - TEST(testOp5d), - TEST(testOp6d), - TEST(testOp7d), - TEST(testOp2u), +static void testRect1(skiatest::Reporter* reporter, const char* filename) { + SkPath path, path2; + path.addRect(0, 0, 60, 60, SkPath::kCCW_Direction); + path.addRect(30, 20, 50, 50, SkPath::kCCW_Direction); + path.addRect(24, 20, 36, 30, SkPath::kCCW_Direction); +// path.addRect(32, 24, 36, 41, SkPath::kCCW_Direction); + testPathOp(reporter, path, path2, kUnion_SkPathOp, filename); +} - TEST(cubicOp24d), - TEST(cubicOp23d), - TEST(cubicOp22d), - TEST(cubicOp21d), - TEST(cubicOp20d), - TEST(cubicOp19i), - TEST(cubicOp18d), - TEST(cubicOp17d), - TEST(cubicOp16d), - TEST(cubicOp15d), - TEST(cubicOp14d), - TEST(cubicOp13d), - TEST(cubicOp12d), - TEST(cubicOp11d), - TEST(cubicOp10d), - TEST(cubicOp1i), - TEST(cubicOp9d), - TEST(quadOp9d), - TEST(lineOp9d), - TEST(cubicOp8d), - TEST(cubicOp7d), - TEST(cubicOp6d), - TEST(cubicOp5d), - TEST(cubicOp3d), - TEST(cubicOp2d), - TEST(cubicOp1d), -}; +static void testRect2(skiatest::Reporter* reporter, const char* filename) { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.addRect(0, 0, 1, 1, SkPath::kCW_Direction); + path.addRect(4, 4, 5, 5, SkPath::kCW_Direction); + pathB.setFillType(SkPath::kEvenOdd_FillType); + pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction); + pathB.addRect(0, 0, 6, 6, SkPath::kCW_Direction); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); +} -static const size_t testCount = SK_ARRAY_COUNT(tests); +static void cubicOp116(skiatest::Reporter* reporter, const char* filename) { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(0,1); + path.cubicTo(4,6, 2,0, 2,0); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(0,2); + pathB.cubicTo(0,2, 1,0, 6,4); + pathB.close(); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); +} static void cubicOp117(skiatest::Reporter* reporter, const char* filename) { SkPath path, pathB; @@ -3770,8 +3589,11 @@ #include "SkPathOpsCubic.h" static void loop1asQuad(skiatest::Reporter* reporter, const char* filename) { - SkDCubic c1 = {{{0,1}, {1,5}, {-5.66666651f,3.33333349f}, {8.83333302f,2.33333349f}}}; - SkDCubic c2 = {{{1,5}, {-5.66666651f,3.33333349f}, {8.83333302f,2.33333349f}, {0,1}}}; + CubicPts cubic1 = {{{0,1}, {1,5}, {-5.66666651f,3.33333349f}, {8.83333302f,2.33333349f}}}; + CubicPts cubic2 = {{{1,5}, {-5.66666651f,3.33333349f}, {8.83333302f,2.33333349f}, {0,1}}}; + SkDCubic c1, c2; + c1.debugSet(cubic1.fPts); + c2.debugSet(cubic2.fPts); double c1InflectionTs[2], c2InflectionTs[2]; SkDEBUGCODE(int c1InfTCount =) c1.findInflections(c1InflectionTs); SkASSERT(c1InfTCount == 2); @@ -4025,11 +3847,10 @@ #include "SkGeometry.h" static void complex_to_quads(const SkPoint pts[], SkPath* path) { - SkScalar loopT; - SkDCubic::CubicType dType; - if (SkDCubic::ComplexBreak(pts, &loopT, &dType)) { - SkPoint cubicPair[7]; - SkChopCubicAt(pts, cubicPair, loopT); + SkScalar loopT[3]; + if (SkDCubic::ComplexBreak(pts, loopT)) { + SkPoint cubicPair[7]; + SkChopCubicAt(pts, cubicPair, loopT[0]); SkDCubic c1, c2; c1.set(cubicPair); c2.set(&cubicPair[3]); @@ -5048,7 +4869,7 @@ } /* -FAILED: d:\cygwin\puregit\tests\pathopsextendedtest.cpp:346 0 */ +FAILED: d:\cygwin\puregit\tests\pathopsextendedtest.cpp:346 0 */ static void loops47i(skiatest::Reporter* reporter, const char* filename) { SkPath path, pathB; path.setFillType(SkPath::kWinding_FillType); @@ -5342,9 +5163,270 @@ path.lineTo(100.34f, 310.156f); path.lineTo(100.34f, 303.312f); path.close(); - testPathOpCheck(reporter, path, pathB, kUnion_SkPathOp, filename, FLAGS_runFail); + testPathOpCheck(reporter, path, pathB, kUnion_SkPathOp, filename, true); } +static void crbug_526025(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x43b40000), SkBits2Float(0xcf000000)); // 360, -2.14748e+09f +path.cubicTo(SkBits2Float(0x4e0d628f), SkBits2Float(0xceffffff), SkBits2Float(0x4e800003), SkBits2Float(0xcec6b143), SkBits2Float(0x4e800002), SkBits2Float(0xce7ffffc)); // 5.93012e+08f, -2.14748e+09f, 1.07374e+09f, -1.66675e+09f, 1.07374e+09f, -1.07374e+09f +path.cubicTo(SkBits2Float(0x4e800002), SkBits2Float(0xcde53aee), SkBits2Float(0x4e0d6292), SkBits2Float(0xc307820e), SkBits2Float(0x44627d00), SkBits2Float(0x437ffff2)); // 1.07374e+09f, -4.80731e+08f, 5.93012e+08f, -135.508f, 905.953f, 256 +path.lineTo(SkBits2Float(0x444bf3bc), SkBits2Float(0x4460537e)); // 815.808f, 897.305f +path.lineTo(SkBits2Float(0x43553abd), SkBits2Float(0x440f3cbd)); // 213.229f, 572.949f +path.lineTo(SkBits2Float(0x42000000), SkBits2Float(0x41800000)); // 32, 16 +path.lineTo(SkBits2Float(0x42c80000), SkBits2Float(0x44000000)); // 100, 512 +path.lineTo(SkBits2Float(0x43553abd), SkBits2Float(0x440f3cbd)); // 213.229f, 572.949f +path.lineTo(SkBits2Float(0x43b40000), SkBits2Float(0x44800000)); // 360, 1024 +path.lineTo(SkBits2Float(0x43b40000), SkBits2Float(0x45816000)); // 360, 4140 + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42fe0000), SkBits2Float(0x43a08000)); // 127, 321 +path.lineTo(SkBits2Float(0x45d5c000), SkBits2Float(0x43870000)); // 6840, 270 +path.lineTo(SkBits2Float(0xd0a00000), SkBits2Float(0x4cbebc20)); // -2.14748e+10f, 1e+08 +path.lineTo(SkBits2Float(0x451f7000), SkBits2Float(0x42800000)); // 2551, 64 +path.lineTo(SkBits2Float(0x42fe0000), SkBits2Float(0x43a08000)); // 127, 321 +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzzX_392(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); +path.moveTo(SkBits2Float(0x41e80000), SkBits2Float(0x43bde212)); // 29, 379.766f +path.lineTo(SkBits2Float(0x41e80000), SkBits2Float(0x43bdc7ef)); // 29, 379.562f +path.conicTo(SkBits2Float(0x42a5861e), SkBits2Float(0x43c61f86), SkBits2Float(0x430b0610), SkBits2Float(0x43c61f86), SkBits2Float(0x3f7d23f3)); // 82.7619f, 396.246f, 139.024f, 396.246f, 0.98883f +path.conicTo(SkBits2Float(0x42a58e20), SkBits2Float(0x43c61f86), SkBits2Float(0x41e80000), SkBits2Float(0x43bde212), SkBits2Float(0x3f7d2cf5)); // 82.7776f, 396.246f, 29, 379.766f, 0.988967f +path.close(); + + SkPath path1(path); + path.setFillType(SkPath::kWinding_FillType); +path.moveTo(SkBits2Float(0xc36c7bd8), SkBits2Float(0xc3a31d72)); // -236.484f, -326.23f +path.lineTo(SkBits2Float(0xc367a4ae), SkBits2Float(0xc3a31d72)); // -231.643f, -326.23f +path.lineTo(SkBits2Float(0x430b0610), SkBits2Float(0x43c61f86)); // 139.024f, 396.246f +path.lineTo(SkBits2Float(0xc36c7bd8), SkBits2Float(0x43c61f86)); // -236.484f, 396.246f + + SkPath path2(path); + testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename); +} + +static void dean2(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b)); // 0.669749f, 13.2891f +path.cubicTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b), SkBits2Float(0x41531912), SkBits2Float(0x3f130322), SkBits2Float(0x4154a02b), SkBits2Float(0x3f2b74b3)); // 0.669749f, 13.2891f, 13.1936f, 0.574267f, 13.2891f, 0.669749f +path.cubicTo(SkBits2Float(0x414a835a), SkBits2Float(0x3ec07ba6), SkBits2Float(0x413fcc0d), SkBits2Float(0x3e193319), SkBits2Float(0x4134a02b), SkBits2Float(0x00000000)); // 12.6571f, 0.375943f, 11.9873f, 0.149609f, 11.2891f, 0 +path.lineTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b)); // 0.669749f, 13.2891f +path.close(); + SkPath path1(path); + + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b)); // 0.669749f, 13.2891f +path.cubicTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b), SkBits2Float(0x41531912), SkBits2Float(0x3f130322), SkBits2Float(0x4154a02b), SkBits2Float(0x3f2b74b3)); // 0.669749f, 13.2891f, 13.1936f, 0.574267f, 13.2891f, 0.669749f +path.lineTo(SkBits2Float(0x417ab74b), SkBits2Float(0x4154a02b)); // 15.6697f, 13.2891f +path.lineTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b)); // 0.669749f, 13.2891f +path.close(); + SkPath path2(path); + testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename); +} + +static void cubics_d(skiatest::Reporter* reporter, const char* filename) { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(0, 1); + path.cubicTo(3, 5, 1, 0, 3, 0); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(0, 1); + pathB.cubicTo(0, 3, 1, 0, 5, 3); + pathB.close(); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); +} + +static void cubics_d2(skiatest::Reporter* reporter, const char* filename) { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(0, 1); + path.cubicTo(2, 5, 2, 0, 2, 1); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(0, 2); + pathB.cubicTo(1, 2, 1, 0, 5, 2); + pathB.close(); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); +} + +static void loops_i1(skiatest::Reporter* reporter, const char* filename) { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(2, 3); + path.cubicTo(0, 4, -0.333333343f, 4.66666651f, 3, 5.83333349f); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(0, 4); + pathB.cubicTo(-0.333333343f, 4.66666651f, 3, 5.83333349f, 2, 3); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); +} + +static void loops_i2(skiatest::Reporter* reporter, const char* filename) { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(2, 4); + path.cubicTo(0, 5, -0.333333343f, 5.66666651f, 3, 6.83333302f); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(0, 5); + pathB.cubicTo(-0.333333343f, 5.66666651f, 3, 6.83333302f, 2, 4); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); +} + +static void loops_i3(skiatest::Reporter* reporter, const char* filename) { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(2, 5); + path.cubicTo(0, 6, -0.333333343f, 6.66666651f, 3, 7.83333302f); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(0, 6); + pathB.cubicTo(-0.333333343f, 6.66666651f, 3, 7.83333302f, 2, 5); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); +} + +static void loops_i4(skiatest::Reporter* reporter, const char* filename) { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(3, 4); + path.cubicTo(1, 5, 0.666666627f, 5.66666651f, 4, 6.83333302f); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(1, 5); + pathB.cubicTo(0.666666627f, 5.66666651f, 4, 6.83333302f, 3, 4); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); +} + +static void loops_i5(skiatest::Reporter* reporter, const char* filename) { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(3, 5); + path.cubicTo(1, 6, 0.666666627f, 6.66666651f, 4, 7.83333302f); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(1, 6); + pathB.cubicTo(0.666666627f, 6.66666651f, 4, 7.83333302f, 3, 5); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); +} + +static void loops_i6(skiatest::Reporter* reporter, const char* filename) { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(4, 5); + path.cubicTo(2, 6, 1.66666663f, 6.66666651f, 5, 7.83333302f); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(2, 6); + pathB.cubicTo(1.66666663f, 6.66666651f, 5, 7.83333302f, 4, 5); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); +} + +static void cubics_d3(skiatest::Reporter* reporter, const char* filename) { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(3, 4); + path.cubicTo(0, 6, 6, 1, 4, 2); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(1, 6); + pathB.cubicTo(2, 4, 4, 3, 6, 0); + pathB.close(); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); +} + +static void cubics_o(skiatest::Reporter* reporter, const char* filename) { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(1, 4); + path.cubicTo(2, 6, 5, 0, 5, 3); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(0, 5); + pathB.cubicTo(3, 5, 4, 1, 6, 2); + pathB.close(); + testPathOp(reporter, path, pathB, kXOR_SkPathOp, filename); +} + +static void cubicOp158(skiatest::Reporter* reporter, const char* filename) { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.moveTo(0,1); + path.cubicTo(2,4, 2,0, 2,0); + path.close(); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(0,2); + pathB.cubicTo(0,2, 1,0, 4,2); + pathB.close(); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); +} + +static void loop17(skiatest::Reporter* reporter, const char* filename) { + SkPath path, pathB; + path.moveTo(1, 2); + path.cubicTo(0, 3, -0.333333343f, 3.33333325f, 0.833333373f, 3.5f); + path.close(); + pathB.moveTo(0, 3); + pathB.cubicTo(-0.333333343f, 3.33333325f, 0.833333373f, 3.5f, 1, 2); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); +} + +static void circlesOp4(skiatest::Reporter* reporter, const char* filename) { + SkPath path, pathB; + path.setFillType(SkPath::kWinding_FillType); + path.addCircle(0, 1, 5, SkPath::kCW_Direction); + pathB.setFillType(SkPath::kWinding_FillType); + pathB.addCircle(0, 1, 0, SkPath::kCW_Direction); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); +} + +static void bug5240(skiatest::Reporter* reporter, const char* filename) { + SkPath path; +path.moveTo(815, 82); +path.cubicTo(814.4794311523438f, 82.7868881225586f, 814.5330810546875f, +82.6266555786133f, 814.5291137695312f, 82.6252212524414f); +path.cubicTo(814.5229492187500f, 82.6230010986328f, 814.3790283203125f, +83.0008087158203f, 813.8533935546875f, 82.7072601318359f); +path.close(); + testPathOp(reporter, path, path, kUnion_SkPathOp, filename); +} + +static void android1(skiatest::Reporter* reporter, const char* filename) { + SkPath path, pathB; +path.moveTo(SkBits2Float(0xc0a00000), SkBits2Float(0x00000000)); // -5, 0 +path.lineTo(SkBits2Float(0x44866000), SkBits2Float(0x00000000)); // 1075, 0 +path.lineTo(SkBits2Float(0x44866000), SkBits2Float(0x43720000)); // 1075, 242 +path.lineTo(SkBits2Float(0xc0a00000), SkBits2Float(0x43720000)); // -5, 242 +path.lineTo(SkBits2Float(0xc0a00000), SkBits2Float(0x00000000)); // -5, 0 +path.close(); +pathB.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +pathB.lineTo(SkBits2Float(0x44870000), SkBits2Float(0x00000000)); // 1080, 0 +pathB.lineTo(SkBits2Float(0x44870000), SkBits2Float(0x43720000)); // 1080, 242 +pathB.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x43720000)); // 0, 242 +pathB.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); +} + + static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0; static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0; static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0; @@ -5352,6 +5434,24 @@ #define TEST(name) { name, #name } static struct TestDesc tests[] = { + TEST(android1), + TEST(bug5240), + TEST(circlesOp4), + TEST(loop17), + TEST(cubicOp158), + TEST(loops_i1), + TEST(loops_i2), + TEST(loops_i3), + TEST(loops_i4), + TEST(loops_i5), + TEST(loops_i6), + TEST(cubics_d3), + TEST(cubics_o), + TEST(cubics_d2), + TEST(cubics_d), + TEST(dean2), + TEST(fuzzX_392), + TEST(crbug_526025), TEST(fuzz38), TEST(cubics44d), TEST(cubics45u), @@ -5427,7 +5527,273 @@ TEST(quadRect6), TEST(cubicOp141), TEST(cubicOp58d), + TEST(loops5i), + TEST(cubicOp140), + TEST(cubicOp139), + TEST(cubics138), + TEST(cubics137), + TEST(cubicOp136a), + TEST(cubicOp136), + TEST(cubicOp135), + TEST(cubicOp134), + TEST(cubicOp133), + TEST(loop12), + TEST(cubicOp132), + TEST(loop11), + TEST(loop10), + TEST(circlesOp3), + TEST(loop9), + TEST(loop8), + TEST(rects5), + TEST(loop7), + TEST(cubicOp130a), + TEST(rRect1x), + TEST(circlesOp2), + TEST(circlesOp1), + TEST(cubicOp131), + TEST(cubicOp130), + TEST(cubicOp129), + TEST(cubicOp128), + TEST(cubicOp127), + TEST(cubicOp126), + TEST(cubicOp125), + TEST(cubicOp124), + TEST(loop6), + TEST(loop5), + TEST(cubicOp123), + TEST(cubicOp122), + TEST(cubicOp121), + TEST(cubicOp120), + TEST(cubicOp119), + TEST(loop4), + TEST(loop3), + TEST(loop2), + TEST(loop1asQuad), + TEST(loop1), + TEST(issue3517), + TEST(cubicOp118), + TEST(cubicOp117), + TEST(cubicOp116), + TEST(testRect2), + TEST(testRect1), + TEST(cubicOp115), + TEST(issue2753), + TEST(cubicOp114), + TEST(issue2808), + TEST(cubicOp114asQuad), + TEST(rects4), + TEST(rects3), + TEST(rects2), + TEST(rects1), + TEST(issue2540), + TEST(issue2504), + TEST(kari1), + TEST(quadOp10i), + TEST(cubicOp113), + TEST(skpcarrot_is24), + TEST(issue1417), + TEST(cubicOp112), + TEST(skpadspert_net23), + TEST(skpadspert_de11), + TEST(findFirst1), + TEST(xOp2i), + TEST(xOp3i), + TEST(xOp1u), + TEST(xOp1i), + TEST(cubicOp111), + TEST(cubicOp110), + TEST(cubicOp109), + TEST(cubicOp108), + TEST(cubicOp107), + TEST(cubicOp106), + TEST(cubicOp105), + TEST(cubicOp104), + TEST(cubicOp103), + TEST(cubicOp102), + TEST(cubicOp101), + TEST(cubicOp100), + TEST(cubicOp99), + TEST(issue1435), + TEST(cubicOp98x), + TEST(cubicOp97x), + TEST(skpcarpetplanet_ru22), + TEST(cubicOp96d), + TEST(cubicOp95u), + TEST(skpadbox_lt15), + TEST(skpagentxsites_com55), + TEST(skpadventistmission_org572), + TEST(skpadoption_org196), + TEST(skpbambootheme_com12), + TEST(skpbakosoft_com10), + TEST(skpakmmos_ru100), + TEST(skpbangalorenest_com4), + TEST(skpbingoentertainment_net189), + TEST(skpbestred_ru37), + TEST(skpbenzoteh_ru152), + TEST(skpcamcorder_kz21), + TEST(skpcaffelavazzait_com_ua21), + TEST(skpcarrefour_ro62), + TEST(skpcavablar_net563), + TEST(skpinsomnia_gr72), + TEST(skpadbox_lt8), + TEST(skpact_com43), + TEST(skpacesoftech_com47), + TEST(skpabcspark_ca103), + TEST(cubicOp94u), + TEST(cubicOp93d), + TEST(cubicOp92i), + TEST(skpadithya_putr4_blogspot_com551), + TEST(skpadindex_de4), + TEST(skpaiaigames_com870), + TEST(skpaaalgarve_org53), + TEST(skpkkiste_to716), + TEST(cubicOp91u), + TEST(cubicOp90u), + TEST(cubicOp89u), + TEST(cubicOp88u), + TEST(cubicOp87u), + TEST(cubicOp86i), + TEST(loopEdge2), + TEST(loopEdge1), + TEST(rectOp3x), + TEST(rectOp2i), + TEST(rectOp1i), + TEST(issue1418b), + TEST(cubicOp85i), + TEST(issue1418), + TEST(skpkkiste_to98), + TEST(skpahrefs_com29), + TEST(cubicOp85d), + TEST(skpahrefs_com88), + TEST(skphealth_com76), + TEST(skpancestry_com1), + TEST(skpbyte_com1), + TEST(skpeldorado_com_ua1), + TEST(skp96prezzi1), + TEST(skpClip2), + TEST(skpClip1), + TEST(cubicOp84d), + TEST(cubicOp83i), + TEST(cubicOp82i), + TEST(cubicOp81d), + TEST(cubicOp80i), + TEST(cubicOp79u), + TEST(cubicOp78u), + TEST(cubicOp77i), + TEST(cubicOp76u), + TEST(cubicOp75d), + TEST(cubicOp74d), + TEST(cubicOp73d), + TEST(cubicOp72i), + TEST(cubicOp71d), + TEST(skp5), + TEST(skp4), + TEST(skp3), + TEST(skp2), + TEST(skp1), + TEST(rRect1), + TEST(cubicOp70d), + TEST(cubicOp69d), + TEST(cubicOp68u), + TEST(cubicOp67u), + TEST(cubicOp66u), + TEST(rectOp1d), + TEST(cubicOp65d), + TEST(cubicOp64d), + TEST(cubicOp63d), + TEST(cubicOp62d), + TEST(cubicOp61d), + TEST(cubicOp60d), + TEST(cubicOp59d), + TEST(cubicOp57d), + TEST(cubicOp56d), + TEST(cubicOp55d), + TEST(cubicOp54d), TEST(cubicOp53d), + TEST(cubicOp52d), + TEST(cubicOp51d), + TEST(cubicOp50d), + TEST(cubicOp49d), + TEST(cubicOp48d), + TEST(cubicOp47d), + TEST(cubicOp46d), + TEST(cubicOp45d), + TEST(cubicOp44d), + TEST(cubicOp43d), + TEST(cubicOp42d), + TEST(cubicOp41i), + TEST(cubicOp40d), + TEST(cubicOp39d), + TEST(cubicOp38d), + TEST(cubicOp37d), + TEST(cubicOp36u), + TEST(cubicOp35d), + TEST(cubicOp34d), + TEST(cubicOp33i), + TEST(cubicOp32d), + TEST(cubicOp31d), + TEST(cubicOp31x), + TEST(cubicOp31u), + TEST(cubicOp30d), + TEST(cubicOp29d), + TEST(cubicOp28u), + TEST(cubicOp27d), + TEST(cubicOp26d), + TEST(cubicOp25i), + TEST(testOp8d), + TEST(testDiff1), + TEST(testIntersect1), + TEST(testUnion1), + TEST(testXor1), + TEST(testDiff2), + TEST(testIntersect2), + TEST(testUnion2), + TEST(testXor2), + TEST(testOp1d), + TEST(testOp2d), + TEST(testOp3d), + TEST(testOp1u), + TEST(testOp4d), + TEST(testOp5d), + TEST(testOp6d), + TEST(testOp7d), + TEST(testOp2u), + + TEST(cubicOp24d), + TEST(cubicOp23d), + TEST(cubicOp22d), + TEST(cubicOp21d), + TEST(cubicOp20d), + TEST(cubicOp19i), + TEST(cubicOp18d), + TEST(cubicOp17d), + TEST(cubicOp16d), + TEST(cubicOp15d), + TEST(cubicOp14d), + TEST(cubicOp13d), + TEST(cubicOp12d), + TEST(cubicOp11d), + TEST(cubicOp10d), + TEST(cubicOp1i), + TEST(cubicOp9d), + TEST(quadOp9d), + TEST(lineOp9d), + TEST(cubicOp8d), + TEST(cubicOp7d), + TEST(cubicOp6d), + TEST(cubicOp5d), + TEST(cubicOp3d), + TEST(cubicOp2d), + TEST(cubicOp1d), +}; + +static const size_t testCount = SK_ARRAY_COUNT(tests); + +static struct TestDesc subTests[] = { + TEST(loops47i), + TEST(loops61i), + TEST(loops62i), + TEST(issue3517), }; static const size_t subTestCount = SK_ARRAY_COUNT(subTests); @@ -5435,7 +5801,7 @@ static void (*firstSubTest)(skiatest::Reporter* , const char* filename) = 0; static bool runSubTests = false; -static bool runSubTestsFirst = false; +static bool runSubTestsFirst = true; static bool runReverse = false; DEF_TEST(PathOpsOp, reporter) { @@ -5443,20 +5809,32 @@ strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH); #endif if (runSubTests && runSubTestsFirst) { - RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse); + RunTestSet(reporter, subTests, subTestCount, firstSubTest, nullptr, stopTest, runReverse); } - RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse); + RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse); if (runSubTests && !runSubTestsFirst) { - RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse); + RunTestSet(reporter, subTests, subTestCount, firstSubTest, nullptr, stopTest, runReverse); } } +static void fuzz535151(skiatest::Reporter* reporter, const char* filename) { + SkPath one; + one.setFillType(SkPath::kWinding_FillType); + SkPath two; + two.setFillType(SkPath::kWinding_FillType); + two.moveTo(0, 0); + two.lineTo(0, 50); + two.lineTo(4.29497e+09f, 50); + SkPath dummy; + testPathOpFuzz(reporter, one, two, kIntersect_SkPathOp, filename); +} + static void bufferOverflow(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.addRect(0,0, 300,170141183460469231731687303715884105728.f); SkPath pathB; pathB.addRect(0,0, 300,16); - testPathFailOp(reporter, path, pathB, kUnion_PathOp, filename); + testPathOpFuzz(reporter, path, pathB, kUnion_SkPathOp, filename); } // m 100,0 60,170 -160,-110 200,0 -170,11000000000 z @@ -5476,7 +5854,7 @@ path2.lineTo(-170 + 20,11000000000.0f + 20); path2.close(); - testPathFailOp(reporter, path1, path2, kIntersect_PathOp, filename); + testPathOpFuzz(reporter, path1, path2, kIntersect_SkPathOp, filename); } static void fuzz433b(skiatest::Reporter* reporter, const char* filename) { @@ -5499,7 +5877,7 @@ path2.lineTo(190, 60); path2.close(); - testPathFailOp(reporter, path1, path2, kUnion_PathOp, filename); + testPathOpFuzz(reporter, path1, path2, kUnion_SkPathOp, filename); } static void fuzz487a(skiatest::Reporter* reporter, const char* filename) { @@ -5545,7 +5923,7 @@ path.close(); SkPath path2(path); - testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename); } static void fuzz487b(skiatest::Reporter* reporter, const char* filename) { @@ -5591,14 +5969,2677 @@ path.close(); SkPath path2(path); - testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename); } +static void fuzz714(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x430c0000), SkBits2Float(0x42200000)); +path.lineTo(SkBits2Float(0x43480000), SkBits2Float(0x43520000)); +path.lineTo(SkBits2Float(0x42200000), SkBits2Float(0x42c80000)); +path.lineTo(SkBits2Float(0x64969569), SkBits2Float(0x42c80000)); // 2.22222e+022f +path.lineTo(SkBits2Float(0x64969569), SkBits2Float(0x43520000)); // 2.22222e+022f +path.lineTo(SkBits2Float(0x430c0000), SkBits2Float(0x42200000)); +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43200000), SkBits2Float(0x42700000)); +path.lineTo(SkBits2Float(0x435c0000), SkBits2Float(0x43660000)); +path.lineTo(SkBits2Float(0x42700000), SkBits2Float(0x42f00000)); +path.lineTo(SkBits2Float(0x64969569), SkBits2Float(0x42f00000)); // 2.22222e+022f +path.lineTo(SkBits2Float(0x64969569), SkBits2Float(0x43660000)); // 2.22222e+022f +path.lineTo(SkBits2Float(0x43200000), SkBits2Float(0x42700000)); +path.close(); + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz1(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x7f800000), SkBits2Float(0x7f800000)); +path.quadTo(SkBits2Float(0x7f800000), SkBits2Float(0x7f800000), SkBits2Float(0x7f800000), SkBits2Float(0x7f800000)); +path.quadTo(SkBits2Float(0x7f800000), SkBits2Float(0x7f800000), SkBits2Float(0x7f800000), SkBits2Float(0x7f800000)); +path.quadTo(SkBits2Float(0xffc00000), SkBits2Float(0x7f800000), SkBits2Float(0xffc00000), SkBits2Float(0x7f800000)); +path.quadTo(SkBits2Float(0xff000001), SkBits2Float(0x7f800000), SkBits2Float(0xff000001), SkBits2Float(0x7f800000)); +path.quadTo(SkBits2Float(0xff000001), SkBits2Float(0xffc00000), SkBits2Float(0xffc00000), SkBits2Float(0xffc00000)); +path.quadTo(SkBits2Float(0xffc00000), SkBits2Float(0xff000001), SkBits2Float(0x7f800000), SkBits2Float(0xff000001)); +path.quadTo(SkBits2Float(0x7f800000), SkBits2Float(0xff000001), SkBits2Float(0x7f800000), SkBits2Float(0xffc00000)); +path.quadTo(SkBits2Float(0x7f800000), SkBits2Float(0xffc00000), SkBits2Float(0x7f800000), SkBits2Float(0x7f800000)); +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename); +} + + +static void fuzz753_91(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42910000), SkBits2Float(0x00000000)); // 72.5f, 0 +path.lineTo(SkBits2Float(0x42166668), SkBits2Float(0x00000000)); // 37.6f, 0 +path.cubicTo(SkBits2Float(0x42166668), SkBits2Float(0xc1966668), SkBits2Float(0x41c66668), SkBits2Float(0xc20a6666), SkBits2Float(0x40f00010), SkBits2Float(0xc21ccccd)); // 37.6f, -18.8f, 24.8f, -34.6f, 7.50001f, -39.2f +path.lineTo(SkBits2Float(0x41840004), SkBits2Float(0xc291cccd)); // 16.5f, -72.9f +path.lineTo(SkBits2Float(0x42fb6668), SkBits2Float(0x42c73334)); // 125.7f, 99.6f +path.lineTo(SkBits2Float(0x43646668), SkBits2Float(0x43880ccd)); // 228.4f, 272.1f + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x428bf702), SkBits2Float(0xcf223cbf)); // 69.9824f, -2.72189e+09f +path.lineTo(SkBits2Float(0x42112d68), SkBits2Float(0xcf223cbf)); // 36.2943f, -2.72189e+09f +path.cubicTo(SkBits2Float(0x4220d9fc), SkBits2Float(0xcf223cc0), SkBits2Float(0x420ee118), SkBits2Float(0xcf223cc0), SkBits2Float(0x41cef2f8), SkBits2Float(0xcf223cc0)); // 40.2129f, -2.72189e+09f, 35.7198f, -2.72189e+09f, 25.8686f, -2.72189e+09f +path.lineTo(SkBits2Float(0x424a99e0), SkBits2Float(0xcf223cc0)); // 50.6503f, -2.72189e+09f +path.cubicTo(SkBits2Float(0x42266e32), SkBits2Float(0xcf223cc0), SkBits2Float(0x41f0fa20), SkBits2Float(0xcf223cc0), SkBits2Float(0x41872ed4), SkBits2Float(0xcf223cc0)); // 41.6076f, -2.72189e+09f, 30.1221f, -2.72189e+09f, 16.8979f, -2.72189e+09f +path.lineTo(SkBits2Float(0x40f8fbe0), SkBits2Float(0xcf223cc0)); // 7.78075f, -2.72189e+09f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void bug597926_0(skiatest::Reporter* reporter, const char* filename) { +SkPath path; +path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43b38000), SkBits2Float(0x433e0000)); // 359, 190 +path.lineTo(SkBits2Float(0x40c00000), SkBits2Float(0x449ce000)); // 6, 1255 +path.cubicTo(SkBits2Float(0x438c0000), SkBits2Float(0x4497a000), SkBits2Float(0x43e40000), SkBits2Float(0x44750000), SkBits2Float(0x41000000), SkBits2Float(0x44aa2000)); // 280, 1213, 456, 980, 8, 1361 +path.moveTo(SkBits2Float(0x43290000), SkBits2Float(0x4431c000)); // 169, 711 +path.lineTo(SkBits2Float(0xd987d6ba), SkBits2Float(0xd93d0ad4)); // -4.7794e+15f, -3.32567e+15f +path.conicTo(SkBits2Float(0x43cc8000), SkBits2Float(0x445b8000), SkBits2Float(0xd888b096), SkBits2Float(0xd9a1ebfa), SkBits2Float(0x3ebcb199)); // 409, 878, -1.20234e+15f, -5.69712e+15f, 0.368542f +path.cubicTo(SkBits2Float(0x43c00000), SkBits2Float(0x443a8000), SkBits2Float(0x42380000), SkBits2Float(0x4421c000), SkBits2Float(0x42500000), SkBits2Float(0x448ca000)); // 384, 746, 46, 647, 52, 1125 +path.quadTo(SkBits2Float(0x43948000), SkBits2Float(0x42ac0000), SkBits2Float(0x43880000), SkBits2Float(0x4487e000)); // 297, 86, 272, 1087 +SkPath path1(path); +path.reset(); +path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0xc51d735c), SkBits2Float(0xc49db029)); // -2519.21f, -1261.51f +path.cubicTo(SkBits2Float(0xc51d1dbd), SkBits2Float(0xc49d7a3f), SkBits2Float(0xc51c524a), SkBits2Float(0xc49d1610), SkBits2Float(0xc51d1a96), SkBits2Float(0xc49d86a6)); // -2513.86f, -1259.82f, -2501.14f, -1256.69f, -2513.66f, -1260.21f +path.cubicTo(SkBits2Float(0xc51cd471), SkBits2Float(0xc49d54d0), SkBits2Float(0xc51c2e51), SkBits2Float(0xc49d0081), SkBits2Float(0xc51d197b), SkBits2Float(0xc49d7927)); // -2509.28f, -1258.65f, -2498.89f, -1256.02f, -2513.59f, -1259.79f +path.quadTo(SkBits2Float(0xc51bf7eb), SkBits2Float(0xc49cf010), SkBits2Float(0xc51ba866), SkBits2Float(0xc49cb9e6)); // -2495.49f, -1255.5f, -2490.52f, -1253.81f +path.cubicTo(SkBits2Float(0xc51bac0d), SkBits2Float(0xc49cc50e), SkBits2Float(0xc51c29eb), SkBits2Float(0xc49cfb01), SkBits2Float(0xc51c5bca), SkBits2Float(0xc49d1fa6)); // -2490.75f, -1254.16f, -2498.62f, -1255.84f, -2501.74f, -1256.99f +SkPath path2(path); +testPathOpFuzz(reporter, path1, path2, (SkPathOp) 1, filename); +} + +static void fuzz1450_0(skiatest::Reporter* reporter, const char* filename) { +SkPath path; +path.moveTo(SkBits2Float(0x43b40000), SkBits2Float(0xcf000000)); // 360, -2.14748e+09f +path.conicTo(SkBits2Float(0x4e800002), SkBits2Float(0xcf000000), SkBits2Float(0x4e800002), SkBits2Float(0xce7ffffe), SkBits2Float(0x3f3504f4)); // 1.07374e+09f, -2.14748e+09f, 1.07374e+09f, -1.07374e+09f, 0.707107f +path.conicTo(SkBits2Float(0x4e800002), SkBits2Float(0x43800001), SkBits2Float(0x43348000), SkBits2Float(0x43800001), SkBits2Float(0x3f3504f4)); // 1.07374e+09f, 256, 180.5f, 256, 0.707107f +SkPath path1(path); +path.reset(); +path.moveTo(SkBits2Float(0x43b40000), SkBits2Float(0x45816000)); // 360, 4140 +path.conicTo(SkBits2Float(0x43b40005), SkBits2Float(0x458a945d), SkBits2Float(0x45610000), SkBits2Float(0x458a945d), SkBits2Float(0x3f3504f3)); // 360, 4434.55f, 3600, 4434.55f, 0.707107f +path.conicTo(SkBits2Float(0x45d5bfff), SkBits2Float(0x458a945d), SkBits2Float(0x45d5bfff), SkBits2Float(0x45816000), SkBits2Float(0x3f3504f3)); // 6840, 4434.55f, 6840, 4140, 0.707107f +path.lineTo(SkBits2Float(0x42c80000), SkBits2Float(0x44000000)); // 100, 512 +path.lineTo(SkBits2Float(0x42000000), SkBits2Float(0x41800000)); // 32, 16 +path.lineTo(SkBits2Float(0x43b40000), SkBits2Float(0x44800000)); // 360, 1024 +path.lineTo(SkBits2Float(0x43b40000), SkBits2Float(0x45816000)); // 360, 4140 +path.close(); +SkPath path2(path); +testPathOpFuzz(reporter, path1, path2, kUnion_SkPathOp, filename); +} + +static void fuzz1450_1(skiatest::Reporter* reporter, const char* filename) { +SkPath path; +path.setFillType(SkPath::kEvenOdd_FillType); +path.moveTo(SkBits2Float(0x4e800002), SkBits2Float(0xce7ffffe)); // 1.07374e+09f, -1.07374e+09f +path.conicTo(SkBits2Float(0x4e800002), SkBits2Float(0xcf000000), SkBits2Float(0x43b40000), SkBits2Float(0xcf000000), SkBits2Float(0x3f3504f4)); // 1.07374e+09f, -2.14748e+09f, 360, -2.14748e+09f, 0.707107f +path.lineTo(SkBits2Float(0x43348000), SkBits2Float(0x43800001)); // 180.5f, 256 +path.lineTo(SkBits2Float(0x42000000), SkBits2Float(0x41800000)); // 32, 16 +path.lineTo(SkBits2Float(0x42c80000), SkBits2Float(0x44000000)); // 100, 512 +path.lineTo(SkBits2Float(0x43553abd), SkBits2Float(0x440f3cbd)); // 213.229f, 572.949f +path.lineTo(SkBits2Float(0x43b40000), SkBits2Float(0x44800000)); // 360, 1024 +path.lineTo(SkBits2Float(0x43b40000), SkBits2Float(0x45816000)); // 360, 4140 +path.conicTo(SkBits2Float(0x43b40005), SkBits2Float(0x458a945d), SkBits2Float(0x45610000), SkBits2Float(0x458a945d), SkBits2Float(0x3f3504f3)); // 360, 4434.55f, 3600, 4434.55f, 0.707107f +path.conicTo(SkBits2Float(0x45d5bfff), SkBits2Float(0x458a945d), SkBits2Float(0x45d5bfff), SkBits2Float(0x45816000), SkBits2Float(0x3f3504f3)); // 6840, 4434.55f, 6840, 4140, 0.707107f +path.lineTo(SkBits2Float(0x43553abd), SkBits2Float(0x440f3cbd)); // 213.229f, 572.949f +path.lineTo(SkBits2Float(0x43348000), SkBits2Float(0x43800001)); // 180.5f, 256 +path.conicTo(SkBits2Float(0x4e800002), SkBits2Float(0x43800001), SkBits2Float(0x4e800002), SkBits2Float(0xce7ffffe), SkBits2Float(0x3f3504f4)); // 1.07374e+09f, 256, 1.07374e+09f, -1.07374e+09f, 0.707107f +path.close(); +SkPath path1(path); +path.reset(); +path.moveTo(SkBits2Float(0x42fe0000), SkBits2Float(0x43a08000)); // 127, 321 +path.lineTo(SkBits2Float(0x45d5c000), SkBits2Float(0x43870000)); // 6840, 270 +path.lineTo(SkBits2Float(0xd0a00000), SkBits2Float(0x4cbebc20)); // -2.14748e+10f, 1e+08 +path.lineTo(SkBits2Float(0x451f7000), SkBits2Float(0x42800000)); // 2551, 64 +path.lineTo(SkBits2Float(0x42fe0000), SkBits2Float(0x43a08000)); // 127, 321 +path.close(); +SkPath path2(path); +testPathOpFuzz(reporter, path1, path2, kUnion_SkPathOp, filename); +} + +static void fuzz763_9(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a21), SkBits2Float(0x7bc00321), SkBits2Float(0xed7a6a4b), SkBits2Float(0x1f212a8c)); // 2.49282e-13f, 4.78968e-34f, 1.99397e+36f, -4.84373e+27f, 3.41283e-20f +path.lineTo(SkBits2Float(0x7bc00321), SkBits2Float(0xed7a6a4b)); // 1.99397e+36f, -4.84373e+27f +path.lineTo(SkBits2Float(0x282a3a21), SkBits2Float(0x3a21df28)); // 9.4495e-15f, 0.000617492f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.close(); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.quadTo(SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3), SkBits2Float(0x1d2a2928), SkBits2Float(0x63962be6)); // -8.10388e-33f, 0.00148185f, 2.25206e-21f, 5.54035e+21f +path.moveTo(SkBits2Float(0x29272a81), SkBits2Float(0x2ab03a55)); // 3.71183e-14f, 3.13044e-13f +path.quadTo(SkBits2Float(0x2720213b), SkBits2Float(0x3a214729), SkBits2Float(0xdf28282a), SkBits2Float(0x8a2f2121)); // 2.22225e-15f, 0.000615227f, -1.2117e+19f, -8.43217e-33f +path.quadTo(SkBits2Float(0x373b3a27), SkBits2Float(0x201fc4c1), SkBits2Float(0x27576c2a), SkBits2Float(0x5921c25d)); // 1.11596e-05f, 1.35329e-19f, 2.98959e-15f, 2.8457e+15f +path.quadTo(SkBits2Float(0x2720213b), SkBits2Float(0x3a214729), SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21)); // 2.22225e-15f, 0.000615227f, -1.2117e+19f, 0.00105459f +path.cubicTo(SkBits2Float(0x373b3ac5), SkBits2Float(0x201fc422), SkBits2Float(0x523a702a), SkBits2Float(0x27576c51), SkBits2Float(0x5921c25d), SkBits2Float(0x51523a70)); // 1.11598e-05f, 1.35327e-19f, 2.00186e+11f, 2.9896e-15f, 2.8457e+15f, 5.64327e+10f +path.quadTo(SkBits2Float(0xd912102a), SkBits2Float(0x284f9a28), SkBits2Float(0xb38a1f30), SkBits2Float(0x3a3ac23a)); // -2.56957e+15f, 1.15242e-14f, -6.4318e-08f, 0.000712428f +path.lineTo(SkBits2Float(0xc809272a), SkBits2Float(0x29b02829)); // -140445, 7.82294e-14f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 1, filename); +} + + +static void fuzz763_4(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0x555b3a2d), SkBits2Float(0x2a212a8c)); // 1.50652e+13f, 1.43144e-13f +path.conicTo(SkBits2Float(0xc0032108), SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0)); // -2.04889f, 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f +path.conicTo(SkBits2Float(0x3a2147ed), SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac2b33a)); // 0.000615238f, -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148544f +path.cubicTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x63962be6), SkBits2Float(0x295b2d2a), SkBits2Float(0x68295b2d), SkBits2Float(0x2d296855), SkBits2Float(0x2a8c275b)); // 2.25206e-21f, 5.54035e+21f, 4.86669e-14f, 3.19905e+24f, 9.6297e-12f, 2.48963e-13f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.close(); +path.moveTo(SkBits2Float(0x55685b1f), SkBits2Float(0x5b2d2968)); // 1.59674e+13f, 4.87407e+16f +path.lineTo(SkBits2Float(0x2a212a8c), SkBits2Float(0x2a21081f)); // 1.43144e-13f, 1.43025e-13f +path.conicTo(SkBits2Float(0xde6a4b7b), SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21), SkBits2Float(0x3a7bc003), SkBits2Float(0x47ed7a6a)); // -4.22068e+18f, 2.50338e-13f, 4.61198e-19f, 0.00096035f, 121589 +path.lineTo(SkBits2Float(0x55685b1f), SkBits2Float(0x5b2d2968)); // 1.59674e+13f, 4.87407e+16f +path.close(); +path.moveTo(SkBits2Float(0x55685b1f), SkBits2Float(0x5b2d2968)); // 1.59674e+13f, 4.87407e+16f +path.quadTo(SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3)); // -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148185f +path.lineTo(SkBits2Float(0x2928088c), SkBits2Float(0x2be61d2a)); // 3.73109e-14f, 1.63506e-12f +path.conicTo(SkBits2Float(0x2a812a63), SkBits2Float(0x2d292a27), SkBits2Float(0x5568295b), SkBits2Float(0x5b2d2968), SkBits2Float(0x552d6829)); // 2.29444e-13f, 9.6159e-12f, 1.5954e+13f, 4.87407e+16f, 1.19164e+13f +path.conicTo(SkBits2Float(0x395b2d5b), SkBits2Float(0x68552768), SkBits2Float(0x555b2df0), SkBits2Float(0x1f722a8c), SkBits2Float(0x082a212a)); // 0.000209024f, 4.02636e+24f, 1.50619e+13f, 5.12807e-20f, 5.11965e-34f +path.lineTo(SkBits2Float(0x55685b1f), SkBits2Float(0x5b2d2968)); // 1.59674e+13f, 4.87407e+16f +path.close(); +path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a)); // 5.7784e-19f, 4.61198e-19f +path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2147ed7a), SkBits2Float(0x28282a3a), SkBits2Float(0x21df212a), SkBits2Float(0x033a8a3a)); // 6.14991e+25f, 6.77381e-19f, 9.33503e-15f, 1.51198e-18f, 5.48192e-37f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 1, filename); +} + +static void fuzz763_3(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0x555b292d), SkBits2Float(0x2a212a8c)); // 1.50606e+13f, 1.43144e-13f +path.conicTo(SkBits2Float(0xc0032108), SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x295b2d1f), SkBits2Float(0x29685568)); // -2.04889f, 3.04132e+35f, 5.77848e-19f, 4.86669e-14f, 5.15884e-14f +path.conicTo(SkBits2Float(0x8c28295b), SkBits2Float(0x1f21212a), SkBits2Float(0xc0032a08), SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced)); // -1.29547e-31f, 3.41205e-20f, -2.04944f, 3.04132e+35f, 5.77848e-19f +path.moveTo(SkBits2Float(0x25682929), SkBits2Float(0x212a8c5b)); // 2.01367e-16f, 5.7784e-19f +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4a7bc0)); // 4.7323e-37f, 6.11969e+25f +path.conicTo(SkBits2Float(0x032108ed), SkBits2Float(0x283a7bc0), SkBits2Float(0x47ed7a6a), SkBits2Float(0x282a3a21), SkBits2Float(0x3a21ff28)); // 4.73239e-37f, 1.03519e-14f, 121589, 9.4495e-15f, 0.000617968f +path.quadTo(SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3), SkBits2Float(0x2a292827), SkBits2Float(0x962be61d)); // -8.10388e-33f, 0.00148185f, 1.50241e-13f, -1.38859e-25f +path.lineTo(SkBits2Float(0x295b2d2a), SkBits2Float(0x2d296868)); // 4.86669e-14f, 9.62972e-12f +path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a)); // 5.7784e-19f, 4.61198e-19f +path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x898ced7a), SkBits2Float(0x21081f21), SkBits2Float(0x3a7bc003), SkBits2Float(0x47ed7a6a)); // 6.14991e+25f, -3.39271e-33f, 4.61198e-19f, 0.00096035f, 121589 +path.lineTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a)); // 5.7784e-19f, 4.61198e-19f +path.close(); +path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a)); // 5.7784e-19f, 4.61198e-19f +path.quadTo(SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0xb38a281a), SkBits2Float(0x29283ac2)); // -1.2117e+19f, 0.00105459f, -6.43342e-08f, 3.73545e-14f +path.moveTo(SkBits2Float(0x962be61d), SkBits2Float(0x432a2927)); // -1.38859e-25f, 170.161f +path.conicTo(SkBits2Float(0x3a2a552a), SkBits2Float(0x3b1e2ab0), SkBits2Float(0x29272021), SkBits2Float(0x3b3ac527), SkBits2Float(0x1fc42236)); // 0.000649768f, 0.00241343f, 3.71093e-14f, 0.00284989f, 8.30658e-20f +path.cubicTo(SkBits2Float(0x27576c2a), SkBits2Float(0x5921c25d), SkBits2Float(0x51503a70), SkBits2Float(0x12102a10), SkBits2Float(0x633a28d9), SkBits2Float(0x29c80927)); // 2.98959e-15f, 2.8457e+15f, 5.58959e+10f, 4.54902e-28f, 3.43404e+21f, 8.88337e-14f +path.lineTo(SkBits2Float(0x272927b0), SkBits2Float(0x5b392929)); // 2.3475e-15f, 5.21181e+16f +path.moveTo(SkBits2Float(0x3a1127b4), SkBits2Float(0x2921ee3b)); // 0.000553723f, 3.59558e-14f +path.cubicTo(SkBits2Float(0x5e215d3b), SkBits2Float(0x7828ee3a), SkBits2Float(0x8e28b03b), SkBits2Float(0x50783be8), SkBits2Float(0x9e0b8a3a), SkBits2Float(0x555b2d68)); // 2.90688e+18f, 1.37053e+34f, -2.07925e-30f, 1.66587e+10f, -7.38718e-21f, 1.50618e+13f +path.moveTo(SkBits2Float(0x21081f3f), SkBits2Float(0x9fd4e62a)); // 4.61199e-19f, -9.01663e-20f +path.cubicTo(SkBits2Float(0x3a293a2a), SkBits2Float(0x0e3bf0c5), SkBits2Float(0x3b29d42a), SkBits2Float(0x0f217265), SkBits2Float(0x2d5d2921), SkBits2Float(0x5568295b)); // 0.000645551f, 2.31655e-30f, 0.00259138f, 7.95994e-30f, 1.25715e-11f, 1.5954e+13f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 1, filename); +} + +static void fuzz763_5(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x5b292d55), SkBits2Float(0x2a2a8c55)); // 4.76191e+16f, 1.51477e-13f +path.conicTo(SkBits2Float(0xc0032108), SkBits2Float(0x7a6a4b79), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0)); // -2.04889f, 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f +path.conicTo(SkBits2Float(0x3a2147ed), SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3)); // 0.000615238f, -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148185f +path.cubicTo(SkBits2Float(0xe62a2928), SkBits2Float(0x2a63962b), SkBits2Float(0x68295b2d), SkBits2Float(0x2d296855), SkBits2Float(0x2a8c555b), SkBits2Float(0x001f2a21)); // -2.0089e+23f, 2.02138e-13f, 3.19905e+24f, 9.6297e-12f, 2.49282e-13f, 2.86201e-39f +path.lineTo(SkBits2Float(0x5b292d55), SkBits2Float(0x2a2a8c55)); // 4.76191e+16f, 1.51477e-13f +path.close(); +path.moveTo(SkBits2Float(0x5b292d55), SkBits2Float(0x2a2a8c55)); // 4.76191e+16f, 1.51477e-13f +path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21), SkBits2Float(0x3a7bc003), SkBits2Float(0x47ed7a6a)); // 6.14991e+25f, 2.50338e-13f, 4.61198e-19f, 0.00096035f, 121589 +path.lineTo(SkBits2Float(0x5b292d55), SkBits2Float(0x2a2a8c55)); // 4.76191e+16f, 1.51477e-13f +path.close(); +path.moveTo(SkBits2Float(0x5b292d55), SkBits2Float(0x2a2a8c55)); // 4.76191e+16f, 1.51477e-13f +path.quadTo(SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3b21), SkBits2Float(0x28ee4f9a), SkBits2Float(0x68293b78)); // -1.2117e+19f, 0.00105462f, 2.64578e-14f, 3.19671e+24f +path.lineTo(SkBits2Float(0x5b2d2968), SkBits2Float(0x5b2d8c55)); // 4.87407e+16f, 4.88495e+16f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename); +} + +static void fuzz763_2(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0x555b292d), SkBits2Float(0x2a212a8c)); // 1.50606e+13f, 1.43144e-13f +path.conicTo(SkBits2Float(0xc0032108), SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0)); // -2.04889f, 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f +path.lineTo(SkBits2Float(0x081f2ad7), SkBits2Float(0x7bc00321)); // 4.78977e-34f, 1.99397e+36f +path.moveTo(SkBits2Float(0x2a3a2147), SkBits2Float(0xdf212828)); // 1.65317e-13f, -1.16126e+19f +path.quadTo(SkBits2Float(0x4f1a3a8a), SkBits2Float(0x3ab38a28), SkBits2Float(0x29283ac2), SkBits2Float(0x962be62a)); // 2.58753e+09f, 0.00136978f, 3.73545e-14f, -1.38859e-25f +path.cubicTo(SkBits2Float(0x272a812a), SkBits2Float(0x3a2a5529), SkBits2Float(0x3b1e2ab0), SkBits2Float(0x29272021), SkBits2Float(0x3b3ac527), SkBits2Float(0x1fc42237)); // 2.36623e-15f, 0.000649768f, 0.00241343f, 3.71093e-14f, 0.00284989f, 8.30658e-20f +path.cubicTo(SkBits2Float(0x27576c2a), SkBits2Float(0x5921c25d), SkBits2Float(0x51523a70), SkBits2Float(0x12102a10), SkBits2Float(0x633a28d9), SkBits2Float(0x29c80927)); // 2.98959e-15f, 2.8457e+15f, 5.64327e+10f, 4.54902e-28f, 3.43404e+21f, 8.88337e-14f +path.lineTo(SkBits2Float(0x29292727), SkBits2Float(0x21475b3b)); // 3.75595e-14f, 6.75446e-19f +path.quadTo(SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3)); // -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148185f +path.cubicTo(SkBits2Float(0x682d2928), SkBits2Float(0x555b6829), SkBits2Float(0x555b292d), SkBits2Float(0x2a212a8c), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 3.27091e+24f, 1.50775e+13f, 1.50606e+13f, 1.43144e-13f, 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x295b2ded), SkBits2Float(0x29685568), SkBits2Float(0x8c555b2d), SkBits2Float(0xe61d2a2a), SkBits2Float(0x2a63962b)); // 4.86676e-14f, 5.15884e-14f, -1.64364e-31f, -1.85547e+23f, 2.02138e-13f +path.conicTo(SkBits2Float(0x5568295b), SkBits2Float(0x5b2d2968), SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a), SkBits2Float(0x4b7bc003)); // 1.5954e+13f, 4.87407e+16f, 5.7784e-19f, 4.61198e-19f, 1.64987e+07f +path.lineTo(SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21)); // 2.50338e-13f, 4.61198e-19f +path.conicTo(SkBits2Float(0x6a3a7bc0), SkBits2Float(0x2147ed7a), SkBits2Float(0x28282a3a), SkBits2Float(0x8a3a21df), SkBits2Float(0x27b42a3a)); // 5.63611e+25f, 6.77381e-19f, 9.33503e-15f, -8.96194e-33f, 5.00058e-15f +path.conicTo(SkBits2Float(0x2921217d), SkBits2Float(0x5e3a3b35), SkBits2Float(0x7828ee3a), SkBits2Float(0x8e28b03b), SkBits2Float(0x783be82a)); // 3.57782e-14f, 3.35484e+18f, 1.37053e+34f, -2.07925e-30f, 1.52448e+34f +path.conicTo(SkBits2Float(0x8e0b8a3a), SkBits2Float(0x279fd4e6), SkBits2Float(0x7a293a2a), SkBits2Float(0x2a0ef0c5), SkBits2Float(0x653b29d4)); // -1.71996e-30f, 4.43622e-15f, 2.19669e+35f, 1.26957e-13f, 5.52409e+22f +path.quadTo(SkBits2Float(0x29210f21), SkBits2Float(0x282a085d), SkBits2Float(0xc2ab2127), SkBits2Float(0xa6800028)); // 3.57623e-14f, 9.43871e-15f, -85.5648f, -8.88183e-16f +path.lineTo(SkBits2Float(0x2a3a2147), SkBits2Float(0xdf212828)); // 1.65317e-13f, -1.16126e+19f +path.close(); +path.moveTo(SkBits2Float(0x2a3a2147), SkBits2Float(0xdf212828)); // 1.65317e-13f, -1.16126e+19f +path.quadTo(SkBits2Float(0x216a2770), SkBits2Float(0x2ab73b28), SkBits2Float(0x4b28f427), SkBits2Float(0x283b5b28)); // 7.93345e-19f, 3.25484e-13f, 1.10726e+07f, 1.04004e-14f +path.lineTo(SkBits2Float(0x2a3a2147), SkBits2Float(0xdf212828)); // 1.65317e-13f, -1.16126e+19f +path.close(); +path.moveTo(SkBits2Float(0x2a3a2147), SkBits2Float(0xdf212828)); // 1.65317e-13f, -1.16126e+19f +path.conicTo(SkBits2Float(0xf86d273b), SkBits2Float(0x27e523e3), SkBits2Float(0x2927e0f5), SkBits2Float(0x2ac0e729), SkBits2Float(0x6b492128)); // -1.92402e+34f, 6.35992e-15f, 3.72766e-14f, 3.42665e-13f, 2.43151e+26f +path.cubicTo(SkBits2Float(0x2f273927), SkBits2Float(0xa83a2c21), SkBits2Float(0xd7122121), SkBits2Float(0x21212921), SkBits2Float(0x3be3db3a), SkBits2Float(0xa9deb63b)); // 1.52089e-10f, -1.03346e-14f, -1.60671e+14f, 5.46034e-19f, 0.00695362f, -9.89039e-14f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 1, filename); +} + +// crbug.com/626164 +static void fuzz763_1c(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); + path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 + path.cubicTo(SkBits2Float(0x1931204a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a4a34), SkBits2Float(0x4a4a4a4a)); // 9.15721e-24f, 1.14845e-12f, 3.31014e+06f, 3.31014e+06f, 3.31432e+06f, 3.31432e+06f + path.moveTo(SkBits2Float(0x000010a1), SkBits2Float(0x19312000)); // 5.96533e-42f, 9.15715e-24f + path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa14a4a4a), SkBits2Float(0x08ff2ba1), SkBits2Float(0x08ff4a4a), SkBits2Float(0x4a344a4a)); // 3.31432e+06f, 3.31432e+06f, -6.85386e-19f, 1.53575e-33f, 1.53647e-33f, 2.95387e+06f + path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4e4a08ff), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa1a181ff)); // 3.31432e+06f, 3.31432e+06f, 1.14845e-12f, 8.47397e+08f, 3.31432e+06f, -1.09442e-18f + + SkPath path2(path); + SkPath dummy; + testPathOpFuzz(reporter, path1, path2, (SkPathOp)4, filename); +} + +// crbug.com/626186 +static void fuzz763_1b(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 + path.cubicTo(SkBits2Float(0x0000ff07), SkBits2Float(0xf9f9ff00), SkBits2Float(0xfe0ef9f4), SkBits2Float(0xd9b105fb), SkBits2Float(0x000000f9), SkBits2Float(0xfe11f901)); // 9.14866e-41f, -1.62257e+35f, -4.75121e+37f, -6.22846e+15f, 3.48923e-43f, -4.85077e+37f + path.lineTo(SkBits2Float(0xda1905ed), SkBits2Float(0x3c05fbfb)); // -1.0768e+16f, 0.00817775f + path.cubicTo(SkBits2Float(0x3c3c3c3c), SkBits2Float(0x3c3c3c3c), SkBits2Float(0x253c7f00), SkBits2Float(0xfa00d3fa), SkBits2Float(0x250025fe), SkBits2Float(0x00000006)); // 0.011489f, 0.011489f, 1.63494e-16f, -1.67228e+35f, 1.11151e-16f, 8.40779e-45f + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); + path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 + path.quadTo(SkBits2Float(0x3c3c3c3c), SkBits2Float(0xfa253c3c), SkBits2Float(0xfefa00d3), SkBits2Float(0x25fad9df)); // 0.011489f, -2.14488e+35f, -1.66156e+38f, 4.35157e-16f + path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 + path.close(); + path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 + path.lineTo(SkBits2Float(0x8dfefa00), SkBits2Float(0xf0f9fad9)); // -1.57141e-30f, -6.1892e+29f + path.cubicTo(SkBits2Float(0x20fe58f9), SkBits2Float(0x0525fbed), SkBits2Float(0x1905ffff), SkBits2Float(0x01f9f9f9), SkBits2Float(0xfbfe0ef9), SkBits2Float(0xfb212fff)); // 4.30882e-19f, 7.80453e-36f, 6.92764e-24f, 9.18268e-38f, -2.63829e+36f, -8.36933e+35f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp)2, filename); +} + +static void fuzz763_1a(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 + path.cubicTo(SkBits2Float(0x154be880), SkBits2Float(0x80000640), SkBits2Float(0x5559a419), SkBits2Float(0x59d55928), SkBits2Float(0x80045959), SkBits2Float(0x40154be8)); // 4.11789e-26f, -2.24208e-42f, 1.49562e+13f, 7.50652e+15f, -3.99394e-40f, 2.33276f + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); + path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 + path.quadTo(SkBits2Float(0x5559a419), SkBits2Float(0x59d55928), SkBits2Float(0xbd595959), SkBits2Float(0x3f3f3f09)); // 1.49562e+13f, 7.50652e+15f, -0.0530637f, 0.747056f + path.moveTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0x3f3f3f3f)); // 0.747059f, 0.747059f + path.moveTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0xff3f3f3f)); // 0.747059f, -2.54211e+38f + path.lineTo(SkBits2Float(0x09090909), SkBits2Float(0x3038d509)); // 1.6495e-33f, 6.72416e-10f + path.conicTo(SkBits2Float(0x5947ffff), SkBits2Float(0x40e88004), SkBits2Float(0x00002059), SkBits2Float(0x28555900), SkBits2Float(0x5959d559)); // 3.51844e+15f, 7.26563f, 1.16042e-41f, 1.18432e-14f, 3.83217e+15f + path.lineTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0xff3f3f3f)); // 0.747059f, -2.54211e+38f + path.close(); + path.moveTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0xff3f3f3f)); // 0.747059f, -2.54211e+38f + path.lineTo(SkBits2Float(0x38d57f4b), SkBits2Float(0x59597f4b)); // 0.000101803f, 3.82625e+15f + path.lineTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0xff3f3f3f)); // 0.747059f, -2.54211e+38f + path.close(); + path.moveTo(SkBits2Float(0x384700ff), SkBits2Float(0x0108804b)); // 4.74462e-05f, 2.50713e-38f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp)0, filename); +} + +// crbug.com/627780 +static void fuzz763_3a(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0x555b292d), SkBits2Float(0x2a212a8c)); // 1.50606e+13f, 1.43144e-13f +path.conicTo(SkBits2Float(0xc0032108), SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0)); // -2.04889f, 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f +path.conicTo(SkBits2Float(0x3a2147ed), SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3)); // 0.000615238f, -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148185f +path.cubicTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x63962be6), SkBits2Float(0x272a812a), SkBits2Float(0x295b2d29), SkBits2Float(0x2a685568), SkBits2Float(0x68295b2d)); // 2.25206e-21f, 5.54035e+21f, 2.36623e-15f, 4.86669e-14f, 2.06354e-13f, 3.19905e+24f +path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a21), SkBits2Float(0x7bc00321), SkBits2Float(0x7a6a4b77), SkBits2Float(0x3a214726)); // 2.49282e-13f, 4.78968e-34f, 1.99397e+36f, 3.04132e+35f, 0.000615226f +path.moveTo(SkBits2Float(0x8adf2028), SkBits2Float(0x3a219a3a)); // -2.14862e-32f, 0.000616464f +path.quadTo(SkBits2Float(0x3ab38e28), SkBits2Float(0x29283ac2), SkBits2Float(0x2be61d2a), SkBits2Float(0x812a4396)); // 0.0013699f, 3.73545e-14f, 1.63506e-12f, -3.12726e-38f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 1, filename); +} + +// crbug.com/627689 +static void fuzz763_5a(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x38bd8610), SkBits2Float(0x00000000)); // 9.03719e-05f, 0 +path.conicTo(SkBits2Float(0x4183d871), SkBits2Float(0x41fea321), SkBits2Float(0xb700ff00), SkBits2Float(0x4240b8b8), SkBits2Float(0x3b058283)); // 16.4807f, 31.8297f, -7.68877e-06f, 48.1804f, 0.0020372f +path.lineTo(SkBits2Float(0x3a3a3ab8), SkBits2Float(0xb8b8b8b8)); // 0.000710409f, -8.80821e-05f +path.conicTo(SkBits2Float(0x3a455ec8), SkBits2Float(0xb8b8b8b3), SkBits2Float(0x38b2418d), SkBits2Float(0xb730d014), SkBits2Float(0x3f7ffff3)); // 0.000752908f, -8.80821e-05f, 8.49991e-05f, -1.05389e-05f, 0.999999f +path.quadTo(SkBits2Float(0x3a51246a), SkBits2Float(0xb6da45a3), SkBits2Float(0x38bc5c3c), SkBits2Float(0x00000000)); // 0.000797814f, -6.50501e-06f, 8.98172e-05f, 0 +path.lineTo(SkBits2Float(0x3a3a3ab8), SkBits2Float(0xb8b8b8b8)); // 0.000710409f, -8.80821e-05f +path.quadTo(SkBits2Float(0x39a32d2d), SkBits2Float(0x00000000), SkBits2Float(0xb8a13a00), SkBits2Float(0x00000000)); // 0.000311234f, 0, -7.68788e-05f, 0 +path.lineTo(SkBits2Float(0x3a3a3ab8), SkBits2Float(0xb8b8b8b8)); // 0.000710409f, -8.80821e-05f +path.quadTo(SkBits2Float(0x39ba814c), SkBits2Float(0xb838fed2), SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0.00035573f, -4.41063e-05f, 0, 0 +path.lineTo(SkBits2Float(0x38bd8610), SkBits2Float(0x00000000)); // 9.03719e-05f, 0 +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename); +} + +// crbug.com/627401 +static void fuzz763_2a(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.quadTo(SkBits2Float(0x3e484500), SkBits2Float(0x164f3a30), SkBits2Float(0x49484801), SkBits2Float(0x7d0100c8)); // 0.195576f, 1.67397e-25f, 820352, 1.07172e+37f +path.conicTo(SkBits2Float(0xff7f36fd), SkBits2Float(0x3e647d01), SkBits2Float(0x0c00f430), SkBits2Float(0x486b6448), SkBits2Float(0x00484848)); // -3.39239e+38f, 0.223133f, 9.93424e-32f, 241041, 6.63809e-39f +path.lineTo(SkBits2Float(0x4f4f557d), SkBits2Float(0x48480112)); // 3.47849e+09f, 204804 +path.lineTo(SkBits2Float(0xf40c01ff), SkBits2Float(0x45008000)); // -4.43702e+31f, 2056 +path.moveTo(SkBits2Float(0x4bfffa00), SkBits2Float(0x7d4ac859)); // 3.35514e+07f, 1.68465e+37f +path.conicTo(SkBits2Float(0x7d014f3e), SkBits2Float(0x00f4ff01), SkBits2Float(0x6b64480c), SkBits2Float(0x48484848), SkBits2Float(0x557d0100)); // 1.07426e+37f, 2.24993e-38f, 2.75975e+26f, 205089, 1.73863e+13f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +// crbug.com/627761 +static void fuzz763_2b(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x5b292d55), SkBits2Float(0x212a8c55)); // 4.76191e+16f, 5.7784e-19f +path.moveTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003)); // 0.00245715f, 1.64987e+07f +path.lineTo(SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21)); // 2.50338e-13f, 4.61198e-19f +path.conicTo(SkBits2Float(0x6a3a7bc0), SkBits2Float(0x4721ed7a), SkBits2Float(0x282a3a21), SkBits2Float(0x3a21df28), SkBits2Float(0x4f9a3a8a)); // 5.63611e+25f, 41453.5f, 9.4495e-15f, 0.000617492f, 5.17506e+09f +path.lineTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003)); // 0.00245715f, 1.64987e+07f +path.close(); +path.moveTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003)); // 0.00245715f, 1.64987e+07f +path.cubicTo(SkBits2Float(0x273ac23a), SkBits2Float(0x1d2a2928), SkBits2Float(0x63962be6), SkBits2Float(0x272a812a), SkBits2Float(0x295b2d29), SkBits2Float(0x29685568)); // 2.5918e-15f, 2.25206e-21f, 5.54035e+21f, 2.36623e-15f, 4.86669e-14f, 5.15884e-14f +path.lineTo(SkBits2Float(0x081f2a21), SkBits2Float(0x7bc00321)); // 4.78968e-34f, 1.99397e+36f +path.lineTo(SkBits2Float(0x282a3a21), SkBits2Float(0x3a21df28)); // 9.4495e-15f, 0.000617492f +path.lineTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003)); // 0.00245715f, 1.64987e+07f +path.close(); +path.moveTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003)); // 0.00245715f, 1.64987e+07f +path.quadTo(SkBits2Float(0x8a4fc29a), SkBits2Float(0x3ab3283a), SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6)); // -1.00033e-32f, 0.00136686f, 2.25206e-21f, 300.343f +path.moveTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829)); // 4.87419e+16f, 3.71718e-14f +path.conicTo(SkBits2Float(0x1e2ab03a), SkBits2Float(0x2920213b), SkBits2Float(0x3b3ac527), SkBits2Float(0xc422333b), SkBits2Float(0x6c2a9f1f)); // 9.03617e-21f, 3.5556e-14f, 0.00284989f, -648.8f, 8.25075e+26f +path.quadTo(SkBits2Float(0xc25d2757), SkBits2Float(0x3a705921), SkBits2Float(0x2a105152), SkBits2Float(0x28d91210)); // -55.2884f, 0.000916855f, 1.2818e-13f, 2.40997e-14f +path.quadTo(SkBits2Float(0x68295b2d), SkBits2Float(0x2d296855), SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a21)); // 3.19905e+24f, 9.6297e-12f, 2.49282e-13f, 4.78968e-34f +path.lineTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829)); // 4.87419e+16f, 3.71718e-14f +path.close(); +path.moveTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829)); // 4.87419e+16f, 3.71718e-14f +path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21), SkBits2Float(0xcb7bc003), SkBits2Float(0x47ed7a6a)); // 6.14991e+25f, 2.50338e-13f, 4.61198e-19f, -1.64987e+07f, 121589 +path.lineTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829)); // 4.87419e+16f, 3.71718e-14f +path.close(); +path.moveTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829)); // 4.87419e+16f, 3.71718e-14f +path.quadTo(SkBits2Float(0xdf28282a), SkBits2Float(0x2d8a3a21), SkBits2Float(0x5b682b68), SkBits2Float(0x5b292d55)); // -1.2117e+19f, 1.57146e-11f, 6.53499e+16f, 4.76191e+16f +path.lineTo(SkBits2Float(0x2a212a8c), SkBits2Float(0x0321081f)); // 1.43144e-13f, 4.7323e-37f +path.conicTo(SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0), SkBits2Float(0x3a21477a)); // 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f, 0.000615231f +path.moveTo(SkBits2Float(0x21df2828), SkBits2Float(0x9a3a8a3a)); // 1.51217e-18f, -3.85756e-23f +path.quadTo(SkBits2Float(0x3ab38a28), SkBits2Float(0x28273ac2), SkBits2Float(0xe61d2a29), SkBits2Float(0x2a63962b)); // 0.00136978f, 9.2831e-15f, -1.85547e+23f, 2.02138e-13f +path.conicTo(SkBits2Float(0x2d29272a), SkBits2Float(0x5568295b), SkBits2Float(0x5b2d2968), SkBits2Float(0x5b2d6829), SkBits2Float(0x212a8c55)); // 9.61523e-12f, 1.5954e+13f, 4.87407e+16f, 4.88097e+16f, 5.7784e-19f +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x3a2147ed), SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3)); // 0.000615238f, -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148185f +path.lineTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.close(); + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename); +} + +static void fuzz763_2c(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x36344a4a)); // 0, 2.68653e-06f +path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x364a4a4a), SkBits2Float(0x364a4a4a), SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0, 3.01436e-06f, 3.01436e-06f, 0, 0 +path.lineTo(SkBits2Float(0x364a4a4a), SkBits2Float(0x00000000)); // 3.01436e-06f, 0 +path.cubicTo(SkBits2Float(0x364a30f0), SkBits2Float(0x344ac7fb), SkBits2Float(0x3656d432), SkBits2Float(0x34cabb48), SkBits2Float(0x367031a9), SkBits2Float(0x351802f1)); // 3.01288e-06f, 1.88855e-07f, 3.2012e-06f, 3.77617e-07f, 3.57917e-06f, 5.66287e-07f +path.cubicTo(SkBits2Float(0x36a7b150), SkBits2Float(0x35ab09db), SkBits2Float(0x371874ed), SkBits2Float(0x3604f2c7), SkBits2Float(0x3784e0c7), SkBits2Float(0x36344a51)); // 4.99763e-06f, 1.27434e-06f, 9.08713e-06f, 1.98108e-06f, 1.58403e-05f, 2.68653e-06f +path.cubicTo(SkBits2Float(0x3743dc9a), SkBits2Float(0x36344a4f), SkBits2Float(0x36fbef33), SkBits2Float(0x36344a4e), SkBits2Float(0x36604a35), SkBits2Float(0x36344a4c)); // 1.16743e-05f, 2.68653e-06f, 7.50823e-06f, 2.68653e-06f, 3.34218e-06f, 2.68653e-06f +path.cubicTo(SkBits2Float(0x36531715), SkBits2Float(0x36344a4c), SkBits2Float(0x3645e3f5), SkBits2Float(0x36344a4b), SkBits2Float(0x3638b0d4), SkBits2Float(0x36344a4b)); // 3.14549e-06f, 2.68653e-06f, 2.9488e-06f, 2.68653e-06f, 2.75211e-06f, 2.68653e-06f +path.cubicTo(SkBits2Float(0x35f64120), SkBits2Float(0x36344a4b), SkBits2Float(0x35764124), SkBits2Float(0x36344a4a), SkBits2Float(0x00000000), SkBits2Float(0x36344a4a)); // 1.83474e-06f, 2.68653e-06f, 9.17369e-07f, 2.68653e-06f, 0, 2.68653e-06f +path.close(); + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.cubicTo(SkBits2Float(0x1931204a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a4a34), SkBits2Float(0x4a4a4a4a)); // 9.15721e-24f, 1.14845e-12f, 3.31014e+06f, 3.31014e+06f, 3.31432e+06f, 3.31432e+06f +path.moveTo(SkBits2Float(0x000010a1), SkBits2Float(0x19312000)); // 5.96533e-42f, 9.15715e-24f +path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa14a4a4a), SkBits2Float(0x08ff2ba1), SkBits2Float(0x08ff4a4a), SkBits2Float(0x4a344a4a)); // 3.31432e+06f, 3.31432e+06f, -6.85386e-19f, 1.53575e-33f, 1.53647e-33f, 2.95387e+06f +path.cubicTo(SkBits2Float(0x544a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4e4a08ff), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa1a181ff)); // 3.47532e+12f, 3.31432e+06f, 1.14845e-12f, 8.47397e+08f, 3.31432e+06f, -1.09442e-18f + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, kReverseDifference_SkPathOp, filename); +} + +static void fuzz763_6(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x6a2a291f)); // 0, 5.14279e+25f +path.cubicTo(SkBits2Float(0x68295b2d), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x68556829)); // 3.19905e+24f, 0, 0, 0, 0, 4.03114e+24f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x68555b2a)); // 0, 4.03018e+24f +path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0x67d55b2a), SkBits2Float(0x67296a4b), SkBits2Float(0x67555b2a), SkBits2Float(0x677e1f70), SkBits2Float(0x66d55b2a)); // 0, 2.01509e+24f, 8.00041e+23f, 1.00755e+24f, 1.20006e+24f, 5.03773e+23f +path.cubicTo(SkBits2Float(0x678f0684), SkBits2Float(0x6684f008), SkBits2Float(0x6798f8ea), SkBits2Float(0x6625a942), SkBits2Float(0x67961914), SkBits2Float(0x65ce709a)); // 1.35084e+24f, 3.1389e+23f, 1.44478e+24f, 1.95578e+23f, 1.41764e+24f, 1.21861e+23f +path.cubicTo(SkBits2Float(0x679174f7), SkBits2Float(0x63199132), SkBits2Float(0x6756c79f), SkBits2Float(0x606478de), SkBits2Float(0x65682bcf), SkBits2Float(0x00000000)); // 1.3738e+24f, 2.83281e+21f, 1.01427e+24f, 6.58526e+19f, 6.85248e+22f, 0 +path.conicTo(SkBits2Float(0x68295b02), SkBits2Float(0x60f7f28b), SkBits2Float(0x00000000), SkBits2Float(0x6a2a291f), SkBits2Float(0x42784f5a)); // 3.19903e+24f, 1.42932e+20f, 0, 5.14279e+25f, 62.0775f +path.close(); +path.moveTo(SkBits2Float(0x654d6d10), SkBits2Float(0x00000000)); // 6.06311e+22f, 0 +path.lineTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x00000000)); // 6.14991e+25f, 0 +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x6a4b7bc0)); // 0, 6.14991e+25f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x3ac23a55), SkBits2Float(0x2a292827)); // 0.00148184f, 1.50241e-13f +path.lineTo(SkBits2Float(0x63962be6), SkBits2Float(0x272a812a)); // 5.54035e+21f, 2.36623e-15f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void fuzz763_7(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 4.03114e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x0f2a312a), SkBits2Float(0xc0032108)); // 8.39112e-30f, -2.04889f +path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a1f2a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0)); // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76395e-19f, 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x2d555b2d)); // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68345b2d), SkBits2Float(0xf0682955)); // 3.40683e+24f, -2.87402e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0xef2a8c55), SkBits2Float(0x295b2d2a), SkBits2Float(0x08685568), SkBits2Float(0x7bc00321)); // 5.76397e-19f, -5.27821e+28f, 4.86669e-14f, 6.99154e-34f, 1.99397e+36f +path.lineTo(SkBits2Float(0x68345b2d), SkBits2Float(0xf0682955)); // 3.40683e+24f, -2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0x68345b2d), SkBits2Float(0xf0682955)); // 3.40683e+24f, -2.87402e+29f +path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55)); // 4.85282e+16f, 5.7784e-19f +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.lineTo(SkBits2Float(0x3a8a3adf), SkBits2Float(0x8a281a4f)); // 0.00105461f, -8.09385e-33f +path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x272a812a), SkBits2Float(0x3a2a5529)); // 2.25206e-21f, 300.343f, 2.36623e-15f, 0.000649768f +path.lineTo(SkBits2Float(0x213b1e2a), SkBits2Float(0x27292720)); // 6.3398e-19f, 2.34747e-15f +path.conicTo(SkBits2Float(0xba1f203a), SkBits2Float(0xc422c538), SkBits2Float(0x215d5927), SkBits2Float(0x70ec2ac2), SkBits2Float(0x2a51523a)); // -0.000607017f, -651.082f, 7.49957e-19f, 5.84721e+29f, 1.85915e-13f +path.quadTo(SkBits2Float(0x633ad912), SkBits2Float(0x29c80927), SkBits2Float(0x272927b0), SkBits2Float(0x683a5b2d)); // 3.44674e+21f, 8.88337e-14f, 2.3475e-15f, 3.52017e+24f +path.lineTo(SkBits2Float(0x295b2d68), SkBits2Float(0x29685568)); // 4.86672e-14f, 5.15884e-14f +path.conicTo(SkBits2Float(0xaa8c555b), SkBits2Float(0x081f2a21), SkBits2Float(0x5b2d0321), SkBits2Float(0x68556829), SkBits2Float(0x2a552d29)); // -2.49282e-13f, 4.78968e-34f, 4.86986e+16f, 4.03114e+24f, 1.89339e-13f +path.cubicTo(SkBits2Float(0x21295b2d), SkBits2Float(0x2a688c5b), SkBits2Float(0x68295b2d), SkBits2Float(0x2d296855), SkBits2Float(0x8c08555b), SkBits2Float(0x2a2a29ca)); // 5.73801e-19f, 2.06544e-13f, 3.19905e+24f, 9.6297e-12f, -1.05027e-31f, 1.51135e-13f +path.quadTo(SkBits2Float(0x68295b21), SkBits2Float(0x2d296855), SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a21)); // 3.19904e+24f, 9.6297e-12f, 2.49282e-13f, 4.78968e-34f +path.lineTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.close(); +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x5b2d6829), SkBits2Float(0x212a8c55), SkBits2Float(0xed7aba1f), SkBits2Float(0x2a212a8c)); // 6.14991e+25f, 4.88097e+16f, 5.7784e-19f, -4.84977e+27f, 1.43144e-13f +path.moveTo(SkBits2Float(0x2d212d08), SkBits2Float(0x5568295b)); // 9.16179e-12f, 1.5954e+13f +path.moveTo(SkBits2Float(0x5529685b), SkBits2Float(0x11295b68)); // 1.16416e+13f, 1.33599e-28f +path.conicTo(SkBits2Float(0x5b782968), SkBits2Float(0x3a292d55), SkBits2Float(0x2a8c555b), SkBits2Float(0x68295a2d), SkBits2Float(0x2d296855)); // 6.98513e+16f, 0.000645359f, 2.49282e-13f, 3.19897e+24f, 9.6297e-12f +path.moveTo(SkBits2Float(0x555b8c55), SkBits2Float(0x21682929)); // 1.50872e+13f, 7.86591e-19f +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0xac2d8ced), SkBits2Float(0x5b682968), SkBits2Float(0x5b292d55), SkBits2Float(0x212a8c55), SkBits2Float(0x081f282a)); // -2.4663e-12f, 6.53477e+16f, 4.76191e+16f, 5.7784e-19f, 4.78945e-34f +path.lineTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.close(); +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2a8ced7a), SkBits2Float(0x03081f21), SkBits2Float(0x6a3a7bc0), SkBits2Float(0x2147ed7a)); // 6.14991e+25f, 2.50338e-13f, 4.00025e-37f, 5.63611e+25f, 6.77381e-19f +path.lineTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.close(); +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.quadTo(SkBits2Float(0x2d28282a), SkBits2Float(0x5568295b), SkBits2Float(0x3a21df68), SkBits2Float(0x4f9a3a8a)); // 9.55861e-12f, 1.5954e+13f, 0.000617495f, 5.17506e+09f +path.lineTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.close(); +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.cubicTo(SkBits2Float(0x5568c23a), SkBits2Float(0x5b2d2968), SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a), SkBits2Float(0x3a7bc003), SkBits2Float(0x294b2827)); // 1.59951e+13f, 4.87407e+16f, 5.7784e-19f, 4.61198e-19f, 0.00096035f, 4.51099e-14f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void kfuzz2(skiatest::Reporter* reporter, const char* filename) { + SkPath path1; + SkPath path; +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xfafadbfa)); // 0, -6.51268e+35f +path.close(); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xfafadbfa)); // 0, -6.51268e+35f +path.cubicTo(SkBits2Float(0xe3000000), SkBits2Float(0xf19e92c7), SkBits2Float(0xf17febcb), SkBits2Float(0xff7febcb), SkBits2Float(0x60600100), SkBits2Float(0x0100ff60)); // -2.36118e+21f, -1.57043e+30f, -1.26726e+30f, -3.40177e+38f, 6.45647e+19f, 2.36931e-38f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xfafadbfa)); // 0, -6.51268e+35f +path.close(); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xfafadbfa)); // 0, -6.51268e+35f +path.lineTo(SkBits2Float(0x60601a1d), SkBits2Float(0x60606060)); // 6.4593e+19f, 6.46721e+19f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xfafadbfa)); // 0, -6.51268e+35f +path.close(); +path.moveTo(SkBits2Float(0xe5e2f300), SkBits2Float(0xee244a40)); // -1.33967e+23f, -1.27113e+28f +path.moveTo(SkBits2Float(0xba98ffee), SkBits2Float(0xfafafa1a)); // -0.0011673f, -6.51573e+35f +path.close(); +path.moveTo(SkBits2Float(0xba98ffee), SkBits2Float(0xfafafa1a)); // -0.0011673f, -6.51573e+35f +path.lineTo(SkBits2Float(0xfafafafa), SkBits2Float(0xe30000fa)); // -6.51582e+35f, -2.36125e+21f +path.conicTo(SkBits2Float(0x92e592e5), SkBits2Float(0xfafafafb), SkBits2Float(0xc4fa0000), SkBits2Float(0x6060fafa), SkBits2Float(0x60606060)); // -1.44881e-27f, -6.51582e+35f, -2000, 6.48462e+19f, 6.46721e+19f +path.lineTo(SkBits2Float(0xba98ffee), SkBits2Float(0xfafafa1a)); // -0.0011673f, -6.51573e+35f +path.close(); +path.moveTo(SkBits2Float(0xba98ffee), SkBits2Float(0xfafafa1a)); // -0.0011673f, -6.51573e+35f +path.cubicTo(SkBits2Float(0xe3000000), SkBits2Float(0xf19e92c7), SkBits2Float(0xf17febcb), SkBits2Float(0xff7febcb), SkBits2Float(0xfafafa00), SkBits2Float(0xfafafafa)); // -2.36118e+21f, -1.57043e+30f, -1.26726e+30f, -3.40177e+38f, -6.51572e+35f, -6.51582e+35f +path.lineTo(SkBits2Float(0xba98ffee), SkBits2Float(0xfafafa1a)); // -0.0011673f, -6.51573e+35f +path.close(); +path.moveTo(SkBits2Float(0xba98ffee), SkBits2Float(0xfafafa1a)); // -0.0011673f, -6.51573e+35f +path.cubicTo(SkBits2Float(0xe3000000), SkBits2Float(0xe39e92c7), SkBits2Float(0xf17febcb), SkBits2Float(0xff7febcb), SkBits2Float(0xeed0ee9a), SkBits2Float(0x9a98ffca)); // -2.36118e+21f, -5.85032e+21f, -1.26726e+30f, -3.40177e+38f, -3.23307e+28f, -6.3279e-23f +path.lineTo(SkBits2Float(0xba98ffee), SkBits2Float(0xfafafa1a)); // -0.0011673f, -6.51573e+35f +path.close(); +SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, kXOR_SkPathOp, filename); +} + +static void fuzz763_10(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x68556829)); // 0, 4.03114e+24f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.quadTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x6a4b7bc4)); // 6.14991e+25f, 0, 0, 6.14991e+25f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x68556829)); // 0, 4.03114e+24f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0x5b2d2968), SkBits2Float(0x2a8c8f55)); // 4.87407e+16f, 2.49685e-13f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.close(); +path.moveTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03)); // -2.51765f, 6.14982e+25f +path.conicTo(SkBits2Float(0x682d2fed), SkBits2Float(0x755b6829), SkBits2Float(0x5b292d2b), SkBits2Float(0xc92a8c55), SkBits2Float(0x081f2a21)); // 3.27141e+24f, 2.78131e+32f, 4.76189e+16f, -698565, 4.78968e-34f +path.lineTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03)); // -2.51765f, 6.14982e+25f +path.close(); +path.moveTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03)); // -2.51765f, 6.14982e+25f +path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21), SkBits2Float(0x3a7bc003), SkBits2Float(0x47ed7a29)); // 6.14991e+25f, 2.50338e-13f, 4.61198e-19f, 0.00096035f, 121588 +path.lineTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03)); // -2.51765f, 6.14982e+25f +path.close(); +path.moveTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03)); // -2.51765f, 6.14982e+25f +path.quadTo(SkBits2Float(0x6829682d), SkBits2Float(0x292d555b), SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a29)); // 3.20001e+24f, 3.84878e-14f, 2.49282e-13f, 4.78969e-34f +path.conicTo(SkBits2Float(0x6a497b19), SkBits2Float(0x218ced7a), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0), SkBits2Float(0x47ed3a7a)); // 6.08939e+25f, 9.54963e-19f, 4.7323e-37f, 5.63611e+25f, 121461 +path.lineTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03)); // -2.51765f, 6.14982e+25f +path.close(); +path.moveTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03)); // -2.51765f, 6.14982e+25f +path.quadTo(SkBits2Float(0x282a282a), SkBits2Float(0x8a3a21df), SkBits2Float(0x2728282a), SkBits2Float(0x8a3a2129)); // 9.4456e-15f, -8.96194e-33f, 2.33365e-15f, -8.96181e-33f +path.quadTo(SkBits2Float(0x8a284f9a), SkBits2Float(0x3a3ac2b3), SkBits2Float(0x2a292827), SkBits2Float(0x962be61d)); // -8.10388e-33f, 0.000712435f, 1.50241e-13f, -1.38859e-25f +path.lineTo(SkBits2Float(0x272a802a), SkBits2Float(0x2a8c2d29)); // 2.36617e-15f, 2.49003e-13f +path.lineTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03)); // -2.51765f, 6.14982e+25f +path.close(); +path.moveTo(SkBits2Float(0x4f9a3a29), SkBits2Float(0x3ab38a28)); // 5.17501e+09f, 0.00136978f +path.quadTo(SkBits2Float(0xc368305b), SkBits2Float(0x5b296855), SkBits2Float(0x2d8c5568), SkBits2Float(0x1f2a2172)); // -232.189f, 4.7684e+16f, 1.59541e-11f, 3.60266e-20f +path.lineTo(SkBits2Float(0x29c00321), SkBits2Float(0x5b4b7b13)); // 8.52706e-14f, 5.72747e+16f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename); +} + +static void fuzz763_11(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 4.03114e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x2a0f312a), SkBits2Float(0xc0032108)); // 1.2718e-13f, -2.04889f +path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a1f2a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0)); // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76395e-19f, 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7b21), SkBits2Float(0x2829ed84), SkBits2Float(0x2d555b2d)); // 5.77848e-19f, 4.7323e-37f, 6.14984e+25f, 9.43289e-15f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68385b2d), SkBits2Float(0x70682955)); // 3.48239e+24f, 2.87402e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0xef2a8c55), SkBits2Float(0x295b2d2a), SkBits2Float(0x08685568), SkBits2Float(0x7bc00321)); // 5.76397e-19f, -5.27821e+28f, 4.86669e-14f, 6.99154e-34f, 1.99397e+36f +path.lineTo(SkBits2Float(0x68385b2d), SkBits2Float(0x70682955)); // 3.48239e+24f, 2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0x68385b2d), SkBits2Float(0x70682955)); // 3.48239e+24f, 2.87402e+29f +path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55)); // 4.85282e+16f, 5.7784e-19f +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.lineTo(SkBits2Float(0x3a8a3adf), SkBits2Float(0x8a281a4f)); // 0.00105461f, -8.09385e-33f +path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x2a812a3b), SkBits2Float(0x2a552927)); // 2.25206e-21f, 300.343f, 2.29443e-13f, 1.89325e-13f +path.quadTo(SkBits2Float(0x3b1e2ab0), SkBits2Float(0x29272021), SkBits2Float(0x203a3b27), SkBits2Float(0x22c5381f)); // 0.00241343f, 3.71093e-14f, 1.57744e-19f, 5.34564e-18f +path.moveTo(SkBits2Float(0x5d27ec2a), SkBits2Float(0x705921c2)); // 7.56256e+17f, 2.68796e+29f +path.quadTo(SkBits2Float(0x102a5152), SkBits2Float(0x5b2dd912), SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 3.35892e-29f, 4.89338e+16f, 4.03114e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032127)); // 3.60396e-20f, -2.0489f +path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0x2a8c684b), SkBits2Float(0xf05b272d), SkBits2Float(0x2a1f1555), SkBits2Float(0x21082a21), SkBits2Float(0x6a4b7b03)); // 3.4979e+24f, 2.49414e-13f, -2.71298e+29f, 1.41294e-13f, 4.61343e-19f, 6.14982e+25f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x2d555b2d)); // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.21279e-11f +path.moveTo(SkBits2Float(0x2a395b2d), SkBits2Float(0xf0682955)); // 1.64629e-13f, -2.87402e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0xef2a8c55), SkBits2Float(0x295b2d2a), SkBits2Float(0x68210368), SkBits2Float(0x7bc05508)); // 5.76397e-19f, -5.27821e+28f, 4.86669e-14f, 3.04146e+24f, 1.99729e+36f +path.lineTo(SkBits2Float(0x2a395b2d), SkBits2Float(0xf0682955)); // 1.64629e-13f, -2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0x2a395b2d), SkBits2Float(0xf0682955)); // 1.64629e-13f, -2.87402e+29f +path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x2a21211f)); // 4.85282e+16f, 1.43112e-13f +path.lineTo(SkBits2Float(0x03552a8c), SkBits2Float(0x6a4f7b28)); // 6.26439e-37f, 6.27073e+25f +path.conicTo(SkBits2Float(0x2347ed93), SkBits2Float(0x282a3a21), SkBits2Float(0x3adf2128), SkBits2Float(0x4f1a3a8a), SkBits2Float(0x3ab38a28)); // 1.08381e-17f, 9.4495e-15f, 0.00170234f, 2.58753e+09f, 0.00136978f +path.lineTo(SkBits2Float(0x2a395b2d), SkBits2Float(0xf0682955)); // 1.64629e-13f, -2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0x2a395b2d), SkBits2Float(0xf0682955)); // 1.64629e-13f, -2.87402e+29f +path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x262a812a), SkBits2Float(0x3a2a5529)); // 2.25206e-21f, 300.343f, 5.91556e-16f, 0.000649768f +path.lineTo(SkBits2Float(0x213b1e2a), SkBits2Float(0x27292720)); // 6.3398e-19f, 2.34747e-15f +path.conicTo(SkBits2Float(0x371f203a), SkBits2Float(0xc52a22c4), SkBits2Float(0xc25d27ec), SkBits2Float(0x3a705921), SkBits2Float(0x5210513a)); // 9.48464e-06f, -2722.17f, -55.289f, 0.000916855f, 1.5496e+11f +path.cubicTo(SkBits2Float(0x63102ad9), SkBits2Float(0x29c80927), SkBits2Float(0x633a27b0), SkBits2Float(0x2909c827), SkBits2Float(0x272927b1), SkBits2Float(0x3a685b2d)); // 2.65942e+21f, 8.88337e-14f, 3.43395e+21f, 3.05937e-14f, 2.3475e-15f, 0.000886368f +path.moveTo(SkBits2Float(0x682d6829), SkBits2Float(0x29685555)); // 3.27556e+24f, 5.15884e-14f +path.conicTo(SkBits2Float(0xaa8c555b), SkBits2Float(0x081f2a21), SkBits2Float(0x5b2d0321), SkBits2Float(0x68556829), SkBits2Float(0x5b2d2729)); // -2.49282e-13f, 4.78968e-34f, 4.86986e+16f, 4.03114e+24f, 4.87382e+16f +path.quadTo(SkBits2Float(0x2d685568), SkBits2Float(0x5568295b), SkBits2Float(0x2a552d29), SkBits2Float(0x295b2d27)); // 1.32066e-11f, 1.5954e+13f, 1.89339e-13f, 4.86669e-14f +path.lineTo(SkBits2Float(0x682d6829), SkBits2Float(0x29685555)); // 3.27556e+24f, 5.15884e-14f +path.close(); + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void fuzz763_12(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x6a29082a)); // 0, 5.10868e+25f +path.conicTo(SkBits2Float(0x6a295ac3), SkBits2Float(0x61bb988e), SkBits2Float(0x6829682d), SkBits2Float(0x5f3ba76a), SkBits2Float(0x42730a87)); // 5.11843e+25f, 4.32567e+20f, 3.20001e+24f, 1.35219e+19f, 60.7603f +path.conicTo(SkBits2Float(0x67aedf99), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x3f801112)); // 1.65163e+24f, 0, 0, 0, 1.00052f +path.close(); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 6.14991e+25f, 0, 0, 4.03114e+24f, 1.50617e+13f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x68555b2a)); // 0, 4.03018e+24f +path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0x67d55b2a), SkBits2Float(0x67296a4b), SkBits2Float(0x67555b2a), SkBits2Float(0x677e1f70), SkBits2Float(0x66d55b2a)); // 0, 2.01509e+24f, 8.00041e+23f, 1.00755e+24f, 1.20006e+24f, 5.03773e+23f +path.cubicTo(SkBits2Float(0x678f0684), SkBits2Float(0x6684f008), SkBits2Float(0x6798f8ea), SkBits2Float(0x6625a942), SkBits2Float(0x67961914), SkBits2Float(0x65ce709a)); // 1.35084e+24f, 3.1389e+23f, 1.44478e+24f, 1.95578e+23f, 1.41764e+24f, 1.21861e+23f +path.cubicTo(SkBits2Float(0x679158b0), SkBits2Float(0x00000000), SkBits2Float(0x67531e34), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 1.37276e+24f, 0, 9.96976e+23f, 0, 0, 0 +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.conicTo(SkBits2Float(0x21081f21), SkBits2Float(0x4b7bc003), SkBits2Float(0xed237a6a), SkBits2Float(0x2d682967), SkBits2Float(0x2a8c555b)); // 4.61198e-19f, 1.64987e+07f, -3.16213e+27f, 1.31969e-11f, 2.49282e-13f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.close(); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0x3a6821df), SkBits2Float(0x2a8c3a8a)); // 0.000885514f, 2.49096e-13f +path.moveTo(SkBits2Float(0x29272a1d), SkBits2Float(0xb03a2a55)); // 3.7118e-14f, -6.77266e-10f +path.moveTo(SkBits2Float(0x20213b1e), SkBits2Float(0xc5272927)); // 1.36568e-19f, -2674.57f +path.quadTo(SkBits2Float(0xc422373b), SkBits2Float(0xec2a201f), SkBits2Float(0x21c25d27), SkBits2Float(0x523a7059)); // -648.863f, -8.22676e+26f, 1.31706e-18f, 2.00187e+11f +path.cubicTo(SkBits2Float(0x12102a10), SkBits2Float(0xe73a28d9), SkBits2Float(0xc8092763), SkBits2Float(0x2927b029), SkBits2Float(0x295b2d27), SkBits2Float(0x2d685568)); // 4.54902e-28f, -8.79114e+23f, -140446, 3.72342e-14f, 4.86669e-14f, 1.32066e-11f +path.moveTo(SkBits2Float(0x68556809), SkBits2Float(0x555b2d29)); // 4.03113e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x1f2a212a), SkBits2Float(0x2d032108)); // 3.60263e-20f, 7.45382e-12f +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x2a552d29)); // 4.03114e+24f, 1.89339e-13f +path.cubicTo(SkBits2Float(0x21295b2d), SkBits2Float(0x2a528c5b), SkBits2Float(0x284f5b2d), SkBits2Float(0x218aa621), SkBits2Float(0x3f2d2db3), SkBits2Float(0x68293a2a)); // 5.73801e-19f, 1.87004e-13f, 1.15106e-14f, 9.39522e-19f, 0.676479f, 3.19661e+24f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void fuzz763_13(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a)); // 5.7784e-19f, 4.61198e-19f +path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x4793ed7a), SkBits2Float(0x282a3a21), SkBits2Float(0x3adf2128), SkBits2Float(0x4f1a3a8a)); // 6.14991e+25f, 75739, 9.4495e-15f, 0.00170234f, 2.58753e+09f +path.lineTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a)); // 5.7784e-19f, 4.61198e-19f +path.close(); +path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a)); // 5.7784e-19f, 4.61198e-19f +path.cubicTo(SkBits2Float(0x3ac2213a), SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x272a8128), SkBits2Float(0x3a2a5529), SkBits2Float(0x3b1e2ab0)); // 0.00148109f, 2.25206e-21f, 300.343f, 2.36623e-15f, 0.000649768f, 0.00241343f +path.lineTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a)); // 5.7784e-19f, 4.61198e-19f +path.close(); +path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a)); // 5.7784e-19f, 4.61198e-19f +path.cubicTo(SkBits2Float(0x3b272927), SkBits2Float(0x381f203a), SkBits2Float(0x2ac422c5), SkBits2Float(0xc25d27ec), SkBits2Float(0x3a705921), SkBits2Float(0x2a105152)); // 0.00255067f, 3.79386e-05f, 3.48407e-13f, -55.289f, 0.000916855f, 1.2818e-13f +path.quadTo(SkBits2Float(0x633ad912), SkBits2Float(0x29c80927), SkBits2Float(0x272927b0), SkBits2Float(0x68295b2d)); // 3.44674e+21f, 8.88337e-14f, 2.3475e-15f, 3.19905e+24f +path.lineTo(SkBits2Float(0x295b2d68), SkBits2Float(0x29685568)); // 4.86672e-14f, 5.15884e-14f +path.conicTo(SkBits2Float(0xaa8c555b), SkBits2Float(0x081f2a21), SkBits2Float(0x5b2d0321), SkBits2Float(0x68556829), SkBits2Float(0x2a552d29)); // -2.49282e-13f, 4.78968e-34f, 4.86986e+16f, 4.03114e+24f, 1.89339e-13f +path.cubicTo(SkBits2Float(0x21295b2d), SkBits2Float(0x2a688c5b), SkBits2Float(0x6829292d), SkBits2Float(0x2d296855), SkBits2Float(0x8c08555b), SkBits2Float(0x2a2a291f)); // 5.73801e-19f, 2.06544e-13f, 3.19536e+24f, 9.6297e-12f, -1.05027e-31f, 1.51133e-13f +path.conicTo(SkBits2Float(0x68295b21), SkBits2Float(0x2d296855), SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a21), SkBits2Float(0x7bc00321)); // 3.19904e+24f, 9.6297e-12f, 2.49282e-13f, 4.78968e-34f, 1.99397e+36f +path.lineTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a)); // 5.7784e-19f, 4.61198e-19f +path.close(); +path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a)); // 5.7784e-19f, 4.61198e-19f +path.lineTo(SkBits2Float(0x5b2d6829), SkBits2Float(0x212a8c55)); // 4.88097e+16f, 5.7784e-19f +path.conicTo(SkBits2Float(0x8ced7aba), SkBits2Float(0x3f2a212a), SkBits2Float(0x2d212d08), SkBits2Float(0x5568295b), SkBits2Float(0x29685b2d)); // -3.65895e-31f, 0.664569f, 9.16179e-12f, 1.5954e+13f, 5.15934e-14f +path.lineTo(SkBits2Float(0x68295b68), SkBits2Float(0x2d296855)); // 3.19906e+24f, 9.6297e-12f +path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a)); // 5.7784e-19f, 4.61198e-19f +path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21), SkBits2Float(0x6aba7b03), SkBits2Float(0x2147ed7a)); // 6.14991e+25f, 2.50338e-13f, 4.61198e-19f, 1.12721e+26f, 6.77381e-19f +path.quadTo(SkBits2Float(0x6028282a), SkBits2Float(0x68292ddf), SkBits2Float(0x5b2d555b), SkBits2Float(0x68556829)); // 4.84679e+19f, 3.1957e+24f, 4.8789e+16f, 4.03114e+24f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename); +} + +static void fuzz763_14(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0xf45b2d29)); // 4.03114e+24f, -6.94598e+31f +path.moveTo(SkBits2Float(0x1f2a302a), SkBits2Float(0xc8032108)); // 3.60387e-20f, -134276 +path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf0db684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x302a5b25), SkBits2Float(0xf0685568)); // 3.4979e+24f, -5.43226e+29f, -1.64207e-31f, 5.76527e-19f, 6.19752e-10f, -2.87615e+29f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void fuzz763_15(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x5b292d55), SkBits2Float(0x212a8c55)); // 4.76191e+16f, 5.7784e-19f +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a6b7bc4)); // 4.7323e-37f, 7.11705e+25f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x2c6829c0), SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a29)); // 5.77848e-19f, 4.7323e-37f, 3.29924e-12f, 2.49282e-13f, 4.78969e-34f +path.lineTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a6b7bc4)); // 4.7323e-37f, 7.11705e+25f +path.close(); +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a6b7bc4)); // 4.7323e-37f, 7.11705e+25f +path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x4793ed7a), SkBits2Float(0x282a3a21), SkBits2Float(0xdf218a28), SkBits2Float(0x4f1a3a3a)); // 6.14991e+25f, 75739, 9.4495e-15f, -1.16402e+19f, 2.58751e+09f +path.quadTo(SkBits2Float(0x3ab38a28), SkBits2Float(0x283ac221), SkBits2Float(0xe6432a29), SkBits2Float(0x2a96812b)); // 0.00136978f, 1.03672e-14f, -2.3041e+23f, 2.6735e-13f +path.lineTo(SkBits2Float(0x5529272a), SkBits2Float(0x1eb03a2a)); // 1.16241e+13f, 1.86588e-20f +path.conicTo(SkBits2Float(0x2a272021), SkBits2Float(0x3ac52729), SkBits2Float(0xc422313b), SkBits2Float(0xec2a201f), SkBits2Float(0x21c25d27)); // 1.48437e-13f, 0.00150416f, -648.769f, -8.22676e+26f, 1.31706e-18f +path.lineTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a6b7bc4)); // 4.7323e-37f, 7.11705e+25f +path.close(); +path.moveTo(SkBits2Float(0x1051523a), SkBits2Float(0xd912102a)); // 4.12813e-29f, -2.56957e+15f +path.close(); +path.moveTo(SkBits2Float(0x1051523a), SkBits2Float(0xd912102a)); // 4.12813e-29f, -2.56957e+15f +path.quadTo(SkBits2Float(0xc82763e7), SkBits2Float(0x2927b029), SkBits2Float(0x295b2d27), SkBits2Float(0x2d685568)); // -171408, 3.72342e-14f, 4.86669e-14f, 1.32066e-11f +path.moveTo(SkBits2Float(0x68556809), SkBits2Float(0x8c555b2d)); // 4.03113e+24f, -1.64364e-31f +path.moveTo(SkBits2Float(0x081f2a21), SkBits2Float(0x252d0321)); // 4.78968e-34f, 1.50064e-16f +path.moveTo(SkBits2Float(0x5568392a), SkBits2Float(0x5b2df068)); // 1.59583e+13f, 4.89595e+16f +path.quadTo(SkBits2Float(0x2a1f2a8c), SkBits2Float(0x21482a21), SkBits2Float(0x4b7bc003), SkBits2Float(0x8ced3a6a)); // 1.41368e-13f, 6.78184e-19f, 1.64987e+07f, -3.65508e-31f +path.moveTo(SkBits2Float(0x21481f21), SkBits2Float(0x4b7bc003)); // 6.78038e-19f, 1.64987e+07f +path.conicTo(SkBits2Float(0x6829ed27), SkBits2Float(0x2d155b2d), SkBits2Float(0x5568295b), SkBits2Float(0x5b2d2968), SkBits2Float(0x2a8c8f55)); // 3.20982e+24f, 8.48991e-12f, 1.5954e+13f, 4.87407e+16f, 2.49685e-13f +path.lineTo(SkBits2Float(0x21481f21), SkBits2Float(0x4b7bc003)); // 6.78038e-19f, 1.64987e+07f +path.close(); +path.moveTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03)); // -2.51765f, 6.14982e+25f +path.conicTo(SkBits2Float(0x682d2fed), SkBits2Float(0x755b6829), SkBits2Float(0x5b292d2b), SkBits2Float(0xc92a8c55), SkBits2Float(0x081f2a21)); // 3.27141e+24f, 2.78131e+32f, 4.76189e+16f, -698565, 4.78968e-34f +path.lineTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03)); // -2.51765f, 6.14982e+25f +path.close(); +path.moveTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03)); // -2.51765f, 6.14982e+25f +path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x212aed7a), SkBits2Float(0x0321081f), SkBits2Float(0x293a7bc0), SkBits2Float(0x2147ed7a)); // 6.14991e+25f, 5.79125e-19f, 4.7323e-37f, 4.14076e-14f, 6.77381e-19f +path.quadTo(SkBits2Float(0x6829682d), SkBits2Float(0x292d555b), SkBits2Float(0x292a8c55), SkBits2Float(0x21081f2a)); // 3.20001e+24f, 3.84878e-14f, 3.78693e-14f, 4.61198e-19f +path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x218ced7a), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0), SkBits2Float(0x47ed3a7a)); // 6.14991e+25f, 9.54963e-19f, 4.7323e-37f, 5.63611e+25f, 121461 +path.lineTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03)); // -2.51765f, 6.14982e+25f +path.close(); +path.moveTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03)); // -2.51765f, 6.14982e+25f +path.quadTo(SkBits2Float(0x282a282a), SkBits2Float(0x8a3a21df), SkBits2Float(0x2728282a), SkBits2Float(0x8a3a21df)); // 9.4456e-15f, -8.96194e-33f, 2.33365e-15f, -8.96194e-33f +path.quadTo(SkBits2Float(0x8a284f9a), SkBits2Float(0x3a3ac2b3), SkBits2Float(0x2a292827), SkBits2Float(0x962be61d)); // -8.10388e-33f, 0.000712435f, 1.50241e-13f, -1.38859e-25f +path.lineTo(SkBits2Float(0x272a802a), SkBits2Float(0x2a8c2d29)); // 2.36617e-15f, 2.49003e-13f +path.lineTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03)); // -2.51765f, 6.14982e+25f +path.close(); +path.moveTo(SkBits2Float(0x4f9a3a29), SkBits2Float(0x3ab38a28)); // 5.17501e+09f, 0.00136978f +path.quadTo(SkBits2Float(0xc368305b), SkBits2Float(0x5b296855), SkBits2Float(0x2d8c5568), SkBits2Float(0x1f2a2172)); // -232.189f, 4.7684e+16f, 1.59541e-11f, 3.60266e-20f +path.lineTo(SkBits2Float(0x29c00321), SkBits2Float(0x5b4b7b13)); // 8.52706e-14f, 5.72747e+16f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename); +} + +static void fuzz763_16(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 4.03114e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108)); // 3.60396e-20f, -2.04889f +path.cubicTo(SkBits2Float(0x68372d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c552775), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0)); // 3.46012e+24f, -2.71613e+29f, -1.64208e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x69555b2d)); // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.61207e+25f +path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0682955)); // 3.35016e+24f, -2.87402e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x8cef552a), SkBits2Float(0x295b2d2a), SkBits2Float(0x68210368), SkBits2Float(0x7bc05508)); // 5.76397e-19f, -3.6875e-31f, 4.86669e-14f, 3.04146e+24f, 1.99729e+36f +path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0682955)); // 3.35016e+24f, -2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0682955)); // 3.35016e+24f, -2.87402e+29f +path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55)); // 4.85282e+16f, 5.7784e-19f +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x68385b2d), SkBits2Float(0x555bf055), SkBits2Float(0x2a1f2a8c), SkBits2Float(0x03212a21), SkBits2Float(0x5a4b7bc0)); // 3.48239e+24f, 1.51141e+13f, 1.41368e-13f, 4.7362e-37f, 1.43189e+16f +path.conicTo(SkBits2Float(0xc08c2aed), SkBits2Float(0x211f2108), SkBits2Float(0x6a4b7b03), SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d)); // -4.38024f, 5.3915e-19f, 6.14982e+25f, 3.20982e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527)); // 3.35016e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x6e2a1f72), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x4793ed7a)); // 2.49282e-13f, 1.31626e+28f, 4.7323e-37f, 6.14991e+25f, 75739 +path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527)); // 3.35016e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527)); // 3.35016e+24f, -2.87614e+29f +path.quadTo(SkBits2Float(0x2128282a), SkBits2Float(0x3a8a3adf), SkBits2Float(0x8a284f1a), SkBits2Float(0x2c213ab3)); // 5.69738e-19f, 0.00105461f, -8.10378e-33f, 2.29121e-12f +path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527)); // 3.35016e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527)); // 3.35016e+24f, -2.87614e+29f +path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x3a2a812a), SkBits2Float(0x2a8ced29)); // 2.25206e-21f, 300.343f, 0.000650423f, 2.50336e-13f +path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527)); // 3.35016e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527)); // 3.35016e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x03210831), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x681aed27), SkBits2Float(0x55555b2d), SkBits2Float(0x1e2a3a2a)); // 4.73231e-37f, 6.14991e+25f, 2.92648e+24f, 1.46617e+13f, 9.01175e-21f +path.conicTo(SkBits2Float(0x27202140), SkBits2Float(0x3a3b2769), SkBits2Float(0xc4371f20), SkBits2Float(0xecc52a22), SkBits2Float(0x21512727)); // 2.22225e-15f, 0.000713936f, -732.486f, -1.90686e+27f, 7.08638e-19f +path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527)); // 3.35016e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x6829523a), SkBits2Float(0x2d555b2d)); // 3.19839e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 4.03114e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x1f2a322a), SkBits2Float(0xc0032108)); // 3.60404e-20f, -2.04889f +path.cubicTo(SkBits2Float(0x68572d55), SkBits2Float(0xf05bd24b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0xed4b7bc0)); // 4.06458e+24f, -2.72126e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, -3.93594e+27f +path.conicTo(SkBits2Float(0x212a8c6a), SkBits2Float(0x0329081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x2d555b2d)); // 5.77841e-19f, 4.9674e-37f, 6.14991e+25f, 9.43289e-15f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0682955)); // 3.33127e+24f, -2.87402e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x8cef552a), SkBits2Float(0x295b2d2a), SkBits2Float(0x68210368), SkBits2Float(0x7bc05508)); // 5.76397e-19f, -3.6875e-31f, 4.86669e-14f, 3.04146e+24f, 1.99729e+36f +path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0682955)); // 3.33127e+24f, -2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0682955)); // 3.33127e+24f, -2.87402e+29f +path.lineTo(SkBits2Float(0x555b6829), SkBits2Float(0x6c212a8c)); // 1.50775e+13f, 7.79352e+26f +path.conicTo(SkBits2Float(0x084b0321), SkBits2Float(0x6ac07b2a), SkBits2Float(0x395b2d7a), SkBits2Float(0x5bf05568), SkBits2Float(0x212a3a8c)); // 6.10918e-34f, 1.16348e+26f, 0.000209024f, 1.35296e+17f, 5.76757e-19f +path.lineTo(SkBits2Float(0x8c558c55), SkBits2Float(0x212a1f2a)); // -1.64512e-31f, 5.76395e-19f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void fuzz763_17(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 4.03114e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108)); // 3.60396e-20f, -2.04889f +path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0)); // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x69555b2d)); // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.61207e+25f +path.moveTo(SkBits2Float(0x6835282d), SkBits2Float(0xf0682955)); // 3.42196e+24f, -2.87402e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x2aef552a), SkBits2Float(0x68295b2d), SkBits2Float(0x08682103), SkBits2Float(0x4b7bc055)); // 5.76397e-19f, 4.2514e-13f, 3.19905e+24f, 6.98538e-34f, 1.64988e+07f +path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55)); // 4.85282e+16f, 5.7784e-19f +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x68395b2d), SkBits2Float(0x555bf055), SkBits2Float(0x2a1f2a8c), SkBits2Float(0x03212a21), SkBits2Float(0x5a4b7bc0)); // 3.50128e+24f, 1.51141e+13f, 1.41368e-13f, 4.7362e-37f, 1.43189e+16f +path.conicTo(SkBits2Float(0xc08c2aed), SkBits2Float(0x211f2108), SkBits2Float(0x6a4b7b03), SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d)); // -4.38024f, 5.3915e-19f, 6.14982e+25f, 3.20982e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527)); // 3.33127e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x212a1f72), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x254793ed)); // 2.49282e-13f, 5.76399e-19f, 4.7323e-37f, 6.14991e+25f, 1.73106e-16f +path.quadTo(SkBits2Float(0x2128282a), SkBits2Float(0x3a8a3adf), SkBits2Float(0x8a284f1a), SkBits2Float(0xc2213ab3)); // 5.69738e-19f, 0.00105461f, -8.10378e-33f, -40.3073f +path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x3a2a812a), SkBits2Float(0x2a8ced29)); // 2.25206e-21f, 300.343f, 0.000650423f, 2.50336e-13f +path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527)); // 3.33127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527)); // 3.33127e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x03210831), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x6829ed27), SkBits2Float(0x55555b2d), SkBits2Float(0x1e2a3a2a)); // 4.73231e-37f, 6.14991e+25f, 3.20982e+24f, 1.46617e+13f, 9.01175e-21f +path.conicTo(SkBits2Float(0x27202140), SkBits2Float(0x3a3b2729), SkBits2Float(0xc4371f20), SkBits2Float(0x16c52a22), SkBits2Float(0x515d27ec)); // 2.22225e-15f, 0.000713932f, -732.486f, 3.18537e-25f, 5.93661e+10f +path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527)); // 3.33127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x6829523a), SkBits2Float(0x2d555b2d)); // 3.19839e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 4.03114e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108)); // 3.60396e-20f, -2.04889f +path.cubicTo(SkBits2Float(0x68572d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0)); // 4.06458e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x2d555b2d)); // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.lineTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x2a212a1f)); // 2.49282e-13f, 1.43143e-13f +path.lineTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.lineTo(SkBits2Float(0x8c2aed7a), SkBits2Float(0x2a1f08c0)); // -1.31678e-31f, 1.41251e-13f +path.lineTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0x2a8cef55), SkBits2Float(0x68295b2d)); // 2.50351e-13f, 3.19905e+24f +path.conicTo(SkBits2Float(0x55086821), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x5b2c6829), SkBits2Float(0x21218c55), SkBits2Float(0x2a6c1f03)); // 9.3738e+12f, 6.14991e+25f, 4.85282e+16f, 5.47346e-19f, 2.09718e-13f +path.lineTo(SkBits2Float(0x2a8cef55), SkBits2Float(0x68295b2d)); // 2.50351e-13f, 3.19905e+24f +path.close(); +path.moveTo(SkBits2Float(0x2a8cef55), SkBits2Float(0x68295b2d)); // 2.50351e-13f, 3.19905e+24f +path.lineTo(SkBits2Float(0x6ac07b2a), SkBits2Float(0x395b2d7a)); // 1.16348e+26f, 0.000209024f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void fuzz763_18(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 4.03114e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108)); // 3.60396e-20f, -2.04889f +path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0)); // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x69555b2d)); // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.61207e+25f +path.moveTo(SkBits2Float(0x6835282d), SkBits2Float(0xf0682955)); // 3.42196e+24f, -2.87402e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x2aef552a), SkBits2Float(0x68295b2d), SkBits2Float(0x08682103), SkBits2Float(0x4b7bc055)); // 5.76397e-19f, 4.2514e-13f, 3.19905e+24f, 6.98538e-34f, 1.64988e+07f +path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55)); // 4.85282e+16f, 5.7784e-19f +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x68395b2d), SkBits2Float(0x555bf055), SkBits2Float(0x2a1f2a8c), SkBits2Float(0x03212a21), SkBits2Float(0x5a4b7bc0)); // 3.50128e+24f, 1.51141e+13f, 1.41368e-13f, 4.7362e-37f, 1.43189e+16f +path.conicTo(SkBits2Float(0xc08c2aed), SkBits2Float(0x211f2108), SkBits2Float(0x6a4b7b03), SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d)); // -4.38024f, 5.3915e-19f, 6.14982e+25f, 3.20982e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527)); // 3.33127e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x212a1f72), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x254793ed)); // 2.49282e-13f, 5.76399e-19f, 4.7323e-37f, 6.14991e+25f, 1.73106e-16f +path.quadTo(SkBits2Float(0x2128282a), SkBits2Float(0x3a8a3adf), SkBits2Float(0x8a284f1a), SkBits2Float(0xc2213ab3)); // 5.69738e-19f, 0.00105461f, -8.10378e-33f, -40.3073f +path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x3a2a812a), SkBits2Float(0x2a8ced29)); // 2.25206e-21f, 300.343f, 0.000650423f, 2.50336e-13f +path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527)); // 3.33127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527)); // 3.33127e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x03210831), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x6829ed27), SkBits2Float(0x55555b2d), SkBits2Float(0x1e2a3a2a)); // 4.73231e-37f, 6.14991e+25f, 3.20982e+24f, 1.46617e+13f, 9.01175e-21f +path.conicTo(SkBits2Float(0x27202140), SkBits2Float(0x3a3b2729), SkBits2Float(0xc4371f20), SkBits2Float(0x16c52a22), SkBits2Float(0x515d27ec)); // 2.22225e-15f, 0.000713932f, -732.486f, 3.18537e-25f, 5.93661e+10f +path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527)); // 3.33127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x6829523a), SkBits2Float(0x2d555b2d)); // 3.19839e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 4.03114e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108)); // 3.60396e-20f, -2.04889f +path.cubicTo(SkBits2Float(0x68572d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0)); // 4.06458e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x2d555b2d)); // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.lineTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x2a212a1f)); // 2.49282e-13f, 1.43143e-13f +path.lineTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.lineTo(SkBits2Float(0x8c2aed7a), SkBits2Float(0x2a1f08c0)); // -1.31678e-31f, 1.41251e-13f + +path.moveTo(SkBits2Float(0x6829523a), SkBits2Float(0x2d555b2d)); // 3.19839e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 4.03114e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108)); // 3.60396e-20f, -2.04889f +path.cubicTo(SkBits2Float(0x68572d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0)); // 4.06458e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x2a8c54ed), SkBits2Float(0x21081f21), SkBits2Float(0x4b7bc003), SkBits2Float(0x29ed846a), SkBits2Float(0x555b2d28)); // 2.49279e-13f, 4.61198e-19f, 1.64987e+07f, 1.05479e-13f, 1.50617e+13f +path.conicTo(SkBits2Float(0x68392d5b), SkBits2Float(0xf0682955), SkBits2Float(0x2a1f5b2d), SkBits2Float(0xef552a21), SkBits2Float(0x5b2d2a8c)); // 3.4979e+24f, -2.87402e+29f, 1.41537e-13f, -6.59712e+28f, 4.8742e+16f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void fuzz763_19(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x21081f21), SkBits2Float(0x4b7bc003)); // 4.61198e-19f, 1.64987e+07f +path.lineTo(SkBits2Float(0x2829ed84), SkBits2Float(0x69555b2d)); // 9.43289e-15f, 1.61207e+25f +path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0682955)); // 3.33127e+24f, -2.87402e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x2aef552a), SkBits2Float(0x68295b2d), SkBits2Float(0x08682103), SkBits2Float(0x4b7bc055)); // 5.76397e-19f, 4.2514e-13f, 3.19905e+24f, 6.98538e-34f, 1.64988e+07f +path.moveTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55)); // 4.85282e+16f, 5.7784e-19f +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x68395b2d), SkBits2Float(0x8c5bf055), SkBits2Float(0x2a1f2a55), SkBits2Float(0x03212a21), SkBits2Float(0x5a4b7bc0)); // 3.50128e+24f, -1.69435e-31f, 1.41367e-13f, 4.7362e-37f, 1.43189e+16f +path.conicTo(SkBits2Float(0xc08c2aed), SkBits2Float(0x211f2108), SkBits2Float(0x6a4b7b03), SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d)); // -4.38024f, 5.3915e-19f, 6.14982e+25f, 3.20982e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527)); // 3.35016e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x212a1f72), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2547937a)); // 2.49282e-13f, 5.76399e-19f, 4.7323e-37f, 6.14991e+25f, 1.73105e-16f +path.quadTo(SkBits2Float(0x2128282a), SkBits2Float(0x3a8a3adf), SkBits2Float(0x8a284f1a), SkBits2Float(0xc2213ab3)); // 5.69738e-19f, 0.00105461f, -8.10378e-33f, -40.3073f +path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x3a2a812a), SkBits2Float(0x2a8ced29)); // 2.25206e-21f, 300.343f, 0.000650423f, 2.50336e-13f +path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527)); // 3.35016e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527)); // 3.35016e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x03210831), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x6829ed27), SkBits2Float(0x55555b2d), SkBits2Float(0x1e2a3a2a)); // 4.73231e-37f, 6.14991e+25f, 3.20982e+24f, 1.46617e+13f, 9.01175e-21f +path.conicTo(SkBits2Float(0x27202140), SkBits2Float(0x3a3b2729), SkBits2Float(0xc4371f20), SkBits2Float(0xecc52a22), SkBits2Float(0x21515d27)); // 2.22225e-15f, 0.000713932f, -732.486f, -1.90686e+27f, 7.09352e-19f +path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527)); // 3.35016e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x6829523a), SkBits2Float(0x2d555b2d)); // 3.19839e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 4.03114e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108)); // 3.60396e-20f, -2.04889f +path.cubicTo(SkBits2Float(0x68572d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0)); // 4.06458e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x2d555b2d)); // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x8cef552a), SkBits2Float(0x295b2d2a), SkBits2Float(0x68210368), SkBits2Float(0x7bc05508)); // 5.76397e-19f, -3.6875e-31f, 4.86669e-14f, 3.04146e+24f, 1.99729e+36f +path.lineTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.lineTo(SkBits2Float(0x555b2c29), SkBits2Float(0x6c212a8c)); // 1.50614e+13f, 7.79352e+26f +path.conicTo(SkBits2Float(0x084b0321), SkBits2Float(0x6ac07b2a), SkBits2Float(0x395b2d7a), SkBits2Float(0xf05b5568), SkBits2Float(0x212a3a8c)); // 6.10918e-34f, 1.16348e+26f, 0.000209024f, -2.71522e+29f, 5.76757e-19f +path.conicTo(SkBits2Float(0x290321d9), SkBits2Float(0x555b2d68), SkBits2Float(0x2a8c558c), SkBits2Float(0x2abe2a1f), SkBits2Float(0x7bc00321)); // 2.91172e-14f, 1.50618e+13f, 2.49284e-13f, 3.378e-13f, 1.99397e+36f +path.lineTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.lineTo(SkBits2Float(0x8c2aed7a), SkBits2Float(0x1f2128c0)); // -1.31678e-31f, 3.41268e-20f +path.lineTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.close(); + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void fuzz763_20(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 4.03114e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108)); // 3.60396e-20f, -2.04889f +path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0)); // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x69555b2d)); // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.61207e+25f +path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0682955)); // 3.33127e+24f, -2.87402e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x2a8c552a), SkBits2Float(0x68295b2d), SkBits2Float(0x08682103), SkBits2Float(0x4b7bc055)); // 5.76397e-19f, 2.49281e-13f, 3.19905e+24f, 6.98538e-34f, 1.64988e+07f +path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55)); // 4.85282e+16f, 5.7784e-19f +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x68395b2d), SkBits2Float(0x555bf055), SkBits2Float(0x2a1f2a8c), SkBits2Float(0x03212a21), SkBits2Float(0x5a4b7bc0)); // 3.50128e+24f, 1.51141e+13f, 1.41368e-13f, 4.7362e-37f, 1.43189e+16f +path.conicTo(SkBits2Float(0xc08c2aed), SkBits2Float(0x211f2108), SkBits2Float(0x6a4b7b03), SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d)); // -4.38024f, 5.3915e-19f, 6.14982e+25f, 3.20982e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527)); // 3.33127e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x6e2a1f72), SkBits2Float(0x0321182a), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x4793ed7a)); // 2.49282e-13f, 1.31626e+28f, 4.73414e-37f, 6.14991e+25f, 75739 +path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527)); // 3.33127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527)); // 3.33127e+24f, -2.87614e+29f +path.quadTo(SkBits2Float(0x2128282a), SkBits2Float(0x3a8a3adf), SkBits2Float(0x8a284f1a), SkBits2Float(0x2c213ab3)); // 5.69738e-19f, 0.00105461f, -8.10378e-33f, 2.29121e-12f +path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527)); // 3.33127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527)); // 3.33127e+24f, -2.87614e+29f +path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x3a2a812a), SkBits2Float(0x2a8ced29)); // 2.25206e-21f, 300.343f, 0.000650423f, 2.50336e-13f +path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527)); // 3.33127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527)); // 3.33127e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x03210831), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x6829ed27), SkBits2Float(0x55555b2d), SkBits2Float(0x1e2a3a2a)); // 4.73231e-37f, 6.14991e+25f, 3.20982e+24f, 1.46617e+13f, 9.01175e-21f +path.conicTo(SkBits2Float(0x27202140), SkBits2Float(0x3a3b2769), SkBits2Float(0xc4371f20), SkBits2Float(0xecc52a22), SkBits2Float(0x51282727)); // 2.22225e-15f, 0.000713936f, -732.486f, -1.90686e+27f, 4.51382e+10f +path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527)); // 3.33127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x6829523a), SkBits2Float(0x2d555b2d)); // 3.19839e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x8c555b2d)); // 4.03114e+24f, -1.64364e-31f +path.moveTo(SkBits2Float(0x081f2a31), SkBits2Float(0xc0032921)); // 4.78969e-34f, -2.04939f +path.cubicTo(SkBits2Float(0x68572d55), SkBits2Float(0xf05bd24b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0xed4b7bc0)); // 4.06458e+24f, -2.72126e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, -3.93594e+27f +path.conicTo(SkBits2Float(0x212a8c6a), SkBits2Float(0x4329081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x5b2d2d55)); // 5.77841e-19f, 169.032f, 6.14991e+25f, 9.43289e-15f, 4.8745e+16f +path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x8cef552a), SkBits2Float(0x295b2d2a), SkBits2Float(0x3a210368), SkBits2Float(0x7bc05508)); // 5.76397e-19f, -3.6875e-31f, 4.86669e-14f, 0.000614217f, 1.99729e+36f +path.lineTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.lineTo(SkBits2Float(0x555b6829), SkBits2Float(0x6c212a8c)); // 1.50775e+13f, 7.79352e+26f +path.lineTo(SkBits2Float(0x5b2d7a6a), SkBits2Float(0xf0556830)); // 4.88298e+16f, -2.64185e+29f +path.lineTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.conicTo(SkBits2Float(0x0321d90a), SkBits2Float(0x555b2d68), SkBits2Float(0x2a8c558c), SkBits2Float(0x212a2a1f), SkBits2Float(0x4b7bc003)); // 4.75628e-37f, 1.50618e+13f, 2.49284e-13f, 5.7654e-19f, 1.64987e+07f +path.lineTo(SkBits2Float(0x8c2aed7a), SkBits2Float(0x212128c0)); // -1.31678e-31f, 5.46029e-19f +path.lineTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955)); // 3.50128e+24f, -2.87402e+29f +path.close(); + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void fuzz763_21(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x6828c6f9), SkBits2Float(0x6614dc9e)); // 3.18811e+24f, 1.75745e+23f +path.cubicTo(SkBits2Float(0x68303469), SkBits2Float(0x661f92fc), SkBits2Float(0x6837d3c3), SkBits2Float(0x662b0eb2), SkBits2Float(0x683fa268), SkBits2Float(0x663759e1)); // 3.32841e+24f, 1.88392e+23f, 3.4724e+24f, 2.01949e+23f, 3.61987e+24f, 2.16463e+23f +path.cubicTo(SkBits2Float(0x68c4391f), SkBits2Float(0x672c5c9f), SkBits2Float(0x688b20ab), SkBits2Float(0x6804b825), SkBits2Float(0x681ddb5e), SkBits2Float(0x6838dc00)); // 7.4131e+24f, 8.13956e+23f, 5.25609e+24f, 2.507e+24f, 2.98183e+24f, 3.49189e+24f +path.lineTo(SkBits2Float(0x6828c6f9), SkBits2Float(0x6614dc9e)); // 3.18811e+24f, 1.75745e+23f +path.close(); +path.moveTo(SkBits2Float(0x68226c73), SkBits2Float(0x660bd15e)); // 3.0681e+24f, 1.65068e+23f +path.cubicTo(SkBits2Float(0x6823b0e1), SkBits2Float(0x660d990f), SkBits2Float(0x6824f6d5), SkBits2Float(0x660f668c), SkBits2Float(0x68263e4e), SkBits2Float(0x66113632)); // 3.09203e+24f, 1.67169e+23f, 3.11609e+24f, 1.69298e+23f, 3.14025e+24f, 1.71436e+23f +path.cubicTo(SkBits2Float(0x682715e4), SkBits2Float(0x6612676d), SkBits2Float(0x6827ee22), SkBits2Float(0x66139997), SkBits2Float(0x6828c709), SkBits2Float(0x6614cba5)); // 3.15616e+24f, 1.72843e+23f, 3.17211e+24f, 1.74255e+23f, 3.18812e+24f, 1.75667e+23f +path.lineTo(SkBits2Float(0x6828d720), SkBits2Float(0x6604a1a2)); // 3.1893e+24f, 1.56583e+23f +path.cubicTo(SkBits2Float(0x68270421), SkBits2Float(0x6601102c), SkBits2Float(0x68252b97), SkBits2Float(0x65fb1edd), SkBits2Float(0x68234ce5), SkBits2Float(0x65f4367f)); // 3.15485e+24f, 1.52371e+23f, 3.11998e+24f, 1.48235e+23f, 3.08466e+24f, 1.44158e+23f +path.conicTo(SkBits2Float(0x6822e012), SkBits2Float(0x6602acc5), SkBits2Float(0x68226c73), SkBits2Float(0x660bd15e), SkBits2Float(0x3f7ffa04)); // 3.07663e+24f, 1.54274e+23f, 3.0681e+24f, 1.65068e+23f, 0.999909f +path.close(); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x6a2a291f)); // 0, 5.14279e+25f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x68555b2a)); // 0, 4.03018e+24f +path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0x68617414), SkBits2Float(0x66af1c42), SkBits2Float(0x68624f96), SkBits2Float(0x6757755b), SkBits2Float(0x685b93f2)); // 0, 4.25869e+24f, 4.13468e+23f, 4.27489e+24f, 1.01747e+24f, 4.14771e+24f +path.cubicTo(SkBits2Float(0x67a63a84), SkBits2Float(0x68fe1c37), SkBits2Float(0x67c05eed), SkBits2Float(0x69930962), SkBits2Float(0x00000000), SkBits2Float(0x6a2a291f)); // 1.56998e+24f, 9.60001e+24f, 1.81689e+24f, 2.22196e+25f, 0, 5.14279e+25f +path.close(); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x6a2a291f)); // 0, 5.14279e+25f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x6a4b7bc4)); // 0, 6.14991e+25f +path.cubicTo(SkBits2Float(0x6a2c8798), SkBits2Float(0x68f7a144), SkBits2Float(0x6951f5ea), SkBits2Float(0x6796ad55), SkBits2Float(0x683fa268), SkBits2Float(0x663759e1)); // 5.21439e+25f, 9.35519e+24f, 1.58642e+25f, 1.4231e+24f, 3.61987e+24f, 2.16463e+23f +path.cubicTo(SkBits2Float(0x683871e3), SkBits2Float(0x66253b4f), SkBits2Float(0x6830da01), SkBits2Float(0x66144d3e), SkBits2Float(0x6828d720), SkBits2Float(0x6604a1a2)); // 3.48407e+24f, 1.95071e+23f, 3.34063e+24f, 1.75084e+23f, 3.1893e+24f, 1.56583e+23f +path.conicTo(SkBits2Float(0x68295b21), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x492bb324)); // 3.19904e+24f, 0, 0, 0, 703282 +path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x677b84f0), SkBits2Float(0x00000000), SkBits2Float(0x68226c73), SkBits2Float(0x660bd15e)); // 0, 0, 1.18777e+24f, 0, 3.0681e+24f, 1.65068e+23f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x68156829)); // 0, 2.82222e+24f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x68555b2a)); // 0, 4.03018e+24f +path.lineTo(SkBits2Float(0x673918f3), SkBits2Float(0x681b0f5f)); // 8.74098e+23f, 2.929e+24f +path.lineTo(SkBits2Float(0x67391759), SkBits2Float(0x681b0fae)); // 8.74068e+23f, 2.92902e+24f +path.cubicTo(SkBits2Float(0x674384e7), SkBits2Float(0x682e2068), SkBits2Float(0x674db698), SkBits2Float(0x6843893b), SkBits2Float(0x6757755b), SkBits2Float(0x685b93f2)); // 9.23313e+23f, 3.28916e+24f, 9.71453e+23f, 3.69357e+24f, 1.01747e+24f, 4.14771e+24f +path.cubicTo(SkBits2Float(0x67a63484), SkBits2Float(0x68556bdd), SkBits2Float(0x67f18c5f), SkBits2Float(0x6848eb25), SkBits2Float(0x681ddb5e), SkBits2Float(0x6838dc00)); // 1.56976e+24f, 4.03142e+24f, 2.28136e+24f, 3.79524e+24f, 2.98183e+24f, 3.49189e+24f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x6a2a291f)); // 0, 5.14279e+25f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 1, filename); +} + +static void fuzz763_22(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x68295b2d)); // 0, 3.19905e+24f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0x6a3a7bc0), SkBits2Float(0x00000000)); // 5.63611e+25f, 0 +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x6a034b21)); // 0, 3.9681e+25f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x68295b2d)); // 0, 3.19905e+24f +path.close(); +path.moveTo(SkBits2Float(0x6617da56), SkBits2Float(0x00000000)); // 1.79276e+23f, 0 +path.conicTo(SkBits2Float(0x5e704d09), SkBits2Float(0x5e3a4dfd), SkBits2Float(0x00000000), SkBits2Float(0x65eb62ef), SkBits2Float(0x430fa5e6)); // 4.32888e+18f, 3.35617e+18f, 0, 1.38948e+23f, 143.648f +path.conicTo(SkBits2Float(0x5e798b32), SkBits2Float(0x627a95c0), SkBits2Float(0x61f5014c), SkBits2Float(0x61fba0fd), SkBits2Float(0x40f8a1a1)); // 4.49538e+18f, 1.15562e+21f, 5.64943e+20f, 5.80217e+20f, 7.76973f +path.conicTo(SkBits2Float(0x62743d2d), SkBits2Float(0x5e49b862), SkBits2Float(0x6617da56), SkBits2Float(0x00000000), SkBits2Float(0x410ef54c)); // 1.12635e+21f, 3.63387e+18f, 1.79276e+23f, 0, 8.93489f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.quadTo(SkBits2Float(0x4f9a3a8a), SkBits2Float(0xc28a0d28), SkBits2Float(0x273a3ab3), SkBits2Float(0x8b2a2928)); // 5.17506e+09f, -69.0257f, 2.58445e-15f, -3.27718e-32f +path.lineTo(SkBits2Float(0x63283ae6), SkBits2Float(0x27282a81)); // 3.1033e+21f, 2.33377e-15f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 3, filename); +} + +static void fuzz763_23(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 4.03114e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108)); // 3.60396e-20f, -2.04889f +path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x03210c2a), SkBits2Float(0x6a4b7bc0)); // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.73276e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x69555b2d)); // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.61207e+25f +path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0682955)); // 3.33127e+24f, -2.87402e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x2aef552a), SkBits2Float(0x29295b2d), SkBits2Float(0x68210368), SkBits2Float(0x7bc05508)); // 5.76397e-19f, 4.2514e-13f, 3.76046e-14f, 3.04146e+24f, 1.99729e+36f +path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0682955)); // 3.33127e+24f, -2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55)); // 4.85282e+16f, 5.7784e-19f +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x68395b2d), SkBits2Float(0x8c5bf055), SkBits2Float(0x2a1f2a55), SkBits2Float(0x03212a21), SkBits2Float(0x5a4b7bc0)); // 3.50128e+24f, -1.69435e-31f, 1.41367e-13f, 4.7362e-37f, 1.43189e+16f +path.conicTo(SkBits2Float(0xc08c2aed), SkBits2Float(0x211f2108), SkBits2Float(0x6a4b7b03), SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d)); // -4.38024f, 5.3915e-19f, 6.14982e+25f, 3.20982e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527)); // 3.35016e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x08211f72), SkBits2Float(0x032a2a21), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2547937a)); // 2.49282e-13f, 4.84861e-34f, 5.00069e-37f, 6.14991e+25f, 1.73105e-16f +path.quadTo(SkBits2Float(0x2128282a), SkBits2Float(0x3a8a3adf), SkBits2Float(0x8a284f1a), SkBits2Float(0xc2213ab3)); // 5.69738e-19f, 0.00105461f, -8.10378e-33f, -40.3073f +path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x3a2a812a), SkBits2Float(0x2a8ced29)); // 2.25206e-21f, 300.343f, 0.000650423f, 2.50336e-13f +path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527)); // 3.35016e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527)); // 3.35016e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x03210831), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x6829ed27), SkBits2Float(0x55555b2d), SkBits2Float(0x1e2a3a2a)); // 4.73231e-37f, 6.14991e+25f, 3.20982e+24f, 1.46617e+13f, 9.01175e-21f +path.conicTo(SkBits2Float(0x27202140), SkBits2Float(0x3a3b2729), SkBits2Float(0xc4371f20), SkBits2Float(0xecc52a22), SkBits2Float(0x21515d27)); // 2.22225e-15f, 0.000713932f, -732.486f, -1.90686e+27f, 7.09352e-19f +path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527)); // 3.35016e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x6829523a), SkBits2Float(0x2d555b2d)); // 3.19839e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 4.03114e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108)); // 3.60396e-20f, -2.04889f +path.cubicTo(SkBits2Float(0x68572d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0)); // 4.06458e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x2a8c54ed), SkBits2Float(0x21081f21), SkBits2Float(0x4b7bc003), SkBits2Float(0x29ed846a), SkBits2Float(0x555b2d28)); // 2.49279e-13f, 4.61198e-19f, 1.64987e+07f, 1.05479e-13f, 1.50617e+13f +path.conicTo(SkBits2Float(0x68392d5b), SkBits2Float(0xf0682955), SkBits2Float(0x2a1f5b2d), SkBits2Float(0xef552a21), SkBits2Float(0x5b2d2a8c)); // 3.4979e+24f, -2.87402e+29f, 1.41537e-13f, -6.59712e+28f, 4.8742e+16f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void fuzz763_24(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0xededed02), SkBits2Float(0xedededed)); // -9.20431e+27f, -9.20445e+27f +path.close(); +path.moveTo(SkBits2Float(0xededed02), SkBits2Float(0xedededed)); // -9.20431e+27f, -9.20445e+27f +path.quadTo(SkBits2Float(0x9fb9c16e), SkBits2Float(0x27737375), SkBits2Float(0xb7c5ff00), SkBits2Float(0x00ff9908)); // -7.86706e-20f, 3.37856e-15f, -2.3603e-05f, 2.34729e-38f +path.moveTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273)); // 1.9288e+31f, 1.9278e+31f +path.cubicTo(SkBits2Float(0x1616ecec), SkBits2Float(0x2c321616), SkBits2Float(0x3516c616), SkBits2Float(0x6e161616), SkBits2Float(0x4c416033), SkBits2Float(0xf6000000)); // 1.21917e-25f, 2.53076e-12f, 5.61676e-07f, 1.16124e+28f, 5.06923e+07f, -6.49037e+32f +path.quadTo(SkBits2Float(0x04007f41), SkBits2Float(0xecececec), SkBits2Float(0xecececec), SkBits2Float(0xecec41ec)); // 1.51048e-36f, -2.2914e+27f, -2.2914e+27f, -2.28494e+27f +path.lineTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273)); // 1.9288e+31f, 1.9278e+31f +path.close(); +path.moveTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273)); // 1.9288e+31f, 1.9278e+31f +path.quadTo(SkBits2Float(0x000000ec), SkBits2Float(0xececcc00), SkBits2Float(0x48ececec), SkBits2Float(0x0278806e)); // 3.30706e-43f, -2.29016e+27f, 485223, 1.8257e-37f +path.lineTo(SkBits2Float(0x72ececec), SkBits2Float(0xecec02ec)); // 9.38559e+30f, -2.28256e+27f +path.quadTo(SkBits2Float(0xec04007f), SkBits2Float(0xecececec), SkBits2Float(0xecececec), SkBits2Float(0xecec0400)); // -6.38322e+26f, -2.2914e+27f, -2.2914e+27f, -2.2826e+27f +path.lineTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273)); // 1.9288e+31f, 1.9278e+31f +path.close(); +path.moveTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273)); // 1.9288e+31f, 1.9278e+31f +path.quadTo(SkBits2Float(0x000040ec), SkBits2Float(0x3a333300), SkBits2Float(0xecec3333), SkBits2Float(0xececdbec)); // 2.32896e-41f, 0.000683591f, -2.28439e+27f, -2.29076e+27f +path.lineTo(SkBits2Float(0x3300007f), SkBits2Float(0x33d83333)); // 2.98028e-08f, 1.00676e-07f +path.lineTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273)); // 1.9288e+31f, 1.9278e+31f +path.close(); +path.moveTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273)); // 1.9288e+31f, 1.9278e+31f +path.quadTo(SkBits2Float(0x9e9ea900), SkBits2Float(0x33ececec), SkBits2Float(0xececec33), SkBits2Float(0xec336e6e)); // -1.67988e-20f, 1.10327e-07f, -2.29138e+27f, -8.67677e+26f +path.lineTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273)); // 1.9288e+31f, 1.9278e+31f +path.close(); +path.moveTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273)); // 1.9288e+31f, 1.9278e+31f +path.lineTo(SkBits2Float(0xedededed), SkBits2Float(0xedededed)); // -9.20445e+27f, -9.20445e+27f +path.lineTo(SkBits2Float(0xecececec), SkBits2Float(0xecececec)); // -2.2914e+27f, -2.2914e+27f +path.lineTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273)); // 1.9288e+31f, 1.9278e+31f +path.close(); +path.moveTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273)); // 1.9288e+31f, 1.9278e+31f +path.lineTo(SkBits2Float(0x01003300), SkBits2Float(0x33d83333)); // 2.35465e-38f, 1.00676e-07f +path.quadTo(SkBits2Float(0xecec3333), SkBits2Float(0x04eeedec), SkBits2Float(0xe0e0e0e0), SkBits2Float(0x9ee0e0e0)); // -2.28439e+27f, 5.6172e-36f, -1.29634e+20f, -2.38099e-20f +path.lineTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273)); // 1.9288e+31f, 1.9278e+31f +path.close(); +path.moveTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273)); // 1.9288e+31f, 1.9278e+31f +path.cubicTo(SkBits2Float(0x299e9e9e), SkBits2Float(0xecececec), SkBits2Float(0xececb6ec), SkBits2Float(0xf0ececec), SkBits2Float(0x0000ecec), SkBits2Float(0x9ebe6e6e)); // 7.04413e-14f, -2.2914e+27f, -2.28936e+27f, -5.86599e+29f, 8.49916e-41f, -2.01627e-20f +path.cubicTo(SkBits2Float(0x9e9e9e9e), SkBits2Float(0xe8009e9e), SkBits2Float(0x9e9e9e9e), SkBits2Float(0xecec9e9e), SkBits2Float(0xec3333ec), SkBits2Float(0xececf0ec)); // -1.67945e-20f, -2.42956e+24f, -1.67945e-20f, -2.28844e+27f, -8.66572e+26f, -2.29155e+27f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_25(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x6a4b7bc4)); // 0, 6.14991e+25f +path.conicTo(SkBits2Float(0x653140d9), SkBits2Float(0x6a4b4f74), SkBits2Float(0x65906630), SkBits2Float(0x6a25a070), SkBits2Float(0x3f6728a2)); // 5.23159e+22f, 6.14468e+25f, 8.52382e+22f, 5.00576e+25f, 0.902964f +path.cubicTo(SkBits2Float(0x68295bc5), SkBits2Float(0x00000000), SkBits2Float(0x682958ff), SkBits2Float(0x00000000), SkBits2Float(0x68286829), SkBits2Float(0x00000000)); // 3.19909e+24f, 0, 3.19889e+24f, 0, 3.18112e+24f, 0 +path.lineTo(SkBits2Float(0x68555b29), SkBits2Float(0x00000000)); // 4.03018e+24f, 0 +path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x682d2927), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x6829686f)); // 0, 3.27091e+24f, 0, 0, 3.20003e+24f +path.lineTo(SkBits2Float(0xdf218a28), SkBits2Float(0x00000000)); // -1.16402e+19f, 0 +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x6a4b7bc4)); // 0, 6.14991e+25f +path.close(); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.conicTo(SkBits2Float(0x6642c40c), SkBits2Float(0x00000000), SkBits2Float(0x65906630), SkBits2Float(0x6a25a070), SkBits2Float(0x3edcd74d)); // 2.29939e+23f, 0, 8.52382e+22f, 5.00576e+25f, 0.43133f +path.conicTo(SkBits2Float(0x68295afa), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x4277a57b)); // 3.19903e+24f, 0, 0, 0, 61.9116f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename); +} + + +static void fuzz763_26(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 4.03114e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc003210a)); // 3.60396e-20f, -2.04889f +path.cubicTo(SkBits2Float(0x68372d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0)); // 3.46012e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x69555b2d)); // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.61207e+25f +path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0682955)); // 3.35016e+24f, -2.87402e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x8cef552a), SkBits2Float(0x295b2d2a), SkBits2Float(0x68210368), SkBits2Float(0x7bc05508)); // 5.76397e-19f, -3.6875e-31f, 4.86669e-14f, 3.04146e+24f, 1.99729e+36f +path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0682955)); // 3.35016e+24f, -2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0682955)); // 3.35016e+24f, -2.87402e+29f +path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55)); // 4.85282e+16f, 5.7784e-19f +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x68385b2d), SkBits2Float(0x555bf055), SkBits2Float(0x2a1f2a8c), SkBits2Float(0x03212121), SkBits2Float(0x5a4b7bc0)); // 3.48239e+24f, 1.51141e+13f, 1.41368e-13f, 4.73517e-37f, 1.43189e+16f +path.conicTo(SkBits2Float(0xc08c2aed), SkBits2Float(0x211f2108), SkBits2Float(0x6a4b7b03), SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d)); // -4.38024f, 5.3915e-19f, 6.14982e+25f, 3.20982e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68355b2d), SkBits2Float(0xf0685527)); // 3.42572e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x6e2a1f72), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x4793ed7a)); // 2.49282e-13f, 1.31626e+28f, 4.7323e-37f, 6.14991e+25f, 75739 +path.lineTo(SkBits2Float(0x68355b2d), SkBits2Float(0xf0685527)); // 3.42572e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68355b2d), SkBits2Float(0xf0685527)); // 3.42572e+24f, -2.87614e+29f +path.quadTo(SkBits2Float(0x2128282a), SkBits2Float(0x3a8a3adf), SkBits2Float(0x8a284f1a), SkBits2Float(0x2c213ab3)); // 5.69738e-19f, 0.00105461f, -8.10378e-33f, 2.29121e-12f +path.lineTo(SkBits2Float(0x68355b2d), SkBits2Float(0xf0685527)); // 3.42572e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68355b2d), SkBits2Float(0xf0685527)); // 3.42572e+24f, -2.87614e+29f +path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x3a2a812a), SkBits2Float(0x2127ed29)); // 2.25206e-21f, 300.343f, 0.000650423f, 5.68957e-19f +path.conicTo(SkBits2Float(0x03210831), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x6829ed27), SkBits2Float(0x55555b2d), SkBits2Float(0x1e2a3a2a)); // 4.73231e-37f, 6.14991e+25f, 3.20982e+24f, 1.46617e+13f, 9.01175e-21f +path.conicTo(SkBits2Float(0x27202140), SkBits2Float(0x3a3b2769), SkBits2Float(0xc4371f20), SkBits2Float(0xecc52a22), SkBits2Float(0x21512727)); // 2.22225e-15f, 0.000713936f, -732.486f, -1.90686e+27f, 7.08638e-19f +path.lineTo(SkBits2Float(0x68355b2d), SkBits2Float(0xf0685527)); // 3.42572e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x6829523a), SkBits2Float(0x2d555b2d)); // 3.19839e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x5b2d5529)); // 4.03114e+24f, 4.87888e+16f +path.moveTo(SkBits2Float(0x1f2a322a), SkBits2Float(0xc0032108)); // 3.60404e-20f, -2.04889f +path.cubicTo(SkBits2Float(0x68572d55), SkBits2Float(0xf05bd24b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0xed4b7bc0)); // 4.06458e+24f, -2.72126e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, -3.93594e+27f +path.conicTo(SkBits2Float(0x212a8c6a), SkBits2Float(0x0329081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x2d555b2d)); // 5.77841e-19f, 4.9674e-37f, 6.14991e+25f, 9.43289e-15f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68385b2d), SkBits2Float(0xf0682955)); // 3.48239e+24f, -2.87402e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x8cef552a), SkBits2Float(0x295b2d2a), SkBits2Float(0x68210368), SkBits2Float(0x7bc05508)); // 5.76397e-19f, -3.6875e-31f, 4.86669e-14f, 3.04146e+24f, 1.99729e+36f +path.lineTo(SkBits2Float(0x68385b2d), SkBits2Float(0xf0682955)); // 3.48239e+24f, -2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0x68385b2d), SkBits2Float(0xf0682955)); // 3.48239e+24f, -2.87402e+29f +path.lineTo(SkBits2Float(0x555b1b29), SkBits2Float(0x6c212a8c)); // 1.50569e+13f, 7.79352e+26f +path.conicTo(SkBits2Float(0x084b0321), SkBits2Float(0x6ac07b2a), SkBits2Float(0x395b2d7a), SkBits2Float(0x8c5bf055), SkBits2Float(0x1f212a3a)); // 6.10918e-34f, 1.16348e+26f, 0.000209024f, -1.69435e-31f, 3.4128e-20f +path.conicTo(SkBits2Float(0x290321d9), SkBits2Float(0x555b2d68), SkBits2Float(0x2a8c558c), SkBits2Float(0x2a212a1f), SkBits2Float(0x7bc00321)); // 2.91172e-14f, 1.50618e+13f, 2.49284e-13f, 1.43143e-13f, 1.99397e+36f +path.lineTo(SkBits2Float(0x68385b2d), SkBits2Float(0xf0682955)); // 3.48239e+24f, -2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0x68385b2d), SkBits2Float(0xf0682955)); // 3.48239e+24f, -2.87402e+29f +path.lineTo(SkBits2Float(0x8c2aed7a), SkBits2Float(0x1f2128c0)); // -1.31678e-31f, 3.41268e-20f +path.lineTo(SkBits2Float(0x68385b2d), SkBits2Float(0xf0682955)); // 3.48239e+24f, -2.87402e+29f +path.close(); + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void fuzz763_28(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 4.03114e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108)); // 3.60396e-20f, -2.04889f +path.cubicTo(SkBits2Float(0x68302d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a1f2a), SkBits2Float(0x0321082a), SkBits2Float(0x6aa37bc0)); // 3.32789e+24f, -2.71613e+29f, -1.64207e-31f, 5.76395e-19f, 4.7323e-37f, 9.88197e+25f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2d28ed84), SkBits2Float(0x5b2d2955)); // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.60243e-12f, 4.87406e+16f +path.moveTo(SkBits2Float(0x6c395b2d), SkBits2Float(0xf0682955)); // 8.96327e+26f, -2.87402e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x2aef8c55), SkBits2Float(0x68295b2d), SkBits2Float(0x21086855), SkBits2Float(0x4b7bc003)); // 5.76397e-19f, 4.25523e-13f, 3.19905e+24f, 4.62167e-19f, 1.64987e+07f +path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55)); // 4.85282e+16f, 5.7784e-19f +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.lineTo(SkBits2Float(0x8a283a28), SkBits2Float(0x284f1a3a)); // -8.09984e-33f, 1.14965e-14f +path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x272a812a), SkBits2Float(0x3a2a5529)); // 2.25206e-21f, 300.343f, 2.36623e-15f, 0.000649768f +path.lineTo(SkBits2Float(0x213b1e2a), SkBits2Float(0x27292720)); // 6.3398e-19f, 2.34747e-15f +path.conicTo(SkBits2Float(0x381f203a), SkBits2Float(0x2ac422c5), SkBits2Float(0xc25d27ec), SkBits2Float(0x3a705921), SkBits2Float(0x2a105152)); // 3.79386e-05f, 3.48407e-13f, -55.289f, 0.000916855f, 1.2818e-13f +path.quadTo(SkBits2Float(0x633ad912), SkBits2Float(0x29c80927), SkBits2Float(0x272927b0), SkBits2Float(0x683a5b2d)); // 3.44674e+21f, 8.88337e-14f, 2.3475e-15f, 3.52017e+24f +path.lineTo(SkBits2Float(0x295b2d68), SkBits2Float(0x29685568)); // 4.86672e-14f, 5.15884e-14f +path.conicTo(SkBits2Float(0xaa8c555b), SkBits2Float(0x081f2a21), SkBits2Float(0x5b2d0321), SkBits2Float(0x68556829), SkBits2Float(0x2a552d29)); // -2.49282e-13f, 4.78968e-34f, 4.86986e+16f, 4.03114e+24f, 1.89339e-13f +path.cubicTo(SkBits2Float(0x21295b2d), SkBits2Float(0x2a688c5b), SkBits2Float(0x68295b2d), SkBits2Float(0x2d296855), SkBits2Float(0x8c08555b), SkBits2Float(0x2a2a29ca)); // 5.73801e-19f, 2.06544e-13f, 3.19905e+24f, 9.6297e-12f, -1.05027e-31f, 1.51135e-13f +path.quadTo(SkBits2Float(0x68295b21), SkBits2Float(0x2d296855), SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a21)); // 3.19904e+24f, 9.6297e-12f, 2.49282e-13f, 4.78968e-34f +path.lineTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.close(); +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x5b2d6829), SkBits2Float(0x1f212a55), SkBits2Float(0x8ced7aba), SkBits2Float(0x3f2a212a)); // 6.14991e+25f, 4.88097e+16f, 3.41281e-20f, -3.65895e-31f, 0.664569f +path.lineTo(SkBits2Float(0x5b2d212d), SkBits2Float(0x2d556829)); // 4.87316e+16f, 1.21308e-11f +path.moveTo(SkBits2Float(0x68552968), SkBits2Float(0x5568295b)); // 4.02651e+24f, 1.5954e+13f +path.moveTo(SkBits2Float(0x5b2d2968), SkBits2Float(0x212a8c55)); // 4.87407e+16f, 5.7784e-19f +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0), SkBits2Float(0x2147ed7a), SkBits2Float(0x28282a3a)); // 5.77848e-19f, 4.7323e-37f, 5.63611e+25f, 6.77381e-19f, 9.33503e-15f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void fuzz763_27(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.quadTo(SkBits2Float(0x30309ab8), SkBits2Float(0x305b3030), SkBits2Float(0x00f53030), SkBits2Float(0x3a3a0000)); // 6.42483e-10f, 7.97402e-10f, 2.2517e-38f, 0.000709534f +path.quadTo(SkBits2Float(0xb8b8d5b8), SkBits2Float(0x0b0b0b03), SkBits2Float(0x0b0b0b0b), SkBits2Float(0x3a3a0b0b)); // -8.81361e-05f, 2.67787e-32f, 2.67787e-32f, 0.000709698f +path.quadTo(SkBits2Float(0xb8b8b8b8), SkBits2Float(0x0b1203b8), SkBits2Float(0x0b0b0b0b), SkBits2Float(0x3a3a2110)); // -8.80821e-05f, 2.81214e-32f, 2.67787e-32f, 0.000710026f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename); +} + +static void fuzz763_29(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x743e0000)); // 0, 6.02134e+31f +path.cubicTo(SkBits2Float(0x74083cf1), SkBits2Float(0x74536e73), SkBits2Float(0x742ac4e4), SkBits2Float(0x7415f5be), SkBits2Float(0x7433ee3c), SkBits2Float(0x7405a69a)); // 4.31756e+31f, 6.70053e+31f, 5.41189e+31f, 4.75242e+31f, 5.70223e+31f, 4.23556e+31f +path.quadTo(SkBits2Float(0x74360ca0), SkBits2Float(0x7401e10c), SkBits2Float(0x7436a382), SkBits2Float(0x7401cc18)); // 5.76937e+31f, 4.11603e+31f, 5.78805e+31f, 4.11344e+31f +path.cubicTo(SkBits2Float(0x74374a91), SkBits2Float(0x7401ef19), SkBits2Float(0x74375c84), SkBits2Float(0x7404d9b9), SkBits2Float(0x7437868f), SkBits2Float(0x740bae8a)); // 5.80873e+31f, 4.11777e+31f, 5.81095e+31f, 4.2102e+31f, 5.81616e+31f, 4.42669e+31f +path.cubicTo(SkBits2Float(0x7437d6c1), SkBits2Float(0x7418b629), SkBits2Float(0x74387e9b), SkBits2Float(0x7433fbc5), SkBits2Float(0x743e2ff7), SkBits2Float(0x74655fa2)); // 5.82609e+31f, 4.83962e+31f, 5.84687e+31f, 5.7039e+31f, 6.02728e+31f, 7.26914e+31f +path.cubicTo(SkBits2Float(0x741ada75), SkBits2Float(0x74745717), SkBits2Float(0x73c106b4), SkBits2Float(0x74744e64), SkBits2Float(0x00000000), SkBits2Float(0x74744006)); // 4.9075e+31f, 7.74345e+31f, 3.05862e+31f, 7.74237e+31f, 0, 7.74059e+31f +path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0x74746c7c), SkBits2Float(0x74244dce), SkBits2Float(0x7474733e), SkBits2Float(0x74400000), SkBits2Float(0x74747445)); // 0, 7.7461e+31f, 5.207e+31f, 7.74693e+31f, 6.08472e+31f, 7.74706e+31f +path.cubicTo(SkBits2Float(0x743f5854), SkBits2Float(0x746f3659), SkBits2Float(0x743ebe05), SkBits2Float(0x746a3017), SkBits2Float(0x743e2ff7), SkBits2Float(0x74655fa2)); // 6.06397e+31f, 7.58094e+31f, 6.04486e+31f, 7.42171e+31f, 6.02728e+31f, 7.26914e+31f +path.cubicTo(SkBits2Float(0x7447a582), SkBits2Float(0x74615dee), SkBits2Float(0x744f74f6), SkBits2Float(0x745c4903), SkBits2Float(0x7455e7e6), SkBits2Float(0x7455d751)); // 6.32705e+31f, 7.14216e+31f, 6.57457e+31f, 6.98112e+31f, 6.77895e+31f, 6.77689e+31f +path.cubicTo(SkBits2Float(0x74747474), SkBits2Float(0x743750a4), SkBits2Float(0x74747474), SkBits2Float(0x73f46f0d), SkBits2Float(0x74747474), SkBits2Float(0x00000000)); // 7.74708e+31f, 5.80948e+31f, 7.74708e+31f, 3.87321e+31f, 7.74708e+31f, 0 +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0xf0682955), SkBits2Float(0x211f5b2d)); // -2.87402e+29f, 5.3992e-19f +path.moveTo(SkBits2Float(0x2d2aff2d), SkBits2Float(0x74747474)); // 9.72004e-12f, 7.74708e+31f +path.cubicTo(SkBits2Float(0x7474748e), SkBits2Float(0x74747490), SkBits2Float(0x8c722174), SkBits2Float(0x181f0080), SkBits2Float(0x74c0e520), SkBits2Float(0x747d7463)); // 7.7471e+31f, 7.7471e+31f, -1.86531e-31f, 2.05505e-24f, 1.22262e+32f, 8.0323e+31f +path.cubicTo(SkBits2Float(0x7b005e4b), SkBits2Float(0xdf3a6a3a), SkBits2Float(0x2a3a2848), SkBits2Float(0x2d2d7821), SkBits2Float(0x8c55212d), SkBits2Float(0x2d2d2d24)); // 6.66526e+35f, -1.34326e+19f, 1.65341e-13f, 9.86059e-12f, -1.64189e-31f, 9.84393e-12f +path.conicTo(SkBits2Float(0xde28804c), SkBits2Float(0x28e03721), SkBits2Float(0x3329df28), SkBits2Float(0x2d291515), SkBits2Float(0x0568295b)); // -3.03545e+18f, 2.48929e-14f, 3.95513e-08f, 9.61122e-12f, 1.09162e-35f +path.conicTo(SkBits2Float(0x556a2d21), SkBits2Float(0x21088c2a), SkBits2Float(0x3a333303), SkBits2Float(0x5b293a8a), SkBits2Float(0x6855683b)); // 1.60925e+13f, 4.62641e-19f, 0.000683591f, 4.76336e+16f, 4.03115e+24f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void fuzz763_30(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x1f2108c0), SkBits2Float(0x4b7b0321)); // 3.41003e-20f, 1.64503e+07f +path.lineTo(SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d)); // 3.20982e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527)); // 3.33127e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x6e2a1f72), SkBits2Float(0x0321082a), SkBits2Float(0x2a4b7bc0), SkBits2Float(0x68295b2d)); // 2.49282e-13f, 1.31626e+28f, 4.7323e-37f, 1.8073e-13f, 3.19905e+24f +path.lineTo(SkBits2Float(0x5b2d2968), SkBits2Float(0x212a8c55)); // 4.87407e+16f, 5.7784e-19f +path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x4b7b28c0)); // 4.7323e-37f, 1.646e+07f +path.lineTo(SkBits2Float(0x2a8ced7a), SkBits2Float(0x2d081f21)); // 2.50338e-13f, 7.73762e-12f +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 4.03114e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108)); // 3.60396e-20f, -2.04889f +path.cubicTo(SkBits2Float(0x69392d55), SkBits2Float(0x2d5b684b), SkBits2Float(0x8c5527f0), SkBits2Float(0x212a1f2a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0)); // 1.39916e+25f, 1.24719e-11f, -1.64209e-31f, 5.76395e-19f, 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0xed7a6a1f), SkBits2Float(0x3a214793), SkBits2Float(0x3328282a), SkBits2Float(0x3a8a3adf)); // 5.77848e-19f, -4.84372e+27f, 0.000615233f, 3.91521e-08f, 0.00105461f +path.conicTo(SkBits2Float(0x4be80304), SkBits2Float(0xdcdcdc15), SkBits2Float(0xdcdcdcdc), SkBits2Float(0x71dcdcdc), SkBits2Float(0x6c107164)); // 3.04102e+07f, -4.97332e+17f, -4.97339e+17f, 2.18732e+30f, 6.98483e+26f +path.conicTo(SkBits2Float(0x6c0f1d6c), SkBits2Float(0x8e406c6e), SkBits2Float(0x6c6c0200), SkBits2Float(0x6c6ce46c), SkBits2Float(0x6c6c6c6c)); // 6.92061e+26f, -2.3718e-30f, 1.14126e+27f, 1.14554e+27f, 1.14327e+27f +path.lineTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108)); // 3.60396e-20f, -2.04889f +path.close(); +path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108)); // 3.60396e-20f, -2.04889f +path.quadTo(SkBits2Float(0x3ab38a28), SkBits2Float(0x3ac22c21), SkBits2Float(0x6c401057), SkBits2Float(0x6d6d6b64)); // 0.00136978f, 0.00148142f, 9.28764e+26f, 4.59236e+27f +path.cubicTo(SkBits2Float(0x6d6d6d6d), SkBits2Float(0x6d6d6d6d), SkBits2Float(0x286d6d6d), SkBits2Float(0x081d2a29), SkBits2Float(0x6d690321), SkBits2Float(0x6b6b026d)); // 4.59251e+27f, 4.59251e+27f, 1.31799e-14f, 4.7295e-34f, 4.50711e+27f, 2.84109e+26f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_31(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0xd72a8c55), SkBits2Float(0x61081f2a)); // -1.8752e+14f, 1.56938e+20f +path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x4793ed7a), SkBits2Float(0x282a3a21), SkBits2Float(0xdf3a2128), SkBits2Float(0x471ac575)); // 6.14991e+25f, 75739, 9.4495e-15f, -1.3412e+19f, 39621.5f +path.lineTo(SkBits2Float(0x28404040), SkBits2Float(0x552a298a)); // 1.06721e-14f, 1.16935e+13f +path.moveTo(SkBits2Float(0x212c685b), SkBits2Float(0x21081f2a)); // 5.8414e-19f, 4.61198e-19f +path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x80ed7a3a), SkBits2Float(0x2a3a2147), SkBits2Float(0xdf212828), SkBits2Float(0x4f1a3a3a)); // 6.14991e+25f, -2.18089e-38f, 1.65317e-13f, -1.16126e+19f, 2.58751e+09f +path.lineTo(SkBits2Float(0x212c685b), SkBits2Float(0x21081f2a)); // 5.8414e-19f, 4.61198e-19f +path.close(); +path.moveTo(SkBits2Float(0x212c685b), SkBits2Float(0x21081f2a)); // 5.8414e-19f, 4.61198e-19f +path.cubicTo(SkBits2Float(0x3ac2213a), SkBits2Float(0x432a2928), SkBits2Float(0x96812be6), SkBits2Float(0x272a1d2a), SkBits2Float(0x3a2a3529), SkBits2Float(0x3b1e2ab0)); // 0.00148109f, 170.161f, -2.08688e-25f, 2.3608e-15f, 0.000649291f, 0.00241343f +path.lineTo(SkBits2Float(0x212c685b), SkBits2Float(0x21081f2a)); // 5.8414e-19f, 4.61198e-19f +path.close(); +path.moveTo(SkBits2Float(0x212c685b), SkBits2Float(0x21081f2a)); // 5.8414e-19f, 4.61198e-19f +path.cubicTo(SkBits2Float(0xc5272927), SkBits2Float(0x22383b39), SkBits2Float(0x1051523a), SkBits2Float(0x2927b029), SkBits2Float(0x685b2d27), SkBits2Float(0x5b2d6855)); // -2674.57f, 2.4968e-18f, 4.12813e-29f, 3.72342e-14f, 4.14012e+24f, 4.88099e+16f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename); +} + +static void fuzz763_33(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x72c185d5), SkBits2Float(0x72c184e8)); // 7.66623e+30f, 7.66608e+30f +path.quadTo(SkBits2Float(0x724341bf), SkBits2Float(0x72433fc4), SkBits2Float(0x6d757575), SkBits2Float(0x6d6d6d6d)); // 3.86746e+30f, 3.86731e+30f, 4.74786e+27f, 4.59251e+27f +path.cubicTo(SkBits2Float(0x6d18b5e5), SkBits2Float(0x6d6d6d6d), SkBits2Float(0x6cbe03bd), SkBits2Float(0x6d4b455b), SkBits2Float(0x6c6c69d8), SkBits2Float(0x6d20df31)); // 2.95385e+27f, 4.59251e+27f, 1.83771e+27f, 3.93183e+27f, 1.14323e+27f, 3.11171e+27f +path.conicTo(SkBits2Float(0x6c6c8b72), SkBits2Float(0x00000000), SkBits2Float(0x6c6c6c6c), SkBits2Float(0x00000000), SkBits2Float(0x400812df)); // 1.14386e+27f, 0, 1.14327e+27f, 0, 2.12615f +path.quadTo(SkBits2Float(0x72432acb), SkBits2Float(0x72432295), SkBits2Float(0x72c185d5), SkBits2Float(0x72c184e8)); // 3.86568e+30f, 3.86505e+30f, 7.66623e+30f, 7.66608e+30f +path.close(); +path.moveTo(SkBits2Float(0x72c185d5), SkBits2Float(0x72c184e8)); // 7.66623e+30f, 7.66608e+30f +path.cubicTo(SkBits2Float(0x74f97d76), SkBits2Float(0x74f97d90), SkBits2Float(0x75381628), SkBits2Float(0x7538182c), SkBits2Float(0x7538153b), SkBits2Float(0x75381835)); // 1.58133e+32f, 1.58133e+32f, 2.33357e+32f, 2.33367e+32f, 2.33353e+32f, 2.33368e+32f +path.cubicTo(SkBits2Float(0x7538144e), SkBits2Float(0x7538183f), SkBits2Float(0x74f9760f), SkBits2Float(0x74f97ddd), SkBits2Float(0x72c185d5), SkBits2Float(0x72c184e8)); // 2.33348e+32f, 2.33368e+32f, 1.58115e+32f, 1.58134e+32f, 7.66623e+30f, 7.66608e+30f +path.close(); +path.moveTo(SkBits2Float(0x6c6c69d8), SkBits2Float(0x6d20df31)); // 1.14323e+27f, 3.11171e+27f +path.conicTo(SkBits2Float(0x6c6c55ae), SkBits2Float(0x6d80b520), SkBits2Float(0x6c6c1071), SkBits2Float(0x6e0f1d6c), SkBits2Float(0x3f96e656)); // 1.14284e+27f, 4.97913e+27f, 1.14154e+27f, 1.1073e+28f, 1.1789f +path.lineTo(SkBits2Float(0x6a674231), SkBits2Float(0x6c0c3394)); // 6.98936e+25f, 6.77973e+26f +path.cubicTo(SkBits2Float(0x6b12c63f), SkBits2Float(0x6c881439), SkBits2Float(0x6bba4ae5), SkBits2Float(0x6ced1e23), SkBits2Float(0x6c6c69d8), SkBits2Float(0x6d20df31)); // 1.77439e+26f, 1.31608e+27f, 4.50428e+26f, 2.29326e+27f, 1.14323e+27f, 3.11171e+27f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0x6c6b6ba7), SkBits2Float(0x886b6b6b)); // 1.13842e+27f, -7.0844e-34f +path.quadTo(SkBits2Float(0x0000206b), SkBits2Float(0x6d6d6d6d), SkBits2Float(0x6d6d6d6d), SkBits2Float(0x6d6d6d6d)); // 1.16294e-41f, 4.59251e+27f, 4.59251e+27f, 4.59251e+27f +path.conicTo(SkBits2Float(0x3e3e3e3e), SkBits2Float(0xafbcad20), SkBits2Float(0x78787878), SkBits2Float(0x78787829), SkBits2Float(0x78787878)); // 0.185784f, -3.432e-10f, 2.01583e+34f, 2.01582e+34f, 2.01583e+34f +path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x95066b78)); // 2.01583e+34f, -2.71459e-26f +path.lineTo(SkBits2Float(0x6c6b6ba7), SkBits2Float(0x886b6b6b)); // 1.13842e+27f, -7.0844e-34f +path.quadTo(SkBits2Float(0x0000206b), SkBits2Float(0x6d6d6d6d), SkBits2Float(0x6d6d6d6d), SkBits2Float(0x6d6d6d6d)); // 1.16294e-41f, 4.59251e+27f, 4.59251e+27f, 4.59251e+27f +path.conicTo(SkBits2Float(0x3e3e3e3e), SkBits2Float(0xafbcad20), SkBits2Float(0x78787878), SkBits2Float(0x78787829), SkBits2Float(0x78787878)); // 0.185784f, -3.432e-10f, 2.01583e+34f, 2.01582e+34f, 2.01583e+34f +path.lineTo(SkBits2Float(0x8787878f), SkBits2Float(0x87878787)); // -2.03922e-34f, -2.03922e-34f +path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x78787878)); // 2.01583e+34f, 2.01583e+34f +path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x78787878)); // 2.01583e+34f, 2.01583e+34f +path.lineTo(SkBits2Float(0x6c105778), SkBits2Float(0x6d406b64)); // 6.97994e+26f, 3.72193e+27f +path.cubicTo(SkBits2Float(0x7575756d), SkBits2Float(0x75757575), SkBits2Float(0x75757575), SkBits2Float(0x75757575), SkBits2Float(0x6d6d7575), SkBits2Float(0x6d6d6d6d)); // 3.11156e+32f, 3.11156e+32f, 3.11156e+32f, 3.11156e+32f, 4.59312e+27f, 4.59251e+27f +path.cubicTo(SkBits2Float(0x6d696d6d), SkBits2Float(0x026d6d6d), SkBits2Float(0x80bc6b6b), SkBits2Float(0xaebcdfd0), SkBits2Float(0x7878bcac), SkBits2Float(0x78787878)); // 4.51514e+27f, 1.74434e-37f, -1.73036e-38f, -8.58901e-11f, 2.01799e+34f, 2.01583e+34f +path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x78787878)); // 2.01583e+34f, 2.01583e+34f +path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x78787878)); // 2.01583e+34f, 2.01583e+34f +path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x78787878)); // 2.01583e+34f, 2.01583e+34f +path.lineTo(SkBits2Float(0xb4bcacbc), SkBits2Float(0xbcadbcbc)); // -3.51434e-07f, -0.0212082f +path.moveTo(SkBits2Float(0xa03aacbc), SkBits2Float(0x757575a0)); // -1.5812e-19f, 3.11157e+32f +path.close(); + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename); +} + +static void fuzz763_32(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.cubicTo(SkBits2Float(0xdedcdcdc), SkBits2Float(0xdcdcdcdc), SkBits2Float(0xdcdcdcdc), SkBits2Float(0xdcdcdcdc), SkBits2Float(0x55dcdcdc), SkBits2Float(0x29407d7f)); // -7.95742e+18f, -4.97339e+17f, -4.97339e+17f, -4.97339e+17f, 3.03551e+13f, 4.27414e-14f +path.cubicTo(SkBits2Float(0x7b93ed4b), SkBits2Float(0x29521472), SkBits2Float(0xdfc83c28), SkBits2Float(0x1a3a834e), SkBits2Float(0x6855e84f), SkBits2Float(0xf2f22a80)); // 1.53616e+36f, 4.66471e-14f, -2.88569e+19f, 3.857e-23f, 4.0406e+24f, -9.59318e+30f +path.moveTo(SkBits2Float(0xe0f2f210), SkBits2Float(0xc3f2eef2)); // -1.40049e+20f, -485.867f +path.cubicTo(SkBits2Float(0x108ced7a), SkBits2Float(0x7bc00308), SkBits2Float(0x287a6a3a), SkBits2Float(0x242847ed), SkBits2Float(0x2bcb302a), SkBits2Float(0xf21003e8)); // 5.55862e-29f, 1.99396e+36f, 1.39008e-14f, 3.64901e-17f, 1.44374e-12f, -2.85252e+30f +path.moveTo(SkBits2Float(0x556c0010), SkBits2Float(0x002a8768)); // 1.62178e+13f, 3.90567e-39f +path.quadTo(SkBits2Float(0xf2f22021), SkBits2Float(0xf2f2f56e), SkBits2Float(0xf2f2f2f2), SkBits2Float(0xf22040d9)); // -9.59158e+30f, -9.62459e+30f, -9.6242e+30f, -3.17414e+30f +path.lineTo(SkBits2Float(0xc013f2f2), SkBits2Float(0x0000294d)); // -2.3117f, 1.48159e-41f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void fuzz763_34(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x63a95a6c), SkBits2Float(0x6cc8e7e2)); // 6.24803e+21f, 1.94304e+27f +path.quadTo(SkBits2Float(0x63690f37), SkBits2Float(0x6d0a3d9b), SkBits2Float(0x00000000), SkBits2Float(0x6d3e3e3e)); // 4.29919e+21f, 2.67396e+27f, 0, 3.67984e+27f +path.conicTo(SkBits2Float(0x6b9253fc), SkBits2Float(0x6c956a8b), SkBits2Float(0x6c6ac798), SkBits2Float(0x692a5d27), SkBits2Float(0x3e56eb72)); // 3.538e+26f, 1.44506e+27f, 1.13532e+27f, 1.28723e+25f, 0.209883f +path.lineTo(SkBits2Float(0x6c6c586c), SkBits2Float(0x00000000)); // 1.1429e+27f, 0 +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.conicTo(SkBits2Float(0x6c8c6c6c), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x6cc8e82a), SkBits2Float(0x5b684b68)); // 1.35809e+27f, 0, 0, 1.94305e+27f, 6.53851e+16f +path.lineTo(SkBits2Float(0x63a95a6c), SkBits2Float(0x6cc8e7e2)); // 6.24803e+21f, 1.94304e+27f +path.close(); +path.moveTo(SkBits2Float(0x63a95a6c), SkBits2Float(0x6cc8e7e2)); // 6.24803e+21f, 1.94304e+27f +path.quadTo(SkBits2Float(0x641ae35f), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 1.14287e+22f, 0, 0, 0 +path.lineTo(SkBits2Float(0x6c6c586c), SkBits2Float(0x00000000)); // 1.1429e+27f, 0 +path.conicTo(SkBits2Float(0x6c6ba1fc), SkBits2Float(0x688c9eb1), SkBits2Float(0x6c6ac798), SkBits2Float(0x692a5d27), SkBits2Float(0x3f7fec32)); // 1.13945e+27f, 5.31247e+24f, 1.13532e+27f, 1.28723e+25f, 0.999698f +path.lineTo(SkBits2Float(0x63a95a6c), SkBits2Float(0x6cc8e7e2)); // 6.24803e+21f, 1.94304e+27f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x6c3e3e3e), SkBits2Float(0x586c79ff)); // 9.19959e+26f, 1.04003e+15f +path.quadTo(SkBits2Float(0x6c6c4a6c), SkBits2Float(0x6c6c6c6c), SkBits2Float(0xc83e6c6c), SkBits2Float(0x3e313e3e)); // 1.14263e+27f, 1.14327e+27f, -194994, 0.173089f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_36(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29)); // 4.03114e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108)); // 3.60396e-20f, -2.04889f +path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0)); // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x2d555b2d)); // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.21279e-11f +path.moveTo(SkBits2Float(0xe8355b2d), SkBits2Float(0xf0682955)); // -3.42572e+24f, -2.87402e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x8cef552a), SkBits2Float(0x295b2d2a), SkBits2Float(0x68210368), SkBits2Float(0x7bc05508)); // 5.76397e-19f, -3.6875e-31f, 4.86669e-14f, 3.04146e+24f, 1.99729e+36f +path.lineTo(SkBits2Float(0xe8355b2d), SkBits2Float(0xf0682955)); // -3.42572e+24f, -2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0xe8355b2d), SkBits2Float(0xf0682955)); // -3.42572e+24f, -2.87402e+29f +path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55)); // 4.85282e+16f, 5.7784e-19f +path.conicTo(SkBits2Float(0x212a081f), SkBits2Float(0x4b7bc003), SkBits2Float(0x5b2d7a6a), SkBits2Float(0xf0556839), SkBits2Float(0x2a8c555b)); // 5.7609e-19f, 1.64987e+07f, 4.88298e+16f, -2.64185e+29f, 2.49282e-13f +path.conicTo(SkBits2Float(0xf42a212a), SkBits2Float(0x4b7bc003), SkBits2Float(0x2aed7a39), SkBits2Float(0x2108c08c), SkBits2Float(0x7b03211f)); // -5.39162e+31f, 1.64987e+07f, 4.21845e-13f, 4.63334e-19f, 6.80863e+35f +path.lineTo(SkBits2Float(0xe8355b2d), SkBits2Float(0xf0682955)); // -3.42572e+24f, -2.87402e+29f +path.close(); +path.moveTo(SkBits2Float(0xe8355b2d), SkBits2Float(0xf0682955)); // -3.42572e+24f, -2.87402e+29f +path.lineTo(SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d)); // 3.20982e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x212a1f72), SkBits2Float(0x03210807), SkBits2Float(0x6a4b7b28), SkBits2Float(0x4793ed7a)); // 2.49282e-13f, 5.76399e-19f, 4.73229e-37f, 6.14984e+25f, 75739 +path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.quadTo(SkBits2Float(0x282a282a), SkBits2Float(0x8a3adf21), SkBits2Float(0x284f1a3a), SkBits2Float(0x213ab38a)); // 9.4456e-15f, -8.99754e-33f, 1.14965e-14f, 6.32569e-19f +path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x3a20002a), SkBits2Float(0x2a8ced29)); // 2.25206e-21f, 300.343f, 0.000610354f, 2.50336e-13f +path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0xed210830), SkBits2Float(0xc04b6a03), SkBits2Float(0x68297b27), SkBits2Float(0x55555b2d), SkBits2Float(0x2ab03a2a)); // -3.11481e+27f, -3.17835f, 3.20141e+24f, 1.46617e+13f, 3.13042e-13f +path.quadTo(SkBits2Float(0x2720213b), SkBits2Float(0x3a3b2729), SkBits2Float(0xc4341f20), SkBits2Float(0xecc52a22)); // 2.22225e-15f, 0.000713932f, -720.486f, -1.90686e+27f +path.cubicTo(SkBits2Float(0x5921c25d), SkBits2Float(0x29523a70), SkBits2Float(0x555b2d68), SkBits2Float(0x1f212a8c), SkBits2Float(0x0321d90a), SkBits2Float(0x5b2d6829)); // 2.8457e+15f, 4.66801e-14f, 1.50618e+13f, 3.41283e-20f, 4.75628e-37f, 4.88097e+16f +path.lineTo(SkBits2Float(0x1f2a2a8c), SkBits2Float(0x03210821)); // 3.60341e-20f, 4.7323e-37f +path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x2eed6a7a), SkBits2Float(0x282a3a21), SkBits2Float(0x3a21df28), SkBits2Float(0x4f1a3a8a), SkBits2Float(0x3ab38a28)); // 1.07964e-10f, 9.4495e-15f, 0.000617492f, 2.58753e+09f, 0.00136978f +path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.quadTo(SkBits2Float(0xe61d2a28), SkBits2Float(0x2a43962b), SkBits2Float(0x29272a81), SkBits2Float(0x2bb02a55)); // -1.85547e+23f, 1.73716e-13f, 3.71183e-14f, 1.25173e-12f +path.quadTo(SkBits2Float(0x2720213b), SkBits2Float(0x3ac52729), SkBits2Float(0xc4223b32), SkBits2Float(0x6c2a201f)); // 2.22225e-15f, 0.00150416f, -648.925f, 8.22676e+26f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void fuzz763_35(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x2aed2a8c), SkBits2Float(0x03210a1f)); // 4.21292e-13f, 4.73253e-37f +path.conicTo(SkBits2Float(0x0000007b), SkBits2Float(0x7474747f), SkBits2Float(0x74747474), SkBits2Float(0x747474c4), SkBits2Float(0x74747474)); // 1.7236e-43f, 7.74709e+31f, 7.74708e+31f, 7.74712e+31f, 7.74708e+31f +path.quadTo(SkBits2Float(0x74747474), SkBits2Float(0x74747474), SkBits2Float(0x20437474), SkBits2Float(0x43a52b02)); // 7.74708e+31f, 7.74708e+31f, 1.65557e-19f, 330.336f +path.moveTo(SkBits2Float(0x3a214781), SkBits2Float(0x2128282a)); // 0.000615232f, 5.69738e-19f +path.lineTo(SkBits2Float(0x4b7bd603), SkBits2Float(0x6cf33b6a)); // 1.65043e+07f, 2.3524e+27f +path.conicTo(SkBits2Float(0x35778caa), SkBits2Float(0x0000002a), SkBits2Float(0x74742164), SkBits2Float(0x2a3a7474), SkBits2Float(0x4cc22157)); // 9.22194e-07f, 5.88545e-44f, 7.7368e+31f, 1.65605e-13f, 1.0178e+08f +path.cubicTo(SkBits2Float(0x21479321), SkBits2Float(0x23434cc2), SkBits2Float(0x3a214793), SkBits2Float(0x2128282a), SkBits2Float(0x323adf81), SkBits2Float(0x77291a3a)); // 6.76185e-19f, 1.05872e-17f, 0.000615233f, 5.69738e-19f, 1.08774e-08f, 3.42981e+33f +path.conicTo(SkBits2Float(0x0000002a), SkBits2Float(0x7474743e), SkBits2Float(0x74747474), SkBits2Float(0x74746474), SkBits2Float(0x74747474)); // 5.88545e-44f, 7.74706e+31f, 7.74708e+31f, 7.7451e+31f, 7.74708e+31f +path.cubicTo(SkBits2Float(0x21e7fc06), SkBits2Float(0x2a212a59), SkBits2Float(0x0321081f), SkBits2Float(0x00002a35), SkBits2Float(0x74744000), SkBits2Float(0x2974e874)); // 1.57199e-18f, 1.43144e-13f, 4.7323e-37f, 1.5141e-41f, 7.74059e+31f, 5.43805e-14f +path.cubicTo(SkBits2Float(0x74647474), SkBits2Float(0x74747474), SkBits2Float(0x12ec7474), SkBits2Float(0x4cc22147), SkBits2Float(0x47932343), SkBits2Float(0x282a3a21)); // 7.24002e+31f, 7.74708e+31f, 1.49224e-27f, 1.0178e+08f, 75334.5f, 9.4495e-15f +path.lineTo(SkBits2Float(0x3a214781), SkBits2Float(0x2128282a)); // 0.000615232f, 5.69738e-19f +path.close(); +path.moveTo(SkBits2Float(0x3a214781), SkBits2Float(0x2128282a)); // 0.000615232f, 5.69738e-19f +path.conicTo(SkBits2Float(0x3a323adf), SkBits2Float(0x4977291a), SkBits2Float(0x0000002a), SkBits2Float(0x7474743e), SkBits2Float(0x74747474)); // 0.000679893f, 1.01237e+06f, 5.88545e-44f, 7.74706e+31f, 7.74708e+31f +path.cubicTo(SkBits2Float(0x74747464), SkBits2Float(0x74747474), SkBits2Float(0x21e7fc06), SkBits2Float(0x2a212a59), SkBits2Float(0x0321081f), SkBits2Float(0x00002a35)); // 7.74708e+31f, 7.74708e+31f, 1.57199e-18f, 1.43144e-13f, 4.7323e-37f, 1.5141e-41f +path.moveTo(SkBits2Float(0x74747440), SkBits2Float(0x742974e8)); // 7.74706e+31f, 5.3703e+31f +path.cubicTo(SkBits2Float(0x74746474), SkBits2Float(0x74747474), SkBits2Float(0xd912ec74), SkBits2Float(0x553a3728), SkBits2Float(0x29202a8c), SkBits2Float(0x5555201b)); // 7.7451e+31f, 7.74708e+31f, -2.58471e+15f, 1.27966e+13f, 3.5564e-14f, 1.46459e+13f +path.moveTo(SkBits2Float(0x31292768), SkBits2Float(0x212d2aff)); // 2.46151e-09f, 5.86716e-19f +path.quadTo(SkBits2Float(0x2128282a), SkBits2Float(0x323adf81), SkBits2Float(0x77291a3a), SkBits2Float(0x00002a49)); // 5.69738e-19f, 1.08774e-08f, 3.42981e+33f, 1.51691e-41f +path.moveTo(SkBits2Float(0x7474743e), SkBits2Float(0x74747474)); // 7.74706e+31f, 7.74708e+31f +path.cubicTo(SkBits2Float(0x74747464), SkBits2Float(0x74747474), SkBits2Float(0x21e7fc06), SkBits2Float(0x2a212a59), SkBits2Float(0x0321081f), SkBits2Float(0x00002a35)); // 7.74708e+31f, 7.74708e+31f, 1.57199e-18f, 1.43144e-13f, 4.7323e-37f, 1.5141e-41f +path.moveTo(SkBits2Float(0x74747440), SkBits2Float(0x74747474)); // 7.74706e+31f, 7.74708e+31f +path.cubicTo(SkBits2Float(0x74747464), SkBits2Float(0x74747474), SkBits2Float(0x43747474), SkBits2Float(0xa52b0220), SkBits2Float(0x47812a43), SkBits2Float(0x282a3a21)); // 7.74708e+31f, 7.74708e+31f, 244.455f, -1.48326e-16f, 66132.5f, 9.4495e-15f +path.lineTo(SkBits2Float(0x74747440), SkBits2Float(0x74747474)); // 7.74706e+31f, 7.74708e+31f +path.close(); +path.moveTo(SkBits2Float(0x74747440), SkBits2Float(0x74747474)); // 7.74706e+31f, 7.74708e+31f +path.conicTo(SkBits2Float(0x3a323adf), SkBits2Float(0x19433b1a), SkBits2Float(0x5921e7fc), SkBits2Float(0x1f2a212a), SkBits2Float(0x35032108)); // 0.000679893f, 1.00932e-23f, 2.84828e+15f, 3.60263e-20f, 4.88494e-07f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename); +} + +static void fuzz763_37(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x5568392a), SkBits2Float(0x5b2d3368)); // 1.59583e+13f, 4.87517e+16f +path.conicTo(SkBits2Float(0x5b2d555b), SkBits2Float(0x68275b2d), SkBits2Float(0x21685527), SkBits2Float(0x0321082a), SkBits2Float(0x6ab485c0)); // 4.8789e+16f, 3.16127e+24f, 7.87174e-19f, 4.7323e-37f, 1.09119e+26f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x5b2d2d55)); // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 4.8745e+16f +path.moveTo(SkBits2Float(0x6839552d), SkBits2Float(0xf0683b5b)); // 3.50084e+24f, -2.87489e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x2a8cef2a), SkBits2Float(0x682d2953), SkBits2Float(0xee682103), SkBits2Float(0x4b7bc055)); // 5.76397e-19f, 2.50349e-13f, 3.27093e+24f, -1.79601e+28f, 1.64988e+07f +path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55)); // 4.85282e+16f, 5.7784e-19f +path.conicTo(SkBits2Float(0x4b03213b), SkBits2Float(0xc07b2a08), SkBits2Float(0x5b2d7a6a), SkBits2Float(0xf0556830), SkBits2Float(0x2a8c555b)); // 8.59372e+06f, -3.92444f, 4.88298e+16f, -2.64185e+29f, 2.49282e-13f +path.conicTo(SkBits2Float(0x0321212a), SkBits2Float(0x4b7bd2c0), SkBits2Float(0xed7ac039), SkBits2Float(0x2f218c08), SkBits2Float(0x1f037b2a)); // 4.73517e-37f, 1.65035e+07f, -4.85023e+27f, 1.46926e-10f, 2.78422e-20f +path.lineTo(SkBits2Float(0x6839552d), SkBits2Float(0xf0683b5b)); // 3.50084e+24f, -2.87489e+29f +path.close(); +path.moveTo(SkBits2Float(0x6839552d), SkBits2Float(0xf0683b5b)); // 3.50084e+24f, -2.87489e+29f +path.lineTo(SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d)); // 3.20982e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x721f2a5b), SkBits2Float(0x212a8c55), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7b28), SkBits2Float(0x4793ed7a)); // 3.1526e+30f, 5.7784e-19f, 4.7323e-37f, 6.14984e+25f, 75739 +path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.quadTo(SkBits2Float(0x28282a2a), SkBits2Float(0x2c682921), SkBits2Float(0x8c555bf6), SkBits2Float(0x6d03de30)); // 9.33502e-15f, 3.2992e-12f, -1.64366e-31f, 2.5507e+27f +path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x081f2a21)); // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 4.78968e-34f +path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0xdf93ed7a), SkBits2Float(0x1a3a803a), SkBits2Float(0xb38a294f), SkBits2Float(0x3ac2213a)); // 6.14991e+25f, -2.13186e+19f, 3.85675e-23f, -6.43364e-08f, 0.00148109f +path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0xe62b291d), SkBits2Float(0x2a812a43), SkBits2Float(0x8ced093a), SkBits2Float(0xb38a5c5c), SkBits2Float(0x3ac2213a)); // -2.02071e+23f, 2.29443e-13f, -3.65212e-31f, -6.44293e-08f, 0.00148109f +path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.lineTo(SkBits2Float(0x8ced293a), SkBits2Float(0x5c5c5c5c)); // -3.65404e-31f, 2.48104e+17f +path.moveTo(SkBits2Float(0x21081f21), SkBits2Float(0x4b7bc003)); // 4.61198e-19f, 1.64987e+07f +path.lineTo(SkBits2Float(0x2829ed84), SkBits2Float(0x5b2d2d55)); // 9.43289e-15f, 4.8745e+16f +path.moveTo(SkBits2Float(0x6839552d), SkBits2Float(0xf0683b5a)); // 3.50084e+24f, -2.87489e+29f +path.lineTo(SkBits2Float(0x682d2952), SkBits2Float(0xee682103)); // 3.27093e+24f, -1.79601e+28f +path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x2a3b0355)); // 4.85282e+16f, 1.66101e-13f +path.lineTo(SkBits2Float(0x6839552d), SkBits2Float(0xf0683b5a)); // 3.50084e+24f, -2.87489e+29f +path.close(); +path.moveTo(SkBits2Float(0x6839552d), SkBits2Float(0xf0683b5a)); // 3.50084e+24f, -2.87489e+29f +path.conicTo(SkBits2Float(0x084b218c), SkBits2Float(0x6ac07b2a), SkBits2Float(0x395b2d7a), SkBits2Float(0x5bf05568), SkBits2Float(0x1f2a8c55)); // 6.11275e-34f, 1.16348e+26f, 0.000209024f, 1.35296e+17f, 3.6115e-20f + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.cubicTo(SkBits2Float(0xbcb4bcac), SkBits2Float(0x000029ff), SkBits2Float(0x010000bc), SkBits2Float(0x00bcbc00), SkBits2Float(0xbebcbcbc), SkBits2Float(0xb6aebcae)); // -0.0220626f, 1.50654e-41f, 2.35104e-38f, 1.73325e-38f, -0.368627f, -5.20757e-06f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename); +} + +static void fuzz763_38(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.conicTo(SkBits2Float(0x5b682968), SkBits2Float(0x5b292d11), SkBits2Float(0x212a8c55), SkBits2Float(0x555b2d2d), SkBits2Float(0x52525268)); // 6.53477e+16f, 4.76188e+16f, 5.7784e-19f, 1.50617e+13f, 2.25831e+11f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.close(); +path.moveTo(SkBits2Float(0xa5252600), SkBits2Float(0x52b4adad)); // -1.43243e-16f, 3.88004e+11f +path.close(); +path.moveTo(SkBits2Float(0xa5252600), SkBits2Float(0x52b4adad)); // -1.43243e-16f, 3.88004e+11f +path.quadTo(SkBits2Float(0x72727270), SkBits2Float(0x52525272), SkBits2Float(0x2ac05252), SkBits2Float(0x727fb721)); // 4.80216e+30f, 2.25832e+11f, 3.41632e-13f, 5.06496e+30f +path.lineTo(SkBits2Float(0x73727322), SkBits2Float(0x555b2d29)); // 1.92088e+31f, 1.50617e+13f +path.lineTo(SkBits2Float(0xab2a212e), SkBits2Float(0x7a27872a)); // -6.04422e-13f, 2.17464e+35f +path.moveTo(SkBits2Float(0x25fffefb), SkBits2Float(0x7bc00321)); // 4.44082e-16f, 1.99397e+36f +path.quadTo(SkBits2Float(0x52524852), SkBits2Float(0x72525228), SkBits2Float(0x72727272), SkBits2Float(0x3a727272)); // 2.25789e+11f, 4.16584e+30f, 4.80216e+30f, 0.000924862f +path.lineTo(SkBits2Float(0x25fffefb), SkBits2Float(0x7bc00321)); // 4.44082e-16f, 1.99397e+36f +path.close(); +path.moveTo(SkBits2Float(0x25fffefb), SkBits2Float(0x7bc00321)); // 4.44082e-16f, 1.99397e+36f +path.quadTo(SkBits2Float(0x2a292827), SkBits2Float(0x962b0080), SkBits2Float(0x5252752a), SkBits2Float(0x72725252)); // 1.50241e-13f, -1.38134e-25f, 2.25977e+11f, 4.79967e+30f +path.quadTo(SkBits2Float(0x72725252), SkBits2Float(0x52525272), SkBits2Float(0x72525252), SkBits2Float(0x72727272)); // 4.79967e+30f, 2.25832e+11f, 4.16585e+30f, 4.80216e+30f +path.quadTo(SkBits2Float(0x72727255), SkBits2Float(0xda000072), SkBits2Float(0x52525ada), SkBits2Float(0x52525252)); // 4.80215e+30f, -9.00732e+15f, 2.25867e+11f, 2.25831e+11f +path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x52525272), SkBits2Float(0x72525248), SkBits2Float(0x72727272)); // 4.80216e+30f, 2.25832e+11f, 4.16584e+30f, 4.80216e+30f +path.quadTo(SkBits2Float(0x72727255), SkBits2Float(0xda007b72), SkBits2Float(0x52525ada), SkBits2Float(0x52525252)); // 4.80215e+30f, -9.04113e+15f, 2.25867e+11f, 2.25831e+11f +path.quadTo(SkBits2Float(0x86727272), SkBits2Float(0x5252528d), SkBits2Float(0x72525252), SkBits2Float(0x72727227)); // -4.55992e-35f, 2.25832e+11f, 4.16585e+30f, 4.80214e+30f +path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x29217272), SkBits2Float(0xc003211c), SkBits2Float(0x556a4b7b)); // 4.80216e+30f, 3.58484e-14f, -2.0489f, 1.61006e+13f +path.moveTo(SkBits2Float(0x72557272), SkBits2Float(0x00727272)); // 4.22775e+30f, 1.05103e-38f +path.moveTo(SkBits2Float(0x5a61dada), SkBits2Float(0x52525252)); // 1.58931e+16f, 2.25831e+11f +path.close(); +path.moveTo(SkBits2Float(0x5a61dada), SkBits2Float(0x52525252)); // 1.58931e+16f, 2.25831e+11f +path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x3a727272), SkBits2Float(0x28273ac2), SkBits2Float(0x00802a29)); // 4.80216e+30f, 0.000924862f, 9.2831e-15f, 1.17701e-38f +path.lineTo(SkBits2Float(0x52752a96), SkBits2Float(0x72525252)); // 2.63245e+11f, 4.16585e+30f +path.quadTo(SkBits2Float(0x72525272), SkBits2Float(0x52527272), SkBits2Float(0x52525252), SkBits2Float(0x72727272)); // 4.16586e+30f, 2.25966e+11f, 2.25831e+11f, 4.80216e+30f +path.quadTo(SkBits2Float(0x72725572), SkBits2Float(0x00007272), SkBits2Float(0x525adada), SkBits2Float(0x52525252)); // 4.79991e+30f, 4.10552e-41f, 2.34994e+11f, 2.25831e+11f +path.lineTo(SkBits2Float(0x5a61dada), SkBits2Float(0x52525252)); // 1.58931e+16f, 2.25831e+11f +path.close(); +path.moveTo(SkBits2Float(0x5a61dada), SkBits2Float(0x52525252)); // 1.58931e+16f, 2.25831e+11f +path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x52525272), SkBits2Float(0x72525248), SkBits2Float(0x72727272)); // 4.80216e+30f, 2.25832e+11f, 4.16584e+30f, 4.80216e+30f +path.quadTo(SkBits2Float(0x72727255), SkBits2Float(0xda007b72), SkBits2Float(0x52525ada), SkBits2Float(0x72525252)); // 4.80215e+30f, -9.04113e+15f, 2.25867e+11f, 4.16585e+30f +path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x72727252), SkBits2Float(0xda007b72), SkBits2Float(0x52525ada)); // 4.80216e+30f, 4.80215e+30f, -9.04113e+15f, 2.25867e+11f +path.lineTo(SkBits2Float(0x5a61dada), SkBits2Float(0x52525252)); // 1.58931e+16f, 2.25831e+11f +path.close(); +path.moveTo(SkBits2Float(0x5a61dada), SkBits2Float(0x52525252)); // 1.58931e+16f, 2.25831e+11f +path.quadTo(SkBits2Float(0x86727272), SkBits2Float(0x5252528d), SkBits2Float(0x72525252), SkBits2Float(0x72727227)); // -4.55992e-35f, 2.25832e+11f, 4.16585e+30f, 4.80214e+30f +path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x29217272), SkBits2Float(0xc003211c), SkBits2Float(0x556a4b7b)); // 4.80216e+30f, 3.58484e-14f, -2.0489f, 1.61006e+13f +path.moveTo(SkBits2Float(0x72557272), SkBits2Float(0x00727272)); // 4.22775e+30f, 1.05103e-38f +path.moveTo(SkBits2Float(0x525adada), SkBits2Float(0x52525252)); // 2.34994e+11f, 2.25831e+11f +path.close(); +path.moveTo(SkBits2Float(0xa5252600), SkBits2Float(0x52b4adad)); // -1.43243e-16f, 3.88004e+11f +path.close(); +path.moveTo(SkBits2Float(0xa5252600), SkBits2Float(0x52b4adad)); // -1.43243e-16f, 3.88004e+11f +path.quadTo(SkBits2Float(0x72727270), SkBits2Float(0x52525272), SkBits2Float(0x72525252), SkBits2Float(0x72727272)); // 4.80216e+30f, 2.25832e+11f, 4.16585e+30f, 4.80216e+30f +path.quadTo(SkBits2Float(0x72727255), SkBits2Float(0xda007b72), SkBits2Float(0x52525ada), SkBits2Float(0x52525252)); // 4.80215e+30f, -9.04113e+15f, 2.25867e+11f, 2.25831e+11f +path.quadTo(SkBits2Float(0x52525272), SkBits2Float(0x3b3b0052), SkBits2Float(0x5b2d553a), SkBits2Float(0x68556829)); // 2.25832e+11f, 0.00285341f, 4.87889e+16f, 4.03114e+24f + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x52528c55), SkBits2Float(0x29215252)); // 2.26074e+11f, 3.58206e-14f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void fuzz763_41(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.quadTo(SkBits2Float(0x7a057c72), SkBits2Float(0x72727272), SkBits2Float(0x725b5e72), SkBits2Float(0x055f0089)); // 1.73275e+35f, 4.80216e+30f, 4.34505e+30f, 1.04855e-35f +path.quadTo(SkBits2Float(0x00057272), SkBits2Float(0x72ff0000), SkBits2Float(0xba405e72), SkBits2Float(0x031b0074)); // 5.00233e-40f, 1.01016e+31f, -0.000733829f, 4.55509e-37f +path.lineTo(SkBits2Float(0x664af700), SkBits2Float(0x56397d39)); // 2.39619e+23f, 5.09869e+13f +path.quadTo(SkBits2Float(0x7a057273), SkBits2Float(0x057300e4), SkBits2Float(0x257c0c9f), SkBits2Float(0x72400006)); // 1.73224e+35f, 1.1426e-35f, 2.18618e-16f, 3.80295e+30f +path.quadTo(SkBits2Float(0xba5b5e72), SkBits2Float(0x030000ff), SkBits2Float(0x74ba00e8), SkBits2Float(0xe8ec4000)); // -0.000836826f, 3.7617e-37f, 1.17894e+32f, -8.92527e+24f +path.moveTo(SkBits2Float(0x39724aff), SkBits2Float(0x7200397d)); // 0.000231069f, 2.53975e+30f +path.quadTo(SkBits2Float(0x827a0572), SkBits2Float(0x08727272), SkBits2Float(0x08080808), SkBits2Float(0x08080808)); // -1.83687e-37f, 7.29588e-34f, 4.09355e-34f, 4.09355e-34f +path.lineTo(SkBits2Float(0x08080808), SkBits2Float(0x08080808)); // 4.09355e-34f, 4.09355e-34f +path.lineTo(SkBits2Float(0x08080808), SkBits2Float(0x08080808)); // 4.09355e-34f, 4.09355e-34f +path.conicTo(SkBits2Float(0x72728c08), SkBits2Float(0x5b5e7272), SkBits2Float(0x000074ba), SkBits2Float(0x03f8e300), SkBits2Float(0x5aff00e8)); // 4.80414e+30f, 6.26133e+16f, 4.18736e-41f, 1.46282e-36f, 3.58886e+16f +path.quadTo(SkBits2Float(0x00800039), SkBits2Float(0x72100039), SkBits2Float(0x727a0572), SkBits2Float(0x7a727272)); // 1.1755e-38f, 2.85223e+30f, 4.95218e+30f, 3.14714e+35f +path.lineTo(SkBits2Float(0x7272727a), SkBits2Float(0xdb5e6472)); // 4.80216e+30f, -6.25979e+16f +path.moveTo(SkBits2Float(0x440039fc), SkBits2Float(0x0000f647)); // 512.906f, 8.83477e-41f +path.lineTo(SkBits2Float(0x666d0100), SkBits2Float(0x726efe62)); // 2.79805e+23f, 4.73376e+30f +path.lineTo(SkBits2Float(0x440039fc), SkBits2Float(0x0000f647)); // 512.906f, 8.83477e-41f +path.close(); +path.moveTo(SkBits2Float(0x440039fc), SkBits2Float(0x0000f647)); // 512.906f, 8.83477e-41f +path.conicTo(SkBits2Float(0x72727272), SkBits2Float(0xf3db5e64), SkBits2Float(0x475afc16), SkBits2Float(0x170100ad), SkBits2Float(0x01008000)); // 4.80216e+30f, -3.47604e+31f, 56060.1f, 4.1683e-25f, 2.36017e-38f +path.quadTo(SkBits2Float(0x72057272), SkBits2Float(0x8c7a3472), SkBits2Float(0x72727272), SkBits2Float(0x00f6475e)); // 2.64319e+30f, -1.92751e-31f, 4.80216e+30f, 2.26171e-38f +path.moveTo(SkBits2Float(0x6d106d43), SkBits2Float(0x6efe6266)); // 2.79362e+27f, 3.93641e+28f +path.quadTo(SkBits2Float(0x72727a05), SkBits2Float(0xba5b7272), SkBits2Float(0x03000074), SkBits2Float(0x5aff00e8)); // 4.80274e+30f, -0.000837124f, 3.76163e-37f, 3.58886e+16f +path.quadTo(SkBits2Float(0x00da0039), SkBits2Float(0x72100039), SkBits2Float(0x727a0572), SkBits2Float(0x7a727272)); // 2.00202e-38f, 2.85223e+30f, 4.95218e+30f, 3.14714e+35f +path.lineTo(SkBits2Float(0x7272727a), SkBits2Float(0xdb5e6472)); // 4.80216e+30f, -6.25979e+16f +path.lineTo(SkBits2Float(0xfc5b97fc), SkBits2Float(0x47440039)); // -4.56078e+36f, 50176.2f +path.lineTo(SkBits2Float(0x00710000), SkBits2Float(0x62766d01)); // 1.03774e-38f, 1.13644e+21f +path.quadTo(SkBits2Float(0x7a05726e), SkBits2Float(0x72727272), SkBits2Float(0xf3db5e64), SkBits2Float(0x4a5afc16)); // 1.73224e+35f, 4.80216e+30f, -3.47604e+31f, 3.58785e+06f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename); +} + +static void fuzz763_40(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x10190004), SkBits2Float(0x7272727a)); // 3.01739e-29f, 4.80216e+30f +path.quadTo(SkBits2Float(0xf3db5e64), SkBits2Float(0x5b97fc16), SkBits2Float(0x000039fc), SkBits2Float(0x01008000)); // -3.47604e+31f, 8.55598e+16f, 2.08009e-41f, 2.36017e-38f +path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0x725b5e72), SkBits2Float(0x41720089)); // 1.73224e+35f, 4.80216e+30f, 4.34505e+30f, 15.1251f +path.lineTo(SkBits2Float(0x63636363), SkBits2Float(0x63606363)); // 4.19457e+21f, 4.13923e+21f +path.lineTo(SkBits2Float(0x01000000), SkBits2Float(0x10010004)); // 2.35099e-38f, 2.54408e-29f +path.conicTo(SkBits2Float(0x72727272), SkBits2Float(0xf3db5e64), SkBits2Float(0x4a5afc16), SkBits2Float(0x0000d07d), SkBits2Float(0x01008000)); // 4.80216e+30f, -3.47604e+31f, 3.58785e+06f, 7.47915e-41f, 2.36017e-38f +path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0x725b5e72), SkBits2Float(0x63720089)); // 1.73224e+35f, 4.80216e+30f, 4.34505e+30f, 4.46415e+21f +path.lineTo(SkBits2Float(0x63636363), SkBits2Float(0x63606363)); // 4.19457e+21f, 4.13923e+21f +path.lineTo(SkBits2Float(0x72000000), SkBits2Float(0x5b5e72b4)); // 2.5353e+30f, 6.26136e+16f +path.quadTo(SkBits2Float(0x05720089), SkBits2Float(0x05727272), SkBits2Float(0x7272727a), SkBits2Float(0x5b5e7272)); // 1.13789e-35f, 1.13998e-35f, 4.80216e+30f, 6.26133e+16f +path.cubicTo(SkBits2Float(0x03000074), SkBits2Float(0x4aff00e8), SkBits2Float(0x397d3972), SkBits2Float(0x01727200), SkBits2Float(0x72727a00), SkBits2Float(0x5e8d7272)); // 3.76163e-37f, 8.35596e+06f, 0.000241494f, 4.45302e-38f, 4.80274e+30f, 5.09617e+18f +path.moveTo(SkBits2Float(0x72008972), SkBits2Float(0x458fe705)); // 2.54594e+30f, 4604.88f +path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0xe8727272), SkBits2Float(0xba5b5e03), SkBits2Float(0x03000074)); // 1.73224e+35f, -4.5797e+24f, -0.00083682f, 3.76163e-37f +path.lineTo(SkBits2Float(0xf3dbff00), SkBits2Float(0x00397d16)); // -3.48598e+31f, 5.2795e-39f +path.cubicTo(SkBits2Float(0x7a101900), SkBits2Float(0x72727272), SkBits2Float(0xf3db5e64), SkBits2Float(0x0197fc16), SkBits2Float(0x200c2010), SkBits2Float(0x20203620)); // 1.87049e+35f, 4.80216e+30f, -3.47604e+31f, 5.58304e-38f, 1.18691e-19f, 1.35704e-19f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_39(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.quadTo(SkBits2Float(0x7a057c72), SkBits2Float(0x72727272), SkBits2Float(0x725b5e72), SkBits2Float(0x055f0089)); // 1.73275e+35f, 4.80216e+30f, 4.34505e+30f, 1.04855e-35f +path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0xba405e72), SkBits2Float(0x03000074)); // 1.73224e+35f, 4.80216e+30f, -0.000733829f, 3.76163e-37f +path.lineTo(SkBits2Float(0x664aff00), SkBits2Float(0x56397d39)); // 2.39655e+23f, 5.09869e+13f +path.quadTo(SkBits2Float(0x7a057273), SkBits2Float(0x057300ff), SkBits2Float(0x257c0c9f), SkBits2Float(0x72787257)); // 1.73224e+35f, 1.1426e-35f, 2.18618e-16f, 4.92099e+30f +path.quadTo(SkBits2Float(0xba5b5e72), SkBits2Float(0x03000093), SkBits2Float(0x74ba00e8), SkBits2Float(0xe8ecff00)); // -0.000836826f, 3.76165e-37f, 1.17894e+32f, -8.95346e+24f +path.moveTo(SkBits2Float(0x39724aff), SkBits2Float(0x7200397d)); // 0.000231069f, 2.53975e+30f +path.quadTo(SkBits2Float(0x827a0572), SkBits2Float(0x72727272), SkBits2Float(0x724adf00), SkBits2Float(0x00397d39)); // -1.83687e-37f, 4.80216e+30f, 4.01828e+30f, 5.27954e-39f +path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x16f3abab), SkBits2Float(0xfc5b97fc), SkBits2Float(0x47440039)); // 1.73224e+35f, 3.93671e-25f, -4.56078e+36f, 50176.2f +path.lineTo(SkBits2Float(0x00710000), SkBits2Float(0x62767201)); // 1.03774e-38f, 1.13653e+21f +path.quadTo(SkBits2Float(0x7a05726e), SkBits2Float(0x72727272), SkBits2Float(0xf3db5e64), SkBits2Float(0x4a5afc16)); // 1.73224e+35f, 4.80216e+30f, -3.47604e+31f, 3.58785e+06f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename); +} + + +static void fuzz763_42(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0x725b5e72), SkBits2Float(0x05720089)); // 1.73224e+35f, 4.80216e+30f, 4.34505e+30f, 1.13789e-35f +path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0xba405e72), SkBits2Float(0x03000074)); // 1.73224e+35f, 4.80216e+30f, -0.000733829f, 3.76163e-37f +path.lineTo(SkBits2Float(0x724aff00), SkBits2Float(0x56397d39)); // 4.02075e+30f, 5.09869e+13f +path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0xfa8d00ff), SkBits2Float(0x25727272), SkBits2Float(0x7272727a)); // 1.73224e+35f, -3.66067e+35f, 2.10289e-16f, 4.80216e+30f +path.quadTo(SkBits2Float(0xba5b5e72), SkBits2Float(0x03000093), SkBits2Float(0x74ba00e8), SkBits2Float(0xe8e0ff00)); // -0.000836826f, 3.76165e-37f, 1.17894e+32f, -8.50011e+24f +path.conicTo(SkBits2Float(0x39724aff), SkBits2Float(0x7200397d), SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0x4aff0072)); // 0.000231069f, 2.53975e+30f, 1.73224e+35f, 4.80216e+30f, 8.3559e+06f +path.quadTo(SkBits2Float(0x00397d39), SkBits2Float(0x05727272), SkBits2Float(0x7272727a), SkBits2Float(0x385e7272)); // 5.27954e-39f, 1.13998e-35f, 4.80216e+30f, 5.30355e-05f +path.quadTo(SkBits2Float(0x057200ff), SkBits2Float(0x25727272), SkBits2Float(0x7272727a), SkBits2Float(0x5b5e7272)); // 1.1379e-35f, 2.10289e-16f, 4.80216e+30f, 6.26133e+16f +path.cubicTo(SkBits2Float(0x03000074), SkBits2Float(0x4aff00e8), SkBits2Float(0x397d3972), SkBits2Float(0x01000400), SkBits2Float(0x72727a10), SkBits2Float(0x5e647272)); // 3.76163e-37f, 8.35596e+06f, 0.000241494f, 2.35128e-38f, 4.80275e+30f, 4.11534e+18f +path.quadTo(SkBits2Float(0x2b2d16f3), SkBits2Float(0x0039fc4d), SkBits2Float(0x68800000), SkBits2Float(0x0100fafa)); // 6.14938e-13f, 5.32513e-39f, 4.8357e+24f, 2.369e-38f +path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0x725b5e72), SkBits2Float(0x63720089)); // 1.73224e+35f, 4.80216e+30f, 4.34505e+30f, 4.46415e+21f +path.lineTo(SkBits2Float(0x63636363), SkBits2Float(0x63606363)); // 4.19457e+21f, 4.13923e+21f +path.lineTo(SkBits2Float(0x72720000), SkBits2Float(0xff725b5e)); // 4.7933e+30f, -3.22148e+38f +path.moveTo(SkBits2Float(0x72720572), SkBits2Float(0x5b5e2572)); // 4.79373e+30f, 6.25286e+16f +path.quadTo(SkBits2Float(0x05720089), SkBits2Float(0x25727272), SkBits2Float(0x72728c7a), SkBits2Float(0x5b5e7272)); // 1.13789e-35f, 2.10289e-16f, 4.80417e+30f, 6.26133e+16f +path.cubicTo(SkBits2Float(0x03000074), SkBits2Float(0x4aff00e8), SkBits2Float(0x397d3972), SkBits2Float(0x01000400), SkBits2Float(0x72727a10), SkBits2Float(0x5e827272)); // 3.76163e-37f, 8.35596e+06f, 0.000241494f, 2.35128e-38f, 4.80275e+30f, 4.69985e+18f +path.quadTo(SkBits2Float(0x97fc16f3), SkBits2Float(0x0039fc5b), SkBits2Float(0x00f6472e), SkBits2Float(0x01008000)); // -1.62909e-24f, 5.32515e-39f, 2.26171e-38f, 2.36017e-38f +path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0xf3db5e64), SkBits2Float(0x4a5afc16)); // 1.73224e+35f, 4.80216e+30f, -3.47604e+31f, 3.58785e+06f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename); +} + +static void fuzz763_43(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x5c386c3a), SkBits2Float(0x4e691a3e)); // 2.07642e+17f, 9.77703e+08f +path.cubicTo(SkBits2Float(0x6f69f9f5), SkBits2Float(0x18ff8791), SkBits2Float(0x2492263c), SkBits2Float(0xbc6fdb48), SkBits2Float(0xc2f82107), SkBits2Float(0x729a18e1)); // 7.24122e+28f, 6.60528e-24f, 6.33822e-17f, -0.0146397f, -124.065f, 6.10442e+30f +path.cubicTo(SkBits2Float(0x07d729d1), SkBits2Float(0xdea6db48), SkBits2Float(0xcd1dfb88), SkBits2Float(0x90826769), SkBits2Float(0x1c20e5a4), SkBits2Float(0xa4c3ba9b)); // 3.23742e-34f, -6.01164e+18f, -1.65657e+08f, -5.14353e-29f, 5.32364e-22f, -8.48839e-17f +path.moveTo(SkBits2Float(0xcc2084b7), SkBits2Float(0x19f68bdb)); // -4.20789e+07f, 2.54923e-23f +path.close(); +path.moveTo(SkBits2Float(0xcc2084b7), SkBits2Float(0x19f68bdb)); // -4.20789e+07f, 2.54923e-23f +path.cubicTo(SkBits2Float(0xdeea1d6e), SkBits2Float(0xc7774804), SkBits2Float(0x27cf0dcf), SkBits2Float(0x6ae8b99f), SkBits2Float(0x24ac3260), SkBits2Float(0x062fa93c)); // -8.43488e+18f, -63304, 5.7469e-15f, 1.40674e+26f, 7.46784e-17f, 3.30382e-35f +path.lineTo(SkBits2Float(0x438a0b9c), SkBits2Float(0x60a1d2c8)); // 276.091f, 9.32848e+19f +path.quadTo(SkBits2Float(0xe13fb902), SkBits2Float(0x07ee536f), SkBits2Float(0x971d8ac1), SkBits2Float(0x2f9f174b)); // -2.21041e+20f, 3.58593e-34f, -5.09046e-25f, 2.89385e-10f +path.lineTo(SkBits2Float(0x0f2cf5d8), SkBits2Float(0xe271654c)); // 8.5276e-30f, -1.11324e+21f +path.lineTo(SkBits2Float(0xe6cf24d2), SkBits2Float(0xd9537742)); // -4.89105e+23f, -3.72015e+15f +path.cubicTo(SkBits2Float(0x1aaaee04), SkBits2Float(0x9e3b804c), SkBits2Float(0x84cba87d), SkBits2Float(0x4e0e8ccc), SkBits2Float(0x2aec611a), SkBits2Float(0x7ae4b639)); // 7.06949e-23f, -9.92623e-21f, -4.78798e-36f, 5.97898e+08f, 4.19894e-13f, 5.9377e+35f +path.conicTo(SkBits2Float(0x73357921), SkBits2Float(0x6f163021), SkBits2Float(0x70ea542c), SkBits2Float(0xe008f404), SkBits2Float(0x1f6c5e52)); // 1.43778e+31f, 4.64809e+28f, 5.8017e+29f, -3.94741e+19f, 5.0053e-20f +path.lineTo(SkBits2Float(0xda45ad4e), SkBits2Float(0xedce4a04)); // -1.39103e+16f, -7.98042e+27f +path.lineTo(SkBits2Float(0xac0e45da), SkBits2Float(0x8f632841)); // -2.02182e-12f, -1.11997e-29f +path.lineTo(SkBits2Float(0xcc2084b7), SkBits2Float(0x19f68bdb)); // -4.20789e+07f, 2.54923e-23f +path.close(); +path.moveTo(SkBits2Float(0xcc2084b7), SkBits2Float(0x19f68bdb)); // -4.20789e+07f, 2.54923e-23f +path.quadTo(SkBits2Float(0xf35c4ad5), SkBits2Float(0x0692f251), SkBits2Float(0x69632126), SkBits2Float(0xb927af67)); // -1.74534e+31f, 5.52751e-35f, 1.71614e+25f, -0.000159917f +path.moveTo(SkBits2Float(0x6534bff9), SkBits2Float(0x434a9986)); // 5.3348e+22f, 202.6f +path.quadTo(SkBits2Float(0x37c603e5), SkBits2Float(0xa0683953), SkBits2Float(0x751915e4), SkBits2Float(0x831c911a)); // 2.36053e-05f, -1.96701e-19f, 1.94059e+32f, -4.60108e-37f +path.cubicTo(SkBits2Float(0xba4f10f1), SkBits2Float(0x5a7571df), SkBits2Float(0x4ec67459), SkBits2Float(0x33c58827), SkBits2Float(0x10b78ccb), SkBits2Float(0xedbd2748)); // -0.000789895f, 1.72716e+16f, 1.66476e+09f, 9.19829e-08f, 7.23977e-29f, -7.31752e+27f +path.cubicTo(SkBits2Float(0x6d06f06a), SkBits2Float(0xe30465cf), SkBits2Float(0xc5458fe7), SkBits2Float(0xca488dc4), SkBits2Float(0x38f9021c), SkBits2Float(0x3e8d58db)); // 2.6101e+27f, -2.44231e+21f, -3160.99f, -3.28587e+06f, 0.000118736f, 0.276069f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename); +} + +static void fuzz763_44(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x7c223bab), SkBits2Float(0x7cf35966)); // 3.36945e+36f, 1.01083e+37f +path.quadTo(SkBits2Float(0x00000000), SkBits2Float(0x7ccaca6d), SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 8.4236e+36f, 0, 0 +path.lineTo(SkBits2Float(0x7d7d7d7d), SkBits2Float(0x00000000)); // 2.10591e+37f, 0 +path.quadTo(SkBits2Float(0x7ccacab0), SkBits2Float(0x7d1817f4), SkBits2Float(0x7c223bab), SkBits2Float(0x7cf35966)); // 8.42364e+36f, 1.26354e+37f, 3.36945e+36f, 1.01083e+37f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x109d0000), SkBits2Float(0xff7bc000)); // 6.19256e-29f, -3.34633e+38f +path.conicTo(SkBits2Float(0x979797ed), SkBits2Float(0x3a214797), SkBits2Float(0x28aa217a), SkBits2Float(0x01007272), SkBits2Float(0x00000072)); // -9.7965e-25f, 0.000615233f, 1.88883e-14f, 2.3592e-38f, 1.59748e-43f +path.quadTo(SkBits2Float(0x72728302), SkBits2Float(0x8b727272), SkBits2Float(0x72727272), SkBits2Float(0xc00308f6)); // 4.80344e+30f, -4.66936e-32f, 4.80216e+30f, -2.04742f +path.conicTo(SkBits2Float(0x7f52753a), SkBits2Float(0x8072ffff), SkBits2Float(0x67af2103), SkBits2Float(0x7d2a6847), SkBits2Float(0x7d7d7d7d)); // 2.79747e+38f, -1.05611e-38f, 1.65405e+24f, 1.41569e+37f, 2.10591e+37f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 3, filename); +} + +static void fuzz763_45(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.cubicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x7a303030), SkBits2Float(0x7a303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 2.28705e+35f, 2.28705e+35f, 6.40969e-10f +path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x74303030), SkBits2Float(0x74303030), SkBits2Float(0x30303030), SkBits2Float(0x74303030)); // 6.40969e-10f, 5.58363e+31f, 5.58363e+31f, 6.40969e-10f, 5.58363e+31f +path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f +path.moveTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f +path.moveTo(SkBits2Float(0x77773030), SkBits2Float(0x30303030)); // 5.01357e+33f, 6.40969e-10f +path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x7a743030), SkBits2Float(0x74303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f, 3.16974e+35f, 5.58363e+31f, 6.40969e-10f +path.lineTo(SkBits2Float(0x77773030), SkBits2Float(0x30303030)); // 5.01357e+33f, 6.40969e-10f +path.close(); +path.moveTo(SkBits2Float(0x77773030), SkBits2Float(0x30303030)); // 5.01357e+33f, 6.40969e-10f +path.lineTo(SkBits2Float(0x7f303030), SkBits2Float(0x7a303030)); // 2.34194e+38f, 2.28705e+35f +path.conicTo(SkBits2Float(0x77303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0xf9303030), SkBits2Float(0x7a303030)); // 3.57352e+33f, 6.40969e-10f, 6.40969e-10f, -5.71764e+34f, 2.28705e+35f +path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f +path.quadTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f +path.quadTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f +path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f +path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x7a303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 2.28705e+35f, 6.40969e-10f +path.cubicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x7a303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 2.28705e+35f, 6.40969e-10f +path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f +path.moveTo(SkBits2Float(0x77303030), SkBits2Float(0xff303030)); // 3.57352e+33f, -2.34194e+38f +path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x7f773030), SkBits2Float(0x7a7a3030), SkBits2Float(0x7a303030)); // 6.40969e-10f, 6.40969e-10f, 3.2857e+38f, 3.24763e+35f, 2.28705e+35f +path.quadTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x77303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f, 3.57352e+33f, 6.40969e-10f +path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x7b303030), SkBits2Float(0x73303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f, 9.14822e+35f, 1.39591e+31f, 6.40969e-10f +path.quadTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x7a7a3030)); // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 3.24763e+35f + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 3, filename); +} + +static void fuzz763_46(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 + path.conicTo(SkBits2Float(0x44444444), SkBits2Float(0x44444444), SkBits2Float(0x44263030), SkBits2Float(0x44304430), SkBits2Float(0x4c444430)); // 785.067f, 785.067f, 664.753f, 705.065f, 5.145e+07f +path.moveTo(SkBits2Float(0x44444444), SkBits2Float(0x44444444)); // 785.067f, 785.067f +path.cubicTo(SkBits2Float(0x30303030), SkBits2Float(0x44444444), SkBits2Float(0x30303030), SkBits2Float(0x44444444), SkBits2Float(0x44444444), SkBits2Float(0x4444444c)); // 6.40969e-10f, 785.067f, 6.40969e-10f, 785.067f, 785.067f, 785.067f + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 3, filename); +} + +static void fuzz763_47(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.cubicTo(SkBits2Float(0x7272728e), SkBits2Float(0x52527272), SkBits2Float(0x2d555252), SkBits2Float(0x68556829), SkBits2Float(0x555b2d29), SkBits2Float(0x2a212a8c)); // 4.80217e+30f, 2.25966e+11f, 1.21259e-11f, 4.03114e+24f, 1.50617e+13f, 1.43144e-13f +path.conicTo(SkBits2Float(0x00296808), SkBits2Float(0x00000002), SkBits2Float(0x52525252), SkBits2Float(0x72007272), SkBits2Float(0x52527272)); // 3.80257e-39f, 2.8026e-45f, 2.25831e+11f, 2.54416e+30f, 2.25966e+11f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.close(); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0x2a212a8c), SkBits2Float(0x7272081f)); // 1.43144e-13f, 4.79393e+30f +path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x5974fa80), SkBits2Float(0x00747474), SkBits2Float(0x59585264)); // 4.80216e+30f, 4.30971e+15f, 1.06947e-38f, 3.80557e+15f +path.cubicTo(SkBits2Float(0x64007474), SkBits2Float(0x088c5852), SkBits2Float(0x80808021), SkBits2Float(0x8c808080), SkBits2Float(0x80802108), SkBits2Float(0x80808080)); // 9.4783e+21f, 8.44671e-34f, -1.18009e-38f, -1.97989e-31f, -1.17668e-38f, -1.1801e-38f +path.quadTo(SkBits2Float(0x80807d80), SkBits2Float(0x80808080), SkBits2Float(0xff7f0000), SkBits2Float(0x80808080)); // -1.18e-38f, -1.1801e-38f, -3.38953e+38f, -1.1801e-38f +path.quadTo(SkBits2Float(0x80808080), SkBits2Float(0x80808080), SkBits2Float(0xed842b00), SkBits2Float(0x7252ff6d)); // -1.1801e-38f, -1.1801e-38f, -5.113e+27f, 4.17924e+30f +path.quadTo(SkBits2Float(0x72577200), SkBits2Float(0x55525352), SkBits2Float(0x2a212a8c), SkBits2Float(0x7272081f)); // 4.26733e+30f, 1.44535e+13f, 1.43144e-13f, 4.79393e+30f +path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x6f740080), SkBits2Float(0x8c556874), SkBits2Float(0x2982ffff)); // 4.80216e+30f, 7.55149e+28f, -1.64404e-31f, 5.81757e-14f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_48(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0xed0081bc), SkBits2Float(0x1b2d8040)); // -2.48568e+27f, 1.43517e-22f +path.moveTo(SkBits2Float(0x74747403), SkBits2Float(0x29747474)); // 7.74703e+31f, 5.42799e-14f +path.close(); +path.moveTo(SkBits2Float(0x74747403), SkBits2Float(0x29747474)); // 7.74703e+31f, 5.42799e-14f +path.conicTo(SkBits2Float(0x662d5576), SkBits2Float(0x2d804066), SkBits2Float(0x8068291b), SkBits2Float(0x740315ff), SkBits2Float(0x74747474)); // 2.04636e+23f, 1.45805e-11f, -9.56564e-39f, 4.15428e+31f, 7.74708e+31f +path.cubicTo(SkBits2Float(0x762d0529), SkBits2Float(0x72525252), SkBits2Float(0x007b7272), SkBits2Float(0x525adada), SkBits2Float(0x52525252), SkBits2Float(0x52727252)); // 8.77316e+32f, 4.16585e+30f, 1.13368e-38f, 2.34994e+11f, 2.25831e+11f, 2.60325e+11f +path.lineTo(SkBits2Float(0x74747403), SkBits2Float(0x29747474)); // 7.74703e+31f, 5.42799e-14f +path.close(); +path.moveTo(SkBits2Float(0xa5252620), SkBits2Float(0x52b4adad)); // -1.43244e-16f, 3.88004e+11f +path.close(); +path.moveTo(SkBits2Float(0xa5252620), SkBits2Float(0x52b4adad)); // -1.43244e-16f, 3.88004e+11f +path.quadTo(SkBits2Float(0x72727270), SkBits2Float(0x52524872), SkBits2Float(0x72525252), SkBits2Float(0x72727272)); // 4.80216e+30f, 2.2579e+11f, 4.16585e+30f, 4.80216e+30f +path.quadTo(SkBits2Float(0x72727255), SkBits2Float(0x80406666), SkBits2Float(0x68291b2d), SkBits2Float(0x0315ff80)); // 4.80215e+30f, -5.91421e-39f, 3.19432e+24f, 4.40805e-37f +path.cubicTo(SkBits2Float(0x74747474), SkBits2Float(0x7b722974), SkBits2Float(0x5adada00), SkBits2Float(0x52525252), SkBits2Float(0x72720052), SkBits2Float(0x72727272)); // 7.74708e+31f, 1.25738e+36f, 3.08006e+16f, 2.25831e+11f, 4.79333e+30f, 4.80216e+30f +path.lineTo(SkBits2Float(0xa5252620), SkBits2Float(0x52b4adad)); // -1.43244e-16f, 3.88004e+11f +path.close(); +path.moveTo(SkBits2Float(0xa5252620), SkBits2Float(0x52b4adad)); // -1.43244e-16f, 3.88004e+11f +path.quadTo(SkBits2Float(0x72727227), SkBits2Float(0x72727272), SkBits2Float(0x74727272), SkBits2Float(0x55747421)); // 4.80214e+30f, 4.80216e+30f, 7.68345e+31f, 1.67987e+13f +path.lineTo(SkBits2Float(0xa5252620), SkBits2Float(0x52b4adad)); // -1.43244e-16f, 3.88004e+11f +path.close(); +path.moveTo(SkBits2Float(0x724b0000), SkBits2Float(0x00725f72)); // 4.02083e+30f, 1.05035e-38f +path.lineTo(SkBits2Float(0x52525252), SkBits2Float(0x72725252)); // 2.25831e+11f, 4.79967e+30f +path.quadTo(SkBits2Float(0x26727272), SkBits2Float(0x0303a525), SkBits2Float(0x52005c03), SkBits2Float(0x72525252)); // 8.41157e-16f, 3.8687e-37f, 1.37825e+11f, 4.16585e+30f +path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x1ff07255), SkBits2Float(0x2a8c5572), SkBits2Float(0x21082a21)); // 4.80216e+30f, 1.01833e-19f, 2.49283e-13f, 4.61343e-19f +path.lineTo(SkBits2Float(0x2a2a3a21), SkBits2Float(0x29212828)); // 1.51192e-13f, 3.5784e-14f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename); +} + +static void fuzz763_49(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x78303030), SkBits2Float(0x78787881), SkBits2Float(0x78787878), SkBits2Float(0x30303030)); // 6.40969e-10f, 1.42941e+34f, 2.01583e+34f, 2.01583e+34f, 6.40969e-10f +path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x78787878)); // 2.01583e+34f, 2.01583e+34f +path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x78787878)); // 2.01583e+34f, 2.01583e+34f +path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x78787878)); // 2.01583e+34f, 2.01583e+34f +path.quadTo(SkBits2Float(0x30303030), SkBits2Float(0x78787878), SkBits2Float(0x78787878), SkBits2Float(0x78787878)); // 6.40969e-10f, 2.01583e+34f, 2.01583e+34f, 2.01583e+34f +path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f +path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f +path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f +path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f +path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f +path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f +path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f +path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f +path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f +path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f +path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f +path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f +path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030)); // 6.40969e-10f, 6.40969e-10f +path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x7878788d)); // 2.01583e+34f, 2.01584e+34f +path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x30303030)); // 2.01583e+34f, 6.40969e-10f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 3, filename); +} + +static void fuzz763_50(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x70621ede), SkBits2Float(0x00000000)); // 2.79924e+29f, 0 +path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x74fc5b97), SkBits2Float(0x7d458fe4)); // 0, 0, 0, 1.59951e+32f, 1.64128e+37f +path.lineTo(SkBits2Float(0xefea1ffe), SkBits2Float(0x00000000)); // -1.44916e+29f, 0 +path.lineTo(SkBits2Float(0x70621ede), SkBits2Float(0x00000000)); // 2.79924e+29f, 0 +path.close(); +path.moveTo(SkBits2Float(0xefea1ffe), SkBits2Float(0x00000000)); // -1.44916e+29f, 0 +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0xefea1ffe), SkBits2Float(0x00000000)); // -1.44916e+29f, 0 +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 3, filename); +} + +static void fuzz763_51(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.quadTo(SkBits2Float(0x868b5aae), SkBits2Float(0x626c45ab), SkBits2Float(0xefea1ffe), SkBits2Float(0x0029fc76)); // -5.24192e-35f, 1.08961e+21f, -1.44916e+29f, 3.85582e-39f +path.moveTo(SkBits2Float(0xfacbff01), SkBits2Float(0x56fc5b97)); // -5.29604e+35f, 1.38735e+14f +path.cubicTo(SkBits2Float(0x7d4559c9), SkBits2Float(0xad801c39), SkBits2Float(0xfbe2091a), SkBits2Float(0x7268e394), SkBits2Float(0x7c800079), SkBits2Float(0xa1d75590)); // 1.63953e+37f, -1.45644e-11f, -2.34729e+36f, 4.61284e+30f, 5.31699e+36f, -1.45916e-18f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_52(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.quadTo(SkBits2Float(0x29ff4bae), SkBits2Float(0xa1d75590), SkBits2Float(0x9fd6f6c3), SkBits2Float(0x70621ede)); // 1.13374e-13f, -1.45916e-18f, -9.10408e-20f, 2.79924e+29f +path.quadTo(SkBits2Float(0x57a839d3), SkBits2Float(0x1a80d34b), SkBits2Float(0x0147a31b), SkBits2Float(0xff7fffff)); // 3.69933e+14f, 5.32809e-23f, 3.66675e-38f, -3.40282e+38f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.close(); +path.moveTo(SkBits2Float(0x1ab8e97c), SkBits2Float(0x94fbe3ef)); // 7.64778e-23f, -2.54344e-26f +path.conicTo(SkBits2Float(0x75757568), SkBits2Float(0x7575755e), SkBits2Float(0x75757575), SkBits2Float(0x75757575), SkBits2Float(0x75756575)); // 3.11156e+32f, 3.11156e+32f, 3.11156e+32f, 3.11156e+32f, 3.11077e+32f +path.lineTo(SkBits2Float(0x1ab8e97c), SkBits2Float(0x94fbe3ef)); // 7.64778e-23f, -2.54344e-26f +path.close(); +path.moveTo(SkBits2Float(0x1ab8e97c), SkBits2Float(0x94fbe3ef)); // 7.64778e-23f, -2.54344e-26f +path.conicTo(SkBits2Float(0x75757575), SkBits2Float(0x75757575), SkBits2Float(0x75757575), SkBits2Float(0x75917575), SkBits2Float(0x75757575)); // 3.11156e+32f, 3.11156e+32f, 3.11156e+32f, 3.68782e+32f, 3.11156e+32f +path.lineTo(SkBits2Float(0x1ab8e97c), SkBits2Float(0x94fbe3ef)); // 7.64778e-23f, -2.54344e-26f +path.close(); +path.moveTo(SkBits2Float(0x1ab8e97c), SkBits2Float(0x94fbe3ef)); // 7.64778e-23f, -2.54344e-26f +path.conicTo(SkBits2Float(0x75757575), SkBits2Float(0x7575758f), SkBits2Float(0x7f757575), SkBits2Float(0x75757575), SkBits2Float(0x75757575)); // 3.11156e+32f, 3.11157e+32f, 3.26271e+38f, 3.11156e+32f, 3.11156e+32f +path.lineTo(SkBits2Float(0x1ab8e97c), SkBits2Float(0x94fbe3ef)); // 7.64778e-23f, -2.54344e-26f +path.close(); + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzz763_53(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x7644b829), SkBits2Float(0x00000000)); // 9.97486e+32f, 0 +path.lineTo(SkBits2Float(0x74fc5b97), SkBits2Float(0x77df944a)); // 1.59951e+32f, 9.06945e+33f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xf8fbe3ff)); // 0, -4.08716e+34f +path.lineTo(SkBits2Float(0x7644b829), SkBits2Float(0x00000000)); // 9.97486e+32f, 0 +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.quadTo(SkBits2Float(0x45ab86ae), SkBits2Float(0xd6d6626c), SkBits2Float(0xd6d6d6d6), SkBits2Float(0x7644d6d6)); // 5488.83f, -1.17859e+14f, -1.18109e+14f, 9.98093e+32f +path.moveTo(SkBits2Float(0xd6d6d6d6), SkBits2Float(0xd6d6d6d6)); // -1.18109e+14f, -1.18109e+14f +path.cubicTo(SkBits2Float(0xd6d6d6d6), SkBits2Float(0x64fed6d6), SkBits2Float(0x7644ef40), SkBits2Float(0x290877fc), SkBits2Float(0x447644b8), SkBits2Float(0x80fafc76)); // -1.18109e+14f, 3.76076e+22f, 9.98577e+32f, 3.03021e-14f, 985.074f, -2.30494e-38f +path.conicTo(SkBits2Float(0x87808080), SkBits2Float(0x764400ae), SkBits2Float(0x764400fc), SkBits2Float(0x450080fc), SkBits2Float(0x3636366c)); // -1.93348e-34f, 9.93852e+32f, 9.93858e+32f, 2056.06f, 2.71518e-06f +path.lineTo(SkBits2Float(0xd6d6d6d6), SkBits2Float(0xd6d6d6d6)); // -1.18109e+14f, -1.18109e+14f +path.close(); +path.moveTo(SkBits2Float(0xef08a412), SkBits2Float(0x5aaeff7f)); // -4.22883e+28f, 2.46288e+16f +path.conicTo(SkBits2Float(0x7644626c), SkBits2Float(0x088912fc), SkBits2Float(0xae8744ef), SkBits2Float(0x76571f5a), SkBits2Float(0x45ab86fc)); // 9.95788e+32f, 8.24985e-34f, -6.15133e-11f, 1.0908e+33f, 5488.87f +path.conicTo(SkBits2Float(0x4064fe62), SkBits2Float(0x290877ef), SkBits2Float(0x780080b8), SkBits2Float(0x553c7644), SkBits2Float(0x644eae87)); // 3.57803f, 3.03021e-14f, 1.04254e+34f, 1.2951e+13f, 1.52504e+22f +path.lineTo(SkBits2Float(0xef08a412), SkBits2Float(0x5aaeff7f)); // -4.22883e+28f, 2.46288e+16f +path.close(); + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +// hangs 654939 +static void fuzz763_54(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.conicTo(SkBits2Float(0x5b682968), SkBits2Float(0xb3b32d11), SkBits2Float(0xb3b3b3b3), SkBits2Float(0x5b29b3b3), SkBits2Float(0x212a8c55)); // 6.53477e+16f, -8.34353e-08f, -8.36802e-08f, 4.77669e+16f, 5.7784e-19f +path.conicTo(SkBits2Float(0x68555b2d), SkBits2Float(0x28296869), SkBits2Float(0x5b252a08), SkBits2Float(0x5d68392a), SkBits2Float(0x29282780)); // 4.03018e+24f, 9.40402e-15f, 4.64896e+16f, 1.04584e+18f, 3.73378e-14f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.close(); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.cubicTo(SkBits2Float(0x52727272), SkBits2Float(0x72727252), SkBits2Float(0x525252c7), SkBits2Float(0x72725252), SkBits2Float(0x72727272), SkBits2Float(0x72727255)); // 2.60326e+11f, 4.80215e+30f, 2.25833e+11f, 4.79967e+30f, 4.80216e+30f, 4.80215e+30f +path.quadTo(SkBits2Float(0xd7da0000), SkBits2Float(0x5252525a), SkBits2Float(0x72525252), SkBits2Float(0x72727272)); // -4.79387e+14f, 2.25831e+11f, 4.16585e+30f, 4.80216e+30f +path.quadTo(SkBits2Float(0x48525252), SkBits2Float(0x72725252), SkBits2Float(0x72727272), SkBits2Float(0x72727255)); // 215369, 4.79967e+30f, 4.80216e+30f, 4.80215e+30f +path.quadTo(SkBits2Float(0xdada007b), SkBits2Float(0x5252525a), SkBits2Float(0x72675252), SkBits2Float(0x72727272)); // -3.0681e+16f, 2.25831e+11f, 4.5818e+30f, 4.80216e+30f +path.quadTo(SkBits2Float(0x52525252), SkBits2Float(0x27725252), SkBits2Float(0x72727272), SkBits2Float(0x72727272)); // 2.25831e+11f, 3.36289e-15f, 4.80216e+30f, 4.80216e+30f +path.quadTo(SkBits2Float(0x1c292172), SkBits2Float(0x7bc00321), SkBits2Float(0x9aaaaaaa), SkBits2Float(0x8c556a4b)); // 5.59606e-22f, 1.99397e+36f, -7.05861e-23f, -1.64409e-31f +path.quadTo(SkBits2Float(0x72725572), SkBits2Float(0x00007272), SkBits2Float(0x525adada), SkBits2Float(0x52525252)); // 4.79991e+30f, 4.10552e-41f, 2.34994e+11f, 2.25831e+11f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.close(); +path.moveTo(SkBits2Float(0xa5252600), SkBits2Float(0x52b4adad)); // -1.43243e-16f, 3.88004e+11f +path.close(); +path.moveTo(SkBits2Float(0xa5252600), SkBits2Float(0x52b4adad)); // -1.43243e-16f, 3.88004e+11f +path.quadTo(SkBits2Float(0x72725570), SkBits2Float(0x52525272), SkBits2Float(0x72525252), SkBits2Float(0x72727272)); // 4.79991e+30f, 2.25832e+11f, 4.16585e+30f, 4.80216e+30f +path.quadTo(SkBits2Float(0x72727255), SkBits2Float(0x555bb672), SkBits2Float(0x29686968), SkBits2Float(0x252a081f)); // 4.80215e+30f, 1.50985e+13f, 5.16058e-14f, 1.47479e-16f +path.moveTo(SkBits2Float(0x5d68392a), SkBits2Float(0x01002780)); // 1.04584e+18f, 2.35382e-38f +path.moveTo(SkBits2Float(0x72727200), SkBits2Float(0x72725252)); // 4.80212e+30f, 4.79967e+30f +path.quadTo(SkBits2Float(0x5adada00), SkBits2Float(0xa5252652), SkBits2Float(0x727272ad), SkBits2Float(0xda007b72)); // 3.08006e+16f, -1.43245e-16f, 4.80218e+30f, -9.04113e+15f +path.lineTo(SkBits2Float(0x5252525a), SkBits2Float(0x72525252)); // 2.25831e+11f, 4.16585e+30f +path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x52525252), SkBits2Float(0x27725252), SkBits2Float(0x72727272)); // 4.80216e+30f, 2.25831e+11f, 3.36289e-15f, 4.80216e+30f +path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x74217472), SkBits2Float(0x005b5574), SkBits2Float(0x72680000)); // 4.80216e+30f, 5.11671e+31f, 8.38768e-39f, 4.59523e+30f +path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x52525252), SkBits2Float(0x007b7272), SkBits2Float(0x525adada)); // 4.80216e+30f, 2.25831e+11f, 1.13368e-38f, 2.34994e+11f +path.lineTo(SkBits2Float(0x72727200), SkBits2Float(0x72725252)); // 4.80212e+30f, 4.79967e+30f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename); +} + + +// afl crash +static void fuzz763_55(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x55415500)); // 0, 1.32857e+13f +path.lineTo(SkBits2Float(0x55555568), SkBits2Float(0x55555555)); // 1.46602e+13f, 1.46602e+13f +path.lineTo(SkBits2Float(0x98989898), SkBits2Float(0x55989898)); // -3.94452e-24f, 2.09726e+13f +path.lineTo(SkBits2Float(0xf6f65555), SkBits2Float(0x101006f6)); // -2.49812e+33f, 2.84044e-29f +path.quadTo(SkBits2Float(0xdca33f10), SkBits2Float(0xf6f6f6f6), SkBits2Float(0xf621f6f6), SkBits2Float(0xf70ff6f6)); // -3.67598e+17f, -2.50452e+33f, -8.21259e+32f, -2.91995e+33f +path.lineTo(SkBits2Float(0x9400f6f6), SkBits2Float(0x10530000)); // -6.51105e-27f, 4.16124e-29f +path.quadTo(SkBits2Float(0x0f101010), SkBits2Float(0x00101010), SkBits2Float(0xf610f720), SkBits2Float(0xf6f6f6f6)); // 7.10284e-30f, 1.47513e-39f, -7.35062e+32f, -2.50452e+33f +path.lineTo(SkBits2Float(0x105352f6), SkBits2Float(0x1cf6ff10)); // 4.16763e-29f, 1.63448e-21f +path.lineTo(SkBits2Float(0xf6f6220a), SkBits2Float(0x003700f6)); // -2.49608e+33f, 5.0513e-39f +path.cubicTo(SkBits2Float(0x0000001e), SkBits2Float(0x00fff4f6), SkBits2Float(0xff101064), SkBits2Float(0xf6b6ac7f), SkBits2Float(0xf6f629f6), SkBits2Float(0x10f6f6f6)); // 4.2039e-44f, 2.35059e-38f, -1.91494e+38f, -1.85253e+33f, -2.4964e+33f, 9.74104e-29f +path.quadTo(SkBits2Float(0x10101007), SkBits2Float(0x10f7fd10), SkBits2Float(0xf6f6f6f6), SkBits2Float(0xf6f645e0)); // 2.84113e-29f, 9.78142e-29f, -2.50452e+33f, -2.4975e+33f +path.lineTo(SkBits2Float(0xed9ef6f6), SkBits2Float(0x53535353)); // -6.14965e+27f, 9.07636e+11f +path.lineTo(SkBits2Float(0x53006cf6), SkBits2Float(0x53295353)); // 5.51584e+11f, 7.27247e+11f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x55415500)); // 0, 1.32857e+13f +path.close(); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x55415500)); // 0, 1.32857e+13f +path.lineTo(SkBits2Float(0xf6f6f6f6), SkBits2Float(0x5353d9f6)); // -2.50452e+33f, 9.09895e+11f + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 3, filename); +} + +// 656149 +static void fuzz763_56(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.conicTo(SkBits2Float(0x5b682968), SkBits2Float(0xb3b32d11), SkBits2Float(0xb3b3b3b3), SkBits2Float(0x5b29b3b3), SkBits2Float(0x72725255)); // 6.53477e+16f, -8.34353e-08f, -8.36802e-08f, 4.77669e+16f, 4.79967e+30f +path.quadTo(SkBits2Float(0x525252c7), SkBits2Float(0x72725252), SkBits2Float(0x72727272), SkBits2Float(0x72727255)); // 2.25833e+11f, 4.79967e+30f, 4.80216e+30f, 4.80215e+30f +path.quadTo(SkBits2Float(0xd7da0000), SkBits2Float(0x5adada00), SkBits2Float(0x52525252), SkBits2Float(0x00005252)); // -4.79387e+14f, 3.08006e+16f, 2.25831e+11f, 2.9531e-41f +path.conicTo(SkBits2Float(0xadada525), SkBits2Float(0x52525ab4), SkBits2Float(0x52525252), SkBits2Float(0x72727272), SkBits2Float(0x52527272)); // -1.97412e-11f, 2.25866e+11f, 2.25831e+11f, 4.80216e+30f, 2.25966e+11f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.close(); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.quadTo(SkBits2Float(0x72725252), SkBits2Float(0x72727272), SkBits2Float(0x72727255), SkBits2Float(0xda007b72)); // 4.79967e+30f, 4.80216e+30f, 4.80215e+30f, -9.04113e+15f +path.lineTo(SkBits2Float(0x5252525a), SkBits2Float(0x72525252)); // 2.25831e+11f, 4.16585e+30f +path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x52525252), SkBits2Float(0x27725252), SkBits2Float(0x72727272)); // 4.80216e+30f, 2.25831e+11f, 3.36289e-15f, 4.80216e+30f +path.lineTo(SkBits2Float(0x7bc00321), SkBits2Float(0x9aaaaaaa)); // 1.99397e+36f, -7.05861e-23f +path.quadTo(SkBits2Float(0x72725572), SkBits2Float(0x00007272), SkBits2Float(0x525adada), SkBits2Float(0x52525252)); // 4.79991e+30f, 4.10552e-41f, 2.34994e+11f, 2.25831e+11f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.close(); +path.moveTo(SkBits2Float(0xa5252600), SkBits2Float(0x52b4adad)); // -1.43243e-16f, 3.88004e+11f +path.close(); +path.moveTo(SkBits2Float(0xa5252600), SkBits2Float(0x52b4adad)); // -1.43243e-16f, 3.88004e+11f +path.quadTo(SkBits2Float(0x72727270), SkBits2Float(0x52525272), SkBits2Float(0x72525252), SkBits2Float(0x72727272)); // 4.80216e+30f, 2.25832e+11f, 4.16585e+30f, 4.80216e+30f +path.quadTo(SkBits2Float(0x72727255), SkBits2Float(0xda007b72), SkBits2Float(0x26525ada), SkBits2Float(0x72ada525)); // 4.80215e+30f, -9.04113e+15f, 7.29815e-16f, 6.87879e+30f +path.quadTo(SkBits2Float(0x007b7272), SkBits2Float(0x525adada), SkBits2Float(0x52525252), SkBits2Float(0x72727252)); // 1.13368e-38f, 2.34994e+11f, 2.25831e+11f, 4.80215e+30f +path.quadTo(SkBits2Float(0x52527272), SkBits2Float(0x52525252), SkBits2Float(0x72722772), SkBits2Float(0x72727272)); // 2.25966e+11f, 2.25831e+11f, 4.79636e+30f, 4.80216e+30f +path.quadTo(SkBits2Float(0x74727272), SkBits2Float(0x55747421), SkBits2Float(0x0000005b), SkBits2Float(0x72727268)); // 7.68345e+31f, 1.67987e+13f, 1.27518e-43f, 4.80216e+30f +path.quadTo(SkBits2Float(0x52527272), SkBits2Float(0x52525252), SkBits2Float(0x72727272), SkBits2Float(0x72557272)); // 2.25966e+11f, 2.25831e+11f, 4.80216e+30f, 4.22775e+30f +path.quadTo(SkBits2Float(0x5adada72), SkBits2Float(0x52525252), SkBits2Float(0x72725252), SkBits2Float(0x72727272)); // 3.08009e+16f, 2.25831e+11f, 4.79967e+30f, 4.80216e+30f + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void fuzz763_57(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x68546829), SkBits2Float(0x555b2d29)); // 4.01225e+24f, 1.50617e+13f +path.moveTo(SkBits2Float(0x1f2a322a), SkBits2Float(0x4b7b2108)); // 3.60404e-20f, 1.6458e+07f +path.lineTo(SkBits2Float(0x2829ed84), SkBits2Float(0x5b2d2d55)); // 9.43289e-15f, 4.8745e+16f +path.moveTo(SkBits2Float(0x6838552d), SkBits2Float(0xf0684f5b)); // 3.48195e+24f, -2.87586e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x2a8cef2a), SkBits2Float(0x682d2953), SkBits2Float(0xce682103), SkBits2Float(0x4b7bc055)); // 5.76397e-19f, 2.50349e-13f, 3.27093e+24f, -9.73619e+08f, 1.64988e+07f +path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x3b2a8c55)); // 4.85282e+16f, 0.00260236f +path.lineTo(SkBits2Float(0x6838552d), SkBits2Float(0xf0684f5b)); // 3.48195e+24f, -2.87586e+29f +path.close(); +path.moveTo(SkBits2Float(0x6838552d), SkBits2Float(0xf0684f5b)); // 3.48195e+24f, -2.87586e+29f +path.conicTo(SkBits2Float(0xd2c00321), SkBits2Float(0xc0394b7b), SkBits2Float(0x8c08ed7a), SkBits2Float(0x211f2f2a), SkBits2Float(0x704b7b03)); // -4.12343e+11f, -2.89523f, -1.05485e-31f, 5.39337e-19f, 2.51897e+29f +path.cubicTo(SkBits2Float(0x2d6829ed), SkBits2Float(0x5b2d555b), SkBits2Float(0x68275b2d), SkBits2Float(0x21685527), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0)); // 1.3197e-11f, 4.8789e+16f, 3.16127e+24f, 7.87174e-19f, 4.7323e-37f, 6.14991e+25f +path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x5b2d2d55)); // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 4.8745e+16f +path.moveTo(SkBits2Float(0x6839552d), SkBits2Float(0xf0683b5b)); // 3.50084e+24f, -2.87489e+29f +path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x228cef2a), SkBits2Float(0x682d2953), SkBits2Float(0xee682103), SkBits2Float(0x287bc055)); // 5.76397e-19f, 3.82003e-18f, 3.27093e+24f, -1.79601e+28f, 1.3975e-14f +path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55)); // 4.85282e+16f, 5.7784e-19f +path.conicTo(SkBits2Float(0x4b03213b), SkBits2Float(0xc07b2a08), SkBits2Float(0x5b2d7a6a), SkBits2Float(0xf0556830), SkBits2Float(0x2a8c555b)); // 8.59372e+06f, -3.92444f, 4.88298e+16f, -2.64185e+29f, 2.49282e-13f +path.conicTo(SkBits2Float(0x0321212a), SkBits2Float(0x4b7bd2c0), SkBits2Float(0xed7ac039), SkBits2Float(0x2f2a8c08), SkBits2Float(0x7b03211f)); // 4.73517e-37f, 1.65035e+07f, -4.85023e+27f, 1.55112e-10f, 6.80863e+35f +path.lineTo(SkBits2Float(0x6839552d), SkBits2Float(0xf0683b5b)); // 3.50084e+24f, -2.87489e+29f +path.close(); +path.moveTo(SkBits2Float(0x6839552d), SkBits2Float(0xf0683b5b)); // 3.50084e+24f, -2.87489e+29f +path.lineTo(SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d)); // 3.20982e+24f, 1.21279e-11f +path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.conicTo(SkBits2Float(0x721f2a5b), SkBits2Float(0x212a8c55), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7b28), SkBits2Float(0x4797ed7a)); // 3.1526e+30f, 5.7784e-19f, 4.7323e-37f, 6.14984e+25f, 77787 +path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.quadTo(SkBits2Float(0x2828102a), SkBits2Float(0x2c682921), SkBits2Float(0x8c555bf6), SkBits2Float(0x6d03de30)); // 9.32938e-15f, 3.2992e-12f, -1.64366e-31f, 2.5507e+27f +path.cubicTo(SkBits2Float(0x683f2d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x211f2a21)); // 3.61123e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 5.39271e-19f +path.lineTo(SkBits2Float(0x3a803adf), SkBits2Float(0x8a294f1a)); // 0.000978317f, -8.15193e-33f +path.quadTo(SkBits2Float(0x291d9628), SkBits2Float(0x2a43e62b), SkBits2Float(0x093a2a81), SkBits2Float(0x5c5c8ced)); // 3.49912e-14f, 1.73993e-13f, 2.24089e-33f, 2.48318e+17f +path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.close(); +path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527)); // 3.16127e+24f, -2.87614e+29f +path.cubicTo(SkBits2Float(0x3ac2213a), SkBits2Float(0x291d9628), SkBits2Float(0x2a43e62b), SkBits2Float(0x293a2a81), SkBits2Float(0x5c5c8ced), SkBits2Float(0x5c5c6e5c)); // 0.00148109f, 3.49912e-14f, 1.73993e-13f, 4.13372e-14f, 2.48318e+17f, 2.48183e+17f +path.lineTo(SkBits2Float(0x1f212a8c), SkBits2Float(0xc0032108)); // 3.41283e-20f, -2.04889f +path.lineTo(SkBits2Float(0xed847b4b), SkBits2Float(0x2d552829)); // -5.12513e+27f, 1.21166e-11f +path.conicTo(SkBits2Float(0x552d5b5b), SkBits2Float(0x3b5a6839), SkBits2Float(0x5b2df068), SkBits2Float(0x2a212a1f), SkBits2Float(0x532a8cef)); // 1.1913e+13f, 0.00333263f, 4.89595e+16f, 1.43143e-13f, 7.32509e+11f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void fuzzhang_1(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x668ece09), SkBits2Float(0x00000000), SkBits2Float(0x6751c81a), SkBits2Float(0x61c4b0fb)); // 0, 0, 3.37188e+23f, 0, 9.90666e+23f, 4.53539e+20f +path.conicTo(SkBits2Float(0x66f837a9), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x3f823406)); // 5.86087e+23f, 0, 0, 0, 1.01721f +path.close(); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.quadTo(SkBits2Float(0x675b1bfe), SkBits2Float(0x00000000), SkBits2Float(0x67d76c42), SkBits2Float(0x6292c469)); // 1.03471e+24f, 0, 2.03461e+24f, 1.35369e+21f +path.cubicTo(SkBits2Float(0x6a16df68), SkBits2Float(0x651a2f15), SkBits2Float(0x6c1e7f31), SkBits2Float(0x67a1f9b4), SkBits2Float(0x00000000), SkBits2Float(0x6a2a291f)); // 4.55985e+25f, 4.55071e+22f, 7.66444e+26f, 1.52981e+24f, 0, 5.14279e+25f +path.conicTo(SkBits2Float(0x680dcb75), SkBits2Float(0x68dd898d), SkBits2Float(0x681a434a), SkBits2Float(0x6871046b), SkBits2Float(0x3fea0440)); // 2.67843e+24f, 8.36944e+24f, 2.91394e+24f, 4.55269e+24f, 1.82825f +path.quadTo(SkBits2Float(0x679e1b26), SkBits2Float(0x687703c4), SkBits2Float(0x00000000), SkBits2Float(0x687d2968)); // 1.49327e+24f, 4.66598e+24f, 0, 4.78209e+24f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.cubicTo(SkBits2Float(0x535353ec), SkBits2Float(0x98989898), SkBits2Float(0x98989898), SkBits2Float(0xf207f36e), SkBits2Float(0xf3f2f2f2), SkBits2Float(0xed3a9781)); // 9.07646e+11f, -3.94452e-24f, -3.94452e-24f, -2.69278e+30f, -3.84968e+31f, -3.60921e+27f +path.quadTo(SkBits2Float(0xf8f8c0ed), SkBits2Float(0xf8f8f8f8), SkBits2Float(0x9f9f9f9f), SkBits2Float(0x3014149f)); // -4.03626e+34f, -4.03981e+34f, -6.76032e-20f, 5.38714e-10f + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 0, filename); +} + +static void release_13(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.setFillType(SkPath::kEvenOdd_FillType); +path.moveTo(SkBits2Float(0xd4438848), SkBits2Float(0xd488cf64)); // -3.35922e+12f, -4.70076e+12f +path.lineTo(SkBits2Float(0xd43a056e), SkBits2Float(0xd4851696)); // -3.19582e+12f, -4.57288e+12f +path.quadTo(SkBits2Float(0xd3d48e79), SkBits2Float(0xd49fb136), SkBits2Float(0x00000000), SkBits2Float(0xd4d4d4d4)); // -1.82585e+12f, -5.48698e+12f, 0, -7.31283e+12f +path.quadTo(SkBits2Float(0xd3d06670), SkBits2Float(0xd4a0bb38), SkBits2Float(0xd41d628f), SkBits2Float(0xd472c531)); // -1.79014e+12f, -5.52269e+12f, -2.70385e+12f, -4.17076e+12f +path.lineTo(SkBits2Float(0xd43a0559), SkBits2Float(0xd485168e)); // -3.19581e+12f, -4.57287e+12f +path.lineTo(SkBits2Float(0xd446958b), SkBits2Float(0xd4810278)); // -3.41165e+12f, -4.43274e+12f +path.lineTo(SkBits2Float(0xd443884a), SkBits2Float(0xd488cf65)); // -3.35922e+12f, -4.70076e+12f +path.quadTo(SkBits2Float(0xd47efa09), SkBits2Float(0xd49fd72a), SkBits2Float(0xd4a63f0f), SkBits2Float(0xd4b83ab3)); // -4.38047e+12f, -5.49208e+12f, -5.71218e+12f, -6.33007e+12f +path.lineTo(SkBits2Float(0xd497ca70), SkBits2Float(0xd4c4d4ae)); // -5.21549e+12f, -6.76305e+12f +path.lineTo(SkBits2Float(0xd459d4d4), SkBits2Float(0xd4c4d4d4)); // -3.74231e+12f, -6.76307e+12f +path.lineTo(SkBits2Float(0xd440daf9), SkBits2Float(0xd4c632d3)); // -3.31323e+12f, -6.81005e+12f +path.lineTo(SkBits2Float(0xd4438848), SkBits2Float(0xd488cf64)); // -3.35922e+12f, -4.70076e+12f +path.close(); +path.moveTo(SkBits2Float(0xd4767560), SkBits2Float(0xd4d1ca84)); // -4.23412e+12f, -7.20837e+12f +path.lineTo(SkBits2Float(0xd4422174), SkBits2Float(0xd4d02069)); // -3.33514e+12f, -7.15118e+12f +path.lineTo(SkBits2Float(0xd440daa3), SkBits2Float(0xd4c632d9)); // -3.31321e+12f, -6.81005e+12f +path.lineTo(SkBits2Float(0xd41017bc), SkBits2Float(0xd4cb99b6)); // -2.47549e+12f, -6.99566e+12f +path.lineTo(SkBits2Float(0xd442213b), SkBits2Float(0xd4d02067)); // -3.33512e+12f, -7.15117e+12f +path.lineTo(SkBits2Float(0xd442d4d4), SkBits2Float(0xd4d4d4d4)); // -3.34718e+12f, -7.31283e+12f +path.lineTo(SkBits2Float(0xd4767560), SkBits2Float(0xd4d1ca84)); // -4.23412e+12f, -7.20837e+12f +path.close(); +path.moveTo(SkBits2Float(0xd46c7a11), SkBits2Float(0xd46c7a2e)); // -4.06264e+12f, -4.06265e+12f +path.lineTo(SkBits2Float(0xd484e02c), SkBits2Float(0xd45fafcd)); // -4.56557e+12f, -3.84291e+12f +path.lineTo(SkBits2Float(0xd462c867), SkBits2Float(0xd45655f7)); // -3.8961e+12f, -3.68226e+12f +path.lineTo(SkBits2Float(0xd45ac463), SkBits2Float(0xd45ac505)); // -3.75839e+12f, -3.75843e+12f +path.lineTo(SkBits2Float(0xd43d2fa9), SkBits2Float(0xd43d2fb5)); // -3.25019e+12f, -3.2502e+12f +path.lineTo(SkBits2Float(0xd41d6287), SkBits2Float(0xd472c52a)); // -2.70385e+12f, -4.17076e+12f +path.quadTo(SkBits2Float(0x00000000), SkBits2Float(0xd3db1b95), SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, -1.88212e+12f, 0, 0 +path.quadTo(SkBits2Float(0xd4b7efac), SkBits2Float(0x00000000), SkBits2Float(0xd4d0e88f), SkBits2Float(0xd40b8b46)); // -6.32e+12f, 0, -7.17804e+12f, -2.39735e+12f +path.lineTo(SkBits2Float(0xd4d4d4d4), SkBits2Float(0x00000000)); // -7.31283e+12f, 0 +path.lineTo(SkBits2Float(0xdcdc154b), SkBits2Float(0x00000000)); // -4.95583e+17f, 0 +path.lineTo(SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4c4d477)); // -7.31283e+12f, -6.76303e+12f +path.lineTo(SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d442)); // -7.31283e+12f, -7.31275e+12f +path.lineTo(SkBits2Float(0xd4d4a691), SkBits2Float(0xd4d4d442)); // -7.30662e+12f, -7.31275e+12f +path.lineTo(SkBits2Float(0xd454d4d4), SkBits2Float(0xd4d4aa30)); // -3.65641e+12f, -7.30711e+12f +path.lineTo(SkBits2Float(0xd4bd9def), SkBits2Float(0xd4d43df0)); // -6.51519e+12f, -7.29258e+12f +path.lineTo(SkBits2Float(0xd4767560), SkBits2Float(0xd4d1ca84)); // -4.23412e+12f, -7.20837e+12f +path.lineTo(SkBits2Float(0xd497ca70), SkBits2Float(0xd4c4d4ae)); // -5.21549e+12f, -6.76305e+12f +path.lineTo(SkBits2Float(0xd4bab953), SkBits2Float(0xd4c4d48e)); // -6.41579e+12f, -6.76304e+12f +path.lineTo(SkBits2Float(0xd4a63f0f), SkBits2Float(0xd4b83ab3)); // -5.71218e+12f, -6.33007e+12f +path.lineTo(SkBits2Float(0xd4ae61eb), SkBits2Float(0xd4ae61f4)); // -5.99174e+12f, -5.99174e+12f +path.lineTo(SkBits2Float(0xd46c7a11), SkBits2Float(0xd46c7a2e)); // -4.06264e+12f, -4.06265e+12f +path.close(); +path.moveTo(SkBits2Float(0xd46c7a11), SkBits2Float(0xd46c7a2e)); // -4.06264e+12f, -4.06265e+12f +path.lineTo(SkBits2Float(0xd446965c), SkBits2Float(0xd4810237)); // -3.4117e+12f, -4.4327e+12f +path.lineTo(SkBits2Float(0xd45ac549), SkBits2Float(0xd45ac55f)); // -3.75845e+12f, -3.75846e+12f +path.lineTo(SkBits2Float(0xd46c7a11), SkBits2Float(0xd46c7a2e)); // -4.06264e+12f, -4.06265e+12f +path.close(); +path.moveTo(SkBits2Float(0xd4b46028), SkBits2Float(0xd41e572a)); // -6.19766e+12f, -2.72027e+12f +path.lineTo(SkBits2Float(0xd4cde20a), SkBits2Float(0xd434bb57)); // -7.07408e+12f, -3.10495e+12f +path.lineTo(SkBits2Float(0xd4c75ffe), SkBits2Float(0xd46f215d)); // -6.85047e+12f, -4.10823e+12f +path.lineTo(SkBits2Float(0xd4b46028), SkBits2Float(0xd41e572a)); // -6.19766e+12f, -2.72027e+12f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.setFillType(SkPath::kWinding_FillType); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.quadTo(SkBits2Float(0x00000000), SkBits2Float(0xa5a50000), SkBits2Float(0xd4d4a5a5), SkBits2Float(0xd4d4d4d4)); // 0, -2.86229e-16f, -7.3065e+12f, -7.31283e+12f +path.quadTo(SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4cfd4d4), SkBits2Float(0xd4d41dd4)); // -7.31283e+12f, -7.31283e+12f, -7.14103e+12f, -7.28827e+12f +path.quadTo(SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d432d4), SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4a5a5d4)); // -7.31283e+12f, -7.29109e+12f, -7.31283e+12f, -5.69161e+12f +path.quadTo(SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d4d4), SkBits2Float(0x00000000)); // -7.31283e+12f, -7.31283e+12f, -7.31283e+12f, 0 +path.moveTo(SkBits2Float(0xa5a5a500), SkBits2Float(0xd4d4d4a5)); // -2.87347e-16f, -7.31281e+12f +path.quadTo(SkBits2Float(0xd4d4d4d4), SkBits2Float(0x2ad4d4d4), SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4cfd4d4)); // -7.31283e+12f, 3.78064e-13f, -7.31283e+12f, -7.14103e+12f +path.quadTo(SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d4d4)); // -7.31283e+12f, -7.31283e+12f, -7.31283e+12f, -7.31283e+12f +path.quadTo(SkBits2Float(0xd4d40000), SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d4d4)); // -7.28426e+12f, -7.31283e+12f, -7.31283e+12f, -7.31283e+12f + + SkPath path2(path); + testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void fuzzhang_2(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.setFillType(SkPath::kWinding_FillType); +path.moveTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268)); // 1.59583e+13f, 5.20715e+30f +path.quadTo(SkBits2Float(0xe0e02972), SkBits2Float(0xe0e060e0), SkBits2Float(0x728e4603), SkBits2Float(0x72727272)); // -1.29221e+20f, -1.29345e+20f, 5.63603e+30f, 4.80216e+30f +path.lineTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268)); // 1.59583e+13f, 5.20715e+30f +path.close(); +path.moveTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268)); // 1.59583e+13f, 5.20715e+30f +path.quadTo(SkBits2Float(0x68720052), SkBits2Float(0x52527372), SkBits2Float(0x00527252), SkBits2Float(0x728e4601)); // 4.57127e+24f, 2.2597e+11f, 7.57152e-39f, 5.63603e+30f +path.quadTo(SkBits2Float(0x52ec7272), SkBits2Float(0x6265527f), SkBits2Float(0x8e460152), SkBits2Float(0x72ff8072)); // 5.07766e+11f, 1.05756e+21f, -2.4406e-30f, 1.01215e+31f +path.lineTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268)); // 1.59583e+13f, 5.20715e+30f +path.close(); +path.moveTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268)); // 1.59583e+13f, 5.20715e+30f +path.lineTo(SkBits2Float(0x52626552), SkBits2Float(0x72727272)); // 2.43091e+11f, 4.80216e+30f +path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x62727272), SkBits2Float(0x39393939), SkBits2Float(0x728bc739)); // 4.80216e+30f, 1.11809e+21f, 0.000176643f, 5.53719e+30f +path.cubicTo(SkBits2Float(0x72728092), SkBits2Float(0x72727260), SkBits2Float(0x4d727272), SkBits2Float(0x5252522a), SkBits2Float(0x72735252), SkBits2Float(0x72707272)); // 4.80325e+30f, 4.80215e+30f, 2.54224e+08f, 2.2583e+11f, 4.81948e+30f, 4.76254e+30f +path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x56727272), SkBits2Float(0x72720152), SkBits2Float(0x72727270)); // 4.80216e+30f, 6.66433e+13f, 4.79341e+30f, 4.80216e+30f +path.quadTo(SkBits2Float(0x52526172), SkBits2Float(0x8e460300), SkBits2Float(0x72727272), SkBits2Float(0x52525272)); // 2.25894e+11f, -2.44068e-30f, 4.80216e+30f, 2.25832e+11f +path.conicTo(SkBits2Float(0xb5727272), SkBits2Float(0x7f2b727f), SkBits2Float(0x607272ff), SkBits2Float(0x72727276), SkBits2Float(0x2a527272)); // -9.03186e-07f, 2.27892e+38f, 6.98812e+19f, 4.80216e+30f, 1.86915e-13f +path.lineTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268)); // 1.59583e+13f, 5.20715e+30f +path.close(); +path.moveTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268)); // 1.59583e+13f, 5.20715e+30f +path.lineTo(SkBits2Float(0x72727272), SkBits2Float(0x52525f72)); // 4.80216e+30f, 2.25886e+11f +path.lineTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268)); // 1.59583e+13f, 5.20715e+30f +path.close(); +path.moveTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268)); // 1.59583e+13f, 5.20715e+30f +path.quadTo(SkBits2Float(0x52727272), SkBits2Float(0x64655252), SkBits2Float(0x72c1c152), SkBits2Float(0x72727272)); // 2.60326e+11f, 1.69209e+22f, 7.67543e+30f, 4.80216e+30f + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.setFillType(SkPath::kWinding_FillType); + + SkPath path2(path); + testPathOpFail(reporter, path1, path2, (SkPathOp) 1, filename); +} + +static void fuzzhang_3(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.setFillType(SkPath::kWinding_FillType); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.setFillType(SkPath::kWinding_FillType); +path.moveTo(SkBits2Float(0x46090052), SkBits2Float(0x7270726c)); // 8768.08f, 4.76254e+30f +path.moveTo(SkBits2Float(0xe0437272), SkBits2Float(0x03e0e060)); // -5.63338e+19f, 1.32171e-36f +path.close(); +path.moveTo(SkBits2Float(0xe0437272), SkBits2Float(0x03e0e060)); // -5.63338e+19f, 1.32171e-36f +path.lineTo(SkBits2Float(0x77727272), SkBits2Float(0x52520072)); // 4.91741e+33f, 2.25488e+11f +path.lineTo(SkBits2Float(0x46090052), SkBits2Float(0x727272ce)); // 8768.08f, 4.80219e+30f +path.quadTo(SkBits2Float(0x725252ec), SkBits2Float(0x72727272), SkBits2Float(0x72727272), SkBits2Float(0x39393962)); // 4.16589e+30f, 4.80216e+30f, 4.80216e+30f, 0.000176644f +path.lineTo(SkBits2Float(0x6c460900), SkBits2Float(0x72727072)); // 9.57639e+26f, 4.802e+30f +path.cubicTo(SkBits2Float(0xe0e060e0), SkBits2Float(0x72943603), SkBits2Float(0x72777272), SkBits2Float(0x5c525200), SkBits2Float(0x46090052), SkBits2Float(0x727272ce)); // -1.29345e+20f, 5.87124e+30f, 4.90119e+30f, 2.368e+17f, 8768.08f, 4.80219e+30f +path.quadTo(SkBits2Float(0x725252ec), SkBits2Float(0x72727272), SkBits2Float(0x72727272), SkBits2Float(0x39393962)); // 4.16589e+30f, 4.80216e+30f, 4.80216e+30f, 0.000176644f +path.lineTo(SkBits2Float(0x0052ca00), SkBits2Float(0x728e4603)); // 7.60297e-39f, 5.63603e+30f +path.quadTo(SkBits2Float(0xff727272), SkBits2Float(0x52527272), SkBits2Float(0x39392072), SkBits2Float(0xe0393939)); // -3.22267e+38f, 2.25966e+11f, 0.000176551f, -5.3387e+19f +path.lineTo(SkBits2Float(0xe0437272), SkBits2Float(0x03e0e060)); // -5.63338e+19f, 1.32171e-36f +path.close(); +path.moveTo(SkBits2Float(0xe0437272), SkBits2Float(0x03e0e060)); // -5.63338e+19f, 1.32171e-36f +path.cubicTo(SkBits2Float(0xdada7272), SkBits2Float(0x2dff7272), SkBits2Float(0x767272f0), SkBits2Float(0x72727272), SkBits2Float(0x21727f72), SkBits2Float(0x0b210929)); // -3.07437e+16f, 2.9041e-11f, 1.22936e+33f, 4.80216e+30f, 8.21615e-19f, 3.10144e-32f +path.cubicTo(SkBits2Float(0xd6d6d6d6), SkBits2Float(0x72a5d6d6), SkBits2Float(0x72553872), SkBits2Float(0xdada7072), SkBits2Float(0x5252525a), SkBits2Float(0x72727252)); // -1.18109e+14f, 6.56957e+30f, 4.22327e+30f, -3.07426e+16f, 2.25831e+11f, 4.80215e+30f +path.quadTo(SkBits2Float(0x72725572), SkBits2Float(0xdada0072), SkBits2Float(0x52524b5a), SkBits2Float(0x72528000)); // 4.79991e+30f, -3.0681e+16f, 2.25802e+11f, 4.16938e+30f +path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0xca005252), SkBits2Float(0x46030052), SkBits2Float(0x7272728e)); // 4.80216e+30f, -2.10242e+06f, 8384.08f, 4.80217e+30f +path.quadTo(SkBits2Float(0x7272ff72), SkBits2Float(0x20725252), SkBits2Float(0x39393939), SkBits2Float(0xd76ee039)); // 4.81307e+30f, 2.05254e-19f, 0.000176643f, -2.62647e+14f +path.cubicTo(SkBits2Float(0xdada7272), SkBits2Float(0x2dff7272), SkBits2Float(0x767272f0), SkBits2Float(0x72727272), SkBits2Float(0x21727f72), SkBits2Float(0x0b210929)); // -3.07437e+16f, 2.9041e-11f, 1.22936e+33f, 4.80216e+30f, 8.21615e-19f, 3.10144e-32f +path.cubicTo(SkBits2Float(0xd6d6d6d6), SkBits2Float(0x72a5d6d6), SkBits2Float(0x72553872), SkBits2Float(0xdada7072), SkBits2Float(0x5252525a), SkBits2Float(0x72727252)); // -1.18109e+14f, 6.56957e+30f, 4.22327e+30f, -3.07426e+16f, 2.25831e+11f, 4.80215e+30f +path.quadTo(SkBits2Float(0x72725572), SkBits2Float(0xdada0072), SkBits2Float(0x52524b5a), SkBits2Float(0x72528000)); // 4.79991e+30f, -3.0681e+16f, 2.25802e+11f, 4.16938e+30f +path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x52525252), SkBits2Float(0x27725252), SkBits2Float(0x72727272)); // 4.80216e+30f, 2.25831e+11f, 3.36289e-15f, 4.80216e+30f +path.quadTo(SkBits2Float(0x72667254), SkBits2Float(0x00000040), SkBits2Float(0x00a70155), SkBits2Float(0x726800ff)); // 4.56447e+30f, 8.96831e-44f, 1.5337e-38f, 4.59531e+30f +path.quadTo(SkBits2Float(0x7b727272), SkBits2Float(0xad000c52), SkBits2Float(0x1c10adad), SkBits2Float(0x72728d8a)); // 1.25886e+36f, -7.27869e-12f, 4.78701e-22f, 4.80425e+30f +path.quadTo(SkBits2Float(0xff056546), SkBits2Float(0x727205ff), SkBits2Float(0x524b5aff), SkBits2Float(0x64005252)); // -1.77313e+38f, 4.79377e+30f, 2.18351e+11f, 9.46846e+21f +path.quadTo(SkBits2Float(0x72524872), SkBits2Float(0xdada7272), SkBits2Float(0x5252525a), SkBits2Float(0x72727252)); // 4.16508e+30f, -3.07437e+16f, 2.25831e+11f, 4.80215e+30f +path.quadTo(SkBits2Float(0x72724172), SkBits2Float(0xdad10072), SkBits2Float(0x52524b5a), SkBits2Float(0x725b8000)); // 4.79837e+30f, -2.94144e+16f, 2.25802e+11f, 4.34765e+30f +path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x52525252), SkBits2Float(0x27725252), SkBits2Float(0x72727272)); // 4.80216e+30f, 2.25831e+11f, 3.36289e-15f, 4.80216e+30f +path.quadTo(SkBits2Float(0x72728372), SkBits2Float(0x00000040), SkBits2Float(0xf6a70147), SkBits2Float(0xc2c2c256)); // 4.80347e+30f, 8.96831e-44f, -1.69363e+33f, -97.3796f +path.lineTo(SkBits2Float(0xe0437272), SkBits2Float(0x03e0e060)); // -5.63338e+19f, 1.32171e-36f +path.close(); +path.moveTo(SkBits2Float(0x7a787a7a), SkBits2Float(0x7a3a7a7a)); // 3.22543e+35f, 2.42063e+35f +path.lineTo(SkBits2Float(0x8f4603e0), SkBits2Float(0x72727272)); // -9.7629e-30f, 4.80216e+30f +path.quadTo(SkBits2Float(0x00807272), SkBits2Float(0x46090052), SkBits2Float(0x7270726c), SkBits2Float(0x60e04372)); // 1.1796e-38f, 8768.08f, 4.76254e+30f, 1.29279e+20f +path.moveTo(SkBits2Float(0x943603e0), SkBits2Float(0x77727272)); // -9.18942e-27f, 4.91741e+33f +path.quadTo(SkBits2Float(0x5c525200), SkBits2Float(0x46090052), SkBits2Float(0x727272ce), SkBits2Float(0x5252ec72)); // 2.368e+17f, 8768.08f, 4.80219e+30f, 2.26478e+11f + + SkPath path2(path); + testPathOpFail(reporter, path1, path2, (SkPathOp) 3, filename); +} + + static struct TestDesc failTests[] = { + TEST(fuzzhang_3), + TEST(fuzzhang_2), + TEST(release_13), + TEST(fuzzhang_1), + TEST(fuzz763_57), + TEST(fuzz763_56), + TEST(fuzz763_55), + TEST(fuzz763_54), + TEST(fuzz763_53), + TEST(fuzz763_52), + TEST(fuzz763_51), + TEST(fuzz763_50), + TEST(fuzz763_49), + TEST(fuzz763_48), + TEST(fuzz763_47), + TEST(fuzz763_46), + TEST(fuzz763_45), + TEST(fuzz763_44), + TEST(fuzz763_43), + TEST(fuzz763_42), + TEST(fuzz763_41), + TEST(fuzz763_40), + TEST(fuzz763_39), + TEST(fuzz763_38), + TEST(fuzz763_37), + TEST(fuzz763_36), + TEST(fuzz763_35), + TEST(fuzz763_34), + TEST(fuzz763_33), + TEST(fuzz763_32), + TEST(fuzz763_31), + TEST(fuzz763_30), + TEST(fuzz763_29), + TEST(fuzz763_28), + TEST(fuzz763_27), + TEST(fuzz763_26), + TEST(fuzz763_25), + TEST(fuzz763_24), + TEST(fuzz763_23), + TEST(fuzz763_22), + TEST(fuzz763_21), + TEST(fuzz763_20), + TEST(fuzz763_19), + TEST(fuzz763_18), + TEST(fuzz763_17), + TEST(fuzz763_16), + TEST(fuzz763_15), + TEST(fuzz763_14), + TEST(fuzz763_13), + TEST(fuzz763_12), + TEST(fuzz763_11), + TEST(fuzz763_10), + TEST(kfuzz2), + TEST(fuzz763_7), + TEST(fuzz763_6), + TEST(fuzz763_2c), + TEST(fuzz763_2b), + TEST(fuzz763_2a), + TEST(fuzz763_5a), + TEST(fuzz763_3a), + TEST(fuzz763_1a), + TEST(fuzz763_1b), + TEST(fuzz763_1c), + TEST(fuzz763_2), + TEST(fuzz763_5), + TEST(fuzz763_3), + TEST(fuzz763_4), + TEST(fuzz763_9), + TEST(fuzz1450_1), + TEST(fuzz1450_0), + TEST(bug597926_0), + TEST(fuzz535151), + TEST(fuzz753_91), + TEST(fuzz714), TEST(fuzz487a), + TEST(fuzz433), + TEST(fuzz1), TEST(fuzz487b), TEST(fuzz433b), - TEST(fuzz433), TEST(bufferOverflow), }; @@ -5608,5 +8649,14 @@ #if DEBUG_SHOW_TEST_NAME strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH); #endif - RunTestSet(reporter, failTests, failTestCount, 0, 0, false); + RunTestSet(reporter, failTests, failTestCount, nullptr, nullptr, nullptr, false); +} + +static struct TestDesc repTests[] = { + TEST(fuzz763_5a), +}; + +DEF_TEST(PathOpsRepOp, reporter) { + for (int index = 0; index < 1; ++index) + RunTestSet(reporter, repTests, SK_ARRAY_COUNT(repTests), nullptr, nullptr, nullptr, false); }
diff --git a/src/third_party/skia/tests/PathOpsQuadIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsQuadIntersectionTest.cpp index 1ddbbcc..f5eb32f 100644 --- a/src/third_party/skia/tests/PathOpsQuadIntersectionTest.cpp +++ b/src/third_party/skia/tests/PathOpsQuadIntersectionTest.cpp
@@ -14,9 +14,13 @@ static void standardTestCases(skiatest::Reporter* reporter) { bool showSkipped = false; for (size_t index = 0; index < quadraticTests_count; ++index) { - const SkDQuad& quad1 = quadraticTests[index][0]; + const QuadPts& q1 = quadraticTests[index][0]; + SkDQuad quad1; + quad1.debugSet(q1.fPts); SkASSERT(ValidQuad(quad1)); - const SkDQuad& quad2 = quadraticTests[index][1]; + const QuadPts& q2 = quadraticTests[index][1]; + SkDQuad quad2; + quad2.debugSet(q2.fPts); SkASSERT(ValidQuad(quad2)); SkReduceOrder reduce1, reduce2; int order1 = reduce1.reduce(quad1); @@ -52,7 +56,71 @@ } } -static const SkDQuad testSet[] = { +static const QuadPts testSet[] = { +{{{123.637985f, 102.405312f}, {125.172699f, 104.575714f}, {123.387383f, 106.91227f}}}, +{{{123.388428f, 106.910896f}, {123.365623f, 106.94088f}, {123.320007f, 107.000946f}}}, + +{{{-0.001019871095195412636, -0.008523519150912761688}, {-0.005396408028900623322, -0.005396373569965362549}, {-0.02855382487177848816, -0.02855364233255386353}}}, +{{{-0.004567248281091451645, -0.01482933573424816132}, {-0.01142475008964538574, -0.01140109263360500336}, {-0.02852955088019371033, -0.02847047336399555206}}}, + +{{{1, 1}, {0, 2}, {3, 3}}}, +{{{3, 0}, {0, 1}, {1, 2}}}, + +{{{0.33333333333333326, 0.81481481481481488}, {0.63395173631977997, 0.68744136726313931}, {1.205684411948591, 0.81344322326274499}}}, +{{{0.33333333333333326, 0.81481481481481488}, {0.63396444791444551, 0.68743368362444768}, {1.205732763658403, 0.81345617746834109}}}, + +{{{4981.9990234375, 1590}, {4981.9990234375, 1617.7523193359375}, {4962.375, 1637.3760986328125}}}, +{{{4962.3759765625, 1637.3760986328125}, {4982, 1617.7523193359375}, {4982, 1590}}}, + +{{{48.7416f, 7.74160004f}, {96.4831848f, -40}, {164, -40}}}, +{{{56.9671326f, 0}, {52.7835083f, 3.69968891f}, {48.7416f, 7.74160004f}}}, + +{{{138, 80}, {147.15692138671875, 80}, {155.12803649902344, 82.86279296875}}}, +{{{155.12803649902344, 82.86279296875}, {153.14971923828125, 82.152290344238281}, {151.09841918945312, 81.618133544921875}}}, + +{{{88, 130}, {88, 131.54483032226562}, {88.081489562988281, 133.0560302734375}}}, +{{{88.081489562988281, 133.0560302734375}, {88, 131.54483032226562}, {88, 130}}}, + +{{{0.59987992,2.14448452}, {0.775417507,1.95606446}, {1.00564098,1.79310346}}}, +{{{1.00564098,1.79310346}, {1.25936198,1.615623}, {1.35901463,1.46834028}}}, + +{{{3,0}, {0,1}, {3,2}}}, +{{{2,0}, {1,1}, {2,2}}}, + +{{{38.656852722167969, 38.656852722167969}, {38.651023864746094, 38.662681579589844}, {38.644744873046875, 38.668937683105469}}}, +{{{38.656852722167969, 38.656852722167969}, {36.313709259033203, 41}, {33, 41}}}, + +{{{4914.9990234375, 1523}, {4942.75146484375, 1523}, {4962.375, 1542.6239013671875}}}, +{{{4962.3759765625, 1542.6239013671875}, {4942.75244140625, 1523}, {4915, 1523}}}, + +{{{4867.623046875, 1637.3760986328125}, {4847.9990234375, 1617.7523193359375}, {4847.9990234375, 1590}}}, +{{{4848, 1590}, {4848, 1617.7523193359375}, {4867.6240234375, 1637.3760986328125}}}, + +{{{102.64466094970703, 165.3553466796875}, {110.79246520996094, 173.50314331054687}, {120.81797790527344, 177.11778259277344}}}, +{{{113.232177734375, 173.57899475097656}, {116.88026428222656, 175.69805908203125}, {120.81797790527344, 177.11778259277344}}}, + +{{{-37.3484879,10.0192947}, {-36.4966316,13.2140198}, {-38.1506348,16.0788383}}}, +{{{-38.1462746,16.08918}, {-36.4904327,13.2193804}, {-37.3484879,10.0192947}}}, + +{{{-37.3513985,10.0082998}, {-36.4938011,13.2090998}, {-38.1506004,16.0788002}}}, +{{{-37.3508987,10.0102997}, {-36.4930992,13.2110004}, {-38.1497993,16.0809002}}}, + +{{{-37.3508987,10.0102997}, {-37.3510017,10.0098}, {-37.3512001,10.0093002}}}, +{{{-49.0778008,19.0097008}, {-38.2086983,6.80954981}, {-37.3508987,10.0102997}}}, + +{{{SkBits2Float(0xc22423b2), SkBits2Float(0x40afae2c)}, + {SkBits2Float(0xc2189b24), SkBits2Float(0x40e3f058)}, + {SkBits2Float(0xc21511d9), SkBits2Float(0x41251125)}}}, +{{{SkBits2Float(0xc2153d2f), SkBits2Float(0x412299db)}, + {SkBits2Float(0xc2153265), SkBits2Float(0x41233845)}, + {SkBits2Float(0xc21527fc), SkBits2Float(0x4123d684)}}}, + +{{{-37.3097496, 10.1625624}, {-37.2992134, 10.2012377}, {-37.2890472, 10.239872}}}, +{{{-41.0348587, 5.49001122}, {-38.1515045, 7.12308884}, {-37.2674294, 10.3166857}}}, + +{{{-52.8062439,14.1493912}, {-53.6638947,10.948595}, {-52.0070419,8.07883835}}}, +{{{-52.8054848,14.1522331}, {-53.6633072,10.9514809}, {-52.0066071,8.08163643}}}, + {{{441.853149, 308.209106}, {434.672272, 315.389984}, {424.516998, 315.389984}}}, {{{385.207275, 334.241272}, {406.481598, 312.96698}, {436.567993, 312.96698}}}, @@ -265,16 +333,20 @@ const size_t testSetCount = SK_ARRAY_COUNT(testSet); static void oneOffTest1(skiatest::Reporter* reporter, size_t outer, size_t inner) { - const SkDQuad& quad1 = testSet[outer]; + const QuadPts& q1 = testSet[outer]; + SkDQuad quad1; + quad1.debugSet(q1.fPts); SkASSERT(ValidQuad(quad1)); - const SkDQuad& quad2 = testSet[inner]; + const QuadPts& q2 = testSet[inner]; + SkDQuad quad2; + quad2.debugSet(q2.fPts); SkASSERT(ValidQuad(quad2)); - SkIntersections intersections2; - intersections2.intersect(quad1, quad2); - for (int pt = 0; pt < intersections2.used(); ++pt) { - double tt1 = intersections2[0][pt]; + SkIntersections intersections; + intersections.intersect(quad1, quad2); + for (int pt = 0; pt < intersections.used(); ++pt) { + double tt1 = intersections[0][pt]; SkDPoint xy1 = quad1.ptAtT(tt1); - double tt2 = intersections2[1][pt]; + double tt2 = intersections[1][pt]; SkDPoint xy2 = quad2.ptAtT(tt2); if (!xy1.approximatelyEqual(xy2)) { SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n", @@ -289,10 +361,6 @@ } } -DEF_TEST(PathOpsQuadIntersectionOneOff, reporter) { - oneOffTest1(reporter, 0, 1); -} - static void oneOffTests(skiatest::Reporter* reporter) { for (size_t outer = 0; outer < testSetCount - 1; ++outer) { for (size_t inner = outer + 1; inner < testSetCount; ++inner) { @@ -301,7 +369,9 @@ } } -static const SkDQuad coincidentTestSet[] = { +static const QuadPts coincidentTestSet[] = { + {{{4914.9990234375, 1523}, {4942.75146484375, 1523}, {4962.375, 1542.6239013671875}}}, + {{{4962.3759765625, 1542.6239013671875}, {4942.75244140625, 1523}, {4915, 1523}}}, #if 0 {{{97.9337615966796875,100}, {88,112.94264984130859375}, {88,130}}}, {{{88,130}, {88,124.80951690673828125}, {88.91983795166015625,120}}}, @@ -315,15 +385,19 @@ static const int coincidentTestSetCount = (int) SK_ARRAY_COUNT(coincidentTestSet); static void coincidentTestOne(skiatest::Reporter* reporter, int test1, int test2) { - const SkDQuad& quad1 = coincidentTestSet[test1]; + const QuadPts& q1 = coincidentTestSet[test1]; + SkDQuad quad1; + quad1.debugSet(q1.fPts); SkASSERT(ValidQuad(quad1)); - const SkDQuad& quad2 = coincidentTestSet[test2]; + const QuadPts& q2 = coincidentTestSet[test2]; + SkDQuad quad2; + quad2.debugSet(q2.fPts); SkASSERT(ValidQuad(quad2)); SkIntersections intersections2; intersections2.intersect(quad1, quad2); - REPORTER_ASSERT(reporter, intersections2.coincidentUsed() == 2); - REPORTER_ASSERT(reporter, intersections2.used() == 2); - for (int pt = 0; pt < intersections2.coincidentUsed(); ++pt) { + REPORTER_ASSERT(reporter, intersections2.debugCoincidentUsed() >= 2); + REPORTER_ASSERT(reporter, intersections2.used() >= 2); + for (int pt = 0; pt < intersections2.debugCoincidentUsed(); pt += 2) { double tt1 = intersections2[0][pt]; double tt2 = intersections2[1][pt]; SkDPoint pt1 = quad1.ptAtT(tt1); @@ -338,79 +412,12 @@ } } -DEF_TEST(PathOpsQuadIntersectionCoincidenceOneOff, reporter) { - coincidentTestOne(reporter, 0, 1); -} - -static int floatSign(double x) { - return x < 0 ? -1 : x > 0 ? 1 : 0; -} - -static const SkDQuad pointFinderTestSet[] = { - //>=0.633974464 0.633974846 <= -{{{1.2071879545809394, 0.82163474041730045}, {1.1534203513372994, 0.52790870069930229}, - {1.0880000000000001, 0.29599999999999982}}}, //t=0.63155333662549329, 0.80000000000000004 -{{{1.2071879545809394, 0.82163474041730045}, {1.2065040319428038, 0.81766753259119995}, - {1.2058123269101506, 0.81370135061854221}}}, //t=0.63155333662549329, 0.6339049773632347 -{{{1.2058123269101506, 0.81370135061854221}, {1.152376363978022, 0.5244097415381026}, - {1.0880000000000001, 0.29599999999999982}}}, //t=0.6339049773632347, 0.80000000000000004 - //>=0.633974083 0.633975227 <= -{{{0.33333333333333326, 0.81481481481481488}, {0.63395173631977997, 0.68744136726313931}, - {1.205684411948591, 0.81344322326274499}}}, //t=0.33333333333333331, 0.63395173631977986 -{{{0.33333333333333326, 0.81481481481481488}, {0.63396444791444551, 0.68743368362444768}, - {1.205732763658403, 0.81345617746834109}}}, //t=0.33333333333333331, 0.63396444791444551 -{{{1.205684411948591, 0.81344322326274499}, {1.2057085875611198, 0.81344969999329253}, - {1.205732763658403, 0.81345617746834109}}}, //t=0.63395173631977986, 0.63396444791444551 -{{{1.205732763658403, 0.81345617746834109}, {1.267928895828891, 0.83008534558465619}, - {1.3333333333333333, 0.85185185185185175}}}, //t=0.63396444791444551, 0.66666666666666663 -}; - -static void pointFinder(const SkDQuad& q1, const SkDQuad& q2) { - for (int index = 0; index < 3; ++index) { - double t = q1.nearestT(q2[index]); - SkDPoint onQuad = q1.ptAtT(t); - SkDebugf("%s t=%1.9g (%1.9g,%1.9g) dist=%1.9g\n", __FUNCTION__, t, onQuad.fX, onQuad.fY, - onQuad.distance(q2[index])); - double left[3]; - left[0] = ((const SkDLine&) q1[0]).isLeft(q2[index]); - left[1] = ((const SkDLine&) q1[1]).isLeft(q2[index]); - SkDLine diag = {{q1[0], q1[2]}}; - left[2] = diag.isLeft(q2[index]); - SkDebugf("%s left=(%d, %d, %d) inHull=%s\n", __FUNCTION__, floatSign(left[0]), - floatSign(left[1]), floatSign(left[2]), - q1.pointInHull(q2[index]) ? "true" : "false"); - } - SkDebugf("\n"); -} - -static void hullIntersect(const SkDQuad& q1, const SkDQuad& q2) { - SkDebugf("%s", __FUNCTION__); - SkIntersections ts; - for (int i1 = 0; i1 < 3; ++i1) { - SkDLine l1 = {{q1[i1], q1[(i1 + 1) % 3]}}; - for (int i2 = 0; i2 < 3; ++i2) { - SkDLine l2 = {{q2[i2], q2[(i2 + 1) % 3]}}; - if (ts.intersect(l1, l2)) { - SkDebugf(" [%d,%d]", i1, i2); - } - } - } - SkDebugf("\n"); -} - -static void QuadraticIntersection_PointFinder() { - pointFinder(pointFinderTestSet[0], pointFinderTestSet[4]); - pointFinder(pointFinderTestSet[4], pointFinderTestSet[0]); - pointFinder(pointFinderTestSet[0], pointFinderTestSet[6]); - pointFinder(pointFinderTestSet[6], pointFinderTestSet[0]); - hullIntersect(pointFinderTestSet[0], pointFinderTestSet[4]); - hullIntersect(pointFinderTestSet[0], pointFinderTestSet[6]); -} - static void intersectionFinder(int test1, int test2) { - const SkDQuad& quad1 = testSet[test1]; - const SkDQuad& quad2 = testSet[test2]; - + const QuadPts& q1 = testSet[test1]; + const QuadPts& q2 = testSet[test2]; + SkDQuad quad1, quad2; + quad1.debugSet(q1.fPts); + quad2.debugSet(q2.fPts); double t1Seed = 0.5; double t2Seed = 0.8; double t1Step = 0.1; @@ -505,10 +512,51 @@ intersectionFinder(0, 1); } +DEF_TEST(PathOpsQuadIntersectionOneOff, reporter) { + oneOffTest1(reporter, 0, 1); +} + +DEF_TEST(PathOpsQuadIntersectionCoincidenceOneOff, reporter) { + coincidentTestOne(reporter, 0, 1); +} + DEF_TEST(PathOpsQuadIntersection, reporter) { oneOffTests(reporter); coincidentTest(reporter); standardTestCases(reporter); if (false) QuadraticIntersection_IntersectionFinder(); - if (false) QuadraticIntersection_PointFinder(); +} + +DEF_TEST(PathOpsQuadBinaryProfile, reporter) { + if (!SkPathOpsDebug::gVeryVerbose) { + return; + } + SkIntersections intersections; + for (int x = 0; x < 100; ++x) { + int outer = 0; + int inner = outer + 1; + do { + const QuadPts& q1 = testSet[outer]; + SkDQuad quad1; + quad1.debugSet(q1.fPts); + const QuadPts& q2 = testSet[inner]; + SkDQuad quad2; + quad2.debugSet(q2.fPts); + (void) intersections.intersect(quad1, quad2); + REPORTER_ASSERT(reporter, intersections.used() >= 0); // make sure code isn't tossed + inner += 2; + outer += 2; + } while (outer < (int) testSetCount); + } + for (int x = 0; x < 100; ++x) { + for (size_t test = 0; test < quadraticTests_count; ++test) { + const QuadPts& q1 = quadraticTests[test][0]; + const QuadPts& q2 = quadraticTests[test][1]; + SkDQuad quad1, quad2; + quad1.debugSet(q1.fPts); + quad2.debugSet(q2.fPts); + (void) intersections.intersect(quad1, quad2); + REPORTER_ASSERT(reporter, intersections.used() >= 0); // make sure code isn't tossed + } + } }
diff --git a/src/third_party/skia/tests/PathOpsQuadIntersectionTestData.cpp b/src/third_party/skia/tests/PathOpsQuadIntersectionTestData.cpp index 0706efc..537b509 100644 --- a/src/third_party/skia/tests/PathOpsQuadIntersectionTestData.cpp +++ b/src/third_party/skia/tests/PathOpsQuadIntersectionTestData.cpp
@@ -7,7 +7,7 @@ #include "PathOpsQuadIntersectionTestData.h" -const SkDQuad quadraticPoints[] = { +const QuadPts quadraticPoints[] = { {{{0, 0}, {1, 0}, {0, 0}}}, {{{0, 0}, {0, 1}, {0, 0}}}, {{{0, 0}, {1, 1}, {0, 0}}}, @@ -16,7 +16,7 @@ const size_t quadraticPoints_count = SK_ARRAY_COUNT(quadraticPoints); -const SkDQuad quadraticLines[] = { +const QuadPts quadraticLines[] = { {{{0, 0}, {0, 0}, {1, 0}}}, {{{1, 0}, {0, 0}, {0, 0}}}, {{{1, 0}, {2, 0}, {3, 0}}}, @@ -44,12 +44,12 @@ const size_t quadraticLines_count = SK_ARRAY_COUNT(quadraticLines); -static const double F = FLT_EPSILON * 3; -static const double H = FLT_EPSILON * 4; -static const double J = FLT_EPSILON * 5; -static const double K = FLT_EPSILON * 8; // INVESTIGATE: why are larger multiples necessary? +static const double F = FLT_EPSILON * 32; +static const double H = FLT_EPSILON * 32; +static const double J = FLT_EPSILON * 32; +static const double K = FLT_EPSILON * 32; // INVESTIGATE: why are larger multiples necessary? -const SkDQuad quadraticModEpsilonLines[] = { +const QuadPts quadraticModEpsilonLines[] = { {{{0, F}, {0, 0}, {1, 0}}}, {{{0, 0}, {1, 0}, {0, F}}}, {{{1, 0}, {0, F}, {0, 0}}}, @@ -64,7 +64,7 @@ {{{1, 1+J}, {2, 2}, {3, 3}}}, {{{1, 1}, {3, 3}, {3+F, 3}}}, {{{1, 1}, {1+F, 1}, {2, 2}}}, - {{{1, 1}, {2, 2}, {1, 1+F}}}, + {{{1, 1}, {2, 2}, {1, 1+K}}}, {{{1, 1}, {1, 1+F}, {3, 3}}}, {{{1+H, 1}, {2, 2}, {4, 4}}}, // no coincident {{{1, 1+K}, {3, 3}, {4, 4}}}, @@ -82,7 +82,7 @@ const size_t quadraticModEpsilonLines_count = SK_ARRAY_COUNT(quadraticModEpsilonLines); -const SkDQuad quadraticTests[][2] = { +const QuadPts quadraticTests[][2] = { { // one intersection {{{0, 0}, {0, 1},
diff --git a/src/third_party/skia/tests/PathOpsQuadIntersectionTestData.h b/src/third_party/skia/tests/PathOpsQuadIntersectionTestData.h index 3090fc9..be46a33 100644 --- a/src/third_party/skia/tests/PathOpsQuadIntersectionTestData.h +++ b/src/third_party/skia/tests/PathOpsQuadIntersectionTestData.h
@@ -4,12 +4,12 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ -#include "SkPathOpsQuad.h" +#include "PathOpsTestCommon.h" -extern const SkDQuad quadraticLines[]; -extern const SkDQuad quadraticPoints[]; -extern const SkDQuad quadraticModEpsilonLines[]; -extern const SkDQuad quadraticTests[][2]; +extern const QuadPts quadraticLines[]; +extern const QuadPts quadraticPoints[]; +extern const QuadPts quadraticModEpsilonLines[]; +extern const QuadPts quadraticTests[][2]; extern const size_t quadraticLines_count; extern const size_t quadraticPoints_count;
diff --git a/src/third_party/skia/tests/PathOpsQuadLineIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsQuadLineIntersectionTest.cpp index 6a9e497..f24b2e4 100644 --- a/src/third_party/skia/tests/PathOpsQuadLineIntersectionTest.cpp +++ b/src/third_party/skia/tests/PathOpsQuadLineIntersectionTest.cpp
@@ -13,7 +13,7 @@ #include "Test.h" static struct lineQuad { - SkDQuad quad; + QuadPts quad; SkDLine line; int result; SkDPoint expected[2]; @@ -56,7 +56,7 @@ } static struct oneLineQuad { - SkDQuad quad; + QuadPts quad; SkDLine line; } oneOffs[] = { {{{{97.9337616,100}, {88,112.94265}, {88,130}}}, @@ -79,7 +79,9 @@ static void testOneOffs(skiatest::Reporter* reporter) { bool flipped = false; for (size_t index = 0; index < oneOffs_count; ++index) { - const SkDQuad& quad = oneOffs[index].quad; + const QuadPts& q = oneOffs[index].quad; + SkDQuad quad; + quad.debugSet(q.fPts); SkASSERT(ValidQuad(quad)); const SkDLine& line = oneOffs[index].line; SkASSERT(ValidLine(line)); @@ -106,7 +108,9 @@ DEF_TEST(PathOpsQuadLineIntersection, reporter) { for (size_t index = 0; index < lineQuadTests_count; ++index) { int iIndex = static_cast<int>(index); - const SkDQuad& quad = lineQuadTests[index].quad; + const QuadPts& q = lineQuadTests[index].quad; + SkDQuad quad; + quad.debugSet(q.fPts); SkASSERT(ValidQuad(quad)); const SkDLine& line = lineQuadTests[index].line; SkASSERT(ValidLine(line));
diff --git a/src/third_party/skia/tests/PathOpsQuadLineIntersectionThreadedTest.cpp b/src/third_party/skia/tests/PathOpsQuadLineIntersectionThreadedTest.cpp index 7e33b7b..a82ac28 100644 --- a/src/third_party/skia/tests/PathOpsQuadLineIntersectionThreadedTest.cpp +++ b/src/third_party/skia/tests/PathOpsQuadLineIntersectionThreadedTest.cpp
@@ -5,11 +5,13 @@ * found in the LICENSE file. */ #include "PathOpsExtendedTest.h" +#include "PathOpsTestCommon.h" #include "PathOpsThreadedCommon.h" #include "SkIntersections.h" #include "SkPathOpsLine.h" #include "SkPathOpsQuad.h" #include "SkReduceOrder.h" +#include "SkString.h" static int doIntersect(SkIntersections& intersections, const SkDQuad& quad, const SkDLine& line, bool& flipped) { @@ -40,14 +42,12 @@ static void testLineIntersect(skiatest::Reporter* reporter, const SkDQuad& quad, const SkDLine& line, const double x, const double y) { - char pathStr[1024]; - sk_bzero(pathStr, sizeof(pathStr)); - char* str = pathStr; - str += sprintf(str, " path.moveTo(%1.9g, %1.9g);\n", quad[0].fX, quad[0].fY); - str += sprintf(str, " path.quadTo(%1.9g, %1.9g, %1.9g, %1.9g);\n", quad[1].fX, + SkString pathStr; + pathStr.appendf(" path.moveTo(%1.9g, %1.9g);\n", quad[0].fX, quad[0].fY); + pathStr.appendf(" path.quadTo(%1.9g, %1.9g, %1.9g, %1.9g);\n", quad[1].fX, quad[1].fY, quad[2].fX, quad[2].fY); - str += sprintf(str, " path.moveTo(%1.9g, %1.9g);\n", line[0].fX, line[0].fY); - str += sprintf(str, " path.lineTo(%1.9g, %1.9g);\n", line[1].fX, line[1].fY); + pathStr.appendf(" path.moveTo(%1.9g, %1.9g);\n", line[0].fX, line[0].fY); + pathStr.appendf(" path.lineTo(%1.9g, %1.9g);\n", line[1].fX, line[1].fY); SkIntersections intersections; bool flipped = false; @@ -80,8 +80,10 @@ int by = state.fB >> 2; int cx = state.fC & 0x03; int cy = state.fC >> 2; - SkDQuad quad = {{{(double) ax, (double) ay}, {(double) bx, (double) by}, + QuadPts q = {{{(double) ax, (double) ay}, {(double) bx, (double) by}, {(double) cx, (double) cy}}}; + SkDQuad quad; + quad.debugSet(q.fPts); SkReduceOrder reducer; int order = reducer.reduce(quad); if (order < 3) { @@ -91,7 +93,7 @@ SkDPoint xy = quad.ptAtT(tIndex / 4.0); for (int h = -2; h <= 2; ++h) { for (int v = -2; v <= 2; ++v) { - if (h == v && abs(h) != 1) { + if (h == v && SkTAbs(h) != 1) { continue; } double x = xy.fX; @@ -116,8 +118,8 @@ for (int a = 0; a < 16; ++a) { for (int b = 0 ; b < 16; ++b) { for (int c = 0 ; c < 16; ++c) { - *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable, - (&testQuadLineIntersectMain, a, b, c, 0, &testRunner)); + *testRunner.fRunnables.append() = new PathOpsThreadedRunnable( + &testQuadLineIntersectMain, a, b, c, 0, &testRunner); } if (!reporter->allowExtendedTest()) goto finish; }
diff --git a/src/third_party/skia/tests/PathOpsQuadParameterizationTest.cpp b/src/third_party/skia/tests/PathOpsQuadParameterizationTest.cpp deleted file mode 100644 index c7a2e87..0000000 --- a/src/third_party/skia/tests/PathOpsQuadParameterizationTest.cpp +++ /dev/null
@@ -1,50 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#include "SkDQuadImplicit.h" -#include "SkPathOpsQuad.h" -#include "Test.h" - -static bool point_on_parameterized_curve(const SkDQuad& quad, const SkDPoint& point) { - SkDQuadImplicit q(quad); - double xx = q.x2() * point.fX * point.fX; - double xy = q.xy() * point.fX * point.fY; - double yy = q.y2() * point.fY * point.fY; - double x = q.x() * point.fX; - double y = q.y() * point.fY; - double c = q.c(); - double sum = xx + xy + yy + x + y + c; - return approximately_zero(sum); -} - -static const SkDQuad quadratics[] = { - {{{0, 0}, {1, 0}, {1, 1}}}, -}; - -static const int quadratics_count = (int) SK_ARRAY_COUNT(quadratics); - -DEF_TEST(PathOpsQuadImplicit, reporter) { - // split large quadratic - // compare original, parts, to see if the are coincident - for (int index = 0; index < quadratics_count; ++index) { - const SkDQuad& test = quadratics[index]; - SkDQuadPair split = test.chopAt(0.5); - SkDQuad midThird = test.subDivide(1.0/3, 2.0/3); - const SkDQuad* quads[] = { - &test, &midThird, &split.first(), &split.second() - }; - int quadsCount = (int) SK_ARRAY_COUNT(quads); - for (int one = 0; one < quadsCount; ++one) { - for (int two = 0; two < quadsCount; ++two) { - for (int inner = 0; inner < 3; inner += 2) { - REPORTER_ASSERT(reporter, point_on_parameterized_curve(*quads[one], - (*quads[two])[inner])); - } - REPORTER_ASSERT(reporter, SkDQuadImplicit::Match(*quads[one], *quads[two])); - } - } - } -}
diff --git a/src/third_party/skia/tests/PathOpsQuadReduceOrderTest.cpp b/src/third_party/skia/tests/PathOpsQuadReduceOrderTest.cpp index 9a0bdcf..4f62026 100644 --- a/src/third_party/skia/tests/PathOpsQuadReduceOrderTest.cpp +++ b/src/third_party/skia/tests/PathOpsQuadReduceOrderTest.cpp
@@ -10,7 +10,7 @@ #include "SkReduceOrder.h" #include "Test.h" -static const SkDQuad testSet[] = { +static const QuadPts testSet[] = { {{{1, 1}, {2, 2}, {1, 1.000003}}}, {{{1, 0}, {2, 6}, {3, 0}}} }; @@ -19,7 +19,9 @@ static void oneOffTest(skiatest::Reporter* reporter) { for (size_t index = 0; index < testSetCount; ++index) { - const SkDQuad& quad = testSet[index]; + const QuadPts& q = testSet[index]; + SkDQuad quad; + quad.debugSet(q.fPts); SkReduceOrder reducer; SkDEBUGCODE(int result = ) reducer.reduce(quad); SkASSERT(result == 3); @@ -47,16 +49,20 @@ : SK_MaxS32; for (index = firstQuadraticLineTest; index < quadraticLines_count; ++index) { - const SkDQuad& quad = quadraticLines[index]; + const QuadPts& q = quadraticLines[index]; + SkDQuad quad; + quad.debugSet(q.fPts); order = reducer.reduce(quad); if (order != 2) { SkDebugf("[%d] line quad order=%d\n", (int) index, order); } } for (index = firstQuadraticModLineTest; index < quadraticModEpsilonLines_count; ++index) { - const SkDQuad& quad = quadraticModEpsilonLines[index]; + const QuadPts& q = quadraticModEpsilonLines[index]; + SkDQuad quad; + quad.debugSet(q.fPts); order = reducer.reduce(quad); - if (order != 3) { + if (order != 2 && order != 3) { // FIXME: data probably is not good SkDebugf("[%d] line mod quad order=%d\n", (int) index, order); } }
diff --git a/src/third_party/skia/tests/PathOpsSimplifyDegenerateThreadedTest.cpp b/src/third_party/skia/tests/PathOpsSimplifyDegenerateThreadedTest.cpp old mode 100755 new mode 100644 index 8e8c58b..3f49718 --- a/src/third_party/skia/tests/PathOpsSimplifyDegenerateThreadedTest.cpp +++ b/src/third_party/skia/tests/PathOpsSimplifyDegenerateThreadedTest.cpp
@@ -6,15 +6,11 @@ */ #include "PathOpsExtendedTest.h" #include "PathOpsThreadedCommon.h" +#include "SkString.h" static void testSimplifyDegeneratesMain(PathOpsThreadState* data) { SkASSERT(data); PathOpsThreadState& state = *data; - char pathStr[1024]; - bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter? - if (progress) { - sk_bzero(pathStr, sizeof(pathStr)); - } int ax = state.fA & 0x03; int ay = state.fA >> 2; int bx = state.fB & 0x03; @@ -34,6 +30,7 @@ != (ey - dy) * (fx - dx)) { continue; } + SkString pathStr; SkPath path, out; path.setFillType(SkPath::kWinding_FillType); path.moveTo(SkIntToScalar(ax), SkIntToScalar(ay)); @@ -44,24 +41,23 @@ path.lineTo(SkIntToScalar(ex), SkIntToScalar(ey)); path.lineTo(SkIntToScalar(fx), SkIntToScalar(fy)); path.close(); - if (progress) { - char* str = pathStr; - str += sprintf(str, " path.moveTo(%d, %d);\n", ax, ay); - str += sprintf(str, " path.lineTo(%d, %d);\n", bx, by); - str += sprintf(str, " path.lineTo(%d, %d);\n", cx, cy); - str += sprintf(str, " path.close();\n"); - str += sprintf(str, " path.moveTo(%d, %d);\n", dx, dy); - str += sprintf(str, " path.lineTo(%d, %d);\n", ex, ey); - str += sprintf(str, " path.lineTo(%d, %d);\n", fx, fy); - str += sprintf(str, " path.close();\n"); - outputProgress(state.fPathStr, pathStr, SkPath::kWinding_FillType); + if (state.fReporter->verbose()) { + pathStr.appendf(" path.moveTo(%d, %d);\n", ax, ay); + pathStr.appendf(" path.lineTo(%d, %d);\n", bx, by); + pathStr.appendf(" path.lineTo(%d, %d);\n", cx, cy); + pathStr.appendf(" path.close();\n"); + pathStr.appendf(" path.moveTo(%d, %d);\n", dx, dy); + pathStr.appendf(" path.lineTo(%d, %d);\n", ex, ey); + pathStr.appendf(" path.lineTo(%d, %d);\n", fx, fy); + pathStr.appendf(" path.close();\n"); + state.outputProgress(pathStr.c_str(), SkPath::kWinding_FillType); } - testSimplify(path, false, out, state, pathStr); + testSimplify(path, false, out, state, pathStr.c_str()); path.setFillType(SkPath::kEvenOdd_FillType); - if (progress) { - outputProgress(state.fPathStr, pathStr, SkPath::kEvenOdd_FillType); + if (state.fReporter->verbose()) { + state.outputProgress(pathStr.c_str(), SkPath::kEvenOdd_FillType); } - testSimplify(path, true, out, state, pathStr); + testSimplify(path, true, out, state, pathStr.c_str()); } } } @@ -80,9 +76,8 @@ int cx = c & 0x03; int cy = c >> 2; bool abcIsATriangle = (bx - ax) * (cy - ay) != (by - ay) * (cx - ax); - *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable, - (&testSimplifyDegeneratesMain, a, b, c, abcIsATriangle, - &testRunner)); + *testRunner.fRunnables.append() = new PathOpsThreadedRunnable( + &testSimplifyDegeneratesMain, a, b, c, abcIsATriangle, &testRunner); } if (!reporter->allowExtendedTest()) goto finish; }
diff --git a/src/third_party/skia/tests/PathOpsSimplifyFailTest.cpp b/src/third_party/skia/tests/PathOpsSimplifyFailTest.cpp index 2a4b0a0..b6f210e 100644 --- a/src/third_party/skia/tests/PathOpsSimplifyFailTest.cpp +++ b/src/third_party/skia/tests/PathOpsSimplifyFailTest.cpp
@@ -4,6 +4,7 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ +#include "PathOpsExtendedTest.h" #include "SkPath.h" #include "SkPathOps.h" #include "SkPoint.h" @@ -86,15 +87,142 @@ SkPath result; result.setFillType(SkPath::kWinding_FillType); bool success = Simplify(path, &result); - // linux 32 debug fails test 13 because the quad is not treated as linear - // there's no error in the math that I can find -- it looks like a processor - // or compiler bug -- so for now, allow either to work - REPORTER_ASSERT(reporter, success || index == 13); + REPORTER_ASSERT(reporter, success); REPORTER_ASSERT(reporter, result.getFillType() != SkPath::kWinding_FillType); reporter->bumpTestCount(); } +static void fuzz_59(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.moveTo(SkBits2Float(0x430c0000), SkBits2Float(0xce58f41c)); // 140, -9.09969e+08f + path.lineTo(SkBits2Float(0x43480000), SkBits2Float(0xce58f419)); // 200, -9.09969e+08f + path.lineTo(SkBits2Float(0x42200000), SkBits2Float(0xce58f41b)); // 40, -9.09969e+08f + path.lineTo(SkBits2Float(0x43700000), SkBits2Float(0xce58f41b)); // 240, -9.09969e+08f + path.lineTo(SkBits2Float(0x428c0000), SkBits2Float(0xce58f419)); // 70, -9.09969e+08f + path.lineTo(SkBits2Float(0x430c0000), SkBits2Float(0xce58f41c)); // 140, -9.09969e+08f + path.close(); + testSimplifyFuzz(reporter, path, filename); +} + +static void fuzz_x1(skiatest::Reporter* reporter, const char* filename) { + SkPath path; +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.cubicTo(SkBits2Float(0x1931204a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a4a34), SkBits2Float(0x4a4a4a4a)); // 9.15721e-24f, 1.14845e-12f, 3.31014e+06f, 3.31014e+06f, 3.31432e+06f, 3.31432e+06f +path.moveTo(SkBits2Float(0x000010a1), SkBits2Float(0x19312000)); // 5.96533e-42f, 9.15715e-24f +path.cubicTo(SkBits2Float(0x4a6a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa14a4a4a), SkBits2Float(0x08ff2ba1), SkBits2Float(0x08ff4a4a), SkBits2Float(0x4a344a4a)); // 3.83861e+06f, 3.31432e+06f, -6.85386e-19f, 1.53575e-33f, 1.53647e-33f, 2.95387e+06f +path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4e4a08ff), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa1a181ff)); // 3.31432e+06f, 3.31432e+06f, 1.14845e-12f, 8.47397e+08f, 3.31432e+06f, -1.09442e-18f + testSimplify(reporter, path, filename); +} + +static void fuzz_x2(skiatest::Reporter* reporter, const char* filename) { + SkPath path; +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.cubicTo(SkBits2Float(0x1931204a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a4a34), SkBits2Float(0x4a4a4a4a)); // 9.15721e-24f, 1.14845e-12f, 3.31014e+06f, 3.31014e+06f, 3.31432e+06f, 3.31432e+06f +path.moveTo(SkBits2Float(0x000010a1), SkBits2Float(0x19312000)); // 5.96533e-42f, 9.15715e-24f +path.cubicTo(SkBits2Float(0x4a6a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa14a4a4a), SkBits2Float(0x08ff2ba1), SkBits2Float(0x08ff4a4a), SkBits2Float(0x4a344a4a)); // 3.83861e+06f, 3.31432e+06f, -6.85386e-19f, 1.53575e-33f, 1.53647e-33f, 2.95387e+06f +path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4e4a08ff), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa1a181ff)); // 3.31432e+06f, 3.31432e+06f, 1.14845e-12f, 8.47397e+08f, 3.31432e+06f, -1.09442e-18f + testSimplify(reporter, path, filename); +} + +static void fuzz763_1(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.cubicTo(SkBits2Float(0xbcb63000), SkBits2Float(0xb6b6b6b7), SkBits2Float(0x38b6b6b6), SkBits2Float(0xafb63a5a), SkBits2Float(0xca000087), SkBits2Float(0xe93ae9e9)); // -0.0222397f, -5.44529e-06f, 8.71247e-05f, -3.31471e-10f, -2.09719e+06f, -1.41228e+25f +path.quadTo(SkBits2Float(0xb6007fb6), SkBits2Float(0xb69fb6b6), SkBits2Float(0xe9e964b6), SkBits2Float(0xe9e9e9e9)); // -1.91478e-06f, -4.75984e-06f, -3.52694e+25f, -3.5348e+25f +path.quadTo(SkBits2Float(0xb6b6b8b7), SkBits2Float(0xb60000b6), SkBits2Float(0xb6b6b6b6), SkBits2Float(0xe9e92064)); // -5.44553e-06f, -1.90739e-06f, -5.44529e-06f, -3.52291e+25f +path.quadTo(SkBits2Float(0x000200e9), SkBits2Float(0xe9e9d100), SkBits2Float(0xe93ae9e9), SkBits2Float(0xe964b6e9)); // 1.83997e-40f, -3.53333e+25f, -1.41228e+25f, -1.72812e+25f +path.quadTo(SkBits2Float(0x40b6e9e9), SkBits2Float(0xe9b60000), SkBits2Float(0x00b6b8e9), SkBits2Float(0xe9000001)); // 5.71605f, -2.75031e+25f, 1.67804e-38f, -9.67141e+24f +path.quadTo(SkBits2Float(0xe9d3b6b2), SkBits2Float(0x40404540), SkBits2Float(0x803d4043), SkBits2Float(0xe9e9e9ff)); // -3.19933e+25f, 3.00423f, -5.62502e-39f, -3.53481e+25f +path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0xe8b3b6b6), SkBits2Float(0xe90a0003), SkBits2Float(0x4040403c), SkBits2Float(0x803d4040), SkBits2Float(0xe9e80900)); // 0, -6.78939e+24f, -1.0427e+25f, 3.00392f, -5.62501e-39f, -3.50642e+25f +path.quadTo(SkBits2Float(0xe9e910e9), SkBits2Float(0xe9e93ae9), SkBits2Float(0x0000b6b6), SkBits2Float(0xb6b6aab6)); // -3.52199e+25f, -3.52447e+25f, 6.55443e-41f, -5.4439e-06f +path.moveTo(SkBits2Float(0xe9e92064), SkBits2Float(0xe9e9d106)); // -3.52291e+25f, -3.53334e+25f +path.quadTo(SkBits2Float(0xe9e93ae9), SkBits2Float(0x0000abb6), SkBits2Float(0xb6b6bdb6), SkBits2Float(0xe92064b6)); // -3.52447e+25f, 6.15983e-41f, -5.44611e-06f, -1.2119e+25f +path.quadTo(SkBits2Float(0x0000e9e9), SkBits2Float(0xb6b6b6e9), SkBits2Float(0x05ffff05), SkBits2Float(0xe9ea06e9)); // 8.39112e-41f, -5.44532e-06f, 2.40738e-35f, -3.53652e+25f +path.quadTo(SkBits2Float(0xe93ae9e9), SkBits2Float(0x02007fe9), SkBits2Float(0xb8b7b600), SkBits2Float(0xe9e9b6b6)); // -1.41228e+25f, 9.44066e-38f, -8.76002e-05f, -3.53178e+25f +path.quadTo(SkBits2Float(0xe9e9e9b6), SkBits2Float(0xedb6b6b6), SkBits2Float(0x5a38a1b6), SkBits2Float(0xe93ae9e9)); // -3.53479e+25f, -7.06839e+27f, 1.29923e+16f, -1.41228e+25f +path.quadTo(SkBits2Float(0x0000b6b6), SkBits2Float(0xb6b6b6b6), SkBits2Float(0xe9e9e9b6), SkBits2Float(0xe9e9e954)); // 6.55443e-41f, -5.44529e-06f, -3.53479e+25f, -3.53477e+25f +path.quadTo(SkBits2Float(0xb6e9e93a), SkBits2Float(0x375837ff), SkBits2Float(0xceb6b6b6), SkBits2Float(0x0039e94f)); // -6.97109e-06f, 1.28876e-05f, -1.53271e+09f, 5.31832e-39f +path.quadTo(SkBits2Float(0xe9e9e9e9), SkBits2Float(0xe9e6e9e9), SkBits2Float(0xb6b641b6), SkBits2Float(0xede9e9e9)); // -3.5348e+25f, -3.48947e+25f, -5.43167e-06f, -9.0491e+27f +path.moveTo(SkBits2Float(0xb6b6e9e9), SkBits2Float(0xb6b60000)); // -5.45125e-06f, -5.42402e-06f +path.moveTo(SkBits2Float(0xe9b6b6b6), SkBits2Float(0xe9b6b8e9)); // -2.76109e+25f, -2.76122e+25f +path.close(); +path.moveTo(SkBits2Float(0xe9b6b6b6), SkBits2Float(0xe9b6b8e9)); // -2.76109e+25f, -2.76122e+25f +path.quadTo(SkBits2Float(0xe93ae9e9), SkBits2Float(0xe964b6e9), SkBits2Float(0x0000203a), SkBits2Float(0xb6000000)); // -1.41228e+25f, -1.72812e+25f, 1.15607e-41f, -1.90735e-06f +path.moveTo(SkBits2Float(0x64b6b6b6), SkBits2Float(0xe9e9e900)); // 2.69638e+22f, -3.53475e+25f +path.quadTo(SkBits2Float(0xb6b6b6e9), SkBits2Float(0xb6b6b6b6), SkBits2Float(0xe9e9b6ce), SkBits2Float(0xe9e93ae9)); // -5.44532e-06f, -5.44529e-06f, -3.53179e+25f, -3.52447e+25f + + testSimplifyFuzz(reporter, path, filename); +} + +static void fuzz763_2(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.cubicTo(SkBits2Float(0x76773011), SkBits2Float(0x5d66fe78), SkBits2Float(0xbbeeff66), SkBits2Float(0x637677a2), SkBits2Float(0x205266fe), SkBits2Float(0xec296fdf)); // 1.25339e+33f, 1.0403e+18f, -0.00729363f, 4.54652e+21f, 1.78218e-19f, -8.19347e+26f +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.close(); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.quadTo(SkBits2Float(0xec4eecec), SkBits2Float(0x6e6f10ec), SkBits2Float(0xb6b6ecf7), SkBits2Float(0xb6b6b6b6)); // -1.00063e+27f, 1.84968e+28f, -5.45161e-06f, -5.44529e-06f +path.moveTo(SkBits2Float(0x002032b8), SkBits2Float(0xecfeb6b6)); // 2.95693e-39f, -2.46344e+27f +path.moveTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273)); // 1.9288e+31f, 1.9278e+31f +path.cubicTo(SkBits2Float(0x1616ece4), SkBits2Float(0xdf020018), SkBits2Float(0x77772965), SkBits2Float(0x1009db73), SkBits2Float(0x80ececec), SkBits2Float(0xf7ffffff)); // 1.21917e-25f, -9.36751e+18f, 5.01303e+33f, 2.71875e-29f, -2.17582e-38f, -1.03846e+34f +path.lineTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273)); // 1.9288e+31f, 1.9278e+31f +path.close(); +path.moveTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273)); // 1.9288e+31f, 1.9278e+31f +path.conicTo(SkBits2Float(0xec0700ec), SkBits2Float(0xecececec), SkBits2Float(0xececccec), SkBits2Float(0x772965ec), SkBits2Float(0x77777377)); // -6.52837e+26f, -2.2914e+27f, -2.29019e+27f, 3.4358e+33f, 5.0189e+33f +path.moveTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266)); // -8.60376e+37f, -1.65964e+19f +path.close(); +path.moveTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266)); // -8.60376e+37f, -1.65964e+19f +path.quadTo(SkBits2Float(0x29ec02ec), SkBits2Float(0x1009ecec), SkBits2Float(0x80ececec), SkBits2Float(0xf7ffffff)); // 1.0481e-13f, 2.7201e-29f, -2.17582e-38f, -1.03846e+34f +path.lineTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266)); // -8.60376e+37f, -1.65964e+19f +path.close(); +path.moveTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266)); // -8.60376e+37f, -1.65964e+19f +path.conicTo(SkBits2Float(0xff003aff), SkBits2Float(0xdbec2300), SkBits2Float(0xecececec), SkBits2Float(0x6fdf6052), SkBits2Float(0x41ecec29)); // -1.70448e+38f, -1.32933e+17f, -2.2914e+27f, 1.38263e+29f, 29.6153f +path.lineTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266)); // -8.60376e+37f, -1.65964e+19f +path.close(); +path.moveTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266)); // -8.60376e+37f, -1.65964e+19f +path.quadTo(SkBits2Float(0xecf76e6f), SkBits2Float(0xeccfddec), SkBits2Float(0xecececcc), SkBits2Float(0x66000066)); // -2.39301e+27f, -2.01037e+27f, -2.2914e+27f, 1.51118e+23f +path.lineTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266)); // -8.60376e+37f, -1.65964e+19f +path.close(); +path.moveTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266)); // -8.60376e+37f, -1.65964e+19f +path.cubicTo(SkBits2Float(0x772965df), SkBits2Float(0x77777377), SkBits2Float(0x77777876), SkBits2Float(0x665266fe), SkBits2Float(0xecececdf), SkBits2Float(0x0285806e)); // 3.4358e+33f, 5.0189e+33f, 5.0193e+33f, 2.48399e+23f, -2.2914e+27f, 1.96163e-37f +path.lineTo(SkBits2Float(0xecececeb), SkBits2Float(0xecec0700)); // -2.2914e+27f, -2.28272e+27f +path.lineTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266)); // -8.60376e+37f, -1.65964e+19f +path.close(); +path.moveTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266)); // -8.60376e+37f, -1.65964e+19f +path.lineTo(SkBits2Float(0x65ecfaec), SkBits2Float(0xde777729)); // 1.39888e+23f, -4.45794e+18f +path.conicTo(SkBits2Float(0x74777777), SkBits2Float(0x66fe7876), SkBits2Float(0xecdf6660), SkBits2Float(0x726eecec), SkBits2Float(0x29d610ec)); // 7.84253e+31f, 6.00852e+23f, -2.16059e+27f, 4.73241e+30f, 9.50644e-14f +path.lineTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266)); // -8.60376e+37f, -1.65964e+19f +path.close(); +path.moveTo(SkBits2Float(0xd0ecec10), SkBits2Float(0x6e6eecdb)); // -3.17991e+10f, 1.84859e+28f +path.quadTo(SkBits2Float(0x003affec), SkBits2Float(0xec2300ef), SkBits2Float(0xecececdb), SkBits2Float(0xcfececec)); // 5.41827e-39f, -7.88237e+26f, -2.2914e+27f, -7.9499e+09f +path.lineTo(SkBits2Float(0xd0ecec10), SkBits2Float(0x6e6eecdb)); // -3.17991e+10f, 1.84859e+28f +path.close(); +path.moveTo(SkBits2Float(0xd0ecec10), SkBits2Float(0x6e6eecdb)); // -3.17991e+10f, 1.84859e+28f +path.quadTo(SkBits2Float(0xecccec80), SkBits2Float(0xfa66ecec), SkBits2Float(0x66fa0000), SkBits2Float(0x772965df)); // -1.9819e+27f, -2.99758e+35f, 5.90296e+23f, 3.4358e+33f +path.moveTo(SkBits2Float(0x77777790), SkBits2Float(0x00807677)); // 5.01923e+33f, 1.17974e-38f +path.close(); +path.moveTo(SkBits2Float(0x77777790), SkBits2Float(0x00807677)); // 5.01923e+33f, 1.17974e-38f +path.cubicTo(SkBits2Float(0xecececec), SkBits2Float(0xfe66eaec), SkBits2Float(0xecdf1452), SkBits2Float(0x806eecec), SkBits2Float(0x10ececec), SkBits2Float(0xec000000)); // -2.2914e+27f, -7.67356e+37f, -2.15749e+27f, -1.01869e-38f, 9.34506e-29f, -6.1897e+26f +path.lineTo(SkBits2Float(0x77777790), SkBits2Float(0x00807677)); // 5.01923e+33f, 1.17974e-38f +path.close(); +path.moveTo(SkBits2Float(0x77777790), SkBits2Float(0x00807677)); // 5.01923e+33f, 1.17974e-38f +path.cubicTo(SkBits2Float(0x52668062), SkBits2Float(0x2965df66), SkBits2Float(0x77777377), SkBits2Float(0x76777773), SkBits2Float(0x1697fe78), SkBits2Float(0xeebfff00)); // 2.47499e+11f, 5.1042e-14f, 5.0189e+33f, 1.2548e+33f, 2.4556e-25f, -2.971e+28f +path.lineTo(SkBits2Float(0x77777790), SkBits2Float(0x00807677)); // 5.01923e+33f, 1.17974e-38f +path.close(); + + testSimplifyFuzz(reporter, path, filename); +} + +#define TEST(test) test(reporter, #test) + DEF_TEST(PathOpsSimplifyFail, reporter) { + TEST(fuzz763_2); + TEST(fuzz763_1); + TEST(fuzz_x2); + TEST(fuzz_x1); + TEST(fuzz_59); for (int index = 0; index < (int) (13 * nonFinitePtsCount * finitePtsCount); ++index) { failOne(reporter, index); } @@ -103,12 +231,14 @@ } } +#undef TEST + DEF_TEST(PathOpsSimplifyFailOne, reporter) { int index = 0; failOne(reporter, index); } DEF_TEST(PathOpsSimplifyDontFailOne, reporter) { - int index = 13; + int index = 17; dontFailOne(reporter, index); }
diff --git a/src/third_party/skia/tests/PathOpsSimplifyQuadThreadedTest.cpp b/src/third_party/skia/tests/PathOpsSimplifyQuadThreadedTest.cpp index 3c92cca..55dc07b 100644 --- a/src/third_party/skia/tests/PathOpsSimplifyQuadThreadedTest.cpp +++ b/src/third_party/skia/tests/PathOpsSimplifyQuadThreadedTest.cpp
@@ -6,16 +6,15 @@ */ #include "PathOpsExtendedTest.h" #include "PathOpsThreadedCommon.h" +#include "SkString.h" + +static int quadTest = 66; static void testSimplifyQuadsMain(PathOpsThreadState* data) { SkASSERT(data); PathOpsThreadState& state = *data; - char pathStr[1024]; - bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter? - if (progress) { - sk_bzero(pathStr, sizeof(pathStr)); - } + SkString pathStr; int ax = state.fA & 0x03; int ay = state.fA >> 2; int bx = state.fB & 0x03; @@ -48,25 +47,28 @@ path.quadTo(SkIntToScalar(gx), SkIntToScalar(gy), SkIntToScalar(hx), SkIntToScalar(hy)); path.close(); - if (progress) { - // gdb: set print elements 400 - char* str = pathStr; - str += sprintf(str, " path.moveTo(%d, %d);\n", ax, ay); - str += sprintf(str, " path.quadTo(%d, %d, %d, %d);\n", bx, by, cx, cy); - str += sprintf(str, " path.lineTo(%d, %d);\n", dx, dy); - str += sprintf(str, " path.close();\n"); - str += sprintf(str, " path.moveTo(%d, %d);\n", ex, ey); - str += sprintf(str, " path.lineTo(%d, %d);\n", fx, fy); - str += sprintf(str, " path.quadTo(%d, %d, %d, %d);\n", gx, gy, hx, hy); - str += sprintf(str, " path.close();\n"); - outputProgress(state.fPathStr, pathStr, SkPath::kWinding_FillType); + if (state.fReporter->verbose()) { + pathStr.printf("static void testQuads%d(skiatest::Reporter* reporter," + "const char* filename) {\n", quadTest); + pathStr.appendf(" SkPath path;\n"); + pathStr.appendf(" path.moveTo(%d, %d);\n", ax, ay); + pathStr.appendf(" path.quadTo(%d, %d, %d, %d);\n", bx, by, cx, cy); + pathStr.appendf(" path.lineTo(%d, %d);\n", dx, dy); + pathStr.appendf(" path.close();\n"); + pathStr.appendf(" path.moveTo(%d, %d);\n", ex, ey); + pathStr.appendf(" path.lineTo(%d, %d);\n", fx, fy); + pathStr.appendf(" path.quadTo(%d, %d, %d, %d);\n", gx, gy, hx, hy); + pathStr.appendf(" path.close();\n"); + pathStr.appendf(" testSimplify(reporter, path, filename);\n"); + pathStr.appendf("}\n"); + state.outputProgress(pathStr.c_str(), SkPath::kWinding_FillType); } - testSimplify(path, false, out, state, pathStr); + testSimplify(path, false, out, state, pathStr.c_str()); path.setFillType(SkPath::kEvenOdd_FillType); - if (progress) { - outputProgress(state.fPathStr, pathStr, SkPath::kEvenOdd_FillType); + if (state.fReporter->verbose()) { + state.outputProgress(pathStr.c_str(), SkPath::kEvenOdd_FillType); } - testSimplify(path, true, out, state, pathStr); + testSimplify(path, true, out, state, pathStr.c_str()); } } } @@ -81,8 +83,8 @@ for (int b = a ; b < 16; ++b) { for (int c = b ; c < 16; ++c) { for (int d = c; d < 16; ++d) { - *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable, - (&testSimplifyQuadsMain, a, b, c, d, &testRunner)); + *testRunner.fRunnables.append() = new PathOpsThreadedRunnable( + &testSimplifyQuadsMain, a, b, c, d, &testRunner); } if (!reporter->allowExtendedTest()) goto finish; } @@ -90,5 +92,4 @@ } finish: testRunner.render(); - ShowTestArray(); }
diff --git a/src/third_party/skia/tests/PathOpsSimplifyQuadralateralsThreadedTest.cpp b/src/third_party/skia/tests/PathOpsSimplifyQuadralateralsThreadedTest.cpp old mode 100755 new mode 100644 index f8e9a6e..6133042 --- a/src/third_party/skia/tests/PathOpsSimplifyQuadralateralsThreadedTest.cpp +++ b/src/third_party/skia/tests/PathOpsSimplifyQuadralateralsThreadedTest.cpp
@@ -6,16 +6,15 @@ */ #include "PathOpsExtendedTest.h" #include "PathOpsThreadedCommon.h" +#include "SkString.h" + +static int loopNo = 1; static void testSimplifyQuadralateralsMain(PathOpsThreadState* data) { SkASSERT(data); PathOpsThreadState& state = *data; - char pathStr[1024]; - bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter? - if (progress) { - sk_bzero(pathStr, sizeof(pathStr)); - } + SkString pathStr; int ax = state.fA & 0x03; int ay = state.fA >> 2; int bx = state.fB & 0x03; @@ -48,27 +47,30 @@ path.lineTo(SkIntToScalar(gx), SkIntToScalar(gy)); path.lineTo(SkIntToScalar(hx), SkIntToScalar(hy)); path.close(); - if (progress) { - // gdb: set print elements 400 - char* str = pathStr; - str += sprintf(str, " path.moveTo(%d, %d);\n", ax, ay); - str += sprintf(str, " path.lineTo(%d, %d);\n", bx, by); - str += sprintf(str, " path.lineTo(%d, %d);\n", cx, cy); - str += sprintf(str, " path.lineTo(%d, %d);\n", dx, dy); - str += sprintf(str, " path.close();\n"); - str += sprintf(str, " path.moveTo(%d, %d);\n", ex, ey); - str += sprintf(str, " path.lineTo(%d, %d);\n", fx, fy); - str += sprintf(str, " path.lineTo(%d, %d);\n", gx, gy); - str += sprintf(str, " path.lineTo(%d, %d);\n", hx, hy); - str += sprintf(str, " path.close();\n"); - outputProgress(state.fPathStr, pathStr, SkPath::kWinding_FillType); + if (state.fReporter->verbose()) { + pathStr.printf("static void quadralateralSimplify%d(skiatest::Reporter*" + "reporter, const char* filename) {\n", loopNo); + pathStr.appendf(" SkPath path;\n"); + pathStr.appendf(" path.moveTo(%d, %d);\n", ax, ay); + pathStr.appendf(" path.lineTo(%d, %d);\n", bx, by); + pathStr.appendf(" path.lineTo(%d, %d);\n", cx, cy); + pathStr.appendf(" path.lineTo(%d, %d);\n", dx, dy); + pathStr.appendf(" path.close();\n"); + pathStr.appendf(" path.moveTo(%d, %d);\n", ex, ey); + pathStr.appendf(" path.lineTo(%d, %d);\n", fx, fy); + pathStr.appendf(" path.lineTo(%d, %d);\n", gx, gy); + pathStr.appendf(" path.lineTo(%d, %d);\n", hx, hy); + pathStr.appendf(" path.close();\n"); + pathStr.appendf(" testPathSimplify(reporter, path, filename);\n"); + pathStr.appendf("}\n"); + state.outputProgress(pathStr.c_str(), SkPath::kWinding_FillType); } - testSimplify(path, false, out, state, pathStr); + testSimplify(path, false, out, state, pathStr.c_str()); path.setFillType(SkPath::kEvenOdd_FillType); - if (progress) { - outputProgress(state.fPathStr, pathStr, SkPath::kEvenOdd_FillType); + if (state.fReporter->verbose()) { + state.outputProgress(pathStr.c_str(), SkPath::kEvenOdd_FillType); } - testSimplify(path, true, out, state, pathStr); + testSimplify(path, true, out, state, pathStr.c_str()); } } } @@ -82,8 +84,8 @@ for (int b = a ; b < 16; ++b) { for (int c = b ; c < 16; ++c) { for (int d = c; d < 16; ++d) { - *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable, - (&testSimplifyQuadralateralsMain, a, b, c, d, &testRunner)); + *testRunner.fRunnables.append() = new PathOpsThreadedRunnable( + &testSimplifyQuadralateralsMain, a, b, c, d, &testRunner); } if (!reporter->allowExtendedTest()) goto finish; }
diff --git a/src/third_party/skia/tests/PathOpsSimplifyRectThreadedTest.cpp b/src/third_party/skia/tests/PathOpsSimplifyRectThreadedTest.cpp index 52a78ec..384d1b0 100644 --- a/src/third_party/skia/tests/PathOpsSimplifyRectThreadedTest.cpp +++ b/src/third_party/skia/tests/PathOpsSimplifyRectThreadedTest.cpp
@@ -6,6 +6,7 @@ */ #include "PathOpsExtendedTest.h" #include "PathOpsThreadedCommon.h" +#include "SkString.h" // four rects, of four sizes // for 3 smaller sizes, tall, wide @@ -18,11 +19,6 @@ { SkASSERT(data); PathOpsThreadState& state = *data; - char pathStr[1024]; // gdb: set print elements 400 - bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter? - if (progress) { - sk_bzero(pathStr, sizeof(pathStr)); - } int aShape = state.fA & 0x03; SkPath::Direction aCW = state.fA >> 2 ? SkPath::kCCW_Direction : SkPath::kCW_Direction; int bShape = state.fB & 0x03; @@ -39,10 +35,11 @@ for (int cYAlign = 0; cYAlign < 5; ++cYAlign) { for (int dXAlign = 0; dXAlign < 5; ++dXAlign) { for (int dYAlign = 0; dYAlign < 5; ++dYAlign) { + SkString pathStr; SkPath path, out; - char* str = pathStr; path.setFillType(SkPath::kWinding_FillType); - int l, t, r, b; + int l SK_INIT_TO_AVOID_WARNING, t SK_INIT_TO_AVOID_WARNING, + r SK_INIT_TO_AVOID_WARNING, b SK_INIT_TO_AVOID_WARNING; if (aShape) { switch (aShape) { case 1: // square @@ -66,8 +63,8 @@ } path.addRect(SkIntToScalar(l), SkIntToScalar(t), SkIntToScalar(r), SkIntToScalar(b), aCW); - if (progress) { - str += sprintf(str, " path.addRect(%d, %d, %d, %d," + if (state.fReporter->verbose()) { + pathStr.appendf(" path.addRect(%d, %d, %d, %d," " SkPath::kC%sW_Direction);\n", l, t, r, b, aCW ? "C" : ""); } } else { @@ -97,8 +94,8 @@ } path.addRect(SkIntToScalar(l), SkIntToScalar(t), SkIntToScalar(r), SkIntToScalar(b), bCW); - if (progress) { - str += sprintf(str, " path.addRect(%d, %d, %d, %d," + if (state.fReporter->verbose()) { + pathStr.appendf(" path.addRect(%d, %d, %d, %d," " SkPath::kC%sW_Direction);\n", l, t, r, b, bCW ? "C" : ""); } } else { @@ -128,8 +125,8 @@ } path.addRect(SkIntToScalar(l), SkIntToScalar(t), SkIntToScalar(r), SkIntToScalar(b), cCW); - if (progress) { - str += sprintf(str, " path.addRect(%d, %d, %d, %d," + if (state.fReporter->verbose()) { + pathStr.appendf(" path.addRect(%d, %d, %d, %d," " SkPath::kC%sW_Direction);\n", l, t, r, b, cCW ? "C" : ""); } } else { @@ -159,8 +156,8 @@ } path.addRect(SkIntToScalar(l), SkIntToScalar(t), SkIntToScalar(r), SkIntToScalar(b), dCW); - if (progress) { - str += sprintf(str, " path.addRect(%d, %d, %d, %d," + if (state.fReporter->verbose()) { + pathStr.appendf(" path.addRect(%d, %d, %d, %d," " SkPath::kC%sW_Direction);\n", l, t, r, b, dCW ? "C" : ""); } } else { @@ -168,14 +165,14 @@ dYAlign = 5; } path.close(); - if (progress) { - outputProgress(state.fPathStr, pathStr, SkPath::kWinding_FillType); + if (state.fReporter->verbose()) { + state.outputProgress(pathStr.c_str(), SkPath::kWinding_FillType); } - testSimplify(path, false, out, state, pathStr); - if (progress) { - outputProgress(state.fPathStr, pathStr, SkPath::kEvenOdd_FillType); + testSimplify(path, false, out, state, pathStr.c_str()); + if (state.fReporter->verbose()) { + state.outputProgress(pathStr.c_str(), SkPath::kEvenOdd_FillType); } - testSimplify(path, true, out, state, pathStr); + testSimplify(path, true, out, state, pathStr.c_str()); } } } @@ -193,8 +190,8 @@ for (int b = a ; b < 8; ++b) { for (int c = b ; c < 8; ++c) { for (int d = c; d < 8; ++d) { - *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable, - (&testSimplify4x4RectsMain, a, b, c, d, &testRunner)); + *testRunner.fRunnables.append() = new PathOpsThreadedRunnable( + &testSimplify4x4RectsMain, a, b, c, d, &testRunner); } if (!reporter->allowExtendedTest()) goto finish; }
diff --git a/src/third_party/skia/tests/PathOpsSimplifyTest.cpp b/src/third_party/skia/tests/PathOpsSimplifyTest.cpp index 88547a0..2de67ff 100644 --- a/src/third_party/skia/tests/PathOpsSimplifyTest.cpp +++ b/src/third_party/skia/tests/PathOpsSimplifyTest.cpp
@@ -3649,7 +3649,7 @@ testSimplify(reporter, path, filename); } -// A test this for this case: +// A test for this case: // contourA has two segments that are coincident // contourB has two segments that are coincident in the same place // each ends up with +2/0 pairs for winding count @@ -4506,8 +4506,6 @@ testSimplify(reporter, path, filename); } -// this fails because there is a short unorderable segment and the unordered state isn't handled -// correctly later on. static void testQuads46x(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -4679,9 +4677,3064 @@ testSimplify(reporter, path, filename); } +static void testRect4(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.addRect(0, 0, 30, 60, SkPath::kCCW_Direction); + path.addRect(10, 0, 40, 30, SkPath::kCCW_Direction); + path.addRect(20, 0, 30, 40, SkPath::kCCW_Direction); + path.addRect(32, 0, 36, 41, SkPath::kCCW_Direction); + testSimplify(reporter, path, filename); +} + +static void testQuads62(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.moveTo(3, 2); + path.quadTo(1, 3, 3, 3); + path.lineTo(3, 3); + path.close(); + path.moveTo(0, 0); + path.lineTo(2, 0); + path.quadTo(1, 3, 3, 3); + path.close(); + testSimplify(reporter, path, filename); +} + +static void testQuads63(skiatest::Reporter* reporter,const char* filename) { + SkPath path; + path.moveTo(3, 0); + path.quadTo(0, 1, 1, 2); + path.lineTo(3, 3); + path.close(); + path.moveTo(0, 0); + path.lineTo(1, 1); + path.quadTo(0, 2, 3, 3); + path.close(); + testSimplify(reporter, path, filename); +} + +static void testQuads64(skiatest::Reporter* reporter,const char* filename) { + SkPath path; + path.moveTo(3, 0); + path.quadTo(0, 1, 1, 2); + path.lineTo(2, 2); + path.close(); + path.moveTo(1, 0); + path.lineTo(1, 1); + path.quadTo(0, 2, 3, 3); + path.close(); + testSimplify(reporter, path, filename); +} + +static void testTriangle1(skiatest::Reporter* reporter,const char* filename) { + SkPath path; + path.moveTo(0, 0); + path.lineTo(1, 0); + path.lineTo(2, 3); + path.close(); + path.moveTo(0, 0); + path.lineTo(1, 2); + path.lineTo(1, 0); + path.close(); + testSimplify(reporter, path, filename); +} + +static void testTriangle2(skiatest::Reporter* reporter,const char* filename) { + SkPath path; + path.moveTo(0, 0); + path.lineTo(1, 0); + path.lineTo(0, 1); + path.close(); + path.moveTo(2, 0); + path.lineTo(0, 2); + path.lineTo(2, 2); + path.close(); + testSimplify(reporter, path, filename); +} + +static void testArc(skiatest::Reporter* reporter,const char* filename) { + SkRect r = SkRect::MakeWH(150, 100); + SkPath path; + path.arcTo(r, 0, 0.0025f, false); + testSimplify(reporter, path, filename); +} + +static void testIssue3838(skiatest::Reporter* reporter,const char* filename) { + SkPath path; + path.moveTo(220, 170); + path.lineTo(200, 170); + path.lineTo(200, 190); + path.lineTo(180, 190); + path.lineTo(180, 210); + path.lineTo(200, 210); + path.lineTo(200, 250); + path.lineTo(260, 250); + path.lineTo(260, 190); + path.lineTo(220, 190); + path.lineTo(220, 170); + path.close(); + path.moveTo(220, 210); + path.lineTo(220, 230); + path.lineTo(240, 230); + path.lineTo(240, 210); + path.lineTo(220, 210); + path.close(); + testSimplify(reporter, path, filename); +} + +static void testIssue3838_3(skiatest::Reporter* reporter,const char* filename) { + SkPath path; + path.moveTo(40, 10); + path.lineTo(60, 10); + path.lineTo(60, 30); + path.lineTo(40, 30); + path.lineTo(40, 10); + path.moveTo(41, 11); + path.lineTo(41, 29); + path.lineTo(59, 29); + path.lineTo(59, 11); + path.lineTo(41, 11); + testSimplify(reporter, path, filename); +} + +static void testQuads65(skiatest::Reporter* reporter,const char* filename) { + SkPath path; + path.moveTo(1, 2); + path.quadTo(3, 2, 0, 3); + path.lineTo(1, 3); + path.close(); + path.moveTo(1, 0); + path.lineTo(1, 2); + path.quadTo(3, 2, 1, 3); + path.close(); + testSimplify(reporter, path, filename); +} + +static void fuzz864a(skiatest::Reporter* reporter,const char* filename) { + SkPath path; + path.moveTo(10, 90); + path.lineTo(10, 90); + path.lineTo(10, 30); + path.lineTo(10, 30); + path.lineTo(10, 90); + path.close(); + path.moveTo(10, 90); + path.lineTo(10, 90); + path.lineTo(10, 30); + path.lineTo(10, 30); + path.lineTo(10, 90); + path.close(); + path.moveTo(10, 90); + path.lineTo(110, 90); + path.lineTo(110, 30); + path.lineTo(10, 30); + path.lineTo(10, 90); + path.close(); + path.moveTo(10, 30); + path.lineTo(32678, 30); + path.lineTo(32678, 30); + path.lineTo(10, 30); + path.close(); + path.moveTo(10, 3.35545e+07f); + path.lineTo(110, 3.35545e+07f); + path.lineTo(110, 30); + path.lineTo(10, 30); + path.lineTo(10, 3.35545e+07f); + path.close(); + path.moveTo(10, 315); + path.lineTo(110, 315); + path.lineTo(110, 255); + path.lineTo(10, 255); + path.lineTo(10, 315); + path.close(); + path.moveTo(0, 60); + path.lineTo(100, 60); + path.lineTo(100, 0); + path.lineTo(0, 0); + path.lineTo(0, 60); + path.close(); + path.moveTo(10, 90); + path.lineTo(110, 90); + path.lineTo(110, 30); + path.lineTo(10, 30); + path.lineTo(10, 90); + path.close(); + path.moveTo(10, 3.35545e+07f); + path.lineTo(110, 3.35545e+07f); + path.lineTo(110, 30); + path.lineTo(10, 30); + path.lineTo(10, 3.35545e+07f); + path.close(); + path.moveTo(10, 90); + path.lineTo(110, 90); + path.lineTo(110, 30); + path.lineTo(10, 30); + path.lineTo(10, 90); + path.close(); + testSimplify(reporter, path, filename); +} + +static void cr514118(skiatest::Reporter* reporter,const char* filename) { + SkPath path; +path.moveTo(SkBits2Float(0x42c80000), SkBits2Float(0x42480000)); // 100, 50 +path.conicTo(SkBits2Float(0x42c80000), SkBits2Float(0x00000000), SkBits2Float(0x42480000), SkBits2Float(0x00000000), SkBits2Float(0x3f3504f3)); // 100, 0, 50, 0, 0.707107f +path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3)); // 0, 0, 0, 50, 0.707107f +path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x42c80000), SkBits2Float(0x42480000), SkBits2Float(0x42c80000), SkBits2Float(0x3f3504f3)); // 0, 100, 50, 100, 0.707107f +path.conicTo(SkBits2Float(0x42c80000), SkBits2Float(0x42c80000), SkBits2Float(0x42c80000), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3)); // 100, 100, 100, 50, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x42c80133), SkBits2Float(0x42480000)); // 100.002f, 50 +path.conicTo(SkBits2Float(0x42c80133), SkBits2Float(0x00000000), SkBits2Float(0x42480267), SkBits2Float(0x00000000), SkBits2Float(0x3f3504f3)); // 100.002f, 0, 50.0023f, 0, 0.707107f +path.conicTo(SkBits2Float(0x3b19b530), SkBits2Float(0x00000000), SkBits2Float(0x3b19b530), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3)); // 0.00234539f, 0, 0.00234539f, 50, 0.707107f +path.conicTo(SkBits2Float(0x3b19b530), SkBits2Float(0x42c80000), SkBits2Float(0x42480267), SkBits2Float(0x42c80000), SkBits2Float(0x3f3504f3)); // 0.00234539f, 100, 50.0023f, 100, 0.707107f +path.conicTo(SkBits2Float(0x42c80133), SkBits2Float(0x42c80000), SkBits2Float(0x42c80133), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3)); // 100.002f, 100, 100.002f, 50, 0.707107f +path.close(); + testSimplify(reporter, path, filename); +} + +static void fuzz994s_11(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000)); // 10, 90 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000)); // 10, 90 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000)); // 10, 30 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000)); // 10, 30 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000)); // 10, 90 +path.close(); +path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000)); // 10, 90 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000)); // 10, 90 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000)); // 10, 30 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000)); // 10, 30 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000)); // 10, 90 +path.close(); +path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000)); // 10, 90 +path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x42b40000)); // 110, 90 +path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x41f00000)); // 110, 30 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000)); // 10, 30 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000)); // 10, 90 +path.close(); +path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000)); // 10, 30 +path.lineTo(SkBits2Float(0x46ff4c00), SkBits2Float(0x41f00000)); // 32678, 30 +path.lineTo(SkBits2Float(0x46ff4c00), SkBits2Float(0x41f00000)); // 32678, 30 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000)); // 10, 30 +path.close(); +path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x4c000006)); // 10, 3.35545e+07f +path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x4c000006)); // 110, 3.35545e+07f +path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x41f00000)); // 110, 30 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000)); // 10, 30 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x4c000006)); // 10, 3.35545e+07f +path.close(); +path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x439d8000)); // 10, 315 +path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x439d8000)); // 110, 315 +path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x437f0000)); // 110, 255 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x437f0000)); // 10, 255 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x439d8000)); // 10, 315 +path.close(); +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x42700000)); // 0, 60 +path.lineTo(SkBits2Float(0x42c80000), SkBits2Float(0x42700000)); // 100, 60 +path.lineTo(SkBits2Float(0x42c80000), SkBits2Float(0x00000000)); // 100, 0 +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x42700000)); // 0, 60 +path.close(); +path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000)); // 10, 90 +path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x42b40000)); // 110, 90 +path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x41f00000)); // 110, 30 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000)); // 10, 30 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000)); // 10, 90 +path.close(); +path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x4c000006)); // 10, 3.35545e+07f +path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x4c000006)); // 110, 3.35545e+07f +path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x41f00000)); // 110, 30 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000)); // 10, 30 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x4c000006)); // 10, 3.35545e+07f +path.close(); +path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000)); // 10, 90 +path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x42b40000)); // 110, 90 +path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x41f00000)); // 110, 30 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000)); // 10, 30 +path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000)); // 10, 90 +path.close(); + + testSimplify(reporter, path, filename); +} + +static void fuzz994s_3414(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42c80000), SkBits2Float(0x42480000)); // 100, 50 +path.conicTo(SkBits2Float(0x42c80000), SkBits2Float(0x00000000), SkBits2Float(0x42480000), SkBits2Float(0x00000000), SkBits2Float(0x3f3504f3)); // 100, 0, 50, 0, 0.707107f +path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3)); // 0, 0, 0, 50, 0.707107f +path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x42c80000), SkBits2Float(0x42480000), SkBits2Float(0x42c80000), SkBits2Float(0x3f3504f3)); // 0, 100, 50, 100, 0.707107f +path.conicTo(SkBits2Float(0x42c80000), SkBits2Float(0x42c80000), SkBits2Float(0x42c80000), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3)); // 100, 100, 100, 50, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x42c84964), SkBits2Float(0x42480000)); // 100.143f, 50 +path.conicTo(SkBits2Float(0x42c84964), SkBits2Float(0x00000000), SkBits2Float(0x424892c8), SkBits2Float(0x00000000), SkBits2Float(0x3f3504f3)); // 100.143f, 0, 50.1433f, 0, 0.707107f +path.conicTo(SkBits2Float(0x3e12c788), SkBits2Float(0x00000000), SkBits2Float(0x3e12c788), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3)); // 0.143339f, 0, 0.143339f, 50, 0.707107f +path.conicTo(SkBits2Float(0x3e12c788), SkBits2Float(0x42c80000), SkBits2Float(0x424892c8), SkBits2Float(0x42c80000), SkBits2Float(0x3f3504f3)); // 0.143339f, 100, 50.1433f, 100, 0.707107f +path.conicTo(SkBits2Float(0x42c84964), SkBits2Float(0x42c80000), SkBits2Float(0x42c84964), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3)); // 100.143f, 100, 100.143f, 50, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x42c80000), SkBits2Float(0x42480000)); // 100, 50 +path.conicTo(SkBits2Float(0x42c80000), SkBits2Float(0x00000000), SkBits2Float(0x42480000), SkBits2Float(0x00000000), SkBits2Float(0x3f3504f3)); // 100, 0, 50, 0, 0.707107f +path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3)); // 0, 0, 0, 50, 0.707107f +path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x42c80000), SkBits2Float(0x42480000), SkBits2Float(0x42c80000), SkBits2Float(0x3f3504f3)); // 0, 100, 50, 100, 0.707107f +path.conicTo(SkBits2Float(0x42c80000), SkBits2Float(0x42c80000), SkBits2Float(0x42c80000), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3)); // 100, 100, 100, 50, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x4c00006b), SkBits2Float(0x424c0000)); // 3.35549e+07f, 51 +path.conicTo(SkBits2Float(0x4c00006b), SkBits2Float(0xcbffffe5), SkBits2Float(0x43d6e720), SkBits2Float(0xcbffffe5), SkBits2Float(0x3f3504f3)); // 3.35549e+07f, -3.35544e+07f, 429.806f, -3.35544e+07f, 0.707107f +path.conicTo(SkBits2Float(0xcbffff28), SkBits2Float(0xcbffffe5), SkBits2Float(0xcbffff28), SkBits2Float(0x424c0000), SkBits2Float(0x3f3504f3)); // -3.3554e+07f, -3.35544e+07f, -3.3554e+07f, 51, 0.707107f +path.conicTo(SkBits2Float(0xcbffff28), SkBits2Float(0x4c00000c), SkBits2Float(0x43d6e720), SkBits2Float(0x4c00000c), SkBits2Float(0x3f3504f3)); // -3.3554e+07f, 3.35545e+07f, 429.806f, 3.35545e+07f, 0.707107f +path.conicTo(SkBits2Float(0x4c00006b), SkBits2Float(0x4c00000c), SkBits2Float(0x4c00006b), SkBits2Float(0x424c0000), SkBits2Float(0x3f3504f3)); // 3.35549e+07f, 3.35545e+07f, 3.35549e+07f, 51, 0.707107f +path.close(); +path.moveTo(SkBits2Float(0x43ef6720), SkBits2Float(0x42480000)); // 478.806f, 50 +path.conicTo(SkBits2Float(0x43ef6720), SkBits2Float(0x00000000), SkBits2Float(0x43d66720), SkBits2Float(0x00000000), SkBits2Float(0x3f3504f3)); // 478.806f, 0, 428.806f, 0, 0.707107f +path.conicTo(SkBits2Float(0x43bd6720), SkBits2Float(0x00000000), SkBits2Float(0x43bd6720), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3)); // 378.806f, 0, 378.806f, 50, 0.707107f +path.conicTo(SkBits2Float(0x43bd6720), SkBits2Float(0x42c80000), SkBits2Float(0x43d66720), SkBits2Float(0x42c80000), SkBits2Float(0x3f3504f3)); // 378.806f, 100, 428.806f, 100, 0.707107f +path.conicTo(SkBits2Float(0x43ef6720), SkBits2Float(0x42c80000), SkBits2Float(0x43ef6720), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3)); // 478.806f, 100, 478.806f, 50, 0.707107f +path.close(); + + testSimplify(reporter, path, filename); +} + +static void fuzz_twister(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(0, 600); +path.lineTo(3.35544e+07f, 600); +path.lineTo(3.35544e+07f, 0); +path.lineTo(0, 0); +path.lineTo(0, 600); +path.close(); +path.moveTo(63, 600); +path.lineTo(3.35545e+07f, 600); +path.lineTo(3.35545e+07f, 0); +path.lineTo(63, 0); +path.lineTo(63, 600); +path.close(); +path.moveTo(93, 600); +path.lineTo(3.35545e+07f, 600); +path.lineTo(3.35545e+07f, 0); +path.lineTo(93, 0); +path.lineTo(93, 600); +path.close(); +path.moveTo(123, 600); +path.lineTo(3.35546e+07f, 600); +path.lineTo(3.35546e+07f, 0); +path.lineTo(123, 0); +path.lineTo(123, 600); +path.close(); + testSimplify(reporter, path, filename); +} + +static void fuzz_twister2(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x44160000)); // 0, 600 +path.lineTo(SkBits2Float(0x4bfffffe), SkBits2Float(0x44160000)); // 3.35544e+07f, 600 +path.lineTo(SkBits2Float(0x4bfffffe), SkBits2Float(0x00000000)); // 3.35544e+07f, 0 +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x44160000)); // 0, 600 +path.close(); + +path.moveTo(SkBits2Float(0x427c0000), SkBits2Float(0x00000000)); // 63, 0 +path.lineTo(SkBits2Float(0x4c00000f), SkBits2Float(0x00000000)); // 3.35545e+07f, 0 +path.lineTo(SkBits2Float(0x4c00000f), SkBits2Float(0x00000000)); // 3.35545e+07f, 0 +path.lineTo(SkBits2Float(0x427c0000), SkBits2Float(0x00000000)); // 63, 0 +path.close(); + +path.moveTo(SkBits2Float(0x42ba0000), SkBits2Float(0x00000000)); // 93, 0 +path.lineTo(SkBits2Float(0x4c000016), SkBits2Float(0x00000000)); // 3.35545e+07f, 0 +path.lineTo(SkBits2Float(0x4c000016), SkBits2Float(0x00000000)); // 3.35545e+07f, 0 +path.lineTo(SkBits2Float(0x42ba0000), SkBits2Float(0x00000000)); // 93, 0 +path.close(); + +path.moveTo(SkBits2Float(0x42f60000), SkBits2Float(0x00000000)); // 123, 0 +path.lineTo(SkBits2Float(0x4c00001e), SkBits2Float(0x00000000)); // 3.35546e+07f, 0 +path.lineTo(SkBits2Float(0x4c00001e), SkBits2Float(0x00000000)); // 3.35546e+07f, 0 +path.lineTo(SkBits2Float(0x42f60000), SkBits2Float(0x00000000)); // 123, 0 +path.close(); + + testSimplify(reporter, path, filename); +} + +static void fuzz763_4713_b(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42240000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000)); +path.close(); + +path.moveTo(SkBits2Float(0x4204f72e), SkBits2Float(0x41c56cd2)); +path.quadTo(SkBits2Float(0x42123842), SkBits2Float(0x41c52adf), SkBits2Float(0x421baed7), SkBits2Float(0x41d7bac6)); +path.quadTo(SkBits2Float(0x4225256d), SkBits2Float(0x41ea4aad), SkBits2Float(0x42254667), SkBits2Float(0x4202666b)); +path.quadTo(SkBits2Float(0x42256760), SkBits2Float(0x420fa77f), SkBits2Float(0x421c1f6c), SkBits2Float(0x42191e14)); +path.quadTo(SkBits2Float(0x421bff97), SkBits2Float(0x42193e89), SkBits2Float(0x421bdf6b), SkBits2Float(0x42195eb8)); +path.quadTo(SkBits2Float(0x421bbff6), SkBits2Float(0x42197f32), SkBits2Float(0x421ba03b), SkBits2Float(0x42199f57)); +path.quadTo(SkBits2Float(0x421b605e), SkBits2Float(0x4219e00a), SkBits2Float(0x421b1fa8), SkBits2Float(0x421a1f22)); +path.quadTo(SkBits2Float(0x421ae0f1), SkBits2Float(0x421a604b), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000)); +path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000)); +path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3)); +path.quadTo(SkBits2Float(0x41db19b1), SkBits2Float(0x41da63d5), SkBits2Float(0x41db755b), SkBits2Float(0x41da0a9b)); +path.quadTo(SkBits2Float(0x41dbce01), SkBits2Float(0x41d9ae59), SkBits2Float(0x41dc285e), SkBits2Float(0x41d952ce)); +path.quadTo(SkBits2Float(0x41dc55b6), SkBits2Float(0x41d924df), SkBits2Float(0x41dc82cd), SkBits2Float(0x41d8f7cd)); +path.quadTo(SkBits2Float(0x41dcaf1e), SkBits2Float(0x41d8ca01), SkBits2Float(0x41dcdc4c), SkBits2Float(0x41d89bf0)); +path.quadTo(SkBits2Float(0x41ef6c33), SkBits2Float(0x41c5aec5), SkBits2Float(0x4204f72e), SkBits2Float(0x41c56cd2)); +path.close(); +testSimplify(reporter, path, filename); +} + +static void dean4(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + + // start region + // start loop, contour: 1 + // Segment 1145.3381097316742 2017.6783947944641 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2017.0033947825432 + path.moveTo(1145.3381347656250, 2017.6783447265625); + path.lineTo(1145.3381347656250, 2017.0034179687500); + // Segment 1145.3381097316742 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6927231521568 2017.0033947825432 + path.lineTo(1143.6927490234375, 2017.0034179687500); + // Segment 1143.6927231521568 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1144.8640675112890 2018.1589246992417 + path.lineTo(1144.8640136718750, 2018.1589355468750); + // Segment 1144.8640675112890 2018.1589246992417 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2017.6783947944641 + path.lineTo(1145.3381347656250, 2017.6783447265625); + path.close(); + // start loop, contour: 2 + // Segment 1145.3381097316742 2016.3216052055359 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1144.8640675258462 2015.8410752863977 + path.moveTo(1145.3381347656250, 2016.3216552734375); + path.lineTo(1144.8640136718750, 2015.8410644531250); + // Segment 1144.8640675258462 2015.8410752863977 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6927230811802 2016.9966052174568 + path.lineTo(1143.6927490234375, 2016.9965820312500); + // Segment 1143.6927230811802 2016.9966052174568 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2016.9966052174568 + path.lineTo(1145.3381347656250, 2016.9965820312500); + // Segment 1145.3381097316742 2016.9966052174568 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2016.3216052055359 + path.lineTo(1145.3381347656250, 2016.3216552734375); + path.close(); + // start loop, contour: 3 + // Segment 1147.3323798179626 2014.3542600870132 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220239557 2014.8347900059885 + path.moveTo(1147.3323974609375, 2014.3542480468750); + path.lineTo(1147.8063964843750, 2014.8348388671875); + // Segment 1147.8064220239557 2014.8347900059885 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220516883 2014.8347899786306 + path.lineTo(1147.8063964843750, 2014.8348388671875); + // Segment 1147.8064220516883 2014.8347899786306 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.3323798179626 2014.3542600870132 + path.lineTo(1147.3323974609375, 2014.3542480468750); + path.close(); + // start loop, contour: 4 + // Segment 1146.3696286678314 2013.4045072346926 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708778083 2013.8850371497379 + path.moveTo(1146.3696289062500, 2013.4045410156250); + path.lineTo(1146.8436279296875, 2013.8850097656250); + // Segment 1146.8436708778083 2013.8850371497379 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436709015571 2013.8850371263100 + path.lineTo(1146.8436279296875, 2013.8850097656250); + // Segment 1146.8436709015571 2013.8850371263100 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.3696286678314 2013.4045072346926 + path.lineTo(1146.3696289062500, 2013.4045410156250); + path.close(); + // start loop, contour: 5 + // Segment 1143.2063037902117 2016.5251235961914 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615802348 2016.0445936811461 + path.moveTo(1143.2062988281250, 2016.5251464843750); + path.lineTo(1142.7322998046875, 2016.0445556640625); + // Segment 1142.7322615802348 2016.0445936811461 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615564860 2016.0445937045740 + path.lineTo(1142.7322998046875, 2016.0445556640625); + // Segment 1142.7322615564860 2016.0445937045740 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.2063037902117 2016.5251235961914 + path.lineTo(1143.2062988281250, 2016.5251464843750); + path.close(); + // start loop, contour: 6 + // Segment 1143.0687679275870 2016.7286419868469 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.5428101613127 2017.2091718784643 + path.moveTo(1143.0687255859375, 2016.7286376953125); + path.lineTo(1143.5428466796875, 2017.2092285156250); + // Segment 1143.5428101613127 2017.2091718784643 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.7437679395080 2017.0109272411960 + path.lineTo(1143.7437744140625, 2017.0109863281250); + // Segment 1143.7437679395080 2017.0109272411960 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.7437679395080 2016.7286419868469 + path.lineTo(1143.7437744140625, 2016.7286376953125); + // Segment 1143.7437679395080 2016.7286419868469 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.0687679275870 2016.7286419868469 + path.lineTo(1143.0687255859375, 2016.7286376953125); + path.close(); + // start loop, contour: 7 + // Segment 1143.2063037902117 2017.4748764038086 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615603032 2017.9554062991915 + path.moveTo(1143.2062988281250, 2017.4748535156250); + path.lineTo(1142.7322998046875, 2017.9554443359375); + // Segment 1142.7322615603032 2017.9554062991915 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615746241 2017.9554063133189 + path.lineTo(1142.7322998046875, 2017.9554443359375); + // Segment 1142.7322615746241 2017.9554063133189 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.2063037902117 2017.4748764038086 + path.lineTo(1143.2062988281250, 2017.4748535156250); + path.close(); + // start loop, contour: 8 + // Segment 1146.3696286678314 2020.5954928398132 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708977399 2020.1149629444303 + path.moveTo(1146.3696289062500, 2020.5954589843750); + path.lineTo(1146.8436279296875, 2020.1149902343750); + // Segment 1146.8436708977399 2020.1149629444303 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708834190 2020.1149629303029 + path.lineTo(1146.8436279296875, 2020.1149902343750); + // Segment 1146.8436708834190 2020.1149629303029 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.3696286678314 2020.5954928398132 + path.lineTo(1146.3696289062500, 2020.5954589843750); + path.close(); + // start loop, contour: 9 + // Segment 1147.3323798179626 2019.6457400321960 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220484741 2019.1652101374082 + path.moveTo(1147.3323974609375, 2019.6457519531250); + path.lineTo(1147.8063964843750, 2019.1651611328125); + // Segment 1147.8064220484741 2019.1652101374082 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220383478 2019.1652101274185 + path.lineTo(1147.8063964843750, 2019.1651611328125); + // Segment 1147.8064220383478 2019.1652101274185 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.3323798179626 2019.6457400321960 + path.lineTo(1147.3323974609375, 2019.6457519531250); + path.close(); + // start loop, contour: 10 + // Segment 1145.3381097316742 2018.3533948063850 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2018.3533948063850 + path.moveTo(1145.3381347656250, 2018.3533935546875); + path.lineTo(1156.6848144531250, 2018.3533935546875); + // Segment 1156.6848182678223 2018.3533948063850 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2017.0033947825432 + path.lineTo(1156.6848144531250, 2017.0034179687500); + // Segment 1156.6848182678223 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2017.0033947825432 + path.lineTo(1145.3381347656250, 2017.0034179687500); + // Segment 1145.3381097316742 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2018.3533948063850 + path.lineTo(1145.3381347656250, 2018.3533935546875); + path.close(); + // start loop, contour: 11 + // Segment 1156.6848182678223 2018.3533948063850 0.3569631313191 0.0000000000000 -0.2645167304388 0.2609454237780 1157.6574279406423 2017.9723661860094 + path.moveTo(1156.6848144531250, 2018.3533935546875); + path.cubicTo(1157.0417480468750, 2018.3533935546875, 1157.3929443359375, 2018.2332763671875, 1157.6574707031250, 2017.9724121093750); + // Segment 1157.6574279406423 2017.9723661860094 0.2653344079822 -0.2617520616521 0.0000000000000 0.3596905289350 1158.0474975705147 2017.0000000000000 + path.cubicTo(1157.9227294921875, 2017.7105712890625, 1158.0474853515625, 2017.3597412109375, 1158.0474853515625, 2017.0000000000000); + // Segment 1158.0474975705147 2017.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6974975466728 2017.0000000000000 + path.lineTo(1156.6975097656250, 2017.0000000000000); + // Segment 1156.6974975466728 2017.0000000000000 0.0028009248351 0.0403311981485 0.0118595244351 -0.0220843520393 1156.6941780622435 2017.0325257649940 + path.cubicTo(1156.7003173828125, 2017.0402832031250, 1156.7060546875000, 2017.0104980468750, 1156.6942138671875, 2017.0324707031250); + // Segment 1156.6941780622435 2017.0325257649940 -0.0032637855860 0.0184860248562 0.0120617528380 -0.0065934603083 1156.7093435710913 2017.0113063061967 + path.cubicTo(1156.6909179687500, 2017.0510253906250, 1156.7214355468750, 2017.0047607421875, 1156.7093505859375, 2017.0113525390625); + // split at 0.4496445953846 + // path.cubicTo(1156.6927490234375, 2017.0407714843750, 1156.6981201171875, 2017.0360107421875, 1156.7033691406250, 2017.0289306640625); + // path.cubicTo(1156.7097167968750, 2017.0201416015625, 1156.7159423828125, 2017.0076904296875, 1156.7093505859375, 2017.0113525390625); + // Segment 1156.7093435710913 2017.0113063061967 -0.0070717276929 0.0122220954353 0.0203483811973 -0.0039136894418 1156.7268834554304 2016.9985353221975 + path.cubicTo(1156.7022705078125, 2017.0235595703125, 1156.7471923828125, 2016.9946289062500, 1156.7269287109375, 2016.9985351562500); + // Segment 1156.7268834554304 2016.9985353221975 -0.0244396787691 0.0123649140586 0.0433322464027 0.0026558844666 1156.6848182678223 2017.0033947825432 + path.cubicTo(1156.7023925781250, 2017.0108642578125, 1156.7281494140625, 2017.0061035156250, 1156.6848144531250, 2017.0034179687500); + // split at 0.4418420493603 + // path.cubicTo(1156.7160644531250, 2017.0040283203125, 1156.7150878906250, 2017.0061035156250, 1156.7136230468750, 2017.0065917968750); + // path.cubicTo(1156.7116699218750, 2017.0070800781250, 1156.7089843750000, 2017.0048828125000, 1156.6848144531250, 2017.0034179687500); + // Segment 1156.6848182678223 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2018.3533948063850 + path.lineTo(1156.6848144531250, 2018.3533935546875); + path.close(); + // start loop, contour: 12 + // Segment 1158.0474975705147 2017.0000000000000 0.0000000000000 -0.3596905289350 0.2653344079822 0.2617520616521 1157.6574279406423 2016.0276338139906 + path.moveTo(1158.0474853515625, 2017.0000000000000); + path.cubicTo(1158.0474853515625, 2016.6402587890625, 1157.9227294921875, 2016.2894287109375, 1157.6574707031250, 2016.0275878906250); + // Segment 1157.6574279406423 2016.0276338139906 -0.2645167304388 -0.2609454237780 0.3569631313191 0.0000000000000 1156.6848182678223 2015.6466051936150 + path.cubicTo(1157.3929443359375, 2015.7667236328125, 1157.0417480468750, 2015.6466064453125, 1156.6848144531250, 2015.6466064453125); + // split at 0.5481675863266 + // path.cubicTo(1157.5124511718750, 2015.8846435546875, 1157.3414306640625, 2015.7839355468750, 1157.1577148437500, 2015.7220458984375); + // path.cubicTo(1157.0062255859375, 2015.6711425781250, 1156.8460693359375, 2015.6466064453125, 1156.6848144531250, 2015.6466064453125); + // Segment 1156.6848182678223 2015.6466051936150 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2016.9966052174568 + path.lineTo(1156.6848144531250, 2016.9965820312500); + // Segment 1156.6848182678223 2016.9966052174568 0.0433322464027 -0.0026558844666 -0.0244396787691 -0.0123649140586 1156.7268834554304 2017.0014646778025 + path.cubicTo(1156.7281494140625, 2016.9938964843750, 1156.7023925781250, 2016.9891357421875, 1156.7269287109375, 2017.0014648437500); + // split at 0.5581579208374 + // path.cubicTo(1156.7089843750000, 2016.9951171875000, 1156.7116699218750, 2016.9929199218750, 1156.7136230468750, 2016.9934082031250); + // path.cubicTo(1156.7150878906250, 2016.9938964843750, 1156.7160644531250, 2016.9959716796875, 1156.7269287109375, 2017.0014648437500); + // Segment 1156.7268834554304 2017.0014646778025 0.0203483811973 0.0039136894418 -0.0070717276929 -0.0122220954353 1156.7093435710913 2016.9886936938033 + path.cubicTo(1156.7471923828125, 2017.0053710937500, 1156.7022705078125, 2016.9764404296875, 1156.7093505859375, 2016.9886474609375); + // Segment 1156.7093435710913 2016.9886936938033 0.0120617528380 0.0065934603083 -0.0032637855860 -0.0184860248562 1156.6941780622435 2016.9674742350060 + path.cubicTo(1156.7214355468750, 2016.9952392578125, 1156.6909179687500, 2016.9489746093750, 1156.6942138671875, 2016.9675292968750); + // Segment 1156.6941780622435 2016.9674742350060 0.0118595244351 0.0220843520393 0.0028009248351 -0.0403311981485 1156.6974975466728 2017.0000000000000 + path.cubicTo(1156.7060546875000, 2016.9895019531250, 1156.7003173828125, 2016.9597167968750, 1156.6975097656250, 2017.0000000000000); + // split at 0.4572408795357 + // path.cubicTo(1156.6995849609375, 2016.9775390625000, 1156.7014160156250, 2016.9768066406250, 1156.7014160156250, 2016.9768066406250); + // path.cubicTo(1156.7014160156250, 2016.9769287109375, 1156.6989746093750, 2016.9781494140625, 1156.6975097656250, 2017.0000000000000); + // Segment 1156.6974975466728 2017.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1158.0474975705147 2017.0000000000000 + path.lineTo(1158.0474853515625, 2017.0000000000000); + path.close(); + // start loop, contour: 13 + // Segment 1156.6848182678223 2015.6466051936150 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2015.6466051936150 + path.moveTo(1156.6848144531250, 2015.6466064453125); + path.lineTo(1145.3381347656250, 2015.6466064453125); + // Segment 1145.3381097316742 2015.6466051936150 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2016.9966052174568 + path.lineTo(1145.3381347656250, 2016.9965820312500); + // Segment 1145.3381097316742 2016.9966052174568 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2016.9966052174568 + path.lineTo(1156.6848144531250, 2016.9965820312500); + // Segment 1156.6848182678223 2016.9966052174568 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2015.6466051936150 + path.lineTo(1156.6848144531250, 2015.6466064453125); + path.close(); + // start loop, contour: 14 + // Segment 1145.8121519375022 2016.8021351246741 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220237907 2014.8347900061515 + path.moveTo(1145.8121337890625, 2016.8021240234375); + path.lineTo(1147.8063964843750, 2014.8348388671875); + // Segment 1147.8064220237907 2014.8347900061515 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583376121346 2013.8737301678750 + path.lineTo(1146.8583984375000, 2013.8737792968750); + // Segment 1146.8583376121346 2013.8737301678750 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1144.8640675258462 2015.8410752863977 + path.lineTo(1144.8640136718750, 2015.8410644531250); + // Segment 1144.8640675258462 2015.8410752863977 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8121519375022 2016.8021351246741 + path.lineTo(1145.8121337890625, 2016.8021240234375); + path.close(); + // start loop, contour: 15 + // Segment 1147.8064220516883 2014.8347899786306 0.5430154146087 -0.5356841365729 0.5430154146087 0.5356841365729 1147.8064220516883 2012.9239773430752 + path.moveTo(1147.8063964843750, 2014.8348388671875); + path.cubicTo(1148.3494873046875, 2014.2990722656250, 1148.3494873046875, 2013.4597167968750, 1147.8063964843750, 2012.9239501953125); + // Segment 1147.8064220516883 2012.9239773430752 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583375842370 2013.8850371263100 + path.lineTo(1146.8583984375000, 2013.8850097656250); + // Segment 1146.8583375842370 2013.8850371263100 0.0071280060876 0.0070317705240 0.0071280060876 -0.0070317705240 1146.8583375842370 2013.8737301953959 + path.cubicTo(1146.8654785156250, 2013.8920898437500, 1146.8654785156250, 2013.8666992187500, 1146.8583984375000, 2013.8737792968750); + // Segment 1146.8583375842370 2013.8737301953959 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220516883 2014.8347899786306 + path.lineTo(1147.8063964843750, 2014.8348388671875); + path.close(); + // start loop, contour: 16 + // Segment 1147.8064220516883 2012.9239773430752 -0.5379138488298 -0.5306514472866 0.5379138488298 -0.5306514472866 1145.8955864341058 2012.9239773430752 + path.moveTo(1147.8063964843750, 2012.9239501953125); + path.cubicTo(1147.2685546875000, 2012.3933105468750, 1146.4334716796875, 2012.3933105468750, 1145.8956298828125, 2012.9239501953125); + // Segment 1145.8955864341058 2012.9239773430752 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436709015571 2013.8850371263100 + path.lineTo(1146.8436279296875, 2013.8850097656250); + // Segment 1146.8436709015571 2013.8850371263100 0.0122295718664 -0.0120644598103 -0.0122295718664 -0.0120644598103 1146.8583375842370 2013.8850371263100 + path.cubicTo(1146.8559570312500, 2013.8729248046875, 1146.8460693359375, 2013.8729248046875, 1146.8583984375000, 2013.8850097656250); + // Segment 1146.8583375842370 2013.8850371263100 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220516883 2012.9239773430752 + path.lineTo(1147.8063964843750, 2012.9239501953125); + path.close(); + // start loop, contour: 17 + // Segment 1145.8955864579798 2012.9239773195236 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615803600 2016.0445936810224 + path.moveTo(1145.8956298828125, 2012.9239501953125); + path.lineTo(1142.7322998046875, 2016.0445556640625); + // Segment 1142.7322615803600 2016.0445936810224 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6803460000633 2017.0056535113604 + path.lineTo(1143.6802978515625, 2017.0056152343750); + // Segment 1143.6803460000633 2017.0056535113604 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708776831 2013.8850371498615 + path.lineTo(1146.8436279296875, 2013.8850097656250); + // Segment 1146.8436708776831 2013.8850371498615 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8955864579798 2012.9239773195236 + path.lineTo(1145.8956298828125, 2012.9239501953125); + path.close(); + // start loop, contour: 18 + // Segment 1142.7322615564860 2016.0445937045740 -0.0343838913237 0.0339196727021 0.0561572931720 -0.0710493024751 1142.5744069596683 2016.2183613784646 + path.moveTo(1142.7322998046875, 2016.0445556640625); + path.cubicTo(1142.6978759765625, 2016.0784912109375, 1142.6306152343750, 2016.1473388671875, 1142.5744628906250, 2016.2183837890625); + // Segment 1142.5744069596683 2016.2183613784646 -0.0547779032556 0.0720510806539 0.0000000000000 -0.2570904015602 1142.3937679156661 2016.7286419868469 + path.cubicTo(1142.5196533203125, 2016.2904052734375, 1142.3937988281250, 2016.4715576171875, 1142.3937988281250, 2016.7286376953125); + // Segment 1142.3937679156661 2016.7286419868469 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.7437679395080 2016.7286419868469 + path.lineTo(1143.7437744140625, 2016.7286376953125); + // Segment 1143.7437679395080 2016.7286419868469 -0.0051909534315 0.0665915567290 0.0133980913650 -0.0361675066532 1143.6976291086639 2016.9514128270803 + path.cubicTo(1143.7385253906250, 2016.7952880859375, 1143.7110595703125, 2016.9152832031250, 1143.6976318359375, 2016.9514160156250); + // Segment 1143.6976291086639 2016.9514128270803 -0.0142876819622 0.0277028472317 0.0040377216094 -0.0063254385208 1143.6490888124401 2017.0354042045738 + path.cubicTo(1143.6833496093750, 2016.9791259765625, 1143.6530761718750, 2017.0290527343750, 1143.6490478515625, 2017.0354003906250); + // Segment 1143.6490888124401 2017.0354042045738 -0.0045813437564 0.0032098513409 -0.0343840362634 0.0339198156850 1143.6803460239373 2017.0056534878088 + path.cubicTo(1143.6445312500000, 2017.0385742187500, 1143.6459960937500, 2017.0395507812500, 1143.6802978515625, 2017.0056152343750); + // Segment 1143.6803460239373 2017.0056534878088 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615564860 2016.0445937045740 + path.lineTo(1142.7322998046875, 2016.0445556640625); + path.close(); + // start loop, contour: 19 + // Segment 1142.5947256938614 2016.2481120952295 -0.1857487117715 0.1832409092043 0.0167379373694 -0.0990717748979 1142.3430278987244 2016.7518748698508 + path.moveTo(1142.5947265625000, 2016.2481689453125); + path.cubicTo(1142.4089355468750, 2016.4313964843750, 1142.3597412109375, 2016.6528320312500, 1142.3430175781250, 2016.7518310546875); + // Segment 1142.3430278987244 2016.7518748698508 -0.0156657977007 0.1069052535795 0.0000000000000 -0.0339197441936 1142.3249999880791 2017.0000000000000 + path.cubicTo(1142.3273925781250, 2016.8587646484375, 1142.3249511718750, 2016.9660644531250, 1142.3249511718750, 2017.0000000000000); + // Segment 1142.3249999880791 2017.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6750000119209 2017.0000000000000 + path.lineTo(1143.6750488281250, 2017.0000000000000); + // Segment 1143.6750000119209 2017.0000000000000 0.0000000000000 -0.0339197441936 -0.0015261841961 -0.0051459911965 1143.6741640831724 2016.9767671169961 + path.cubicTo(1143.6750488281250, 2016.9660644531250, 1143.6726074218750, 2016.9716796875000, 1143.6741943359375, 2016.9768066406250); + // Segment 1143.6741640831724 2016.9767671169961 -0.0007886982052 0.0013596649622 0.0074114058388 -0.0224954551713 1143.6525251830094 2017.0486861571169 + path.cubicTo(1143.6733398437500, 2016.9781494140625, 1143.6599121093750, 2017.0262451171875, 1143.6524658203125, 2017.0487060546875); + // split at 0.4203657805920 + // path.cubicTo(1143.6738281250000, 2016.9774169921875, 1143.6712646484375, 2016.9862060546875, 1143.6678466796875, 2016.9979248046875); + // path.cubicTo(1143.6630859375000, 2017.0140380859375, 1143.6567382812500, 2017.0356445312500, 1143.6524658203125, 2017.0487060546875); + // Segment 1143.6525251830094 2017.0486861571169 -0.0119644334077 0.0236755853369 0.0381324473830 -0.0447670202574 1143.5428101613127 2017.2091718784643 + path.cubicTo(1143.6405029296875, 2017.0723876953125, 1143.5809326171875, 2017.1644287109375, 1143.5428466796875, 2017.2092285156250); + // Segment 1143.5428101613127 2017.2091718784643 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.5947256938614 2016.2481120952295 + path.lineTo(1142.5947265625000, 2016.2481689453125); + path.close(); + // start loop, contour: 20 + // Segment 1142.3249999880791 2017.0000000000000 0.0000000000000 0.0339197441936 -0.0156657977007 -0.1069052535795 1142.3430278987244 2017.2481251301492 + path.moveTo(1142.3249511718750, 2017.0000000000000); + path.cubicTo(1142.3249511718750, 2017.0339355468750, 1142.3273925781250, 2017.1412353515625, 1142.3430175781250, 2017.2481689453125); + // Segment 1142.3430278987244 2017.2481251301492 0.0167379373694 0.0990717748979 -0.1857487117715 -0.1832409092043 1142.5947256938614 2017.7518879047705 + path.cubicTo(1142.3597412109375, 2017.3471679687500, 1142.4089355468750, 2017.5686035156250, 1142.5947265625000, 2017.7518310546875); + // split at 0.4008532166481 + // path.cubicTo(1142.3497314453125, 2017.2878417968750, 1142.3616943359375, 2017.3471679687500, 1142.3854980468750, 2017.4158935546875); + // path.cubicTo(1142.4211425781250, 2017.5185546875000, 1142.4833984375000, 2017.6420898437500, 1142.5947265625000, 2017.7518310546875); + // Segment 1142.5947256938614 2017.7518879047705 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.5428101613127 2016.7908281215357 + path.lineTo(1143.5428466796875, 2016.7907714843750); + // Segment 1143.5428101613127 2016.7908281215357 0.0381324473830 0.0447670202574 -0.0119644334077 -0.0236755853369 1143.6525251830094 2016.9513138428831 + path.cubicTo(1143.5809326171875, 2016.8355712890625, 1143.6405029296875, 2016.9276123046875, 1143.6524658203125, 2016.9512939453125); + // Segment 1143.6525251830094 2016.9513138428831 0.0074114058388 0.0224954551713 -0.0007886982052 -0.0013596649622 1143.6741640831724 2017.0232328830039 + path.cubicTo(1143.6599121093750, 2016.9737548828125, 1143.6733398437500, 2017.0218505859375, 1143.6741943359375, 2017.0231933593750); + // Segment 1143.6741640831724 2017.0232328830039 -0.0015261841961 0.0051459911965 0.0000000000000 0.0339197441936 1143.6750000119209 2017.0000000000000 + path.cubicTo(1143.6726074218750, 2017.0283203125000, 1143.6750488281250, 2017.0339355468750, 1143.6750488281250, 2017.0000000000000); + // Segment 1143.6750000119209 2017.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.3249999880791 2017.0000000000000 + path.lineTo(1142.3249511718750, 2017.0000000000000); + path.close(); + // start loop, contour: 21 + // Segment 1142.5947256938614 2017.7518879047705 -0.0799271403989 -0.1522613934208 -0.2174629955730 -0.2879403701950 1142.7322615564860 2017.9554062954260 + path.moveTo(1142.5947265625000, 2017.7518310546875); + path.cubicTo(1142.5147705078125, 2017.5996093750000, 1142.5147705078125, 2017.6674804687500, 1142.7322998046875, 2017.9554443359375); + // Segment 1142.7322615564860 2017.9554062954260 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6803460239373 2016.9943465121912 + path.lineTo(1143.6802978515625, 2016.9943847656250); + // Segment 1143.6803460239373 2016.9943465121912 0.0799271403989 0.1522613934208 0.2174629955730 0.2879403701950 1143.5428101613127 2016.7908281215357 + path.cubicTo(1143.7602539062500, 2017.1466064453125, 1143.7602539062500, 2017.0787353515625, 1143.5428466796875, 2016.7907714843750); + // Segment 1143.5428101613127 2016.7908281215357 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.5947256938614 2017.7518879047705 + path.lineTo(1142.5947265625000, 2017.7518310546875); + path.close(); + // start loop, contour: 22 + // Segment 1142.7322615746241 2017.9554063133189 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8955864522438 2021.0760227493236 + path.moveTo(1142.7322998046875, 2017.9554443359375); + path.lineTo(1145.8956298828125, 2021.0760498046875); + // Segment 1145.8955864522438 2021.0760227493236 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708834190 2020.1149629303029 + path.lineTo(1146.8436279296875, 2020.1149902343750); + // Segment 1146.8436708834190 2020.1149629303029 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6803460057993 2016.9943464942983 + path.lineTo(1143.6802978515625, 2016.9943847656250); + // Segment 1143.6803460057993 2016.9943464942983 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615746241 2017.9554063133189 + path.lineTo(1142.7322998046875, 2017.9554443359375); + path.close(); + // start loop, contour: 23 + // Segment 1145.8955864341058 2021.0760227314306 0.2730164534637 0.2693304447891 -0.3016608168437 0.0000000000000 1146.8510041236877 2021.4740112423897 + path.moveTo(1145.8956298828125, 2021.0760498046875); + path.cubicTo(1146.1685791015625, 2021.3453369140625, 1146.5493164062500, 2021.4739990234375, 1146.8509521484375, 2021.4739990234375); + // Segment 1146.8510041236877 2021.4740112423897 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8510041236877 2020.1240112185478 + path.lineTo(1146.8509521484375, 2020.1240234375000); + // Segment 1146.8510041236877 2020.1240112185478 -0.0031276099109 0.0031991747760 0.0281856144058 0.0140930868099 1146.8580791488898 2020.1202473991566 + path.cubicTo(1146.8479003906250, 2020.1271972656250, 1146.8862304687500, 2020.1343994140625, 1146.8580322265625, 2020.1202392578125); + // split at 0.3845077157021 + // path.cubicTo(1146.8497314453125, 2020.1252441406250, 1146.8547363281250, 2020.1270751953125, 1146.8596191406250, 2020.1280517578125); + // path.cubicTo(1146.8675537109375, 2020.1296386718750, 1146.8753662109375, 2020.1289062500000, 1146.8580322265625, 2020.1202392578125); + // Segment 1146.8580791488898 2020.1202473991566 -0.0369995545027 -0.0123195805663 0.0067223483810 0.0136883790721 1146.8436709015571 2020.1149629481959 + path.cubicTo(1146.8210449218750, 2020.1079101562500, 1146.8503417968750, 2020.1286621093750, 1146.8436279296875, 2020.1149902343750); + // Segment 1146.8436709015571 2020.1149629481959 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8955864341058 2021.0760227314306 + path.lineTo(1145.8956298828125, 2021.0760498046875); + path.close(); + // start loop, contour: 24 + // Segment 1146.8510041236877 2021.4740112423897 0.3016605789999 0.0000000000000 -0.2730166120260 0.2693306012106 1147.8064220516883 2021.0760227314306 + path.moveTo(1146.8509521484375, 2021.4739990234375); + path.cubicTo(1147.1527099609375, 2021.4739990234375, 1147.5334472656250, 2021.3453369140625, 1147.8063964843750, 2021.0760498046875); + // Segment 1147.8064220516883 2021.0760227314306 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583375842370 2020.1149629481959 + path.lineTo(1146.8583984375000, 2020.1149902343750); + // Segment 1146.8583375842370 2020.1149629481959 -0.0067222671256 0.0136883164611 0.0369996293611 -0.0123196021258 1146.8439293663473 2020.1202473404985 + path.cubicTo(1146.8515625000000, 2020.1286621093750, 1146.8809814453125, 2020.1079101562500, 1146.8438720703125, 2020.1202392578125); + // Segment 1146.8439293663473 2020.1202473404985 -0.0281857033438 0.0140931104690 0.0031276541428 0.0031991704542 1146.8510041236877 2020.1240112185478 + path.cubicTo(1146.8157958984375, 2020.1343994140625, 1146.8541259765625, 2020.1271972656250, 1146.8509521484375, 2020.1240234375000); + // Segment 1146.8510041236877 2020.1240112185478 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8510041236877 2021.4740112423897 + path.lineTo(1146.8509521484375, 2021.4739990234375); + path.close(); + // start loop, contour: 25 + // Segment 1147.8064220516883 2021.0760227314306 0.5430154146087 -0.5356841365729 0.5430154146087 0.5356841365729 1147.8064220516883 2019.1652101405787 + path.moveTo(1147.8063964843750, 2021.0760498046875); + path.cubicTo(1148.3494873046875, 2020.5402832031250, 1148.3494873046875, 2019.7009277343750, 1147.8063964843750, 2019.1651611328125); + // Segment 1147.8064220516883 2019.1652101405787 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583375842370 2020.1262699238134 + path.lineTo(1146.8583984375000, 2020.1262207031250); + // Segment 1146.8583375842370 2020.1262699238134 0.0071280060876 0.0070317705240 0.0071280060876 -0.0070317705240 1146.8583375842370 2020.1149629481959 + path.cubicTo(1146.8654785156250, 2020.1333007812500, 1146.8654785156250, 2020.1079101562500, 1146.8583984375000, 2020.1149902343750); + // Segment 1146.8583375842370 2020.1149629481959 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220516883 2021.0760227314306 + path.lineTo(1147.8063964843750, 2021.0760498046875); + path.close(); + // start loop, contour: 26 + // Segment 1147.8064220383478 2019.1652101274185 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8121519520594 2017.1978648896866 + path.moveTo(1147.8063964843750, 2019.1651611328125); + path.lineTo(1145.8121337890625, 2017.1978759765625); + // Segment 1145.8121519520594 2017.1978648896866 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1144.8640675112890 2018.1589246992417 + path.lineTo(1144.8640136718750, 2018.1589355468750); + // Segment 1144.8640675112890 2018.1589246992417 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583375975775 2020.1262699369736 + path.lineTo(1146.8583984375000, 2020.1262207031250); + // Segment 1146.8583375975775 2020.1262699369736 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220383478 2019.1652101274185 + path.lineTo(1147.8063964843750, 2019.1651611328125); + path.close(); + +testSimplify(reporter, path, filename); +} + +static void testQuads66(skiatest::Reporter* reporter,const char* filename) { + SkPath path; + path.moveTo(2, 0); + path.quadTo(3, 1, 2, 2); + path.lineTo(2, 3); + path.close(); + path.moveTo(2, 1); + path.lineTo(2, 1); + path.quadTo(1, 2, 2, 2); + path.close(); + testSimplify(reporter, path, filename); +} + +static void testQuads67(skiatest::Reporter* reporter,const char* filename) { + SkPath path; + path.moveTo(3, 2); + path.quadTo(1, 3, 3, 3); + path.lineTo(3, 3); + path.close(); + path.moveTo(0, 0); + path.lineTo(1, 0); + path.quadTo(2, 3, 3, 3); + path.close(); + testSimplify(reporter, path, filename); +} + +static void testQuads68(skiatest::Reporter* reporter,const char* filename) { + SkPath path; + path.moveTo(1, 2); + path.quadTo(0, 3, 2, 3); + path.lineTo(2, 3); + path.close(); + path.moveTo(1, 0); + path.lineTo(0, 1); + path.quadTo(1, 3, 2, 3); + path.close(); + testSimplify(reporter, path, filename); +} + +static void testQuads69(skiatest::Reporter* reporter,const char* filename) { + SkPath path; + path.moveTo(1, 0); + path.quadTo(2, 2, 2, 3); + path.lineTo(2, 3); + path.close(); + path.moveTo(1, 0); + path.lineTo(1, 0); + path.quadTo(3, 0, 1, 3); + path.close(); + testSimplify(reporter, path, filename); +} + +static void testQuads70(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.moveTo(1, 1); + path.quadTo(2, 3, 3, 3); + path.lineTo(3, 3); + path.close(); + path.moveTo(2, 0); + path.lineTo(2, 2); + path.quadTo(1, 3, 3, 3); + path.close(); + testSimplify(reporter, path, filename); +} + +static void testQuads71(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.moveTo(1, 1); + path.quadTo(2, 3, 3, 3); + path.lineTo(3, 3); + path.close(); + path.moveTo(3, 0); + path.lineTo(2, 2); + path.quadTo(1, 3, 3, 3); + path.close(); + testSimplify(reporter, path, filename); +} + +static void testQuads72(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.moveTo(1, 1); + path.quadTo(2, 3, 3, 3); + path.lineTo(3, 3); + path.close(); + path.moveTo(0, 1); + path.lineTo(2, 2); + path.quadTo(1, 3, 3, 3); + path.close(); + testSimplify(reporter, path, filename); +} + +static void testQuads73(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.moveTo(0, 0); + path.quadTo(0, 1, 1, 2); + path.lineTo(0, 3); + path.close(); + path.moveTo(0, 0); + path.lineTo(0, 0); + path.quadTo(0, 1, 1, 1); + path.close(); + testSimplify(reporter, path, filename); +} + +static void bug5169(skiatest::Reporter* reporter, const char* filename) { + SkPath path; +path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x4281c71c)); // 0, 64.8889f +path.cubicTo(SkBits2Float(0x434e0000), SkBits2Float(0x4281c71c), SkBits2Float(0x00000000), SkBits2Float(0xc2a238e4), SkBits2Float(0x00000000), SkBits2Float(0x4281c71c)); // 206, 64.8889f, 0, -81.1111f, 0, 64.8889f +path.moveTo(SkBits2Float(0x43300000), SkBits2Float(0x41971c72)); // 176, 18.8889f +path.cubicTo(SkBits2Float(0xc29e0000), SkBits2Float(0xc25c71c7), SkBits2Float(0x42b20000), SkBits2Float(0x42fbc71c), SkBits2Float(0x43300000), SkBits2Float(0x41971c72)); // -79, -55.1111f, 89, 125.889f, 176, 18.8889f + testSimplify(reporter, path, filename); +} + +static void tiger8_393(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42b93333), SkBits2Float(0x43d5a666)); // 92.6f, 427.3f +path.cubicTo(SkBits2Float(0x42b93333), SkBits2Float(0x43d5a666), SkBits2Float(0x42b5cccd), SkBits2Float(0x43da1999), SkBits2Float(0x42b80000), SkBits2Float(0x43ddf333)); // 92.6f, 427.3f, 90.9f, 436.2f, 92, 443.9f +path.cubicTo(SkBits2Float(0x42b80000), SkBits2Float(0x43ddf333), SkBits2Float(0x42b30000), SkBits2Float(0x43e17333), SkBits2Float(0x42cf999a), SkBits2Float(0x43e1b333)); // 92, 443.9f, 89.5f, 450.9f, 103.8f, 451.4f +path.cubicTo(SkBits2Float(0x42ec3334), SkBits2Float(0x43e14ccd), SkBits2Float(0x42e73334), SkBits2Float(0x43ddf333), SkBits2Float(0x42e73334), SkBits2Float(0x43ddf333)); // 118.1f, 450.6f, 115.6f, 443.9f, 115.6f, 443.9f +path.cubicTo(SkBits2Float(0x42e7999a), SkBits2Float(0x43de8000), SkBits2Float(0x42ea6667), SkBits2Float(0x43db4000), SkBits2Float(0x42e60001), SkBits2Float(0x43d5a666)); // 115.8f, 445, 117.2f, 438.5f, 115, 427.3f + testSimplify(reporter, path, filename); +} + +// triggers angle assert from distance field code +static void carsvg_1(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4393d61e), SkBits2Float(0x43e768f9)); // 295.673f, 462.82f +path.cubicTo(SkBits2Float(0x4396b50e), SkBits2Float(0x43e63c20), SkBits2Float(0x43998931), SkBits2Float(0x43e6c43e), SkBits2Float(0x439cb6a8), SkBits2Float(0x43e70ef9)); // 301.414f, 460.47f, 307.072f, 461.533f, 313.427f, 462.117f +path.cubicTo(SkBits2Float(0x439dfc1e), SkBits2Float(0x43e72ce0), SkBits2Float(0x439a285c), SkBits2Float(0x43e717fb), SkBits2Float(0x4398e23c), SkBits2Float(0x43e7027c)); // 315.97f, 462.351f, 308.315f, 462.187f, 305.767f, 462.019f +path.cubicTo(SkBits2Float(0x4398136f), SkBits2Float(0x43e6f4db), SkBits2Float(0x439a7e14), SkBits2Float(0x43e6d390), SkBits2Float(0x439b4ba9), SkBits2Float(0x43e6b956)); // 304.152f, 461.913f, 308.985f, 461.653f, 310.591f, 461.448f +path.cubicTo(SkBits2Float(0x439c2b19), SkBits2Float(0x43e68603), SkBits2Float(0x43abf4df), SkBits2Float(0x43e9ca9e), SkBits2Float(0x43a1daea), SkBits2Float(0x43e912a5)); // 312.337f, 461.047f, 343.913f, 467.583f, 323.71f, 466.146f +path.cubicTo(SkBits2Float(0x43a4f45a), SkBits2Float(0x43e78baf), SkBits2Float(0x43a2a391), SkBits2Float(0x43e86a82), SkBits2Float(0x43a946bd), SkBits2Float(0x43e90c56)); // 329.909f, 463.091f, 325.278f, 464.832f, 338.553f, 466.096f +path.lineTo(SkBits2Float(0x43a4250b), SkBits2Float(0x43e998dc)); // 328.289f, 467.194f +path.cubicTo(SkBits2Float(0x43a8a9c8), SkBits2Float(0x43e8f06c), SkBits2Float(0x43a95cb5), SkBits2Float(0x43e84ea6), SkBits2Float(0x43a6f7c1), SkBits2Float(0x43e9bdb5)); // 337.326f, 465.878f, 338.724f, 464.614f, 333.936f, 467.482f +path.cubicTo(SkBits2Float(0x43a59ed0), SkBits2Float(0x43e9d2ca), SkBits2Float(0x4395ea4d), SkBits2Float(0x43e92afe), SkBits2Float(0x43a06569), SkBits2Float(0x43e7773d)); // 331.241f, 467.647f, 299.83f, 466.336f, 320.792f, 462.932f +path.cubicTo(SkBits2Float(0x438bf0ff), SkBits2Float(0x43ea0fef), SkBits2Float(0x43a0e17a), SkBits2Float(0x43e5f41b), SkBits2Float(0x4398f3fb), SkBits2Float(0x43e804c8)); // 279.883f, 468.124f, 321.762f, 459.907f, 305.906f, 464.037f +path.lineTo(SkBits2Float(0x4393d61e), SkBits2Float(0x43e768f9)); // 295.673f, 462.82f +path.close(); + + testSimplify(reporter, path, filename); +} + +static void simplifyTest_1(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x42bfefd4), SkBits2Float(0x42ef80ef)); // 95.9684f, 119.752f +path.quadTo(SkBits2Float(0x42c26810), SkBits2Float(0x42e214b8), SkBits2Float(0x42cdcad5), SkBits2Float(0x42d82aa2)); // 97.2032f, 113.04f, 102.896f, 108.083f +path.lineTo(SkBits2Float(0x42cdcb21), SkBits2Float(0x42d82a61)); // 102.897f, 108.083f +path.quadTo(SkBits2Float(0x42d5e3c8), SkBits2Float(0x42d12140), SkBits2Float(0x42e20ee8), SkBits2Float(0x42cdc937)); // 106.945f, 104.565f, 113.029f, 102.893f +path.lineTo(SkBits2Float(0x42e256e3), SkBits2Float(0x42cdbc92)); // 113.17f, 102.868f +path.lineTo(SkBits2Float(0x42f5eadb), SkBits2Float(0x42cc2cb3)); // 122.959f, 102.087f +path.lineTo(SkBits2Float(0x42f746a6), SkBits2Float(0x42cccf85)); // 123.638f, 102.405f +path.quadTo(SkBits2Float(0x42fa586c), SkBits2Float(0x42d126c4), SkBits2Float(0x42f6c657), SkBits2Float(0x42d5d315)); // 125.173f, 104.576f, 123.387f, 106.912f +path.lineTo(SkBits2Float(0x42f591eb), SkBits2Float(0x42d4e76d)); // 122.785f, 106.452f +path.lineTo(SkBits2Float(0x42f6c6e0), SkBits2Float(0x42d5d261)); // 123.388f, 106.911f +path.quadTo(SkBits2Float(0x42f6bb33), SkBits2Float(0x42d5e1bb), SkBits2Float(0x42f6a3d8), SkBits2Float(0x42d6007c)); // 123.366f, 106.941f, 123.32f, 107.001f +path.quadTo(SkBits2Float(0x42ea3850), SkBits2Float(0x42e65af0), SkBits2Float(0x42d97a6e), SkBits2Float(0x42ed841c)); // 117.11f, 115.178f, 108.739f, 118.758f +path.lineTo(SkBits2Float(0x42d91d92), SkBits2Float(0x42ed9ec0)); // 108.558f, 118.81f +path.lineTo(SkBits2Float(0x42c1a959), SkBits2Float(0x42f146b0)); // 96.8308f, 120.638f +path.lineTo(SkBits2Float(0x42bfefd4), SkBits2Float(0x42ef80f0)); // 95.9684f, 119.752f +path.lineTo(SkBits2Float(0x42bfefd4), SkBits2Float(0x42ef80ef)); // 95.9684f, 119.752f +path.close(); +path.moveTo(SkBits2Float(0x42c2eb4e), SkBits2Float(0x42f00d68)); // 97.4596f, 120.026f +path.lineTo(SkBits2Float(0x42c16d91), SkBits2Float(0x42efc72c)); // 96.714f, 119.889f +path.lineTo(SkBits2Float(0x42c131c9), SkBits2Float(0x42ee47a8)); // 96.5972f, 119.14f +path.lineTo(SkBits2Float(0x42d8a602), SkBits2Float(0x42ea9fb8)); // 108.324f, 117.312f +path.lineTo(SkBits2Float(0x42d8e1ca), SkBits2Float(0x42ec1f3c)); // 108.441f, 118.061f +path.lineTo(SkBits2Float(0x42d84926), SkBits2Float(0x42eaba5c)); // 108.143f, 117.364f +path.quadTo(SkBits2Float(0x42e84a40), SkBits2Float(0x42e3e1f0), SkBits2Float(0x42f439a2), SkBits2Float(0x42d42af8)); // 116.145f, 113.941f, 122.113f, 106.084f +path.quadTo(SkBits2Float(0x42f45121), SkBits2Float(0x42d40c08), SkBits2Float(0x42f45cf6), SkBits2Float(0x42d3fc79)); // 122.158f, 106.023f, 122.182f, 105.993f +path.lineTo(SkBits2Float(0x42f45d7f), SkBits2Float(0x42d3fbc5)); // 122.183f, 105.992f +path.quadTo(SkBits2Float(0x42f69510), SkBits2Float(0x42d114f4), SkBits2Float(0x42f4ccce), SkBits2Float(0x42ce8fb7)); // 123.291f, 104.541f, 122.4f, 103.281f +path.lineTo(SkBits2Float(0x42f609ba), SkBits2Float(0x42cdaf9e)); // 123.019f, 102.843f +path.lineTo(SkBits2Float(0x42f62899), SkBits2Float(0x42cf3289)); // 123.079f, 103.599f +path.lineTo(SkBits2Float(0x42e294a1), SkBits2Float(0x42d0c268)); // 113.29f, 104.38f +path.lineTo(SkBits2Float(0x42e275c2), SkBits2Float(0x42cf3f7d)); // 113.23f, 103.624f +path.lineTo(SkBits2Float(0x42e2dc9c), SkBits2Float(0x42d0b5c3)); // 113.431f, 104.355f +path.quadTo(SkBits2Float(0x42d75bb8), SkBits2Float(0x42d3df08), SkBits2Float(0x42cfc853), SkBits2Float(0x42da7457)); // 107.679f, 105.936f, 103.891f, 109.227f +path.lineTo(SkBits2Float(0x42cec9ba), SkBits2Float(0x42d94f5c)); // 103.394f, 108.655f +path.lineTo(SkBits2Float(0x42cfc89f), SkBits2Float(0x42da7416)); // 103.892f, 109.227f +path.quadTo(SkBits2Float(0x42c53268), SkBits2Float(0x42e3ac00), SkBits2Float(0x42c2eb4e), SkBits2Float(0x42f00d67)); // 98.5984f, 113.836f, 97.4596f, 120.026f +path.lineTo(SkBits2Float(0x42c2eb4e), SkBits2Float(0x42f00d68)); // 97.4596f, 120.026f +path.close(); + + testSimplify(reporter, path, filename); +} + +static void joel_1(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(144.859f, 285.172f); +path.lineTo(144.859f, 285.172f); +path.lineTo(144.859f, 285.172f); +path.lineTo(143.132f, 284.617f); +path.lineTo(144.859f, 285.172f); +path.close(); +path.moveTo(135.922f, 286.844f); +path.lineTo(135.922f, 286.844f); +path.lineTo(135.922f, 286.844f); +path.lineTo(135.367f, 288.571f); +path.lineTo(135.922f, 286.844f); +path.close(); +path.moveTo(135.922f, 286.844f); +path.cubicTo(137.07f, 287.219f, 138.242f, 287.086f, 139.242f, 286.578f); +path.cubicTo(140.234f, 286.078f, 141.031f, 285.203f, 141.406f, 284.055f); +path.lineTo(144.859f, 285.172f); +path.cubicTo(143.492f, 289.375f, 138.992f, 291.656f, 134.797f, 290.297f); +path.lineTo(135.922f, 286.844f); +path.close(); +path.moveTo(129.68f, 280.242f); +path.lineTo(129.68f, 280.242f); +path.lineTo(129.68f, 280.242f); +path.lineTo(131.407f, 280.804f); +path.lineTo(129.68f, 280.242f); +path.close(); +path.moveTo(133.133f, 281.367f); +path.cubicTo(132.758f, 282.508f, 132.883f, 283.687f, 133.391f, 284.679f); +path.cubicTo(133.907f, 285.679f, 134.774f, 286.468f, 135.922f, 286.843f); +path.lineTo(134.797f, 290.296f); +path.cubicTo(130.602f, 288.929f, 128.313f, 284.437f, 129.68f, 280.241f); +path.lineTo(133.133f, 281.367f); +path.close(); +path.moveTo(139.742f, 275.117f); +path.lineTo(139.742f, 275.117f); +path.lineTo(139.18f, 276.844f); +path.lineTo(139.742f, 275.117f); +path.close(); +path.moveTo(138.609f, 278.57f); +path.cubicTo(137.461f, 278.203f, 136.297f, 278.328f, 135.297f, 278.836f); +path.cubicTo(134.297f, 279.344f, 133.508f, 280.219f, 133.133f, 281.367f); +path.lineTo(129.68f, 280.242f); +path.cubicTo(131.047f, 276.039f, 135.539f, 273.758f, 139.742f, 275.117f); +path.lineTo(138.609f, 278.57f); +path.close(); +path.moveTo(141.406f, 284.055f); +path.cubicTo(141.773f, 282.907f, 141.648f, 281.735f, 141.148f, 280.735f); +path.cubicTo(140.625f, 279.735f, 139.757f, 278.946f, 138.609f, 278.571f); +path.lineTo(139.742f, 275.118f); +path.cubicTo(143.937f, 276.493f, 146.219f, 280.977f, 144.859f, 285.173f); +path.lineTo(141.406f, 284.055f); +path.close(); + testSimplify(reporter, path, filename); +} + +static void joel_2(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); + +path.moveTo(403.283f, 497.197f); +path.cubicTo(403.424f, 497.244f, 391.111f, 495.556f, 391.111f, 495.556f); +path.lineTo(392.291f, 493.165f); +path.cubicTo(392.291f, 493.165f, 388.994f, 492.056f, 386.65f, 491.821f); +path.cubicTo(384.244f, 491.454f, 381.603f, 490.774f, 381.603f, 490.774f); +path.lineTo(383.392f, 488.383f); +path.cubicTo(383.392f, 488.383f, 379.119f, 487.453f, 378.939f, 485.695f); +path.cubicTo(378.791f, 483.57f, 383.064f, 485.25f, 384.877f, 485.843f); +path.lineTo(387.697f, 484.351f); +path.cubicTo(382.752f, 483.835f, 376.595f, 482.124f, 374.478f, 480.312f); +path.lineTo(356.22f, 496.304f); +path.lineTo(368.095f, 510.499f); +path.lineTo(373.884f, 510.202f); +path.lineTo(374.478f, 509.007f); +path.lineTo(370.916f, 506.913f); +path.lineTo(371.807f, 506.022f); +path.cubicTo(371.807f, 506.022f, 374.807f, 507.28f, 377.752f, 507.514f); +path.cubicTo(380.752f, 507.881f, 387.4f, 508.108f, 387.4f, 508.108f); +path.lineTo(388.884f, 506.764f); +path.cubicTo(388.884f, 506.764f, 378.345f, 504.998f, 378.345f, 504.819f); +path.lineTo(378.04f, 503.03f); +path.cubicTo(378.04f, 503.03f, 391.415f, 505.796f, 391.399f, 505.866f); +path.lineTo(386.063f, 502.132f); +path.lineTo(387.547f, 500.335f); +path.lineTo(398.375f, 501.976f); +path.lineTo(403.283f, 497.197f); +path.lineTo(403.283f, 497.197f); +path.close(); + testSimplify(reporter, path, filename); +} + +static void joel_3(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(391.097f, 334.453f); +path.lineTo(390.761f, 334.617f); +path.lineTo(390.425f, 333.937f); +path.lineTo(390.761f, 333.765f); +path.lineTo(391.097f, 334.453f); +path.close(); +path.moveTo(391.128f, 334.438f); +path.lineTo(390.808f, 334.633f); +path.lineTo(390.402f, 333.992f); +path.lineTo(390.73f, 333.781f); +path.lineTo(391.128f, 334.438f); +path.lineTo(391.128f, 334.438f); +path.close(); +path.moveTo(455.073f, 302.219f); +path.lineTo(455.018f, 302.375f); +path.lineTo(454.87f, 302.453f); +path.lineTo(454.706f, 302.109f); +path.lineTo(455.073f, 302.219f); +path.close(); +path.moveTo(454.87f, 302.453f); +path.lineTo(391.097f, 334.453f); +path.lineTo(390.761f, 333.765f); +path.lineTo(454.534f, 301.765f); +path.lineTo(454.87f, 302.453f); +path.close(); +path.moveTo(456.245f, 296.867f); +path.lineTo(456.659f, 296.953f); +path.lineTo(456.526f, 297.351f); +path.lineTo(456.174f, 297.242f); +path.lineTo(456.245f, 296.867f); +path.lineTo(456.245f, 296.867f); +path.close(); +path.moveTo(456.526f, 297.352f); +path.lineTo(455.073f, 302.219f); +path.lineTo(454.339f, 302); +path.lineTo(455.808f, 297.133f); +path.lineTo(456.526f, 297.352f); +path.lineTo(456.526f, 297.352f); +path.close(); +path.moveTo(450.979f, 295.891f); +path.lineTo(451.112f, 295.813f); +path.lineTo(451.26f, 295.836f); +path.lineTo(451.19f, 296.211f); +path.lineTo(450.979f, 295.891f); +path.close(); +path.moveTo(451.261f, 295.836f); +path.lineTo(456.245f, 296.867f); +path.lineTo(456.089f, 297.617f); +path.lineTo(451.105f, 296.586f); +path.lineTo(451.261f, 295.836f); +path.close(); +path.moveTo(390.729f, 333.781f); +path.lineTo(450.979f, 295.89f); +path.lineTo(451.385f, 296.531f); +path.lineTo(391.127f, 334.437f); +path.lineTo(390.729f, 333.781f); +path.close(); + testSimplify(reporter, path, filename); +} + +static void joel_4(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4199d4fe), SkBits2Float(0x4265ac08)); // 19.229f, 57.418f +path.cubicTo(SkBits2Float(0x419be979), SkBits2Float(0x426574bc), SkBits2Float(0x419c2b02), SkBits2Float(0x42653c6a), SkBits2Float(0x419af5c3), SkBits2Float(0x42645f3b)); // 19.489f, 57.364f, 19.521f, 57.309f, 19.37f, 57.093f +path.cubicTo(SkBits2Float(0x419a1894), SkBits2Float(0x4263a3d7), SkBits2Float(0x4198cccd), SkBits2Float(0x4262f2b0), SkBits2Float(0x4197c290), SkBits2Float(0x4262374b)); // 19.262f, 56.91f, 19.1f, 56.737f, 18.97f, 56.554f +path.cubicTo(SkBits2Float(0x41960832), SkBits2Float(0x42610c49), SkBits2Float(0x41944dd4), SkBits2Float(0x425fd709), SkBits2Float(0x41927cee), SkBits2Float(0x425ea0c4)); // 18.754f, 56.262f, 18.538f, 55.96f, 18.311f, 55.657f +path.cubicTo(SkBits2Float(0x4191b646), SkBits2Float(0x425e1cab), SkBits2Float(0x418edd30), SkBits2Float(0x425ca4dd), SkBits2Float(0x418f4bc7), SkBits2Float(0x425bdd2e)); // 18.214f, 55.528f, 17.858f, 55.161f, 17.912f, 54.966f +path.lineTo(SkBits2Float(0x41903f7d), SkBits2Float(0x425b6e96)); // 18.031f, 54.858f +path.cubicTo(SkBits2Float(0x41921062), SkBits2Float(0x425aa6e8), SkBits2Float(0x4193872b), SkBits2Float(0x425bd1ea), SkBits2Float(0x41947ae1), SkBits2Float(0x425c77cd)); // 18.258f, 54.663f, 18.441f, 54.955f, 18.56f, 55.117f +path.cubicTo(SkBits2Float(0x4195dd2f), SkBits2Float(0x425d6b83), SkBits2Float(0x4197ae14), SkBits2Float(0x425e1caa), SkBits2Float(0x419924dd), SkBits2Float(0x425ef9d9)); // 18.733f, 55.355f, 18.96f, 55.528f, 19.143f, 55.744f +path.cubicTo(SkBits2Float(0x419a1893), SkBits2Float(0x425f9479), SkBits2Float(0x419adf3b), SkBits2Float(0x42601997), SkBits2Float(0x419bd2f1), SkBits2Float(0x42609db0)); // 19.262f, 55.895f, 19.359f, 56.025f, 19.478f, 56.154f +path.cubicTo(SkBits2Float(0x419c147a), SkBits2Float(0x4260c9b8), SkBits2Float(0x419c8312), SkBits2Float(0x4260e03f), SkBits2Float(0x419cb020), SkBits2Float(0x42610104)); // 19.51f, 56.197f, 19.564f, 56.219f, 19.586f, 56.251f +path.cubicTo(SkBits2Float(0x419d0830), SkBits2Float(0x42613850), SkBits2Float(0x419da3d6), SkBits2Float(0x4261bd6e), SkBits2Float(0x419e126e), SkBits2Float(0x4261d2f0)); // 19.629f, 56.305f, 19.705f, 56.435f, 19.759f, 56.456f +path.lineTo(SkBits2Float(0x419e28f5), SkBits2Float(0x4261d2f0)); // 19.77f, 56.456f +path.lineTo(SkBits2Float(0x419e28f5), SkBits2Float(0x4261f4bb)); // 19.77f, 56.489f +path.cubicTo(SkBits2Float(0x419e3d70), SkBits2Float(0x4261fef8), SkBits2Float(0x419e53f7), SkBits2Float(0x4261f4bb), SkBits2Float(0x419e8105), SkBits2Float(0x4261fef8)); // 19.78f, 56.499f, 19.791f, 56.489f, 19.813f, 56.499f +path.cubicTo(SkBits2Float(0x419eac07), SkBits2Float(0x426220c3), SkBits2Float(0x419eac07), SkBits2Float(0x42624187), SkBits2Float(0x419eef9d), SkBits2Float(0x4262580f)); // 19.834f, 56.532f, 19.834f, 56.564f, 19.867f, 56.586f +path.cubicTo(SkBits2Float(0x419fe353), SkBits2Float(0x4262f2af), SkBits2Float(0x41a0eb84), SkBits2Float(0x426377cd), SkBits2Float(0x41a1b22c), SkBits2Float(0x4263fbe6)); // 19.986f, 56.737f, 20.115f, 56.867f, 20.212f, 56.996f +path.cubicTo(SkBits2Float(0x41a20a3c), SkBits2Float(0x42641db1), SkBits2Float(0x41a2e76b), SkBits2Float(0x4264a1c9), SkBits2Float(0x41a34188), SkBits2Float(0x4264ad0d)); // 20.255f, 57.029f, 20.363f, 57.158f, 20.407f, 57.169f +path.cubicTo(SkBits2Float(0x41a36c8a), SkBits2Float(0x4264ad0d), SkBits2Float(0x41a3c6a7), SkBits2Float(0x4264a1c9), SkBits2Float(0x41a3f1a9), SkBits2Float(0x4264ad0d)); // 20.428f, 57.169f, 20.472f, 57.158f, 20.493f, 57.169f +path.cubicTo(SkBits2Float(0x41a3f1a9), SkBits2Float(0x42648c48), SkBits2Float(0x41a41eb7), SkBits2Float(0x42648105), SkBits2Float(0x41a449b9), SkBits2Float(0x426475c1)); // 20.493f, 57.137f, 20.515f, 57.126f, 20.536f, 57.115f +path.cubicTo(SkBits2Float(0x41a48d4f), SkBits2Float(0x4263f1a8), SkBits2Float(0x41a46040), SkBits2Float(0x42634082), SkBits2Float(0x41a48d4f), SkBits2Float(0x4262bb63)); // 20.569f, 56.986f, 20.547f, 56.813f, 20.569f, 56.683f +path.cubicTo(SkBits2Float(0x41a51061), SkBits2Float(0x426122d0), SkBits2Float(0x41a63126), SkBits2Float(0x425f51ea), SkBits2Float(0x41a82d0d), SkBits2Float(0x425e0624)); // 20.633f, 56.284f, 20.774f, 55.83f, 21.022f, 55.506f +path.cubicTo(SkBits2Float(0x41a90a3c), SkBits2Float(0x425d820b), SkBits2Float(0x41aab01f), SkBits2Float(0x425cba5d), SkBits2Float(0x41ab0830), SkBits2Float(0x425c147a)); // 21.13f, 55.377f, 21.336f, 55.182f, 21.379f, 55.02f +path.cubicTo(SkBits2Float(0x41aa147a), SkBits2Float(0x425bf3b5), SkBits2Float(0x41a8df3a), SkBits2Float(0x425c0936), SkBits2Float(0x41a7d4fd), SkBits2Float(0x425c147a)); // 21.26f, 54.988f, 21.109f, 55.009f, 20.979f, 55.02f +path.cubicTo(SkBits2Float(0x41a74fde), SkBits2Float(0x425c147a), SkBits2Float(0x41a65e34), SkBits2Float(0x425c4082), SkBits2Float(0x41a5c28e), SkBits2Float(0x425c4082)); // 20.914f, 55.02f, 20.796f, 55.063f, 20.72f, 55.063f +path.cubicTo(SkBits2Float(0x41a56a7e), SkBits2Float(0x425c353e), SkBits2Float(0x41a4fbe6), SkBits2Float(0x425c147a), SkBits2Float(0x41a4ced8), SkBits2Float(0x425c0936)); // 20.677f, 55.052f, 20.623f, 55.02f, 20.601f, 55.009f +path.cubicTo(SkBits2Float(0x41a53d70), SkBits2Float(0x425af4bb), SkBits2Float(0x41a5ed90), SkBits2Float(0x425abd6f), SkBits2Float(0x41a85a1c), SkBits2Float(0x425aa6e8)); // 20.655f, 54.739f, 20.741f, 54.685f, 21.044f, 54.663f +path.cubicTo(SkBits2Float(0x41a920c4), SkBits2Float(0x425a9cab), SkBits2Float(0x41a9d0e5), SkBits2Float(0x425aa6e8), SkBits2Float(0x41aa5603), SkBits2Float(0x425a9167)); // 21.141f, 54.653f, 21.227f, 54.663f, 21.292f, 54.642f +path.cubicTo(SkBits2Float(0x41aa8311), SkBits2Float(0x425a8623), SkBits2Float(0x41aa9999), SkBits2Float(0x425a655f), SkBits2Float(0x41aab020), SkBits2Float(0x425a655f)); // 21.314f, 54.631f, 21.325f, 54.599f, 21.336f, 54.599f +path.cubicTo(SkBits2Float(0x41aa3f7c), SkBits2Float(0x42599eb7), SkBits2Float(0x41a9a5e3), SkBits2Float(0x42591998), SkBits2Float(0x41a9374b), SkBits2Float(0x42586871)); // 21.281f, 54.405f, 21.206f, 54.275f, 21.152f, 54.102f +path.cubicTo(SkBits2Float(0x41a8c8b3), SkBits2Float(0x4257e458), SkBits2Float(0x41a8b22c), SkBits2Float(0x42575f3a), SkBits2Float(0x41a85a1c), SkBits2Float(0x4256c49a)); // 21.098f, 53.973f, 21.087f, 53.843f, 21.044f, 53.692f +path.cubicTo(SkBits2Float(0x41a76666), SkBits2Float(0x42551479), SkBits2Float(0x41a68937), SkBits2Float(0x4252cabf), SkBits2Float(0x41a74fdf), SkBits2Float(0x4250a1c9)); // 20.925f, 53.27f, 20.817f, 52.698f, 20.914f, 52.158f +path.cubicTo(SkBits2Float(0x41a77ced), SkBits2Float(0x42500729), SkBits2Float(0x41a870a4), SkBits2Float(0x424e8417), SkBits2Float(0x41a8b22d), SkBits2Float(0x424e4ccb)); // 20.936f, 52.007f, 21.055f, 51.629f, 21.087f, 51.575f +path.cubicTo(SkBits2Float(0x41a8b22d), SkBits2Float(0x424e4187), SkBits2Float(0x41aa147b), SkBits2Float(0x424cc9b9), SkBits2Float(0x41aab021), SkBits2Float(0x424c2f19)); // 21.087f, 51.564f, 21.26f, 51.197f, 21.336f, 51.046f +path.cubicTo(SkBits2Float(0x41aac49c), SkBits2Float(0x424c1892), SkBits2Float(0x41ab49bb), SkBits2Float(0x424b9eb7), SkBits2Float(0x41ab8b44), SkBits2Float(0x424b676b)); // 21.346f, 51.024f, 21.411f, 50.905f, 21.443f, 50.851f +path.cubicTo(SkBits2Float(0x41ac3d71), SkBits2Float(0x424ab644), SkBits2Float(0x41ad45a2), SkBits2Float(0x424a26e8), SkBits2Float(0x41ae22d1), SkBits2Float(0x42498105)); // 21.53f, 50.678f, 21.659f, 50.538f, 21.767f, 50.376f +path.cubicTo(SkBits2Float(0x41ae6667), SkBits2Float(0x42496b84), SkBits2Float(0x41aeeb85), SkBits2Float(0x42491db1), SkBits2Float(0x41af0000), SkBits2Float(0x4248fbe6)); // 21.8f, 50.355f, 21.865f, 50.279f, 21.875f, 50.246f +path.cubicTo(SkBits2Float(0x41b0624e), SkBits2Float(0x4248353e), SkBits2Float(0x41b1db23), SkBits2Float(0x424779da), SkBits2Float(0x41b353f8), SkBits2Float(0x4246bd6f)); // 22.048f, 50.052f, 22.232f, 49.869f, 22.416f, 49.685f +path.cubicTo(SkBits2Float(0x41b3c083), SkBits2Float(0x42468623), SkBits2Float(0x41b445a2), SkBits2Float(0x42464ed7), SkBits2Float(0x41b4cac1), SkBits2Float(0x4246178c)); // 22.469f, 49.631f, 22.534f, 49.577f, 22.599f, 49.523f +path.cubicTo(SkBits2Float(0x41b56667), SkBits2Float(0x4245c9b9), SkBits2Float(0x41b62d0f), SkBits2Float(0x4245872a), SkBits2Float(0x41b6c8b5), SkBits2Float(0x4245449a)); // 22.675f, 49.447f, 22.772f, 49.382f, 22.848f, 49.317f +path.cubicTo(SkBits2Float(0x41b7624f), SkBits2Float(0x42450311), SkBits2Float(0x41b7e76d), SkBits2Float(0x4244a9fa), SkBits2Float(0x41b88313), SkBits2Float(0x42445d2d)); // 22.923f, 49.253f, 22.988f, 49.166f, 23.064f, 49.091f +path.cubicTo(SkBits2Float(0x41b949bb), SkBits2Float(0x4243ee95), SkBits2Float(0x41ba1063), SkBits2Float(0x424374ba), SkBits2Float(0x41baed92), SkBits2Float(0x42431166)); // 23.161f, 48.983f, 23.258f, 48.864f, 23.366f, 48.767f +path.cubicTo(SkBits2Float(0x41bb45a2), SkBits2Float(0x4242c393), SkBits2Float(0x41bbb43a), SkBits2Float(0x424276c6), SkBits2Float(0x41bc0e57), SkBits2Float(0x424228f3)); // 23.409f, 48.691f, 23.463f, 48.616f, 23.507f, 48.54f +path.cubicTo(SkBits2Float(0x41bc6667), SkBits2Float(0x4241e664), SkBits2Float(0x41bc7ae2), SkBits2Float(0x4241a4da), SkBits2Float(0x41bcd2f3), SkBits2Float(0x4241624b)); // 23.55f, 48.475f, 23.56f, 48.411f, 23.603f, 48.346f +path.cubicTo(SkBits2Float(0x41bd0001), SkBits2Float(0x42411478), SkBits2Float(0x41bd0001), SkBits2Float(0x4240c6a5), SkBits2Float(0x41bd1689), SkBits2Float(0x4240851c)); // 23.625f, 48.27f, 23.625f, 48.194f, 23.636f, 48.13f +path.cubicTo(SkBits2Float(0x41bd2d10), SkBits2Float(0x42404cca), SkBits2Float(0x41bdb023), SkBits2Float(0x423fd3f5), SkBits2Float(0x41bd8521), SkBits2Float(0x423f7adf)); // 23.647f, 48.075f, 23.711f, 47.957f, 23.69f, 47.87f +path.lineTo(SkBits2Float(0x41bd6e9a), SkBits2Float(0x423f7adf)); // 23.679f, 47.87f +path.cubicTo(SkBits2Float(0x41bd6e9a), SkBits2Float(0x423f7adf), SkBits2Float(0x41bd5813), SkBits2Float(0x423f4ed7), SkBits2Float(0x41bd168a), SkBits2Float(0x423f4499)); // 23.679f, 47.87f, 23.668f, 47.827f, 23.636f, 47.817f +path.cubicTo(SkBits2Float(0x41bc916b), SkBits2Float(0x423f22ce), SkBits2Float(0x41bc22d4), SkBits2Float(0x423f3955), SkBits2Float(0x41bb893a), SkBits2Float(0x423f178b)); // 23.571f, 47.784f, 23.517f, 47.806f, 23.442f, 47.773f +path.cubicTo(SkBits2Float(0x41bb2f1d), SkBits2Float(0x423f0c47), SkBits2Float(0x41bb041b), SkBits2Float(0x423ee03f), SkBits2Float(0x41baac0b), SkBits2Float(0x423ec9b8)); // 23.398f, 47.762f, 23.377f, 47.719f, 23.334f, 47.697f +path.cubicTo(SkBits2Float(0x41baac0b), SkBits2Float(0x423ebf7b), SkBits2Float(0x41bac086), SkBits2Float(0x423ea8f3), SkBits2Float(0x41bac086), SkBits2Float(0x423e926c)); // 23.334f, 47.687f, 23.344f, 47.665f, 23.344f, 47.643f +path.cubicTo(SkBits2Float(0x41bb2f1e), SkBits2Float(0x423e882f), SkBits2Float(0x41bc0e59), SkBits2Float(0x423e6664), SkBits2Float(0x41bc916b), SkBits2Float(0x423e5c26)); // 23.398f, 47.633f, 23.507f, 47.6f, 23.571f, 47.59f +path.cubicTo(SkBits2Float(0x41be4bc9), SkBits2Float(0x423e50e2), SkBits2Float(0x41c53542), SkBits2Float(0x423e926c), SkBits2Float(0x41c5ba61), SkBits2Float(0x423e24da)); // 23.787f, 47.579f, 24.651f, 47.643f, 24.716f, 47.536f +path.cubicTo(SkBits2Float(0x41c61271), SkBits2Float(0x423de24b), SkBits2Float(0x41c61271), SkBits2Float(0x423d1a9d), SkBits2Float(0x41c63f80), SkBits2Float(0x423ca1c8)); // 24.759f, 47.471f, 24.759f, 47.276f, 24.781f, 47.158f +path.cubicTo(SkBits2Float(0x41c68109), SkBits2Float(0x423bda1a), SkBits2Float(0x41c6ae18), SkBits2Float(0x423afceb), SkBits2Float(0x41c70628), SkBits2Float(0x423a2aff)); // 24.813f, 46.963f, 24.835f, 46.747f, 24.878f, 46.542f +path.cubicTo(SkBits2Float(0x41c71caf), SkBits2Float(0x42399ba3), SkBits2Float(0x41c81065), SkBits2Float(0x42379eb5), SkBits2Float(0x41c79fc2), SkBits2Float(0x4237459f)); // 24.889f, 46.402f, 25.008f, 45.905f, 24.953f, 45.818f +path.cubicTo(SkBits2Float(0x41c70628), SkBits2Float(0x4236e24b), SkBits2Float(0x41c4dd33), SkBits2Float(0x4237459f), SkBits2Float(0x41c45814), SkBits2Float(0x423750e3)); // 24.878f, 45.721f, 24.608f, 45.818f, 24.543f, 45.829f +path.cubicTo(SkBits2Float(0x41c245a5), SkBits2Float(0x42379eb6), SkBits2Float(0x41bea5e7), SkBits2Float(0x42380d4d), SkBits2Float(0x41bbf5c6), SkBits2Float(0x4237ec89)); // 24.284f, 45.905f, 23.831f, 46.013f, 23.495f, 45.981f +path.cubicTo(SkBits2Float(0x41b9f9df), SkBits2Float(0x4237e145), SkBits2Float(0x41b7e770), SkBits2Float(0x4237a9fa), SkBits2Float(0x41b62d12), SkBits2Float(0x4237676a)); // 23.247f, 45.97f, 22.988f, 45.916f, 22.772f, 45.851f +path.cubicTo(SkBits2Float(0x41b4312b), SkBits2Float(0x423724db), SkBits2Float(0x41b1f1ae), SkBits2Float(0x42369fbc), SkBits2Float(0x41af9baa), SkBits2Float(0x423673b4)); // 22.524f, 45.786f, 22.243f, 45.656f, 21.951f, 45.613f +path.cubicTo(SkBits2Float(0x41ae7ae5), SkBits2Float(0x42366977), SkBits2Float(0x41aced96), SkBits2Float(0x42365d2d), SkBits2Float(0x41ab8b48), SkBits2Float(0x42366977)); // 21.81f, 45.603f, 21.616f, 45.591f, 21.443f, 45.603f +path.cubicTo(SkBits2Float(0x41a9e771), SkBits2Float(0x42368a3c), SkBits2Float(0x41a82d13), SkBits2Float(0x4236d708), SkBits2Float(0x41a65e3a), SkBits2Float(0x4236b644)); // 21.238f, 45.635f, 21.022f, 45.71f, 20.796f, 45.678f +path.cubicTo(SkBits2Float(0x41a65e3a), SkBits2Float(0x4236ab00), SkBits2Float(0x41a647b3), SkBits2Float(0x42369fbd), SkBits2Float(0x41a65e3a), SkBits2Float(0x42369479)); // 20.796f, 45.667f, 20.785f, 45.656f, 20.796f, 45.645f +path.cubicTo(SkBits2Float(0x41a672b5), SkBits2Float(0x42366977), SkBits2Float(0x41a7a7f4), SkBits2Float(0x42363125), SkBits2Float(0x41a81898), SkBits2Float(0x42361ba4)); // 20.806f, 45.603f, 20.957f, 45.548f, 21.012f, 45.527f +path.cubicTo(SkBits2Float(0x41a85a21), SkBits2Float(0x42361060), SkBits2Float(0x41a8df40), SkBits2Float(0x4235d915), SkBits2Float(0x41a94dd7), SkBits2Float(0x4235cdd1)); // 21.044f, 45.516f, 21.109f, 45.462f, 21.163f, 45.451f +path.cubicTo(SkBits2Float(0x41ab8b48), SkBits2Float(0x42356a7d), SkBits2Float(0x41af8523), SkBits2Float(0x423575c1), SkBits2Float(0x41b249be), SkBits2Float(0x42359685)); // 21.443f, 45.354f, 21.94f, 45.365f, 22.286f, 45.397f +path.cubicTo(SkBits2Float(0x41b3d70e), SkBits2Float(0x4235a1c9), SkBits2Float(0x41b6168b), SkBits2Float(0x4235cdd1), SkBits2Float(0x41b7e770), SkBits2Float(0x4235ad0c)); // 22.48f, 45.408f, 22.761f, 45.451f, 22.988f, 45.419f +path.cubicTo(SkBits2Float(0x41bac087), SkBits2Float(0x42359685), SkBits2Float(0x41bd6e9b), SkBits2Float(0x4234fbe5), SkBits2Float(0x41c03337), SkBits2Float(0x4234af18)); // 23.344f, 45.397f, 23.679f, 45.246f, 24.025f, 45.171f +path.cubicTo(SkBits2Float(0x41c2cac4), SkBits2Float(0x42346145), SkBits2Float(0x41c56252), SkBits2Float(0x4234820a), SkBits2Float(0x41c81066), SkBits2Float(0x42346145)); // 24.349f, 45.095f, 24.673f, 45.127f, 25.008f, 45.095f +path.cubicTo(SkBits2Float(0x41c824e1), SkBits2Float(0x42340935), SkBits2Float(0x41c89378), SkBits2Float(0x42330a3b), SkBits2Float(0x41c7b649), SkBits2Float(0x4232fef7)); // 25.018f, 45.009f, 25.072f, 44.76f, 24.964f, 44.749f +path.cubicTo(SkBits2Float(0x41c6d91a), SkBits2Float(0x4232e976), SkBits2Float(0x41c5a3da), SkBits2Float(0x42338416), SkBits2Float(0x41c51ebc), SkBits2Float(0x4233a4da)); // 24.856f, 44.728f, 24.705f, 44.879f, 24.64f, 44.911f +path.cubicTo(SkBits2Float(0x41c42b06), SkBits2Float(0x4233bb61), SkBits2Float(0x41c2cac4), SkBits2Float(0x4233d0e2), SkBits2Float(0x41c1d70e), SkBits2Float(0x4233e769)); // 24.521f, 44.933f, 24.349f, 44.954f, 24.23f, 44.976f +path.cubicTo(SkBits2Float(0x41c08b47), SkBits2Float(0x4233f2ad), SkBits2Float(0x41bf1272), SkBits2Float(0x4233c6a4), SkBits2Float(0x41bdf3ba), SkBits2Float(0x4233bb61)); // 24.068f, 44.987f, 23.884f, 44.944f, 23.744f, 44.933f +path.cubicTo(SkBits2Float(0x41bcd2f5), SkBits2Float(0x4233b01d), SkBits2Float(0x41bbf5c6), SkBits2Float(0x4233b01d), SkBits2Float(0x41baed95), SkBits2Float(0x4233a4da)); // 23.603f, 44.922f, 23.495f, 44.922f, 23.366f, 44.911f +path.cubicTo(SkBits2Float(0x41ba26ed), SkBits2Float(0x42338f59), SkBits2Float(0x41b91cb0), SkBits2Float(0x4233580d), SkBits2Float(0x41b83f81), SkBits2Float(0x4233580d)); // 23.269f, 44.89f, 23.139f, 44.836f, 23.031f, 44.836f +path.cubicTo(SkBits2Float(0x41b4b43d), SkBits2Float(0x42333642), SkBits2Float(0x41b19791), SkBits2Float(0x4233a4da), SkBits2Float(0x41aea7f4), SkBits2Float(0x4233d0e2)); // 22.588f, 44.803f, 22.199f, 44.911f, 21.832f, 44.954f +path.cubicTo(SkBits2Float(0x41aba1cf), SkBits2Float(0x42340934), SkBits2Float(0x41a7666b), SkBits2Float(0x4233e769), SkBits2Float(0x41a4b856), SkBits2Float(0x42338415)); // 21.454f, 45.009f, 20.925f, 44.976f, 20.59f, 44.879f +path.cubicTo(SkBits2Float(0x41a46046), SkBits2Float(0x423378d1), SkBits2Float(0x41a3f1ae), SkBits2Float(0x4233580d), SkBits2Float(0x41a3c6ac), SkBits2Float(0x42334cc9)); // 20.547f, 44.868f, 20.493f, 44.836f, 20.472f, 44.825f +path.cubicTo(SkBits2Float(0x41a28f60), SkBits2Float(0x4233157d), SkBits2Float(0x41a19db6), SkBits2Float(0x42330a3a), SkBits2Float(0x41a0c087), SkBits2Float(0x4232c7aa)); // 20.32f, 44.771f, 20.202f, 44.76f, 20.094f, 44.695f +path.cubicTo(SkBits2Float(0x41a0eb89), SkBits2Float(0x4232bc66), SkBits2Float(0x41a0eb89), SkBits2Float(0x4232905e), SkBits2Float(0x41a10210), SkBits2Float(0x4232905e)); // 20.115f, 44.684f, 20.115f, 44.641f, 20.126f, 44.641f +path.cubicTo(SkBits2Float(0x41a19db6), SkBits2Float(0x42325912), SkBits2Float(0x41a2645e), SkBits2Float(0x42326f99), SkBits2Float(0x41a35608), SkBits2Float(0x42326f99)); // 20.202f, 44.587f, 20.299f, 44.609f, 20.417f, 44.609f +path.cubicTo(SkBits2Float(0x41a476cd), SkBits2Float(0x42324ed4), SkBits2Float(0x41a5ed95), SkBits2Float(0x4232384d), SkBits2Float(0x41a724e1), SkBits2Float(0x42320c45)); // 20.558f, 44.577f, 20.741f, 44.555f, 20.893f, 44.512f +path.cubicTo(SkBits2Float(0x41a8c8b8), SkBits2Float(0x4231c9b6), SkBits2Float(0x41aa999d), SkBits2Float(0x42316662), SkBits2Float(0x41ac26ed), SkBits2Float(0x4231188f)); // 21.098f, 44.447f, 21.325f, 44.35f, 21.519f, 44.274f +path.cubicTo(SkBits2Float(0x41af168b), SkBits2Float(0x423072ac), SkBits2Float(0x41b249be), SkBits2Float(0x42300f58), SkBits2Float(0x41b57ae5), SkBits2Float(0x422fe249)); // 21.886f, 44.112f, 22.286f, 44.015f, 22.685f, 43.971f +path.cubicTo(SkBits2Float(0x41b66e9b), SkBits2Float(0x422fd80c), SkBits2Float(0x41b7d0e9), SkBits2Float(0x422fee93), SkBits2Float(0x41b89791), SkBits2Float(0x422fee93)); // 22.804f, 43.961f, 22.977f, 43.983f, 23.074f, 43.983f +path.cubicTo(SkBits2Float(0x41bb1897), SkBits2Float(0x42300f58), SkBits2Float(0x41bd2d12), SkBits2Float(0x423024d9), SkBits2Float(0x41bfc49f), SkBits2Float(0x4230301c)); // 23.387f, 44.015f, 23.647f, 44.036f, 23.971f, 44.047f +path.cubicTo(SkBits2Float(0x41c0e357), SkBits2Float(0x423046a3), SkBits2Float(0x41c245a5), SkBits2Float(0x42305c24), SkBits2Float(0x41c3a7f3), SkBits2Float(0x423051e7)); // 24.111f, 44.069f, 24.284f, 44.09f, 24.457f, 44.08f +path.cubicTo(SkBits2Float(0x41c50835), SkBits2Float(0x423046a3), SkBits2Float(0x41c69791), SkBits2Float(0x42300f58), SkBits2Float(0x41c79fc2), SkBits2Float(0x422fb641)); // 24.629f, 44.069f, 24.824f, 44.015f, 24.953f, 43.928f +path.cubicTo(SkBits2Float(0x41c7f9df), SkBits2Float(0x422fa0c0), SkBits2Float(0x41c86876), SkBits2Float(0x422f5e31), SkBits2Float(0x41c8eb89), SkBits2Float(0x422f52ed)); // 24.997f, 43.907f, 25.051f, 43.842f, 25.115f, 43.831f +path.cubicTo(SkBits2Float(0x41c9b43d), SkBits2Float(0x422f3c66), SkBits2Float(0x41c9df3f), SkBits2Float(0x422fb641), SkBits2Float(0x41c9f5c6), SkBits2Float(0x42300f57)); // 25.213f, 43.809f, 25.234f, 43.928f, 25.245f, 44.015f +path.cubicTo(SkBits2Float(0x41ca0c4d), SkBits2Float(0x4230e143), SkBits2Float(0x41c9df3f), SkBits2Float(0x42319ca7), SkBits2Float(0x41c9f5c6), SkBits2Float(0x4232384d)); // 25.256f, 44.22f, 25.234f, 44.403f, 25.245f, 44.555f +path.cubicTo(SkBits2Float(0x41ca395c), SkBits2Float(0x4234fbe2), SkBits2Float(0x41ca22d4), SkBits2Float(0x4237cabc), SkBits2Float(0x41ca7ae5), SkBits2Float(0x423a6d8c)); // 25.278f, 45.246f, 25.267f, 45.948f, 25.31f, 46.607f +path.cubicTo(SkBits2Float(0x41ca916c), SkBits2Float(0x423b3f78), SkBits2Float(0x41ca645e), SkBits2Float(0x423ca1c5), SkBits2Float(0x41ca916c), SkBits2Float(0x423d9475)); // 25.321f, 46.812f, 25.299f, 47.158f, 25.321f, 47.395f +path.cubicTo(SkBits2Float(0x41ca916c), SkBits2Float(0x423daafc), SkBits2Float(0x41ca7ae5), SkBits2Float(0x423dd704), SkBits2Float(0x41ca916c), SkBits2Float(0x423dec85)); // 25.321f, 47.417f, 25.31f, 47.46f, 25.321f, 47.481f +path.cubicTo(SkBits2Float(0x41caa5e7), SkBits2Float(0x423e0e50), SkBits2Float(0x41cb0004), SkBits2Float(0x423e459c), SkBits2Float(0x41cb2b06), SkBits2Float(0x423e50df)); // 25.331f, 47.514f, 25.375f, 47.568f, 25.396f, 47.579f +path.cubicTo(SkBits2Float(0x41cb6e9c), SkBits2Float(0x423e5c23), SkBits2Float(0x41ce47b2), SkBits2Float(0x423e7ce7), SkBits2Float(0x41ce8b48), SkBits2Float(0x423e6660)); // 25.429f, 47.59f, 25.785f, 47.622f, 25.818f, 47.6f +path.lineTo(SkBits2Float(0x41ceb64a), SkBits2Float(0x423e5c23)); // 25.839f, 47.59f +path.cubicTo(SkBits2Float(0x41d1395c), SkBits2Float(0x423e5c23), SkBits2Float(0x41d41273), SkBits2Float(0x423e50df), SkBits2Float(0x41d6666b), SkBits2Float(0x423e6660)); // 26.153f, 47.59f, 26.509f, 47.579f, 26.8f, 47.6f +path.cubicTo(SkBits2Float(0x41d71898), SkBits2Float(0x423e7ce7), SkBits2Float(0x41d80a42), SkBits2Float(0x423e5c23), SkBits2Float(0x41d8a5e8), SkBits2Float(0x423e7ce7)); // 26.887f, 47.622f, 27.005f, 47.59f, 27.081f, 47.622f +path.cubicTo(SkBits2Float(0x41d8d2f6), SkBits2Float(0x423e882b), SkBits2Float(0x41d8d2f6), SkBits2Float(0x423e9268), SkBits2Float(0x41d8fdf8), SkBits2Float(0x423e9eb2)); // 27.103f, 47.633f, 27.103f, 47.643f, 27.124f, 47.655f +path.cubicTo(SkBits2Float(0x41d8e771), SkBits2Float(0x423ebf77), SkBits2Float(0x41d8fdf8), SkBits2Float(0x423ed4f8), SkBits2Float(0x41d8e771), SkBits2Float(0x423eeb7f)); // 27.113f, 47.687f, 27.124f, 47.708f, 27.113f, 47.73f +path.cubicTo(SkBits2Float(0x41d88f61), SkBits2Float(0x423f4496), SkBits2Float(0x41d71898), SkBits2Float(0x423f4496), SkBits2Float(0x41d6aa00), SkBits2Float(0x423f9162)); // 27.07f, 47.817f, 26.887f, 47.817f, 26.833f, 47.892f +path.cubicTo(SkBits2Float(0x41d547b2), SkBits2Float(0x42406e91), SkBits2Float(0x41d43d75), SkBits2Float(0x4241ba58), SkBits2Float(0x41d38d54), SkBits2Float(0x4242b952)); // 26.66f, 48.108f, 26.53f, 48.432f, 26.444f, 48.681f +path.cubicTo(SkBits2Float(0x41d1395c), SkBits2Float(0x4245a8f0), SkBits2Float(0x41d0b231), SkBits2Float(0x42491dac), SkBits2Float(0x41d2147f), SkBits2Float(0x424c2f15)); // 26.153f, 49.415f, 26.087f, 50.279f, 26.26f, 51.046f +path.cubicTo(SkBits2Float(0x41d2418d), SkBits2Float(0x424c7be2), SkBits2Float(0x41d2999e), SkBits2Float(0x424cc9b5), SkBits2Float(0x41d2b025), SkBits2Float(0x424d0c44)); // 26.282f, 51.121f, 26.325f, 51.197f, 26.336f, 51.262f +path.cubicTo(SkBits2Float(0x41d33544), SkBits2Float(0x424dc7a8), SkBits2Float(0x41d3a3db), SkBits2Float(0x424e8413), SkBits2Float(0x41d453fc), SkBits2Float(0x424f136f)); // 26.401f, 51.445f, 26.455f, 51.629f, 26.541f, 51.769f +path.cubicTo(SkBits2Float(0x41d453fc), SkBits2Float(0x424f136f), SkBits2Float(0x41d59fc3), SkBits2Float(0x42506a79), SkBits2Float(0x41d6c087), SkBits2Float(0x4250e454)); // 26.541f, 51.769f, 26.703f, 52.104f, 26.844f, 52.223f +path.cubicTo(SkBits2Float(0x41d6c087), SkBits2Float(0x4250ef98), SkBits2Float(0x41d6eb89), SkBits2Float(0x4251105c), SkBits2Float(0x41d70210), SkBits2Float(0x4251105c)); // 26.844f, 52.234f, 26.865f, 52.266f, 26.876f, 52.266f +path.cubicTo(SkBits2Float(0x41d71897), SkBits2Float(0x42511ba0), SkBits2Float(0x41d75a20), SkBits2Float(0x4251105c), SkBits2Float(0x41d7872f), SkBits2Float(0x4251105c)); // 26.887f, 52.277f, 26.919f, 52.266f, 26.941f, 52.266f +path.cubicTo(SkBits2Float(0x41d87ae5), SkBits2Float(0x42501ca6), SkBits2Float(0x41d9147f), SkBits2Float(0x424f136e), SkBits2Float(0x41da0835), SkBits2Float(0x424e157b)); // 27.06f, 52.028f, 27.135f, 51.769f, 27.254f, 51.521f +path.cubicTo(SkBits2Float(0x41da1ebc), SkBits2Float(0x424df4b6), SkBits2Float(0x41db1066), SkBits2Float(0x424d0c44), SkBits2Float(0x41db1066), SkBits2Float(0x424d0100)); // 27.265f, 51.489f, 27.383f, 51.262f, 27.383f, 51.251f +path.cubicTo(SkBits2Float(0x41db3d74), SkBits2Float(0x424cc9b4), SkBits2Float(0x41db9585), SkBits2Float(0x424c8725), SkBits2Float(0x41dbd91a), SkBits2Float(0x424c5b1d)); // 27.405f, 51.197f, 27.448f, 51.132f, 27.481f, 51.089f +path.cubicTo(SkBits2Float(0x41dc5e39), SkBits2Float(0x424bcbc1), SkBits2Float(0x41dcf7d2), SkBits2Float(0x424b301b), SkBits2Float(0x41dd7cf1), SkBits2Float(0x424aac02)); // 27.546f, 50.949f, 27.621f, 50.797f, 27.686f, 50.668f +path.cubicTo(SkBits2Float(0x41ddd501), SkBits2Float(0x424a5e2f), SkBits2Float(0x41ddeb89), SkBits2Float(0x424a105c), SkBits2Float(0x41de4399), SkBits2Float(0x4249b84c)); // 27.729f, 50.592f, 27.74f, 50.516f, 27.783f, 50.43f +path.cubicTo(SkBits2Float(0x41de70a7), SkBits2Float(0x4249a1c5), SkBits2Float(0x41def5c6), SkBits2Float(0x42490725), SkBits2Float(0x41df20c8), SkBits2Float(0x4248e660)); // 27.805f, 50.408f, 27.87f, 50.257f, 27.891f, 50.225f +path.cubicTo(SkBits2Float(0x41df8f60), SkBits2Float(0x42488206), SkBits2Float(0x41e0c49f), SkBits2Float(0x42474cc6), SkBits2Float(0x41e10835), SkBits2Float(0x42472c02)); // 27.945f, 50.127f, 28.096f, 49.825f, 28.129f, 49.793f +path.cubicTo(SkBits2Float(0x41e11ebc), SkBits2Float(0x42472c02), SkBits2Float(0x41e13337), SkBits2Float(0x4246fef4), SkBits2Float(0x41e13337), SkBits2Float(0x4246f4b6)); // 28.14f, 49.793f, 28.15f, 49.749f, 28.15f, 49.739f +path.cubicTo(SkBits2Float(0x41e149be), SkBits2Float(0x4246c7a8), SkBits2Float(0x41e226ed), SkBits2Float(0x42461787), SkBits2Float(0x41e253fc), SkBits2Float(0x4245df35)); // 28.161f, 49.695f, 28.269f, 49.523f, 28.291f, 49.468f +path.cubicTo(SkBits2Float(0x41e27efe), SkBits2Float(0x4245d3f1), SkBits2Float(0x41e2ac0c), SkBits2Float(0x42459ca6), SkBits2Float(0x41e2ac0c), SkBits2Float(0x42459162)); // 28.312f, 49.457f, 28.334f, 49.403f, 28.334f, 49.392f +path.cubicTo(SkBits2Float(0x41e372b4), SkBits2Float(0x4244e141), SkBits2Float(0x41e4666a), SkBits2Float(0x42445c23), SkBits2Float(0x41e4eb89), SkBits2Float(0x42437ef3)); // 28.431f, 49.22f, 28.55f, 49.09f, 28.615f, 48.874f +path.cubicTo(SkBits2Float(0x41e4a7f3), SkBits2Float(0x424373af), SkBits2Float(0x41e47ae5), SkBits2Float(0x42435e2e), SkBits2Float(0x41e4666a), SkBits2Float(0x42435e2e)); // 28.582f, 48.863f, 28.56f, 48.842f, 28.55f, 48.842f +path.cubicTo(SkBits2Float(0x41e3893b), SkBits2Float(0x42433c63), SkBits2Float(0x41e1fbeb), SkBits2Float(0x4243686b), SkBits2Float(0x41e18b47), SkBits2Float(0x42431b9f)); // 28.442f, 48.809f, 28.248f, 48.852f, 28.193f, 48.777f +path.cubicTo(SkBits2Float(0x41e16045), SkBits2Float(0x4242f9d4), SkBits2Float(0x41e18b47), SkBits2Float(0x4242ee91), SkBits2Float(0x41e16045), SkBits2Float(0x4242d910)); // 28.172f, 48.744f, 28.193f, 48.733f, 28.172f, 48.712f +path.cubicTo(SkBits2Float(0x41e1a1ce), SkBits2Float(0x4242b84b), SkBits2Float(0x41e1fbeb), SkBits2Float(0x42429681), SkBits2Float(0x41e226ed), SkBits2Float(0x42429681)); // 28.204f, 48.68f, 28.248f, 48.647f, 28.269f, 48.647f +path.cubicTo(SkBits2Float(0x41e3cac4), SkBits2Float(0x42425f35), SkBits2Float(0x41e9c087), SkBits2Float(0x4242b84c), SkBits2Float(0x41ea5c2c), SkBits2Float(0x424248ae)); // 28.474f, 48.593f, 29.219f, 48.68f, 29.295f, 48.571f +path.cubicTo(SkBits2Float(0x41eacac4), SkBits2Float(0x4241fbe1), SkBits2Float(0x41eacac4), SkBits2Float(0x42414aba), SkBits2Float(0x41eaf7d2), SkBits2Float(0x4240d0df)); // 29.349f, 48.496f, 29.349f, 48.323f, 29.371f, 48.204f +path.cubicTo(SkBits2Float(0x41eb395b), SkBits2Float(0x4240580a), SkBits2Float(0x41eba7f3), SkBits2Float(0x423fb121), SkBits2Float(0x41ebd501), SkBits2Float(0x423f21c4)); // 29.403f, 48.086f, 29.457f, 47.923f, 29.479f, 47.783f +path.cubicTo(SkBits2Float(0x41ec2d11), SkBits2Float(0x423e4fd8), SkBits2Float(0x41ec5813), SkBits2Float(0x423d936e), SkBits2Float(0x41ecb230), SkBits2Float(0x423cb63f)); // 29.522f, 47.578f, 29.543f, 47.394f, 29.587f, 47.178f +path.cubicTo(SkBits2Float(0x41ecc8b7), SkBits2Float(0x423c5e2f), SkBits2Float(0x41edba61), SkBits2Float(0x423b332d), SkBits2Float(0x41ed8f5f), SkBits2Float(0x423ac495)); // 29.598f, 47.092f, 29.716f, 46.8f, 29.695f, 46.692f +path.cubicTo(SkBits2Float(0x41ed6251), SkBits2Float(0x423a8d49), SkBits2Float(0x41ec9ba9), SkBits2Float(0x423a407c), SkBits2Float(0x41ec2d11), SkBits2Float(0x423a3539)); // 29.673f, 46.638f, 29.576f, 46.563f, 29.522f, 46.552f +path.cubicTo(SkBits2Float(0x41ec0003), SkBits2Float(0x423a29f5), SkBits2Float(0x41ebeb88), SkBits2Float(0x423a3539), SkBits2Float(0x41ebd501), SkBits2Float(0x423a3539)); // 29.5f, 46.541f, 29.49f, 46.552f, 29.479f, 46.552f +path.cubicTo(SkBits2Float(0x41eb6669), SkBits2Float(0x423a29f5), SkBits2Float(0x41ea72b3), SkBits2Float(0x4239f2aa), SkBits2Float(0x41e9c086), SkBits2Float(0x423a0931)); // 29.425f, 46.541f, 29.306f, 46.487f, 29.219f, 46.509f +path.cubicTo(SkBits2Float(0x41e99584), SkBits2Float(0x423a0931), SkBits2Float(0x41e96876), SkBits2Float(0x423a29f6), SkBits2Float(0x41e953fb), SkBits2Float(0x423a3539)); // 29.198f, 46.509f, 29.176f, 46.541f, 29.166f, 46.552f +path.cubicTo(SkBits2Float(0x41e96876), SkBits2Float(0x423a8d49), SkBits2Float(0x41e9c086), SkBits2Float(0x423acfd9), SkBits2Float(0x41e9d70d), SkBits2Float(0x423b28ef)); // 29.176f, 46.638f, 29.219f, 46.703f, 29.23f, 46.79f +path.cubicTo(SkBits2Float(0x41ea041b), SkBits2Float(0x423bd910), SkBits2Float(0x41e8fbea), SkBits2Float(0x423c73b0), SkBits2Float(0x41e849bd), SkBits2Float(0x423cac01)); // 29.252f, 46.962f, 29.123f, 47.113f, 29.036f, 47.168f +path.cubicTo(SkBits2Float(0x41e75607), SkBits2Float(0x423cf8ce), SkBits2Float(0x41e5f3b9), SkBits2Float(0x423ced8a), SkBits2Float(0x41e4eb88), SkBits2Float(0x423cd809)); // 28.917f, 47.243f, 28.744f, 47.232f, 28.615f, 47.211f +path.cubicTo(SkBits2Float(0x41e372b3), SkBits2Float(0x423cb63e), SkBits2Float(0x41e2ac0b), SkBits2Float(0x423c0517), SkBits2Float(0x41e10834), SkBits2Float(0x423c52ea)); // 28.431f, 47.178f, 28.334f, 47.005f, 28.129f, 47.081f +path.cubicTo(SkBits2Float(0x41e0db26), SkBits2Float(0x423cd809), SkBits2Float(0x41e0999c), SkBits2Float(0x423d46a0), SkBits2Float(0x41dfd0e8), SkBits2Float(0x423d72a8)); // 28.107f, 47.211f, 28.075f, 47.319f, 27.977f, 47.362f +path.cubicTo(SkBits2Float(0x41deb230), SkBits2Float(0x423dcab8), SkBits2Float(0x41dd3b67), SkBits2Float(0x423d8829), SkBits2Float(0x41dc312a), SkBits2Float(0x423d46a0)); // 27.837f, 47.448f, 27.654f, 47.383f, 27.524f, 47.319f +path.cubicTo(SkBits2Float(0x41dae563), SkBits2Float(0x423cf8cd), SkBits2Float(0x41d98316), SkBits2Float(0x423cccc5), SkBits2Float(0x41d8645d), SkBits2Float(0x423c6971)); // 27.362f, 47.243f, 27.189f, 47.2f, 27.049f, 47.103f +path.cubicTo(SkBits2Float(0x41d7df3e), SkBits2Float(0x423c52ea), SkBits2Float(0x41d72d11), SkBits2Float(0x423c311f), SkBits2Float(0x41d6a9ff), SkBits2Float(0x423c0517)); // 26.984f, 47.081f, 26.897f, 47.048f, 26.833f, 47.005f +path.cubicTo(SkBits2Float(0x41d67cf1), SkBits2Float(0x423bfada), SkBits2Float(0x41d572b3), SkBits2Float(0x423b967f), SkBits2Float(0x41d5893a), SkBits2Float(0x423b967f)); // 26.811f, 46.995f, 26.681f, 46.897f, 26.692f, 46.897f +path.cubicTo(SkBits2Float(0x41d5893a), SkBits2Float(0x423b967f), SkBits2Float(0x41d5b648), SkBits2Float(0x423b6a77), SkBits2Float(0x41d5ccd0), SkBits2Float(0x423b6a77)); // 26.692f, 46.897f, 26.714f, 46.854f, 26.725f, 46.854f +path.cubicTo(SkBits2Float(0x41d6eb88), SkBits2Float(0x423b3e6f), SkBits2Float(0x41d8374f), SkBits2Float(0x423b967f), SkBits2Float(0x41d8fdf7), SkBits2Float(0x423bad06)); // 26.865f, 46.811f, 27.027f, 46.897f, 27.124f, 46.919f +path.cubicTo(SkBits2Float(0x41d9c6ab), SkBits2Float(0x423bb84a), SkBits2Float(0x41da49be), SkBits2Float(0x423bb84a), SkBits2Float(0x41db1066), SkBits2Float(0x423bd90e)); // 27.222f, 46.93f, 27.286f, 46.93f, 27.383f, 46.962f +path.cubicTo(SkBits2Float(0x41db810a), SkBits2Float(0x423bd90e), SkBits2Float(0x41dc5e39), SkBits2Float(0x423bfad9), SkBits2Float(0x41dcf7d3), SkBits2Float(0x423bef95)); // 27.438f, 46.962f, 27.546f, 46.995f, 27.621f, 46.984f +path.cubicTo(SkBits2Float(0x41ddd502), SkBits2Float(0x423bc38d), SkBits2Float(0x41dd4fe3), SkBits2Float(0x423b332b), SkBits2Float(0x41dd7cf2), SkBits2Float(0x423ab94f)); // 27.729f, 46.941f, 27.664f, 46.8f, 27.686f, 46.681f +path.cubicTo(SkBits2Float(0x41dda7f4), SkBits2Float(0x423a77c6), SkBits2Float(0x41de2d13), SkBits2Float(0x423a29f3), SkBits2Float(0x41de70a8), SkBits2Float(0x423a136c)); // 27.707f, 46.617f, 27.772f, 46.541f, 27.805f, 46.519f +path.cubicTo(SkBits2Float(0x41dfba62), SkBits2Float(0x4239c69f), SkBits2Float(0x41e253fc), SkBits2Float(0x423a092f), SkBits2Float(0x41e372b4), SkBits2Float(0x423a4bbe)); // 27.966f, 46.444f, 28.291f, 46.509f, 28.431f, 46.574f +path.cubicTo(SkBits2Float(0x41e40e5a), SkBits2Float(0x423a6c83), SkBits2Float(0x41e49379), SkBits2Float(0x423a8d47), SkBits2Float(0x41e55a21), SkBits2Float(0x423ab94f)); // 28.507f, 46.606f, 28.572f, 46.638f, 28.669f, 46.681f +path.lineTo(SkBits2Float(0x41e58523), SkBits2Float(0x423acfd6)); // 28.69f, 46.703f +path.cubicTo(SkBits2Float(0x41e5b231), SkBits2Float(0x423acfd6), SkBits2Float(0x41e60a42), SkBits2Float(0x423ac492), SkBits2Float(0x41e66252), SkBits2Float(0x423acfd6)); // 28.712f, 46.703f, 28.755f, 46.692f, 28.798f, 46.703f +path.cubicTo(SkBits2Float(0x41e66252), SkBits2Float(0x423ab94f), SkBits2Float(0x41e68f60), SkBits2Float(0x423ab94f), SkBits2Float(0x41e6a5e8), SkBits2Float(0x423aae0b)); // 28.798f, 46.681f, 28.82f, 46.681f, 28.831f, 46.67f +path.cubicTo(SkBits2Float(0x41e6fdf8), SkBits2Float(0x423a136b), SkBits2Float(0x41e5dd34), SkBits2Float(0x423978cc), SkBits2Float(0x41e68f61), SkBits2Float(0x4238fef0)); // 28.874f, 46.519f, 28.733f, 46.368f, 28.82f, 46.249f +path.cubicTo(SkBits2Float(0x41e72b07), SkBits2Float(0x42389058), SkBits2Float(0x41eaf7d4), SkBits2Float(0x42391577), SkBits2Float(0x41ec5815), SkBits2Float(0x4238f3ac)); // 28.896f, 46.141f, 29.371f, 46.271f, 29.543f, 46.238f +path.cubicTo(SkBits2Float(0x41ef1cb1), SkBits2Float(0x4238bd66), SkBits2Float(0x41ed6252), SkBits2Float(0x4237d4f4), SkBits2Float(0x41ede771), SkBits2Float(0x42369eae)); // 29.889f, 46.185f, 29.673f, 45.958f, 29.738f, 45.655f +path.cubicTo(SkBits2Float(0x41ee28fa), SkBits2Float(0x423651e1), SkBits2Float(0x41ee8317), SkBits2Float(0x42366868), SkBits2Float(0x41eedb27), SkBits2Float(0x42365c1f)); // 29.77f, 45.58f, 29.814f, 45.602f, 29.857f, 45.59f +path.cubicTo(SkBits2Float(0x41ef0629), SkBits2Float(0x4236a9f2), SkBits2Float(0x41ef3337), SkBits2Float(0x42371889), SkBits2Float(0x41ef3337), SkBits2Float(0x42375b19)); // 29.878f, 45.666f, 29.9f, 45.774f, 29.9f, 45.839f +path.cubicTo(SkBits2Float(0x41ef49be), SkBits2Float(0x4237e038), SkBits2Float(0x41ef3337), SkBits2Float(0x42386450), SkBits2Float(0x41ef49be), SkBits2Float(0x4238d2e8)); // 29.911f, 45.969f, 29.9f, 46.098f, 29.911f, 46.206f +path.cubicTo(SkBits2Float(0x41ef8b47), SkBits2Float(0x42394cc3), SkBits2Float(0x41eff9df), SkBits2Float(0x4239e763), SkBits2Float(0x41f026ed), SkBits2Float(0x423a613e)); // 29.943f, 46.325f, 29.997f, 46.476f, 30.019f, 46.595f +path.cubicTo(SkBits2Float(0x41f0ac0c), SkBits2Float(0x423b967d), SkBits2Float(0x41f11897), SkBits2Float(0x423ca0bb), SkBits2Float(0x41f1893b), SkBits2Float(0x423dd5fa)); // 30.084f, 46.897f, 30.137f, 47.157f, 30.192f, 47.459f +path.cubicTo(SkBits2Float(0x41f19db6), SkBits2Float(0x423e1889), SkBits2Float(0x41f1f5c6), SkBits2Float(0x423e7bdd), SkBits2Float(0x41f20e5a), SkBits2Float(0x423ebe6d)); // 30.202f, 47.524f, 30.245f, 47.621f, 30.257f, 47.686f +path.cubicTo(SkBits2Float(0x41f27ae5), SkBits2Float(0x423f9059), SkBits2Float(0x41f2be7b), SkBits2Float(0x42406d88), SkBits2Float(0x41f3168b), SkBits2Float(0x424128ec)); // 30.31f, 47.891f, 30.343f, 48.107f, 30.386f, 48.29f +path.cubicTo(SkBits2Float(0x41f35814), SkBits2Float(0x42418203), SkBits2Float(0x41f35814), SkBits2Float(0x4241e556), SkBits2Float(0x41f38523), SkBits2Float(0x42423329)); // 30.418f, 48.377f, 30.418f, 48.474f, 30.44f, 48.55f +path.cubicTo(SkBits2Float(0x41f3b025), SkBits2Float(0x424248aa), SkBits2Float(0x41f420c9), SkBits2Float(0x424275b8), SkBits2Float(0x41f46252), SkBits2Float(0x424280fc)); // 30.461f, 48.571f, 30.516f, 48.615f, 30.548f, 48.626f +path.cubicTo(SkBits2Float(0x41f4fdf8), SkBits2Float(0x4242967d), SkBits2Float(0x41f5db27), SkBits2Float(0x424275b8), SkBits2Float(0x41f674c1), SkBits2Float(0x424280fc)); // 30.624f, 48.647f, 30.732f, 48.615f, 30.807f, 48.626f +path.cubicTo(SkBits2Float(0x41f8f5c7), SkBits2Float(0x4242967d), SkBits2Float(0x41fc5609), SkBits2Float(0x424280fc), SkBits2Float(0x41feeb8a), SkBits2Float(0x4242a1c1)); // 31.12f, 48.647f, 31.542f, 48.626f, 31.865f, 48.658f +path.cubicTo(SkBits2Float(0x41ff45a7), SkBits2Float(0x4242a1c1), SkBits2Float(0x41ffdf40), SkBits2Float(0x424280fc), SkBits2Float(0x4200322f), SkBits2Float(0x4242a1c1)); // 31.909f, 48.658f, 31.984f, 48.626f, 32.049f, 48.658f +path.cubicTo(SkBits2Float(0x420048b6), SkBits2Float(0x4242a1c1), SkBits2Float(0x42005e37), SkBits2Float(0x4242c286), SkBits2Float(0x420074be), SkBits2Float(0x4242d90d)); // 32.071f, 48.658f, 32.092f, 48.69f, 32.114f, 48.712f +path.cubicTo(SkBits2Float(0x420074be), SkBits2Float(0x4242ee8e), SkBits2Float(0x42008002), SkBits2Float(0x42431b9c), SkBits2Float(0x420074be), SkBits2Float(0x4243311d)); // 32.114f, 48.733f, 32.125f, 48.777f, 32.114f, 48.798f +path.lineTo(SkBits2Float(0x420052f3), SkBits2Float(0x42433c61)); // 32.081f, 48.809f +path.cubicTo(SkBits2Float(0x42001cad), SkBits2Float(0x42439fb5), SkBits2Float(0x41ff2f1d), SkBits2Float(0x42436869), SkBits2Float(0x41fe7cf0), SkBits2Float(0x4243aaf9)); // 32.028f, 48.906f, 31.898f, 48.852f, 31.811f, 48.917f +path.cubicTo(SkBits2Float(0x41fe24e0), SkBits2Float(0x4243cbbe), SkBits2Float(0x41fd3336), SkBits2Float(0x4244cab7), SkBits2Float(0x41fd0627), SkBits2Float(0x42450203)); // 31.768f, 48.949f, 31.65f, 49.198f, 31.628f, 49.252f +path.cubicTo(SkBits2Float(0x41fcc291), SkBits2Float(0x4245438c), SkBits2Float(0x41fcc291), SkBits2Float(0x42457bde), SkBits2Float(0x41fcae17), SkBits2Float(0x4245be6d)); // 31.595f, 49.316f, 31.595f, 49.371f, 31.585f, 49.436f +path.cubicTo(SkBits2Float(0x41fc9790), SkBits2Float(0x4245fff6), SkBits2Float(0x41fc28f8), SkBits2Float(0x4246634a), SkBits2Float(0x41fc1271), SkBits2Float(0x4246b11d)); // 31.574f, 49.5f, 31.52f, 49.597f, 31.509f, 49.673f +path.cubicTo(SkBits2Float(0x41fbba61), SkBits2Float(0x42478e4c), SkBits2Float(0x41fba3d9), SkBits2Float(0x424880fc), SkBits2Float(0x41fbba61), SkBits2Float(0x424974b2)); // 31.466f, 49.889f, 31.455f, 50.126f, 31.466f, 50.364f +path.cubicTo(SkBits2Float(0x41fbd0e8), SkBits2Float(0x424a7de9), SkBits2Float(0x41fc8109), SkBits2Float(0x424b5b18), SkBits2Float(0x41fd47b1), SkBits2Float(0x424c4ecf)); // 31.477f, 50.623f, 31.563f, 50.839f, 31.66f, 51.077f +path.cubicTo(SkBits2Float(0x41fd8b47), SkBits2Float(0x424c915e), SkBits2Float(0x41fdccd0), SkBits2Float(0x424cde2b), SkBits2Float(0x41fe3b67), SkBits2Float(0x424d167d)); // 31.693f, 51.142f, 31.725f, 51.217f, 31.779f, 51.272f +path.cubicTo(SkBits2Float(0x41fe9377), SkBits2Float(0x424d4dc9), SkBits2Float(0x41fec086), SkBits2Float(0x424d8f52), SkBits2Float(0x41ff2f1d), SkBits2Float(0x424dc69e)); // 31.822f, 51.326f, 31.844f, 51.39f, 31.898f, 51.444f +path.cubicTo(SkBits2Float(0x41ff70a6), SkBits2Float(0x424df3ac), SkBits2Float(0x41ffdf3e), SkBits2Float(0x424e092d), SkBits2Float(0x42000626), SkBits2Float(0x424e3536)); // 31.93f, 51.488f, 31.984f, 51.509f, 32.006f, 51.552f +path.cubicTo(SkBits2Float(0x42003d72), SkBits2Float(0x424e6c82), SkBits2Float(0x4200c18a), SkBits2Float(0x424f3e6d), SkBits2Float(0x4201041a), SkBits2Float(0x424f49b1)); // 32.06f, 51.606f, 32.189f, 51.811f, 32.254f, 51.822f +path.cubicTo(SkBits2Float(0x420172b2), SkBits2Float(0x424f6b7c), SkBits2Float(0x4201ec8d), SkBits2Float(0x424e8309), SkBits2Float(0x42020d51), SkBits2Float(0x424e4bbd)); // 32.362f, 51.855f, 32.481f, 51.628f, 32.513f, 51.574f +path.cubicTo(SkBits2Float(0x4202be78), SkBits2Float(0x424d5807), SkBits2Float(0x42037ae2), SkBits2Float(0x424c6557), SkBits2Float(0x42044cce), SkBits2Float(0x424b9265)); // 32.686f, 51.336f, 32.87f, 51.099f, 33.075f, 50.893f +path.cubicTo(SkBits2Float(0x42049aa1), SkBits2Float(0x424b4598), SkBits2Float(0x4204e874), SkBits2Float(0x424ae13e), SkBits2Float(0x42054084), SkBits2Float(0x424a9471)); // 33.151f, 50.818f, 33.227f, 50.72f, 33.313f, 50.645f +path.cubicTo(SkBits2Float(0x42058d51), SkBits2Float(0x424a51e2), SkBits2Float(0x4206ef9f), SkBits2Float(0x4248fad7), SkBits2Float(0x42071063), SkBits2Float(0x4248cecf)); // 33.388f, 50.58f, 33.734f, 50.245f, 33.766f, 50.202f +path.cubicTo(SkBits2Float(0x42075e36), SkBits2Float(0x424876bf), SkBits2Float(0x4207cccd), SkBits2Float(0x4248342f), SkBits2Float(0x42083021), SkBits2Float(0x4247e65c)); // 33.842f, 50.116f, 33.95f, 50.051f, 34.047f, 49.975f +path.cubicTo(SkBits2Float(0x42088831), SkBits2Float(0x42478308), SkBits2Float(0x4208f6c9), SkBits2Float(0x4247363b), SkBits2Float(0x420970a4), SkBits2Float(0x4246f3ac)); // 34.133f, 49.878f, 34.241f, 49.803f, 34.36f, 49.738f +path.cubicTo(SkBits2Float(0x4209f5c3), SkBits2Float(0x42469a95), SkBits2Float(0x420a645a), SkBits2Float(0x42464285), SkBits2Float(0x420add2f), SkBits2Float(0x4245f4b2)); // 34.49f, 49.651f, 34.598f, 49.565f, 34.716f, 49.489f +path.cubicTo(SkBits2Float(0x420b2b02), SkBits2Float(0x4245be6c), SkBits2Float(0x420bc5a2), SkBits2Float(0x42455a12), SkBits2Float(0x420b8418), SkBits2Float(0x4244eb7b)); // 34.792f, 49.436f, 34.943f, 49.338f, 34.879f, 49.23f +path.cubicTo(SkBits2Float(0x420b624d), SkBits2Float(0x4244cab6), SkBits2Float(0x420b1fbe), SkBits2Float(0x42449eae), SkBits2Float(0x420b0a3d), SkBits2Float(0x42448827)); // 34.846f, 49.198f, 34.781f, 49.155f, 34.76f, 49.133f +path.cubicTo(SkBits2Float(0x420abd70), SkBits2Float(0x424450db), SkBits2Float(0x420a9ba5), SkBits2Float(0x42440e4c), SkBits2Float(0x420a5916), SkBits2Float(0x4243d700)); // 34.685f, 49.079f, 34.652f, 49.014f, 34.587f, 48.96f +path.cubicTo(SkBits2Float(0x420a3851), SkBits2Float(0x4243b63b), SkBits2Float(0x420a21ca), SkBits2Float(0x4243b63b), SkBits2Float(0x4209f5c2), SkBits2Float(0x42439fb4)); // 34.555f, 48.928f, 34.533f, 48.928f, 34.49f, 48.906f +path.cubicTo(SkBits2Float(0x4209ea7e), SkBits2Float(0x42439470), SkBits2Float(0x4209ea7e), SkBits2Float(0x424373ac), SkBits2Float(0x4209d3f7), SkBits2Float(0x42436868)); // 34.479f, 48.895f, 34.479f, 48.863f, 34.457f, 48.852f +path.cubicTo(SkBits2Float(0x4209b332), SkBits2Float(0x424352e7), SkBits2Float(0x42099db1), SkBits2Float(0x42435e2b), SkBits2Float(0x42097be7), SkBits2Float(0x424352e7)); // 34.425f, 48.831f, 34.404f, 48.842f, 34.371f, 48.831f +path.cubicTo(SkBits2Float(0x420970a3), SkBits2Float(0x42433c60), SkBits2Float(0x42096560), SkBits2Float(0x42431b9b), SkBits2Float(0x4209449b), SkBits2Float(0x42431b9b)); // 34.36f, 48.809f, 34.349f, 48.777f, 34.317f, 48.777f +path.cubicTo(SkBits2Float(0x4208f6c8), SkBits2Float(0x4242e349), SkBits2Float(0x42089eb8), SkBits2Float(0x4242c284), SkBits2Float(0x42083020), SkBits2Float(0x4242a1c0)); // 34.241f, 48.722f, 34.155f, 48.69f, 34.047f, 48.658f +path.cubicTo(SkBits2Float(0x42080f5b), SkBits2Float(0x4242967c), SkBits2Float(0x4207d810), SkBits2Float(0x42425f31), SkBits2Float(0x4207c188), SkBits2Float(0x42425f31)); // 34.015f, 48.647f, 33.961f, 48.593f, 33.939f, 48.593f +path.cubicTo(SkBits2Float(0x420748b3), SkBits2Float(0x424227e5), SkBits2Float(0x42066040), SkBits2Float(0x4241fbdd), SkBits2Float(0x4205b957), SkBits2Float(0x42421ca2)); // 33.821f, 48.539f, 33.594f, 48.496f, 33.431f, 48.528f +path.cubicTo(SkBits2Float(0x4204c6a7), SkBits2Float(0x42423329), SkBits2Float(0x42041580), SkBits2Float(0x4242ad04), SkBits2Float(0x42032d0d), SkBits2Float(0x4242c285)); // 33.194f, 48.55f, 33.021f, 48.669f, 32.794f, 48.69f +path.cubicTo(SkBits2Float(0x42032d0d), SkBits2Float(0x4242b848), SkBits2Float(0x420322d0), SkBits2Float(0x4242a1c0), SkBits2Float(0x42032d0d), SkBits2Float(0x4242a1c0)); // 32.794f, 48.68f, 32.784f, 48.658f, 32.794f, 48.658f +path.cubicTo(SkBits2Float(0x42032d0d), SkBits2Float(0x424280fb), SkBits2Float(0x42036459), SkBits2Float(0x424275b8), SkBits2Float(0x42036f9c), SkBits2Float(0x42426a74)); // 32.794f, 48.626f, 32.848f, 48.615f, 32.859f, 48.604f +path.cubicTo(SkBits2Float(0x4203e977), SkBits2Float(0x4241cfd4), SkBits2Float(0x4204580f), SkBits2Float(0x42418201), SkBits2Float(0x420529fa), SkBits2Float(0x42413f72)); // 32.978f, 48.453f, 33.086f, 48.377f, 33.291f, 48.312f +path.lineTo(SkBits2Float(0x42054abf), SkBits2Float(0x424128eb)); // 33.323f, 48.29f +path.cubicTo(SkBits2Float(0x4205cfde), SkBits2Float(0x4240fde9), SkBits2Float(0x420649b9), SkBits2Float(0x4240fde9), SkBits2Float(0x4206b850), SkBits2Float(0x4240b016)); // 33.453f, 48.248f, 33.572f, 48.248f, 33.68f, 48.172f +path.cubicTo(SkBits2Float(0x4206a1c9), SkBits2Float(0x4240998f), SkBits2Float(0x4206b850), SkBits2Float(0x42408e4b), SkBits2Float(0x4206a1c9), SkBits2Float(0x424078ca)); // 33.658f, 48.15f, 33.68f, 48.139f, 33.658f, 48.118f +path.cubicTo(SkBits2Float(0x42068104), SkBits2Float(0x4240363b), SkBits2Float(0x42054081), SkBits2Float(0x423fb11c), SkBits2Float(0x4204d1ea), SkBits2Float(0x423f9057)); // 33.626f, 48.053f, 33.313f, 47.923f, 33.205f, 47.891f +path.cubicTo(SkBits2Float(0x42044ccb), SkBits2Float(0x423f79d0), SkBits2Float(0x42035915), SkBits2Float(0x423f644f), SkBits2Float(0x4202be75), SkBits2Float(0x423f8513)); // 33.075f, 47.869f, 32.837f, 47.848f, 32.686f, 47.88f +path.cubicTo(SkBits2Float(0x42022f19), SkBits2Float(0x423f9b9a), SkBits2Float(0x4201c081), SkBits2Float(0x423fde2a), SkBits2Float(0x420125e2), SkBits2Float(0x423ff3ab)); // 32.546f, 47.902f, 32.438f, 47.967f, 32.287f, 47.988f +path.lineTo(SkBits2Float(0x42010f5b), SkBits2Float(0x423fc7a3)); // 32.265f, 47.945f +path.cubicTo(SkBits2Float(0x4201a9fb), SkBits2Float(0x423f167c), SkBits2Float(0x42036459), SkBits2Float(0x423d5c1e), SkBits2Float(0x4204580f), SkBits2Float(0x423d198f)); // 32.416f, 47.772f, 32.848f, 47.34f, 33.086f, 47.275f +path.cubicTo(SkBits2Float(0x4205b957), SkBits2Float(0x423cabfe), SkBits2Float(0x4207c188), SkBits2Float(0x423cd806), SkBits2Float(0x42090d4e), SkBits2Float(0x423d24d3)); // 33.431f, 47.168f, 33.939f, 47.211f, 34.263f, 47.286f +path.cubicTo(SkBits2Float(0x420ae871), SkBits2Float(0x423d936b), SkBits2Float(0x420c9892), SkBits2Float(0x423e7bdd), SkBits2Float(0x420e6871), SkBits2Float(0x423ed3ee)); // 34.727f, 47.394f, 35.149f, 47.621f, 35.602f, 47.707f +path.cubicTo(SkBits2Float(0x42103956), SkBits2Float(0x423f438c), SkBits2Float(0x42121479), SkBits2Float(0x423f0b3a), SkBits2Float(0x4213c49a), SkBits2Float(0x423e2e0b)); // 36.056f, 47.816f, 36.52f, 47.761f, 36.942f, 47.545f +path.cubicTo(SkBits2Float(0x4214cdd1), SkBits2Float(0x423db536), SkBits2Float(0x4215c081), SkBits2Float(0x423d24d4), SkBits2Float(0x42169db1), SkBits2Float(0x423c696f)); // 37.201f, 47.427f, 37.438f, 47.286f, 37.654f, 47.103f +path.cubicTo(SkBits2Float(0x4216eb84), SkBits2Float(0x423c26e0), SkBits2Float(0x4217df3a), SkBits2Float(0x423afbde), SkBits2Float(0x4218580f), SkBits2Float(0x423b75b9)); // 37.73f, 47.038f, 37.968f, 46.746f, 38.086f, 46.865f +path.cubicTo(SkBits2Float(0x42189a9e), SkBits2Float(0x423bad05), SkBits2Float(0x421820c3), SkBits2Float(0x423c1b9c), SkBits2Float(0x4217ffff), SkBits2Float(0x423c311d)); // 38.151f, 46.919f, 38.032f, 47.027f, 38, 47.048f +path.cubicTo(SkBits2Float(0x4217a6e8), SkBits2Float(0x423c9577), SkBits2Float(0x42173851), SkBits2Float(0x423ced87), SkBits2Float(0x4216cac0), SkBits2Float(0x423d5c1f)); // 37.913f, 47.146f, 37.805f, 47.232f, 37.698f, 47.34f +path.cubicTo(SkBits2Float(0x42168831), SkBits2Float(0x423d9eae), SkBits2Float(0x421650e5), SkBits2Float(0x423deb7b), SkBits2Float(0x4215f7ce), SkBits2Float(0x423e23cd)); // 37.633f, 47.405f, 37.579f, 47.48f, 37.492f, 47.535f +path.lineTo(SkBits2Float(0x4215f7ce), SkBits2Float(0x423e4492)); // 37.492f, 47.567f +path.cubicTo(SkBits2Float(0x4215ed91), SkBits2Float(0x423e4fd6), SkBits2Float(0x4215d709), SkBits2Float(0x423e4492), SkBits2Float(0x4215cbc6), SkBits2Float(0x423e4fd6)); // 37.482f, 47.578f, 37.46f, 47.567f, 37.449f, 47.578f +path.cubicTo(SkBits2Float(0x42158937), SkBits2Float(0x423e8722), SkBits2Float(0x42153126), SkBits2Float(0x423f00fd), SkBits2Float(0x4214ee97), SkBits2Float(0x423f3849)); // 37.384f, 47.632f, 37.298f, 47.751f, 37.233f, 47.805f +path.cubicTo(SkBits2Float(0x4214d810), SkBits2Float(0x423f438d), SkBits2Float(0x4214cdd2), SkBits2Float(0x423f590e), SkBits2Float(0x4214cdd2), SkBits2Float(0x423f590e)); // 37.211f, 47.816f, 37.201f, 47.837f, 37.201f, 47.837f +path.lineTo(SkBits2Float(0x4214b74b), SkBits2Float(0x423f590e)); // 37.179f, 47.837f +path.lineTo(SkBits2Float(0x4214b74b), SkBits2Float(0x423f79d3)); // 37.179f, 47.869f +path.cubicTo(SkBits2Float(0x42147fff), SkBits2Float(0x423f905a), SkBits2Float(0x421474bc), SkBits2Float(0x423fb11f), SkBits2Float(0x421448b3), SkBits2Float(0x423fc7a6)); // 37.125f, 47.891f, 37.114f, 47.923f, 37.071f, 47.945f +path.lineTo(SkBits2Float(0x421448b3), SkBits2Float(0x423fdd27)); // 37.071f, 47.966f +path.lineTo(SkBits2Float(0x42143332), SkBits2Float(0x423fdd27)); // 37.05f, 47.966f +path.lineTo(SkBits2Float(0x4213b957), SkBits2Float(0x424077c7)); // 36.931f, 48.117f +path.cubicTo(SkBits2Float(0x4213a2d0), SkBits2Float(0x4240830b), SkBits2Float(0x4213b957), SkBits2Float(0x4240988c), SkBits2Float(0x4213b957), SkBits2Float(0x4240988c)); // 36.909f, 48.128f, 36.931f, 48.149f, 36.931f, 48.149f +path.cubicTo(SkBits2Float(0x4213c49b), SkBits2Float(0x4240988c), SkBits2Float(0x4213b957), SkBits2Float(0x4240ba57), SkBits2Float(0x4213da1c), SkBits2Float(0x4240af13)); // 36.942f, 48.149f, 36.931f, 48.182f, 36.963f, 48.171f +path.cubicTo(SkBits2Float(0x42141cab), SkBits2Float(0x4240af13), SkBits2Float(0x4214a1ca), SkBits2Float(0x42405703), SkBits2Float(0x4214ee97), SkBits2Float(0x42403538)); // 37.028f, 48.171f, 37.158f, 48.085f, 37.233f, 48.052f +path.cubicTo(SkBits2Float(0x42153126), SkBits2Float(0x42401473), SkBits2Float(0x42157ef9), SkBits2Float(0x423ffdec), SkBits2Float(0x4215cbc6), SkBits2Float(0x423fd1e4)); // 37.298f, 48.02f, 37.374f, 47.998f, 37.449f, 47.955f +path.cubicTo(SkBits2Float(0x421650e5), SkBits2Float(0x423f8f55), SkBits2Float(0x4216cac0), SkBits2Float(0x423f4288), SkBits2Float(0x42178624), SkBits2Float(0x423f20bd)); // 37.579f, 47.89f, 37.698f, 47.815f, 37.881f, 47.782f +path.cubicTo(SkBits2Float(0x42177ae0), SkBits2Float(0x423f8f55), SkBits2Float(0x421770a3), SkBits2Float(0x423fc6a0), SkBits2Float(0x42174395), SkBits2Float(0x423ffdec)); // 37.87f, 47.89f, 37.86f, 47.944f, 37.816f, 47.998f +path.cubicTo(SkBits2Float(0x4216bf7c), SkBits2Float(0x4240ba56), SkBits2Float(0x4215ab02), SkBits2Float(0x4241332b), SkBits2Float(0x4214f9db), SkBits2Float(0x4241c38e)); // 37.687f, 48.182f, 37.417f, 48.3f, 37.244f, 48.441f +path.cubicTo(SkBits2Float(0x42143333), SkBits2Float(0x424274b5), SkBits2Float(0x42136b85), SkBits2Float(0x42433019), SkBits2Float(0x4212c5a2), SkBits2Float(0x4243f7c7)); // 37.05f, 48.614f, 36.855f, 48.797f, 36.693f, 48.992f +path.cubicTo(SkBits2Float(0x42115917), SkBits2Float(0x42459b9e), SkBits2Float(0x421022d1), SkBits2Float(0x42476c83), SkBits2Float(0x420f0313), SkBits2Float(0x4249311f)); // 36.337f, 49.402f, 36.034f, 49.856f, 35.753f, 50.298f +path.cubicTo(SkBits2Float(0x420e1ba6), SkBits2Float(0x424a936d), SkBits2Float(0x420d75c3), SkBits2Float(0x424c21c3), SkBits2Float(0x420cdb23), SkBits2Float(0x424dba56)); // 35.527f, 50.644f, 35.365f, 51.033f, 35.214f, 51.432f +path.cubicTo(SkBits2Float(0x420c3f7d), SkBits2Float(0x424f6a77), SkBits2Float(0x420b8419), SkBits2Float(0x42510e4e), SkBits2Float(0x420b1fbf), SkBits2Float(0x4252d3f0)); // 35.062f, 51.854f, 34.879f, 52.264f, 34.781f, 52.707f +path.cubicTo(SkBits2Float(0x420ad2f2), SkBits2Float(0x42548e4e), SkBits2Float(0x420ab127), SkBits2Float(0x42565e2d), SkBits2Float(0x420a9063), SkBits2Float(0x4258188c)); // 34.706f, 53.139f, 34.673f, 53.592f, 34.641f, 54.024f +path.cubicTo(SkBits2Float(0x420a7ae2), SkBits2Float(0x4258882a), SkBits2Float(0x420a9ba7), SkBits2Float(0x4258e03a), SkBits2Float(0x420a9ba7), SkBits2Float(0x42594ed2)); // 34.62f, 54.133f, 34.652f, 54.219f, 34.652f, 54.327f +path.cubicTo(SkBits2Float(0x420aa6eb), SkBits2Float(0x425e301a), SkBits2Float(0x420c820d), SkBits2Float(0x4262c495), SkBits2Float(0x420ecbc8), SkBits2Float(0x4266fff9)); // 34.663f, 55.547f, 35.127f, 56.692f, 35.699f, 57.75f +path.cubicTo(SkBits2Float(0x420eed93), SkBits2Float(0x426721c4), SkBits2Float(0x420f0e57), SkBits2Float(0x42674dcc), SkBits2Float(0x420f3022), SkBits2Float(0x42676e91)); // 35.732f, 57.783f, 35.764f, 57.826f, 35.797f, 57.858f +path.cubicTo(SkBits2Float(0x420f7df5), SkBits2Float(0x42680a37), SkBits2Float(0x420fbf7e), SkBits2Float(0x42689993), SkBits2Float(0x42100d51), SkBits2Float(0x42693433)); // 35.873f, 58.01f, 35.937f, 58.15f, 36.013f, 58.301f +path.cubicTo(SkBits2Float(0x42102e16), SkBits2Float(0x426955fe), SkBits2Float(0x42105a1e), SkBits2Float(0x426976c2), SkBits2Float(0x42106561), SkBits2Float(0x42698d4a)); // 36.045f, 58.334f, 36.088f, 58.366f, 36.099f, 58.388f +path.cubicTo(SkBits2Float(0x4210872c), SkBits2Float(0x4269e55a), SkBits2Float(0x4210a7f0), SkBits2Float(0x426a3d6b), SkBits2Float(0x4210ea80), SkBits2Float(0x426a6a79)); // 36.132f, 58.474f, 36.164f, 58.56f, 36.229f, 58.604f +path.cubicTo(SkBits2Float(0x42119aa1), SkBits2Float(0x426acdcd), SkBits2Float(0x42131376), SkBits2Float(0x426a48ae), SkBits2Float(0x4213e561), SkBits2Float(0x426a6a79)); // 36.401f, 58.701f, 36.769f, 58.571f, 36.974f, 58.604f +path.cubicTo(SkBits2Float(0x4213fae2), SkBits2Float(0x426a75bd), SkBits2Float(0x42141cad), SkBits2Float(0x426a8b3e), SkBits2Float(0x42143d71), SkBits2Float(0x426a8b3e)); // 36.995f, 58.615f, 37.028f, 58.636f, 37.06f, 58.636f +path.cubicTo(SkBits2Float(0x42141cac), SkBits2Float(0x426acdcd), SkBits2Float(0x42143334), SkBits2Float(0x426aee92), SkBits2Float(0x42141cac), SkBits2Float(0x426b25de)); // 37.028f, 58.701f, 37.05f, 58.733f, 37.028f, 58.787f +path.cubicTo(SkBits2Float(0x4213e560), SkBits2Float(0x426b9fb9), SkBits2Float(0x4212dc29), SkBits2Float(0x426d0d4b), SkBits2Float(0x4212f1aa), SkBits2Float(0x426da7ea)); // 36.974f, 58.906f, 36.715f, 59.263f, 36.736f, 59.414f +path.cubicTo(SkBits2Float(0x4212f1aa), SkBits2Float(0x426dfffa), SkBits2Float(0x4213b958), SkBits2Float(0x426ed1e6), SkBits2Float(0x4213c49c), SkBits2Float(0x426edd29)); // 36.736f, 59.5f, 36.931f, 59.705f, 36.942f, 59.716f +path.cubicTo(SkBits2Float(0x4213e561), SkBits2Float(0x426f1fb8), SkBits2Float(0x42143d71), SkBits2Float(0x426f9993), SkBits2Float(0x421448b5), SkBits2Float(0x426ffce7)); // 36.974f, 59.781f, 37.06f, 59.9f, 37.071f, 59.997f +path.cubicTo(SkBits2Float(0x421448b5), SkBits2Float(0x427076c2), SkBits2Float(0x4214072c), SkBits2Float(0x4270ef97), SkBits2Float(0x4213fae2), SkBits2Float(0x427148ae)); // 37.071f, 60.116f, 37.007f, 60.234f, 36.995f, 60.321f +path.cubicTo(SkBits2Float(0x4213e561), SkBits2Float(0x42717ffa), SkBits2Float(0x4213fae2), SkBits2Float(0x42718b3d), SkBits2Float(0x4213e561), SkBits2Float(0x4271b746)); // 36.974f, 60.375f, 36.995f, 60.386f, 36.974f, 60.429f +path.cubicTo(SkBits2Float(0x4213da1d), SkBits2Float(0x4271ccc7), SkBits2Float(0x4213b959), SkBits2Float(0x42721a9a), SkBits2Float(0x4213a2d2), SkBits2Float(0x42721a9a)); // 36.963f, 60.45f, 36.931f, 60.526f, 36.909f, 60.526f +path.cubicTo(SkBits2Float(0x42134ac2), SkBits2Float(0x42723c65), SkBits2Float(0x4212d0e6), SkBits2Float(0x427225de), SkBits2Float(0x42126d93), SkBits2Float(0x427225de)); // 36.823f, 60.559f, 36.704f, 60.537f, 36.607f, 60.537f +path.cubicTo(SkBits2Float(0x42124bc8), SkBits2Float(0x427225de), SkBits2Float(0x4211bc6c), SkBits2Float(0x42723c65), SkBits2Float(0x42119064), SkBits2Float(0x42723c65)); // 36.574f, 60.537f, 36.434f, 60.559f, 36.391f, 60.559f +path.cubicTo(SkBits2Float(0x4210d3fa), SkBits2Float(0x427246a2), SkBits2Float(0x420ff6ca), SkBits2Float(0x4272301b), SkBits2Float(0x420f676e), SkBits2Float(0x4272686d)); // 36.207f, 60.569f, 35.991f, 60.547f, 35.851f, 60.602f +path.cubicTo(SkBits2Float(0x420eb647), SkBits2Float(0x4272b53a), SkBits2Float(0x420e52f3), SkBits2Float(0x42737ce8), SkBits2Float(0x420dc291), SkBits2Float(0x4273f5bd)); // 35.678f, 60.677f, 35.581f, 60.872f, 35.44f, 60.99f +path.cubicTo(SkBits2Float(0x420d116a), SkBits2Float(0x4274861f), SkBits2Float(0x420c5606), SkBits2Float(0x4274e973), SkBits2Float(0x420b999b), SkBits2Float(0x4275580b)); // 35.267f, 61.131f, 35.084f, 61.228f, 34.9f, 61.336f +path.cubicTo(SkBits2Float(0x420a9ba7), SkBits2Float(0x4275fdee), SkBits2Float(0x4209b335), SkBits2Float(0x42768d4a), SkBits2Float(0x42089eba), SkBits2Float(0x4276f1a5)); // 34.652f, 61.498f, 34.425f, 61.638f, 34.155f, 61.736f +path.cubicTo(SkBits2Float(0x4207ab04), SkBits2Float(0x42773e72), SkBits2Float(0x4206a1cc), SkBits2Float(0x42778101), SkBits2Float(0x4205b95a), SkBits2Float(0x4277c391)); // 33.917f, 61.811f, 33.658f, 61.876f, 33.431f, 61.941f +path.cubicTo(SkBits2Float(0x4203bd73), SkBits2Float(0x42786974), SkBits2Float(0x4201cbc9), SkBits2Float(0x42793b60), SkBits2Float(0x4200ac0a), SkBits2Float(0x427af5be)); // 32.935f, 62.103f, 32.449f, 62.308f, 32.168f, 62.74f +path.cubicTo(SkBits2Float(0x420074be), SkBits2Float(0x427b428b), SkBits2Float(0x41ffb43d), SkBits2Float(0x427c4cc8), SkBits2Float(0x41ff872f), SkBits2Float(0x427ca4d9)); // 32.114f, 62.815f, 31.963f, 63.075f, 31.941f, 63.161f +path.cubicTo(SkBits2Float(0x41ff872f), SkBits2Float(0x427cbb60), SkBits2Float(0x41ff9db6), SkBits2Float(0x427cd0e1), SkBits2Float(0x41ff872f), SkBits2Float(0x427ce768)); // 31.941f, 63.183f, 31.952f, 63.204f, 31.941f, 63.226f +path.cubicTo(SkBits2Float(0x41ffb43d), SkBits2Float(0x427cfce9), SkBits2Float(0x41ffb43d), SkBits2Float(0x427cfce9), SkBits2Float(0x41ffdf3f), SkBits2Float(0x427d1370)); // 31.963f, 63.247f, 31.963f, 63.247f, 31.984f, 63.269f +path.cubicTo(SkBits2Float(0x4200ac0a), SkBits2Float(0x427cfce9), SkBits2Float(0x42010f5e), SkBits2Float(0x427cd0e1), SkBits2Float(0x4201a9fe), SkBits2Float(0x427ca4d8)); // 32.168f, 63.247f, 32.265f, 63.204f, 32.416f, 63.161f +path.cubicTo(SkBits2Float(0x4201c085), SkBits2Float(0x427c9994), SkBits2Float(0x4201f7d1), SkBits2Float(0x427c78d0), SkBits2Float(0x42020315), SkBits2Float(0x427c78d0)); // 32.438f, 63.15f, 32.492f, 63.118f, 32.503f, 63.118f +path.cubicTo(SkBits2Float(0x420223da), SkBits2Float(0x427c6249), SkBits2Float(0x42022f1d), SkBits2Float(0x427c78d0), SkBits2Float(0x42023a61), SkBits2Float(0x427c78d0)); // 32.535f, 63.096f, 32.546f, 63.118f, 32.557f, 63.118f +path.cubicTo(SkBits2Float(0x42025b26), SkBits2Float(0x427c6249), SkBits2Float(0x42028834), SkBits2Float(0x427c4184), SkBits2Float(0x4202a8f9), SkBits2Float(0x427c4184)); // 32.589f, 63.096f, 32.633f, 63.064f, 32.665f, 63.064f +path.cubicTo(SkBits2Float(0x4203e97c), SkBits2Float(0x427bc7a9), SkBits2Float(0x42061db5), SkBits2Float(0x427ba6e4), SkBits2Float(0x4207b649), SkBits2Float(0x427bfef5)); // 32.978f, 62.945f, 33.529f, 62.913f, 33.928f, 62.999f +path.cubicTo(SkBits2Float(0x42089ebc), SkBits2Float(0x427c20c0), SkBits2Float(0x420970a7), SkBits2Float(0x427c78d0), SkBits2Float(0x420a21ce), SkBits2Float(0x427cc59d)); // 34.155f, 63.032f, 34.36f, 63.118f, 34.533f, 63.193f +path.cubicTo(SkBits2Float(0x420a6fa1), SkBits2Float(0x427cdc24), SkBits2Float(0x420ab12a), SkBits2Float(0x427ce768), SkBits2Float(0x420af3ba), SkBits2Float(0x427d1370)); // 34.609f, 63.215f, 34.673f, 63.226f, 34.738f, 63.269f +path.cubicTo(SkBits2Float(0x420b0a41), SkBits2Float(0x427d1370), SkBits2Float(0x420af3ba), SkBits2Float(0x427d353b), SkBits2Float(0x420b1585), SkBits2Float(0x427d407e)); // 34.76f, 63.269f, 34.738f, 63.302f, 34.771f, 63.313f +path.cubicTo(SkBits2Float(0x420b0a41), SkBits2Float(0x427d6143), SkBits2Float(0x420b0a41), SkBits2Float(0x427d8207), SkBits2Float(0x420af3ba), SkBits2Float(0x427dae0f)); // 34.76f, 63.345f, 34.76f, 63.377f, 34.738f, 63.42f +path.cubicTo(SkBits2Float(0x420ad2f5), SkBits2Float(0x427df09e), SkBits2Float(0x420a2d12), SkBits2Float(0x427e54f8), SkBits2Float(0x420a4293), SkBits2Float(0x427ee455)); // 34.706f, 63.485f, 34.544f, 63.583f, 34.565f, 63.723f +path.cubicTo(SkBits2Float(0x420a591a), SkBits2Float(0x427f051a), SkBits2Float(0x420ad2f5), SkBits2Float(0x427f3122), SkBits2Float(0x420af3ba), SkBits2Float(0x427f47a9)); // 34.587f, 63.755f, 34.706f, 63.798f, 34.738f, 63.82f +path.cubicTo(SkBits2Float(0x420af3ba), SkBits2Float(0x427f5d2a), SkBits2Float(0x420af3ba), SkBits2Float(0x427f73b1), SkBits2Float(0x420b0a41), SkBits2Float(0x427f7ef5)); // 34.738f, 63.841f, 34.738f, 63.863f, 34.76f, 63.874f +path.cubicTo(SkBits2Float(0x420add33), SkBits2Float(0x427fccc8), SkBits2Float(0x420a21ce), SkBits2Float(0x42803e74), SkBits2Float(0x420a2d12), SkBits2Float(0x4280701e)); // 34.716f, 63.95f, 34.533f, 64.122f, 34.544f, 64.219f +path.cubicTo(SkBits2Float(0x420a3856), SkBits2Float(0x42808bc4), SkBits2Float(0x420ad2f5), SkBits2Float(0x4280a7ed), SkBits2Float(0x420ae876), SkBits2Float(0x4280b2ad)); // 34.555f, 64.273f, 34.706f, 64.328f, 34.727f, 64.349f +path.cubicTo(SkBits2Float(0x420af3ba), SkBits2Float(0x4280bdf1), SkBits2Float(0x420add32), SkBits2Float(0x4280c8b1), SkBits2Float(0x420af3ba), SkBits2Float(0x4280d3f5)); // 34.738f, 64.371f, 34.716f, 64.392f, 34.738f, 64.414f +path.cubicTo(SkBits2Float(0x420abd74), SkBits2Float(0x4280f53d), SkBits2Float(0x4209f5c6), SkBits2Float(0x4281428d), SkBits2Float(0x420a21ce), SkBits2Float(0x42816e95)); // 34.685f, 64.479f, 34.49f, 64.63f, 34.533f, 64.716f +path.cubicTo(SkBits2Float(0x420a4293), SkBits2Float(0x4281957e), SkBits2Float(0x420ad2f5), SkBits2Float(0x4281a664), SkBits2Float(0x420ae876), SkBits2Float(0x4281c187)); // 34.565f, 64.792f, 34.706f, 64.825f, 34.727f, 64.878f +path.cubicTo(SkBits2Float(0x420ae876), SkBits2Float(0x4281c729), SkBits2Float(0x420add32), SkBits2Float(0x4281d26c), SkBits2Float(0x420ae876), SkBits2Float(0x4281d26c)); // 34.727f, 64.889f, 34.716f, 64.911f, 34.727f, 64.911f +path.cubicTo(SkBits2Float(0x420aa6ed), SkBits2Float(0x4281fe74), SkBits2Float(0x420a591a), SkBits2Float(0x42821a1a), SkBits2Float(0x4209f5c6), SkBits2Float(0x42823b62)); // 34.663f, 64.997f, 34.587f, 65.051f, 34.49f, 65.116f +path.cubicTo(SkBits2Float(0x420a168b), SkBits2Float(0x42825caa), SkBits2Float(0x420a010a), SkBits2Float(0x4282624b), SkBits2Float(0x420a2d12), SkBits2Float(0x42827850)); // 34.522f, 65.181f, 34.501f, 65.192f, 34.544f, 65.235f +path.cubicTo(SkBits2Float(0x420a645e), SkBits2Float(0x428288b2), SkBits2Float(0x420a9baa), SkBits2Float(0x428293f6), SkBits2Float(0x420ad2f5), SkBits2Float(0x4282a458)); // 34.598f, 65.267f, 34.652f, 65.289f, 34.706f, 65.321f +path.cubicTo(SkBits2Float(0x420ad2f5), SkBits2Float(0x4282a458), SkBits2Float(0x420add32), SkBits2Float(0x4282d685), SkBits2Float(0x420abd74), SkBits2Float(0x4282c5a0)); // 34.706f, 65.321f, 34.716f, 65.419f, 34.685f, 65.386f +path.cubicTo(SkBits2Float(0x420aa6ed), SkBits2Float(0x4282cb42), SkBits2Float(0x420a9066), SkBits2Float(0x4282e146), SkBits2Float(0x420a6fa1), SkBits2Float(0x4282e6e8)); // 34.663f, 65.397f, 34.641f, 65.44f, 34.609f, 65.451f +path.cubicTo(SkBits2Float(0x4209df3f), SkBits2Float(0x42830830), SkBits2Float(0x4208f6cc), SkBits2Float(0x4282bae0), SkBits2Float(0x42088834), SkBits2Float(0x4282a459)); // 34.468f, 65.516f, 34.241f, 65.365f, 34.133f, 65.321f +path.cubicTo(SkBits2Float(0x420846ab), SkBits2Float(0x42829915), SkBits2Float(0x42080f5f), SkBits2Float(0x42829915), SkBits2Float(0x4207c18c), SkBits2Float(0x428293f7)); // 34.069f, 65.299f, 34.015f, 65.299f, 33.939f, 65.289f +path.cubicTo(SkBits2Float(0x42079584), SkBits2Float(0x428288b3), SkBits2Float(0x420748b7), SkBits2Float(0x42826d0e), SkBits2Float(0x42071ba9), SkBits2Float(0x42826d0e)); // 33.896f, 65.267f, 33.821f, 65.213f, 33.777f, 65.213f +path.cubicTo(SkBits2Float(0x4206cedc), SkBits2Float(0x4282624d), SkBits2Float(0x42068109), SkBits2Float(0x4282624d), SkBits2Float(0x42061272), SkBits2Float(0x4282624d)); // 33.702f, 65.192f, 33.626f, 65.192f, 33.518f, 65.192f +path.cubicTo(SkBits2Float(0x4205cfe3), SkBits2Float(0x42825cab), SkBits2Float(0x4205614b), SkBits2Float(0x42824bc6), SkBits2Float(0x42051ebc), SkBits2Float(0x42824bc6)); // 33.453f, 65.181f, 33.345f, 65.148f, 33.28f, 65.148f +path.cubicTo(SkBits2Float(0x42037ae5), SkBits2Float(0x428246a7), SkBits2Float(0x4201cbca), SkBits2Float(0x42829eb8), SkBits2Float(0x4200ac0c), SkBits2Float(0x4282e147)); // 32.87f, 65.138f, 32.449f, 65.31f, 32.168f, 65.44f +path.cubicTo(SkBits2Float(0x42008b47), SkBits2Float(0x4282e6e9), SkBits2Float(0x42005e39), SkBits2Float(0x4282fced), SkBits2Float(0x42003d74), SkBits2Float(0x4283028f)); // 32.136f, 65.451f, 32.092f, 65.494f, 32.06f, 65.505f +path.cubicTo(SkBits2Float(0x41fdf9e2), SkBits2Float(0x42833f7d), SkBits2Float(0x41fa4190), SkBits2Float(0x42836041), SkBits2Float(0x41f674c3), SkBits2Float(0x42834fdf)); // 31.747f, 65.624f, 31.282f, 65.688f, 30.807f, 65.656f +path.cubicTo(SkBits2Float(0x41f59794), SkBits2Float(0x4283451e), SkBits2Float(0x41f48d56), SkBits2Float(0x4283451e), SkBits2Float(0x41f3b027), SkBits2Float(0x428339db)); // 30.699f, 65.635f, 30.569f, 65.635f, 30.461f, 65.613f +path.cubicTo(SkBits2Float(0x41f32d15), SkBits2Float(0x42832e97), SkBits2Float(0x41f2666d), SkBits2Float(0x428312f2), SkBits2Float(0x41f1b440), SkBits2Float(0x42830831)); // 30.397f, 65.591f, 30.3f, 65.537f, 30.213f, 65.516f +path.cubicTo(SkBits2Float(0x41f1041f), SkBits2Float(0x4282fced), SkBits2Float(0x41f07f01), SkBits2Float(0x4282f74c), SkBits2Float(0x41efb859), SkBits2Float(0x4282e6e9)); // 30.127f, 65.494f, 30.062f, 65.483f, 29.965f, 65.451f +path.cubicTo(SkBits2Float(0x41efa1d2), SkBits2Float(0x4282e147), SkBits2Float(0x41ef6049), SkBits2Float(0x4282d687), SkBits2Float(0x41ef49c1), SkBits2Float(0x4282d687)); // 29.954f, 65.44f, 29.922f, 65.419f, 29.911f, 65.419f +path.cubicTo(SkBits2Float(0x41ef062b), SkBits2Float(0x4282cb43), SkBits2Float(0x41eec4a2), SkBits2Float(0x4282cb43), SkBits2Float(0x41ee560b), SkBits2Float(0x4282c5a2)); // 29.878f, 65.397f, 29.846f, 65.397f, 29.792f, 65.386f +path.cubicTo(SkBits2Float(0x41ee1275), SkBits2Float(0x4282c000), SkBits2Float(0x41ed8f63), SkBits2Float(0x4282a45a), SkBits2Float(0x41ed3546), SkBits2Float(0x42829eb9)); // 29.759f, 65.375f, 29.695f, 65.321f, 29.651f, 65.31f +path.cubicTo(SkBits2Float(0x41ebbe7d), SkBits2Float(0x42827d71), SkBits2Float(0x41ea72b7), SkBits2Float(0x42825cad), SkBits2Float(0x41e91069), SkBits2Float(0x42823b65)); // 29.468f, 65.245f, 29.306f, 65.181f, 29.133f, 65.116f +path.cubicTo(SkBits2Float(0x41e6fdfa), SkBits2Float(0x42820419), SkBits2Float(0x41e4a7f6), SkBits2Float(0x4281ab86), SkBits2Float(0x41e18b4a), SkBits2Float(0x4281ab86)); // 28.874f, 65.008f, 28.582f, 64.835f, 28.193f, 64.835f +path.cubicTo(SkBits2Float(0x41de9bac), SkBits2Float(0x4281b128), SkBits2Float(0x41dcf7d5), SkBits2Float(0x4281fe78), SkBits2Float(0x41db3d77), SkBits2Float(0x428246a9)); // 27.826f, 64.846f, 27.621f, 64.997f, 27.405f, 65.138f +path.cubicTo(SkBits2Float(0x41dacedf), SkBits2Float(0x4282570b), SkBits2Float(0x41da76cf), SkBits2Float(0x4282570b), SkBits2Float(0x41da0838), SkBits2Float(0x4282676e)); // 27.351f, 65.17f, 27.308f, 65.17f, 27.254f, 65.202f +path.cubicTo(SkBits2Float(0x41d9f1b1), SkBits2Float(0x4282676e), SkBits2Float(0x41d9f1b1), SkBits2Float(0x42827853), SkBits2Float(0x41d9db2a), SkBits2Float(0x42827d72)); // 27.243f, 65.202f, 27.243f, 65.235f, 27.232f, 65.245f +path.cubicTo(SkBits2Float(0x41d96c92), SkBits2Float(0x428288b6), SkBits2Float(0x41d91482), SkBits2Float(0x428288b6), SkBits2Float(0x41d8a5eb), SkBits2Float(0x42829eba)); // 27.178f, 65.267f, 27.135f, 65.267f, 27.081f, 65.31f +path.lineTo(SkBits2Float(0x41d88f64), SkBits2Float(0x4282a9fe)); // 27.07f, 65.332f +path.cubicTo(SkBits2Float(0x41d6eb8d), SkBits2Float(0x4282e14a), SkBits2Float(0x41d4ac10), SkBits2Float(0x42830291), SkBits2Float(0x41d25818), SkBits2Float(0x428312f4)); // 26.865f, 65.44f, 26.584f, 65.505f, 26.293f, 65.537f +path.cubicTo(SkBits2Float(0x41d0b235), SkBits2Float(0x42831896), SkBits2Float(0x41ce74c4), SkBits2Float(0x428312f4), SkBits2Float(0x41cce568), SkBits2Float(0x42830292)); // 26.087f, 65.548f, 25.807f, 65.537f, 25.612f, 65.505f +path.cubicTo(SkBits2Float(0x41cca3df), SkBits2Float(0x4282fcf0), SkBits2Float(0x41cc1ec0), SkBits2Float(0x4282f1ad), SkBits2Float(0x41cbf3be), SkBits2Float(0x4282f1ad)); // 25.58f, 65.494f, 25.515f, 65.472f, 25.494f, 65.472f +path.cubicTo(SkBits2Float(0x41ca9170), SkBits2Float(0x4282dba9), SkBits2Float(0x41c99dba), SkBits2Float(0x4282e14b), SkBits2Float(0x41c8687a), SkBits2Float(0x4282cb47)); // 25.321f, 65.429f, 25.202f, 65.44f, 25.051f, 65.397f +path.cubicTo(SkBits2Float(0x41c7b64d), SkBits2Float(0x4282c003), SkBits2Float(0x41c71cb3), SkBits2Float(0x4282bae5), SkBits2Float(0x41c6560b), SkBits2Float(0x4282a9ff)); // 24.964f, 65.375f, 24.889f, 65.365f, 24.792f, 65.332f +path.lineTo(SkBits2Float(0x41c628fd), SkBits2Float(0x42829ebb)); // 24.77f, 65.31f +path.cubicTo(SkBits2Float(0x41c58d57), SkBits2Float(0x428293fa), SkBits2Float(0x41c53547), SkBits2Float(0x42829919), SkBits2Float(0x41c4b028), SkBits2Float(0x428293fa)); // 24.694f, 65.289f, 24.651f, 65.299f, 24.586f, 65.289f +path.lineTo(SkBits2Float(0x41c46e9f), SkBits2Float(0x42828315)); // 24.554f, 65.256f +path.cubicTo(SkBits2Float(0x41c1d712), SkBits2Float(0x4282570d), SkBits2Float(0x41be20cc), SkBits2Float(0x428209bd), SkBits2Float(0x41bb0420), SkBits2Float(0x42820f5f)); // 24.23f, 65.17f, 23.766f, 65.019f, 23.377f, 65.03f +path.cubicTo(SkBits2Float(0x41b9a1d2), SkBits2Float(0x42820f5f), SkBits2Float(0x41b7e774), SkBits2Float(0x42823024), SkBits2Float(0x41b6dd37), SkBits2Float(0x428246ab)); // 23.204f, 65.03f, 22.988f, 65.094f, 22.858f, 65.138f +path.cubicTo(SkBits2Float(0x41b5eb8d), SkBits2Float(0x4282570d), SkBits2Float(0x41b54fe7), SkBits2Float(0x4282570d), SkBits2Float(0x41b45c31), SkBits2Float(0x42826770)); // 22.74f, 65.17f, 22.664f, 65.17f, 22.545f, 65.202f +path.cubicTo(SkBits2Float(0x41b3ed99), SkBits2Float(0x42826d12), SkBits2Float(0x41b35400), SkBits2Float(0x428288b8), SkBits2Float(0x41b2fbef), SkBits2Float(0x428293fb)); // 22.491f, 65.213f, 22.416f, 65.267f, 22.373f, 65.289f +path.cubicTo(SkBits2Float(0x41b274c4), SkBits2Float(0x4282991a), SkBits2Float(0x41b249c2), SkBits2Float(0x428293fb), SkBits2Float(0x41b1c4a3), SkBits2Float(0x42829ebc)); // 22.307f, 65.299f, 22.286f, 65.289f, 22.221f, 65.31f +path.cubicTo(SkBits2Float(0x41b1560b), SkBits2Float(0x4282a45e), SkBits2Float(0x41b08f64), SkBits2Float(0x4282c004), SkBits2Float(0x41aff3be), SkBits2Float(0x4282cb47)); // 22.167f, 65.321f, 22.07f, 65.375f, 21.994f, 65.397f +path.cubicTo(SkBits2Float(0x41aea7f7), SkBits2Float(0x4282e14b), SkBits2Float(0x41ad893f), SkBits2Float(0x4282f1ad), SkBits2Float(0x41ac3d78), SkBits2Float(0x42830835)); // 21.832f, 65.44f, 21.692f, 65.472f, 21.53f, 65.516f +path.cubicTo(SkBits2Float(0x41ac106a), SkBits2Float(0x428312f6), SkBits2Float(0x41aba1d2), SkBits2Float(0x42831e39), SkBits2Float(0x41ab76d0), SkBits2Float(0x428323db)); // 21.508f, 65.537f, 21.454f, 65.559f, 21.433f, 65.57f +path.cubicTo(SkBits2Float(0x41aac4a3), SkBits2Float(0x4283343d), SkBits2Float(0x41aa560b), SkBits2Float(0x4283343d), SkBits2Float(0x41a9ba66), SkBits2Float(0x42833f81)); // 21.346f, 65.602f, 21.292f, 65.602f, 21.216f, 65.624f +path.lineTo(SkBits2Float(0x41a98f64), SkBits2Float(0x42834fe3)); // 21.195f, 65.656f +path.cubicTo(SkBits2Float(0x41a96256), SkBits2Float(0x42834fe3), SkBits2Float(0x41a93754), SkBits2Float(0x42834522), SkBits2Float(0x41a920cc), SkBits2Float(0x42834fe3)); // 21.173f, 65.656f, 21.152f, 65.635f, 21.141f, 65.656f +path.cubicTo(SkBits2Float(0x41a90a45), SkBits2Float(0x42834fe3), SkBits2Float(0x41a8b234), SkBits2Float(0x42836045), SkBits2Float(0x41a89bad), SkBits2Float(0x42836b89)); // 21.13f, 65.656f, 21.087f, 65.688f, 21.076f, 65.71f +path.cubicTo(SkBits2Float(0x41a7d505), SkBits2Float(0x42837beb), SkBits2Float(0x41a7666e), SkBits2Float(0x4283818d), SkBits2Float(0x41a6cac8), SkBits2Float(0x42839d33)); // 20.979f, 65.742f, 20.925f, 65.753f, 20.849f, 65.807f +path.cubicTo(SkBits2Float(0x41a6b64d), SkBits2Float(0x4283a2d5), SkBits2Float(0x41a672b8), SkBits2Float(0x4283b3ba), SkBits2Float(0x41a65e3d), SkBits2Float(0x4283b8d9)); // 20.839f, 65.818f, 20.806f, 65.851f, 20.796f, 65.861f +path.cubicTo(SkBits2Float(0x41a6312f), SkBits2Float(0x4283be7b), SkBits2Float(0x41a60420), SkBits2Float(0x4283b8d9), SkBits2Float(0x41a5ed99), SkBits2Float(0x4283be7b)); // 20.774f, 65.872f, 20.752f, 65.861f, 20.741f, 65.872f +path.cubicTo(SkBits2Float(0x41a5810e), SkBits2Float(0x4283cedd), SkBits2Float(0x41a4e568), SkBits2Float(0x428406ac), SkBits2Float(0x41a48d57), SkBits2Float(0x42840bcb)); // 20.688f, 65.904f, 20.612f, 66.013f, 20.569f, 66.023f +path.lineTo(SkBits2Float(0x41a41ebf), SkBits2Float(0x42840bcb)); // 20.515f, 66.023f +path.cubicTo(SkBits2Float(0x41a40838), SkBits2Float(0x4283fb69), SkBits2Float(0x41a3f1b1), SkBits2Float(0x428406ac), SkBits2Float(0x41a3f1b1), SkBits2Float(0x4283fb69)); // 20.504f, 65.991f, 20.493f, 66.013f, 20.493f, 65.991f +path.cubicTo(SkBits2Float(0x41a38319), SkBits2Float(0x4283b8da), SkBits2Float(0x41a4b859), SkBits2Float(0x4282f750), SkBits2Float(0x41a4e567), SkBits2Float(0x4282cb48)); // 20.439f, 65.861f, 20.59f, 65.483f, 20.612f, 65.397f +path.cubicTo(SkBits2Float(0x41a5ed98), SkBits2Float(0x4281d273), SkBits2Float(0x41a74fe6), SkBits2Float(0x4280ea00), SkBits2Float(0x41a96255), SkBits2Float(0x42802e19)); // 20.741f, 64.911f, 20.914f, 64.457f, 21.173f, 64.09f +path.cubicTo(SkBits2Float(0x41aa2b09), SkBits2Float(0x427fccd6), SkBits2Float(0x41ab1ebf), SkBits2Float(0x427f6982), SkBits2Float(0x41abfbef), SkBits2Float(0x427eefa7)); // 21.271f, 63.95f, 21.39f, 63.853f, 21.498f, 63.734f +path.cubicTo(SkBits2Float(0x41ac7f01), SkBits2Float(0x427e9690), SkBits2Float(0x41aced99), SkBits2Float(0x427e49c4), SkBits2Float(0x41ad893f), SkBits2Float(0x427e0734)); // 21.562f, 63.647f, 21.616f, 63.572f, 21.692f, 63.507f +path.cubicTo(SkBits2Float(0x41aed506), SkBits2Float(0x427d8215), SkBits2Float(0x41b020cc), SkBits2Float(0x427d137e), SkBits2Float(0x41b1831a), SkBits2Float(0x427cbb6d)); // 21.854f, 63.377f, 22.016f, 63.269f, 22.189f, 63.183f +path.cubicTo(SkBits2Float(0x41b1f1b2), SkBits2Float(0x427c99a2), SkBits2Float(0x41b26049), SkBits2Float(0x427c6256), SkBits2Float(0x41b2cee1), SkBits2Float(0x427c4cd5)); // 22.243f, 63.15f, 22.297f, 63.096f, 22.351f, 63.075f +path.cubicTo(SkBits2Float(0x41b3106a), SkBits2Float(0x427c2b0a), SkBits2Float(0x41b445aa), SkBits2Float(0x427bff02), SkBits2Float(0x41b49dba), SkBits2Float(0x427bde3d)); // 22.383f, 63.042f, 22.534f, 62.999f, 22.577f, 62.967f +path.cubicTo(SkBits2Float(0x41b49dba), SkBits2Float(0x427bd2f9), SkBits2Float(0x41b4cac8), SkBits2Float(0x427ba6f1), SkBits2Float(0x41b4cac8), SkBits2Float(0x427ba6f1)); // 22.577f, 62.956f, 22.599f, 62.913f, 22.599f, 62.913f +path.cubicTo(SkBits2Float(0x41b4cac8), SkBits2Float(0x427b6462), SkBits2Float(0x41b33b6c), SkBits2Float(0x427a4fe7), SkBits2Float(0x41b2fbef), SkBits2Float(0x427a189b)); // 22.599f, 62.848f, 22.404f, 62.578f, 22.373f, 62.524f +path.cubicTo(SkBits2Float(0x41b2cee1), SkBits2Float(0x4279f7d6), SkBits2Float(0x41b19795), SkBits2Float(0x42796774), SkBits2Float(0x41b1560c), SkBits2Float(0x42795c31)); // 22.351f, 62.492f, 22.199f, 62.351f, 22.167f, 62.34f +path.cubicTo(SkBits2Float(0x41b0e774), SkBits2Float(0x42793b6c), SkBits2Float(0x41aff3be), SkBits2Float(0x42795c31), SkBits2Float(0x41af70ac), SkBits2Float(0x42795c31)); // 22.113f, 62.308f, 21.994f, 62.34f, 21.93f, 62.34f +path.cubicTo(SkBits2Float(0x41ae0e5e), SkBits2Float(0x42796775), SkBits2Float(0x41ac9589), SkBits2Float(0x427946b0), SkBits2Float(0x41ab76d1), SkBits2Float(0x42793b6c)); // 21.757f, 62.351f, 21.573f, 62.319f, 21.433f, 62.308f +path.cubicTo(SkBits2Float(0x41aa3f85), SkBits2Float(0x42793028), SkBits2Float(0x41a94ddb), SkBits2Float(0x42793b6c), SkBits2Float(0x41a82d17), SkBits2Float(0x42793028)); // 21.281f, 62.297f, 21.163f, 62.308f, 21.022f, 62.297f +path.cubicTo(SkBits2Float(0x41a5c298), SkBits2Float(0x42791aa7), SkBits2Float(0x41a2e775), SkBits2Float(0x4278ed99), SkBits2Float(0x41a07cf6), SkBits2Float(0x4278c190)); // 20.72f, 62.276f, 20.363f, 62.232f, 20.061f, 62.189f +path.cubicTo(SkBits2Float(0x419f47b7), SkBits2Float(0x4278b753), SkBits2Float(0x419e810f), SkBits2Float(0x4278b753), SkBits2Float(0x419d4bcf), SkBits2Float(0x4278a0cb)); // 19.91f, 62.179f, 19.813f, 62.179f, 19.662f, 62.157f +path.cubicTo(SkBits2Float(0x419c831b), SkBits2Float(0x42788a44), SkBits2Float(0x419b20cd), SkBits2Float(0x42785e3c), SkBits2Float(0x419a45aa), SkBits2Float(0x427847b4)); // 19.564f, 62.135f, 19.391f, 62.092f, 19.284f, 62.07f +path.cubicTo(SkBits2Float(0x41949171), SkBits2Float(0x4277e460), SkBits2Float(0x418e5819), SkBits2Float(0x42778c50), SkBits2Float(0x41896a87), SkBits2Float(0x4275dd35)); // 18.571f, 61.973f, 17.793f, 61.887f, 17.177f, 61.466f +path.cubicTo(SkBits2Float(0x4182efa6), SkBits2Float(0x4273a8fc), SkBits2Float(0x417fd71a), SkBits2Float(0x42703f83), SkBits2Float(0x4180dd37), SkBits2Float(0x426c5b29)); // 16.367f, 60.915f, 15.99f, 60.062f, 16.108f, 59.089f +path.cubicTo(SkBits2Float(0x41813547), SkBits2Float(0x426b5d35), SkBits2Float(0x41821276), SkBits2Float(0x426a8006), SkBits2Float(0x4182560c), SkBits2Float(0x426976cf)); // 16.151f, 58.841f, 16.259f, 58.625f, 16.292f, 58.366f +path.cubicTo(SkBits2Float(0x418228fe), SkBits2Float(0x426976cf), SkBits2Float(0x41823f85), SkBits2Float(0x42696b8b), SkBits2Float(0x418228fe), SkBits2Float(0x42694ac7)); // 16.27f, 58.366f, 16.281f, 58.355f, 16.27f, 58.323f +path.cubicTo(SkBits2Float(0x4181a5ec), SkBits2Float(0x42696b8c), SkBits2Float(0x41813548), SkBits2Float(0x42696b8c), SkBits2Float(0x41809bae), SkBits2Float(0x4269560b)); // 16.206f, 58.355f, 16.151f, 58.355f, 16.076f, 58.334f +path.cubicTo(SkBits2Float(0x4180b235), SkBits2Float(0x4269560b), SkBits2Float(0x4180439e), SkBits2Float(0x426976d0), SkBits2Float(0x4180168f), SkBits2Float(0x42696b8c)); // 16.087f, 58.334f, 16.033f, 58.366f, 16.011f, 58.355f +path.cubicTo(SkBits2Float(0x417eccdc), SkBits2Float(0x4269560b), SkBits2Float(0x417e9fce), SkBits2Float(0x4268d0ec), SkBits2Float(0x417f4fef), SkBits2Float(0x42688319)); // 15.925f, 58.334f, 15.914f, 58.204f, 15.957f, 58.128f +path.cubicTo(SkBits2Float(0x4180168f), SkBits2Float(0x4268364c), SkBits2Float(0x41849589), SkBits2Float(0x4267a5ea), SkBits2Float(0x4185b441), SkBits2Float(0x42679069)); // 16.011f, 58.053f, 16.573f, 57.912f, 16.713f, 57.891f +path.cubicTo(SkBits2Float(0x41891276), SkBits2Float(0x42674296), SkBits2Float(0x418c9dba), SkBits2Float(0x4266df42), SkBits2Float(0x418fd0ed), SkBits2Float(0x4266916f)); // 17.134f, 57.815f, 17.577f, 57.718f, 17.977f, 57.642f +path.cubicTo(SkBits2Float(0x4190ae1c), SkBits2Float(0x42668732), SkBits2Float(0x4191333b), SkBits2Float(0x42668732), SkBits2Float(0x4192106a), SkBits2Float(0x426670aa)); // 18.085f, 57.632f, 18.15f, 57.632f, 18.258f, 57.61f +path.cubicTo(SkBits2Float(0x4193189b), SkBits2Float(0x42665a23), SkBits2Float(0x4194a5eb), SkBits2Float(0x426622d7), SkBits2Float(0x4195dd37), SkBits2Float(0x42660d56)); // 18.387f, 57.588f, 18.581f, 57.534f, 18.733f, 57.513f +path.cubicTo(SkBits2Float(0x41975400), SkBits2Float(0x4265e254), SkBits2Float(0x41988b4b), SkBits2Float(0x4265c18f), SkBits2Float(0x4199d506), SkBits2Float(0x4265ac0e)); // 18.916f, 57.471f, 19.068f, 57.439f, 19.229f, 57.418f +path.moveTo(SkBits2Float(0x41a4e568), SkBits2Float(0x4277d0eb)); // 20.612f, 61.954f +path.cubicTo(SkBits2Float(0x41a4cee1), SkBits2Float(0x4277d0eb), SkBits2Float(0x41a48d58), SkBits2Float(0x4277f1b0), SkBits2Float(0x41a48d58), SkBits2Float(0x4277f1b0)); // 20.601f, 61.954f, 20.569f, 61.986f, 20.569f, 61.986f +path.cubicTo(SkBits2Float(0x41a3831b), SkBits2Float(0x42781275), SkBits2Float(0x41a0c08b), SkBits2Float(0x4277c5a8), SkBits2Float(0x419fe35c), SkBits2Float(0x4277af21)); // 20.439f, 62.018f, 20.094f, 61.943f, 19.986f, 61.921f +path.cubicTo(SkBits2Float(0x419dd0ed), SkBits2Float(0x42778319), SkBits2Float(0x419bbc73), SkBits2Float(0x42775711), SkBits2Float(0x4199c08b), SkBits2Float(0x42771481)); // 19.727f, 61.878f, 19.467f, 61.835f, 19.219f, 61.77f +path.cubicTo(SkBits2Float(0x4199687b), SkBits2Float(0x4277093d), SkBits2Float(0x4198f7d7), SkBits2Float(0x4276f3bc), SkBits2Float(0x4198b64e), SkBits2Float(0x4276dd35)); // 19.176f, 61.759f, 19.121f, 61.738f, 19.089f, 61.716f +path.cubicTo(SkBits2Float(0x419847b6), SkBits2Float(0x4276d1f1), SkBits2Float(0x4198062d), SkBits2Float(0x4276dd35), SkBits2Float(0x4197ae1d), SkBits2Float(0x4276d1f1)); // 19.035f, 61.705f, 19.003f, 61.716f, 18.96f, 61.705f +path.cubicTo(SkBits2Float(0x4196fbf0), SkBits2Float(0x4276c6ad), SkBits2Float(0x4196083a), SkBits2Float(0x42768f62), SkBits2Float(0x4195831b), SkBits2Float(0x427679e1)); // 18.873f, 61.694f, 18.754f, 61.64f, 18.689f, 61.619f +path.cubicTo(SkBits2Float(0x41951690), SkBits2Float(0x4276635a), SkBits2Float(0x41950009), SkBits2Float(0x427679e1), SkBits2Float(0x4194a5ec), SkBits2Float(0x4276635a)); // 18.636f, 61.597f, 18.625f, 61.619f, 18.581f, 61.597f +path.cubicTo(SkBits2Float(0x41940c52), SkBits2Float(0x42764dd9), SkBits2Float(0x41935a25), SkBits2Float(0x4276168d), SkBits2Float(0x4192c08c), SkBits2Float(0x42760006)); // 18.506f, 61.576f, 18.419f, 61.522f, 18.344f, 61.5f +path.cubicTo(SkBits2Float(0x4190c298), SkBits2Float(0x42759cb2), SkBits2Float(0x418f6257), SkBits2Float(0x427544a2), SkBits2Float(0x418e2b0b), SkBits2Float(0x42748837)); // 18.095f, 61.403f, 17.923f, 61.317f, 17.771f, 61.133f +path.cubicTo(SkBits2Float(0x418e1690), SkBits2Float(0x4274666c), SkBits2Float(0x418dd2fb), SkBits2Float(0x4274666c), SkBits2Float(0x418dbe80), SkBits2Float(0x42745c2f)); // 17.761f, 61.1f, 17.728f, 61.1f, 17.718f, 61.09f +path.cubicTo(SkBits2Float(0x418da7f9), SkBits2Float(0x42742f21), SkBits2Float(0x418da7f9), SkBits2Float(0x42740e5c), SkBits2Float(0x418d6670), SkBits2Float(0x4273ed97)); // 17.707f, 61.046f, 17.707f, 61.014f, 17.675f, 60.982f +path.cubicTo(SkBits2Float(0x418d22da), SkBits2Float(0x42739fc4), SkBits2Float(0x418ccaca), SkBits2Float(0x427373bc), SkBits2Float(0x418c9dbc), SkBits2Float(0x42731aa5)); // 17.642f, 60.906f, 17.599f, 60.863f, 17.577f, 60.776f +path.cubicTo(SkBits2Float(0x418bd714), SkBits2Float(0x4271b95d), SkBits2Float(0x418d22db), SkBits2Float(0x4270999f), SkBits2Float(0x418fd0ef), SkBits2Float(0x4270418e)); // 17.48f, 60.431f, 17.642f, 60.15f, 17.977f, 60.064f +path.cubicTo(SkBits2Float(0x41919fc8), SkBits2Float(0x426ffeff), SkBits2Float(0x4193df45), SkBits2Float(0x42701fc3), SkBits2Float(0x4195f3c0), SkBits2Float(0x4270841d)); // 18.203f, 59.999f, 18.484f, 60.031f, 18.744f, 60.129f +path.cubicTo(SkBits2Float(0x419847b8), SkBits2Float(0x4270e771), SkBits2Float(0x419a5a26), SkBits2Float(0x42718211), SkBits2Float(0x419bd2fb), SkBits2Float(0x42723231)); // 19.035f, 60.226f, 19.294f, 60.377f, 19.478f, 60.549f +path.cubicTo(SkBits2Float(0x419be982), SkBits2Float(0x42723e7b), SkBits2Float(0x419be982), SkBits2Float(0x42726a83), SkBits2Float(0x419c1484), SkBits2Float(0x42726a83)); // 19.489f, 60.561f, 19.489f, 60.604f, 19.51f, 60.604f +path.cubicTo(SkBits2Float(0x419c4192), SkBits2Float(0x42728004), SkBits2Float(0x419c831c), SkBits2Float(0x42728004), SkBits2Float(0x419c99a3), SkBits2Float(0x4272968b)); // 19.532f, 60.625f, 19.564f, 60.625f, 19.575f, 60.647f +path.cubicTo(SkBits2Float(0x419cdb2c), SkBits2Float(0x4272b750), SkBits2Float(0x419d083b), SkBits2Float(0x4272ee9b), SkBits2Float(0x419d3549), SkBits2Float(0x427325e7)); // 19.607f, 60.679f, 19.629f, 60.733f, 19.651f, 60.787f +path.cubicTo(SkBits2Float(0x419e28ff), SkBits2Float(0x4273cbca), SkBits2Float(0x419f062e), SkBits2Float(0x4274666a), SkBits2Float(0x419ff7d8), SkBits2Float(0x42750c4d)); // 19.77f, 60.949f, 19.878f, 61.1f, 19.996f, 61.262f +path.cubicTo(SkBits2Float(0x41a0c08c), SkBits2Float(0x42758628), SkBits2Float(0x41a1f5cc), SkBits2Float(0x4275df3f), SkBits2Float(0x41a2d2fb), SkBits2Float(0x42766357)); // 20.094f, 61.381f, 20.245f, 61.468f, 20.353f, 61.597f +path.cubicTo(SkBits2Float(0x41a31484), SkBits2Float(0x42769aa3), SkBits2Float(0x41a36c95), SkBits2Float(0x4276f3b9), SkBits2Float(0x41a3db2c), SkBits2Float(0x42771fc1)); // 20.385f, 61.651f, 20.428f, 61.738f, 20.482f, 61.781f +path.cubicTo(SkBits2Float(0x41a4083a), SkBits2Float(0x42774bc9), SkBits2Float(0x41a4b85b), SkBits2Float(0x42778315), SkBits2Float(0x41a4e569), SkBits2Float(0x4277af1d)); // 20.504f, 61.824f, 20.59f, 61.878f, 20.612f, 61.921f +path.cubicTo(SkBits2Float(0x41a4e569), SkBits2Float(0x4277ba61), SkBits2Float(0x41a4cee2), SkBits2Float(0x4277c5a4), SkBits2Float(0x41a4e569), SkBits2Float(0x4277d0e8)); // 20.612f, 61.932f, 20.601f, 61.943f, 20.612f, 61.954f +path.moveTo(SkBits2Float(0x41ad72b9), SkBits2Float(0x42786044)); // 21.681f, 62.094f +path.cubicTo(SkBits2Float(0x41ac106b), SkBits2Float(0x42788c4c), SkBits2Float(0x41a9d0ee), SkBits2Float(0x4277d0e8), SkBits2Float(0x41a8b236), SkBits2Float(0x42778e58)); // 21.508f, 62.137f, 21.227f, 61.954f, 21.087f, 61.889f +path.cubicTo(SkBits2Float(0x41a2fdfd), SkBits2Float(0x42761689), SkBits2Float(0x41a10215), SkBits2Float(0x42733c6c), SkBits2Float(0x419fb64f), SkBits2Float(0x42704ccf)); // 20.374f, 61.522f, 20.126f, 60.809f, 19.964f, 60.075f +path.cubicTo(SkBits2Float(0x419f9fc8), SkBits2Float(0x42700a40), SkBits2Float(0x419f47b7), SkBits2Float(0x426f9ba8), SkBits2Float(0x419f3130), SkBits2Float(0x426f5919)); // 19.953f, 60.01f, 19.91f, 59.902f, 19.899f, 59.837f +path.cubicTo(SkBits2Float(0x419f3130), SkBits2Float(0x426f0b46), SkBits2Float(0x419f47b7), SkBits2Float(0x426ec9bd), SkBits2Float(0x419f3130), SkBits2Float(0x426e70a6)); // 19.899f, 59.761f, 19.91f, 59.697f, 19.899f, 59.61f +path.cubicTo(SkBits2Float(0x419f1aa9), SkBits2Float(0x426de14a), SkBits2Float(0x419f062e), SkBits2Float(0x426ced94), SkBits2Float(0x419f3130), SkBits2Float(0x426c5d31)); // 19.888f, 59.47f, 19.878f, 59.232f, 19.899f, 59.091f +path.cubicTo(SkBits2Float(0x419f72b9), SkBits2Float(0x426befa0), SkBits2Float(0x419fe35d), SkBits2Float(0x426b8108), SkBits2Float(0x41a00e5f), SkBits2Float(0x426b3335)); // 19.931f, 58.984f, 19.986f, 58.876f, 20.007f, 58.8f +path.cubicTo(SkBits2Float(0x41a0666f), SkBits2Float(0x426acfe1), SkBits2Float(0x41a10215), SkBits2Float(0x4269c6aa), SkBits2Float(0x41a19dbb), SkBits2Float(0x4269bb66)); // 20.05f, 58.703f, 20.126f, 58.444f, 20.202f, 58.433f +path.cubicTo(SkBits2Float(0x41a220cd), SkBits2Float(0x4269bb66), SkBits2Float(0x41a2a5ec), SkBits2Float(0x4269f2b2), SkBits2Float(0x41a31484), SkBits2Float(0x426a3f7f)); // 20.266f, 58.433f, 20.331f, 58.487f, 20.385f, 58.562f +path.cubicTo(SkBits2Float(0x41a3c6b1), SkBits2Float(0x426aa3d9), SkBits2Float(0x41a449c3), SkBits2Float(0x426b1cae), SkBits2Float(0x41a476d2), SkBits2Float(0x426b3e79)); // 20.472f, 58.66f, 20.536f, 58.778f, 20.558f, 58.811f +path.cubicTo(SkBits2Float(0x41a5ac11), SkBits2Float(0x426c0521), SkBits2Float(0x41a6caca), SkBits2Float(0x426ce250), SkBits2Float(0x41a8189d), SkBits2Float(0x426da9fe)); // 20.709f, 59.005f, 20.849f, 59.221f, 21.012f, 59.416f +path.cubicTo(SkBits2Float(0x41aa3f86), SkBits2Float(0x426f1689), SkBits2Float(0x41ac5401), SkBits2Float(0x4270841b), SkBits2Float(0x41ae7aeb), SkBits2Float(0x4271f0a6)); // 21.281f, 59.772f, 21.541f, 60.129f, 21.81f, 60.485f +path.cubicTo(SkBits2Float(0x41af000a), SkBits2Float(0x427248b6), SkBits2Float(0x41afb237), SkBits2Float(0x4272a1cd), SkBits2Float(0x41b020ce), SkBits2Float(0x4272ee9a)); // 21.875f, 60.571f, 21.962f, 60.658f, 22.016f, 60.733f +path.cubicTo(SkBits2Float(0x41b06257), SkBits2Float(0x42731aa2), SkBits2Float(0x41b19797), SkBits2Float(0x4273f7d1), SkBits2Float(0x41b19797), SkBits2Float(0x4274199c)); // 22.048f, 60.776f, 22.199f, 60.992f, 22.199f, 61.025f +path.cubicTo(SkBits2Float(0x41b1c4a5), SkBits2Float(0x427424e0), SkBits2Float(0x41b1831c), SkBits2Float(0x42746669), SkBits2Float(0x41b1831c), SkBits2Float(0x42746669)); // 22.221f, 61.036f, 22.189f, 61.1f, 22.189f, 61.1f +path.cubicTo(SkBits2Float(0x41ac3d7a), SkBits2Float(0x42742f1d), SkBits2Float(0x41a96257), SkBits2Float(0x4271ae17), SkBits2Float(0x41a7a7f9), SkBits2Float(0x426fb12a)); // 21.53f, 61.046f, 21.173f, 60.42f, 20.957f, 59.923f +path.cubicTo(SkBits2Float(0x41a77cf7), SkBits2Float(0x426f9ba9), SkBits2Float(0x41a73b6e), SkBits2Float(0x426f79de), SkBits2Float(0x41a73b6e), SkBits2Float(0x426f591a)); // 20.936f, 59.902f, 20.904f, 59.869f, 20.904f, 59.837f +path.cubicTo(SkBits2Float(0x41a6e151), SkBits2Float(0x426eea82), SkBits2Float(0x41a68941), SkBits2Float(0x426e6564), SkBits2Float(0x41a672ba), SkBits2Float(0x426dec8f)); // 20.86f, 59.729f, 20.817f, 59.599f, 20.806f, 59.481f +path.cubicTo(SkBits2Float(0x41a65e3f), SkBits2Float(0x426daa00), SkBits2Float(0x41a68941), SkBits2Float(0x426d71ae), SkBits2Float(0x41a65e3f), SkBits2Float(0x426d50e9)); // 20.796f, 59.416f, 20.817f, 59.361f, 20.796f, 59.329f +path.cubicTo(SkBits2Float(0x41a63131), SkBits2Float(0x426d24e1), SkBits2Float(0x41a56a89), SkBits2Float(0x426cf8d9), SkBits2Float(0x41a4fbf1), SkBits2Float(0x426cf8d9)); // 20.774f, 59.286f, 20.677f, 59.243f, 20.623f, 59.243f +path.cubicTo(SkBits2Float(0x41a449c4), SkBits2Float(0x426ced95), SkBits2Float(0x41a36c95), SkBits2Float(0x426cf8d9), SkBits2Float(0x41a31484), SkBits2Float(0x426d24e1)); // 20.536f, 59.232f, 20.428f, 59.243f, 20.385f, 59.286f +path.cubicTo(SkBits2Float(0x41a20a47), SkBits2Float(0x426d71ae), SkBits2Float(0x41a1f5cc), SkBits2Float(0x426f645e), SkBits2Float(0x41a220ce), SkBits2Float(0x42701fc2)); // 20.255f, 59.361f, 20.245f, 59.848f, 20.266f, 60.031f +path.cubicTo(SkBits2Float(0x41a28f66), SkBits2Float(0x4272e45e), SkBits2Float(0x41a4b85b), SkBits2Float(0x4274c9be), SkBits2Float(0x41a7eb8e), SkBits2Float(0x427621ce)); // 20.32f, 60.723f, 20.59f, 61.197f, 20.99f, 61.533f +path.cubicTo(SkBits2Float(0x41a82d17), SkBits2Float(0x42764293), SkBits2Float(0x41a870ad), SkBits2Float(0x42764293), SkBits2Float(0x41a8b236), SkBits2Float(0x4276591a)); // 21.022f, 61.565f, 21.055f, 61.565f, 21.087f, 61.587f +path.cubicTo(SkBits2Float(0x41a90a46), SkBits2Float(0x427679df), SkBits2Float(0x41a93755), SkBits2Float(0x4276b12a), SkBits2Float(0x41a98f65), SkBits2Float(0x4276c6ab)); // 21.13f, 61.619f, 21.152f, 61.673f, 21.195f, 61.694f +path.cubicTo(SkBits2Float(0x41aadb2c), SkBits2Float(0x42774086), SkBits2Float(0x41ac958a), SkBits2Float(0x42778e59), SkBits2Float(0x41adb64e), SkBits2Float(0x427828f9)); // 21.357f, 61.813f, 21.573f, 61.889f, 21.714f, 62.04f +path.cubicTo(SkBits2Float(0x41adb64e), SkBits2Float(0x427828f9), SkBits2Float(0x41ad8940), SkBits2Float(0x42786045), SkBits2Float(0x41ad72b8), SkBits2Float(0x42786045)); // 21.714f, 62.04f, 21.692f, 62.094f, 21.681f, 62.094f +path.moveTo(SkBits2Float(0x41bd168f), SkBits2Float(0x4267be7a)); // 23.636f, 57.936f +path.cubicTo(SkBits2Float(0x41bd168f), SkBits2Float(0x42679caf), SkBits2Float(0x41bd2d16), SkBits2Float(0x4267666a), SkBits2Float(0x41bd168f), SkBits2Float(0x42674fe2)); // 23.636f, 57.903f, 23.647f, 57.85f, 23.636f, 57.828f +path.cubicTo(SkBits2Float(0x41bd168f), SkBits2Float(0x4267449e), SkBits2Float(0x41bd0008), SkBits2Float(0x42674fe2), SkBits2Float(0x41bce981), SkBits2Float(0x42672f1d)); // 23.636f, 57.817f, 23.625f, 57.828f, 23.614f, 57.796f +path.cubicTo(SkBits2Float(0x41bcd2fa), SkBits2Float(0x42672f1d), SkBits2Float(0x41bc9171), SkBits2Float(0x4267449e), SkBits2Float(0x41bc7ae9), SkBits2Float(0x42672f1d)); // 23.603f, 57.796f, 23.571f, 57.817f, 23.56f, 57.796f +path.cubicTo(SkBits2Float(0x41bb9dba), SkBits2Float(0x4267d500), SkBits2Float(0x41bbb441), SkBits2Float(0x42693648), SkBits2Float(0x41bb72b8), SkBits2Float(0x426a1377)); // 23.452f, 57.958f, 23.463f, 58.303f, 23.431f, 58.519f +path.cubicTo(SkBits2Float(0x41bb45aa), SkBits2Float(0x426a6c8e), SkBits2Float(0x41bb2f22), SkBits2Float(0x426acfe1), SkBits2Float(0x41bb189b), SkBits2Float(0x426b3335)); // 23.409f, 58.606f, 23.398f, 58.703f, 23.387f, 58.8f +path.lineTo(SkBits2Float(0x41baed99), SkBits2Float(0x426b5f3d)); // 23.366f, 58.843f +path.cubicTo(SkBits2Float(0x41baac10), SkBits2Float(0x426bd918), SkBits2Float(0x41bac08b), SkBits2Float(0x426c3129), SkBits2Float(0x41baac10), SkBits2Float(0x426cab04)); // 23.334f, 58.962f, 23.344f, 59.048f, 23.334f, 59.167f +path.cubicTo(SkBits2Float(0x41ba7f02), SkBits2Float(0x426d50e7), SkBits2Float(0x41ba3b6c), SkBits2Float(0x426e0d52), SkBits2Float(0x41ba106a), SkBits2Float(0x426ec9bc)); // 23.312f, 59.329f, 23.279f, 59.513f, 23.258f, 59.697f +path.cubicTo(SkBits2Float(0x41b9ccd4), SkBits2Float(0x426f645c), SkBits2Float(0x41b974c4), SkBits2Float(0x42701fc0), SkBits2Float(0x41b949c2), SkBits2Float(0x4270c5a3)); // 23.225f, 59.848f, 23.182f, 60.031f, 23.161f, 60.193f +path.cubicTo(SkBits2Float(0x41b9333b), SkBits2Float(0x42713f7e), SkBits2Float(0x41b98b4b), SkBits2Float(0x4271820d), SkBits2Float(0x41b9f9e3), SkBits2Float(0x4271ae16)); // 23.15f, 60.312f, 23.193f, 60.377f, 23.247f, 60.42f +path.cubicTo(SkBits2Float(0x41ba3b6c), SkBits2Float(0x42718d51), SkBits2Float(0x41ba7f02), SkBits2Float(0x4271b95a), SkBits2Float(0x41ba9589), SkBits2Float(0x42716b87)); // 23.279f, 60.388f, 23.312f, 60.431f, 23.323f, 60.355f +path.cubicTo(SkBits2Float(0x41baac10), SkBits2Float(0x4271343b), SkBits2Float(0x41ba9589), SkBits2Float(0x4270e76e), SkBits2Float(0x41ba9589), SkBits2Float(0x4270999b)); // 23.334f, 60.301f, 23.323f, 60.226f, 23.323f, 60.15f +path.cubicTo(SkBits2Float(0x41ba9589), SkBits2Float(0x4270418b), SkBits2Float(0x41bac08b), SkBits2Float(0x426fd1ed), SkBits2Float(0x41baed99), SkBits2Float(0x426f645c)); // 23.323f, 60.064f, 23.344f, 59.955f, 23.366f, 59.848f +path.cubicTo(SkBits2Float(0x41bb2f22), SkBits2Float(0x426e6562), SkBits2Float(0x41bb9dba), SkBits2Float(0x426d3b66), SkBits2Float(0x41bbf5ca), SkBits2Float(0x426c3c6c)); // 23.398f, 59.599f, 23.452f, 59.308f, 23.495f, 59.059f +path.cubicTo(SkBits2Float(0x41bc0e5d), SkBits2Float(0x426bb853), SkBits2Float(0x41bc0e5d), SkBits2Float(0x426b5f3d), SkBits2Float(0x41bc22d8), SkBits2Float(0x426ae562)); // 23.507f, 58.93f, 23.507f, 58.843f, 23.517f, 58.724f +path.cubicTo(SkBits2Float(0x41bc395f), SkBits2Float(0x426a820e), SkBits2Float(0x41bc9170), SkBits2Float(0x4269f2b2), SkBits2Float(0x41bca7f7), SkBits2Float(0x42698f5e)); // 23.528f, 58.627f, 23.571f, 58.487f, 23.582f, 58.39f +path.cubicTo(SkBits2Float(0x41bcd2f9), SkBits2Float(0x426920c6), SkBits2Float(0x41bca7f7), SkBits2Float(0x4268d2f4), SkBits2Float(0x41bcd2f9), SkBits2Float(0x4268645c)); // 23.603f, 58.282f, 23.582f, 58.206f, 23.603f, 58.098f +path.cubicTo(SkBits2Float(0x41bcd2f9), SkBits2Float(0x42684291), SkBits2Float(0x41bd168f), SkBits2Float(0x4267df3d), SkBits2Float(0x41bd168f), SkBits2Float(0x4267be79)); // 23.603f, 58.065f, 23.636f, 57.968f, 23.636f, 57.936f +path.moveTo(SkBits2Float(0x41bd6e9f), SkBits2Float(0x426e916b)); // 23.679f, 59.642f +path.cubicTo(SkBits2Float(0x41bdb028), SkBits2Float(0x426d199c), SkBits2Float(0x41bdf3be), SkBits2Float(0x426bb854), SkBits2Float(0x41be6255), SkBits2Float(0x426a343c)); // 23.711f, 59.275f, 23.744f, 58.93f, 23.798f, 58.551f +path.cubicTo(SkBits2Float(0x41be78dc), SkBits2Float(0x4269f2b3), SkBits2Float(0x41bed0ed), SkBits2Float(0x4269841b), SkBits2Float(0x41bed0ed), SkBits2Float(0x4269418c)); // 23.809f, 58.487f, 23.852f, 58.379f, 23.852f, 58.314f +path.cubicTo(SkBits2Float(0x41bee774), SkBits2Float(0x4268bc6d), SkBits2Float(0x41bee774), SkBits2Float(0x42684edc), SkBits2Float(0x41bf1276), SkBits2Float(0x4267df3e)); // 23.863f, 58.184f, 23.863f, 58.077f, 23.884f, 57.968f +path.cubicTo(SkBits2Float(0x41bf3f84), SkBits2Float(0x4267a7f2), SkBits2Float(0x41bf3f84), SkBits2Float(0x4267872e), SkBits2Float(0x41bf9795), SkBits2Float(0x426770a6)); // 23.906f, 57.914f, 23.906f, 57.882f, 23.949f, 57.86f +path.cubicTo(SkBits2Float(0x41c0ccd4), SkBits2Float(0x42675b25), SkBits2Float(0x41c6810e), SkBits2Float(0x4268d2f4), SkBits2Float(0x41c6d91e), SkBits2Float(0x426920c7)); // 24.1f, 57.839f, 24.813f, 58.206f, 24.856f, 58.282f +path.cubicTo(SkBits2Float(0x41c7333b), SkBits2Float(0x42696d94), SkBits2Float(0x41c7062c), SkBits2Float(0x4270e76f), SkBits2Float(0x41c6ae1c), SkBits2Float(0x42713f7f)); // 24.9f, 58.357f, 24.878f, 60.226f, 24.835f, 60.312f +path.cubicTo(SkBits2Float(0x41c63f84), SkBits2Float(0x4271a2d3), SkBits2Float(0x41c3a7f7), SkBits2Float(0x42716b87), SkBits2Float(0x41c2cac8), SkBits2Float(0x427176cb)); // 24.781f, 60.409f, 24.457f, 60.355f, 24.349f, 60.366f +path.cubicTo(SkBits2Float(0x41c2b441), SkBits2Float(0x427176cb), SkBits2Float(0x41c270ab), SkBits2Float(0x4271a2d3), SkBits2Float(0x41c245a9), SkBits2Float(0x4271a2d3)); // 24.338f, 60.366f, 24.305f, 60.409f, 24.284f, 60.409f +path.cubicTo(SkBits2Float(0x41c1aa03), SkBits2Float(0x4271b95a), SkBits2Float(0x41c1106a), SkBits2Float(0x4271ae17), SkBits2Float(0x41c05e3c), SkBits2Float(0x4271b95a)); // 24.208f, 60.431f, 24.133f, 60.42f, 24.046f, 60.431f +path.cubicTo(SkBits2Float(0x41bf1275), SkBits2Float(0x4271e562), SkBits2Float(0x41be4bcd), SkBits2Float(0x427227f2), SkBits2Float(0x41bcd2f8), SkBits2Float(0x4272322f)); // 23.884f, 60.474f, 23.787f, 60.539f, 23.603f, 60.549f +path.cubicTo(SkBits2Float(0x41bc395e), SkBits2Float(0x427128f8), SkBits2Float(0x41bd2d15), SkBits2Float(0x426f8f5e), SkBits2Float(0x41bd6e9e), SkBits2Float(0x426e916a)); // 23.528f, 60.29f, 23.647f, 59.89f, 23.679f, 59.642f +path.moveTo(SkBits2Float(0x41d21481), SkBits2Float(0x42700a3f)); // 26.26f, 60.01f +path.cubicTo(SkBits2Float(0x41d22b08), SkBits2Float(0x42704cce), SkBits2Float(0x41d299a0), SkBits2Float(0x4270f1ac), SkBits2Float(0x41d2418f), SkBits2Float(0x42713f7e)); // 26.271f, 60.075f, 26.325f, 60.236f, 26.282f, 60.312f +path.cubicTo(SkBits2Float(0x41d2418f), SkBits2Float(0x42714ac2), SkBits2Float(0x41d22b08), SkBits2Float(0x42713f7e), SkBits2Float(0x41d21481), SkBits2Float(0x42715605)); // 26.282f, 60.323f, 26.271f, 60.312f, 26.26f, 60.334f +path.cubicTo(SkBits2Float(0x41d1bc71), SkBits2Float(0x42715605), SkBits2Float(0x41d1916f), SkBits2Float(0x42715605), SkBits2Float(0x41d1395e), SkBits2Float(0x42714ac1)); // 26.217f, 60.334f, 26.196f, 60.334f, 26.153f, 60.323f +path.cubicTo(SkBits2Float(0x41d0b233), SkBits2Float(0x42708419), SkBits2Float(0x41d0c8ba), SkBits2Float(0x426f645b), SkBits2Float(0x41d09db8), SkBits2Float(0x426e5a1d)); // 26.087f, 60.129f, 26.098f, 59.848f, 26.077f, 59.588f +path.cubicTo(SkBits2Float(0x41d09db8), SkBits2Float(0x426e23d7), SkBits2Float(0x41d05a22), SkBits2Float(0x426d9375), SkBits2Float(0x41d070aa), SkBits2Float(0x426d50e6)); // 26.077f, 59.535f, 26.044f, 59.394f, 26.055f, 59.329f +path.cubicTo(SkBits2Float(0x41d09db8), SkBits2Float(0x426d3b65), SkBits2Float(0x41d0b233), SkBits2Float(0x426d50e6), SkBits2Float(0x41d0b233), SkBits2Float(0x426d2f1b)); // 26.077f, 59.308f, 26.087f, 59.329f, 26.087f, 59.296f +path.cubicTo(SkBits2Float(0x41d1395e), SkBits2Float(0x426d3b65), SkBits2Float(0x41d14dd9), SkBits2Float(0x426d2f1b), SkBits2Float(0x41d1916e), SkBits2Float(0x426d50e6)); // 26.153f, 59.308f, 26.163f, 59.296f, 26.196f, 59.329f +path.cubicTo(SkBits2Float(0x41d1a5e9), SkBits2Float(0x426d50e6), SkBits2Float(0x41d1e97e), SkBits2Float(0x426de148), SkBits2Float(0x41d1e97e), SkBits2Float(0x426dec8c)); // 26.206f, 59.329f, 26.239f, 59.47f, 26.239f, 59.481f +path.cubicTo(SkBits2Float(0x41d22b07), SkBits2Float(0x426e9cad), SkBits2Float(0x41d1e97e), SkBits2Float(0x426f4dd4), SkBits2Float(0x41d21480), SkBits2Float(0x42700a3e)); // 26.271f, 59.653f, 26.239f, 59.826f, 26.26f, 60.01f +path.moveTo(SkBits2Float(0x41ee1274), SkBits2Float(0x42564ac1)); // 29.759f, 53.573f +path.cubicTo(SkBits2Float(0x41ee1274), SkBits2Float(0x42566b86), SkBits2Float(0x41ee3f82), SkBits2Float(0x4256c49c), SkBits2Float(0x41ee28fb), SkBits2Float(0x4256fbe8)); // 29.759f, 53.605f, 29.781f, 53.692f, 29.77f, 53.746f +path.cubicTo(SkBits2Float(0x41ee28fb), SkBits2Float(0x42571cad), SkBits2Float(0x41ede772), SkBits2Float(0x425748b5), SkBits2Float(0x41ede772), SkBits2Float(0x42576a80)); // 29.77f, 53.778f, 29.738f, 53.821f, 29.738f, 53.854f +path.cubicTo(SkBits2Float(0x41ed8f62), SkBits2Float(0x425774bd), SkBits2Float(0x41ed20ca), SkBits2Float(0x42579688), SkBits2Float(0x41ec6e9d), SkBits2Float(0x42579688)); // 29.695f, 53.864f, 29.641f, 53.897f, 29.554f, 53.897f +path.cubicTo(SkBits2Float(0x41ebeb8b), SkBits2Float(0x42579688), SkBits2Float(0x41eb666c), SkBits2Float(0x425774bd), SkBits2Float(0x41eaf7d4), SkBits2Float(0x42576a80)); // 29.49f, 53.897f, 29.425f, 53.864f, 29.371f, 53.854f +path.cubicTo(SkBits2Float(0x41eacac6), SkBits2Float(0x425676ca), SkBits2Float(0x41eb666c), SkBits2Float(0x42556d92), SkBits2Float(0x41ebbe7c), SkBits2Float(0x42549063)); // 29.349f, 53.616f, 29.425f, 53.357f, 29.468f, 53.141f +path.cubicTo(SkBits2Float(0x41ebd503), SkBits2Float(0x425421cb), SkBits2Float(0x41ebd503), SkBits2Float(0x4253d3f9), SkBits2Float(0x41ec0005), SkBits2Float(0x42537be8)); // 29.479f, 53.033f, 29.479f, 52.957f, 29.5f, 52.871f +path.cubicTo(SkBits2Float(0x41ec2d13), SkBits2Float(0x42535a1d), SkBits2Float(0x41ec6e9d), SkBits2Float(0x42531894), SkBits2Float(0x41ecb232), SkBits2Float(0x42531894)); // 29.522f, 52.838f, 29.554f, 52.774f, 29.587f, 52.774f +path.cubicTo(SkBits2Float(0x41ed3544), SkBits2Float(0x4253020d), SkBits2Float(0x41edd0ea), SkBits2Float(0x42531894), SkBits2Float(0x41ede771), SkBits2Float(0x4253449c)); // 29.651f, 52.752f, 29.727f, 52.774f, 29.738f, 52.817f +path.cubicTo(SkBits2Float(0x41ee1273), SkBits2Float(0x42534fe0), SkBits2Float(0x41ede771), SkBits2Float(0x42536561), SkBits2Float(0x41ede771), SkBits2Float(0x42537be8)); // 29.759f, 52.828f, 29.738f, 52.849f, 29.738f, 52.871f +path.cubicTo(SkBits2Float(0x41ee3f81), SkBits2Float(0x42544290), SkBits2Float(0x41ede771), SkBits2Float(0x42554ccd), SkBits2Float(0x41ee1273), SkBits2Float(0x42564ac1)); // 29.781f, 53.065f, 29.738f, 53.325f, 29.759f, 53.573f +path.moveTo(SkBits2Float(0x41f51273), SkBits2Float(0x4258cbc7)); // 30.634f, 54.199f +path.cubicTo(SkBits2Float(0x41f4e771), SkBits2Float(0x4259199a), SkBits2Float(0x41f3b025), SkBits2Float(0x4259bf7d), SkBits2Float(0x41f35815), SkBits2Float(0x4259eb85)); // 30.613f, 54.275f, 30.461f, 54.437f, 30.418f, 54.48f +path.cubicTo(SkBits2Float(0x41f2395d), SkBits2Float(0x425aa6e9), SkBits2Float(0x41f2395d), SkBits2Float(0x425a449c), SkBits2Float(0x41f222d6), SkBits2Float(0x42596666)); // 30.278f, 54.663f, 30.278f, 54.567f, 30.267f, 54.35f +path.cubicTo(SkBits2Float(0x41f222d6), SkBits2Float(0x425945a1), SkBits2Float(0x41f1f5c8), SkBits2Float(0x4258e24d), SkBits2Float(0x41f222d6), SkBits2Float(0x4258ab02)); // 30.267f, 54.318f, 30.245f, 54.221f, 30.267f, 54.167f +path.cubicTo(SkBits2Float(0x41f2395d), SkBits2Float(0x42589fbe), SkBits2Float(0x41f2e97e), SkBits2Float(0x42588a3d), SkBits2Float(0x41f30005), SkBits2Float(0x425873b6)); // 30.278f, 54.156f, 30.364f, 54.135f, 30.375f, 54.113f +path.cubicTo(SkBits2Float(0x41f3b026), SkBits2Float(0x42586872), SkBits2Float(0x41f48d55), SkBits2Float(0x42588937), SkBits2Float(0x41f51274), SkBits2Float(0x4258947b)); // 30.461f, 54.102f, 30.569f, 54.134f, 30.634f, 54.145f +path.cubicTo(SkBits2Float(0x41f4fdf9), SkBits2Float(0x42589fbf), SkBits2Float(0x41f51274), SkBits2Float(0x4258b646), SkBits2Float(0x41f51274), SkBits2Float(0x4258cbc7)); // 30.624f, 54.156f, 30.634f, 54.178f, 30.634f, 54.199f +path.moveTo(SkBits2Float(0x41f20e5b), SkBits2Float(0x425727f0)); // 30.257f, 53.789f +path.cubicTo(SkBits2Float(0x41f1cac5), SkBits2Float(0x4256da1d), SkBits2Float(0x41f222d6), SkBits2Float(0x42561375), SkBits2Float(0x41f222d6), SkBits2Float(0x4255d0e6)); // 30.224f, 53.713f, 30.267f, 53.519f, 30.267f, 53.454f +path.cubicTo(SkBits2Float(0x41f222d6), SkBits2Float(0x42553646), SkBits2Float(0x41f1b43e), SkBits2Float(0x4254374c), SkBits2Float(0x41f20e5b), SkBits2Float(0x42539169)); // 30.267f, 53.303f, 30.213f, 53.054f, 30.257f, 52.892f +path.cubicTo(SkBits2Float(0x41f222d6), SkBits2Float(0x42536561), SkBits2Float(0x41f2916d), SkBits2Float(0x4253449c), SkBits2Float(0x41f2be7c), SkBits2Float(0x4253449c)); // 30.267f, 52.849f, 30.321f, 52.817f, 30.343f, 52.817f +path.cubicTo(SkBits2Float(0x41f3b026), SkBits2Float(0x42532e15), SkBits2Float(0x41f845a7), SkBits2Float(0x42539cac), SkBits2Float(0x41f88730), SkBits2Float(0x4253d3f8)); // 30.461f, 52.795f, 31.034f, 52.903f, 31.066f, 52.957f +path.cubicTo(SkBits2Float(0x41f8cac6), SkBits2Float(0x42540000), SkBits2Float(0x41f8cac6), SkBits2Float(0x42544290), SkBits2Float(0x41f8e14d), SkBits2Float(0x4254851f)); // 31.099f, 53, 31.099f, 53.065f, 31.11f, 53.13f +path.cubicTo(SkBits2Float(0x41f8f5c8), SkBits2Float(0x4254d1ec), SkBits2Float(0x41f97ae7), SkBits2Float(0x425578d5), SkBits2Float(0x41f9666c), SkBits2Float(0x4255e76d)); // 31.12f, 53.205f, 31.185f, 53.368f, 31.175f, 53.476f +path.cubicTo(SkBits2Float(0x41f94dd9), SkBits2Float(0x42561375), SkBits2Float(0x41f88731), SkBits2Float(0x4256a2d1), SkBits2Float(0x41f85c2f), SkBits2Float(0x4256c49c)); // 31.163f, 53.519f, 31.066f, 53.659f, 31.045f, 53.692f +path.cubicTo(SkBits2Float(0x41f845a8), SkBits2Float(0x4256da1d), SkBits2Float(0x41f7d710), SkBits2Float(0x4256f0a4), SkBits2Float(0x41f7d710), SkBits2Float(0x4256fbe8)); // 31.034f, 53.713f, 30.98f, 53.735f, 30.98f, 53.746f +path.lineTo(SkBits2Float(0x41f7d710), SkBits2Float(0x42571cad)); // 30.98f, 53.778f +path.cubicTo(SkBits2Float(0x41f79587), SkBits2Float(0x4257322e), SkBits2Float(0x41f73b6a), SkBits2Float(0x425748b5), SkBits2Float(0x41f6f9e1), SkBits2Float(0x42575f3c)); // 30.948f, 53.799f, 30.904f, 53.821f, 30.872f, 53.843f +path.cubicTo(SkBits2Float(0x41f6062b), SkBits2Float(0x425774bd), SkBits2Float(0x41f2395e), SkBits2Float(0x425774bd), SkBits2Float(0x41f20e5c), SkBits2Float(0x425727f0)); // 30.753f, 53.864f, 30.278f, 53.864f, 30.257f, 53.789f +path.moveTo(SkBits2Float(0x42048f5f), SkBits2Float(0x426b072b)); // 33.14f, 58.757f +path.cubicTo(SkBits2Float(0x42046d94), SkBits2Float(0x426acfdf), SkBits2Float(0x42048f5f), SkBits2Float(0x426ab958), SkBits2Float(0x420478d8), SkBits2Float(0x426a77cf)); // 33.107f, 58.703f, 33.14f, 58.681f, 33.118f, 58.617f +path.cubicTo(SkBits2Float(0x42045813), SkBits2Float(0x4269d0e6), SkBits2Float(0x42042c0b), SkBits2Float(0x42693646), SkBits2Float(0x42041584), SkBits2Float(0x4268851f)); // 33.086f, 58.454f, 33.043f, 58.303f, 33.021f, 58.13f +path.cubicTo(SkBits2Float(0x4203e97c), SkBits2Float(0x4267c9bb), SkBits2Float(0x42039caf), SkBits2Float(0x42670d50), SkBits2Float(0x4203a6ec), SkBits2Float(0x426624dd)); // 32.978f, 57.947f, 32.903f, 57.763f, 32.913f, 57.536f +path.cubicTo(SkBits2Float(0x4203a6ec), SkBits2Float(0x426624dd), SkBits2Float(0x4203de38), SkBits2Float(0x4265f8d5), SkBits2Float(0x4203e97b), SkBits2Float(0x4265f8d5)); // 32.913f, 57.536f, 32.967f, 57.493f, 32.978f, 57.493f +path.cubicTo(SkBits2Float(0x42042c0a), SkBits2Float(0x4265ee98), SkBits2Float(0x4204c6aa), SkBits2Float(0x4266199a), SkBits2Float(0x4204e875), SkBits2Float(0x42663b64)); // 33.043f, 57.483f, 33.194f, 57.525f, 33.227f, 57.558f +path.cubicTo(SkBits2Float(0x42051ebb), SkBits2Float(0x42668937), SkBits2Float(0x42051ebb), SkBits2Float(0x42671893), SkBits2Float(0x42054085), SkBits2Float(0x426770a3)); // 33.28f, 57.634f, 33.28f, 57.774f, 33.313f, 57.86f +path.cubicTo(SkBits2Float(0x42058314), SkBits2Float(0x4268a6e9), SkBits2Float(0x4206072d), SkBits2Float(0x4269d0e5), SkBits2Float(0x42061271), SkBits2Float(0x426b3e76)); // 33.378f, 58.163f, 33.507f, 58.454f, 33.518f, 58.811f +path.cubicTo(SkBits2Float(0x4205e669), SkBits2Float(0x426b3e76), SkBits2Float(0x4205e669), SkBits2Float(0x426b49ba), SkBits2Float(0x4205b95a), SkBits2Float(0x426b5f3b)); // 33.475f, 58.811f, 33.475f, 58.822f, 33.431f, 58.843f +path.cubicTo(SkBits2Float(0x42056c8d), SkBits2Float(0x426b5f3b), SkBits2Float(0x4204e875), SkBits2Float(0x426b75c2), SkBits2Float(0x4204b023), SkBits2Float(0x426b49ba)); // 33.356f, 58.843f, 33.227f, 58.865f, 33.172f, 58.822f +path.lineTo(SkBits2Float(0x4204b023), SkBits2Float(0x426b3333)); // 33.172f, 58.8f +path.cubicTo(SkBits2Float(0x4204b023), SkBits2Float(0x426b27ef), SkBits2Float(0x42048f5e), SkBits2Float(0x426b072b), SkBits2Float(0x42048f5e), SkBits2Float(0x426b072b)); // 33.172f, 58.789f, 33.14f, 58.757f, 33.14f, 58.757f +path.moveTo(SkBits2Float(0x42035918), SkBits2Float(0x426b6a7f)); // 32.837f, 58.854f +path.cubicTo(SkBits2Float(0x42032d10), SkBits2Float(0x426b6a7f), SkBits2Float(0x42030108), SkBits2Float(0x426b75c3), SkBits2Float(0x4202d4ff), SkBits2Float(0x426b75c3)); // 32.794f, 58.854f, 32.751f, 58.865f, 32.708f, 58.865f +path.cubicTo(SkBits2Float(0x42026667), SkBits2Float(0x426b75c3), SkBits2Float(0x42020d51), SkBits2Float(0x426b5f3c), SkBits2Float(0x4201ec8c), SkBits2Float(0x426b27f0)); // 32.6f, 58.865f, 32.513f, 58.843f, 32.481f, 58.789f +path.cubicTo(SkBits2Float(0x4201cbc7), SkBits2Float(0x426ae561), SkBits2Float(0x4201cbc7), SkBits2Float(0x426a6c8c), SkBits2Float(0x4201b540), SkBits2Float(0x426a0832)); // 32.449f, 58.724f, 32.449f, 58.606f, 32.427f, 58.508f +path.cubicTo(SkBits2Float(0x42018938), SkBits2Float(0x426920c5), SkBits2Float(0x42016873), SkBits2Float(0x42683853), SkBits2Float(0x42013021), SkBits2Float(0x42672f1b)); // 32.384f, 58.282f, 32.352f, 58.055f, 32.297f, 57.796f +path.cubicTo(SkBits2Float(0x42013021), SkBits2Float(0x4267020d), SkBits2Float(0x4200f9db), SkBits2Float(0x42669375), SkBits2Float(0x4200f9db), SkBits2Float(0x426651ec)); // 32.297f, 57.752f, 32.244f, 57.644f, 32.244f, 57.58f +path.cubicTo(SkBits2Float(0x42010418), SkBits2Float(0x4266199a), SkBits2Float(0x420151eb), SkBits2Float(0x4265ee98), SkBits2Float(0x42018937), SkBits2Float(0x4265ee98)); // 32.254f, 57.525f, 32.33f, 57.483f, 32.384f, 57.483f +path.cubicTo(SkBits2Float(0x4201e147), SkBits2Float(0x4265e24e), SkBits2Float(0x42022f1a), SkBits2Float(0x4265ee98), SkBits2Float(0x42023a5e), SkBits2Float(0x4266199a)); // 32.47f, 57.471f, 32.546f, 57.483f, 32.557f, 57.525f +path.cubicTo(SkBits2Float(0x420271aa), SkBits2Float(0x42665c29), SkBits2Float(0x42027be7), SkBits2Float(0x42670d50), SkBits2Float(0x42029db2), SkBits2Float(0x426770a4)); // 32.611f, 57.59f, 32.621f, 57.763f, 32.654f, 57.86f +path.cubicTo(SkBits2Float(0x42029db2), SkBits2Float(0x4267be77), SkBits2Float(0x4202d4fe), SkBits2Float(0x4268178d), SkBits2Float(0x4202e041), SkBits2Float(0x42684ed9)); // 32.654f, 57.936f, 32.708f, 58.023f, 32.719f, 58.077f +path.cubicTo(SkBits2Float(0x4202ea7e), SkBits2Float(0x4268bc6a), SkBits2Float(0x4202ea7e), SkBits2Float(0x4268fefa), SkBits2Float(0x42030106), SkBits2Float(0x42695810)); // 32.729f, 58.184f, 32.729f, 58.249f, 32.751f, 58.336f +path.cubicTo(SkBits2Float(0x420322d1), SkBits2Float(0x4269fced), SkBits2Float(0x4203645a), SkBits2Float(0x426a820c), SkBits2Float(0x4203645a), SkBits2Float(0x426b49ba)); // 32.784f, 58.497f, 32.848f, 58.627f, 32.848f, 58.822f +path.cubicTo(SkBits2Float(0x42034395), SkBits2Float(0x426b49ba), SkBits2Float(0x42035916), SkBits2Float(0x426b49ba), SkBits2Float(0x42035916), SkBits2Float(0x426b6a7f)); // 32.816f, 58.822f, 32.837f, 58.822f, 32.837f, 58.854f +path.moveTo(SkBits2Float(0x42009580), SkBits2Float(0x426b6a7f)); // 32.146f, 58.854f +path.lineTo(SkBits2Float(0x42008b43), SkBits2Float(0x426b8106)); // 32.136f, 58.876f +path.cubicTo(SkBits2Float(0x42007fff), SkBits2Float(0x426b8106), SkBits2Float(0x42005e35), SkBits2Float(0x426b75c2), SkBits2Float(0x420048b4), SkBits2Float(0x426b8106)); // 32.125f, 58.876f, 32.092f, 58.865f, 32.071f, 58.876f +path.cubicTo(SkBits2Float(0x41fdcccc), SkBits2Float(0x426bad0e), SkBits2Float(0x41f94dd2), SkBits2Float(0x426b8c4a), SkBits2Float(0x41f6cccc), SkBits2Float(0x426b8c4a)); // 31.725f, 58.919f, 31.163f, 58.887f, 30.85f, 58.887f +path.cubicTo(SkBits2Float(0x41f65e34), SkBits2Float(0x426b8106), SkBits2Float(0x41f39ba5), SkBits2Float(0x426b8106), SkBits2Float(0x41f35810), SkBits2Float(0x426b49bb)); // 30.796f, 58.876f, 30.451f, 58.876f, 30.418f, 58.822f +path.cubicTo(SkBits2Float(0x41f35810), SkBits2Float(0x426b3334), SkBits2Float(0x41f2e978), SkBits2Float(0x4267926f), SkBits2Float(0x41f31687), SkBits2Float(0x426723d8)); // 30.418f, 58.8f, 30.364f, 57.893f, 30.386f, 57.785f +path.lineTo(SkBits2Float(0x41f36e97), SkBits2Float(0x4266ec8c)); // 30.429f, 57.731f +path.cubicTo(SkBits2Float(0x41f3f3b6), SkBits2Float(0x4266b540), SkBits2Float(0x41f4d0e5), SkBits2Float(0x4266b540), SkBits2Float(0x41f58106), SkBits2Float(0x42669eb9)); // 30.494f, 57.677f, 30.602f, 57.677f, 30.688f, 57.655f +path.cubicTo(SkBits2Float(0x41f7ed91), SkBits2Float(0x42663b65), SkBits2Float(0x41fac6a8), SkBits2Float(0x4265ee98), SkBits2Float(0x41fdb646), SkBits2Float(0x4265d811)); // 30.991f, 57.558f, 31.347f, 57.483f, 31.714f, 57.461f +path.cubicTo(SkBits2Float(0x41fe51ec), SkBits2Float(0x4265c18a), SkBits2Float(0x41ff2f1b), SkBits2Float(0x4265d811), SkBits2Float(0x41ff872b), SkBits2Float(0x4265f8d6)); // 31.79f, 57.439f, 31.898f, 57.461f, 31.941f, 57.493f +path.cubicTo(SkBits2Float(0x41ffb439), SkBits2Float(0x4266199b), SkBits2Float(0x41ffb439), SkBits2Float(0x42669eb9), SkBits2Float(0x41ffdf3b), SkBits2Float(0x4266d605)); // 31.963f, 57.525f, 31.963f, 57.655f, 31.984f, 57.709f +path.cubicTo(SkBits2Float(0x41fff5c2), SkBits2Float(0x42670d51), SkBits2Float(0x42001cac), SkBits2Float(0x42675b24), SkBits2Float(0x42001cac), SkBits2Float(0x4267926f)); // 31.995f, 57.763f, 32.028f, 57.839f, 32.028f, 57.893f +path.cubicTo(SkBits2Float(0x42003d71), SkBits2Float(0x42684290), SkBits2Float(0x420048b4), SkBits2Float(0x4268c7ae), SkBits2Float(0x42005e35), SkBits2Float(0x42696d92)); // 32.06f, 58.065f, 32.071f, 58.195f, 32.092f, 58.357f +path.cubicTo(SkBits2Float(0x42008000), SkBits2Float(0x4269d0e6), SkBits2Float(0x4200ac08), SkBits2Float(0x426a5605), SkBits2Float(0x4200b74c), SkBits2Float(0x426acfe0)); // 32.125f, 58.454f, 32.168f, 58.584f, 32.179f, 58.703f +path.cubicTo(SkBits2Float(0x4200c189), SkBits2Float(0x426b072c), SkBits2Float(0x4200b74c), SkBits2Float(0x426b49bb), SkBits2Float(0x42009581), SkBits2Float(0x426b6a80)); // 32.189f, 58.757f, 32.179f, 58.822f, 32.146f, 58.854f +path.moveTo(SkBits2Float(0x41eeae14), SkBits2Float(0x426bef9f)); // 29.835f, 58.984f +path.cubicTo(SkBits2Float(0x41ee8312), SkBits2Float(0x426c26eb), SkBits2Float(0x41ed353f), SkBits2Float(0x426c52f3), SkBits2Float(0x41ecc8b4), SkBits2Float(0x426c73b8)); // 29.814f, 59.038f, 29.651f, 59.081f, 29.598f, 59.113f +path.cubicTo(SkBits2Float(0x41eb7ae1), SkBits2Float(0x426cd70c), SkBits2Float(0x41ea3127), SkBits2Float(0x426d9376), SkBits2Float(0x41e96872), SkBits2Float(0x426e2e16)); // 29.435f, 59.21f, 29.274f, 59.394f, 29.176f, 59.545f +path.cubicTo(SkBits2Float(0x41e88b43), SkBits2Float(0x426ed3f9), SkBits2Float(0x41e7c49b), SkBits2Float(0x426fdd31), SkBits2Float(0x41e6a5e3), SkBits2Float(0x4270570c)); // 29.068f, 59.707f, 28.971f, 59.966f, 28.831f, 60.085f +path.cubicTo(SkBits2Float(0x41e678d5), SkBits2Float(0x427078d7), SkBits2Float(0x41e6624d), SkBits2Float(0x42706d93), SkBits2Float(0x41e620c4), SkBits2Float(0x427078d7)); // 28.809f, 60.118f, 28.798f, 60.107f, 28.766f, 60.118f +path.cubicTo(SkBits2Float(0x41e60a3d), SkBits2Float(0x4270841b), SkBits2Float(0x41e5f3b6), SkBits2Float(0x4270999c), SkBits2Float(0x41e5f3b6), SkBits2Float(0x4270999c)); // 28.755f, 60.129f, 28.744f, 60.15f, 28.744f, 60.15f +path.cubicTo(SkBits2Float(0x41e52d0e), SkBits2Float(0x4270d0e8), SkBits2Float(0x41e49374), SkBits2Float(0x4270e76f), SkBits2Float(0x41e39fbe), SkBits2Float(0x4270fcf0)); // 28.647f, 60.204f, 28.572f, 60.226f, 28.453f, 60.247f +path.cubicTo(SkBits2Float(0x41e2c28f), SkBits2Float(0x42711377), SkBits2Float(0x41e1a1ca), SkBits2Float(0x42714ac3), SkBits2Float(0x41e03f7c), SkBits2Float(0x4271343c)); // 28.345f, 60.269f, 28.204f, 60.323f, 28.031f, 60.301f +path.cubicTo(SkBits2Float(0x41de2d0d), SkBits2Float(0x42711377), SkBits2Float(0x41e0c49b), SkBits2Float(0x426e9caf), SkBits2Float(0x41e149b9), SkBits2Float(0x426e23da)); // 27.772f, 60.269f, 28.096f, 59.653f, 28.161f, 59.535f +path.cubicTo(SkBits2Float(0x41e23d6f), SkBits2Float(0x426d2f1e), SkBits2Float(0x41e38936), SkBits2Float(0x426c52f5), SkBits2Float(0x41e4eb84), SkBits2Float(0x426b8109)); // 28.28f, 59.296f, 28.442f, 59.081f, 28.615f, 58.876f +path.cubicTo(SkBits2Float(0x41e55a1c), SkBits2Float(0x426b49bd), SkBits2Float(0x41e5dd2e), SkBits2Float(0x426b1caf), SkBits2Float(0x41e6624d), SkBits2Float(0x426ae563)); // 28.669f, 58.822f, 28.733f, 58.778f, 28.798f, 58.724f +path.cubicTo(SkBits2Float(0x41e78312), SkBits2Float(0x426a77d2), SkBits2Float(0x41e88b43), SkBits2Float(0x4269fcf0), SkBits2Float(0x41e99580), SkBits2Float(0x42698f5f)); // 28.939f, 58.617f, 29.068f, 58.497f, 29.198f, 58.39f +path.cubicTo(SkBits2Float(0x41ea3126), SkBits2Float(0x42695813), SkBits2Float(0x41edd0e4), SkBits2Float(0x4267a7f2), SkBits2Float(0x41eeae13), SkBits2Float(0x42684292)); // 29.274f, 58.336f, 29.727f, 57.914f, 29.835f, 58.065f +path.cubicTo(SkBits2Float(0x41eeae13), SkBits2Float(0x42684292), SkBits2Float(0x41eec49a), SkBits2Float(0x42684edc), SkBits2Float(0x41eec49a), SkBits2Float(0x42685919)); // 29.835f, 58.065f, 29.846f, 58.077f, 29.846f, 58.087f +path.cubicTo(SkBits2Float(0x41ef0623), SkBits2Float(0x4268a6ec), SkBits2Float(0x41eedb21), SkBits2Float(0x426bb854), SkBits2Float(0x41eeae13), SkBits2Float(0x426befa0)); // 29.878f, 58.163f, 29.857f, 58.93f, 29.835f, 58.984f +path.moveTo(SkBits2Float(0x41eaf7cd), SkBits2Float(0x4258947d)); // 29.371f, 54.145f +path.cubicTo(SkBits2Float(0x41ebd4fc), SkBits2Float(0x425873b8), SkBits2Float(0x41ed353e), SkBits2Float(0x42589fc1), SkBits2Float(0x41edba5c), SkBits2Float(0x4258ab04)); // 29.479f, 54.113f, 29.651f, 54.156f, 29.716f, 54.167f +path.cubicTo(SkBits2Float(0x41ede76a), SkBits2Float(0x4259c9bc), SkBits2Float(0x41ee3f7b), SkBits2Float(0x425b6e9a), SkBits2Float(0x41ee126c), SkBits2Float(0x425c8314)); // 29.738f, 54.447f, 29.781f, 54.858f, 29.759f, 55.128f +path.cubicTo(SkBits2Float(0x41ede76a), SkBits2Float(0x425d343b), SkBits2Float(0x41ee5602), SkBits2Float(0x425dda1e), SkBits2Float(0x41edd0e3), SkBits2Float(0x425e74be)); // 29.738f, 55.301f, 29.792f, 55.463f, 29.727f, 55.614f +path.cubicTo(SkBits2Float(0x41ed624b), SkBits2Float(0x425f1aa1), SkBits2Float(0x41ec6e95), SkBits2Float(0x425f947c), SkBits2Float(0x41ebd4fc), SkBits2Float(0x426023d9)); // 29.673f, 55.776f, 29.554f, 55.895f, 29.479f, 56.035f +path.cubicTo(SkBits2Float(0x41eb22cf), SkBits2Float(0x4260c9bc), SkBits2Float(0x41ea5c27), SkBits2Float(0x4261645c), SkBits2Float(0x41e9957f), SkBits2Float(0x42621583)); // 29.392f, 56.197f, 29.295f, 56.348f, 29.198f, 56.521f +path.cubicTo(SkBits2Float(0x41e8e55e), SkBits2Float(0x4262c6aa), SkBits2Float(0x41e849b8), SkBits2Float(0x42638314), SkBits2Float(0x41e78310), SkBits2Float(0x426427f2)); // 29.112f, 56.694f, 29.036f, 56.878f, 28.939f, 57.039f +path.cubicTo(SkBits2Float(0x41e72b00), SkBits2Float(0x42646b88), SkBits2Float(0x41e6e76a), SkBits2Float(0x4264b854), SkBits2Float(0x41e68f5a), SkBits2Float(0x4264efa0)); // 28.896f, 57.105f, 28.863f, 57.18f, 28.82f, 57.234f +path.cubicTo(SkBits2Float(0x41e6624c), SkBits2Float(0x42651ba8), SkBits2Float(0x41e60a3b), SkBits2Float(0x4265322f), SkBits2Float(0x41e5dd2d), SkBits2Float(0x426552f4)); // 28.798f, 57.277f, 28.755f, 57.299f, 28.733f, 57.331f +path.cubicTo(SkBits2Float(0x41e570a2), SkBits2Float(0x4264ad11), SkBits2Float(0x41e620c3), SkBits2Float(0x4263c49e), SkBits2Float(0x41e6624c), SkBits2Float(0x426329fe)); // 28.68f, 57.169f, 28.766f, 56.942f, 28.798f, 56.791f +path.cubicTo(SkBits2Float(0x41e6a5e2), SkBits2Float(0x4262418b), SkBits2Float(0x41e6e76b), SkBits2Float(0x42617ae3), SkBits2Float(0x41e72b00), SkBits2Float(0x42609271)); // 28.831f, 56.564f, 28.863f, 56.37f, 28.896f, 56.143f +path.cubicTo(SkBits2Float(0x41e75602), SkBits2Float(0x42604fe2), SkBits2Float(0x41e7978b), SkBits2Float(0x425fe250), SkBits2Float(0x41e7c49a), SkBits2Float(0x425f9fc1)); // 28.917f, 56.078f, 28.949f, 55.971f, 28.971f, 55.906f +path.cubicTo(SkBits2Float(0x41e7db21), SkBits2Float(0x425f25e6), SkBits2Float(0x41e7db21), SkBits2Float(0x425ec18c), SkBits2Float(0x41e80623), SkBits2Float(0x425e53fa)); // 28.982f, 55.787f, 28.982f, 55.689f, 29.003f, 55.582f +path.lineTo(SkBits2Float(0x41e849b9), SkBits2Float(0x425e26ec)); // 29.036f, 55.538f +path.cubicTo(SkBits2Float(0x41e874bb), SkBits2Float(0x425da2d3), SkBits2Float(0x41e8b851), SkBits2Float(0x425d28f8), SkBits2Float(0x41e8e55f), SkBits2Float(0x425caf1d)); // 29.057f, 55.409f, 29.09f, 55.29f, 29.112f, 55.171f +path.cubicTo(SkBits2Float(0x41e93b63), SkBits2Float(0x425b8f5f), SkBits2Float(0x41e97ef9), SkBits2Float(0x425a7ae4), SkBits2Float(0x41ea0417), SkBits2Float(0x42596669)); // 29.154f, 54.89f, 29.187f, 54.62f, 29.252f, 54.35f +path.cubicTo(SkBits2Float(0x41ea3125), SkBits2Float(0x4259199c), SkBits2Float(0x41ea5c27), SkBits2Float(0x4258ab05), SkBits2Float(0x41eaf7cd), SkBits2Float(0x4258947d)); // 29.274f, 54.275f, 29.295f, 54.167f, 29.371f, 54.145f +path.moveTo(SkBits2Float(0x41e96871), SkBits2Float(0x4256a2d3)); // 29.176f, 53.659f +path.cubicTo(SkBits2Float(0x41e953f6), SkBits2Float(0x4256e562), SkBits2Float(0x41e96871), SkBits2Float(0x425727f2), SkBits2Float(0x41e93b63), SkBits2Float(0x42575f3d)); // 29.166f, 53.724f, 29.176f, 53.789f, 29.154f, 53.843f +path.cubicTo(SkBits2Float(0x41e8fbe6), SkBits2Float(0x42578002), SkBits2Float(0x41e88b42), SkBits2Float(0x42578002), SkBits2Float(0x41e81cab), SkBits2Float(0x42578002)); // 29.123f, 53.875f, 29.068f, 53.875f, 29.014f, 53.875f +path.cubicTo(SkBits2Float(0x41e7db22), SkBits2Float(0x42578002), SkBits2Float(0x41e78311), SkBits2Float(0x42576a81), SkBits2Float(0x41e75603), SkBits2Float(0x42575f3d)); // 28.982f, 53.875f, 28.939f, 53.854f, 28.917f, 53.843f +path.cubicTo(SkBits2Float(0x41e72b01), SkBits2Float(0x4257322f), SkBits2Float(0x41e72b01), SkBits2Float(0x4257322f), SkBits2Float(0x41e72b01), SkBits2Float(0x4256fbe9)); // 28.896f, 53.799f, 28.896f, 53.799f, 28.896f, 53.746f +path.cubicTo(SkBits2Float(0x41e72b01), SkBits2Float(0x4256b95a), SkBits2Float(0x41e78311), SkBits2Float(0x42564ac2), SkBits2Float(0x41e7978c), SkBits2Float(0x42561376)); // 28.896f, 53.681f, 28.939f, 53.573f, 28.949f, 53.519f +path.cubicTo(SkBits2Float(0x41e7db22), SkBits2Float(0x4255570c), SkBits2Float(0x41e80624), SkBits2Float(0x4254b128), SkBits2Float(0x41e86040), SkBits2Float(0x42540b45)); // 28.982f, 53.335f, 29.003f, 53.173f, 29.047f, 53.011f +path.cubicTo(SkBits2Float(0x41e874bb), SkBits2Float(0x4253cac2), SkBits2Float(0x41e86040), SkBits2Float(0x4253916a), SkBits2Float(0x41e8b850), SkBits2Float(0x42536562)); // 29.057f, 52.948f, 29.047f, 52.892f, 29.09f, 52.849f +path.cubicTo(SkBits2Float(0x41e8ced7), SkBits2Float(0x42534fe1), SkBits2Float(0x41e953f6), SkBits2Float(0x42532e16), SkBits2Float(0x41e97ef8), SkBits2Float(0x42532e16)); // 29.101f, 52.828f, 29.166f, 52.795f, 29.187f, 52.795f +path.cubicTo(SkBits2Float(0x41ea0417), SkBits2Float(0x425323d9), SkBits2Float(0x41ea3125), SkBits2Float(0x42534fe1), SkBits2Float(0x41ea72ae), SkBits2Float(0x42535a1e)); // 29.252f, 52.785f, 29.274f, 52.828f, 29.306f, 52.838f +path.cubicTo(SkBits2Float(0x41ea72ae), SkBits2Float(0x42548520), SkBits2Float(0x41e9d708), SkBits2Float(0x4255a4df), SkBits2Float(0x41e96871), SkBits2Float(0x4256a2d2)); // 29.306f, 53.13f, 29.23f, 53.411f, 29.176f, 53.659f +path.moveTo(SkBits2Float(0x41e874bb), SkBits2Float(0x4258b647)); // 29.057f, 54.178f +path.cubicTo(SkBits2Float(0x41e86040), SkBits2Float(0x42595c2a), SkBits2Float(0x41e849b9), SkBits2Float(0x4259bf7e), SkBits2Float(0x41e80623), SkBits2Float(0x425a4eda)); // 29.047f, 54.34f, 29.036f, 54.437f, 29.003f, 54.577f +path.cubicTo(SkBits2Float(0x41e7db21), SkBits2Float(0x425ad3f9), SkBits2Float(0x41e76c89), SkBits2Float(0x425b8520), SkBits2Float(0x41e72b00), SkBits2Float(0x425c147c)); // 28.982f, 54.707f, 28.928f, 54.88f, 28.896f, 55.02f +path.cubicTo(SkBits2Float(0x41e71479), SkBits2Float(0x425c570b), SkBits2Float(0x41e72b00), SkBits2Float(0x425c77d0), SkBits2Float(0x41e71479), SkBits2Float(0x425cba5f)); // 28.885f, 55.085f, 28.896f, 55.117f, 28.885f, 55.182f +path.cubicTo(SkBits2Float(0x41e68f5a), SkBits2Float(0x425dfae2), SkBits2Float(0x41e5dd2d), SkBits2Float(0x425f676d), SkBits2Float(0x41e570a2), SkBits2Float(0x4260a8f7)); // 28.82f, 55.495f, 28.733f, 55.851f, 28.68f, 56.165f +path.cubicTo(SkBits2Float(0x41e52d0c), SkBits2Float(0x42610c4b), SkBits2Float(0x41e55a1b), SkBits2Float(0x42614eda), SkBits2Float(0x41e52d0c), SkBits2Float(0x42619ba7)); // 28.647f, 56.262f, 28.669f, 56.327f, 28.647f, 56.402f +path.cubicTo(SkBits2Float(0x41e51685), SkBits2Float(0x4261f4be), SkBits2Float(0x41e4be74), SkBits2Float(0x42624cce), SkBits2Float(0x41e4a7ed), SkBits2Float(0x42628f5d)); // 28.636f, 56.489f, 28.593f, 56.575f, 28.582f, 56.64f +path.cubicTo(SkBits2Float(0x41e46664), SkBits2Float(0x42634bc7), SkBits2Float(0x41e43b62), SkBits2Float(0x4263e667), SkBits2Float(0x41e3f7cc), SkBits2Float(0x4264a1cc)); // 28.55f, 56.824f, 28.529f, 56.975f, 28.496f, 57.158f +path.cubicTo(SkBits2Float(0x41e39fbc), SkBits2Float(0x42657efb), SkBits2Float(0x41e31a9d), SkBits2Float(0x42669376), SkBits2Float(0x41e2ac05), SkBits2Float(0x426770a5)); // 28.453f, 57.374f, 28.388f, 57.644f, 28.334f, 57.86f +path.cubicTo(SkBits2Float(0x41e27ef7), SkBits2Float(0x426821cc), SkBits2Float(0x41e253f5), SkBits2Float(0x4268bc6c), SkBits2Float(0x41e2105f), SkBits2Float(0x42695812)); // 28.312f, 58.033f, 28.291f, 58.184f, 28.258f, 58.336f +path.cubicTo(SkBits2Float(0x41e1ced6), SkBits2Float(0x4269f2b2), SkBits2Float(0x41e1082e), SkBits2Float(0x426aa3d9), SkBits2Float(0x41e09996), SkBits2Float(0x426b3335)); // 28.226f, 58.487f, 28.129f, 58.66f, 28.075f, 58.8f +path.lineTo(SkBits2Float(0x41e05600), SkBits2Float(0x426b3e79)); // 28.042f, 58.811f +path.cubicTo(SkBits2Float(0x41dfe768), SkBits2Float(0x426bb854), SkBits2Float(0x41dfba5a), SkBits2Float(0x426c3129), SkBits2Float(0x41df4dcf), SkBits2Float(0x426ccccf)); // 27.988f, 58.93f, 27.966f, 59.048f, 27.913f, 59.2f +path.cubicTo(SkBits2Float(0x41def5bf), SkBits2Float(0x426d50e8), SkBits2Float(0x41de5a19), SkBits2Float(0x426de14a), SkBits2Float(0x41ddeb81), SkBits2Float(0x426e70a6)); // 27.87f, 59.329f, 27.794f, 59.47f, 27.74f, 59.61f +path.cubicTo(SkBits2Float(0x41dd3b60), SkBits2Float(0x426f79dd), SkBits2Float(0x41dd4fdb), SkBits2Float(0x426e1896), SkBits2Float(0x41dd6662), SkBits2Float(0x426db43c)); // 27.654f, 59.869f, 27.664f, 59.524f, 27.675f, 59.426f +path.cubicTo(SkBits2Float(0x41de9ba1), SkBits2Float(0x426aa3da), SkBits2Float(0x41e01476), SkBits2Float(0x42679271), SkBits2Float(0x41e1332f), SkBits2Float(0x42648109)); // 27.826f, 58.66f, 28.01f, 57.893f, 28.15f, 57.126f +path.cubicTo(SkBits2Float(0x41e149b6), SkBits2Float(0x42645f3e), SkBits2Float(0x41e1a1c7), SkBits2Float(0x4264072e), SkBits2Float(0x41e1a1c7), SkBits2Float(0x4263f1ad)); // 28.161f, 57.093f, 28.204f, 57.007f, 28.204f, 56.986f +path.cubicTo(SkBits2Float(0x41e253f4), SkBits2Float(0x42626e9b), SkBits2Float(0x41e2c28c), SkBits2Float(0x42610109), SkBits2Float(0x41e3459e), SkBits2Float(0x425f72b3)); // 28.291f, 56.608f, 28.345f, 56.251f, 28.409f, 55.862f +path.cubicTo(SkBits2Float(0x41e372ac), SkBits2Float(0x425f51ee), SkBits2Float(0x41e3b642), SkBits2Float(0x425ef9de), SkBits2Float(0x41e3b642), SkBits2Float(0x425ed813)); // 28.431f, 55.83f, 28.464f, 55.744f, 28.464f, 55.711f +path.cubicTo(SkBits2Float(0x41e46663), SkBits2Float(0x425d76cb), SkBits2Float(0x41e4be73), SkBits2Float(0x425c3542), SkBits2Float(0x41e570a0), SkBits2Float(0x425ad3fa)); // 28.55f, 55.366f, 28.593f, 55.052f, 28.68f, 54.707f +path.cubicTo(SkBits2Float(0x41e570a0), SkBits2Float(0x425a916b), SkBits2Float(0x41e5dd2b), SkBits2Float(0x425a22d3), SkBits2Float(0x41e5f3b2), SkBits2Float(0x4259e044)); // 28.68f, 54.642f, 28.733f, 54.534f, 28.744f, 54.469f +path.cubicTo(SkBits2Float(0x41e620c0), SkBits2Float(0x42595c2b), SkBits2Float(0x41e60a39), SkBits2Float(0x4258ab05), SkBits2Float(0x41e72afe), SkBits2Float(0x4258947d)); // 28.766f, 54.34f, 28.755f, 54.167f, 28.896f, 54.145f +path.cubicTo(SkBits2Float(0x41e79789), SkBits2Float(0x4258947d), SkBits2Float(0x41e80621), SkBits2Float(0x4258ab04), SkBits2Float(0x41e874b8), SkBits2Float(0x4258b648)); // 28.949f, 54.145f, 29.003f, 54.167f, 29.057f, 54.178f +path.moveTo(SkBits2Float(0x41e5b229), SkBits2Float(0x4256a2d3)); // 28.712f, 53.659f +path.cubicTo(SkBits2Float(0x41e5851b), SkBits2Float(0x4256e562), SkBits2Float(0x41e59ba2), SkBits2Float(0x425727f2), SkBits2Float(0x41e570a0), SkBits2Float(0x42575f3d)); // 28.69f, 53.724f, 28.701f, 53.789f, 28.68f, 53.843f +path.cubicTo(SkBits2Float(0x41e52d0a), SkBits2Float(0x42578002), SkBits2Float(0x41e4a7ec), SkBits2Float(0x42579689), SkBits2Float(0x41e43b61), SkBits2Float(0x42578002)); // 28.647f, 53.875f, 28.582f, 53.897f, 28.529f, 53.875f +path.cubicTo(SkBits2Float(0x41e3f7cb), SkBits2Float(0x42578002), SkBits2Float(0x41e39fbb), SkBits2Float(0x425748b6), SkBits2Float(0x41e3459e), SkBits2Float(0x42573e79)); // 28.496f, 53.875f, 28.453f, 53.821f, 28.409f, 53.811f +path.cubicTo(SkBits2Float(0x41e39fbb), SkBits2Float(0x42566044), SkBits2Float(0x41e40e52), SkBits2Float(0x42558e58), SkBits2Float(0x41e47add), SkBits2Float(0x4254c7b0)); // 28.453f, 53.594f, 28.507f, 53.389f, 28.56f, 53.195f +path.cubicTo(SkBits2Float(0x41e49370), SkBits2Float(0x425479dd), SkBits2Float(0x41e49370), SkBits2Float(0x42541689), SkBits2Float(0x41e4eb81), SkBits2Float(0x4253df3d)); // 28.572f, 53.119f, 28.572f, 53.022f, 28.615f, 52.968f +path.cubicTo(SkBits2Float(0x41e4fffc), SkBits2Float(0x4253c9bc), SkBits2Float(0x41e5b229), SkBits2Float(0x4253916a), SkBits2Float(0x41e60a39), SkBits2Float(0x4253916a)); // 28.625f, 52.947f, 28.712f, 52.892f, 28.755f, 52.892f +path.cubicTo(SkBits2Float(0x41e68f58), SkBits2Float(0x4253872d), SkBits2Float(0x41e68f58), SkBits2Float(0x4253a7f1), SkBits2Float(0x41e6e768), SkBits2Float(0x4253be78)); // 28.82f, 52.882f, 28.82f, 52.914f, 28.863f, 52.936f +path.cubicTo(SkBits2Float(0x41e68f58), SkBits2Float(0x4254c7af), SkBits2Float(0x41e60a39), SkBits2Float(0x4255af1c), SkBits2Float(0x41e5b229), SkBits2Float(0x4256a2d2)); // 28.82f, 53.195f, 28.755f, 53.421f, 28.712f, 53.659f +path.moveTo(SkBits2Float(0x41e372ac), SkBits2Float(0x42589fc0)); // 28.431f, 54.156f +path.cubicTo(SkBits2Float(0x41e55a19), SkBits2Float(0x42586874), SkBits2Float(0x41e40e52), SkBits2Float(0x425a178f), SkBits2Float(0x41e3cabc), SkBits2Float(0x425a7ae3)); // 28.669f, 54.102f, 28.507f, 54.523f, 28.474f, 54.62f +path.cubicTo(SkBits2Float(0x41e1fbe3), SkBits2Float(0x425f3b66), SkBits2Float(0x41dfd0e1), SkBits2Float(0x4263f1ac), SkBits2Float(0x41ddeb81), SkBits2Float(0x4268c7b0)); // 28.248f, 55.808f, 27.977f, 56.986f, 27.74f, 58.195f +path.cubicTo(SkBits2Float(0x41ddd4fa), SkBits2Float(0x42690a3f), SkBits2Float(0x41dd7ce9), SkBits2Float(0x42696d93), SkBits2Float(0x41dd6662), SkBits2Float(0x4269999c)); // 27.729f, 58.26f, 27.686f, 58.357f, 27.675f, 58.4f +path.cubicTo(SkBits2Float(0x41dd3b60), SkBits2Float(0x426a29fe), SkBits2Float(0x41dd3b60), SkBits2Float(0x426a8d52), SkBits2Float(0x41dcf7ca), SkBits2Float(0x426b1cae)); // 27.654f, 58.541f, 27.654f, 58.638f, 27.621f, 58.778f +path.cubicTo(SkBits2Float(0x41dcb641), SkBits2Float(0x426bf9dd), SkBits2Float(0x41dc0414), SkBits2Float(0x426cf8d7), SkBits2Float(0x41db957c), SkBits2Float(0x426dec8d)); // 27.589f, 58.994f, 27.502f, 59.243f, 27.448f, 59.481f +path.cubicTo(SkBits2Float(0x41db53f3), SkBits2Float(0x426e916a), SkBits2Float(0x41db3d6c), SkBits2Float(0x426eea81), SkBits2Float(0x41daa3d2), SkBits2Float(0x426f5918)); // 27.416f, 59.642f, 27.405f, 59.729f, 27.33f, 59.837f +path.cubicTo(SkBits2Float(0x41da76c4), SkBits2Float(0x426f4dd4), SkBits2Float(0x41da49b5), SkBits2Float(0x426f4291), SkBits2Float(0x41da082c), SkBits2Float(0x426f21cc)); // 27.308f, 59.826f, 27.286f, 59.815f, 27.254f, 59.783f +path.cubicTo(SkBits2Float(0x41d9db1e), SkBits2Float(0x426f0b45), SkBits2Float(0x41d9f1a5), SkBits2Float(0x426f0b45), SkBits2Float(0x41d9c6a3), SkBits2Float(0x426eea80)); // 27.232f, 59.761f, 27.243f, 59.761f, 27.222f, 59.729f +path.lineTo(SkBits2Float(0x41d99995), SkBits2Float(0x426edf3c)); // 27.2f, 59.718f +path.cubicTo(SkBits2Float(0x41d91476), SkBits2Float(0x426ea7f0), SkBits2Float(0x41d8e768), SkBits2Float(0x426e6561), SkBits2Float(0x41d8a5df), SkBits2Float(0x426e020d)); // 27.135f, 59.664f, 27.113f, 59.599f, 27.081f, 59.502f +path.cubicTo(SkBits2Float(0x41d8a5df), SkBits2Float(0x426e020d), SkBits2Float(0x41d86456), SkBits2Float(0x426dd605), SkBits2Float(0x41d8a5df), SkBits2Float(0x426dd605)); // 27.081f, 59.502f, 27.049f, 59.459f, 27.081f, 59.459f +path.cubicTo(SkBits2Float(0x41d8e768), SkBits2Float(0x426d5c2a), SkBits2Float(0x41d8fdef), SkBits2Float(0x426cf8d6), SkBits2Float(0x41d92afe), SkBits2Float(0x426c7efb)); // 27.113f, 59.34f, 27.124f, 59.243f, 27.146f, 59.124f +path.cubicTo(SkBits2Float(0x41d9830e), SkBits2Float(0x426bb853), SkBits2Float(0x41da1eb4), SkBits2Float(0x426ae561), SkBits2Float(0x41da8d4c), SkBits2Float(0x426a29fd)); // 27.189f, 58.93f, 27.265f, 58.724f, 27.319f, 58.541f +path.cubicTo(SkBits2Float(0x41dccabd), SkBits2Float(0x4265d811), SkBits2Float(0x41e02afe), SkBits2Float(0x42617ae2), SkBits2Float(0x41e1332f), SkBits2Float(0x425cfcef)); // 27.599f, 57.461f, 28.021f, 56.37f, 28.15f, 55.247f +path.cubicTo(SkBits2Float(0x41e149b6), SkBits2Float(0x425c4085), SkBits2Float(0x41e1a1c7), SkBits2Float(0x425b8f5e), SkBits2Float(0x41e1fbe3), SkBits2Float(0x425adf3d)); // 28.161f, 55.063f, 28.204f, 54.89f, 28.248f, 54.718f +path.cubicTo(SkBits2Float(0x41e226e5), SkBits2Float(0x425a4edb), SkBits2Float(0x41e226e5), SkBits2Float(0x42598833), SkBits2Float(0x41e2ac04), SkBits2Float(0x4258f7d0)); // 28.269f, 54.577f, 28.269f, 54.383f, 28.334f, 54.242f +path.cubicTo(SkBits2Float(0x41e2c28b), SkBits2Float(0x4258ec8c), SkBits2Float(0x41e372ac), SkBits2Float(0x42589fc0), SkBits2Float(0x41e372ac), SkBits2Float(0x42589fc0)); // 28.345f, 54.231f, 28.431f, 54.156f, 28.431f, 54.156f +path.moveTo(SkBits2Float(0x41d9830e), SkBits2Float(0x427128f7)); // 27.189f, 60.29f +path.cubicTo(SkBits2Float(0x41d95600), SkBits2Float(0x42714ac2), SkBits2Float(0x41d92afe), SkBits2Float(0x427176ca), SkBits2Float(0x41d8e768), SkBits2Float(0x427176ca)); // 27.167f, 60.323f, 27.146f, 60.366f, 27.113f, 60.366f +path.cubicTo(SkBits2Float(0x41d86456), SkBits2Float(0x42718d51), SkBits2Float(0x41d67ce9), SkBits2Float(0x4271820e), SkBits2Float(0x41d60e51), SkBits2Float(0x42716b86)); // 27.049f, 60.388f, 26.811f, 60.377f, 26.757f, 60.355f +path.cubicTo(SkBits2Float(0x41d5f7ca), SkBits2Float(0x42716b86), SkBits2Float(0x41d5ccc8), SkBits2Float(0x42714ac1), SkBits2Float(0x41d5b641), SkBits2Float(0x42713f7e)); // 26.746f, 60.355f, 26.725f, 60.323f, 26.714f, 60.312f +path.cubicTo(SkBits2Float(0x41d5b641), SkBits2Float(0x42708e57), SkBits2Float(0x41d5f7ca), SkBits2Float(0x426ffefb), SkBits2Float(0x41d69370), SkBits2Float(0x426f8f5d)); // 26.714f, 60.139f, 26.746f, 59.999f, 26.822f, 59.89f +path.cubicTo(SkBits2Float(0x41d6eb80), SkBits2Float(0x426f9ba7), SkBits2Float(0x41d7188f), SkBits2Float(0x426f8f5d), SkBits2Float(0x41d7709f), SkBits2Float(0x426f9ba7)); // 26.865f, 59.902f, 26.887f, 59.89f, 26.93f, 59.902f +path.cubicTo(SkBits2Float(0x41d7b228), SkBits2Float(0x426fb128), SkBits2Float(0x41d99995), SkBits2Float(0x42706d93), SkBits2Float(0x41d9c6a3), SkBits2Float(0x42708e57)); // 26.962f, 59.923f, 27.2f, 60.107f, 27.222f, 60.139f +path.cubicTo(SkBits2Float(0x41d9db1e), SkBits2Float(0x4270d0e6), SkBits2Float(0x41d99995), SkBits2Float(0x42710832), SkBits2Float(0x41d9830d), SkBits2Float(0x427128f7)); // 27.232f, 60.204f, 27.2f, 60.258f, 27.189f, 60.29f +path.moveTo(SkBits2Float(0x41e1603c), SkBits2Float(0x4255f1ab)); // 28.172f, 53.486f +path.cubicTo(SkBits2Float(0x41e149b5), SkBits2Float(0x42563f7e), SkBits2Float(0x41e1603c), SkBits2Float(0x425676ca), SkBits2Float(0x41e1332e), SkBits2Float(0x4256c49d)); // 28.161f, 53.562f, 28.172f, 53.616f, 28.15f, 53.692f +path.cubicTo(SkBits2Float(0x41e11eb3), SkBits2Float(0x4256f0a5), SkBits2Float(0x41e0db1e), SkBits2Float(0x425727f1), SkBits2Float(0x41e0b01c), SkBits2Float(0x425748b6)); // 28.14f, 53.735f, 28.107f, 53.789f, 28.086f, 53.821f +path.lineTo(SkBits2Float(0x41e055ff), SkBits2Float(0x425748b6)); // 28.042f, 53.821f +path.cubicTo(SkBits2Float(0x41e055ff), SkBits2Float(0x425748b6), SkBits2Float(0x41df4dce), SkBits2Float(0x4256e562), SkBits2Float(0x41df3747), SkBits2Float(0x4256da1e)); // 28.042f, 53.821f, 27.913f, 53.724f, 27.902f, 53.713f +path.cubicTo(SkBits2Float(0x41deb228), SkBits2Float(0x4256820e), SkBits2Float(0x41de4391), SkBits2Float(0x42561376), SkBits2Float(0x41ddbe72), SkBits2Float(0x4255ba60)); // 27.837f, 53.627f, 27.783f, 53.519f, 27.718f, 53.432f +path.lineTo(SkBits2Float(0x41dd7ce9), SkBits2Float(0x4255af1c)); // 27.686f, 53.421f +path.cubicTo(SkBits2Float(0x41dd7ce9), SkBits2Float(0x4255a4df), SkBits2Float(0x41dda7eb), SkBits2Float(0x425578d6), SkBits2Float(0x41dd7ce9), SkBits2Float(0x42556d93)); // 27.686f, 53.411f, 27.707f, 53.368f, 27.686f, 53.357f +path.cubicTo(SkBits2Float(0x41de9ba1), SkBits2Float(0x4255147c), SkBits2Float(0x41df8f58), SkBits2Float(0x4254c7b0), SkBits2Float(0x41e0b01c), SkBits2Float(0x42549064)); // 27.826f, 53.27f, 27.945f, 53.195f, 28.086f, 53.141f +path.cubicTo(SkBits2Float(0x41e0c497), SkBits2Float(0x42548520), SkBits2Float(0x41e11eb4), SkBits2Float(0x4254645c), SkBits2Float(0x41e1332e), SkBits2Float(0x4254645c)); // 28.096f, 53.13f, 28.14f, 53.098f, 28.15f, 53.098f +path.cubicTo(SkBits2Float(0x41e18b3e), SkBits2Float(0x42545918), SkBits2Float(0x41e1ced4), SkBits2Float(0x425479dd), SkBits2Float(0x41e1fbe2), SkBits2Float(0x425479dd)); // 28.193f, 53.087f, 28.226f, 53.119f, 28.248f, 53.119f +path.cubicTo(SkBits2Float(0x41e1fbe2), SkBits2Float(0x4255147d), SkBits2Float(0x41e1a1c5), SkBits2Float(0x4255841a), SkBits2Float(0x41e1603c), SkBits2Float(0x4255f1ac)); // 28.248f, 53.27f, 28.204f, 53.379f, 28.172f, 53.486f +path.moveTo(SkBits2Float(0x41df6248), SkBits2Float(0x425b4ccf)); // 27.923f, 54.825f +path.cubicTo(SkBits2Float(0x41dfe767), SkBits2Float(0x425b9aa2), SkBits2Float(0x41df4dcd), SkBits2Float(0x425c6c8d), SkBits2Float(0x41df20bf), SkBits2Float(0x425cd0e8)); // 27.988f, 54.901f, 27.913f, 55.106f, 27.891f, 55.204f +path.cubicTo(SkBits2Float(0x41ddeb80), SkBits2Float(0x425f893a), SkBits2Float(0x41dc8932), SkBits2Float(0x4262374e), SkBits2Float(0x41db105d), SkBits2Float(0x4264e45d)); // 27.74f, 55.884f, 27.567f, 56.554f, 27.383f, 57.223f +path.cubicTo(SkBits2Float(0x41daced4), SkBits2Float(0x42657efd), SkBits2Float(0x41d78726), SkBits2Float(0x426c52f5), SkBits2Float(0x41d6c07e), SkBits2Float(0x426c3c6d)); // 27.351f, 57.374f, 26.941f, 59.081f, 26.844f, 59.059f +path.cubicTo(SkBits2Float(0x41d58932), SkBits2Float(0x426c3129), SkBits2Float(0x41d50620), SkBits2Float(0x426b1caf), SkBits2Float(0x41d48101), SkBits2Float(0x426aa3da)); // 26.692f, 59.048f, 26.628f, 58.778f, 26.563f, 58.66f +path.cubicTo(SkBits2Float(0x41d3d0e0), SkBits2Float(0x426a0834), SkBits2Float(0x41d34bc2), SkBits2Float(0x42696d94), SkBits2Float(0x41d2db1e), SkBits2Float(0x4268bc6d)); // 26.477f, 58.508f, 26.412f, 58.357f, 26.357f, 58.184f +path.cubicTo(SkBits2Float(0x41d21476), SkBits2Float(0x42674fe2), SkBits2Float(0x41d19164), SkBits2Float(0x4265c18c), SkBits2Float(0x41d19164), SkBits2Float(0x426449bd)); // 26.26f, 57.828f, 26.196f, 57.439f, 26.196f, 57.072f +path.cubicTo(SkBits2Float(0x41d1a5df), SkBits2Float(0x4261bd73), SkBits2Float(0x41d3d0e1), SkBits2Float(0x425f51ee), SkBits2Float(0x41d79dae), SkBits2Float(0x425d820f)); // 26.206f, 56.435f, 26.477f, 55.83f, 26.952f, 55.377f +path.cubicTo(SkBits2Float(0x41d8a5df), SkBits2Float(0x425cfcf0), SkBits2Float(0x41d9db1f), SkBits2Float(0x425c8e59), SkBits2Float(0x41db105e), SkBits2Float(0x425c3542)); // 27.081f, 55.247f, 27.232f, 55.139f, 27.383f, 55.052f +path.cubicTo(SkBits2Float(0x41dbed8d), SkBits2Float(0x425bf4bf), SkBits2Float(0x41ddbe72), SkBits2Float(0x425b21cd), SkBits2Float(0x41dec8b0), SkBits2Float(0x425b21cd)); // 27.491f, 54.989f, 27.718f, 54.783f, 27.848f, 54.783f +path.cubicTo(SkBits2Float(0x41df20c0), SkBits2Float(0x425b374e), SkBits2Float(0x41df4dcf), SkBits2Float(0x425b4292), SkBits2Float(0x41df624a), SkBits2Float(0x425b4ccf)); // 27.891f, 54.804f, 27.913f, 54.815f, 27.923f, 54.825f +path.moveTo(SkBits2Float(0x41d453f4), SkBits2Float(0x426fbc6d)); // 26.541f, 59.934f +path.cubicTo(SkBits2Float(0x41d48102), SkBits2Float(0x426f8521), SkBits2Float(0x41d51a9c), SkBits2Float(0x426ea7f2), SkBits2Float(0x41d4957d), SkBits2Float(0x426e872e)); // 26.563f, 59.88f, 26.638f, 59.664f, 26.573f, 59.632f +path.cubicTo(SkBits2Float(0x41d453f4), SkBits2Float(0x426e70a7), SkBits2Float(0x41d428f2), SkBits2Float(0x426e872e), SkBits2Float(0x41d3fbe3), SkBits2Float(0x426e70a7)); // 26.541f, 59.61f, 26.52f, 59.632f, 26.498f, 59.61f +path.cubicTo(SkBits2Float(0x41d3d0e1), SkBits2Float(0x426e916c), SkBits2Float(0x41d3b84d), SkBits2Float(0x426e872e), SkBits2Float(0x41d3a3d3), SkBits2Float(0x426e916c)); // 26.477f, 59.642f, 26.465f, 59.632f, 26.455f, 59.642f +path.cubicTo(SkBits2Float(0x41d3603d), SkBits2Float(0x426f010a), SkBits2Float(0x41d3d0e1), SkBits2Float(0x426f9ba9), SkBits2Float(0x41d4126b), SkBits2Float(0x426fdd33)); // 26.422f, 59.751f, 26.477f, 59.902f, 26.509f, 59.966f +path.lineTo(SkBits2Float(0x41d43d6d), SkBits2Float(0x426fdd33)); // 26.53f, 59.966f +path.cubicTo(SkBits2Float(0x41d43d6d), SkBits2Float(0x426fd1ef), SkBits2Float(0x41d43d6d), SkBits2Float(0x426fbc6e), SkBits2Float(0x41d453f4), SkBits2Float(0x426fbc6e)); // 26.53f, 59.955f, 26.53f, 59.934f, 26.541f, 59.934f +path.moveTo(SkBits2Float(0x42071ba4), SkBits2Float(0x42670210)); // 33.777f, 57.752f +path.cubicTo(SkBits2Float(0x42075e33), SkBits2Float(0x42670d54), SkBits2Float(0x4207957f), SkBits2Float(0x42671897), SkBits2Float(0x4207cccb), SkBits2Float(0x42672f1e)); // 33.842f, 57.763f, 33.896f, 57.774f, 33.95f, 57.796f +path.cubicTo(SkBits2Float(0x4208a9fa), SkBits2Float(0x4267872e), SkBits2Float(0x42097be6), SkBits2Float(0x42681791), SkBits2Float(0x420a3850), SkBits2Float(0x42688522)); // 34.166f, 57.882f, 34.371f, 58.023f, 34.555f, 58.13f +path.cubicTo(SkBits2Float(0x420b0a3c), SkBits2Float(0x4268fefd), SkBits2Float(0x420d1167), SkBits2Float(0x4269e770), SkBits2Float(0x420d27ee), SkBits2Float(0x426ae564)); // 34.76f, 58.249f, 35.267f, 58.476f, 35.289f, 58.724f +path.cubicTo(SkBits2Float(0x420d0729), SkBits2Float(0x426af0a8), SkBits2Float(0x420cdb21), SkBits2Float(0x426afbeb), SkBits2Float(0x420cb956), SkBits2Float(0x426b072f)); // 35.257f, 58.735f, 35.214f, 58.746f, 35.181f, 58.757f +path.cubicTo(SkBits2Float(0x420b9998), SkBits2Float(0x426b27f4), SkBits2Float(0x420a6f9c), SkBits2Float(0x426b27f4), SkBits2Float(0x42095b21), SkBits2Float(0x426b3337)); // 34.9f, 58.789f, 34.609f, 58.789f, 34.339f, 58.8f +path.cubicTo(SkBits2Float(0x42090d4e), SkBits2Float(0x426b3337), SkBits2Float(0x4207b644), SkBits2Float(0x426b49be), SkBits2Float(0x420773b4), SkBits2Float(0x426b3337)); // 34.263f, 58.8f, 33.928f, 58.822f, 33.863f, 58.8f +path.cubicTo(SkBits2Float(0x4207322b), SkBits2Float(0x426b072f), SkBits2Float(0x4206ef9b), SkBits2Float(0x4269999d), SkBits2Float(0x4206ced7), SkBits2Float(0x426920c8)); // 33.799f, 58.757f, 33.734f, 58.4f, 33.702f, 58.282f +path.cubicTo(SkBits2Float(0x42069685), SkBits2Float(0x4268645e), SkBits2Float(0x4205c49a), SkBits2Float(0x4266b543), SkBits2Float(0x42071ba4), SkBits2Float(0x42670210)); // 33.647f, 58.098f, 33.442f, 57.677f, 33.777f, 57.752f +path.moveTo(SkBits2Float(0x41f026e6), SkBits2Float(0x423f0c4e)); // 30.019f, 47.762f +path.cubicTo(SkBits2Float(0x41effbe4), SkBits2Float(0x42400004), SkBits2Float(0x41f0105f), SkBits2Float(0x4240e877), SkBits2Float(0x41f03b61), SkBits2Float(0x4241d0ea)); // 29.998f, 48, 30.008f, 48.227f, 30.029f, 48.454f +path.lineTo(SkBits2Float(0x41f03b61), SkBits2Float(0x424228fa)); // 30.029f, 48.54f +path.cubicTo(SkBits2Float(0x41f051e8), SkBits2Float(0x42423f81), SkBits2Float(0x41f0c080), SkBits2Float(0x424276cd), SkBits2Float(0x41f11890), SkBits2Float(0x424276cd)); // 30.04f, 48.562f, 30.094f, 48.616f, 30.137f, 48.616f +path.cubicTo(SkBits2Float(0x41f11890), SkBits2Float(0x424276cd), SkBits2Float(0x41f18934), SkBits2Float(0x42426b89), SkBits2Float(0x41f12f17), SkBits2Float(0x42426b89)); // 30.137f, 48.616f, 30.192f, 48.605f, 30.148f, 48.605f +path.cubicTo(SkBits2Float(0x41f1459e), SkBits2Float(0x42426045), SkBits2Float(0x41f18934), SkBits2Float(0x42426b89), SkBits2Float(0x41f18934), SkBits2Float(0x42426b89)); // 30.159f, 48.594f, 30.192f, 48.605f, 30.192f, 48.605f +path.cubicTo(SkBits2Float(0x41f19daf), SkBits2Float(0x424249be), SkBits2Float(0x41f19daf), SkBits2Float(0x42423f81), SkBits2Float(0x41f1b436), SkBits2Float(0x42423f81)); // 30.202f, 48.572f, 30.202f, 48.562f, 30.213f, 48.562f +path.cubicTo(SkBits2Float(0x41f18934), SkBits2Float(0x42414087), SkBits2Float(0x41f11890), SkBits2Float(0x424079df), SkBits2Float(0x41f0ac05), SkBits2Float(0x423f9cb0)); // 30.192f, 48.313f, 30.137f, 48.119f, 30.084f, 47.903f +path.cubicTo(SkBits2Float(0x41f0957e), SkBits2Float(0x423f7ae5), SkBits2Float(0x41f0c080), SkBits2Float(0x423f5a21), SkBits2Float(0x41f0957e), SkBits2Float(0x423f395c)); // 30.073f, 47.87f, 30.094f, 47.838f, 30.073f, 47.806f +path.lineTo(SkBits2Float(0x41f026e6), SkBits2Float(0x423f0c4e)); // 30.019f, 47.762f +path.moveTo(SkBits2Float(0x41ed4dcf), SkBits2Float(0x423fd3fc)); // 29.663f, 47.957f +path.cubicTo(SkBits2Float(0x41ecc8b0), SkBits2Float(0x42408523), SkBits2Float(0x41ec580c), SkBits2Float(0x42414bcb), SkBits2Float(0x41ec580c), SkBits2Float(0x42423f81)); // 29.598f, 48.13f, 29.543f, 48.324f, 29.543f, 48.562f +path.cubicTo(SkBits2Float(0x41ec6e93), SkBits2Float(0x42423f81), SkBits2Float(0x41ec9ba2), SkBits2Float(0x42426046), SkBits2Float(0x41ecb229), SkBits2Float(0x42426b89)); // 29.554f, 48.562f, 29.576f, 48.594f, 29.587f, 48.605f +path.cubicTo(SkBits2Float(0x41ecc8b0), SkBits2Float(0x42426b89), SkBits2Float(0x41ecdd2b), SkBits2Float(0x42426045), SkBits2Float(0x41ecf3b2), SkBits2Float(0x42426b89)); // 29.598f, 48.605f, 29.608f, 48.594f, 29.619f, 48.605f +path.cubicTo(SkBits2Float(0x41ecf3b2), SkBits2Float(0x42426b89), SkBits2Float(0x41eda5df), SkBits2Float(0x42426045), SkBits2Float(0x41edba5a), SkBits2Float(0x42423f81)); // 29.619f, 48.605f, 29.706f, 48.594f, 29.716f, 48.562f +path.cubicTo(SkBits2Float(0x41ee126a), SkBits2Float(0x4241e66a), SkBits2Float(0x41edd0e1), SkBits2Float(0x42403750), SkBits2Float(0x41eda5df), SkBits2Float(0x423fdf3f)); // 29.759f, 48.475f, 29.727f, 48.054f, 29.706f, 47.968f +path.lineTo(SkBits2Float(0x41ed4dcf), SkBits2Float(0x423fd3fb)); // 29.663f, 47.957f +path.moveTo(SkBits2Float(0x41d05a19), SkBits2Float(0x4258ab05)); // 26.044f, 54.167f +path.cubicTo(SkBits2Float(0x41d05a19), SkBits2Float(0x42589fc1), SkBits2Float(0x41d070a0), SkBits2Float(0x42588a40), SkBits2Float(0x41d05a19), SkBits2Float(0x42586876)); // 26.044f, 54.156f, 26.055f, 54.135f, 26.044f, 54.102f +path.cubicTo(SkBits2Float(0x41d05a19), SkBits2Float(0x42583c6e), SkBits2Float(0x41d02f17), SkBits2Float(0x4257ee9b), SkBits2Float(0x41d00209), SkBits2Float(0x4257c293)); // 26.044f, 54.059f, 26.023f, 53.983f, 26.001f, 53.94f +path.cubicTo(SkBits2Float(0x41cfeb82), SkBits2Float(0x42571cb0), SkBits2Float(0x41d00209), SkBits2Float(0x42568210), SkBits2Float(0x41cfeb82), SkBits2Float(0x4255c5a5)); // 25.99f, 53.778f, 26.001f, 53.627f, 25.99f, 53.443f +path.cubicTo(SkBits2Float(0x41cfeb82), SkBits2Float(0x4255a4e0), SkBits2Float(0x41cfc080), SkBits2Float(0x42552b05), SkBits2Float(0x41cfd4fb), SkBits2Float(0x4254dd32)); // 25.99f, 53.411f, 25.969f, 53.292f, 25.979f, 53.216f +path.cubicTo(SkBits2Float(0x41cfeb82), SkBits2Float(0x4254b12a), SkBits2Float(0x41d05a1a), SkBits2Float(0x4254b12a), SkBits2Float(0x41d0df38), SkBits2Float(0x4254c7b1)); // 25.99f, 53.173f, 26.044f, 53.173f, 26.109f, 53.195f +path.cubicTo(SkBits2Float(0x41d24186), SkBits2Float(0x42552b05), SkBits2Float(0x41d4ac05), SkBits2Float(0x42563f80), SkBits2Float(0x41d50621), SkBits2Float(0x42566044)); // 26.282f, 53.292f, 26.584f, 53.562f, 26.628f, 53.594f +path.cubicTo(SkBits2Float(0x41d60e52), SkBits2Float(0x4256da1f), SkBits2Float(0x41d70208), SkBits2Float(0x425748b7), SkBits2Float(0x41d80a3a), SkBits2Float(0x4257c292)); // 26.757f, 53.713f, 26.876f, 53.821f, 27.005f, 53.94f +path.cubicTo(SkBits2Float(0x41d8a5e0), SkBits2Float(0x4257f9de), SkBits2Float(0x41da1eb5), SkBits2Float(0x4258947e), SkBits2Float(0x41d8a5e0), SkBits2Float(0x4258ab05)); // 27.081f, 53.994f, 27.265f, 54.145f, 27.081f, 54.167f +path.cubicTo(SkBits2Float(0x41d7df38), SkBits2Float(0x4258cbca), SkBits2Float(0x41d72d0b), SkBits2Float(0x4258b649), SkBits2Float(0x41d66663), SkBits2Float(0x4258b649)); // 26.984f, 54.199f, 26.897f, 54.178f, 26.8f, 54.178f +path.cubicTo(SkBits2Float(0x41d547ab), SkBits2Float(0x4258cbca), SkBits2Float(0x41d1bc67), SkBits2Float(0x42592f1e), SkBits2Float(0x41d0b22a), SkBits2Float(0x4258e251)); // 26.66f, 54.199f, 26.217f, 54.296f, 26.087f, 54.221f +path.lineTo(SkBits2Float(0x41d0b22a), SkBits2Float(0x4258d70d)); // 26.087f, 54.21f +path.cubicTo(SkBits2Float(0x41d09daf), SkBits2Float(0x4258d70d), SkBits2Float(0x41d070a1), SkBits2Float(0x4258b648), SkBits2Float(0x41d05a1a), SkBits2Float(0x4258ab05)); // 26.077f, 54.21f, 26.055f, 54.178f, 26.044f, 54.167f +path.moveTo(SkBits2Float(0x41ce8b41), SkBits2Float(0x42588a40)); // 25.818f, 54.135f +path.cubicTo(SkBits2Float(0x41ceb643), SkBits2Float(0x4258ab05), SkBits2Float(0x41ce74ba), SkBits2Float(0x4258ab05), SkBits2Float(0x41ceccca), SkBits2Float(0x4258ab05)); // 25.839f, 54.167f, 25.807f, 54.167f, 25.85f, 54.167f +path.cubicTo(SkBits2Float(0x41cef7cc), SkBits2Float(0x4258ab05), SkBits2Float(0x41cf0e53), SkBits2Float(0x4258b336), SkBits2Float(0x41cf0e53), SkBits2Float(0x42589db5)); // 25.871f, 54.167f, 25.882f, 54.175f, 25.882f, 54.154f +path.cubicTo(SkBits2Float(0x41cf0e53), SkBits2Float(0x4258395b), SkBits2Float(0x41cf0a3a), SkBits2Float(0x42579790), SkBits2Float(0x41cedd2c), SkBits2Float(0x4257343c)); // 25.882f, 54.056f, 25.88f, 53.898f, 25.858f, 53.801f +path.cubicTo(SkBits2Float(0x41cec8b1), SkBits2Float(0x42564086), SkBits2Float(0x41ceccca), SkBits2Float(0x4254f3b9), SkBits2Float(0x41ce5e32), SkBits2Float(0x425421cd)); // 25.848f, 53.563f, 25.85f, 53.238f, 25.796f, 53.033f +path.lineTo(SkBits2Float(0x41cdef9a), SkBits2Float(0x425421cd)); // 25.742f, 53.033f +path.cubicTo(SkBits2Float(0x41cdd913), SkBits2Float(0x4254dd31), SkBits2Float(0x41ce126b), SkBits2Float(0x425626ec), SkBits2Float(0x41ce28f2), SkBits2Float(0x4256e250)); // 25.731f, 53.216f, 25.759f, 53.538f, 25.77f, 53.721f +path.cubicTo(SkBits2Float(0x41ce3f79), SkBits2Float(0x42579377), SkBits2Float(0x41ce47aa), SkBits2Float(0x42580f5e), SkBits2Float(0x41ce8b40), SkBits2Float(0x42588a40)); // 25.781f, 53.894f, 25.785f, 54.015f, 25.818f, 54.135f +path.moveTo(SkBits2Float(0x41c58d4c), SkBits2Float(0x425271ad)); // 24.694f, 52.611f +path.cubicTo(SkBits2Float(0x41c58d4c), SkBits2Float(0x42525c2c), SkBits2Float(0x41c5ba5a), SkBits2Float(0x42523024), SkBits2Float(0x41c5fbe4), SkBits2Float(0x425224e0)); // 24.694f, 52.59f, 24.716f, 52.547f, 24.748f, 52.536f +path.lineTo(SkBits2Float(0x41c6126b), SkBits2Float(0x4252199c)); // 24.759f, 52.525f +path.cubicTo(SkBits2Float(0x41c6978a), SkBits2Float(0x42520f5f), SkBits2Float(0x41c774b9), SkBits2Float(0x42523023), SkBits2Float(0x41c79fbb), SkBits2Float(0x42525c2b)); // 24.824f, 52.515f, 24.932f, 52.547f, 24.953f, 52.59f +path.cubicTo(SkBits2Float(0x41c7f9d8), SkBits2Float(0x4252a9fe), SkBits2Float(0x41c79fbb), SkBits2Float(0x4258e250), SkBits2Float(0x41c78b40), SkBits2Float(0x4259199c)); // 24.997f, 52.666f, 24.953f, 54.221f, 24.943f, 54.275f +path.lineTo(SkBits2Float(0x41c78b40), SkBits2Float(0x42592f1d)); // 24.943f, 54.296f +path.cubicTo(SkBits2Float(0x41c747aa), SkBits2Float(0x42595c2b), SkBits2Float(0x41c68103), SkBits2Float(0x42596669), SkBits2Float(0x41c5fbe4), SkBits2Float(0x42596669)); // 24.91f, 54.34f, 24.813f, 54.35f, 24.748f, 54.35f +path.cubicTo(SkBits2Float(0x41c5353c), SkBits2Float(0x425971ad), SkBits2Float(0x41c41684), SkBits2Float(0x425971ad), SkBits2Float(0x41c3e975), SkBits2Float(0x42592f1d)); // 24.651f, 54.361f, 24.511f, 54.361f, 24.489f, 54.296f +path.cubicTo(SkBits2Float(0x41c3a7ec), SkBits2Float(0x4258cbc9), SkBits2Float(0x41c42afe), SkBits2Float(0x4257d919), SkBits2Float(0x41c44185), SkBits2Float(0x42578002)); // 24.457f, 54.199f, 24.521f, 53.962f, 24.532f, 53.875f +path.cubicTo(SkBits2Float(0x41c46e93), SkBits2Float(0x42563f7f), SkBits2Float(0x41c4c6a4), SkBits2Float(0x42550a3f), SkBits2Float(0x41c5353b), SkBits2Float(0x4253df3d)); // 24.554f, 53.562f, 24.597f, 53.26f, 24.651f, 52.968f +path.cubicTo(SkBits2Float(0x41c54bc2), SkBits2Float(0x42537be9), SkBits2Float(0x41c56249), SkBits2Float(0x42530d51), SkBits2Float(0x41c58d4b), SkBits2Float(0x4252cac2)); // 24.662f, 52.871f, 24.673f, 52.763f, 24.694f, 52.698f +path.cubicTo(SkBits2Float(0x41c58d4b), SkBits2Float(0x4252a9fd), SkBits2Float(0x41c56249), SkBits2Float(0x42528833), SkBits2Float(0x41c58d4b), SkBits2Float(0x425271ab)); // 24.694f, 52.666f, 24.673f, 52.633f, 24.694f, 52.611f +path.moveTo(SkBits2Float(0x41c36662), SkBits2Float(0x42534fe0)); // 24.425f, 52.828f +path.cubicTo(SkBits2Float(0x41c33954), SkBits2Float(0x4253c9bb), SkBits2Float(0x41c34dcf), SkBits2Float(0x42541688), SkBits2Float(0x41c322cc), SkBits2Float(0x42549063)); // 24.403f, 52.947f, 24.413f, 53.022f, 24.392f, 53.141f +path.cubicTo(SkBits2Float(0x41c2f5be), SkBits2Float(0x4254fefb), SkBits2Float(0x41c2b434), SkBits2Float(0x42558e57), SkBits2Float(0x41c29dad), SkBits2Float(0x42560832)); // 24.37f, 53.249f, 24.338f, 53.389f, 24.327f, 53.508f +path.cubicTo(SkBits2Float(0x41c2709f), SkBits2Float(0x4256e561), SkBits2Float(0x41c2459d), SkBits2Float(0x4257ad0f), SkBits2Float(0x41c1ed8c), SkBits2Float(0x42586874)); // 24.305f, 53.724f, 24.284f, 53.919f, 24.241f, 54.102f +path.cubicTo(SkBits2Float(0x41c1d705), SkBits2Float(0x4258cbc8), SkBits2Float(0x41c20207), SkBits2Float(0x42590e57), SkBits2Float(0x41c1c07e), SkBits2Float(0x425950e7)); // 24.23f, 54.199f, 24.251f, 54.264f, 24.219f, 54.329f +path.cubicTo(SkBits2Float(0x41c1c07e), SkBits2Float(0x42596668), SkBits2Float(0x41c1686e), SkBits2Float(0x42599270), SkBits2Float(0x41c13b5f), SkBits2Float(0x42599270)); // 24.219f, 54.35f, 24.176f, 54.393f, 24.154f, 54.393f +path.cubicTo(SkBits2Float(0x41c0ccc7), SkBits2Float(0x4259a8f7), SkBits2Float(0x41c074b7), SkBits2Float(0x42599270), SkBits2Float(0x41c00620), SkBits2Float(0x425971ab)); // 24.1f, 54.415f, 24.057f, 54.393f, 24.003f, 54.361f +path.cubicTo(SkBits2Float(0x41c00620), SkBits2Float(0x425825e4), SkBits2Float(0x41c08b3f), SkBits2Float(0x4256da1e), SkBits2Float(0x41c0f9d6), SkBits2Float(0x42558e57)); // 24.003f, 54.037f, 24.068f, 53.713f, 24.122f, 53.389f +path.cubicTo(SkBits2Float(0x41c151e6), SkBits2Float(0x425479dc), SkBits2Float(0x41c151e6), SkBits2Float(0x42534fe0), SkBits2Float(0x41c1ed8c), SkBits2Float(0x425245a3)); // 24.165f, 53.119f, 24.165f, 52.828f, 24.241f, 52.568f +path.cubicTo(SkBits2Float(0x41c22f15), SkBits2Float(0x42520f5d), SkBits2Float(0x41c22f15), SkBits2Float(0x4251d70b), SkBits2Float(0x41c25c24), SkBits2Float(0x4251ccce)); // 24.273f, 52.515f, 24.273f, 52.46f, 24.295f, 52.45f +path.cubicTo(SkBits2Float(0x41c2e143), SkBits2Float(0x4251b647), SkBits2Float(0x41c34dce), SkBits2Float(0x4251e24f), SkBits2Float(0x41c3a7eb), SkBits2Float(0x4251e24f)); // 24.36f, 52.428f, 24.413f, 52.471f, 24.457f, 52.471f +path.cubicTo(SkBits2Float(0x41c3be72), SkBits2Float(0x42525c2a), SkBits2Float(0x41c37add), SkBits2Float(0x4252e149), SkBits2Float(0x41c36662), SkBits2Float(0x42534fe0)); // 24.468f, 52.59f, 24.435f, 52.72f, 24.425f, 52.828f +path.moveTo(SkBits2Float(0x41b3105e), SkBits2Float(0x426e020d)); // 22.383f, 59.502f +path.cubicTo(SkBits2Float(0x41b2ced5), SkBits2Float(0x426dcac1), SkBits2Float(0x41b28b3f), SkBits2Float(0x426d9375), SkBits2Float(0x41b21ca8), SkBits2Float(0x426d676d)); // 22.351f, 59.448f, 22.318f, 59.394f, 22.264f, 59.351f +path.lineTo(SkBits2Float(0x41b1f1a6), SkBits2Float(0x426d676d)); // 22.243f, 59.351f +path.lineTo(SkBits2Float(0x41b1f1a6), SkBits2Float(0x426d50e6)); // 22.243f, 59.329f +path.cubicTo(SkBits2Float(0x41b1f1a6), SkBits2Float(0x426d2f1b), SkBits2Float(0x41b1830e), SkBits2Float(0x426d199a), SkBits2Float(0x41b15600), SkBits2Float(0x426d0f5d)); // 22.243f, 59.296f, 22.189f, 59.275f, 22.167f, 59.265f +path.cubicTo(SkBits2Float(0x41b0e768), SkBits2Float(0x426cccce), SkBits2Float(0x41af1683), SkBits2Float(0x426bd917), SkBits2Float(0x41aefffc), SkBits2Float(0x426b8107)); // 22.113f, 59.2f, 21.886f, 58.962f, 21.875f, 58.876f +path.cubicTo(SkBits2Float(0x41aeeb81), SkBits2Float(0x426b3334), SkBits2Float(0x41af5a19), SkBits2Float(0x426acfe0), SkBits2Float(0x41af70a0), SkBits2Float(0x426a8d51)); // 21.865f, 58.8f, 21.919f, 58.703f, 21.93f, 58.638f +path.cubicTo(SkBits2Float(0x41b04dcf), SkBits2Float(0x42693647), SkBits2Float(0x41b1db1f), SkBits2Float(0x4268645b), SkBits2Float(0x41b43123), SkBits2Float(0x4267c9bc)); // 22.038f, 58.303f, 22.232f, 58.098f, 22.524f, 57.947f +path.cubicTo(SkBits2Float(0x41b472ac), SkBits2Float(0x4267a7f1), SkBits2Float(0x41b4f7cb), SkBits2Float(0x426770a5), SkBits2Float(0x41b56662), SkBits2Float(0x42676668)); // 22.556f, 57.914f, 22.621f, 57.86f, 22.675f, 57.85f +path.cubicTo(SkBits2Float(0x41b5a7eb), SkBits2Float(0x42675b24), SkBits2Float(0x41b5d4fa), SkBits2Float(0x42676668), SkBits2Float(0x41b62d0a), SkBits2Float(0x42675b24)); // 22.707f, 57.839f, 22.729f, 57.85f, 22.772f, 57.839f +path.cubicTo(SkBits2Float(0x41b69ba2), SkBits2Float(0x42674fe0), SkBits2Float(0x41b78f58), SkBits2Float(0x42671895), SkBits2Float(0x41b828f1), SkBits2Float(0x42671895)); // 22.826f, 57.828f, 22.945f, 57.774f, 23.02f, 57.774f +path.cubicTo(SkBits2Float(0x41b8ae10), SkBits2Float(0x42671895), SkBits2Float(0x41b8c497), SkBits2Float(0x42672f1c), SkBits2Float(0x41b91ca7), SkBits2Float(0x4267449d)); // 23.085f, 57.774f, 23.096f, 57.796f, 23.139f, 57.817f +path.lineTo(SkBits2Float(0x41b91ca7), SkBits2Float(0x42675b24)); // 23.139f, 57.839f +path.cubicTo(SkBits2Float(0x41b91ca7), SkBits2Float(0x42674fe0), SkBits2Float(0x41b9332e), SkBits2Float(0x426770a5), SkBits2Float(0x41b9332e), SkBits2Float(0x4267872c)); // 23.139f, 57.828f, 23.15f, 57.86f, 23.15f, 57.882f +path.cubicTo(SkBits2Float(0x41b91ca7), SkBits2Float(0x4267df3c), SkBits2Float(0x41b90620), SkBits2Float(0x42685918), SkBits2Float(0x41b8db1e), SkBits2Float(0x4268bc6b)); // 23.139f, 57.968f, 23.128f, 58.087f, 23.107f, 58.184f +path.cubicTo(SkBits2Float(0x41b855ff), SkBits2Float(0x426a29fc), SkBits2Float(0x41b7d0e1), SkBits2Float(0x426bc290), SkBits2Float(0x41b76249), SkBits2Float(0x426d2f1b)); // 23.042f, 58.541f, 22.977f, 58.94f, 22.923f, 59.296f +path.cubicTo(SkBits2Float(0x41b720c0), SkBits2Float(0x426e0d50), SkBits2Float(0x41b720c0), SkBits2Float(0x426ed3f8), SkBits2Float(0x41b69ba1), SkBits2Float(0x426f79dc)); // 22.891f, 59.513f, 22.891f, 59.707f, 22.826f, 59.869f +path.cubicTo(SkBits2Float(0x41b64391), SkBits2Float(0x426f645b), SkBits2Float(0x41b62d09), SkBits2Float(0x426f79dc), SkBits2Float(0x41b5eb80), SkBits2Float(0x426f645b)); // 22.783f, 59.848f, 22.772f, 59.869f, 22.74f, 59.848f +path.cubicTo(SkBits2Float(0x41b5a7ea), SkBits2Float(0x426f5917), SkBits2Float(0x41b57adc), SkBits2Float(0x426f374d), SkBits2Float(0x41b53953), SkBits2Float(0x426f1688)); // 22.707f, 59.837f, 22.685f, 59.804f, 22.653f, 59.772f +path.lineTo(SkBits2Float(0x41b53953), SkBits2Float(0x426f0107)); // 22.653f, 59.751f +path.cubicTo(SkBits2Float(0x41b472ab), SkBits2Float(0x426ea7f0), SkBits2Float(0x41b3ac03), SkBits2Float(0x426e5a1e), SkBits2Float(0x41b3105d), SkBits2Float(0x426e020d)); // 22.556f, 59.664f, 22.459f, 59.588f, 22.383f, 59.502f + testSimplify(reporter, path, filename); +} + +static void joel_5(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x43c5145a), SkBits2Float(0x43dc82f2)); // 394.159f, 441.023f +path.lineTo(SkBits2Float(0x43c5145a), SkBits2Float(0x43dc82f2)); // 394.159f, 441.023f +path.close(); +path.moveTo(SkBits2Float(0x43af4e56), SkBits2Float(0x43dbc604)); // 350.612f, 439.547f +path.lineTo(SkBits2Float(0x43af4e56), SkBits2Float(0x43dbc604)); // 350.612f, 439.547f +path.close(); +path.moveTo(SkBits2Float(0x43af4e56), SkBits2Float(0x43dbc604)); // 350.612f, 439.547f +path.cubicTo(SkBits2Float(0x43b64a5e), SkBits2Float(0x43dc9604), SkBits2Float(0x43be0958), SkBits2Float(0x43dbb604), SkBits2Float(0x43c5145a), SkBits2Float(0x43dc8312)); // 364.581f, 441.172f, 380.073f, 439.422f, 394.159f, 441.024f +path.cubicTo(SkBits2Float(0x43be0958), SkBits2Float(0x43dbb604), SkBits2Float(0x43b64a5e), SkBits2Float(0x43dc9604), SkBits2Float(0x43af4e56), SkBits2Float(0x43dbc604)); // 380.073f, 439.422f, 364.581f, 441.172f, 350.612f, 439.547f +path.close(); +path.moveTo(SkBits2Float(0x43a9126f), SkBits2Float(0x43e11604)); // 338.144f, 450.172f +path.lineTo(SkBits2Float(0x43a9126f), SkBits2Float(0x43e11604)); // 338.144f, 450.172f +path.close(); +path.moveTo(SkBits2Float(0x43a9126f), SkBits2Float(0x43e11604)); // 338.144f, 450.172f +path.cubicTo(SkBits2Float(0x43ab3c6b), SkBits2Float(0x43debc08), SkBits2Float(0x43ad1b65), SkBits2Float(0x43de18f6), SkBits2Float(0x43af4e77), SkBits2Float(0x43dbc604)); // 342.472f, 445.469f, 346.214f, 444.195f, 350.613f, 439.547f +path.cubicTo(SkBits2Float(0x43ad1b65), SkBits2Float(0x43de18f6), SkBits2Float(0x43ab3c6b), SkBits2Float(0x43debc08), SkBits2Float(0x43a9126f), SkBits2Float(0x43e11604)); // 346.214f, 444.195f, 342.472f, 445.469f, 338.144f, 450.172f +path.close(); +path.moveTo(SkBits2Float(0x43aa9d50), SkBits2Float(0x43e173f8)); // 341.229f, 450.906f +path.lineTo(SkBits2Float(0x43aa9d50), SkBits2Float(0x43e173f8)); // 341.229f, 450.906f +path.close(); +path.moveTo(SkBits2Float(0x43aa9d50), SkBits2Float(0x43e173f8)); // 341.229f, 450.906f +path.cubicTo(SkBits2Float(0x43aa0852), SkBits2Float(0x43e183f8), SkBits2Float(0x43a9be56), SkBits2Float(0x43e0d2f2), SkBits2Float(0x43a9124e), SkBits2Float(0x43e11604)); // 340.065f, 451.031f, 339.487f, 449.648f, 338.143f, 450.172f +path.cubicTo(SkBits2Float(0x43a9be56), SkBits2Float(0x43e0d2f2), SkBits2Float(0x43aa0852), SkBits2Float(0x43e183f8), SkBits2Float(0x43aa9d50), SkBits2Float(0x43e173f8)); // 339.487f, 449.648f, 340.065f, 451.031f, 341.229f, 450.906f +path.close(); +path.moveTo(SkBits2Float(0x43b13667), SkBits2Float(0x43dce106)); // 354.425f, 441.758f +path.lineTo(SkBits2Float(0x43b13667), SkBits2Float(0x43dce106)); // 354.425f, 441.758f +path.close(); +path.moveTo(SkBits2Float(0x43b13667), SkBits2Float(0x43dce106)); // 354.425f, 441.758f +path.cubicTo(SkBits2Float(0x43aead71), SkBits2Float(0x43dd9d0e), SkBits2Float(0x43acd375), SkBits2Float(0x43dff20c), SkBits2Float(0x43aa9d71), SkBits2Float(0x43e173f8)); // 349.355f, 443.227f, 345.652f, 447.891f, 341.23f, 450.906f +path.cubicTo(SkBits2Float(0x43acd354), SkBits2Float(0x43dff20c), SkBits2Float(0x43aead50), SkBits2Float(0x43dd9d0f), SkBits2Float(0x43b13667), SkBits2Float(0x43dce106)); // 345.651f, 447.891f, 349.354f, 443.227f, 354.425f, 441.758f +path.close(); +path.moveTo(SkBits2Float(0x43ac8561), SkBits2Float(0x43e30106)); // 345.042f, 454.008f +path.lineTo(SkBits2Float(0x43ac8561), SkBits2Float(0x43e30106)); // 345.042f, 454.008f +path.close(); +path.moveTo(SkBits2Float(0x43ac8561), SkBits2Float(0x43e30106)); // 345.042f, 454.008f +path.cubicTo(SkBits2Float(0x43adc76d), SkBits2Float(0x43e0f4fe), SkBits2Float(0x43b21a5f), SkBits2Float(0x43df7efa), SkBits2Float(0x43b13667), SkBits2Float(0x43dce106)); // 347.558f, 449.914f, 356.206f, 446.992f, 354.425f, 441.758f +path.cubicTo(SkBits2Float(0x43b21a5f), SkBits2Float(0x43df7efa), SkBits2Float(0x43adc76d), SkBits2Float(0x43e0f4fe), SkBits2Float(0x43ac8561), SkBits2Float(0x43e30106)); // 356.206f, 446.992f, 347.558f, 449.914f, 345.042f, 454.008f +path.close(); +path.moveTo(SkBits2Float(0x43b33169), SkBits2Float(0x43dc82f2)); // 358.386f, 441.023f +path.lineTo(SkBits2Float(0x43b33169), SkBits2Float(0x43dc82f2)); // 358.386f, 441.023f +path.close(); +path.moveTo(SkBits2Float(0x43b33169), SkBits2Float(0x43dc82f2)); // 358.386f, 441.023f +path.cubicTo(SkBits2Float(0x43b16169), SkBits2Float(0x43ded7f0), SkBits2Float(0x43aef375), SkBits2Float(0x43e13be8), SkBits2Float(0x43ac8561), SkBits2Float(0x43e300e6)); // 354.761f, 445.687f, 349.902f, 450.468f, 345.042f, 454.007f +path.cubicTo(SkBits2Float(0x43aef355), SkBits2Float(0x43e13c09), SkBits2Float(0x43b16169), SkBits2Float(0x43ded811), SkBits2Float(0x43b33169), SkBits2Float(0x43dc82f2)); // 349.901f, 450.469f, 354.761f, 445.688f, 358.386f, 441.023f +path.close(); +path.moveTo(SkBits2Float(0x43b4bb65), SkBits2Float(0x43dd4000)); // 361.464f, 442.5f +path.lineTo(SkBits2Float(0x43b4bb65), SkBits2Float(0x43dd4000)); // 361.464f, 442.5f +path.close(); +path.moveTo(SkBits2Float(0x43b4bb65), SkBits2Float(0x43dd4000)); // 361.464f, 442.5f +path.cubicTo(SkBits2Float(0x43b44959), SkBits2Float(0x43dcddf4), SkBits2Float(0x43b3e76d), SkBits2Float(0x43dc48f6), SkBits2Float(0x43b33169), SkBits2Float(0x43dc82f2)); // 360.573f, 441.734f, 359.808f, 440.57f, 358.386f, 441.023f +path.cubicTo(SkBits2Float(0x43b3e76d), SkBits2Float(0x43dc48f6), SkBits2Float(0x43b44959), SkBits2Float(0x43dcddf4), SkBits2Float(0x43b4bb65), SkBits2Float(0x43dd4000)); // 359.808f, 440.57f, 360.573f, 441.734f, 361.464f, 442.5f +path.close(); +path.moveTo(SkBits2Float(0x43ae7f5d), SkBits2Float(0x43e5a70a)); // 348.995f, 459.305f +path.lineTo(SkBits2Float(0x43ae7f5d), SkBits2Float(0x43e5a70a)); // 348.995f, 459.305f +path.close(); +path.moveTo(SkBits2Float(0x43ae7f5d), SkBits2Float(0x43e5a70a)); // 348.995f, 459.305f +path.cubicTo(SkBits2Float(0x43af945b), SkBits2Float(0x43e21d0e), SkBits2Float(0x43b3a74d), SkBits2Float(0x43e0ce14), SkBits2Float(0x43b4bb65), SkBits2Float(0x43dd4000)); // 351.159f, 452.227f, 359.307f, 449.61f, 361.464f, 442.5f +path.cubicTo(SkBits2Float(0x43b3a76d), SkBits2Float(0x43e0cdf4), SkBits2Float(0x43af945b), SkBits2Float(0x43e21d0e), SkBits2Float(0x43ae7f5d), SkBits2Float(0x43e5a70a)); // 359.308f, 449.609f, 351.159f, 452.227f, 348.995f, 459.305f +path.close(); +path.moveTo(SkBits2Float(0x43b58a5f), SkBits2Float(0x43dce106)); // 363.081f, 441.758f +path.lineTo(SkBits2Float(0x43b58a5f), SkBits2Float(0x43dce106)); // 363.081f, 441.758f +path.close(); +path.moveTo(SkBits2Float(0x43b58a5f), SkBits2Float(0x43dce106)); // 363.081f, 441.758f +path.cubicTo(SkBits2Float(0x43b2c063), SkBits2Float(0x43dfa604), SkBits2Float(0x43b1d561), SkBits2Float(0x43e374fe), SkBits2Float(0x43ae7f5d), SkBits2Float(0x43e5a70a)); // 357.503f, 447.297f, 355.667f, 454.914f, 348.995f, 459.305f +path.cubicTo(SkBits2Float(0x43b1d561), SkBits2Float(0x43e374fe), SkBits2Float(0x43b2c063), SkBits2Float(0x43dfa604), SkBits2Float(0x43b58a5f), SkBits2Float(0x43dce106)); // 355.667f, 454.914f, 357.503f, 447.297f, 363.081f, 441.758f +path.close(); +path.moveTo(SkBits2Float(0x43b6b561), SkBits2Float(0x43dd4000)); // 365.417f, 442.5f +path.lineTo(SkBits2Float(0x43b6b561), SkBits2Float(0x43dd4000)); // 365.417f, 442.5f +path.close(); +path.moveTo(SkBits2Float(0x43b6b561), SkBits2Float(0x43dd4000)); // 365.417f, 442.5f +path.lineTo(SkBits2Float(0x43b58a5f), SkBits2Float(0x43dce106)); // 363.081f, 441.758f +path.lineTo(SkBits2Float(0x43b6b561), SkBits2Float(0x43dd4000)); // 365.417f, 442.5f +path.close(); +path.moveTo(SkBits2Float(0x43b07a5f), SkBits2Float(0x43e7220c)); // 352.956f, 462.266f +path.lineTo(SkBits2Float(0x43b07a5f), SkBits2Float(0x43e7220c)); // 352.956f, 462.266f +path.close(); +path.moveTo(SkBits2Float(0x43b07a5f), SkBits2Float(0x43e7220c)); // 352.956f, 462.266f +path.cubicTo(SkBits2Float(0x43b29f5d), SkBits2Float(0x43e3e810), SkBits2Float(0x43b59667), SkBits2Float(0x43e0f916), SkBits2Float(0x43b6b561), SkBits2Float(0x43dd4000)); // 357.245f, 455.813f, 363.175f, 449.946f, 365.417f, 442.5f +path.cubicTo(SkBits2Float(0x43b59667), SkBits2Float(0x43e0f8f6), SkBits2Float(0x43b29f5d), SkBits2Float(0x43e3e7f0), SkBits2Float(0x43b07a5f), SkBits2Float(0x43e7220c)); // 363.175f, 449.945f, 357.245f, 455.812f, 352.956f, 462.266f +path.close(); +path.moveTo(SkBits2Float(0x43b0d853), SkBits2Float(0x43e84efa)); // 353.69f, 464.617f +path.lineTo(SkBits2Float(0x43b0d853), SkBits2Float(0x43e84efa)); // 353.69f, 464.617f +path.close(); +path.moveTo(SkBits2Float(0x43b0d853), SkBits2Float(0x43e84efa)); // 353.69f, 464.617f +path.cubicTo(SkBits2Float(0x43b03a5f), SkBits2Float(0x43e934fe), SkBits2Float(0x43b1345b), SkBits2Float(0x43e7870a), SkBits2Float(0x43b07a5f), SkBits2Float(0x43e721ec)); // 352.456f, 466.414f, 354.409f, 463.055f, 352.956f, 462.265f +path.cubicTo(SkBits2Float(0x43b1345b), SkBits2Float(0x43e7870b), SkBits2Float(0x43b03a5f), SkBits2Float(0x43e934fe), SkBits2Float(0x43b0d853), SkBits2Float(0x43e84efa)); // 354.409f, 463.055f, 352.456f, 466.414f, 353.69f, 464.617f +path.close(); +path.moveTo(SkBits2Float(0x43b84063), SkBits2Float(0x43ddb106)); // 368.503f, 443.383f +path.lineTo(SkBits2Float(0x43b84063), SkBits2Float(0x43ddb106)); // 368.503f, 443.383f +path.close(); +path.moveTo(SkBits2Float(0x43b84063), SkBits2Float(0x43ddb106)); // 368.503f, 443.383f +path.cubicTo(SkBits2Float(0x43b42667), SkBits2Float(0x43e039fc), SkBits2Float(0x43b39d71), SkBits2Float(0x43e4e000), SkBits2Float(0x43b0d873), SkBits2Float(0x43e84efa)); // 360.3f, 448.453f, 359.23f, 457.75f, 353.691f, 464.617f +path.cubicTo(SkBits2Float(0x43b39d50), SkBits2Float(0x43e4e000), SkBits2Float(0x43b42667), SkBits2Float(0x43e039fc), SkBits2Float(0x43b84063), SkBits2Float(0x43ddb106)); // 359.229f, 457.75f, 360.3f, 448.453f, 368.503f, 443.383f +path.close(); +path.moveTo(SkBits2Float(0x43b89d51), SkBits2Float(0x43de0efa)); // 369.229f, 444.117f +path.lineTo(SkBits2Float(0x43b89d51), SkBits2Float(0x43de0efa)); // 369.229f, 444.117f +path.close(); +path.moveTo(SkBits2Float(0x43b89d51), SkBits2Float(0x43de0efa)); // 369.229f, 444.117f +path.lineTo(SkBits2Float(0x43b84043), SkBits2Float(0x43ddb106)); // 368.502f, 443.383f +path.lineTo(SkBits2Float(0x43b89d51), SkBits2Float(0x43de0efa)); // 369.229f, 444.117f +path.close(); +path.moveTo(SkBits2Float(0x43b26270), SkBits2Float(0x43e90c08)); // 356.769f, 466.094f +path.lineTo(SkBits2Float(0x43b26270), SkBits2Float(0x43e90c08)); // 356.769f, 466.094f +path.close(); +path.moveTo(SkBits2Float(0x43b26270), SkBits2Float(0x43e90c08)); // 356.769f, 466.094f +path.cubicTo(SkBits2Float(0x43b48d72), SkBits2Float(0x43e569fc), SkBits2Float(0x43b7897a), SkBits2Float(0x43e21d0e), SkBits2Float(0x43b89d72), SkBits2Float(0x43de0efa)); // 361.105f, 458.828f, 367.074f, 452.227f, 369.23f, 444.117f +path.cubicTo(SkBits2Float(0x43b78959), SkBits2Float(0x43e21d0e), SkBits2Float(0x43b48d51), SkBits2Float(0x43e569fc), SkBits2Float(0x43b26270), SkBits2Float(0x43e90c08)); // 367.073f, 452.227f, 361.104f, 458.828f, 356.769f, 466.094f +path.close(); +path.moveTo(SkBits2Float(0x43b3316a), SkBits2Float(0x43e90c08)); // 358.386f, 466.094f +path.lineTo(SkBits2Float(0x43b3316a), SkBits2Float(0x43e90c08)); // 358.386f, 466.094f +path.close(); +path.moveTo(SkBits2Float(0x43b3316a), SkBits2Float(0x43e90c08)); // 358.386f, 466.094f +path.lineTo(SkBits2Float(0x43b26270), SkBits2Float(0x43e90c08)); // 356.769f, 466.094f +path.lineTo(SkBits2Float(0x43b3316a), SkBits2Float(0x43e90c08)); // 358.386f, 466.094f +path.close(); +path.moveTo(SkBits2Float(0x43ba2853), SkBits2Float(0x43ddb106)); // 372.315f, 443.383f +path.lineTo(SkBits2Float(0x43ba2853), SkBits2Float(0x43ddb106)); // 372.315f, 443.383f +path.close(); +path.moveTo(SkBits2Float(0x43ba2853), SkBits2Float(0x43ddb106)); // 372.315f, 443.383f +path.cubicTo(SkBits2Float(0x43b7d74d), SkBits2Float(0x43e17604), SkBits2Float(0x43b5824f), SkBits2Float(0x43e59604), SkBits2Float(0x43b33149), SkBits2Float(0x43e90c08)); // 367.682f, 450.922f, 363.018f, 459.172f, 358.385f, 466.094f +path.cubicTo(SkBits2Float(0x43b58270), SkBits2Float(0x43e59604), SkBits2Float(0x43b7d76e), SkBits2Float(0x43e17604), SkBits2Float(0x43ba2853), SkBits2Float(0x43ddb106)); // 363.019f, 459.172f, 367.683f, 450.922f, 372.315f, 443.383f +path.close(); +path.moveTo(SkBits2Float(0x43bb5355), SkBits2Float(0x43de0efa)); // 374.651f, 444.117f +path.lineTo(SkBits2Float(0x43bb5355), SkBits2Float(0x43de0efa)); // 374.651f, 444.117f +path.close(); +path.moveTo(SkBits2Float(0x43bb5355), SkBits2Float(0x43de0efa)); // 374.651f, 444.117f +path.cubicTo(SkBits2Float(0x43bb1853), SkBits2Float(0x43dd92f2), SkBits2Float(0x43ba9e57), SkBits2Float(0x43ddab02), SkBits2Float(0x43ba2853), SkBits2Float(0x43ddb106)); // 374.19f, 443.148f, 373.237f, 443.336f, 372.315f, 443.383f +path.cubicTo(SkBits2Float(0x43ba9e57), SkBits2Float(0x43ddab02), SkBits2Float(0x43bb1853), SkBits2Float(0x43dd92f2), SkBits2Float(0x43bb5355), SkBits2Float(0x43de0efa)); // 373.237f, 443.336f, 374.19f, 443.148f, 374.651f, 444.117f +path.close(); +path.moveTo(SkBits2Float(0x43b58a5f), SkBits2Float(0x43e90c08)); // 363.081f, 466.094f +path.lineTo(SkBits2Float(0x43b58a5f), SkBits2Float(0x43e90c08)); // 363.081f, 466.094f +path.close(); +path.moveTo(SkBits2Float(0x43b58a5f), SkBits2Float(0x43e90c08)); // 363.081f, 466.094f +path.cubicTo(SkBits2Float(0x43b76c6b), SkBits2Float(0x43e55d0e), SkBits2Float(0x43ba4a5f), SkBits2Float(0x43e21312), SkBits2Float(0x43bb5355), SkBits2Float(0x43de0efa)); // 366.847f, 458.727f, 372.581f, 452.149f, 374.651f, 444.117f +path.cubicTo(SkBits2Float(0x43ba4a5f), SkBits2Float(0x43e212f2), SkBits2Float(0x43b76c6c), SkBits2Float(0x43e55d0e), SkBits2Float(0x43b58a5f), SkBits2Float(0x43e90c08)); // 372.581f, 452.148f, 366.847f, 458.727f, 363.081f, 466.094f +path.close(); +path.moveTo(SkBits2Float(0x43b6b561), SkBits2Float(0x43e90c08)); // 365.417f, 466.094f +path.lineTo(SkBits2Float(0x43b6b561), SkBits2Float(0x43e90c08)); // 365.417f, 466.094f +path.close(); +path.moveTo(SkBits2Float(0x43b6b561), SkBits2Float(0x43e90c08)); // 365.417f, 466.094f +path.lineTo(SkBits2Float(0x43b58a5f), SkBits2Float(0x43e90c08)); // 363.081f, 466.094f +path.lineTo(SkBits2Float(0x43b6b561), SkBits2Float(0x43e90c08)); // 365.417f, 466.094f +path.close(); +path.moveTo(SkBits2Float(0x43bc8063), SkBits2Float(0x43e058f6)); // 377.003f, 448.695f +path.lineTo(SkBits2Float(0x43bc8063), SkBits2Float(0x43e058f6)); // 377.003f, 448.695f +path.close(); +path.moveTo(SkBits2Float(0x43bc8063), SkBits2Float(0x43e058f6)); // 377.003f, 448.695f +path.cubicTo(SkBits2Float(0x43b9de57), SkBits2Float(0x43e29df4), SkBits2Float(0x43b84355), SkBits2Float(0x43e5fefa), SkBits2Float(0x43b6b561), SkBits2Float(0x43e90be8)); // 371.737f, 453.234f, 368.526f, 459.992f, 365.417f, 466.093f +path.cubicTo(SkBits2Float(0x43b84355), SkBits2Float(0x43e5fefa), SkBits2Float(0x43b9de57), SkBits2Float(0x43e29df4), SkBits2Float(0x43bc8063), SkBits2Float(0x43e058f6)); // 368.526f, 459.992f, 371.737f, 453.234f, 377.003f, 448.695f +path.close(); +path.moveTo(SkBits2Float(0x43b89d51), SkBits2Float(0x43e969fc)); // 369.229f, 466.828f +path.lineTo(SkBits2Float(0x43b89d51), SkBits2Float(0x43e969fc)); // 369.229f, 466.828f +path.close(); +path.moveTo(SkBits2Float(0x43b89d51), SkBits2Float(0x43e969fc)); // 369.229f, 466.828f +path.cubicTo(SkBits2Float(0x43b98149), SkBits2Float(0x43e637f0), SkBits2Float(0x43bd3355), SkBits2Float(0x43e3adf4), SkBits2Float(0x43bc8043), SkBits2Float(0x43e058f6)); // 371.01f, 460.437f, 378.401f, 455.359f, 377.002f, 448.695f +path.cubicTo(SkBits2Float(0x43bd3355), SkBits2Float(0x43e3adf4), SkBits2Float(0x43b9816a), SkBits2Float(0x43e638f6), SkBits2Float(0x43b89d51), SkBits2Float(0x43e969fc)); // 378.401f, 455.359f, 371.011f, 460.445f, 369.229f, 466.828f +path.close(); +path.moveTo(SkBits2Float(0x43ba8668), SkBits2Float(0x43e9c7f0)); // 373.05f, 467.562f +path.lineTo(SkBits2Float(0x43ba8668), SkBits2Float(0x43e9c7f0)); // 373.05f, 467.562f +path.close(); +path.moveTo(SkBits2Float(0x43ba8668), SkBits2Float(0x43e9c7f0)); // 373.05f, 467.562f +path.cubicTo(SkBits2Float(0x43ba1376), SkBits2Float(0x43e90000), SkBits2Float(0x43b94270), SkBits2Float(0x43e8f1ec), SkBits2Float(0x43b89d72), SkBits2Float(0x43e969fc)); // 372.152f, 466, 370.519f, 465.89f, 369.23f, 466.828f +path.cubicTo(SkBits2Float(0x43b94270), SkBits2Float(0x43e8f20c), SkBits2Float(0x43ba1355), SkBits2Float(0x43e90000), SkBits2Float(0x43ba8668), SkBits2Float(0x43e9c7f0)); // 370.519f, 465.891f, 372.151f, 466, 373.05f, 467.562f +path.close(); +path.moveTo(SkBits2Float(0x43c0c064), SkBits2Float(0x43dc82f2)); // 385.503f, 441.023f +path.lineTo(SkBits2Float(0x43c0c064), SkBits2Float(0x43dc82f2)); // 385.503f, 441.023f +path.close(); +path.moveTo(SkBits2Float(0x43c0c064), SkBits2Float(0x43dc82f2)); // 385.503f, 441.023f +path.cubicTo(SkBits2Float(0x43be095a), SkBits2Float(0x43e0acee), SkBits2Float(0x43bd8a60), SkBits2Float(0x43e5c0e6), SkBits2Float(0x43ba8668), SkBits2Float(0x43e9c7f0)); // 380.073f, 449.351f, 379.081f, 459.507f, 373.05f, 467.562f +path.cubicTo(SkBits2Float(0x43bd8a60), SkBits2Float(0x43e5c107), SkBits2Float(0x43be095a), SkBits2Float(0x43e0ad0f), SkBits2Float(0x43c0c064), SkBits2Float(0x43dc82f2)); // 379.081f, 459.508f, 380.073f, 449.352f, 385.503f, 441.023f +path.close(); +path.moveTo(SkBits2Float(0x43c00562), SkBits2Float(0x43e23000)); // 384.042f, 452.375f +path.lineTo(SkBits2Float(0x43c00562), SkBits2Float(0x43e23000)); // 384.042f, 452.375f +path.close(); +path.moveTo(SkBits2Float(0x43c00562), SkBits2Float(0x43e23000)); // 384.042f, 452.375f +path.cubicTo(SkBits2Float(0x43bfaf5e), SkBits2Float(0x43e013f8), SkBits2Float(0x43c40668), SkBits2Float(0x43ddc2f2), SkBits2Float(0x43c0c064), SkBits2Float(0x43dc82f2)); // 383.37f, 448.156f, 392.05f, 443.523f, 385.503f, 441.023f +path.cubicTo(SkBits2Float(0x43c40668), SkBits2Float(0x43ddc2f2), SkBits2Float(0x43bfaf5e), SkBits2Float(0x43e013f8), SkBits2Float(0x43c00562), SkBits2Float(0x43e23000)); // 392.05f, 443.523f, 383.37f, 448.156f, 384.042f, 452.375f +path.close(); +path.moveTo(SkBits2Float(0x43bed854), SkBits2Float(0x43e5370a)); // 381.69f, 458.43f +path.lineTo(SkBits2Float(0x43bed854), SkBits2Float(0x43e5370a)); // 381.69f, 458.43f +path.close(); +path.moveTo(SkBits2Float(0x43bed854), SkBits2Float(0x43e5370a)); // 381.69f, 458.43f +path.cubicTo(SkBits2Float(0x43c06562), SkBits2Float(0x43e4b4fe), SkBits2Float(0x43bf095a), SkBits2Float(0x43e2fd0e), SkBits2Float(0x43c00562), SkBits2Float(0x43e23000)); // 384.792f, 457.414f, 382.073f, 453.977f, 384.042f, 452.375f +path.cubicTo(SkBits2Float(0x43bf095a), SkBits2Float(0x43e2fdf4), SkBits2Float(0x43c06562), SkBits2Float(0x43e4b4fe), SkBits2Float(0x43bed854), SkBits2Float(0x43e5370a)); // 382.073f, 453.984f, 384.792f, 457.414f, 381.69f, 458.43f +path.close(); +path.moveTo(SkBits2Float(0x43bf3668), SkBits2Float(0x43e5a70a)); // 382.425f, 459.305f +path.lineTo(SkBits2Float(0x43bf3668), SkBits2Float(0x43e5a70a)); // 382.425f, 459.305f +path.close(); +path.moveTo(SkBits2Float(0x43bf3668), SkBits2Float(0x43e5a70a)); // 382.425f, 459.305f +path.lineTo(SkBits2Float(0x43bed874), SkBits2Float(0x43e5370a)); // 381.691f, 458.43f +path.lineTo(SkBits2Float(0x43bf3668), SkBits2Float(0x43e5a70a)); // 382.425f, 459.305f +path.close(); +path.moveTo(SkBits2Float(0x43bcde58), SkBits2Float(0x43e9c7ef)); // 377.737f, 467.562f +path.lineTo(SkBits2Float(0x43bcde58), SkBits2Float(0x43e9c7ef)); // 377.737f, 467.562f +path.close(); +path.moveTo(SkBits2Float(0x43bcde58), SkBits2Float(0x43e9c7ef)); // 377.737f, 467.562f +path.cubicTo(SkBits2Float(0x43bdfb66), SkBits2Float(0x43e888f5), SkBits2Float(0x43bd6854), SkBits2Float(0x43e69ced), SkBits2Float(0x43bf3668), SkBits2Float(0x43e5a6e9)); // 379.964f, 465.07f, 378.815f, 461.226f, 382.425f, 459.304f +path.cubicTo(SkBits2Float(0x43bd6854), SkBits2Float(0x43e69d0e), SkBits2Float(0x43bdfb66), SkBits2Float(0x43e888f5), SkBits2Float(0x43bcde58), SkBits2Float(0x43e9c7ef)); // 378.815f, 461.227f, 379.964f, 465.07f, 377.737f, 467.562f +path.close(); +path.moveTo(SkBits2Float(0x43bf3668), SkBits2Float(0x43ea9810)); // 382.425f, 469.188f +path.lineTo(SkBits2Float(0x43bf3668), SkBits2Float(0x43ea9810)); // 382.425f, 469.188f +path.close(); +path.moveTo(SkBits2Float(0x43bf3668), SkBits2Float(0x43ea9810)); // 382.425f, 469.188f +path.cubicTo(SkBits2Float(0x43bebf5e), SkBits2Float(0x43e99e14), SkBits2Float(0x43bdc562), SkBits2Float(0x43e9d70a), SkBits2Float(0x43bcde58), SkBits2Float(0x43e9c810)); // 381.495f, 467.235f, 379.542f, 467.68f, 377.737f, 467.563f +path.cubicTo(SkBits2Float(0x43bdc562), SkBits2Float(0x43e9d70a), SkBits2Float(0x43bebf5e), SkBits2Float(0x43e99df3), SkBits2Float(0x43bf3668), SkBits2Float(0x43ea9810)); // 379.542f, 467.68f, 381.495f, 467.234f, 382.425f, 469.188f +path.close(); +path.moveTo(SkBits2Float(0x43c0c064), SkBits2Float(0x43e78000)); // 385.503f, 463 +path.lineTo(SkBits2Float(0x43c0c064), SkBits2Float(0x43e78000)); // 385.503f, 463 +path.close(); +path.moveTo(SkBits2Float(0x43c0c064), SkBits2Float(0x43e78000)); // 385.503f, 463 +path.cubicTo(SkBits2Float(0x43bfaf5e), SkBits2Float(0x43e7f9fc), SkBits2Float(0x43bfbe58), SkBits2Float(0x43e98b02), SkBits2Float(0x43bf3668), SkBits2Float(0x43ea9810)); // 383.37f, 463.953f, 383.487f, 467.086f, 382.425f, 469.188f +path.cubicTo(SkBits2Float(0x43bfbe58), SkBits2Float(0x43e98b02), SkBits2Float(0x43bfaf5e), SkBits2Float(0x43e7f9fc), SkBits2Float(0x43c0c064), SkBits2Float(0x43e78000)); // 383.487f, 467.086f, 383.37f, 463.953f, 385.503f, 463 +path.close(); +path.moveTo(SkBits2Float(0x43c1316a), SkBits2Float(0x43e35efa)); // 386.386f, 454.742f +path.lineTo(SkBits2Float(0x43c1316a), SkBits2Float(0x43e35efa)); // 386.386f, 454.742f +path.close(); +path.moveTo(SkBits2Float(0x43c1316a), SkBits2Float(0x43e35efa)); // 386.386f, 454.742f +path.cubicTo(SkBits2Float(0x43c35270), SkBits2Float(0x43e586ea), SkBits2Float(0x43beb064), SkBits2Float(0x43e561ec), SkBits2Float(0x43c0c064), SkBits2Float(0x43e78000)); // 390.644f, 459.054f, 381.378f, 458.765f, 385.503f, 463 +path.cubicTo(SkBits2Float(0x43beb064), SkBits2Float(0x43e5620c), SkBits2Float(0x43c35270), SkBits2Float(0x43e5870a), SkBits2Float(0x43c1316a), SkBits2Float(0x43e35efa)); // 381.378f, 458.766f, 390.644f, 459.055f, 386.386f, 454.742f +path.close(); +path.moveTo(SkBits2Float(0x43c3e76e), SkBits2Float(0x43df2b02)); // 391.808f, 446.336f +path.lineTo(SkBits2Float(0x43c3e76e), SkBits2Float(0x43df2b02)); // 391.808f, 446.336f +path.close(); +path.moveTo(SkBits2Float(0x43c3e76e), SkBits2Float(0x43df2b02)); // 391.808f, 446.336f +path.cubicTo(SkBits2Float(0x43c2ba60), SkBits2Float(0x43e07810), SkBits2Float(0x43c32a60), SkBits2Float(0x43e31106), SkBits2Float(0x43c1316a), SkBits2Float(0x43e35efa)); // 389.456f, 448.938f, 390.331f, 454.133f, 386.386f, 454.742f +path.cubicTo(SkBits2Float(0x43c32a60), SkBits2Float(0x43e31106), SkBits2Float(0x43c2ba60), SkBits2Float(0x43e07811), SkBits2Float(0x43c3e76e), SkBits2Float(0x43df2b02)); // 390.331f, 454.133f, 389.456f, 448.938f, 391.808f, 446.336f +path.close(); +path.moveTo(SkBits2Float(0x43c3e76e), SkBits2Float(0x43dd4000)); // 391.808f, 442.5f +path.lineTo(SkBits2Float(0x43c3e76e), SkBits2Float(0x43dd4000)); // 391.808f, 442.5f +path.close(); +path.moveTo(SkBits2Float(0x43c3e76e), SkBits2Float(0x43dd4000)); // 391.808f, 442.5f +path.cubicTo(SkBits2Float(0x43c2a668), SkBits2Float(0x43ddbefa), SkBits2Float(0x43c35f7e), SkBits2Float(0x43def4fe), SkBits2Float(0x43c3e76e), SkBits2Float(0x43df2b02)); // 389.3f, 443.492f, 390.746f, 445.914f, 391.808f, 446.336f +path.cubicTo(SkBits2Float(0x43c35f5e), SkBits2Float(0x43def4fe), SkBits2Float(0x43c2a668), SkBits2Float(0x43ddbefa), SkBits2Float(0x43c3e76e), SkBits2Float(0x43dd4000)); // 390.745f, 445.914f, 389.3f, 443.492f, 391.808f, 442.5f +path.close(); +path.moveTo(SkBits2Float(0x43c44562), SkBits2Float(0x43ddb106)); // 392.542f, 443.383f +path.lineTo(SkBits2Float(0x43c44562), SkBits2Float(0x43ddb106)); // 392.542f, 443.383f +path.close(); +path.moveTo(SkBits2Float(0x43c44562), SkBits2Float(0x43ddb106)); // 392.542f, 443.383f +path.lineTo(SkBits2Float(0x43c3e76e), SkBits2Float(0x43dd4000)); // 391.808f, 442.5f +path.lineTo(SkBits2Float(0x43c44562), SkBits2Float(0x43ddb106)); // 392.542f, 443.383f +path.close(); +path.moveTo(SkBits2Float(0x43c5145c), SkBits2Float(0x43dc82f2)); // 394.159f, 441.023f +path.lineTo(SkBits2Float(0x43c44562), SkBits2Float(0x43ddb0e6)); // 392.542f, 443.382f +path.lineTo(SkBits2Float(0x43c5145c), SkBits2Float(0x43dc82f2)); // 394.159f, 441.023f +path.close(); + testSimplify(reporter, path, filename); +} + +static void joel_6(skiatest::Reporter* reporter, const char* filename) { + SkPath path; +path.moveTo(SkBits2Float(0x43c38c6a), SkBits2Float(0x43a739fc)); // 391.097f, 334.453f +path.lineTo(SkBits2Float(0x43c36168), SkBits2Float(0x43a74efa)); // 390.761f, 334.617f +path.lineTo(SkBits2Float(0x43c33666), SkBits2Float(0x43a6f7f0)); // 390.425f, 333.937f +path.lineTo(SkBits2Float(0x43c36168), SkBits2Float(0x43a6e1ec)); // 390.761f, 333.765f +path.lineTo(SkBits2Float(0x43c38c6a), SkBits2Float(0x43a739fc)); // 391.097f, 334.453f +path.close(); +path.moveTo(SkBits2Float(0x43c39062), SkBits2Float(0x43a73810)); // 391.128f, 334.438f +path.lineTo(SkBits2Float(0x43c3676c), SkBits2Float(0x43a75106)); // 390.808f, 334.633f +path.lineTo(SkBits2Float(0x43c33374), SkBits2Float(0x43a6fefa)); // 390.402f, 333.992f +path.lineTo(SkBits2Float(0x43c35d70), SkBits2Float(0x43a6e3f8)); // 390.73f, 333.781f +path.lineTo(SkBits2Float(0x43c39062), SkBits2Float(0x43a73811)); // 391.128f, 334.438f +path.lineTo(SkBits2Float(0x43c39062), SkBits2Float(0x43a73810)); // 391.128f, 334.438f +path.close(); +path.moveTo(SkBits2Float(0x43e38958), SkBits2Float(0x43971c08)); // 455.073f, 302.219f +path.lineTo(SkBits2Float(0x43e3824e), SkBits2Float(0x43973000)); // 455.018f, 302.375f +path.lineTo(SkBits2Float(0x43e36f5c), SkBits2Float(0x439739fc)); // 454.87f, 302.453f +path.lineTo(SkBits2Float(0x43e35a5e), SkBits2Float(0x43970df4)); // 454.706f, 302.109f +path.lineTo(SkBits2Float(0x43e38958), SkBits2Float(0x43971c08)); // 455.073f, 302.219f +path.close(); +path.moveTo(SkBits2Float(0x43e36f5c), SkBits2Float(0x439739fc)); // 454.87f, 302.453f +path.lineTo(SkBits2Float(0x43c38c6a), SkBits2Float(0x43a739fc)); // 391.097f, 334.453f +path.lineTo(SkBits2Float(0x43c36168), SkBits2Float(0x43a6e1ec)); // 390.761f, 333.765f +path.lineTo(SkBits2Float(0x43e3445a), SkBits2Float(0x4396e1ec)); // 454.534f, 301.765f +path.lineTo(SkBits2Float(0x43e36f5c), SkBits2Float(0x439739fc)); // 454.87f, 302.453f +path.close(); +path.moveTo(SkBits2Float(0x43e41f5c), SkBits2Float(0x43946efa)); // 456.245f, 296.867f +path.lineTo(SkBits2Float(0x43e4545a), SkBits2Float(0x439479fc)); // 456.659f, 296.953f +path.lineTo(SkBits2Float(0x43e44354), SkBits2Float(0x4394acee)); // 456.526f, 297.351f +path.lineTo(SkBits2Float(0x43e41646), SkBits2Float(0x43949efa)); // 456.174f, 297.242f +path.lineTo(SkBits2Float(0x43e41f5d), SkBits2Float(0x43946efa)); // 456.245f, 296.867f +path.lineTo(SkBits2Float(0x43e41f5c), SkBits2Float(0x43946efa)); // 456.245f, 296.867f +path.close(); +path.moveTo(SkBits2Float(0x43e44354), SkBits2Float(0x4394ad0e)); // 456.526f, 297.352f +path.lineTo(SkBits2Float(0x43e38958), SkBits2Float(0x43971c08)); // 455.073f, 302.219f +path.lineTo(SkBits2Float(0x43e32b64), SkBits2Float(0x43970000)); // 454.339f, 302 +path.lineTo(SkBits2Float(0x43e3e76c), SkBits2Float(0x43949106)); // 455.808f, 297.133f +path.lineTo(SkBits2Float(0x43e44353), SkBits2Float(0x4394ad0e)); // 456.526f, 297.352f +path.lineTo(SkBits2Float(0x43e44354), SkBits2Float(0x4394ad0e)); // 456.526f, 297.352f +path.close(); +path.moveTo(SkBits2Float(0x43e17d50), SkBits2Float(0x4393f20c)); // 450.979f, 295.891f +path.lineTo(SkBits2Float(0x43e18e56), SkBits2Float(0x4393e810)); // 451.112f, 295.813f +path.lineTo(SkBits2Float(0x43e1a148), SkBits2Float(0x4393eb02)); // 451.26f, 295.836f +path.lineTo(SkBits2Float(0x43e19852), SkBits2Float(0x43941b02)); // 451.19f, 296.211f +path.lineTo(SkBits2Float(0x43e17d50), SkBits2Float(0x4393f20c)); // 450.979f, 295.891f +path.close(); +path.moveTo(SkBits2Float(0x43e1a169), SkBits2Float(0x4393eb02)); // 451.261f, 295.836f +path.lineTo(SkBits2Float(0x43e41f5d), SkBits2Float(0x43946efa)); // 456.245f, 296.867f +path.lineTo(SkBits2Float(0x43e40b65), SkBits2Float(0x4394cefa)); // 456.089f, 297.617f +path.lineTo(SkBits2Float(0x43e18d71), SkBits2Float(0x43944b02)); // 451.105f, 296.586f +path.lineTo(SkBits2Float(0x43e1a169), SkBits2Float(0x4393eb02)); // 451.261f, 295.836f +path.close(); +path.moveTo(SkBits2Float(0x43c35d50), SkBits2Float(0x43a6e3f8)); // 390.729f, 333.781f +path.lineTo(SkBits2Float(0x43e17d50), SkBits2Float(0x4393f1ec)); // 450.979f, 295.89f +path.lineTo(SkBits2Float(0x43e1b148), SkBits2Float(0x439443f8)); // 451.385f, 296.531f +path.lineTo(SkBits2Float(0x43c39042), SkBits2Float(0x43a737f0)); // 391.127f, 334.437f +path.lineTo(SkBits2Float(0x43c35d50), SkBits2Float(0x43a6e3f8)); // 390.729f, 333.781f +path.close(); +testSimplify(reporter, path, filename); +} + +static void joel_7(skiatest::Reporter* reporter, const char* filename) { + SkPath path; +path.moveTo(SkBits2Float(0x4321220c), SkBits2Float(0x43eac70a)); // 161.133f, 469.555f +path.lineTo(SkBits2Float(0x4321220c), SkBits2Float(0x43eac70a)); // 161.133f, 469.555f +path.lineTo(SkBits2Float(0x431f8e14), SkBits2Float(0x43eb3b02)); // 159.555f, 470.461f +path.lineTo(SkBits2Float(0x4321220c), SkBits2Float(0x43eac70a)); // 161.133f, 469.555f +path.close(); +path.moveTo(SkBits2Float(0x431e33f8), SkBits2Float(0x43f03b02)); // 158.203f, 480.461f +path.lineTo(SkBits2Float(0x431e33f8), SkBits2Float(0x43f03b02)); // 158.203f, 480.461f +path.lineTo(SkBits2Float(0x431e33f8), SkBits2Float(0x43f03b02)); // 158.203f, 480.461f +path.lineTo(SkBits2Float(0x431d4c08), SkBits2Float(0x43ef720c)); // 157.297f, 478.891f +path.lineTo(SkBits2Float(0x431e33f8), SkBits2Float(0x43f03b02)); // 158.203f, 480.461f +path.close(); +path.moveTo(SkBits2Float(0x431c6419), SkBits2Float(0x43eea7f0)); // 156.391f, 477.312f +path.cubicTo(SkBits2Float(0x431d6e15), SkBits2Float(0x43ee5ae2), SkBits2Float(0x431e2000), SkBits2Float(0x43ede000), SkBits2Float(0x431e69fc), SkBits2Float(0x43ed55e4)); // 157.43f, 476.71f, 158.125f, 475.75f, 158.414f, 474.671f +path.cubicTo(SkBits2Float(0x431eb3f8), SkBits2Float(0x43eccbc8), SkBits2Float(0x431e93f8), SkBits2Float(0x43ec35e4), SkBits2Float(0x431df9db), SkBits2Float(0x43ebafe0)); // 158.703f, 473.592f, 158.578f, 472.421f, 157.976f, 471.374f +path.lineTo(SkBits2Float(0x432121cb), SkBits2Float(0x43eac6ea)); // 161.132f, 469.554f +path.cubicTo(SkBits2Float(0x432355c3), SkBits2Float(0x43ecb0e6), SkBits2Float(0x432207ae), SkBits2Float(0x43ef1fe0), SkBits2Float(0x431e33b7), SkBits2Float(0x43f03ae2)); // 163.335f, 473.382f, 162.03f, 478.249f, 158.202f, 480.46f +path.lineTo(SkBits2Float(0x431c6419), SkBits2Float(0x43eea7f0)); // 156.391f, 477.312f +path.close(); +path.moveTo(SkBits2Float(0x43134c08), SkBits2Float(0x43eec4fe)); // 147.297f, 477.539f +path.lineTo(SkBits2Float(0x43134c08), SkBits2Float(0x43eec4fe)); // 147.297f, 477.539f +path.lineTo(SkBits2Float(0x43134c08), SkBits2Float(0x43eec4fe)); // 147.297f, 477.539f +path.lineTo(SkBits2Float(0x4314e20c), SkBits2Float(0x43ee5106)); // 148.883f, 476.633f +path.lineTo(SkBits2Float(0x43134c08), SkBits2Float(0x43eec4fe)); // 147.297f, 477.539f +path.close(); +path.moveTo(SkBits2Float(0x431673f8), SkBits2Float(0x43eddc08)); // 150.453f, 475.719f +path.cubicTo(SkBits2Float(0x43170e15), SkBits2Float(0x43ee620c), SkBits2Float(0x43180000), SkBits2Float(0x43eebb02), SkBits2Float(0x43191604), SkBits2Float(0x43eee000)); // 151.055f, 476.766f, 152, 477.461f, 153.086f, 477.75f +path.cubicTo(SkBits2Float(0x431a2c08), SkBits2Float(0x43ef04fe), SkBits2Float(0x431b5810), SkBits2Float(0x43eef4fe), SkBits2Float(0x431c6418), SkBits2Float(0x43eea7f0)); // 154.172f, 478.039f, 155.344f, 477.914f, 156.391f, 477.312f +path.lineTo(SkBits2Float(0x431e33f7), SkBits2Float(0x43f03ae2)); // 158.203f, 480.46f +path.cubicTo(SkBits2Float(0x431a620b), SkBits2Float(0x43f154de), SkBits2Float(0x4315820c), SkBits2Float(0x43f0add4), SkBits2Float(0x43134c07), SkBits2Float(0x43eec4de)); // 154.383f, 482.663f, 149.508f, 481.358f, 147.297f, 477.538f +path.lineTo(SkBits2Float(0x431673f8), SkBits2Float(0x43eddc08)); // 150.453f, 475.719f +path.close(); +path.moveTo(SkBits2Float(0x43163a1d), SkBits2Float(0x43e95106)); // 150.227f, 466.633f +path.lineTo(SkBits2Float(0x43163a1d), SkBits2Float(0x43e95106)); // 150.227f, 466.633f +path.lineTo(SkBits2Float(0x4317220d), SkBits2Float(0x43ea19fc)); // 151.133f, 468.203f +path.lineTo(SkBits2Float(0x43163a1d), SkBits2Float(0x43e95106)); // 150.227f, 466.633f +path.close(); +path.moveTo(SkBits2Float(0x43180c08), SkBits2Float(0x43eae3f8)); // 152.047f, 469.781f +path.cubicTo(SkBits2Float(0x43170000), SkBits2Float(0x43eb31ec), SkBits2Float(0x43164e14), SkBits2Float(0x43ebabe8), SkBits2Float(0x43160418), SkBits2Float(0x43ec3604)); // 151, 470.39f, 150.305f, 471.343f, 150.016f, 472.422f +path.cubicTo(SkBits2Float(0x4315ba1c), SkBits2Float(0x43ecc106), SkBits2Float(0x4315d810), SkBits2Float(0x43ed570a), SkBits2Float(0x43167439), SkBits2Float(0x43eddc08)); // 149.727f, 473.508f, 149.844f, 474.68f, 150.454f, 475.719f +path.lineTo(SkBits2Float(0x43134c49), SkBits2Float(0x43eec4fe)); // 147.298f, 477.539f +path.cubicTo(SkBits2Float(0x43111851), SkBits2Float(0x43ecdb02), SkBits2Float(0x43126830), SkBits2Float(0x43ea6c08), SkBits2Float(0x43163a5d), SkBits2Float(0x43e95106)); // 145.095f, 473.711f, 146.407f, 468.844f, 150.228f, 466.633f +path.lineTo(SkBits2Float(0x43180c08), SkBits2Float(0x43eae3f8)); // 152.047f, 469.781f +path.close(); +path.moveTo(SkBits2Float(0x431dfa1d), SkBits2Float(0x43ebb000)); // 157.977f, 471.375f +path.cubicTo(SkBits2Float(0x431d620d), SkBits2Float(0x43eb29fc), SkBits2Float(0x431c6e15), SkBits2Float(0x43ead20c), SkBits2Float(0x431b5811), SkBits2Float(0x43eaad0e)); // 157.383f, 470.328f, 156.43f, 469.641f, 155.344f, 469.352f +path.cubicTo(SkBits2Float(0x431a420d), SkBits2Float(0x43ea8810), SkBits2Float(0x43191605), SkBits2Float(0x43ea970a), SkBits2Float(0x43180c09), SkBits2Float(0x43eae418)); // 154.258f, 469.063f, 153.086f, 469.18f, 152.047f, 469.782f +path.lineTo(SkBits2Float(0x43163a1d), SkBits2Float(0x43e95126)); // 150.227f, 466.634f +path.cubicTo(SkBits2Float(0x431a0c09), SkBits2Float(0x43e8372a), SkBits2Float(0x431eec08), SkBits2Float(0x43e8de34), SkBits2Float(0x4321220d), SkBits2Float(0x43eac72a)); // 154.047f, 464.431f, 158.922f, 465.736f, 161.133f, 469.556f +path.lineTo(SkBits2Float(0x431dfa1d), SkBits2Float(0x43ebb000)); // 157.977f, 471.375f +path.close(); +testSimplify(reporter, path, filename); +} + +static void joel_8(skiatest::Reporter* reporter, const char* filename) { + SkPath path; +path.moveTo(SkBits2Float(0x42d97520), SkBits2Float(0x410ac429)); // 108.729f, 8.67289f +path.cubicTo(SkBits2Float(0x42d97520), SkBits2Float(0x410ac429), SkBits2Float(0x42e9a9ce), SkBits2Float(0x41834e87), SkBits2Float(0x42e99c8c), SkBits2Float(0x41c5c960)); // 108.729f, 8.67289f, 116.832f, 16.4133f, 116.806f, 24.7233f +path.cubicTo(SkBits2Float(0x42e98f49), SkBits2Float(0x4204221c), SkBits2Float(0x42d97520), SkBits2Float(0x4223825f), SkBits2Float(0x42d97520), SkBits2Float(0x4223825f)); // 116.78f, 33.0333f, 108.729f, 40.8773f, 108.729f, 40.8773f +path.cubicTo(SkBits2Float(0x42d97520), SkBits2Float(0x4223825f), SkBits2Float(0x42dbbc54), SkBits2Float(0x42099f18), SkBits2Float(0x42d1cb74), SkBits2Float(0x41f77dc0)); // 108.729f, 40.8773f, 109.868f, 34.4054f, 104.897f, 30.9364f +path.cubicTo(SkBits2Float(0x42c7da94), SkBits2Float(0x41dbbd4f), SkBits2Float(0x42b1b1a1), SkBits2Float(0x41d802fb), SkBits2Float(0x42b1b1a1), SkBits2Float(0x41d802fb)); // 99.9269f, 27.4674f, 88.8469f, 27.0015f, 88.8469f, 27.0015f +path.cubicTo(SkBits2Float(0x42a75637), SkBits2Float(0x41d6909f), SkBits2Float(0x4296c543), SkBits2Float(0x41f1b139), SkBits2Float(0x4296c543), SkBits2Float(0x41f1b139)); // 83.6684f, 26.8206f, 75.3853f, 30.2115f, 75.3853f, 30.2115f +path.lineTo(SkBits2Float(0x42824475), SkBits2Float(0x41c69d70)); // 65.1337f, 24.8269f +path.lineTo(SkBits2Float(0x4296c543), SkBits2Float(0x419b89a8)); // 75.3853f, 19.4422f +path.cubicTo(SkBits2Float(0x4296c543), SkBits2Float(0x419b89a8), SkBits2Float(0x42a6b798), SkBits2Float(0x41b89815), SkBits2Float(0x42b1b1a1), SkBits2Float(0x41b95c48)); // 75.3853f, 19.4422f, 83.3586f, 23.0743f, 88.8469f, 23.1701f +path.cubicTo(SkBits2Float(0x42b1b1a1), SkBits2Float(0x41b95c48), SkBits2Float(0x42c80258), SkBits2Float(0x41b03f7a), SkBits2Float(0x42d1cb74), SkBits2Float(0x419340ee)); // 88.8469f, 23.1701f, 100.005f, 22.031f, 104.897f, 18.4067f +path.cubicTo(SkBits2Float(0x42db9490), SkBits2Float(0x416c84c2), SkBits2Float(0x42d97520), SkBits2Float(0x410ac42a), SkBits2Float(0x42d97520), SkBits2Float(0x410ac42a)); // 109.79f, 14.7824f, 108.729f, 8.67289f, 108.729f, 8.67289f +path.lineTo(SkBits2Float(0x42d97520), SkBits2Float(0x410ac429)); // 108.729f, 8.67289f +path.close(); +testSimplify(reporter, path, filename); +} + +static void joel_9(skiatest::Reporter* reporter, const char* filename) { +#if DEBUG_UNDER_DEVELOPMENT +// fails with image mismatch + SkPath path; +path.moveTo(SkBits2Float(0x4310dbe7), SkBits2Float(0x438e9604)); // 144.859f, 285.172f +path.lineTo(SkBits2Float(0x4310dbe7), SkBits2Float(0x438e9604)); // 144.859f, 285.172f +path.lineTo(SkBits2Float(0x4310dbe7), SkBits2Float(0x438e9604)); // 144.859f, 285.172f +path.lineTo(SkBits2Float(0x430f21ca), SkBits2Float(0x438e4efa)); // 143.132f, 284.617f +path.lineTo(SkBits2Float(0x4310dbe7), SkBits2Float(0x438e9604)); // 144.859f, 285.172f +path.close(); +path.moveTo(SkBits2Float(0x4307ec08), SkBits2Float(0x438f6c08)); // 135.922f, 286.844f +path.lineTo(SkBits2Float(0x4307ec08), SkBits2Float(0x438f6c08)); // 135.922f, 286.844f +path.lineTo(SkBits2Float(0x4307ec08), SkBits2Float(0x438f6c08)); // 135.922f, 286.844f +path.lineTo(SkBits2Float(0x43075df4), SkBits2Float(0x43904916)); // 135.367f, 288.571f +path.lineTo(SkBits2Float(0x4307ec08), SkBits2Float(0x438f6c08)); // 135.922f, 286.844f +path.close(); +path.moveTo(SkBits2Float(0x4307ec08), SkBits2Float(0x438f6c08)); // 135.922f, 286.844f +path.cubicTo(SkBits2Float(0x430911eb), SkBits2Float(0x438f9c08), SkBits2Float(0x430a3df4), SkBits2Float(0x438f8b02), SkBits2Float(0x430b3df4), SkBits2Float(0x438f49fc)); // 137.07f, 287.219f, 138.242f, 287.086f, 139.242f, 286.578f +path.cubicTo(SkBits2Float(0x430c3be8), SkBits2Float(0x438f09fc), SkBits2Float(0x430d07f0), SkBits2Float(0x438e99fc), SkBits2Float(0x430d67f0), SkBits2Float(0x438e070a)); // 140.234f, 286.078f, 141.031f, 285.203f, 141.406f, 284.055f +path.lineTo(SkBits2Float(0x4310dbe8), SkBits2Float(0x438e9604)); // 144.859f, 285.172f +path.cubicTo(SkBits2Float(0x430f7df4), SkBits2Float(0x4390b000), SkBits2Float(0x430afdf4), SkBits2Float(0x4391d3f8), SkBits2Float(0x4306cc09), SkBits2Float(0x43912604)); // 143.492f, 289.375f, 138.992f, 291.656f, 134.797f, 290.297f +path.lineTo(SkBits2Float(0x4307ec08), SkBits2Float(0x438f6c08)); // 135.922f, 286.844f +path.close(); +path.moveTo(SkBits2Float(0x4301ae14), SkBits2Float(0x438c1efa)); // 129.68f, 280.242f +path.lineTo(SkBits2Float(0x4301ae14), SkBits2Float(0x438c1efa)); // 129.68f, 280.242f +path.lineTo(SkBits2Float(0x4301ae14), SkBits2Float(0x438c1efa)); // 129.68f, 280.242f +path.lineTo(SkBits2Float(0x43036831), SkBits2Float(0x438c66ea)); // 131.407f, 280.804f +path.lineTo(SkBits2Float(0x4301ae14), SkBits2Float(0x438c1efa)); // 129.68f, 280.242f +path.close(); +path.moveTo(SkBits2Float(0x4305220c), SkBits2Float(0x438caefa)); // 133.133f, 281.367f +path.cubicTo(SkBits2Float(0x4304c20c), SkBits2Float(0x438d4106), SkBits2Float(0x4304e20c), SkBits2Float(0x438dd7f0), SkBits2Float(0x43056418), SkBits2Float(0x438e56ea)); // 132.758f, 282.508f, 132.883f, 283.687f, 133.391f, 284.679f +path.cubicTo(SkBits2Float(0x4305e831), SkBits2Float(0x438ed6ea), SkBits2Float(0x4306c624), SkBits2Float(0x438f3be8), SkBits2Float(0x4307ec08), SkBits2Float(0x438f6be8)); // 133.907f, 285.679f, 134.774f, 286.468f, 135.922f, 286.843f +path.lineTo(SkBits2Float(0x4306cc08), SkBits2Float(0x439125e4)); // 134.797f, 290.296f +path.cubicTo(SkBits2Float(0x43029a1c), SkBits2Float(0x439076ea), SkBits2Float(0x43005021), SkBits2Float(0x438e37f0), SkBits2Float(0x4301ae14), SkBits2Float(0x438c1eda)); // 130.602f, 288.929f, 128.313f, 284.437f, 129.68f, 280.241f +path.lineTo(SkBits2Float(0x4305220c), SkBits2Float(0x438caefa)); // 133.133f, 281.367f +path.close(); +path.moveTo(SkBits2Float(0x430bbdf4), SkBits2Float(0x43898efa)); // 139.742f, 275.117f +path.lineTo(SkBits2Float(0x430bbdf4), SkBits2Float(0x43898efa)); // 139.742f, 275.117f +path.lineTo(SkBits2Float(0x430b2e15), SkBits2Float(0x438a6c08)); // 139.18f, 276.844f +path.lineTo(SkBits2Float(0x430bbdf4), SkBits2Float(0x43898efa)); // 139.742f, 275.117f +path.close(); +path.moveTo(SkBits2Float(0x430a9be7), SkBits2Float(0x438b48f6)); // 138.609f, 278.57f +path.cubicTo(SkBits2Float(0x43097604), SkBits2Float(0x438b19fc), SkBits2Float(0x43084c08), SkBits2Float(0x438b29fc), SkBits2Float(0x43074c08), SkBits2Float(0x438b6b02)); // 137.461f, 278.203f, 136.297f, 278.328f, 135.297f, 278.836f +path.cubicTo(SkBits2Float(0x43064c08), SkBits2Float(0x438bac08), SkBits2Float(0x4305820c), SkBits2Float(0x438c1c08), SkBits2Float(0x4305220c), SkBits2Float(0x438caefa)); // 134.297f, 279.344f, 133.508f, 280.219f, 133.133f, 281.367f +path.lineTo(SkBits2Float(0x4301ae14), SkBits2Float(0x438c1efa)); // 129.68f, 280.242f +path.cubicTo(SkBits2Float(0x43030c08), SkBits2Float(0x438a04fe), SkBits2Float(0x430789fb), SkBits2Float(0x4388e106), SkBits2Float(0x430bbdf3), SkBits2Float(0x43898efa)); // 131.047f, 276.039f, 135.539f, 273.758f, 139.742f, 275.117f +path.lineTo(SkBits2Float(0x430a9be7), SkBits2Float(0x438b48f6)); // 138.609f, 278.57f +path.close(); +path.moveTo(SkBits2Float(0x430d67f0), SkBits2Float(0x438e070a)); // 141.406f, 284.055f +path.cubicTo(SkBits2Float(0x430dc5e4), SkBits2Float(0x438d7418), SkBits2Float(0x430da5e4), SkBits2Float(0x438cde14), SkBits2Float(0x430d25e4), SkBits2Float(0x438c5e14)); // 141.773f, 282.907f, 141.648f, 281.735f, 141.148f, 280.735f +path.cubicTo(SkBits2Float(0x430ca001), SkBits2Float(0x438bde14), SkBits2Float(0x430bc1cb), SkBits2Float(0x438b7916), SkBits2Float(0x430a9be8), SkBits2Float(0x438b4916)); // 140.625f, 279.735f, 139.757f, 278.946f, 138.609f, 278.571f +path.lineTo(SkBits2Float(0x430bbdf4), SkBits2Float(0x43898f1a)); // 139.742f, 275.118f +path.cubicTo(SkBits2Float(0x430fefe0), SkBits2Float(0x438a3f1a), SkBits2Float(0x43123811), SkBits2Float(0x438c7d0e), SkBits2Float(0x4310dbe8), SkBits2Float(0x438e9624)); // 143.937f, 276.493f, 146.219f, 280.977f, 144.859f, 285.173f +path.lineTo(SkBits2Float(0x430d67f0), SkBits2Float(0x438e070a)); // 141.406f, 284.055f +path.close(); +testSimplify(reporter, path, filename); +#endif +} + +static void joel_10(skiatest::Reporter* reporter, const char* filename) { +#if DEBUG_UNDER_DEVELOPMENT +// fails with image mismatch + SkPath path; +path.moveTo(SkBits2Float(0x440fc979), SkBits2Float(0x43d88000)); // 575.148f, 433 +path.lineTo(SkBits2Float(0x440fc979), SkBits2Float(0x43d88000)); // 575.148f, 433 +path.lineTo(SkBits2Float(0x440fc979), SkBits2Float(0x43d88000)); // 575.148f, 433 +path.lineTo(SkBits2Float(0x44103800), SkBits2Float(0x43d8c7f0)); // 576.875f, 433.562f +path.lineTo(SkBits2Float(0x440fc979), SkBits2Float(0x43d88000)); // 575.148f, 433 +path.close(); +path.moveTo(SkBits2Float(0x44124d81), SkBits2Float(0x43d5f000)); // 585.211f, 427.875f +path.lineTo(SkBits2Float(0x44124d81), SkBits2Float(0x43d5f000)); // 585.211f, 427.875f +path.lineTo(SkBits2Float(0x44122906), SkBits2Float(0x43d6cdf4)); // 584.641f, 429.609f +path.lineTo(SkBits2Float(0x44124d81), SkBits2Float(0x43d5f000)); // 585.211f, 427.875f +path.close(); +path.moveTo(SkBits2Float(0x44120581), SkBits2Float(0x43d7ab02)); // 584.086f, 431.336f +path.cubicTo(SkBits2Float(0x4411bc08), SkBits2Float(0x43d77b02), SkBits2Float(0x44117083), SkBits2Float(0x43d78b02), SkBits2Float(0x44113106), SkBits2Float(0x43d7cc08)); // 582.938f, 430.961f, 581.758f, 431.086f, 580.766f, 431.594f +path.cubicTo(SkBits2Float(0x4410f189), SkBits2Float(0x43d80d0e), SkBits2Float(0x4410be87), SkBits2Float(0x43d87d0e), SkBits2Float(0x4410a687), SkBits2Float(0x43d91000)); // 579.774f, 432.102f, 578.977f, 432.977f, 578.602f, 434.125f +path.lineTo(SkBits2Float(0x440fc979), SkBits2Float(0x43d88000)); // 575.148f, 433 +path.cubicTo(SkBits2Float(0x441020f6), SkBits2Float(0x43d66604), SkBits2Float(0x441140f6), SkBits2Float(0x43d5420c), SkBits2Float(0x44124d71), SkBits2Float(0x43d5f000)); // 576.515f, 428.797f, 581.015f, 426.516f, 585.21f, 427.875f +path.lineTo(SkBits2Float(0x44120581), SkBits2Float(0x43d7ab02)); // 584.086f, 431.336f +path.close(); +path.moveTo(SkBits2Float(0x441394fe), SkBits2Float(0x43daf810)); // 590.328f, 437.938f +path.lineTo(SkBits2Float(0x441394fe), SkBits2Float(0x43daf810)); // 590.328f, 437.938f +path.lineTo(SkBits2Float(0x441394fe), SkBits2Float(0x43daf810)); // 590.328f, 437.938f +path.lineTo(SkBits2Float(0x44132677), SkBits2Float(0x43dab020)); // 588.601f, 437.376f +path.lineTo(SkBits2Float(0x441394fe), SkBits2Float(0x43daf810)); // 590.328f, 437.938f +path.close(); +path.moveTo(SkBits2Float(0x4412b800), SkBits2Float(0x43da67f0)); // 586.875f, 436.812f +path.cubicTo(SkBits2Float(0x4412d000), SkBits2Float(0x43d9d4fe), SkBits2Float(0x4412c800), SkBits2Float(0x43d94000), SkBits2Float(0x4412a77d), SkBits2Float(0x43d8befa)); // 587.25f, 435.664f, 587.125f, 434.5f, 586.617f, 433.492f +path.cubicTo(SkBits2Float(0x44128677), SkBits2Float(0x43d84000), SkBits2Float(0x44124efa), SkBits2Float(0x43d7d9fc), SkBits2Float(0x44120581), SkBits2Float(0x43d7ab02)); // 586.101f, 432.5f, 585.234f, 431.703f, 584.086f, 431.336f +path.lineTo(SkBits2Float(0x44124d81), SkBits2Float(0x43d5f000)); // 585.211f, 427.875f +path.cubicTo(SkBits2Float(0x441359fc), SkBits2Float(0x43d69efa), SkBits2Float(0x4413ec7b), SkBits2Float(0x43d8ddf4), SkBits2Float(0x441394fe), SkBits2Float(0x43daf7f0)); // 589.406f, 429.242f, 591.695f, 433.734f, 590.328f, 437.937f +path.lineTo(SkBits2Float(0x4412b800), SkBits2Float(0x43da67f0)); // 586.875f, 436.812f +path.close(); +path.moveTo(SkBits2Float(0x44111106), SkBits2Float(0x43dd870a)); // 580.266f, 443.055f +path.lineTo(SkBits2Float(0x44111106), SkBits2Float(0x43dd870a)); // 580.266f, 443.055f +path.lineTo(SkBits2Float(0x44111106), SkBits2Float(0x43dd870a)); // 580.266f, 443.055f +path.lineTo(SkBits2Float(0x441134fe), SkBits2Float(0x43dca9fc)); // 580.828f, 441.328f +path.lineTo(SkBits2Float(0x44111106), SkBits2Float(0x43dd870a)); // 580.266f, 443.055f +path.close(); +path.moveTo(SkBits2Float(0x44115979), SkBits2Float(0x43dbcd0e)); // 581.398f, 439.602f +path.cubicTo(SkBits2Float(0x4411a27f), SkBits2Float(0x43dbfc08), SkBits2Float(0x4411ed71), SkBits2Float(0x43dbed0e), SkBits2Float(0x44122d71), SkBits2Float(0x43dbac08)); // 582.539f, 439.969f, 583.71f, 439.852f, 584.71f, 439.344f +path.cubicTo(SkBits2Float(0x44126cee), SkBits2Float(0x43db6b02), SkBits2Float(0x44129ff0), SkBits2Float(0x43dafb02), SkBits2Float(0x4412b7f0), SkBits2Float(0x43da6810)); // 585.702f, 438.836f, 586.499f, 437.961f, 586.874f, 436.813f +path.lineTo(SkBits2Float(0x441394ee), SkBits2Float(0x43daf810)); // 590.327f, 437.938f +path.cubicTo(SkBits2Float(0x44133cee), SkBits2Float(0x43dd1106), SkBits2Float(0x44121df4), SkBits2Float(0x43de3604), SkBits2Float(0x441110f6), SkBits2Float(0x43dd870a)); // 588.952f, 442.133f, 584.468f, 444.422f, 580.265f, 443.055f +path.lineTo(SkBits2Float(0x44115979), SkBits2Float(0x43dbcd0e)); // 581.398f, 439.602f +path.close(); +path.moveTo(SkBits2Float(0x4410a687), SkBits2Float(0x43d91000)); // 578.602f, 434.125f +path.cubicTo(SkBits2Float(0x44108f0a), SkBits2Float(0x43d9a2f2), SkBits2Float(0x44109687), SkBits2Float(0x43da37f0), SkBits2Float(0x4410b70a), SkBits2Float(0x43dab8f6)); // 578.235f, 435.273f, 578.352f, 436.437f, 578.86f, 437.445f +path.cubicTo(SkBits2Float(0x4410d78d), SkBits2Float(0x43db37f0), SkBits2Float(0x44111010), SkBits2Float(0x43db9cee), SkBits2Float(0x44115989), SkBits2Float(0x43dbccee)); // 579.368f, 438.437f, 580.251f, 439.226f, 581.399f, 439.601f +path.lineTo(SkBits2Float(0x44111106), SkBits2Float(0x43dd86ea)); // 580.266f, 443.054f +path.cubicTo(SkBits2Float(0x4410048b), SkBits2Float(0x43dcd7f0), SkBits2Float(0x440f720c), SkBits2Float(0x43da99dc), SkBits2Float(0x440fc989), SkBits2Float(0x43d87fe0)); // 576.071f, 441.687f, 573.782f, 437.202f, 575.149f, 432.999f +path.lineTo(SkBits2Float(0x4410a687), SkBits2Float(0x43d91000)); // 578.602f, 434.125f +path.close(); +testSimplify(reporter, path, filename); +#endif +} + +static void joel_11(skiatest::Reporter* reporter, const char* filename) { +#if DEBUG_UNDER_DEVELOPMENT +// fails with image mismatch + SkPath path; +path.moveTo(SkBits2Float(0x43c9d000), SkBits2Float(0x4411977d)); // 403.625f, 582.367f +path.lineTo(SkBits2Float(0x43c9d000), SkBits2Float(0x4411977d)); // 403.625f, 582.367f +path.lineTo(SkBits2Float(0x43c9d000), SkBits2Float(0x4411977d)); // 403.625f, 582.367f +path.lineTo(SkBits2Float(0x43ca0106), SkBits2Float(0x441208f6)); // 404.008f, 584.14f +path.lineTo(SkBits2Float(0x43c9d000), SkBits2Float(0x4411977d)); // 403.625f, 582.367f +path.close(); +path.moveTo(SkBits2Float(0x43ce8d0e), SkBits2Float(0x44132106)); // 413.102f, 588.516f +path.lineTo(SkBits2Float(0x43ce8d0e), SkBits2Float(0x44132106)); // 413.102f, 588.516f +path.lineTo(SkBits2Float(0x43ce8d0e), SkBits2Float(0x44132106)); // 413.102f, 588.516f +path.lineTo(SkBits2Float(0x43cda916), SkBits2Float(0x44133989)); // 411.321f, 588.899f +path.lineTo(SkBits2Float(0x43ce8d0e), SkBits2Float(0x44132106)); // 413.102f, 588.516f +path.close(); +path.moveTo(SkBits2Float(0x43ccc4fe), SkBits2Float(0x44135179)); // 409.539f, 589.273f +path.cubicTo(SkBits2Float(0x43cca604), SkBits2Float(0x44130571), SkBits2Float(0x43cc4c08), SkBits2Float(0x4412c8f6), SkBits2Float(0x43cbd4fe), SkBits2Float(0x4412a179)); // 409.297f, 588.085f, 408.594f, 587.14f, 407.664f, 586.523f +path.cubicTo(SkBits2Float(0x43cb5c08), SkBits2Float(0x44127a7f), SkBits2Float(0x43cac7f0), SkBits2Float(0x44126af2), SkBits2Float(0x43ca3106), SkBits2Float(0x44127af2)); // 406.719f, 585.914f, 405.562f, 585.671f, 404.383f, 585.921f +path.lineTo(SkBits2Float(0x43c9d000), SkBits2Float(0x4411976d)); // 403.625f, 582.366f +path.cubicTo(SkBits2Float(0x43cbf9fc), SkBits2Float(0x44115cee), SkBits2Float(0x43ce170a), SkBits2Float(0x44120cee), SkBits2Float(0x43ce8d0e), SkBits2Float(0x441320e6)); // 407.953f, 581.452f, 412.18f, 584.202f, 413.102f, 588.514f +path.lineTo(SkBits2Float(0x43ccc4fe), SkBits2Float(0x44135179)); // 409.539f, 589.273f +path.close(); +path.moveTo(SkBits2Float(0x43cb78f6), SkBits2Float(0x44157efa)); // 406.945f, 597.984f +path.lineTo(SkBits2Float(0x43cb78f6), SkBits2Float(0x44157f7d)); // 406.945f, 597.992f +path.lineTo(SkBits2Float(0x43cb78f6), SkBits2Float(0x44157efa)); // 406.945f, 597.984f +path.lineTo(SkBits2Float(0x43cb49fc), SkBits2Float(0x44150d81)); // 406.578f, 596.211f +path.lineTo(SkBits2Float(0x43cb78f6), SkBits2Float(0x44157efa)); // 406.945f, 597.984f +path.close(); +path.moveTo(SkBits2Float(0x43cb18f6), SkBits2Float(0x44149b85)); // 406.195f, 594.43f +path.cubicTo(SkBits2Float(0x43cbb000), SkBits2Float(0x44148b85), SkBits2Float(0x43cc28f6), SkBits2Float(0x44145f0a), SkBits2Float(0x43cc76ea), SkBits2Float(0x44142302)); // 407.375f, 594.18f, 408.32f, 593.485f, 408.929f, 592.547f +path.cubicTo(SkBits2Float(0x43ccc4de), SkBits2Float(0x4413e687), SkBits2Float(0x43cce4de), SkBits2Float(0x44139cfe), SkBits2Float(0x43ccc4de), SkBits2Float(0x44135189)); // 409.538f, 591.602f, 409.788f, 590.453f, 409.538f, 589.274f +path.lineTo(SkBits2Float(0x43ce8cce), SkBits2Float(0x44132106)); // 413.1f, 588.516f +path.cubicTo(SkBits2Float(0x43cf00c6), SkBits2Float(0x44143581), SkBits2Float(0x43cda2d2), SkBits2Float(0x44154408), SkBits2Float(0x43cb78d6), SkBits2Float(0x44157f0a)); // 414.006f, 592.836f, 411.272f, 597.063f, 406.944f, 597.985f +path.lineTo(SkBits2Float(0x43cb18f6), SkBits2Float(0x44149b85)); // 406.195f, 594.43f +path.close(); +path.moveTo(SkBits2Float(0x43c883f8), SkBits2Float(0x4413c4fe)); // 401.031f, 591.078f +path.lineTo(SkBits2Float(0x43c883f8), SkBits2Float(0x4413c4fe)); // 401.031f, 591.078f +path.lineTo(SkBits2Float(0x43c883f8), SkBits2Float(0x4413c4fe)); // 401.031f, 591.078f +path.lineTo(SkBits2Float(0x43c7a106), SkBits2Float(0x4413dd81)); // 399.258f, 591.461f +path.lineTo(SkBits2Float(0x43c883f8), SkBits2Float(0x4413c4fe)); // 401.031f, 591.078f +path.close(); +path.moveTo(SkBits2Float(0x43c883f8), SkBits2Float(0x4413c4fe)); // 401.031f, 591.078f +path.cubicTo(SkBits2Float(0x43c8a4fe), SkBits2Float(0x44141083), SkBits2Float(0x43c8fdf4), SkBits2Float(0x44144d81), SkBits2Float(0x43c974fe), SkBits2Float(0x4414747b)); // 401.289f, 592.258f, 401.984f, 593.211f, 402.914f, 593.82f +path.cubicTo(SkBits2Float(0x43c9edf4), SkBits2Float(0x44149b75), SkBits2Float(0x43ca820c), SkBits2Float(0x4414ab75), SkBits2Float(0x43cb18f6), SkBits2Float(0x44149b75)); // 403.859f, 594.429f, 405.016f, 594.679f, 406.195f, 594.429f +path.lineTo(SkBits2Float(0x43cb78f6), SkBits2Float(0x44157efa)); // 406.945f, 597.984f +path.cubicTo(SkBits2Float(0x43c95000), SkBits2Float(0x4415b979), SkBits2Float(0x43c732f2), SkBits2Float(0x441509fc), SkBits2Float(0x43c6bcee), SkBits2Float(0x4413f581)); // 402.625f, 598.898f, 398.398f, 596.156f, 397.476f, 591.836f +path.lineTo(SkBits2Float(0x43c883f8), SkBits2Float(0x4413c4fe)); // 401.031f, 591.078f +path.close(); +path.moveTo(SkBits2Float(0x43ca3106), SkBits2Float(0x44127b02)); // 404.383f, 585.922f +path.cubicTo(SkBits2Float(0x43c999fc), SkBits2Float(0x44128b02), SkBits2Float(0x43c92000), SkBits2Float(0x4412b77d), SkBits2Float(0x43c8d20c), SkBits2Float(0x4412f408)); // 403.203f, 586.172f, 402.25f, 586.867f, 401.641f, 587.813f +path.cubicTo(SkBits2Float(0x43c88418), SkBits2Float(0x44133010), SkBits2Float(0x43c86418), SkBits2Float(0x44137989), SkBits2Float(0x43c88418), SkBits2Float(0x4413c50e)); // 401.032f, 588.751f, 400.782f, 589.899f, 401.032f, 591.079f +path.lineTo(SkBits2Float(0x43c6bd0e), SkBits2Float(0x4413f591)); // 397.477f, 591.837f +path.cubicTo(SkBits2Float(0x43c64810), SkBits2Float(0x4412e116), SkBits2Float(0x43c7a70a), SkBits2Float(0x4411d28f), SkBits2Float(0x43c9d000), SkBits2Float(0x4411978d)); // 396.563f, 587.517f, 399.305f, 583.29f, 403.625f, 582.368f +path.lineTo(SkBits2Float(0x43ca3106), SkBits2Float(0x44127b02)); // 404.383f, 585.922f +path.close(); +testSimplify(reporter, path, filename); +#endif +} + +static void make_joel_12(SkPath& path) { +path.moveTo(SkBits2Float(0x4324e9fc), SkBits2Float(0x437211ec)); // 164.914f, 242.07f +path.lineTo(SkBits2Float(0x4324e9fc), SkBits2Float(0x437211ec)); // 164.914f, 242.07f +path.lineTo(SkBits2Float(0x4324e9fc), SkBits2Float(0x437211ec)); // 164.914f, 242.07f +path.lineTo(SkBits2Float(0x43235810), SkBits2Float(0x437129fc)); // 163.344f, 241.164f +path.lineTo(SkBits2Float(0x4324e9fc), SkBits2Float(0x437211ec)); // 164.914f, 242.07f +path.close(); +path.moveTo(SkBits2Float(0x431a020c), SkBits2Float(0x4374fdf4)); // 154.008f, 244.992f +path.lineTo(SkBits2Float(0x431a020c), SkBits2Float(0x4374fdf4)); // 154.008f, 244.992f +path.lineTo(SkBits2Float(0x431a020c), SkBits2Float(0x4374fdf4)); // 154.008f, 244.992f +path.lineTo(SkBits2Float(0x431aec08), SkBits2Float(0x437369fc)); // 154.922f, 243.414f +path.lineTo(SkBits2Float(0x431a020c), SkBits2Float(0x4374fdf4)); // 154.008f, 244.992f +path.close(); +path.moveTo(SkBits2Float(0x431bd3f8), SkBits2Float(0x4371d810)); // 155.828f, 241.844f +path.cubicTo(SkBits2Float(0x431ce000), SkBits2Float(0x4372722d), SkBits2Float(0x431e0e15), SkBits2Float(0x43729020), SkBits2Float(0x431f2000), SkBits2Float(0x43724831)); // 156.875f, 242.446f, 158.055f, 242.563f, 159.125f, 242.282f +path.cubicTo(SkBits2Float(0x43203604), SkBits2Float(0x4371fe35), SkBits2Float(0x43212c08), SkBits2Float(0x43714a3d), SkBits2Float(0x4321c5e3), SkBits2Float(0x43704041)); // 160.211f, 241.993f, 161.172f, 241.29f, 161.773f, 240.251f +path.lineTo(SkBits2Float(0x4324e9fc), SkBits2Float(0x4372122d)); // 164.914f, 242.071f +path.cubicTo(SkBits2Float(0x4322b3f8), SkBits2Float(0x4375e419), SkBits2Float(0x431dd810), SkBits2Float(0x4377322d), SkBits2Float(0x431a020c), SkBits2Float(0x4374fe35)); // 162.703f, 245.891f, 157.844f, 247.196f, 154.008f, 244.993f +path.lineTo(SkBits2Float(0x431bd3f8), SkBits2Float(0x4371d810)); // 155.828f, 241.844f +path.close(); +path.moveTo(SkBits2Float(0x43171810), SkBits2Float(0x436a1604)); // 151.094f, 234.086f +path.lineTo(SkBits2Float(0x43171810), SkBits2Float(0x436a1604)); // 151.094f, 234.086f +path.lineTo(SkBits2Float(0x43171810), SkBits2Float(0x436a1604)); // 151.094f, 234.086f +path.lineTo(SkBits2Float(0x4318a9fc), SkBits2Float(0x436afdf4)); // 152.664f, 234.992f +path.lineTo(SkBits2Float(0x43171810), SkBits2Float(0x436a1604)); // 151.094f, 234.086f +path.close(); +path.moveTo(SkBits2Float(0x431a4000), SkBits2Float(0x436be7f0)); // 154.25f, 235.906f +path.cubicTo(SkBits2Float(0x4319a20c), SkBits2Float(0x436cf3f8), SkBits2Float(0x431985e3), SkBits2Float(0x436e1df4), SkBits2Float(0x4319ce14), SkBits2Float(0x436f33f8)); // 153.633f, 236.953f, 153.523f, 238.117f, 153.805f, 239.203f +path.cubicTo(SkBits2Float(0x431a1a1c), SkBits2Float(0x437047f0), SkBits2Float(0x431ac831), SkBits2Float(0x43713df4), SkBits2Float(0x431bd3f7), SkBits2Float(0x4371d811)); // 154.102f, 240.281f, 154.782f, 241.242f, 155.828f, 241.844f +path.lineTo(SkBits2Float(0x431a020b), SkBits2Float(0x4374fdf4)); // 154.008f, 244.992f +path.cubicTo(SkBits2Float(0x4316322c), SkBits2Float(0x4372c5e4), SkBits2Float(0x4314e417), SkBits2Float(0x436de9fc), SkBits2Float(0x4317180f), SkBits2Float(0x436a1604)); // 150.196f, 242.773f, 148.891f, 237.914f, 151.094f, 234.086f +path.lineTo(SkBits2Float(0x431a4000), SkBits2Float(0x436be7f0)); // 154.25f, 235.906f +path.close(); +path.moveTo(SkBits2Float(0x43220000), SkBits2Float(0x436729fc)); // 162, 231.164f +path.lineTo(SkBits2Float(0x43220000), SkBits2Float(0x436729fc)); // 162, 231.164f +path.lineTo(SkBits2Float(0x43220000), SkBits2Float(0x436729fc)); // 162, 231.164f +path.lineTo(SkBits2Float(0x43211810), SkBits2Float(0x4368bbe8)); // 161.094f, 232.734f +path.lineTo(SkBits2Float(0x43220000), SkBits2Float(0x436729fc)); // 162, 231.164f +path.close(); +path.moveTo(SkBits2Float(0x43202e14), SkBits2Float(0x436a4fdf)); // 160.18f, 234.312f +path.cubicTo(SkBits2Float(0x431f2418), SkBits2Float(0x4369b5c2), SkBits2Float(0x431df810), SkBits2Float(0x436995c2), SkBits2Float(0x431ce20c), SkBits2Float(0x4369dfbe)); // 159.141f, 233.71f, 157.969f, 233.585f, 156.883f, 233.874f +path.cubicTo(SkBits2Float(0x431bcc08), SkBits2Float(0x436a2bc6), SkBits2Float(0x431ad810), SkBits2Float(0x436adba5), SkBits2Float(0x431a4000), SkBits2Float(0x436be7ae)); // 155.797f, 234.171f, 154.844f, 234.858f, 154.25f, 235.905f +path.lineTo(SkBits2Float(0x43171810), SkBits2Float(0x436a15c2)); // 151.094f, 234.085f +path.cubicTo(SkBits2Float(0x43194e14), SkBits2Float(0x436643d6), SkBits2Float(0x431e2c08), SkBits2Float(0x4364f3b6), SkBits2Float(0x43220000), SkBits2Float(0x436729ba)); // 153.305f, 230.265f, 158.172f, 228.952f, 162, 231.163f +path.lineTo(SkBits2Float(0x43202e14), SkBits2Float(0x436a4fdf)); // 160.18f, 234.312f +path.close(); +path.moveTo(SkBits2Float(0x4321c5e3), SkBits2Float(0x43704000)); // 161.773f, 240.25f +path.cubicTo(SkBits2Float(0x43226000), SkBits2Float(0x436f3604), SkBits2Float(0x43228000), SkBits2Float(0x436e09fc), SkBits2Float(0x43223604), SkBits2Float(0x436cf3f8)); // 162.375f, 239.211f, 162.5f, 238.039f, 162.211f, 236.953f +path.cubicTo(SkBits2Float(0x4321ec08), SkBits2Float(0x436be000), SkBits2Float(0x43213a1d), SkBits2Float(0x436ae9fc), SkBits2Float(0x43202e14), SkBits2Float(0x436a4fdf)); // 161.922f, 235.875f, 161.227f, 234.914f, 160.18f, 234.312f +path.lineTo(SkBits2Float(0x43220000), SkBits2Float(0x436729fc)); // 162, 231.164f +path.cubicTo(SkBits2Float(0x4325d1ec), SkBits2Float(0x43696000), SkBits2Float(0x4327220c), SkBits2Float(0x436e4000), SkBits2Float(0x4324e9fc), SkBits2Float(0x437211ec)); // 165.82f, 233.375f, 167.133f, 238.25f, 164.914f, 242.07f +path.lineTo(SkBits2Float(0x4321c5e3), SkBits2Float(0x43704000)); // 161.773f, 240.25f +path.close(); +} + +static void joel_12(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + make_joel_12(path); + testSimplify(reporter, path, filename); +} + +static void joel_12x(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + make_joel_12(path); + testSimplify(reporter, path, filename); +} + +static void make_joel_13(SkPath& path) { +path.moveTo(SkBits2Float(0x43b4126f), SkBits2Float(0x43c058f6)); // 360.144f, 384.695f +path.cubicTo(SkBits2Float(0x43bd7c6b), SkBits2Float(0x43c05b02), SkBits2Float(0x43c51d71), SkBits2Float(0x43b8e8f6), SkBits2Float(0x43c5276d), SkBits2Float(0x43afc1ec)); // 378.972f, 384.711f, 394.23f, 369.82f, 394.308f, 351.515f +path.cubicTo(SkBits2Float(0x43c51d71), SkBits2Float(0x43a688f6), SkBits2Float(0x43bd7c6b), SkBits2Float(0x439f16ea), SkBits2Float(0x43b4126f), SkBits2Float(0x439f16ea)); // 394.23f, 333.07f, 378.972f, 318.179f, 360.144f, 318.179f +path.cubicTo(SkBits2Float(0x43aaa979), SkBits2Float(0x439f16ea), SkBits2Float(0x43a3076d), SkBits2Float(0x43a688f6), SkBits2Float(0x43a31063), SkBits2Float(0x43afc1ec)); // 341.324f, 318.179f, 326.058f, 333.07f, 326.128f, 351.515f +path.cubicTo(SkBits2Float(0x43a3076d), SkBits2Float(0x43b8e8f6), SkBits2Float(0x43aaa959), SkBits2Float(0x43c05b02), SkBits2Float(0x43b4126f), SkBits2Float(0x43c058f6)); // 326.058f, 369.82f, 341.323f, 384.711f, 360.144f, 384.695f +path.close(); +} + +static void joel_13(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + make_joel_13(path); + testSimplify(reporter, path, filename); +} + + +static void joel_13x(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + make_joel_13(path); + testSimplify(reporter, path, filename); +} + +static void make_joel_14(SkPath& path) { +path.moveTo(SkBits2Float(0x43f3b354), SkBits2Float(0x43d6770a)); // 487.401f, 428.93f +path.lineTo(SkBits2Float(0x43f3b354), SkBits2Float(0x43d6770a)); // 487.401f, 428.93f +path.close(); +path.moveTo(SkBits2Float(0x43f0fd50), SkBits2Float(0x43d6770a)); // 481.979f, 428.93f +path.lineTo(SkBits2Float(0x43f0fd50), SkBits2Float(0x43d6770a)); // 481.979f, 428.93f +path.close(); +path.moveTo(SkBits2Float(0x43f0fd50), SkBits2Float(0x43d6770a)); // 481.979f, 428.93f +path.lineTo(SkBits2Float(0x43f3b354), SkBits2Float(0x43d6770a)); // 487.401f, 428.93f +path.lineTo(SkBits2Float(0x43f0fd50), SkBits2Float(0x43d6770a)); // 481.979f, 428.93f +path.close(); +path.moveTo(SkBits2Float(0x43dfe76d), SkBits2Float(0x43d792f1)); // 447.808f, 431.148f +path.lineTo(SkBits2Float(0x43dfe76d), SkBits2Float(0x43d792f1)); // 447.808f, 431.148f +path.close(); +path.moveTo(SkBits2Float(0x43dfe76d), SkBits2Float(0x43d792f1)); // 447.808f, 431.148f +path.cubicTo(SkBits2Float(0x43e51979), SkBits2Float(0x43d611eb), SkBits2Float(0x43eb8667), SkBits2Float(0x43d765e3), SkBits2Float(0x43f0fd71), SkBits2Float(0x43d676e9)); // 458.199f, 428.14f, 471.05f, 430.796f, 481.98f, 428.929f +path.cubicTo(SkBits2Float(0x43eb8667), SkBits2Float(0x43d76604), SkBits2Float(0x43e51958), SkBits2Float(0x43d6120c), SkBits2Float(0x43dfe76d), SkBits2Float(0x43d792f1)); // 471.05f, 430.797f, 458.198f, 428.141f, 447.808f, 431.148f +path.close(); +path.moveTo(SkBits2Float(0x43df776d), SkBits2Float(0x43d6d603)); // 446.933f, 429.672f +path.lineTo(SkBits2Float(0x43df776d), SkBits2Float(0x43d6d603)); // 446.933f, 429.672f +path.close(); +path.moveTo(SkBits2Float(0x43df776d), SkBits2Float(0x43d6d603)); // 446.933f, 429.672f +path.lineTo(SkBits2Float(0x43dfe76d), SkBits2Float(0x43d79311)); // 447.808f, 431.149f +path.lineTo(SkBits2Float(0x43df776d), SkBits2Float(0x43d6d603)); // 446.933f, 429.672f +path.close(); +path.moveTo(SkBits2Float(0x43dd3169), SkBits2Float(0x43d792f1)); // 442.386f, 431.148f +path.lineTo(SkBits2Float(0x43dd3169), SkBits2Float(0x43d792f1)); // 442.386f, 431.148f +path.close(); +path.moveTo(SkBits2Float(0x43dd3169), SkBits2Float(0x43d792f1)); // 442.386f, 431.148f +path.cubicTo(SkBits2Float(0x43de376d), SkBits2Float(0x43d743f7), SkBits2Float(0x43de2873), SkBits2Float(0x43d68df3), SkBits2Float(0x43df776d), SkBits2Float(0x43d6d5e3)); // 444.433f, 430.531f, 444.316f, 429.109f, 446.933f, 429.671f +path.cubicTo(SkBits2Float(0x43de2852), SkBits2Float(0x43d68df3), SkBits2Float(0x43de376d), SkBits2Float(0x43d743f7), SkBits2Float(0x43dd3169), SkBits2Float(0x43d792f1)); // 444.315f, 429.109f, 444.433f, 430.531f, 442.386f, 431.148f +path.close(); +path.moveTo(SkBits2Float(0x43dcc169), SkBits2Float(0x43d6d603)); // 441.511f, 429.672f +path.lineTo(SkBits2Float(0x43dcc169), SkBits2Float(0x43d6d603)); // 441.511f, 429.672f +path.close(); +path.moveTo(SkBits2Float(0x43dcc169), SkBits2Float(0x43d6d603)); // 441.511f, 429.672f +path.lineTo(SkBits2Float(0x43dd3169), SkBits2Float(0x43d79311)); // 442.386f, 431.149f +path.lineTo(SkBits2Float(0x43dcc169), SkBits2Float(0x43d6d603)); // 441.511f, 429.672f +path.close(); +path.moveTo(SkBits2Float(0x43dad959), SkBits2Float(0x43d6d603)); // 437.698f, 429.672f +path.lineTo(SkBits2Float(0x43dad959), SkBits2Float(0x43d6d603)); // 437.698f, 429.672f +path.close(); +path.moveTo(SkBits2Float(0x43dad959), SkBits2Float(0x43d6d603)); // 437.698f, 429.672f +path.lineTo(SkBits2Float(0x43dcc149), SkBits2Float(0x43d6d603)); // 441.51f, 429.672f +path.lineTo(SkBits2Float(0x43dad959), SkBits2Float(0x43d6d603)); // 437.698f, 429.672f +path.close(); +path.moveTo(SkBits2Float(0x43e3cb65), SkBits2Float(0x43e3bd0d)); // 455.589f, 455.477f +path.lineTo(SkBits2Float(0x43e3cb65), SkBits2Float(0x43e3bd0d)); // 455.589f, 455.477f +path.close(); +path.moveTo(SkBits2Float(0x43e3cb65), SkBits2Float(0x43e3bd0d)); // 455.589f, 455.477f +path.lineTo(SkBits2Float(0x43dad959), SkBits2Float(0x43d6d603)); // 437.698f, 429.672f +path.lineTo(SkBits2Float(0x43e3cb65), SkBits2Float(0x43e3bd0d)); // 455.589f, 455.477f +path.close(); +path.moveTo(SkBits2Float(0x43e22d51), SkBits2Float(0x43e41b01)); // 452.354f, 456.211f +path.lineTo(SkBits2Float(0x43e22d51), SkBits2Float(0x43e41b01)); // 452.354f, 456.211f +path.close(); +path.moveTo(SkBits2Float(0x43e22d51), SkBits2Float(0x43e41b01)); // 452.354f, 456.211f +path.cubicTo(SkBits2Float(0x43e2ba5f), SkBits2Float(0x43e3f9fb), SkBits2Float(0x43e37e57), SkBits2Float(0x43e46df3), SkBits2Float(0x43e3cb45), SkBits2Float(0x43e3bd0d)); // 453.456f, 455.953f, 454.987f, 456.859f, 455.588f, 455.477f +path.cubicTo(SkBits2Float(0x43e37e57), SkBits2Float(0x43e46df2), SkBits2Float(0x43e2ba60), SkBits2Float(0x43e3f9fb), SkBits2Float(0x43e22d51), SkBits2Float(0x43e41b01)); // 454.987f, 456.859f, 453.456f, 455.953f, 452.354f, 456.211f +path.close(); +path.moveTo(SkBits2Float(0x43e22d51), SkBits2Float(0x43e479fb)); // 452.354f, 456.953f +path.lineTo(SkBits2Float(0x43e22d51), SkBits2Float(0x43e479fb)); // 452.354f, 456.953f +path.close(); +path.moveTo(SkBits2Float(0x43e22d51), SkBits2Float(0x43e479fb)); // 452.354f, 456.953f +path.lineTo(SkBits2Float(0x43e22d51), SkBits2Float(0x43e41b01)); // 452.354f, 456.211f +path.lineTo(SkBits2Float(0x43e22d51), SkBits2Float(0x43e479fb)); // 452.354f, 456.953f +path.close(); +path.moveTo(SkBits2Float(0x43e35a5f), SkBits2Float(0x43e479fb)); // 454.706f, 456.953f +path.lineTo(SkBits2Float(0x43e35a5f), SkBits2Float(0x43e479fb)); // 454.706f, 456.953f +path.close(); +path.moveTo(SkBits2Float(0x43e35a5f), SkBits2Float(0x43e479fb)); // 454.706f, 456.953f +path.lineTo(SkBits2Float(0x43e22d51), SkBits2Float(0x43e479fb)); // 452.354f, 456.953f +path.lineTo(SkBits2Float(0x43e35a5f), SkBits2Float(0x43e479fb)); // 454.706f, 456.953f +path.close(); +path.moveTo(SkBits2Float(0x43e35a5f), SkBits2Float(0x43e41b01)); // 454.706f, 456.211f +path.lineTo(SkBits2Float(0x43e35a5f), SkBits2Float(0x43e41b01)); // 454.706f, 456.211f +path.close(); +path.moveTo(SkBits2Float(0x43e35a5f), SkBits2Float(0x43e41b01)); // 454.706f, 456.211f +path.lineTo(SkBits2Float(0x43e35a5f), SkBits2Float(0x43e479fb)); // 454.706f, 456.953f +path.lineTo(SkBits2Float(0x43e35a5f), SkBits2Float(0x43e41b01)); // 454.706f, 456.211f +path.close(); +path.moveTo(SkBits2Float(0x43e1726f), SkBits2Float(0x43e90c07)); // 450.894f, 466.094f +path.lineTo(SkBits2Float(0x43e1726f), SkBits2Float(0x43e90c07)); // 450.894f, 466.094f +path.close(); +path.moveTo(SkBits2Float(0x43e1726f), SkBits2Float(0x43e90c07)); // 450.894f, 466.094f +path.cubicTo(SkBits2Float(0x43e2226f), SkBits2Float(0x43e769fb), SkBits2Float(0x43e50a7f), SkBits2Float(0x43e63915), SkBits2Float(0x43e35a5f), SkBits2Float(0x43e41b01)); // 452.269f, 462.828f, 458.082f, 460.446f, 454.706f, 456.211f +path.cubicTo(SkBits2Float(0x43e50a5f), SkBits2Float(0x43e638f5), SkBits2Float(0x43e2226f), SkBits2Float(0x43e769fb), SkBits2Float(0x43e1726f), SkBits2Float(0x43e90c07)); // 458.081f, 460.445f, 452.269f, 462.828f, 450.894f, 466.094f +path.close(); +path.moveTo(SkBits2Float(0x43f09f5d), SkBits2Float(0x43ea2709)); // 481.245f, 468.305f +path.lineTo(SkBits2Float(0x43f09f5d), SkBits2Float(0x43ea2709)); // 481.245f, 468.305f +path.close(); +path.moveTo(SkBits2Float(0x43f09f5d), SkBits2Float(0x43ea2709)); // 481.245f, 468.305f +path.cubicTo(SkBits2Float(0x43ebbc6b), SkBits2Float(0x43ea4105), SkBits2Float(0x43e56c6b), SkBits2Float(0x43ec9fff), SkBits2Float(0x43e1724f), SkBits2Float(0x43e90c07)); // 471.472f, 468.508f, 458.847f, 473.25f, 450.893f, 466.094f +path.cubicTo(SkBits2Float(0x43e56c6c), SkBits2Float(0x43ec9fff), SkBits2Float(0x43ebbc6c), SkBits2Float(0x43ea4105), SkBits2Float(0x43f09f5d), SkBits2Float(0x43ea2709)); // 458.847f, 473.25f, 471.472f, 468.508f, 481.245f, 468.305f +path.close(); +path.moveTo(SkBits2Float(0x43eea45b), SkBits2Float(0x43e9c7ee)); // 477.284f, 467.562f +path.lineTo(SkBits2Float(0x43eea45b), SkBits2Float(0x43e9c7ee)); // 477.284f, 467.562f +path.close(); +path.moveTo(SkBits2Float(0x43eea45b), SkBits2Float(0x43e9c7ee)); // 477.284f, 467.562f +path.cubicTo(SkBits2Float(0x43ef0c4b), SkBits2Float(0x43ea7ef8), SkBits2Float(0x43eff355), SkBits2Float(0x43ea10e4), SkBits2Float(0x43f09f5d), SkBits2Float(0x43ea26e8)); // 478.096f, 468.992f, 479.901f, 468.132f, 481.245f, 468.304f +path.cubicTo(SkBits2Float(0x43eff355), SkBits2Float(0x43ea1105), SkBits2Float(0x43ef0c6b), SkBits2Float(0x43ea7ef8), SkBits2Float(0x43eea45b), SkBits2Float(0x43e9c7ee)); // 479.901f, 468.133f, 478.097f, 468.992f, 477.284f, 467.562f +path.close(); +path.moveTo(SkBits2Float(0x43ee4667), SkBits2Float(0x43ea2709)); // 476.55f, 468.305f +path.lineTo(SkBits2Float(0x43ee4667), SkBits2Float(0x43ea2709)); // 476.55f, 468.305f +path.close(); +path.moveTo(SkBits2Float(0x43ee4667), SkBits2Float(0x43ea2709)); // 476.55f, 468.305f +path.lineTo(SkBits2Float(0x43eea45b), SkBits2Float(0x43e9c80f)); // 477.284f, 467.563f +path.lineTo(SkBits2Float(0x43ee4667), SkBits2Float(0x43ea2709)); // 476.55f, 468.305f +path.close(); +path.moveTo(SkBits2Float(0x43e9f26f), SkBits2Float(0x43e6c2f0)); // 467.894f, 461.523f +path.lineTo(SkBits2Float(0x43e9f26f), SkBits2Float(0x43e6c2f0)); // 467.894f, 461.523f +path.close(); +path.moveTo(SkBits2Float(0x43e9f26f), SkBits2Float(0x43e6c2f0)); // 467.894f, 461.523f +path.cubicTo(SkBits2Float(0x43eb8873), SkBits2Float(0x43e7dcec), SkBits2Float(0x43eb747b), SkBits2Float(0x43ea9b00), SkBits2Float(0x43ee4667), SkBits2Float(0x43ea26e8)); // 471.066f, 463.726f, 470.91f, 469.211f, 476.55f, 468.304f +path.cubicTo(SkBits2Float(0x43eb745b), SkBits2Float(0x43ea9b01), SkBits2Float(0x43eb8853), SkBits2Float(0x43e7dd0d), SkBits2Float(0x43e9f26f), SkBits2Float(0x43e6c2f0)); // 470.909f, 469.211f, 471.065f, 463.727f, 467.894f, 461.523f +path.close(); +path.moveTo(SkBits2Float(0x43ebee56), SkBits2Float(0x43decc07)); // 471.862f, 445.594f +path.lineTo(SkBits2Float(0x43ebee56), SkBits2Float(0x43decc07)); // 471.862f, 445.594f +path.close(); +path.moveTo(SkBits2Float(0x43ebee56), SkBits2Float(0x43decc07)); // 471.862f, 445.594f +path.cubicTo(SkBits2Float(0x43e85f5c), SkBits2Float(0x43e04915), SkBits2Float(0x43eaa148), SkBits2Float(0x43e41c07), SkBits2Float(0x43e9f24e), SkBits2Float(0x43e6c311)); // 464.745f, 448.571f, 469.26f, 456.219f, 467.893f, 461.524f +path.cubicTo(SkBits2Float(0x43eaa169), SkBits2Float(0x43e41c07), SkBits2Float(0x43e85f5c), SkBits2Float(0x43e048f4), SkBits2Float(0x43ebee56), SkBits2Float(0x43decc07)); // 469.261f, 456.219f, 464.745f, 448.57f, 471.862f, 445.594f +path.close(); +path.moveTo(SkBits2Float(0x43eac168), SkBits2Float(0x43dd3fff)); // 469.511f, 442.5f +path.lineTo(SkBits2Float(0x43eac168), SkBits2Float(0x43dd3fff)); // 469.511f, 442.5f +path.close(); +path.moveTo(SkBits2Float(0x43eac168), SkBits2Float(0x43dd3fff)); // 469.511f, 442.5f +path.cubicTo(SkBits2Float(0x43eb245a), SkBits2Float(0x43ddc7ef), SkBits2Float(0x43eaf45a), SkBits2Float(0x43dedd0d), SkBits2Float(0x43ebee76), SkBits2Float(0x43decc07)); // 470.284f, 443.562f, 469.909f, 445.727f, 471.863f, 445.594f +path.cubicTo(SkBits2Float(0x43eaf459), SkBits2Float(0x43dedd0d), SkBits2Float(0x43eb2459), SkBits2Float(0x43ddc7ee), SkBits2Float(0x43eac168), SkBits2Float(0x43dd3fff)); // 469.909f, 445.727f, 470.284f, 443.562f, 469.511f, 442.5f +path.close(); +path.moveTo(SkBits2Float(0x43ec4c6a), SkBits2Float(0x43dce105)); // 472.597f, 441.758f +path.lineTo(SkBits2Float(0x43ec4c6a), SkBits2Float(0x43dce105)); // 472.597f, 441.758f +path.close(); +path.moveTo(SkBits2Float(0x43ec4c6a), SkBits2Float(0x43dce105)); // 472.597f, 441.758f +path.cubicTo(SkBits2Float(0x43ebcb64), SkBits2Float(0x43dd08f5), SkBits2Float(0x43eb0c6a), SkBits2Float(0x43dc9603), SkBits2Float(0x43eac168), SkBits2Float(0x43dd3fff)); // 471.589f, 442.07f, 470.097f, 441.172f, 469.511f, 442.5f +path.cubicTo(SkBits2Float(0x43eb0c6a), SkBits2Float(0x43dc9603), SkBits2Float(0x43ebcb64), SkBits2Float(0x43dd08f5), SkBits2Float(0x43ec4c6a), SkBits2Float(0x43dce105)); // 470.097f, 441.172f, 471.589f, 442.07f, 472.597f, 441.758f +path.close(); +path.moveTo(SkBits2Float(0x43ecbb64), SkBits2Float(0x43ddb105)); // 473.464f, 443.383f +path.lineTo(SkBits2Float(0x43ecbb64), SkBits2Float(0x43ddb105)); // 473.464f, 443.383f +path.close(); +path.moveTo(SkBits2Float(0x43ecbb64), SkBits2Float(0x43ddb105)); // 473.464f, 443.383f +path.lineTo(SkBits2Float(0x43ec4c6a), SkBits2Float(0x43dce105)); // 472.597f, 441.758f +path.lineTo(SkBits2Float(0x43ecbb64), SkBits2Float(0x43ddb105)); // 473.464f, 443.383f +path.close(); +path.moveTo(SkBits2Float(0x43eea45a), SkBits2Float(0x43dc24fd)); // 477.284f, 440.289f +path.lineTo(SkBits2Float(0x43eea45a), SkBits2Float(0x43dc24fd)); // 477.284f, 440.289f +path.close(); +path.moveTo(SkBits2Float(0x43eea45a), SkBits2Float(0x43dc24fd)); // 477.284f, 440.289f +path.cubicTo(SkBits2Float(0x43eef354), SkBits2Float(0x43dd4c07), SkBits2Float(0x43ed4a5e), SkBits2Float(0x43dcfef9), SkBits2Float(0x43ecbb64), SkBits2Float(0x43ddb105)); // 477.901f, 442.594f, 474.581f, 441.992f, 473.464f, 443.383f +path.cubicTo(SkBits2Float(0x43ed4a5e), SkBits2Float(0x43dcfef9), SkBits2Float(0x43eef354), SkBits2Float(0x43dd4c07), SkBits2Float(0x43eea45a), SkBits2Float(0x43dc24fd)); // 474.581f, 441.992f, 477.901f, 442.594f, 477.284f, 440.289f +path.close(); +path.moveTo(SkBits2Float(0x43f09f5c), SkBits2Float(0x43dc24fd)); // 481.245f, 440.289f +path.lineTo(SkBits2Float(0x43f09f5c), SkBits2Float(0x43dc24fd)); // 481.245f, 440.289f +path.close(); +path.moveTo(SkBits2Float(0x43f09f5c), SkBits2Float(0x43dc24fd)); // 481.245f, 440.289f +path.cubicTo(SkBits2Float(0x43effc6a), SkBits2Float(0x43daeced), SkBits2Float(0x43ef6a5e), SkBits2Float(0x43dbe4fd), SkBits2Float(0x43eea45a), SkBits2Float(0x43dc24fd)); // 479.972f, 437.851f, 478.831f, 439.789f, 477.284f, 440.289f +path.cubicTo(SkBits2Float(0x43ef6a5e), SkBits2Float(0x43dbe4fd), SkBits2Float(0x43effc6a), SkBits2Float(0x43daed0d), SkBits2Float(0x43f09f5c), SkBits2Float(0x43dc24fd)); // 478.831f, 439.789f, 479.972f, 437.852f, 481.245f, 440.289f +path.close(); +path.moveTo(SkBits2Float(0x43f2f76c), SkBits2Float(0x43dbc603)); // 485.933f, 439.547f +path.lineTo(SkBits2Float(0x43f2f76c), SkBits2Float(0x43dbc603)); // 485.933f, 439.547f +path.close(); +path.moveTo(SkBits2Float(0x43f2f76c), SkBits2Float(0x43dbc603)); // 485.933f, 439.547f +path.cubicTo(SkBits2Float(0x43f24c6a), SkBits2Float(0x43dc3b01), SkBits2Float(0x43f16b64), SkBits2Float(0x43dc2311), SkBits2Float(0x43f09f5c), SkBits2Float(0x43dc24fd)); // 484.597f, 440.461f, 482.839f, 440.274f, 481.245f, 440.289f +path.cubicTo(SkBits2Float(0x43f16b64), SkBits2Float(0x43dc23f7), SkBits2Float(0x43f24c6a), SkBits2Float(0x43dc3b01), SkBits2Float(0x43f2f76c), SkBits2Float(0x43dbc603)); // 482.839f, 440.281f, 484.597f, 440.461f, 485.933f, 439.547f +path.close(); +path.moveTo(SkBits2Float(0x43f4de55), SkBits2Float(0x43d97d0d)); // 489.737f, 434.977f +path.lineTo(SkBits2Float(0x43f4de55), SkBits2Float(0x43d97d0d)); // 489.737f, 434.977f +path.close(); +path.moveTo(SkBits2Float(0x43f4de55), SkBits2Float(0x43d97d0d)); // 489.737f, 434.977f +path.cubicTo(SkBits2Float(0x43f47665), SkBits2Float(0x43da020b), SkBits2Float(0x43f42851), SkBits2Float(0x43db9417), SkBits2Float(0x43f2f74b), SkBits2Float(0x43dbc603)); // 488.925f, 436.016f, 488.315f, 439.157f, 485.932f, 439.547f +path.cubicTo(SkBits2Float(0x43f42851), SkBits2Float(0x43db93f7), SkBits2Float(0x43f47666), SkBits2Float(0x43da020b), SkBits2Float(0x43f4de55), SkBits2Float(0x43d97d0d)); // 488.315f, 439.156f, 488.925f, 436.016f, 489.737f, 434.977f +path.close(); +path.moveTo(SkBits2Float(0x43f48061), SkBits2Float(0x43d97d0d)); // 489.003f, 434.977f +path.lineTo(SkBits2Float(0x43f48061), SkBits2Float(0x43d97d0d)); // 489.003f, 434.977f +path.close(); +path.moveTo(SkBits2Float(0x43f48061), SkBits2Float(0x43d97d0d)); // 489.003f, 434.977f +path.lineTo(SkBits2Float(0x43f4de55), SkBits2Float(0x43d97d0d)); // 489.737f, 434.977f +path.lineTo(SkBits2Float(0x43f48061), SkBits2Float(0x43d97d0d)); // 489.003f, 434.977f +path.close(); +path.moveTo(SkBits2Float(0x43f3b353), SkBits2Float(0x43d67709)); // 487.401f, 428.93f +path.cubicTo(SkBits2Float(0x43f39957), SkBits2Float(0x43d79ef9), SkBits2Float(0x43f3ca5d), SkBits2Float(0x43d8a603), SkBits2Float(0x43f48061), SkBits2Float(0x43d97d0d)); // 487.198f, 431.242f, 487.581f, 433.297f, 489.003f, 434.977f +path.cubicTo(SkBits2Float(0x43f3ca5d), SkBits2Float(0x43d8a603), SkBits2Float(0x43f39957), SkBits2Float(0x43d79ef9), SkBits2Float(0x43f3b353), SkBits2Float(0x43d67709)); // 487.581f, 433.297f, 487.198f, 431.242f, 487.401f, 428.93f +path.close(); +} + +static void joel_14(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + make_joel_14(path); +testSimplify(reporter, path, filename); +} + +static void joel_14x(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + make_joel_14(path); +testSimplify(reporter, path, filename); +} + +static void make_joel_15(SkPath& path) { +path.moveTo(SkBits2Float(0x439e276d), SkBits2Float(0x43dad106)); // 316.308f, 437.633f +path.lineTo(SkBits2Float(0x439e276d), SkBits2Float(0x43dad106)); // 316.308f, 437.633f +path.close(); +path.moveTo(SkBits2Float(0x439c1959), SkBits2Float(0x43d78000)); // 312.198f, 431 +path.lineTo(SkBits2Float(0x439c1959), SkBits2Float(0x43d78000)); // 312.198f, 431 +path.close(); +path.moveTo(SkBits2Float(0x439c1959), SkBits2Float(0x43d78000)); // 312.198f, 431 +path.cubicTo(SkBits2Float(0x439ea45b), SkBits2Float(0x43d6d000), SkBits2Float(0x439cce57), SkBits2Float(0x43d9f3f8), SkBits2Float(0x439e274d), SkBits2Float(0x43dad106)); // 317.284f, 429.625f, 313.612f, 435.906f, 316.307f, 437.633f +path.cubicTo(SkBits2Float(0x439cce57), SkBits2Float(0x43d9f3f8), SkBits2Float(0x439ea45b), SkBits2Float(0x43d6d000), SkBits2Float(0x439c1959), SkBits2Float(0x43d78000)); // 313.612f, 435.906f, 317.284f, 429.625f, 312.198f, 431 +path.close(); +path.moveTo(SkBits2Float(0x439c1959), SkBits2Float(0x43d8f8f6)); // 312.198f, 433.945f +path.lineTo(SkBits2Float(0x439c1959), SkBits2Float(0x43d8f8f6)); // 312.198f, 433.945f +path.close(); +path.moveTo(SkBits2Float(0x439c1959), SkBits2Float(0x43d8f8f6)); // 312.198f, 433.945f +path.lineTo(SkBits2Float(0x439c1959), SkBits2Float(0x43d78000)); // 312.198f, 431 +path.lineTo(SkBits2Float(0x439c1959), SkBits2Float(0x43d8f8f6)); // 312.198f, 433.945f +path.close(); +path.moveTo(SkBits2Float(0x439f7853), SkBits2Float(0x43e5820c)); // 318.94f, 459.016f +path.lineTo(SkBits2Float(0x439f7853), SkBits2Float(0x43e5820c)); // 318.94f, 459.016f +path.close(); +path.moveTo(SkBits2Float(0x439f7853), SkBits2Float(0x43e5820c)); // 318.94f, 459.016f +path.cubicTo(SkBits2Float(0x439e1647), SkBits2Float(0x43e17106), SkBits2Float(0x439d945b), SkBits2Float(0x43dd020c), SkBits2Float(0x439c1959), SkBits2Float(0x43d8f916)); // 316.174f, 450.883f, 315.159f, 442.016f, 312.198f, 433.946f +path.cubicTo(SkBits2Float(0x439d945b), SkBits2Float(0x43dd020c), SkBits2Float(0x439e1667), SkBits2Float(0x43e17106), SkBits2Float(0x439f7853), SkBits2Float(0x43e5820c)); // 315.159f, 442.016f, 316.175f, 450.883f, 318.94f, 459.016f +path.close(); +path.moveTo(SkBits2Float(0x439ffc6c), SkBits2Float(0x43e7f106)); // 319.972f, 463.883f +path.lineTo(SkBits2Float(0x439ffc6c), SkBits2Float(0x43e7f106)); // 319.972f, 463.883f +path.close(); +path.moveTo(SkBits2Float(0x439ffc6c), SkBits2Float(0x43e7f106)); // 319.972f, 463.883f +path.cubicTo(SkBits2Float(0x439f5668), SkBits2Float(0x43e758f6), SkBits2Float(0x439fec6c), SkBits2Float(0x43e63604), SkBits2Float(0x439f7874), SkBits2Float(0x43e5820c)); // 318.675f, 462.695f, 319.847f, 460.422f, 318.941f, 459.016f +path.cubicTo(SkBits2Float(0x439fec6c), SkBits2Float(0x43e63604), SkBits2Float(0x439f5668), SkBits2Float(0x43e758f5), SkBits2Float(0x439ffc6c), SkBits2Float(0x43e7f106)); // 319.847f, 460.422f, 318.675f, 462.695f, 319.972f, 463.883f +path.close(); +path.moveTo(SkBits2Float(0x43a12853), SkBits2Float(0x43ede9fc)); // 322.315f, 475.828f +path.lineTo(SkBits2Float(0x43a12853), SkBits2Float(0x43ede9fc)); // 322.315f, 475.828f +path.close(); +path.moveTo(SkBits2Float(0x43a12853), SkBits2Float(0x43ede9fc)); // 322.315f, 475.828f +path.cubicTo(SkBits2Float(0x43a18c4b), SkBits2Float(0x43eb7604), SkBits2Float(0x439fe45b), SkBits2Float(0x43ea4b02), SkBits2Float(0x439ffc4b), SkBits2Float(0x43e7f106)); // 323.096f, 470.922f, 319.784f, 468.586f, 319.971f, 463.883f +path.cubicTo(SkBits2Float(0x439fe45b), SkBits2Float(0x43ea4b02), SkBits2Float(0x43a18c6c), SkBits2Float(0x43eb7604), SkBits2Float(0x43a12853), SkBits2Float(0x43ede9fc)); // 319.784f, 468.586f, 323.097f, 470.922f, 322.315f, 475.828f +path.close(); +path.moveTo(SkBits2Float(0x43a1e45b), SkBits2Float(0x43ef63f8)); // 323.784f, 478.781f +path.lineTo(SkBits2Float(0x43a1e45b), SkBits2Float(0x43ef63f8)); // 323.784f, 478.781f +path.close(); +path.moveTo(SkBits2Float(0x43a1e45b), SkBits2Float(0x43ef63f8)); // 323.784f, 478.781f +path.cubicTo(SkBits2Float(0x43a20561), SkBits2Float(0x43eeb9fc), SkBits2Float(0x43a1ae57), SkBits2Float(0x43ee4be8), SkBits2Float(0x43a12853), SkBits2Float(0x43ede9fc)); // 324.042f, 477.453f, 323.362f, 476.593f, 322.315f, 475.828f +path.cubicTo(SkBits2Float(0x43a1ae57), SkBits2Float(0x43ee4c08), SkBits2Float(0x43a20561), SkBits2Float(0x43eeb9fc), SkBits2Float(0x43a1e45b), SkBits2Float(0x43ef63f8)); // 323.362f, 476.594f, 324.042f, 477.453f, 323.784f, 478.781f +path.close(); +path.moveTo(SkBits2Float(0x439fb169), SkBits2Float(0x43f032f2)); // 319.386f, 480.398f +path.lineTo(SkBits2Float(0x439fb169), SkBits2Float(0x43f032f2)); // 319.386f, 480.398f +path.close(); +path.moveTo(SkBits2Float(0x439fb169), SkBits2Float(0x43f032f2)); // 319.386f, 480.398f +path.cubicTo(SkBits2Float(0x43a08063), SkBits2Float(0x43f022f2), SkBits2Float(0x43a1ec6b), SkBits2Float(0x43f078f6), SkBits2Float(0x43a1e45b), SkBits2Float(0x43ef63f8)); // 321.003f, 480.273f, 323.847f, 480.945f, 323.784f, 478.781f +path.cubicTo(SkBits2Float(0x43a1ec6b), SkBits2Float(0x43f078f6), SkBits2Float(0x43a08063), SkBits2Float(0x43f022f2), SkBits2Float(0x439fb169), SkBits2Float(0x43f032f2)); // 323.847f, 480.945f, 321.003f, 480.273f, 319.386f, 480.398f +path.close(); +path.moveTo(SkBits2Float(0x439e4d50), SkBits2Float(0x43f16106)); // 316.604f, 482.758f +path.lineTo(SkBits2Float(0x439e4d50), SkBits2Float(0x43f16106)); // 316.604f, 482.758f +path.close(); +path.moveTo(SkBits2Float(0x439e4d50), SkBits2Float(0x43f16106)); // 316.604f, 482.758f +path.cubicTo(SkBits2Float(0x439de45a), SkBits2Float(0x43f05000), SkBits2Float(0x439f445a), SkBits2Float(0x43f0b20c), SkBits2Float(0x439fb148), SkBits2Float(0x43f03312)); // 315.784f, 480.625f, 318.534f, 481.391f, 319.385f, 480.399f +path.cubicTo(SkBits2Float(0x439f445a), SkBits2Float(0x43f0b20c), SkBits2Float(0x439de45a), SkBits2Float(0x43f05000), SkBits2Float(0x439e4d50), SkBits2Float(0x43f16106)); // 318.534f, 481.391f, 315.784f, 480.625f, 316.604f, 482.758f +path.close(); +path.moveTo(SkBits2Float(0x43a0de56), SkBits2Float(0x43f7470a)); // 321.737f, 494.555f +path.lineTo(SkBits2Float(0x43a0de56), SkBits2Float(0x43f7470a)); // 321.737f, 494.555f +path.close(); +path.moveTo(SkBits2Float(0x43a0de56), SkBits2Float(0x43f7470a)); // 321.737f, 494.555f +path.cubicTo(SkBits2Float(0x439f4062), SkBits2Float(0x43f5a106), SkBits2Float(0x439f2b64), SkBits2Float(0x43f33106), SkBits2Float(0x439e4d50), SkBits2Float(0x43f16106)); // 318.503f, 491.258f, 318.339f, 486.383f, 316.604f, 482.758f +path.cubicTo(SkBits2Float(0x439f2b64), SkBits2Float(0x43f33106), SkBits2Float(0x439f4062), SkBits2Float(0x43f5a106), SkBits2Float(0x43a0de56), SkBits2Float(0x43f7470a)); // 318.339f, 486.383f, 318.503f, 491.258f, 321.737f, 494.555f +path.close(); +path.moveTo(SkBits2Float(0x43a3945a), SkBits2Float(0x43fa13f8)); // 327.159f, 500.156f +path.lineTo(SkBits2Float(0x43a3945a), SkBits2Float(0x43fa13f8)); // 327.159f, 500.156f +path.close(); +path.moveTo(SkBits2Float(0x43a3945a), SkBits2Float(0x43fa13f8)); // 327.159f, 500.156f +path.cubicTo(SkBits2Float(0x43a2dc4a), SkBits2Float(0x43f8ab02), SkBits2Float(0x43a0d74c), SkBits2Float(0x43f8f4fe), SkBits2Float(0x43a0de56), SkBits2Float(0x43f746ea)); // 325.721f, 497.336f, 321.682f, 497.914f, 321.737f, 494.554f +path.cubicTo(SkBits2Float(0x43a0d76d), SkBits2Float(0x43f8f4fe), SkBits2Float(0x43a2dc6a), SkBits2Float(0x43f8ab03), SkBits2Float(0x43a3945a), SkBits2Float(0x43fa13f8)); // 321.683f, 497.914f, 325.722f, 497.336f, 327.159f, 500.156f +path.close(); +path.moveTo(SkBits2Float(0x43a58e56), SkBits2Float(0x43fa98f6)); // 331.112f, 501.195f +path.lineTo(SkBits2Float(0x43a58e56), SkBits2Float(0x43fa98f6)); // 331.112f, 501.195f +path.close(); +path.moveTo(SkBits2Float(0x43a58e56), SkBits2Float(0x43fa98f6)); // 331.112f, 501.195f +path.cubicTo(SkBits2Float(0x43a50148), SkBits2Float(0x43fa2be8), SkBits2Float(0x43a45646), SkBits2Float(0x43fa02f2), SkBits2Float(0x43a3945a), SkBits2Float(0x43fa13f8)); // 330.01f, 500.343f, 328.674f, 500.023f, 327.159f, 500.156f +path.cubicTo(SkBits2Float(0x43a45666), SkBits2Float(0x43fa02f2), SkBits2Float(0x43a50168), SkBits2Float(0x43fa2c08), SkBits2Float(0x43a58e56), SkBits2Float(0x43fa98f6)); // 328.675f, 500.023f, 330.011f, 500.344f, 331.112f, 501.195f +path.close(); +path.moveTo(SkBits2Float(0x43a64958), SkBits2Float(0x43f8c000)); // 332.573f, 497.5f +path.lineTo(SkBits2Float(0x43a64958), SkBits2Float(0x43f8c000)); // 332.573f, 497.5f +path.close(); +path.moveTo(SkBits2Float(0x43a64958), SkBits2Float(0x43f8c000)); // 332.573f, 497.5f +path.lineTo(SkBits2Float(0x43a58e56), SkBits2Float(0x43fa98f6)); // 331.112f, 501.195f +path.lineTo(SkBits2Float(0x43a64958), SkBits2Float(0x43f8c000)); // 332.573f, 497.5f +path.close(); +path.moveTo(SkBits2Float(0x43a73e56), SkBits2Float(0x43f5820c)); // 334.487f, 491.016f +path.lineTo(SkBits2Float(0x43a73e56), SkBits2Float(0x43f5820c)); // 334.487f, 491.016f +path.close(); +path.moveTo(SkBits2Float(0x43a73e56), SkBits2Float(0x43f5820c)); // 334.487f, 491.016f +path.cubicTo(SkBits2Float(0x43a64d50), SkBits2Float(0x43f654fe), SkBits2Float(0x43a7174c), SkBits2Float(0x43f7de14), SkBits2Float(0x43a64958), SkBits2Float(0x43f8c000)); // 332.604f, 492.664f, 334.182f, 495.735f, 332.573f, 497.5f +path.cubicTo(SkBits2Float(0x43a7176c), SkBits2Float(0x43f7ddf4), SkBits2Float(0x43a64d50), SkBits2Float(0x43f654fe), SkBits2Float(0x43a73e56), SkBits2Float(0x43f5820c)); // 334.183f, 495.734f, 332.604f, 492.664f, 334.487f, 491.016f +path.close(); +path.moveTo(SkBits2Float(0x43a6f26f), SkBits2Float(0x43f20b02)); // 333.894f, 484.086f +path.lineTo(SkBits2Float(0x43a6f26f), SkBits2Float(0x43f20b02)); // 333.894f, 484.086f +path.close(); +path.moveTo(SkBits2Float(0x43a6f26f), SkBits2Float(0x43f20b02)); // 333.894f, 484.086f +path.cubicTo(SkBits2Float(0x43a78d71), SkBits2Float(0x43f2f810), SkBits2Float(0x43a72873), SkBits2Float(0x43f453f8), SkBits2Float(0x43a73e77), SkBits2Float(0x43f5820c)); // 335.105f, 485.938f, 334.316f, 488.656f, 334.488f, 491.016f +path.cubicTo(SkBits2Float(0x43a72852), SkBits2Float(0x43f453f8), SkBits2Float(0x43a78d50), SkBits2Float(0x43f2f810), SkBits2Float(0x43a6f26f), SkBits2Float(0x43f20b02)); // 334.315f, 488.656f, 335.104f, 485.938f, 333.894f, 484.086f +path.close(); +path.moveTo(SkBits2Float(0x43a6ba5f), SkBits2Float(0x43ef3d0e)); // 333.456f, 478.477f +path.lineTo(SkBits2Float(0x43a6ba5f), SkBits2Float(0x43ef3d0e)); // 333.456f, 478.477f +path.close(); +path.moveTo(SkBits2Float(0x43a6ba5f), SkBits2Float(0x43ef3d0e)); // 333.456f, 478.477f +path.cubicTo(SkBits2Float(0x43a60e57), SkBits2Float(0x43f04000), SkBits2Float(0x43a82355), SkBits2Float(0x43f0fc08), SkBits2Float(0x43a6f26f), SkBits2Float(0x43f20b02)); // 332.112f, 480.5f, 336.276f, 481.969f, 333.894f, 484.086f +path.cubicTo(SkBits2Float(0x43a82354), SkBits2Float(0x43f0fc08), SkBits2Float(0x43a60e56), SkBits2Float(0x43f04000), SkBits2Float(0x43a6ba5f), SkBits2Float(0x43ef3d0e)); // 336.276f, 481.969f, 332.112f, 480.5f, 333.456f, 478.477f +path.close(); +path.moveTo(SkBits2Float(0x43a35c6b), SkBits2Float(0x43ef88f5)); // 326.722f, 479.07f +path.lineTo(SkBits2Float(0x43a35c6b), SkBits2Float(0x43ef88f5)); // 326.722f, 479.07f +path.close(); +path.moveTo(SkBits2Float(0x43a35c6b), SkBits2Float(0x43ef88f5)); // 326.722f, 479.07f +path.cubicTo(SkBits2Float(0x43a4b26f), SkBits2Float(0x43efe105), SkBits2Float(0x43a5b76d), SkBits2Float(0x43ee2ef9), SkBits2Float(0x43a6ba5f), SkBits2Float(0x43ef3ced)); // 329.394f, 479.758f, 331.433f, 476.367f, 333.456f, 478.476f +path.cubicTo(SkBits2Float(0x43a5b76d), SkBits2Float(0x43ee2ef9), SkBits2Float(0x43a4b26f), SkBits2Float(0x43efe106), SkBits2Float(0x43a35c6b), SkBits2Float(0x43ef88f5)); // 331.433f, 476.367f, 329.394f, 479.758f, 326.722f, 479.07f +path.close(); +path.moveTo(SkBits2Float(0x43a08063), SkBits2Float(0x43e5a70a)); // 321.003f, 459.305f +path.lineTo(SkBits2Float(0x43a08063), SkBits2Float(0x43e5a70a)); // 321.003f, 459.305f +path.close(); +path.moveTo(SkBits2Float(0x43a08063), SkBits2Float(0x43e5a70a)); // 321.003f, 459.305f +path.cubicTo(SkBits2Float(0x43a15169), SkBits2Float(0x43e90312), SkBits2Float(0x43a2626f), SkBits2Float(0x43ec4312), SkBits2Float(0x43a35c6b), SkBits2Float(0x43ef8916)); // 322.636f, 466.024f, 324.769f, 472.524f, 326.722f, 479.071f +path.cubicTo(SkBits2Float(0x43a2626f), SkBits2Float(0x43ec42f1), SkBits2Float(0x43a15169), SkBits2Float(0x43e902f1), SkBits2Float(0x43a08063), SkBits2Float(0x43e5a70a)); // 324.769f, 472.523f, 322.636f, 466.023f, 321.003f, 459.305f +path.close(); +path.moveTo(SkBits2Float(0x43a05a5f), SkBits2Float(0x43e407ef)); // 320.706f, 456.062f +path.lineTo(SkBits2Float(0x43a05a5f), SkBits2Float(0x43e407ef)); // 320.706f, 456.062f +path.close(); +path.moveTo(SkBits2Float(0x43a05a5f), SkBits2Float(0x43e407ef)); // 320.706f, 456.062f +path.lineTo(SkBits2Float(0x43a08063), SkBits2Float(0x43e5a6e9)); // 321.003f, 459.304f +path.lineTo(SkBits2Float(0x43a05a5f), SkBits2Float(0x43e407ef)); // 320.706f, 456.062f +path.close(); +path.moveTo(SkBits2Float(0x439ecf5d), SkBits2Float(0x43dd3fff)); // 317.62f, 442.5f +path.lineTo(SkBits2Float(0x439ecf5d), SkBits2Float(0x43dd3fff)); // 317.62f, 442.5f +path.close(); +path.moveTo(SkBits2Float(0x439ecf5d), SkBits2Float(0x43dd3fff)); // 317.62f, 442.5f +path.cubicTo(SkBits2Float(0x439e9c6b), SkBits2Float(0x43dfcb01), SkBits2Float(0x439fbe57), SkBits2Float(0x43e1cc07), SkBits2Float(0x43a05a5f), SkBits2Float(0x43e407ef)); // 317.222f, 447.586f, 319.487f, 451.594f, 320.706f, 456.062f +path.cubicTo(SkBits2Float(0x439fbe57), SkBits2Float(0x43e1cc08), SkBits2Float(0x439e9c6b), SkBits2Float(0x43dfcb01), SkBits2Float(0x439ecf5d), SkBits2Float(0x43dd3fff)); // 319.487f, 451.594f, 317.222f, 447.586f, 317.62f, 442.5f +path.close(); +path.moveTo(SkBits2Float(0x439e276d), SkBits2Float(0x43dad105)); // 316.308f, 437.633f +path.cubicTo(SkBits2Float(0x439e4979), SkBits2Float(0x43dba4fd), SkBits2Float(0x439dc375), SkBits2Float(0x43dce915), SkBits2Float(0x439ecf5d), SkBits2Float(0x43dd3fff)); // 316.574f, 439.289f, 315.527f, 441.821f, 317.62f, 442.5f +path.cubicTo(SkBits2Float(0x439dc355), SkBits2Float(0x43dce8f5), SkBits2Float(0x439e4959), SkBits2Float(0x43dba4fd), SkBits2Float(0x439e276d), SkBits2Float(0x43dad105)); // 315.526f, 441.82f, 316.573f, 439.289f, 316.308f, 437.633f +path.close(); +} + +static void joel_15(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + make_joel_15(path); +testSimplify(reporter, path, filename); +} + +static void joel_15x(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + make_joel_15(path); +testSimplify(reporter, path, filename); +} + +static void make_joel_16(SkPath& path) { +path.moveTo(SkBits2Float(0x420e6c8b), SkBits2Float(0x426bdf3b)); // 35.606f, 58.968f +path.lineTo(SkBits2Float(0x420fcccd), SkBits2Float(0x426c7ef9)); // 35.95f, 59.124f +path.cubicTo(SkBits2Float(0x420fcccd), SkBits2Float(0x426c7ef9), SkBits2Float(0x42093d71), SkBits2Float(0x426c6e97), SkBits2Float(0x42036c8b), SkBits2Float(0x426cbf7c)); // 35.95f, 59.124f, 34.31f, 59.108f, 32.856f, 59.187f +path.cubicTo(SkBits2Float(0x41fb3958), SkBits2Float(0x426d0f5b), SkBits2Float(0x41f076c8), SkBits2Float(0x426d48b3), SkBits2Float(0x41ef47ae), SkBits2Float(0x426d947a)); // 31.403f, 59.265f, 30.058f, 59.321f, 29.91f, 59.395f +path.cubicTo(SkBits2Float(0x41ee1aa0), SkBits2Float(0x426ddf3b), SkBits2Float(0x41ec6041), SkBits2Float(0x426edb22), SkBits2Float(0x41eb1aa0), SkBits2Float(0x426fee97)); // 29.763f, 59.468f, 29.547f, 59.714f, 29.388f, 59.983f +path.cubicTo(SkBits2Float(0x41eb1eb9), SkBits2Float(0x426feb85), SkBits2Float(0x41e9ba5e), SkBits2Float(0x42711eb8), SkBits2Float(0x41e9ba5e), SkBits2Float(0x42711eb8)); // 29.39f, 59.98f, 29.216f, 60.28f, 29.216f, 60.28f +path.lineTo(SkBits2Float(0x41e99999), SkBits2Float(0x42718f5c)); // 29.2f, 60.39f +path.cubicTo(SkBits2Float(0x41ea76c8), SkBits2Float(0x4271a5e3), SkBits2Float(0x4212dd2f), SkBits2Float(0x42707efa), SkBits2Float(0x4212dd2f), SkBits2Float(0x42707efa)); // 29.308f, 60.412f, 36.716f, 60.124f, 36.716f, 60.124f +path.cubicTo(SkBits2Float(0x4212dd2f), SkBits2Float(0x42707efa), SkBits2Float(0x42124395), SkBits2Float(0x42707be8), SkBits2Float(0x42131ba6), SkBits2Float(0x4270b646)); // 36.716f, 60.124f, 36.566f, 60.121f, 36.777f, 60.178f +path.cubicTo(SkBits2Float(0x42131581), SkBits2Float(0x42710000), SkBits2Float(0x42130831), SkBits2Float(0x42711688), SkBits2Float(0x4213072b), SkBits2Float(0x42711688)); // 36.771f, 60.25f, 36.758f, 60.272f, 36.757f, 60.272f +path.cubicTo(SkBits2Float(0x4212fae1), SkBits2Float(0x42711aa1), SkBits2Float(0x42127cee), SkBits2Float(0x42714eda), SkBits2Float(0x42127cee), SkBits2Float(0x42714eda)); // 36.745f, 60.276f, 36.622f, 60.327f, 36.622f, 60.327f +path.cubicTo(SkBits2Float(0x42127ae2), SkBits2Float(0x42714eda), SkBits2Float(0x41c67ae2), SkBits2Float(0x42730f5d), SkBits2Float(0x41c345a2), SkBits2Float(0x427329fd)); // 36.62f, 60.327f, 24.81f, 60.765f, 24.409f, 60.791f +path.cubicTo(SkBits2Float(0x41c247ae), SkBits2Float(0x42733e78), SkBits2Float(0x41c04396), SkBits2Float(0x42738e57), SkBits2Float(0x41bf4bc7), SkBits2Float(0x4273e45b)); // 24.285f, 60.811f, 24.033f, 60.889f, 23.912f, 60.973f +path.cubicTo(SkBits2Float(0x41bf5c29), SkBits2Float(0x4273e042), SkBits2Float(0x41be9db3), SkBits2Float(0x4274322e), SkBits2Float(0x41be9db3), SkBits2Float(0x4274322e)); // 23.92f, 60.969f, 23.827f, 61.049f, 23.827f, 61.049f +path.lineTo(SkBits2Float(0x41be26ea), SkBits2Float(0x42746c8c)); // 23.769f, 61.106f +path.cubicTo(SkBits2Float(0x41be1eb9), SkBits2Float(0x427470a5), SkBits2Float(0x41bde354), SkBits2Float(0x42748313), SkBits2Float(0x41bde354), SkBits2Float(0x42748313)); // 23.765f, 61.11f, 23.736f, 61.128f, 23.736f, 61.128f +path.lineTo(SkBits2Float(0x41bcc083), SkBits2Float(0x42751582)); // 23.594f, 61.271f +path.lineTo(SkBits2Float(0x41bcf3b6), SkBits2Float(0x427526ea)); // 23.619f, 61.288f +path.lineTo(SkBits2Float(0x41bd0e56), SkBits2Float(0x42756979)); // 23.632f, 61.353f +path.lineTo(SkBits2Float(0x41bd7cee), SkBits2Float(0x42758313)); // 23.686f, 61.378f +path.cubicTo(SkBits2Float(0x41be8107), SkBits2Float(0x427572b1), SkBits2Float(0x41bf2d0f), SkBits2Float(0x42754290), SkBits2Float(0x41bfd2f2), SkBits2Float(0x4275147b)); // 23.813f, 61.362f, 23.897f, 61.315f, 23.978f, 61.27f +path.lineTo(SkBits2Float(0x41c0ba5f), SkBits2Float(0x4274da1d)); // 24.091f, 61.213f +path.lineTo(SkBits2Float(0x41c0ef9e), SkBits2Float(0x4274de36)); // 24.117f, 61.217f +path.lineTo(SkBits2Float(0x41c13f7d), SkBits2Float(0x4274d3f9)); // 24.156f, 61.207f +path.cubicTo(SkBits2Float(0x41c13f7d), SkBits2Float(0x4274d3f9), SkBits2Float(0x41c174bc), SkBits2Float(0x4274c18a), SkBits2Float(0x41c17cee), SkBits2Float(0x4274be78)); // 24.156f, 61.207f, 24.182f, 61.189f, 24.186f, 61.186f +path.cubicTo(SkBits2Float(0x41c18107), SkBits2Float(0x4274bf7e), SkBits2Float(0x41c1e561), SkBits2Float(0x4274b022), SkBits2Float(0x41c1e561), SkBits2Float(0x4274b022)); // 24.188f, 61.187f, 24.237f, 61.172f, 24.237f, 61.172f +path.lineTo(SkBits2Float(0x41c45e36), SkBits2Float(0x42746e99)); // 24.546f, 61.108f +path.cubicTo(SkBits2Float(0x41c4624f), SkBits2Float(0x42746e99), SkBits2Float(0x41cf999a), SkBits2Float(0x42743853), SkBits2Float(0x41cf999a), SkBits2Float(0x42743853)); // 24.548f, 61.108f, 25.95f, 61.055f, 25.95f, 61.055f +path.lineTo(SkBits2Float(0x420d126f), SkBits2Float(0x4272b43a)); // 35.268f, 60.676f +path.cubicTo(SkBits2Float(0x420d0938), SkBits2Float(0x4272c084), SkBits2Float(0x420cfcee), SkBits2Float(0x4272c49c), SkBits2Float(0x420cfcee), SkBits2Float(0x4272d917)); // 35.259f, 60.688f, 35.247f, 60.692f, 35.247f, 60.712f +path.lineTo(SkBits2Float(0x420d0938), SkBits2Float(0x4272b43a)); // 35.259f, 60.676f +path.cubicTo(SkBits2Float(0x420c7be8), SkBits2Float(0x42737efb), SkBits2Float(0x420b3128), SkBits2Float(0x42743128), SkBits2Float(0x420a27f0), SkBits2Float(0x4274c18a)); // 35.121f, 60.874f, 34.798f, 61.048f, 34.539f, 61.189f +path.lineTo(SkBits2Float(0x42099eb9), SkBits2Float(0x42750c4b)); // 34.405f, 61.262f +path.cubicTo(SkBits2Float(0x420872b1), SkBits2Float(0x4275b022), SkBits2Float(0x4206fbe8), SkBits2Float(0x42764397), SkBits2Float(0x42054396), SkBits2Float(0x4276c084)); // 34.112f, 61.422f, 33.746f, 61.566f, 33.316f, 61.688f +path.cubicTo(SkBits2Float(0x42028313), SkBits2Float(0x42776b86), SkBits2Float(0x42007be8), SkBits2Float(0x4278de36), SkBits2Float(0x41fe7ae2), SkBits2Float(0x427b0f5d)); // 32.628f, 61.855f, 32.121f, 62.217f, 31.81f, 62.765f +path.cubicTo(SkBits2Float(0x41fe4fe0), SkBits2Float(0x427b21cc), SkBits2Float(0x41fdbe78), SkBits2Float(0x427b8419), SkBits2Float(0x41fdbe78), SkBits2Float(0x427b8419)); // 31.789f, 62.783f, 31.718f, 62.879f, 31.718f, 62.879f +path.cubicTo(SkBits2Float(0x41fdccce), SkBits2Float(0x427b71aa), SkBits2Float(0x41fd1cad), SkBits2Float(0x427c27f0), SkBits2Float(0x41fd1cad), SkBits2Float(0x427c27f0)); // 31.725f, 62.861f, 31.639f, 63.039f, 31.639f, 63.039f +path.lineTo(SkBits2Float(0x41fc1eb9), SkBits2Float(0x427d178e)); // 31.515f, 63.273f +path.lineTo(SkBits2Float(0x41fc7efb), SkBits2Float(0x427d020d)); // 31.562f, 63.252f +path.lineTo(SkBits2Float(0x41fbb647), SkBits2Float(0x427d3646)); // 31.464f, 63.303f +path.lineTo(SkBits2Float(0x41fbe76e), SkBits2Float(0x427d25e4)); // 31.488f, 63.287f +path.lineTo(SkBits2Float(0x41fae149), SkBits2Float(0x427d1fbf)); // 31.36f, 63.281f +path.lineTo(SkBits2Float(0x41fa5812), SkBits2Float(0x427d178e)); // 31.293f, 63.273f +path.cubicTo(SkBits2Float(0x41f88108), SkBits2Float(0x427cf9dc), SkBits2Float(0x41f73541), SkBits2Float(0x427cb646), SkBits2Float(0x41f5d70c), SkBits2Float(0x427c6d92)); // 31.063f, 63.244f, 30.901f, 63.178f, 30.73f, 63.107f +path.lineTo(SkBits2Float(0x41f5999b), SkBits2Float(0x427c6148)); // 30.7f, 63.095f +path.cubicTo(SkBits2Float(0x41f5999b), SkBits2Float(0x427c6148), SkBits2Float(0x41f2d0e7), SkBits2Float(0x427bdc29), SkBits2Float(0x41f2a9fd), SkBits2Float(0x427bd4fe)); // 30.7f, 63.095f, 30.352f, 62.965f, 30.333f, 62.958f +path.cubicTo(SkBits2Float(0x41f28d51), SkBits2Float(0x427bc49c), SkBits2Float(0x41f26667), SkBits2Float(0x427bb021), SkBits2Float(0x41f26667), SkBits2Float(0x427bb021)); // 30.319f, 62.942f, 30.3f, 62.922f, 30.3f, 62.922f +path.lineTo(SkBits2Float(0x41efed92), SkBits2Float(0x427b1db2)); // 29.991f, 62.779f +path.lineTo(SkBits2Float(0x41ec9582), SkBits2Float(0x427a624e)); // 29.573f, 62.596f +path.cubicTo(SkBits2Float(0x41eca1cc), SkBits2Float(0x427a645a), SkBits2Float(0x41eaf9dc), SkBits2Float(0x427a3021), SkBits2Float(0x41eaf9dc), SkBits2Float(0x427a3021)); // 29.579f, 62.598f, 29.372f, 62.547f, 29.372f, 62.547f +path.cubicTo(SkBits2Float(0x41eaf9dc), SkBits2Float(0x427a3021), SkBits2Float(0x41ea126f), SkBits2Float(0x427a1894), SkBits2Float(0x41e9f3b7), SkBits2Float(0x427a1687)); // 29.372f, 62.547f, 29.259f, 62.524f, 29.244f, 62.522f +path.cubicTo(SkBits2Float(0x41e9ccce), SkBits2Float(0x427a072b), SkBits2Float(0x41e99375), SkBits2Float(0x4279f1aa), SkBits2Float(0x41e99375), SkBits2Float(0x4279f1aa)); // 29.225f, 62.507f, 29.197f, 62.486f, 29.197f, 62.486f +path.lineTo(SkBits2Float(0x41e86e98), SkBits2Float(0x4279d604)); // 29.054f, 62.459f +path.lineTo(SkBits2Float(0x41e6147b), SkBits2Float(0x4279a3d7)); // 28.76f, 62.41f +path.cubicTo(SkBits2Float(0x41e00625), SkBits2Float(0x42796b85), SkBits2Float(0x41db49ba), SkBits2Float(0x427a7ae1), SkBits2Float(0x41d62b02), SkBits2Float(0x427bc8b4)); // 28.003f, 62.355f, 27.411f, 62.62f, 26.771f, 62.946f +path.cubicTo(SkBits2Float(0x41d24fdf), SkBits2Float(0x427cba5e), SkBits2Float(0x41cecccd), SkBits2Float(0x427ce872), SkBits2Float(0x41ca0e56), SkBits2Float(0x427c6872)); // 26.289f, 63.182f, 25.85f, 63.227f, 25.257f, 63.102f +path.cubicTo(SkBits2Float(0x41ca0a3d), SkBits2Float(0x427c676c), SkBits2Float(0x41c9353f), SkBits2Float(0x427c570a), SkBits2Float(0x41c9353f), SkBits2Float(0x427c570a)); // 25.255f, 63.101f, 25.151f, 63.085f, 25.151f, 63.085f +path.lineTo(SkBits2Float(0x41c73b64), SkBits2Float(0x427c26e9)); // 24.904f, 63.038f +path.lineTo(SkBits2Float(0x41c774bc), SkBits2Float(0x427c374b)); // 24.932f, 63.054f +path.lineTo(SkBits2Float(0x41c67ef9), SkBits2Float(0x427c0312)); // 24.812f, 63.003f +path.cubicTo(SkBits2Float(0x41c4df3b), SkBits2Float(0x427bc5a1), SkBits2Float(0x41c2a3d6), SkBits2Float(0x427b8d4f), SkBits2Float(0x41c0851e), SkBits2Float(0x427b6978)); // 24.609f, 62.943f, 24.33f, 62.888f, 24.065f, 62.853f +path.cubicTo(SkBits2Float(0x41bf1893), SkBits2Float(0x427b52f1), SkBits2Float(0x41bd2d0e), SkBits2Float(0x427b52f1), SkBits2Float(0x41bc020c), SkBits2Float(0x427b5e34)); // 23.887f, 62.831f, 23.647f, 62.831f, 23.501f, 62.842f +path.lineTo(SkBits2Float(0x41bac6a8), SkBits2Float(0x427b6871)); // 23.347f, 62.852f +path.cubicTo(SkBits2Float(0x41b9db23), SkBits2Float(0x427b72ae), SkBits2Float(0x41b87cee), SkBits2Float(0x427b820b), SkBits2Float(0x41b7fbe7), SkBits2Float(0x427b655f)); // 23.232f, 62.862f, 23.061f, 62.877f, 22.998f, 62.849f +path.cubicTo(SkBits2Float(0x41b7fbe7), SkBits2Float(0x427b5f3a), SkBits2Float(0x41b7dd2f), SkBits2Float(0x427b48b3), SkBits2Float(0x41b7dd2f), SkBits2Float(0x427b48b3)); // 22.998f, 62.843f, 22.983f, 62.821f, 22.983f, 62.821f +path.lineTo(SkBits2Float(0x41b7a5e3), SkBits2Float(0x427b22d0)); // 22.956f, 62.784f +path.cubicTo(SkBits2Float(0x41b7be76), SkBits2Float(0x427b3332), SkBits2Float(0x41b74395), SkBits2Float(0x427aed91), SkBits2Float(0x41b74395), SkBits2Float(0x427aed91)); // 22.968f, 62.8f, 22.908f, 62.732f, 22.908f, 62.732f +path.lineTo(SkBits2Float(0x41b70c49), SkBits2Float(0x427acfdf)); // 22.881f, 62.703f +path.cubicTo(SkBits2Float(0x41b70418), SkBits2Float(0x427ad916), SkBits2Float(0x41b6d70a), SkBits2Float(0x427a9168), SkBits2Float(0x41b6d70a), SkBits2Float(0x427a9168)); // 22.877f, 62.712f, 22.855f, 62.642f, 22.855f, 62.642f +path.lineTo(SkBits2Float(0x41b6bc6a), SkBits2Float(0x427a645a)); // 22.842f, 62.598f +path.lineTo(SkBits2Float(0x41b66e97), SkBits2Float(0x427a75c2)); // 22.804f, 62.615f +path.cubicTo(SkBits2Float(0x41b6872a), SkBits2Float(0x427a71a9), SkBits2Float(0x41b5a9fb), SkBits2Float(0x4279c6a7), SkBits2Float(0x41b5a9fb), SkBits2Float(0x4279c6a7)); // 22.816f, 62.611f, 22.708f, 62.444f, 22.708f, 62.444f +path.lineTo(SkBits2Float(0x41b59580), SkBits2Float(0x4279b645)); // 22.698f, 62.428f +path.lineTo(SkBits2Float(0x41b549b9), SkBits2Float(0x42799fbe)); // 22.661f, 62.406f +path.lineTo(SkBits2Float(0x41b53957), SkBits2Float(0x42799ba5)); // 22.653f, 62.402f +path.cubicTo(SkBits2Float(0x41b52b01), SkBits2Float(0x42798d4f), SkBits2Float(0x41b4a3d6), SkBits2Float(0x427920c4), SkBits2Float(0x41b4a3d6), SkBits2Float(0x427920c4)); // 22.646f, 62.388f, 22.58f, 62.282f, 22.58f, 62.282f +path.lineTo(SkBits2Float(0x41b43126), SkBits2Float(0x4278be76)); // 22.524f, 62.186f +path.lineTo(SkBits2Float(0x41b3ed90), SkBits2Float(0x4278ab01)); // 22.491f, 62.167f +path.lineTo(SkBits2Float(0x41b3be75), SkBits2Float(0x42789ba5)); // 22.468f, 62.152f +path.lineTo(SkBits2Float(0x41b3d0e4), SkBits2Float(0x4278b957)); // 22.477f, 62.181f +path.lineTo(SkBits2Float(0x41b351ea), SkBits2Float(0x42786353)); // 22.415f, 62.097f +path.lineTo(SkBits2Float(0x41b33957), SkBits2Float(0x42786353)); // 22.403f, 62.097f +path.cubicTo(SkBits2Float(0x41b326e8), SkBits2Float(0x42785a1c), SkBits2Float(0x41b2fbe6), SkBits2Float(0x427846a7), SkBits2Float(0x41b2fbe6), SkBits2Float(0x427846a7)); // 22.394f, 62.088f, 22.373f, 62.069f, 22.373f, 62.069f +path.lineTo(SkBits2Float(0x41b2353e), SkBits2Float(0x4277f8d4)); // 22.276f, 61.993f +path.cubicTo(SkBits2Float(0x41b26040), SkBits2Float(0x42780624), SkBits2Float(0x41b16e96), SkBits2Float(0x4277d0e4), SkBits2Float(0x41b16e96), SkBits2Float(0x4277d0e4)); // 22.297f, 62.006f, 22.179f, 61.954f, 22.179f, 61.954f +path.cubicTo(SkBits2Float(0x41b16e96), SkBits2Float(0x4277d0e4), SkBits2Float(0x41b10417), SkBits2Float(0x4277c188), SkBits2Float(0x41b0fffe), SkBits2Float(0x4277c188)); // 22.179f, 61.954f, 22.127f, 61.939f, 22.125f, 61.939f +path.cubicTo(SkBits2Float(0x41b0fffe), SkBits2Float(0x4277bf7c), SkBits2Float(0x41b03f7b), SkBits2Float(0x427778d4), SkBits2Float(0x41b03f7b), SkBits2Float(0x427778d4)); // 22.125f, 61.937f, 22.031f, 61.868f, 22.031f, 61.868f +path.lineTo(SkBits2Float(0x41ae8729), SkBits2Float(0x4276f7ce)); // 21.816f, 61.742f +path.cubicTo(SkBits2Float(0x41adb644), SkBits2Float(0x4276d0e5), SkBits2Float(0x41ad22cf), SkBits2Float(0x42768e55), SkBits2Float(0x41ac8729), SkBits2Float(0x427648b3)); // 21.714f, 61.704f, 21.642f, 61.639f, 21.566f, 61.571f +path.lineTo(SkBits2Float(0x41ab957f), SkBits2Float(0x4275e24d)); // 21.448f, 61.471f +path.cubicTo(SkBits2Float(0x41aa8f5a), SkBits2Float(0x42757df3), SkBits2Float(0x41a9b644), SkBits2Float(0x42751fbe), SkBits2Float(0x41a8a3d5), SkBits2Float(0x42747fff)); // 21.32f, 61.373f, 21.214f, 61.281f, 21.08f, 61.125f +path.cubicTo(SkBits2Float(0x41a6d708), SkBits2Float(0x4273a3d6), SkBits2Float(0x41a645a0), SkBits2Float(0x4272dd2e), SkBits2Float(0x41a58935), SkBits2Float(0x4271b126)); // 20.855f, 60.91f, 20.784f, 60.716f, 20.692f, 60.423f +path.lineTo(SkBits2Float(0x41a5851c), SkBits2Float(0x4271a7ef)); // 20.69f, 60.414f +path.lineTo(SkBits2Float(0x41a56a7c), SkBits2Float(0x42719687)); // 20.677f, 60.397f +path.lineTo(SkBits2Float(0x41a54dd0), SkBits2Float(0x4271820c)); // 20.663f, 60.377f +path.cubicTo(SkBits2Float(0x41a50209), SkBits2Float(0x42711062), SkBits2Float(0x41a4ced6), SkBits2Float(0x42707efa), SkBits2Float(0x41a4be74), SkBits2Float(0x426ff4bc)); // 20.626f, 60.266f, 20.601f, 60.124f, 20.593f, 59.989f +path.cubicTo(SkBits2Float(0x41a51478), SkBits2Float(0x427073b6), SkBits2Float(0x41a576c6), SkBits2Float(0x42710b43), SkBits2Float(0x41a576c6), SkBits2Float(0x42710b43)); // 20.635f, 60.113f, 20.683f, 60.261f, 20.683f, 60.261f +path.cubicTo(SkBits2Float(0x41a71478), SkBits2Float(0x42730418), SkBits2Float(0x41a9df39), SkBits2Float(0x42746666), SkBits2Float(0x41adc6a5), SkBits2Float(0x427526e9)); // 20.885f, 60.754f, 21.234f, 61.1f, 21.722f, 61.288f +path.cubicTo(SkBits2Float(0x41adc499), SkBits2Float(0x427525e3), SkBits2Float(0x41ae47ab), SkBits2Float(0x42754395), SkBits2Float(0x41ae47ab), SkBits2Float(0x42754395)); // 21.721f, 61.287f, 21.785f, 61.316f, 21.785f, 61.316f +path.lineTo(SkBits2Float(0x41afe55d), SkBits2Float(0x4275978d)); // 21.987f, 61.398f +path.cubicTo(SkBits2Float(0x41b27cea), SkBits2Float(0x4275e147), SkBits2Float(0x41b54dd0), SkBits2Float(0x4275d916), SkBits2Float(0x41b772ad), SkBits2Float(0x42758106)); // 22.311f, 61.47f, 22.663f, 61.462f, 22.931f, 61.376f +path.cubicTo(SkBits2Float(0x41b8df38), SkBits2Float(0x42753d70), SkBits2Float(0x41ba1684), SkBits2Float(0x4274d1eb), SkBits2Float(0x41bb4186), SkBits2Float(0x42746979)); // 23.109f, 61.31f, 23.261f, 61.205f, 23.407f, 61.103f +path.lineTo(SkBits2Float(0x41bdbc67), SkBits2Float(0x4273a1cb)); // 23.717f, 60.908f +path.cubicTo(SkBits2Float(0x41c0f1a6), SkBits2Float(0x4272cccd), SkBits2Float(0x41c3cabd), SkBits2Float(0x4272b958), SkBits2Float(0x41c71684), SkBits2Float(0x4272a3d7)); // 24.118f, 60.7f, 24.474f, 60.681f, 24.886f, 60.66f +path.lineTo(SkBits2Float(0x41ca4392), SkBits2Float(0x42728831)); // 25.283f, 60.633f +path.lineTo(SkBits2Float(0x41def9d8), SkBits2Float(0x42723f7d)); // 27.872f, 60.562f +path.cubicTo(SkBits2Float(0x41e15a1a), SkBits2Float(0x42722d0e), SkBits2Float(0x41e4105f), SkBits2Float(0x42723333), SkBits2Float(0x41e60e53), SkBits2Float(0x4271c7ae)); // 28.169f, 60.544f, 28.508f, 60.55f, 28.757f, 60.445f +path.cubicTo(SkBits2Float(0x41e87ceb), SkBits2Float(0x42715810), SkBits2Float(0x41e97ef7), SkBits2Float(0x427077cf), SkBits2Float(0x41ea9165), SkBits2Float(0x426f8a3d)); // 29.061f, 60.336f, 29.187f, 60.117f, 29.321f, 59.885f +path.lineTo(SkBits2Float(0x41ebccc9), SkBits2Float(0x426e8a3d)); // 29.475f, 59.635f +path.cubicTo(SkBits2Float(0x41ebced5), SkBits2Float(0x426e8937), SkBits2Float(0x41ec2d0b), SkBits2Float(0x426e4ccc), SkBits2Float(0x41ec2d0b), SkBits2Float(0x426e4ccc)); // 29.476f, 59.634f, 29.522f, 59.575f, 29.522f, 59.575f +path.lineTo(SkBits2Float(0x41ecae11), SkBits2Float(0x426dde34)); // 29.585f, 59.467f +path.lineTo(SkBits2Float(0x41ecdf38), SkBits2Float(0x426dde34)); // 29.609f, 59.467f +path.lineTo(SkBits2Float(0x41ed26e6), SkBits2Float(0x426dc082)); // 29.644f, 59.438f +path.cubicTo(SkBits2Float(0x41ee1ca9), SkBits2Float(0x426d5a1c), SkBits2Float(0x41eeccc9), SkBits2Float(0x426d1061), SkBits2Float(0x41f01684), SkBits2Float(0x426ce978)); // 29.764f, 59.338f, 29.85f, 59.266f, 30.011f, 59.228f +path.cubicTo(SkBits2Float(0x41f29fbb), SkBits2Float(0x426c8e55), SkBits2Float(0x420cced8), SkBits2Float(0x426bd4fd), SkBits2Float(0x420e6c8a), SkBits2Float(0x426bdf3b)); // 30.328f, 59.139f, 35.202f, 58.958f, 35.606f, 58.968f +path.moveTo(SkBits2Float(0x41b60622), SkBits2Float(0x427adb22)); // 22.753f, 62.714f +path.lineTo(SkBits2Float(0x41b60416), SkBits2Float(0x427ad709)); // 22.752f, 62.71f +path.cubicTo(SkBits2Float(0x41b60416), SkBits2Float(0x427ad603), SkBits2Float(0x41b60416), SkBits2Float(0x427ad915), SkBits2Float(0x41b60622), SkBits2Float(0x427adb22)); // 22.752f, 62.709f, 22.752f, 62.712f, 22.753f, 62.714f +path.moveTo(SkBits2Float(0x41bed2ef), SkBits2Float(0x4274cbc6)); // 23.853f, 61.199f +path.close(); +path.moveTo(SkBits2Float(0x41c04fdd), SkBits2Float(0x42746560)); // 24.039f, 61.099f +path.close(); +} + +static void joel_16(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + make_joel_16(path); +testSimplify(reporter, path, filename); +} + +static void joel_16x(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + make_joel_16(path); +testSimplify(reporter, path, filename); +} + +static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0; static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0; +static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0; static TestDesc tests[] = { + TEST(joel_16x), + TEST(joel_16), + TEST(joel_15x), + TEST(joel_15), + TEST(joel_14x), + TEST(joel_14), + TEST(joel_13x), + TEST(joel_13), + TEST(joel_12x), + TEST(joel_12), + TEST(joel_11), + TEST(joel_10), + TEST(joel_9), + TEST(joel_8), + TEST(joel_7), + TEST(joel_6), + TEST(joel_5), + TEST(joel_4), + TEST(joel_3), + TEST(joel_2), + TEST(joel_1), + TEST(simplifyTest_1), + TEST(carsvg_1), + TEST(tiger8_393), + TEST(bug5169), + TEST(testQuads73), + TEST(testQuads72), + TEST(testQuads71), + TEST(testQuads70), + TEST(testQuads69), + TEST(testQuads68), + TEST(testQuads67), + TEST(testQuads66), + TEST(dean4), + TEST(fuzz763_4713_b), + TEST(fuzz_twister2), + TEST(fuzz_twister), + TEST(fuzz994s_3414), + TEST(fuzz994s_11), + TEST(cr514118), + TEST(fuzz864a), + TEST(testQuads65), + TEST(testIssue3838_3), + TEST(testIssue3838), + TEST(testArc), + TEST(testTriangle2), + TEST(testTriangle1), + TEST(testQuads64), + TEST(testQuads63), + TEST(testQuads62), + TEST(testRect4), TEST(testRect3), TEST(testQuadralateral10), TEST(testQuads61), @@ -4738,7 +7791,7 @@ TEST(testQuadralateral3), TEST(testDegenerate5), TEST(testQuad12), - TEST(testQuadratic51), // has unorderable angles + TEST(testQuadratic51), TEST(testQuad8), TEST(testQuad11), TEST(testQuad10), @@ -5099,9 +8152,8 @@ static const size_t testCount = SK_ARRAY_COUNT(tests); static TestDesc subTests[] = { - TEST(testLine3), - TEST(testLine2), - TEST(testLine1), + TEST(fuzz994s_3414), + TEST(fuzz994s_11), }; static const size_t subTestCount = SK_ARRAY_COUNT(subTests); @@ -5111,14 +8163,13 @@ static bool runSubTests = false; static bool runSubTestsFirst = false; static bool runReverse = false; -static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0; DEF_TEST(PathOpsSimplify, reporter) { if (runSubTests && runSubTestsFirst) { - RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse); + RunTestSet(reporter, subTests, subTestCount, firstSubTest, nullptr, stopTest, runReverse); } - RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse); + RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse); if (runSubTests && !runSubTestsFirst) { - RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse); + RunTestSet(reporter, subTests, subTestCount, firstSubTest, nullptr, stopTest, runReverse); } }
diff --git a/src/third_party/skia/tests/PathOpsSimplifyTrianglesThreadedTest.cpp b/src/third_party/skia/tests/PathOpsSimplifyTrianglesThreadedTest.cpp old mode 100755 new mode 100644 index ee0ca2b..372b667 --- a/src/third_party/skia/tests/PathOpsSimplifyTrianglesThreadedTest.cpp +++ b/src/third_party/skia/tests/PathOpsSimplifyTrianglesThreadedTest.cpp
@@ -6,15 +6,11 @@ */ #include "PathOpsExtendedTest.h" #include "PathOpsThreadedCommon.h" +#include "SkString.h" static void testSimplifyTrianglesMain(PathOpsThreadState* data) { SkASSERT(data); PathOpsThreadState& state = *data; - char pathStr[1024]; - bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter? - if (progress) { - sk_bzero(pathStr, sizeof(pathStr)); - } state.fKey = "?"; int ax = state.fA & 0x03; int ay = state.fA >> 2; @@ -37,6 +33,7 @@ if ((ex - dx) * (fy - dy) == (ey - dy) * (fx - dx)) { continue; } + SkString pathStr; SkPath path, out; path.setFillType(SkPath::kWinding_FillType); path.moveTo(SkIntToScalar(ax), SkIntToScalar(ay)); @@ -47,26 +44,25 @@ path.lineTo(SkIntToScalar(ex), SkIntToScalar(ey)); path.lineTo(SkIntToScalar(fx), SkIntToScalar(fy)); path.close(); - if (progress) { - char* str = pathStr; - str += sprintf(str, " path.moveTo(%d, %d);\n", ax, ay); - str += sprintf(str, " path.lineTo(%d, %d);\n", bx, by); - str += sprintf(str, " path.lineTo(%d, %d);\n", cx, cy); - str += sprintf(str, " path.close();\n"); - str += sprintf(str, " path.moveTo(%d, %d);\n", dx, dy); - str += sprintf(str, " path.lineTo(%d, %d);\n", ex, ey); - str += sprintf(str, " path.lineTo(%d, %d);\n", fx, fy); - str += sprintf(str, " path.close();\n"); - outputProgress(state.fPathStr, pathStr, SkPath::kWinding_FillType); + if (state.fReporter->verbose()) { + pathStr.appendf(" path.moveTo(%d, %d);\n", ax, ay); + pathStr.appendf(" path.lineTo(%d, %d);\n", bx, by); + pathStr.appendf(" path.lineTo(%d, %d);\n", cx, cy); + pathStr.appendf(" path.close();\n"); + pathStr.appendf(" path.moveTo(%d, %d);\n", dx, dy); + pathStr.appendf(" path.lineTo(%d, %d);\n", ex, ey); + pathStr.appendf(" path.lineTo(%d, %d);\n", fx, fy); + pathStr.appendf(" path.close();\n"); + state.outputProgress(pathStr.c_str(), SkPath::kWinding_FillType); } ShowTestName(&state, d, e, f, 0); - testSimplify(path, false, out, state, pathStr); + testSimplify(path, false, out, state, pathStr.c_str()); path.setFillType(SkPath::kEvenOdd_FillType); - if (progress) { - outputProgress(state.fPathStr, pathStr, SkPath::kEvenOdd_FillType); + if (state.fReporter->verbose()) { + state.outputProgress(pathStr.c_str(), SkPath::kEvenOdd_FillType); } ShowTestName(&state, d, e, f, 1); - testSimplify(path, true, out, state, pathStr); + testSimplify(path, true, out, state, pathStr.c_str()); } } } @@ -90,8 +86,8 @@ if ((bx - ax) * (cy - ay) == (by - ay) * (cx - ax)) { continue; } - *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable, - (&testSimplifyTrianglesMain, a, b, c, 0, &testRunner)); + *testRunner.fRunnables.append() = new PathOpsThreadedRunnable( + &testSimplifyTrianglesMain, a, b, c, 0, &testRunner); } if (!reporter->allowExtendedTest()) goto finish; }
diff --git a/src/third_party/skia/tests/PathOpsSkpClipTest.cpp b/src/third_party/skia/tests/PathOpsSkpClipTest.cpp deleted file mode 100755 index b8142cd..0000000 --- a/src/third_party/skia/tests/PathOpsSkpClipTest.cpp +++ /dev/null
@@ -1,1105 +0,0 @@ -#include "CrashHandler.h" -// #include "OverwriteLine.h" -#include "Resources.h" -#include "SkBitmap.h" -#include "SkCanvas.h" -#include "SkColor.h" -#include "SkColorPriv.h" -#include "SkCommandLineFlags.h" -#include "SkDevice.h" -#include "SkForceLinking.h" -#include "SkGraphics.h" -#include "SkImageDecoder.h" -#include "SkImageEncoder.h" -#include "SkOSFile.h" -#include "SkPathOpsDebug.h" -#include "SkPicture.h" -#include "SkRTConf.h" -#include "SkTSort.h" -#include "SkStream.h" -#include "SkString.h" -#include "SkTArray.h" -#include "SkTDArray.h" -#include "SkTaskGroup.h" -#include "SkTemplates.h" -#include "SkTime.h" - -__SK_FORCE_IMAGE_DECODER_LINKING; - -/* add local exceptions here */ -/* TODO : add command flag interface */ -const struct SkipOverTest { - int directory; - const char* filename; - bool blamePathOps; -} skipOver[] = { - { 2, "http___www_groupon_sg_.skp", false}, // SkAAClip::Builder::addRun SkASSERT(fBounds.contains(x, y)); - { 6, "http___www_googleventures_com_.skp", true}, // addTCoincident SkASSERT(test->fT < 1); - { 7, "http___www_foxsports_nl_.skp", true}, // (no repro on mac) addT SkASSERT(this != other || fVerb == SkPath::kCubic_Verb) - {13, "http___www_modernqigong_com_.skp", false}, // SkAAClip::Builder::addRun SkASSERT(fBounds.contains(x, y)); - {14, "http___www_devbridge_com_.skp", true}, // checkSmallCoincidence SkASSERT(!next->fSmall || checkMultiple); - {16, "http___www_1023world_net_.skp", false}, // bitmap decode assert (corrupt skp?) - {19, "http___www_alamdi_com_.skp", true}, // cubic/quad intersection - {26, "http___www_liveencounters_net_.skp", true}, // (no repro on mac) checkSmall addT:549 (line, expects cubic) - {28, "http___www_encros_fr_.skp", false}, // SkAAClip::Builder::addRun SkASSERT(fBounds.contains(x, y)); - {37, "http___www_familysurvivalprotocol_wordpress_com_.skp", true}, // bumpSpan SkASSERT(span->fOppValue >= 0); - {39, "http___sufeinet_com_.skp", false}, // bitmap decode assert (corrupt skp?) - {41, "http___www_rano360_com_.skp", true}, // checkSmallCoincidence SkASSERT(!next->fSmall || checkMultiple); - {44, "http___www_firstunitedbank_com_.skp", true}, // addTCancel SkASSERT(oIndex > 0); - {46, "http___www_shinydemos_com_.skp", true}, // addSimpleAngle SkASSERT(index == count() - 2); - {48, "http___www_familysurvivalprotocol_com_.skp", true}, // bumpSpan SkASSERT "span->fOppValue >= 0" - {57, "http___www_lptemp_com_.skp", true}, // addTCoincident oPeek = &other->fTs[++oPeekIndex]; - {71, "http___www_1milyonkahraman_org_.skp", true}, // addTCoincident SkASSERT(test->fT < 1); - {88, "http___www_apuntesdelechuza_wordpress_com_.skp", true}, // bumpSpan SkASSERT "span->fOppValue >= 0" - {89, "http___www_mobilizedconsulting_com_.skp", true}, // addTCancel SkASSERT(oIndex > 0); - {93, "http___www_simple_living_in_suffolk_co_uk_.skp", true}, // bumpSpan SkASSERT "span->fOppValue >= 0" -}; - -size_t skipOverCount = sizeof(skipOver) / sizeof(skipOver[0]); - - -/* customize file in/out here */ -/* TODO : add command flag interface */ -#define CHROME_VERSION "1e5dfa4-4a995df" -#define SUMMARY_RUN 1 - -#ifdef SK_BUILD_FOR_WIN - #define DRIVE_SPEC "D:" - #define PATH_SLASH "\\" -#else - #define DRIVE_SPEC "" - #define PATH_SLASH "/" -#endif - -#define IN_DIR_PRE DRIVE_SPEC PATH_SLASH "skps" PATH_SLASH "slave" -#define OUT_DIR_PRE DRIVE_SPEC PATH_SLASH "skpOut" PATH_SLASH "slave" -#define OUT_DIR_SUM DRIVE_SPEC PATH_SLASH "skpOut" PATH_SLASH "summary" -#define DIR_POST PATH_SLASH "All" PATH_SLASH CHROME_VERSION - -static const char outOpDir[] = "opClip"; -static const char outOldDir[] = "oldClip"; -static const char outStatusDir[] = "statusTest"; - -static SkString get_in_path(int dirNo, const char* filename) { - SkString path; - SkASSERT(dirNo); - path.appendf("%s%d%s", IN_DIR_PRE, dirNo, DIR_POST); - if (!sk_exists(path.c_str())) { - SkDebugf("could not read %s\n", path.c_str()); - return SkString(); - } - if (filename) { - path.appendf("%s%s", PATH_SLASH, filename); - if (!sk_exists(path.c_str())) { - SkDebugf("could not read %s\n", path.c_str()); - return SkString(); - } - } - return path; -} - -static void make_recursive_dir(const SkString& path) { - if (sk_exists(path.c_str())) { - return; - } - const char* pathStr = path.c_str(); - int last = (int) path.size(); - do { - while (last > 0 && pathStr[--last] != PATH_SLASH[0]) - ; - SkASSERT(last > 0); - SkString shorter(pathStr, last); - if (sk_mkdir(shorter.c_str())) { - break; - } - } while (true); - do { - while (last < (int) path.size() && pathStr[++last] != PATH_SLASH[0]) - ; - SkString shorter(pathStr, last); - SkAssertResult(sk_mkdir(shorter.c_str())); - } while (last < (int) path.size()); -} - -static SkString get_out_path(int dirNo, const char* dirName) { - SkString path; - SkASSERT(dirNo); - SkASSERT(dirName); - path.appendf("%s%d%s%s%s", OUT_DIR_PRE, dirNo, DIR_POST, PATH_SLASH, dirName); - make_recursive_dir(path); - return path; -} - -static SkString get_sum_path(const char* dirName) { - SkString path; - SkASSERT(dirName); - path.appendf("%s%d%s%s", OUT_DIR_SUM, SUMMARY_RUN, PATH_SLASH, dirName); - SkDebugf("%s\n", path.c_str()); - make_recursive_dir(path); - return path; -} - -static SkString make_png_name(const char* filename) { - SkString pngName = SkString(filename); - pngName.remove(pngName.size() - 3, 3); - pngName.append("png"); - return pngName; -} - -//////////////////////////////////////////////////////// - -enum TestStep { - kCompareBits, - kEncodeFiles, -}; - -enum { - kMaxLength = 256, - kMaxFiles = 128, - kSmallLimit = 1000, -}; - -struct TestResult { - void init(int dirNo) { - fDirNo = dirNo; - sk_bzero(fFilename, sizeof(fFilename)); - fTestStep = kCompareBits; - fScale = 1; - } - - void init(int dirNo, const SkString& filename) { - fDirNo = dirNo; - strcpy(fFilename, filename.c_str()); - fTestStep = kCompareBits; - fScale = 1; - } - - SkString status() { - SkString outStr; - outStr.printf("%s %d %d\n", fFilename, fPixelError, fTime); - return outStr; - } - - SkString progress() { - SkString outStr; - outStr.printf("dir=%d %s ", fDirNo, fFilename); - if (fPixelError) { - outStr.appendf(" err=%d", fPixelError); - } - if (fTime) { - outStr.appendf(" time=%d", fTime); - } - if (fScale != 1) { - outStr.appendf(" scale=%d", fScale); - } - outStr.appendf("\n"); - return outStr; - - } - - void test(int dirNo, const SkString& filename) { - init(dirNo); - strcpy(fFilename, filename.c_str()); - testOne(); - } - - void testOne(); - - char fFilename[kMaxLength]; - TestStep fTestStep; - int fDirNo; - int fPixelError; - int fTime; - int fScale; -}; - -class SortByPixel : public TestResult { -public: - bool operator<(const SortByPixel& rh) const { - return fPixelError < rh.fPixelError; - } -}; - -class SortByTime : public TestResult { -public: - bool operator<(const SortByTime& rh) const { - return fTime < rh.fTime; - } -}; - -class SortByName : public TestResult { -public: - bool operator<(const SortByName& rh) const { - return strcmp(fFilename, rh.fFilename) < 0; - } -}; - -struct TestState { - void init(int dirNo) { - fResult.init(dirNo); - } - - SkTDArray<SortByPixel> fPixelWorst; - SkTDArray<SortByTime> fSlowest; - TestResult fResult; -}; - -struct TestRunner { - ~TestRunner(); - void render(); - SkTDArray<class TestRunnable*> fRunnables; -}; - -class TestRunnable : public SkRunnable { -public: - virtual void run() SK_OVERRIDE { - SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024); - (*fTestFun)(&fState); - } - - TestState fState; - void (*fTestFun)(TestState*); -}; - - -class TestRunnableDir : public TestRunnable { -public: - TestRunnableDir(void (*testFun)(TestState*), int dirNo, TestRunner* runner) { - fState.init(dirNo); - fTestFun = testFun; - } - -}; - -class TestRunnableFile : public TestRunnable { -public: - TestRunnableFile(void (*testFun)(TestState*), int dirNo, const char* name, TestRunner* runner) { - fState.init(dirNo); - strcpy(fState.fResult.fFilename, name); - fTestFun = testFun; - } -}; - -class TestRunnableEncode : public TestRunnableFile { -public: - TestRunnableEncode(void (*testFun)(TestState*), int dirNo, const char* name, TestRunner* runner) - : TestRunnableFile(testFun, dirNo, name, runner) { - fState.fResult.fTestStep = kEncodeFiles; - } -}; - -TestRunner::~TestRunner() { - for (int index = 0; index < fRunnables.count(); index++) { - SkDELETE(fRunnables[index]); - } -} - -void TestRunner::render() { - SkTaskGroup tg; - for (int index = 0; index < fRunnables.count(); ++ index) { - tg.add(fRunnables[index]); - } -} - -//////////////////////////////////////////////// - - -static int similarBits(const SkBitmap& gr, const SkBitmap& sk) { - const int kRowCount = 3; - const int kThreshold = 3; - int width = SkTMin(gr.width(), sk.width()); - if (width < kRowCount) { - return true; - } - int height = SkTMin(gr.height(), sk.height()); - if (height < kRowCount) { - return true; - } - int errorTotal = 0; - SkTArray<int, true> errorRows; - errorRows.push_back_n(width * kRowCount); - SkAutoLockPixels autoGr(gr); - SkAutoLockPixels autoSk(sk); - for (int y = 0; y < height; ++y) { - SkPMColor* grRow = gr.getAddr32(0, y); - SkPMColor* skRow = sk.getAddr32(0, y); - int* base = &errorRows[0]; - int* cOut = &errorRows[y % kRowCount]; - for (int x = 0; x < width; ++x) { - SkPMColor grColor = grRow[x]; - SkPMColor skColor = skRow[x]; - int dr = SkGetPackedR32(grColor) - SkGetPackedR32(skColor); - int dg = SkGetPackedG32(grColor) - SkGetPackedG32(skColor); - int db = SkGetPackedB32(grColor) - SkGetPackedB32(skColor); - int error = cOut[x] = SkTMax(SkAbs32(dr), SkTMax(SkAbs32(dg), SkAbs32(db))); - if (error < kThreshold || x < 2) { - continue; - } - if (base[x - 2] < kThreshold - || base[width + x - 2] < kThreshold - || base[width * 2 + x - 2] < kThreshold - || base[x - 1] < kThreshold - || base[width + x - 1] < kThreshold - || base[width * 2 + x - 1] < kThreshold - || base[x] < kThreshold - || base[width + x] < kThreshold - || base[width * 2 + x] < kThreshold) { - continue; - } - errorTotal += error; - } - } - return errorTotal; -} - -static bool addError(TestState* data, const TestResult& testResult) { - if (testResult.fPixelError <= 0 && testResult.fTime <= 0) { - return false; - } - int worstCount = data->fPixelWorst.count(); - int pixelError = testResult.fPixelError; - if (pixelError > 0) { - for (int index = 0; index < worstCount; ++index) { - if (pixelError > data->fPixelWorst[index].fPixelError) { - data->fPixelWorst[index] = *(SortByPixel*) &testResult; - return true; - } - } - } - int slowCount = data->fSlowest.count(); - int time = testResult.fTime; - if (time > 0) { - for (int index = 0; index < slowCount; ++index) { - if (time > data->fSlowest[index].fTime) { - data->fSlowest[index] = *(SortByTime*) &testResult; - return true; - } - } - } - if (pixelError > 0 && worstCount < kMaxFiles) { - *data->fPixelWorst.append() = *(SortByPixel*) &testResult; - return true; - } - if (time > 0 && slowCount < kMaxFiles) { - *data->fSlowest.append() = *(SortByTime*) &testResult; - return true; - } - return false; -} - -static SkMSec timePict(SkPicture* pic, SkCanvas* canvas) { - canvas->save(); - SkScalar pWidth = pic->cullRect().width(); - SkScalar pHeight = pic->cullRect().height(); - const SkScalar maxDimension = 1000.0f; - const int slices = 3; - SkScalar xInterval = SkTMax(pWidth - maxDimension, 0.0f) / (slices - 1); - SkScalar yInterval = SkTMax(pHeight - maxDimension, 0.0f) / (slices - 1); - SkRect rect = {0, 0, SkTMin(maxDimension, pWidth), SkTMin(maxDimension, pHeight) }; - canvas->clipRect(rect); - SkMSec start = SkTime::GetMSecs(); - for (int x = 0; x < slices; ++x) { - for (int y = 0; y < slices; ++y) { - pic->playback(canvas); - canvas->translate(0, yInterval); - } - canvas->translate(xInterval, -yInterval * slices); - } - SkMSec end = SkTime::GetMSecs(); - canvas->restore(); - return end - start; -} - -static void drawPict(SkPicture* pic, SkCanvas* canvas, int scale) { - canvas->clear(SK_ColorWHITE); - if (scale != 1) { - canvas->save(); - canvas->scale(1.0f / scale, 1.0f / scale); - } - pic->playback(canvas); - if (scale != 1) { - canvas->restore(); - } -} - -static void writePict(const SkBitmap& bitmap, const char* outDir, const char* pngName) { - SkString outFile = get_sum_path(outDir); - outFile.appendf("%s%s", PATH_SLASH, pngName); - if (!SkImageEncoder::EncodeFile(outFile.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100)) { - SkDebugf("unable to encode gr %s (width=%d height=%d)\n", pngName, - bitmap.width(), bitmap.height()); - } -} - -void TestResult::testOne() { - SkPicture* pic = NULL; - { - #if DEBUG_SHOW_TEST_NAME - if (fTestStep == kCompareBits) { - SkString testName(fFilename); - const char http[] = "http"; - if (testName.startsWith(http)) { - testName.remove(0, sizeof(http) - 1); - } - while (testName.startsWith("_")) { - testName.remove(0, 1); - } - const char dotSkp[] = ".skp"; - if (testName.endsWith(dotSkp)) { - size_t len = testName.size(); - testName.remove(len - (sizeof(dotSkp) - 1), sizeof(dotSkp) - 1); - } - testName.prepend("skp"); - testName.append("1"); - strncpy(DEBUG_FILENAME_STRING, testName.c_str(), DEBUG_FILENAME_STRING_LENGTH); - } else if (fTestStep == kEncodeFiles) { - strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH); - } - #endif - SkString path = get_in_path(fDirNo, fFilename); - SkFILEStream stream(path.c_str()); - if (!stream.isValid()) { - SkDebugf("invalid stream %s\n", path.c_str()); - goto finish; - } - pic = SkPicture::CreateFromStream(&stream, &SkImageDecoder::DecodeMemory); - if (!pic) { - SkDebugf("unable to decode %s\n", fFilename); - goto finish; - } - SkScalar width = pic->cullRect().width(); - SkScalar height = pic->cullRect().height(); - SkBitmap oldBitmap, opBitmap; - fScale = 1; - while (width / fScale > 32767 || height / fScale > 32767) { - ++fScale; - } - do { - int dimX = SkScalarCeilToInt(width / fScale); - int dimY = SkScalarCeilToInt(height / fScale); - if (oldBitmap.tryAllocN32Pixels(dimX, dimY) && opBitmap.tryAllocN32Pixels(dimX, dimY)) { - break; - } - SkDebugf("-%d-", fScale); - } while (++fScale < 256); - if (fScale >= 256) { - SkDebugf("unable to allocate bitmap for %s (w=%f h=%f)\n", fFilename, - width, height); - goto finish; - } - oldBitmap.eraseColor(SK_ColorWHITE); - SkCanvas oldCanvas(oldBitmap); - oldCanvas.setAllowSimplifyClip(false); - opBitmap.eraseColor(SK_ColorWHITE); - SkCanvas opCanvas(opBitmap); - opCanvas.setAllowSimplifyClip(true); - drawPict(pic, &oldCanvas, fScale); - drawPict(pic, &opCanvas, fScale); - if (fTestStep == kCompareBits) { - fPixelError = similarBits(oldBitmap, opBitmap); - int oldTime = timePict(pic, &oldCanvas); - int opTime = timePict(pic, &opCanvas); - fTime = SkTMax(0, oldTime - opTime); - } else if (fTestStep == kEncodeFiles) { - SkString pngStr = make_png_name(fFilename); - const char* pngName = pngStr.c_str(); - writePict(oldBitmap, outOldDir, pngName); - writePict(opBitmap, outOpDir, pngName); - } - } -finish: - if (pic) { - pic->unref(); - } -} - -DEFINE_string2(match, m, "PathOpsSkpClipThreaded", - "[~][^]substring[$] [...] of test name to run.\n" - "Multiple matches may be separated by spaces.\n" - "~ causes a matching test to always be skipped\n" - "^ requires the start of the test to match\n" - "$ requires the end of the test to match\n" - "^ and $ requires an exact match\n" - "If a test does not match any list entry,\n" - "it is skipped unless some list entry starts with ~"); -DEFINE_string2(dir, d, NULL, "range of directories (e.g., 1-100)"); -DEFINE_string2(skp, s, NULL, "skp to test"); -DEFINE_bool2(single, z, false, "run tests on a single thread internally."); -DEFINE_int32(testIndex, 0, "override local test index (PathOpsSkpClipOneOff only)."); -DEFINE_bool2(verbose, v, false, "enable verbose output."); - -static bool verbose() { - return FLAGS_verbose; -} - -class Dirs { -public: - Dirs() { - reset(); - sk_bzero(fRun, sizeof(fRun)); - fSet = false; - } - - int first() const { - int index = 0; - while (++index < kMaxDir) { - if (fRun[index]) { - return index; - } - } - SkASSERT(0); - return -1; - } - - int last() const { - int index = kMaxDir; - while (--index > 0 && !fRun[index]) - ; - return index; - } - - int next() { - while (++fIndex < kMaxDir) { - if (fRun[fIndex]) { - return fIndex; - } - } - return -1; - } - - void reset() { - fIndex = -1; - } - - void set(int start, int end) { - while (start < end) { - fRun[start++] = 1; - } - fSet = true; - } - - void setDefault() { - if (!fSet) { - set(1, 100); - } - } - -private: - enum { - kMaxDir = 101 - }; - char fRun[kMaxDir]; - int fIndex; - bool fSet; -} gDirs; - -class Filenames { -public: - Filenames() - : fIndex(-1) { - } - - const char* next() { - while (fNames && ++fIndex < fNames->count()) { - return (*fNames)[fIndex]; - } - return NULL; - } - - void set(const SkCommandLineFlags::StringArray& names) { - fNames = &names; - } - -private: - int fIndex; - const SkCommandLineFlags::StringArray* fNames; -} gNames; - -static bool buildTestDir(int dirNo, int firstDirNo, - SkTDArray<TestResult>* tests, SkTDArray<SortByName*>* sorted) { - SkString dirName = get_out_path(dirNo, outStatusDir); - if (!dirName.size()) { - return false; - } - SkOSFile::Iter iter(dirName.c_str(), "skp"); - SkString filename; - while (iter.next(&filename)) { - TestResult test; - test.init(dirNo); - SkString spaceFile(filename); - char* spaces = spaceFile.writable_str(); - int spaceSize = (int) spaceFile.size(); - for (int index = 0; index < spaceSize; ++index) { - if (spaces[index] == '.') { - spaces[index] = ' '; - } - } - int success = sscanf(spaces, "%s %d %d skp", test.fFilename, - &test.fPixelError, &test.fTime); - if (success < 3) { - SkDebugf("failed to scan %s matched=%d\n", filename.c_str(), success); - return false; - } - *tests[dirNo - firstDirNo].append() = test; - } - if (!sorted) { - return true; - } - SkTDArray<TestResult>& testSet = tests[dirNo - firstDirNo]; - int count = testSet.count(); - for (int index = 0; index < count; ++index) { - *sorted[dirNo - firstDirNo].append() = (SortByName*) &testSet[index]; - } - if (sorted[dirNo - firstDirNo].count()) { - SkTQSort<SortByName>(sorted[dirNo - firstDirNo].begin(), - sorted[dirNo - firstDirNo].end() - 1); - if (verbose()) { - SkDebugf("+"); - } - } - return true; -} - -static void testSkpClip(TestState* data) { - data->fResult.testOne(); - SkString statName(data->fResult.fFilename); - SkASSERT(statName.endsWith(".skp")); - statName.remove(statName.size() - 4, 4); - statName.appendf(".%d.%d.skp", data->fResult.fPixelError, data->fResult.fTime); - SkString statusFile = get_out_path(data->fResult.fDirNo, outStatusDir); - if (!statusFile.size()) { - SkDebugf("failed to create %s", statusFile.c_str()); - return; - } - statusFile.appendf("%s%s", PATH_SLASH, statName.c_str()); - SkFILE* file = sk_fopen(statusFile.c_str(), kWrite_SkFILE_Flag); - if (!file) { - SkDebugf("failed to create %s", statusFile.c_str()); - return; - } - sk_fclose(file); - if (verbose()) { - if (data->fResult.fPixelError || data->fResult.fTime) { - SkDebugf("%s", data->fResult.progress().c_str()); - } else { - SkDebugf("."); - } - } -} - -bool Less(const SortByName& a, const SortByName& b); -bool Less(const SortByName& a, const SortByName& b) { - return a < b; -} - -static bool doOneDir(TestState* state, bool threaded) { - int dirNo = state->fResult.fDirNo; - SkString dirName = get_in_path(dirNo, NULL); - if (!dirName.size()) { - return false; - } - SkTDArray<TestResult> tests[1]; - SkTDArray<SortByName*> sorted[1]; - if (!buildTestDir(dirNo, dirNo, tests, sorted)) { - return false; - } - SkOSFile::Iter iter(dirName.c_str(), "skp"); - SkString filename; - while (iter.next(&filename)) { - for (size_t index = 0; index < skipOverCount; ++index) { - if (skipOver[index].directory == dirNo - && strcmp(filename.c_str(), skipOver[index].filename) == 0) { - goto checkEarlyExit; - } - } - { - SortByName name; - name.init(dirNo); - strncpy(name.fFilename, filename.c_str(), filename.size() - 4); // drop .skp - int count = sorted[0].count(); - int idx = SkTSearch<SortByName, Less>(sorted[0].begin(), count, &name, sizeof(&name)); - if (idx >= 0) { - SortByName* found = sorted[0][idx]; - (void) addError(state, *found); - continue; - } - TestResult test; - test.init(dirNo, filename); - state->fResult = test; - testSkpClip(state); -#if 0 // artificially limit to a few while debugging code - static int debugLimit = 0; - if (++debugLimit == 5) { - return true; - } -#endif - } -checkEarlyExit: - ; - } - return true; -} - -static void initTest() { -#if !defined SK_BUILD_FOR_WIN && !defined SK_BUILD_FOR_MAC - SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); - SK_CONF_SET("images.png.suppressDecoderWarnings", true); -#endif -} - -static void testSkpClipEncode(TestState* data) { - data->fResult.testOne(); - if (verbose()) { - SkDebugf("+"); - } -} - -static void encodeFound(TestState& state) { - if (verbose()) { - if (state.fPixelWorst.count()) { - SkTDArray<SortByPixel*> worst; - for (int index = 0; index < state.fPixelWorst.count(); ++index) { - *worst.append() = &state.fPixelWorst[index]; - } - SkTQSort<SortByPixel>(worst.begin(), worst.end() - 1); - for (int index = 0; index < state.fPixelWorst.count(); ++index) { - const TestResult& result = *worst[index]; - SkDebugf("%d %s pixelError=%d\n", result.fDirNo, result.fFilename, result.fPixelError); - } - } - if (state.fSlowest.count()) { - SkTDArray<SortByTime*> slowest; - for (int index = 0; index < state.fSlowest.count(); ++index) { - *slowest.append() = &state.fSlowest[index]; - } - if (slowest.count() > 0) { - SkTQSort<SortByTime>(slowest.begin(), slowest.end() - 1); - for (int index = 0; index < slowest.count(); ++index) { - const TestResult& result = *slowest[index]; - SkDebugf("%d %s time=%d\n", result.fDirNo, result.fFilename, result.fTime); - } - } - } - } - TestRunner testRunner; - for (int index = 0; index < state.fPixelWorst.count(); ++index) { - const TestResult& result = state.fPixelWorst[index]; - SkString filename(result.fFilename); - if (!filename.endsWith(".skp")) { - filename.append(".skp"); - } - *testRunner.fRunnables.append() = SkNEW_ARGS(TestRunnableEncode, - (&testSkpClipEncode, result.fDirNo, filename.c_str(), &testRunner)); - } - testRunner.render(); -} - -class Test { -public: - Test() {} - virtual ~Test() {} - - const char* getName() { onGetName(&fName); return fName.c_str(); } - void run() { onRun(); } - -protected: - virtual void onGetName(SkString*) = 0; - virtual void onRun() = 0; - -private: - SkString fName; -}; - -typedef SkTRegistry<Test*(*)(void*)> TestRegistry; - -#define DEF_TEST(name) \ - static void test_##name(); \ - class name##Class : public Test { \ - public: \ - static Test* Factory(void*) { return SkNEW(name##Class); } \ - protected: \ - virtual void onGetName(SkString* name) SK_OVERRIDE { \ - name->set(#name); \ - } \ - virtual void onRun() SK_OVERRIDE { test_##name(); } \ - }; \ - static TestRegistry gReg_##name##Class(name##Class::Factory); \ - static void test_##name() - -DEF_TEST(PathOpsSkpClip) { - gDirs.setDefault(); - initTest(); - SkTArray<TestResult, true> errors; - TestState state; - state.init(0); - int dirNo; - gDirs.reset(); - while ((dirNo = gDirs.next()) > 0) { - if (verbose()) { - SkDebugf("dirNo=%d\n", dirNo); - } - state.fResult.fDirNo = dirNo; - if (!doOneDir(&state, false)) { - break; - } - } - encodeFound(state); -} - -static void testSkpClipMain(TestState* data) { - (void) doOneDir(data, true); -} - -DEF_TEST(PathOpsSkpClipThreaded) { - gDirs.setDefault(); - initTest(); - TestRunner testRunner; - int dirNo; - gDirs.reset(); - while ((dirNo = gDirs.next()) > 0) { - *testRunner.fRunnables.append() = SkNEW_ARGS(TestRunnableDir, - (&testSkpClipMain, dirNo, &testRunner)); - } - testRunner.render(); - TestState state; - state.init(0); - gDirs.reset(); - while ((dirNo = gDirs.next()) > 0) { - TestState& testState = testRunner.fRunnables[dirNo - 1]->fState; - SkASSERT(testState.fResult.fDirNo == dirNo); - for (int inner = 0; inner < testState.fPixelWorst.count(); ++inner) { - addError(&state, testState.fPixelWorst[inner]); - } - for (int inner = 0; inner < testState.fSlowest.count(); ++inner) { - addError(&state, testState.fSlowest[inner]); - } - } - encodeFound(state); -} - -static bool buildTests(SkTDArray<TestResult>* tests, SkTDArray<SortByName*>* sorted) { - int firstDirNo = gDirs.first(); - int dirNo; - while ((dirNo = gDirs.next()) > 0) { - if (!buildTestDir(dirNo, firstDirNo, tests, sorted)) { - return false; - } - } - return true; -} - -DEF_TEST(PathOpsSkpClipUberThreaded) { - gDirs.setDefault(); - const int firstDirNo = gDirs.next(); - const int lastDirNo = gDirs.last(); - initTest(); - int dirCount = lastDirNo - firstDirNo + 1; - SkAutoTDeleteArray<SkTDArray<TestResult> > tests(new SkTDArray<TestResult>[dirCount]); - SkAutoTDeleteArray<SkTDArray<SortByName*> > sorted(new SkTDArray<SortByName*>[dirCount]); - if (!buildTests(tests.get(), sorted.get())) { - return; - } - TestRunner testRunner; - int dirNo; - gDirs.reset(); - while ((dirNo = gDirs.next()) > 0) { - SkString dirName = get_in_path(dirNo, NULL); - if (!dirName.size()) { - continue; - } - SkOSFile::Iter iter(dirName.c_str(), "skp"); - SkString filename; - while (iter.next(&filename)) { - for (size_t index = 0; index < skipOverCount; ++index) { - if (skipOver[index].directory == dirNo - && strcmp(filename.c_str(), skipOver[index].filename) == 0) { - goto checkEarlyExit; - } - } - { - SortByName name; - name.init(dirNo); - strncpy(name.fFilename, filename.c_str(), filename.size() - 4); // drop .skp - int count = sorted.get()[dirNo - firstDirNo].count(); - if (SkTSearch<SortByName, Less>(sorted.get()[dirNo - firstDirNo].begin(), - count, &name, sizeof(&name)) < 0) { - *testRunner.fRunnables.append() = SkNEW_ARGS(TestRunnableFile, - (&testSkpClip, dirNo, filename.c_str(), &testRunner)); - } - } - checkEarlyExit: - ; - } - - } - testRunner.render(); - SkAutoTDeleteArray<SkTDArray<TestResult> > results(new SkTDArray<TestResult>[dirCount]); - if (!buildTests(results.get(), NULL)) { - return; - } - SkTDArray<TestResult> allResults; - for (int dirNo = firstDirNo; dirNo <= lastDirNo; ++dirNo) { - SkTDArray<TestResult>& array = results.get()[dirNo - firstDirNo]; - allResults.append(array.count(), array.begin()); - } - int allCount = allResults.count(); - SkTDArray<SortByPixel*> pixels; - SkTDArray<SortByTime*> times; - for (int index = 0; index < allCount; ++index) { - *pixels.append() = (SortByPixel*) &allResults[index]; - *times.append() = (SortByTime*) &allResults[index]; - } - TestState state; - if (pixels.count()) { - SkTQSort<SortByPixel>(pixels.begin(), pixels.end() - 1); - for (int inner = 0; inner < kMaxFiles; ++inner) { - *state.fPixelWorst.append() = *pixels[allCount - inner - 1]; - } - } - if (times.count()) { - SkTQSort<SortByTime>(times.begin(), times.end() - 1); - for (int inner = 0; inner < kMaxFiles; ++inner) { - *state.fSlowest.append() = *times[allCount - inner - 1]; - } - } - encodeFound(state); -} - -DEF_TEST(PathOpsSkpClipOneOff) { - const int testIndex = FLAGS_testIndex; - int dirNo = gDirs.next(); - if (dirNo < 0) { - dirNo = skipOver[testIndex].directory; - } - const char* skp = gNames.next(); - if (!skp) { - skp = skipOver[testIndex].filename; - } - initTest(); - SkAssertResult(get_in_path(dirNo, skp).size()); - SkString filename(skp); - TestResult state; - state.test(dirNo, filename); - if (verbose()) { - SkDebugf("%s", state.status().c_str()); - } - state.fTestStep = kEncodeFiles; - state.testOne(); -} - -DEF_TEST(PathOpsTestSkipped) { - for (size_t index = 0; index < skipOverCount; ++index) { - const SkipOverTest& skip = skipOver[index]; - if (!skip.blamePathOps) { - continue; - } - int dirNo = skip.directory; - const char* skp = skip.filename; - initTest(); - SkAssertResult(get_in_path(dirNo, skp).size()); - SkString filename(skp); - TestResult state; - state.test(dirNo, filename); - if (verbose()) { - SkDebugf("%s", state.status().c_str()); - } - state.fTestStep = kEncodeFiles; - state.testOne(); - } -} - -DEF_TEST(PathOpsCopyFails) { - FLAGS_verbose = true; - for (size_t index = 0; index < skipOverCount; ++index) { - int dirNo = skipOver[index].directory; - SkDebugf("mkdir -p " IN_DIR_PRE "%d" DIR_POST "\n", dirNo); - } - for (size_t index = 0; index < skipOverCount; ++index) { - int dirNo = skipOver[index].directory; - const char* filename = skipOver[index].filename; - SkDebugf("rsync -av cary-linux.cnc:/tera" PATH_SLASH "skps" PATH_SLASH "slave" - "%d" DIR_POST "/%s " IN_DIR_PRE "%d" DIR_POST "\n", dirNo, filename, dirNo); - } -} - -template TestRegistry* TestRegistry::gHead; - -class Iter { -public: - Iter() { this->reset(); } - void reset() { fReg = TestRegistry::Head(); } - - Test* next() { - if (fReg) { - TestRegistry::Factory fact = fReg->factory(); - fReg = fReg->next(); - Test* test = fact(NULL); - return test; - } - return NULL; - } - -private: - const TestRegistry* fReg; -}; - -int tool_main(int argc, char** argv); -int tool_main(int argc, char** argv) { - SetupCrashHandler(); - SkCommandLineFlags::SetUsage(""); - SkCommandLineFlags::Parse(argc, argv); - SkGraphics::Init(); - SkString header("PathOps SkpClip:"); - if (!FLAGS_match.isEmpty()) { - header.appendf(" --match"); - for (int index = 0; index < FLAGS_match.count(); ++index) { - header.appendf(" %s", FLAGS_match[index]); - } - } - if (!FLAGS_dir.isEmpty()) { - int count = FLAGS_dir.count(); - for (int i = 0; i < count; ++i) { - const char* range = FLAGS_dir[i]; - const char* dash = strchr(range, '-'); - if (!dash) { - dash = strchr(range, ','); - } - int first = atoi(range); - int last = dash ? atoi(dash + 1) : first; - if (!first || !last) { - SkDebugf("couldn't parse --dir %s\n", range); - return 1; - } - gDirs.set(first, last); - } - } - if (!FLAGS_skp.isEmpty()) { - gNames.set(FLAGS_skp); - } -#ifdef SK_DEBUG - header.append(" SK_DEBUG"); -#else - header.append(" SK_RELEASE"); -#endif - header.appendf(" skia_arch_width=%d", (int)sizeof(void*) * 8); - if (FLAGS_verbose) { - header.appendf("\n"); - } - SkDebugf(header.c_str()); - Iter iter; - Test* test; - while ((test = iter.next()) != NULL) { - SkAutoTDelete<Test> owned(test); - if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, test->getName())) { - test->run(); - } - } - SkGraphics::Term(); - return 0; -} - -#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) -int main(int argc, char * const argv[]) { - return tool_main(argc, (char**) argv); -} -#endif
diff --git a/src/third_party/skia/tests/PathOpsSkpTest.cpp b/src/third_party/skia/tests/PathOpsSkpTest.cpp old mode 100755 new mode 100644 index 32ddf61..5e17422 --- a/src/third_party/skia/tests/PathOpsSkpTest.cpp +++ b/src/third_party/skia/tests/PathOpsSkpTest.cpp
@@ -8,8 +8,6 @@ #define TEST(name) { name, #name } -#define TEST_NEW_FAILURES 0 - static void skpcheeseandburger_com225(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -368,7 +366,7 @@ pathB.lineTo(716.868225f, 365.046783f); pathB.cubicTo(716.868225f, 363.740021f, 716.960083f, 363.043213f, 717.597961f, 362); pathB.cubicTo(715.331848f, 363.104095f, 714.19873f, 363.657166f, 711.928711f, 364.782227f); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpeverytechpro_blogspot_com100(skiatest::Reporter* reporter, const char* filename) { @@ -402,7 +400,7 @@ pathB.lineTo(1075, 628); pathB.lineTo(1116.5f, 644.5f); pathB.lineTo(1134, 627); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpflite_com41(skiatest::Reporter* reporter, const char* filename) { @@ -425,7 +423,7 @@ pathB.lineTo(304.510101f, 438.724121f); pathB.lineTo(295.849854f, 433.724121f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpilkoora_com37(skiatest::Reporter* reporter, const char* filename) { @@ -457,7 +455,7 @@ pathB.lineTo(1001.5f, 325.5f); pathB.lineTo(1001.5f, 782.5f); pathB.lineTo(1185, 966); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpmm4everfriends_com43(skiatest::Reporter* reporter, const char* filename) { @@ -480,7 +478,7 @@ pathB.lineTo(576.435852f, 247.626068f); pathB.lineTo(535.280823f, 235.165573f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpmtrk_uz27(skiatest::Reporter* reporter, const char* filename) { @@ -507,7 +505,7 @@ pathB.quadTo(41.7867432f, 802, 37.3919678f, 797.608032f); pathB.quadTo(33, 793.213196f, 33, 787); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpfrauen_magazin_com83(skiatest::Reporter* reporter, const char* filename) { @@ -531,7 +529,7 @@ pathB.lineTo(803, 891); pathB.cubicTo(803, 888.238586f, 805.238586f, 886, 808, 886); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpi_gino_com16(skiatest::Reporter* reporter, const char* filename) { @@ -556,7 +554,7 @@ pathB.cubicTo(61, 789.06897f, 116.068977f, 734, 184, 734); pathB.lineTo(185, 734); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skppchappy_com_au102(skiatest::Reporter* reporter, const char* filename) { @@ -584,7 +582,7 @@ pathB.lineTo(359, 496); pathB.cubicTo(359, 494.895416f, 360.34314f, 494, 362, 494); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpsciality_com161(skiatest::Reporter* reporter, const char* filename) { @@ -612,7 +610,7 @@ pathB.lineTo(652, 731); pathB.cubicTo(652, 729.895447f, 653.34314f, 729, 655, 729); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpsudoestenegocios_com186(skiatest::Reporter* reporter, const char* filename) { @@ -642,7 +640,7 @@ pathB.lineTo(24, 471); pathB.lineTo(24, 317); pathB.lineTo(48, 293); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpthesuburbanite_com213(skiatest::Reporter* reporter, const char* filename) { @@ -663,7 +661,7 @@ pathB.lineTo(866.016724f, 701.620361f); pathB.lineTo(785.84491f, 723.102356f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skphostloco_com11(skiatest::Reporter* reporter, const char* filename) { @@ -686,7 +684,7 @@ pathB.lineTo(30, 648); pathB.lineTo(0, 648); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpsergeychunkevich_com8(skiatest::Reporter* reporter, const char* filename) { @@ -708,7 +706,7 @@ pathB.lineTo(34, 371); pathB.cubicTo(35.6568565f, 371, 37, 372.34314f, 37, 374); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skptracksflow_com9(skiatest::Reporter* reporter, const char* filename) { @@ -742,7 +740,7 @@ pathB.cubicTo(26.0091248f, 64.2129364f, 24.2174377f, 66.0046234f, 22.0072803f, 66.0046234f); pathB.cubicTo(19.7970943f, 66.0045929f, 18.0054054f, 64.2129059f, 18.0054054f, 62.0027809f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpautobutler_dk29(skiatest::Reporter* reporter, const char* filename) { @@ -761,7 +759,7 @@ pathB.lineTo(8.57224448e-15f, 301); pathB.lineTo(6.12303177e-17f, 162); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skponlinecollege_org144(skiatest::Reporter* reporter, const char* filename) { @@ -789,7 +787,7 @@ pathB.lineTo(177, 410); pathB.cubicTo(177, 408.895416f, 177.895432f, 408, 179, 408); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpnational_com_au81(skiatest::Reporter* reporter, const char* filename) { @@ -815,7 +813,7 @@ pathB.lineTo(806, 818); pathB.cubicTo(806, 817.447693f, 806.447693f, 817, 807, 817); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skprentacheat_com30(skiatest::Reporter* reporter, const char* filename) { @@ -841,7 +839,7 @@ pathB.lineTo(966, 264); pathB.cubicTo(966, 263.447723f, 966.447693f, 263, 967, 263); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpbreakmystyle_com10(skiatest::Reporter* reporter, const char* filename) { @@ -870,7 +868,7 @@ pathB.quadTo(231.516815f, -40, 279.258392f, 7.74160004f); pathB.quadTo(327, 55.4831848f, 327, 123); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpsd_graphic_net104(skiatest::Reporter* reporter, const char* filename) { @@ -895,22 +893,9 @@ pathB.lineTo(390.578583f, 867.014099f); pathB.lineTo(433, 852.000061f); pathB.lineTo(490.435486f, 879.40741f); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -/* this cubic/quad pair - c = 430,280 430,278.895416 473.876068,278 528,278 - q = 430,280 430.009796,277.101196 458.703552,275.050262 - only intersect at the shared point (430,280) - they sort backwards because the tangent from pt[0] to control pt[1] - c' = (0.00000000000000000, -1.1045837402343750) - q' = (0.0097961425781250000, -2.8988037109375000) - suggests that the quad is counterclockwise of the cubic, when the reverse is true - the angle code is fooled because the control pt[1] of both the quad and cubic - is far away from cubic cntl [2] and quad pt [2]. - Maybe in angle setup, this instability can be detected to suppress sorting on the initial tangent - Or the error term can be passed to NearRay that is magnified by the distance from the next ctrl? - */ static void skpnaoxrane_ru23(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -940,14 +925,9 @@ pathB.lineTo(430, 280); pathB.cubicTo(430, 278.895416f, 473.876068f, 278, 528, 278); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -/* didn't investigate thoroughly, but looks to be missorting quad and cubic - {{468.507751,560.724426}, {467.275146,552.856262}, {465.84668,547.288391}} - {{463.779907,542.671143}, {464.829529,542.672974}, {466.946289,550.755676}, {468.507751,560.724426}} - decision maker is case 14 leftLessThanRight - */ static void skptcmevents_org23(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -973,7 +953,7 @@ pathB.lineTo(325.968597f, 560.475708f); pathB.cubicTo(324.407104f, 550.506958f, 341.01001f, 542.456909f, 363.052246f, 542.495361f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpredbullskatearcade_es16(skiatest::Reporter* reporter, const char* filename) { @@ -1004,7 +984,7 @@ pathB.lineTo(652.258179f, 468.503662f); pathB.cubicTo(652.520996f, 463.401611f, 656.829834f, 459.128235f, 661.882263f, 458.958862f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpfinanzasdigital_com9(skiatest::Reporter* reporter, const char* filename) { @@ -1030,7 +1010,7 @@ pathB.lineTo(153, 130); pathB.cubicTo(153, 127.790863f, 154.34314f, 126, 156, 126); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skppartainasdemo250_org56(skiatest::Reporter* reporter, const char* filename) { @@ -1053,7 +1033,7 @@ pathB.lineTo(206.748749f, 634.748718f); pathB.lineTo(182.000015f, 610); pathB.lineTo(132.502533f, 610); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpmlk_com326(skiatest::Reporter* reporter, const char* filename) { @@ -1081,7 +1061,7 @@ pathB.lineTo(149, 675); pathB.cubicTo(149, 672.790833f, 151.238571f, 671, 154, 671); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpcyclist_friends_gr52(skiatest::Reporter* reporter, const char* filename) { @@ -1107,10 +1087,9 @@ pathB.cubicTo(52.238575f, 207, 50, 204.761429f, 50, 202); pathB.lineTo(50, 183); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -/* cubic ends just above opp line */ static void skpwww_fj_p_com_22(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1127,10 +1106,9 @@ pathB.lineTo(161, 199); pathB.lineTo(223, 199.000015f); pathB.lineTo(223, 202); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// pair of lines are not quite coincident, so sorting line/cubic fails (i think) static void skpwww_lavoixdunord_fr_11(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1160,12 +1138,9 @@ pathB.lineTo(808, 56); pathB.lineTo(935.02002f, 56.0200005f); pathB.lineTo(933, 54); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// pair of curves have nearly the same initial tangent but are sorting by -// that alone sorts them incorrectly. Need to detect that tangents are nearly -// identical and not reliable by themselves static void skppptv_com_62(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1189,10 +1164,9 @@ pathB.lineTo(169, 5346); pathB.cubicTo(169, 5343.79102f, 170.790863f, 5342, 173, 5342); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// nearly identical to lavoixdunord -- to not-quite-coincident lines static void skpwww_booking_com_68(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1222,10 +1196,9 @@ pathB.lineTo(92, 186); pathB.lineTo(593.02002f, 186.020004f); pathB.lineTo(591, 184); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// visually looks like lavoixdunord and www_booking_com static void skpwww_despegar_com_mx_272(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1255,7 +1228,7 @@ pathB.lineTo(833, 1787); pathB.lineTo(832.97998f, 1817.02002f); pathB.lineTo(835, 1815); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_joomla_org_23(skiatest::Reporter* reporter, const char* filename) { @@ -1283,7 +1256,7 @@ pathB.lineTo(320, 378); pathB.lineTo(421, 378.000031f); pathB.lineTo(421, 383); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_macrumors_com_131(skiatest::Reporter* reporter, const char* filename) { @@ -1309,7 +1282,7 @@ pathB.cubicTo(137.790863f, 14093, 136, 14091.209f, 136, 14089); pathB.lineTo(136, 14057); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_leadpages_net_84(skiatest::Reporter* reporter, const char* filename) { @@ -1330,7 +1303,7 @@ pathB.lineTo(378.481873f, 5909); pathB.lineTo(379.999878f, 5976); pathB.lineTo(376, 5976); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_briian_com_34(skiatest::Reporter* reporter, const char* filename) { @@ -1362,7 +1335,7 @@ pathB.lineTo(843, 779); pathB.lineTo(1196, 779.000061f); pathB.lineTo(1196, 784); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_sciality_com_100(skiatest::Reporter* reporter, const char* filename) { @@ -1390,7 +1363,7 @@ pathB.cubicTo(158, 469.34314f, 159.34314f, 468, 161, 468); pathB.lineTo(275, 468); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_sciality_com_101(skiatest::Reporter* reporter, const char* filename) { @@ -1418,7 +1391,7 @@ pathB.lineTo(158, 471); pathB.cubicTo(158, 469.895416f, 159.34314f, 469, 161, 469); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_meb_gov_tr_5(skiatest::Reporter* reporter, const char* filename) { @@ -1441,7 +1414,7 @@ pathB.lineTo(250, 177); pathB.lineTo(135, 177); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_meb_gov_tr_6(skiatest::Reporter* reporter, const char* filename) { @@ -1467,7 +1440,7 @@ pathB.lineTo(135, 151); pathB.cubicTo(135, 146.581726f, 138.581726f, 143, 143, 143); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpgithub_io_25(skiatest::Reporter* reporter, const char* filename) { @@ -1499,7 +1472,7 @@ pathB.lineTo(1003, 18); pathB.cubicTo(1003, 16.8954296f, 1003.89545f, 16, 1005, 16); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpgithub_io_26(skiatest::Reporter* reporter, const char* filename) { @@ -1535,7 +1508,7 @@ pathB.lineTo(1106, 16); pathB.lineTo(1105.97998f, 46.0200005f); pathB.lineTo(1108, 44); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpskpicture14(skiatest::Reporter* reporter, const char* filename) { @@ -1558,7 +1531,7 @@ pathB.lineTo(323, 193); pathB.lineTo(-317, 193); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpskpicture15(skiatest::Reporter* reporter, const char* filename) { @@ -1582,14 +1555,9 @@ pathB.lineTo(-317, 168); pathB.cubicTo(-317, 166.34314f, -315.65686f, 165, -314, 165); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -/* Three edges are partially coincident. Only one of the three knows about the other two. - Subsequently, when the angle loop is created, it misses one of the edges. - After coincident edges are processed, probably need a check-and-correct that makes sure the - coincidences are all self-consistent. - */ static void skpelpais_com_18(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1605,16 +1573,9 @@ pathB.lineTo(183, 8506.99023f); pathB.lineTo(552, 8507); pathB.lineTo(552, 8508); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -/* this generates a pair of lines that are essentially coincident; but the next line at a right - angle is not treated as if it intersects at the same point. - There are several of options: - move the intersection of the right angle line to the coincident point (should 'near' do this? - construct another coincident pair from the right angle line to the coincident point - treat the intersection as simple and not coincident - */ static void skpnamecheap_com_405(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1630,10 +1591,9 @@ pathB.lineTo(141.008835f, 837.9646f); pathB.lineTo(141.235291f, 1109.05884f); pathB.lineTo(140, 1114); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// fails on angle insert -- haven't investigated yet static void skpwww_alrakoba_net_62(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1656,10 +1616,9 @@ pathB.lineTo(135.962357f, 9800); pathB.lineTo(140, 9830); pathB.lineTo(132, 9830); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -/* asserts in alignSpanState looks like a coincident related bug */ static void skpwww_cityads_ru_249(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1693,10 +1652,9 @@ pathB.lineTo(1000, 13); pathB.lineTo(999.969971f, 37.0299988f); pathB.lineTo(1003, 34); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// fails on angle insert static void skpwww_dealnews_com_315(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1720,10 +1678,9 @@ pathB.lineTo(967.716675f, 4260); pathB.lineTo(970, 4281); pathB.lineTo(965, 4281); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// fails in intersections insert static void skpwww_inmotionhosting_com_9(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1743,7 +1700,7 @@ pathB.lineTo(1018.73242f, 1894.26501f); pathB.lineTo(963.734985f, 1893.73242f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_alucinados_net_101(skiatest::Reporter* reporter, const char* filename) { @@ -1765,10 +1722,9 @@ pathB.lineTo(-43515.8555f, -177415.594f); pathB.lineTo(1129.76465f, 1173.05884f); pathB.lineTo(1131, 1178); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// /SkOpContour.cpp:278: failed assertion "!approximately_negative(oEndT - oStartT) static void skpwww_hairjobsearch_com_31(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1786,10 +1742,9 @@ pathB.lineTo(144, 0); pathB.lineTo(1122, 0); pathB.lineTo(1123, 1); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// SkOpSegment::checkSmallCoincidence; line 1958 SkASSERT(span.fWindValue); static void skpwww_heartiste_wordpress_com_86(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1805,7 +1760,7 @@ pathB.lineTo(741, 9431.99023f); pathB.lineTo(761, 9432); pathB.lineTo(761, 9433); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_argus_presse_fr_41(skiatest::Reporter* reporter, const char* filename) { @@ -1824,10 +1779,9 @@ pathB.lineTo(1000, 364.869904f); pathB.lineTo(165, 364.869904f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// SkOpSegment::checkSmallCoincidence; line 1958 SkASSERT(span.fWindValue); static void skpwww_320kbps_net_2231(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1843,10 +1797,9 @@ pathB.lineTo(838, 9124.99023f); pathB.lineTo(862, 9125); pathB.lineTo(862, 9126); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// debugValidateLoop loop sum fails static void skpwww_exystence_net_61(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1862,10 +1815,9 @@ pathB.lineTo(143, 9073.99023f); pathB.lineTo(316, 9074); pathB.lineTo(316, 9075); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// debugValidateLoop loop sum fails static void skpwww_trashness_com_36(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1881,10 +1833,9 @@ pathB.lineTo(91.5f, 4835.99512f); pathB.lineTo(541.5f, 4836); pathB.lineTo(541.5f, 4836.5f); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// SkIntersections::lineVertical fUsed >= fMax static void skpwww_getgold_jp_731(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1903,10 +1854,9 @@ pathB.lineTo(286.05957f, 10129.8809f); pathB.lineTo(285.399994f, 10216.2002f); pathB.lineTo(284, 10219); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// SkOpContour::calcPartialCoincidentWinding SkASSERT(!approximately_negative(endT - startT)); static void skpwww_maturesupertube_com_21(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1944,10 +1894,9 @@ pathB.lineTo(4.5f, 11832.5f); pathB.lineTo(1260.5f, 11832.5f); pathB.lineTo(1263, 11830); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// can't find winding of remaining vertical edges static void skpwww_hubbyscook_com_22(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -1973,7 +1922,7 @@ pathB.quadTo(1005.02942f, 920, 1001.51471f, 917.071045f); pathB.quadTo(998, 914.142151f, 998, 910); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void skpwww_gruposejaumdivulgador_com_br_4(skiatest::Reporter* reporter, const char* filename) { @@ -1992,10 +1941,9 @@ pathB.lineTo(1084, 469); pathB.lineTo(611, 469); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// asserts in bridgeOp simple->isClosed() static void skpwww_phototransferapp_com_24(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -2018,7 +1966,7 @@ pathB.lineTo(1219.10657f, 13); pathB.lineTo(80.1065979f, 13); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_phototransferapp_com_24x(skiatest::Reporter* reporter, const char* filename) { @@ -2043,7 +1991,7 @@ pathB.lineTo(119.10657f, 13); pathB.lineTo(80.1065979f, 13); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_helha_be_109(skiatest::Reporter* reporter, const char* filename) { @@ -2065,7 +2013,7 @@ pathB.lineTo(117.686981f, 3339.08423f); pathB.lineTo(98.4669647f, 3351.56104f); pathB.lineTo(104.291214f, 3359.87891f); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_cooksnaps_com_32(skiatest::Reporter* reporter, const char* filename) { @@ -2117,7 +2065,7 @@ pathB.quadTo(509.696686f, 241.450104f, 497.29361f, 238.126709f); pathB.quadTo(484.890533f, 234.803314f, 478.470215f, 223.683014f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_cooksnaps_com_32a(skiatest::Reporter* reporter, const char* filename) { @@ -2133,10 +2081,9 @@ pathB.quadTo(478.196686f, 186.890503f, 489.316986f, 180.4702f); pathB.lineTo(490.183014f, 179.9702f); pathB.quadTo(501.303345f, 173.549896f, 513.706421f, 176.873276f); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// !simple->isClosed() static void skpwww_contextualnewsfeeds_com_346(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -2157,10 +2104,9 @@ pathB.lineTo(458.828979f, 1203.67822f); pathB.lineTo(465.914215f, 1196.62122f); pathB.lineTo(467.32843f, 1198.03552f); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// line quad intersection SkIntersections::assert static void skpwww_pindosiya_com_99(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -2179,10 +2125,9 @@ pathB.lineTo(901.086914f, 547); pathB.lineTo(899, 556); pathB.lineTo(898, 556); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// SkOpAngle::setSector SkASSERT(fSectorStart >= 0); static void skpwww_karnivool_com_au_11(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -2199,7 +2144,7 @@ pathB.lineTo(427, 1081); pathB.lineTo(-3.81469727e-06f, 1081); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_tunero_de_24(skiatest::Reporter* reporter, const char* filename) { @@ -2238,7 +2183,7 @@ pathB.quadTo(1013.13599f, 2273, 1012.06104f, 2271.53564f); pathB.quadTo(1010.98615f, 2270.07104f, 1011.53705f, 2268); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_docgelo_com_66(skiatest::Reporter* reporter, const char* filename) { @@ -2256,7 +2201,7 @@ pathB.lineTo(22.5f, 24174.498f); pathB.lineTo(185.5f, 24174.5f); pathB.lineTo(185.5f, 24174.75f); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_kpopexplorer_net_22(skiatest::Reporter* reporter, const char* filename) { @@ -2284,7 +2229,7 @@ pathB.quadTo(1005.02942f, 884, 1001.51471f, 881.071045f); pathB.quadTo(998, 878.142151f, 998, 874); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void skpwww_artblart_com_8(skiatest::Reporter* reporter, const char* filename) { @@ -2302,10 +2247,9 @@ pathB.lineTo(22.5f, 24527.248f); pathB.lineTo(45, 24527.25f); pathB.lineTo(45, 24527.5f); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// joinCoincidence / findT / assert static void skpwww_jessicaslens_wordpress_com_222(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -2331,10 +2275,9 @@ pathB.quadTo(1005.02942f, 862, 1001.51471f, 859.071045f); pathB.quadTo(998, 856.142151f, 998, 852); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } -// joinCoincidence / findT / assert static void skpwww_simplysaru_com_40(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -2360,10 +2303,9 @@ pathB.quadTo(1005.02942f, 884, 1001.51471f, 881.071045f); pathB.quadTo(998, 878.142151f, 998, 874); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } -// cubic-cubic intersection reduce checkLinear assert static void skpwww_partsdata_de_53(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -2659,10 +2601,9 @@ pathB.cubicTo(631.989807f, 46.6754761f, 632.04364f, 47.0436478f, 632.04364f, 47.4595947f); pathB.cubicTo(632.042847f, 47.949852f, 631.916565f, 48.3282623f, 631.656494f, 48.6171875f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// SkOpAngle::setSector SkASSERT(fSectorStart >= 0); static void skpwww_seopack_blogspot_com_2153(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -2683,10 +2624,9 @@ pathB.lineTo(924, 245.472672f); pathB.lineTo(1143, 247); pathB.lineTo(1143, 248); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// joinCoincidence / findT / assert static void skpwww_lokado_de_173(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -2720,10 +2660,9 @@ pathB.quadTo(951.857849f, 916, 948.928955f, 913.071045f); pathB.quadTo(946, 910.142151f, 946, 906); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } -// !simple->isClosed() static void skpwww_wartepop_blogspot_com_br_6(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -2753,7 +2692,7 @@ pathB.lineTo(90, 163.666672f); pathB.lineTo(90, 163.666672f); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void skpwww_wartepop_blogspot_com_br_6a(skiatest::Reporter* reporter, const char* filename) { @@ -2785,10 +2724,9 @@ pathB.lineTo(90, 163.666672f); pathB.lineTo(90, 163.666672f); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } -// !simple->isClosed() static void skpwww_odia_com_br_26(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -2833,7 +2771,7 @@ pathB.lineTo(364.345337f, 754.288269f); pathB.lineTo(352.711792f, 751.345337f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_evolvehq_com_210(skiatest::Reporter* reporter, const char* filename) { @@ -2869,10 +2807,9 @@ pathB.quadTo(171.17157f, 1174, 170.585785f, 1173.12134f); pathB.quadTo(170, 1172.24268f, 170, 1171); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } -// hangs static void skpwww_catingueiraonline_com_352(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -2888,10 +2825,9 @@ pathB.lineTo(444.01001f, 8140); pathB.lineTo(444, 8292); pathB.lineTo(443, 8292); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// hangs static void skpwww_galaxystwo_com_4(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -2907,7 +2843,7 @@ pathB.lineTo(10105, 2509.98999f); pathB.lineTo(10123, 2510); pathB.lineTo(10123, 2511); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_thaienews_blogspot_com_36(skiatest::Reporter* reporter, const char* filename) { @@ -2925,7 +2861,7 @@ pathB.lineTo(429.994995f, 2187); pathB.lineTo(430, 6268); pathB.lineTo(430.5f, 6268); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_fashionscandal_com_94(skiatest::Reporter* reporter, const char* filename) { @@ -2961,7 +2897,7 @@ pathB.quadTo(26.2238579f, 418, 26.0285969f, 417.75592f); pathB.quadTo(25.833334f, 417.511841f, 25.833334f, 417.166656f); pathB.close(); - testPathOp(reporter, path, pathB, kDifference_PathOp, filename); + testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename); } static void skpwww_kenlevine_blogspot_com_28(skiatest::Reporter* reporter, const char* filename) { @@ -2979,7 +2915,7 @@ pathB.lineTo(277.01001f, 7531); pathB.lineTo(277, 9506); pathB.lineTo(276, 9506); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_defense_studies_blogspot_com_64(skiatest::Reporter* reporter, const char* filename) { @@ -2997,7 +2933,7 @@ pathB.lineTo(277.01001f, 7703); pathB.lineTo(277, 9600); pathB.lineTo(276, 9600); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_uniquefx_net_442(skiatest::Reporter* reporter, const char* filename) { @@ -3015,7 +2951,7 @@ pathB.lineTo(958.997253f, 306.002747f); pathB.lineTo(1017, 307); pathB.lineTo(1019, 305); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_kitcheninspirations_wordpress_com_32(skiatest::Reporter* reporter, const char* filename) { @@ -3033,7 +2969,7 @@ pathB.lineTo(47.1666679f, 19651.332f); pathB.lineTo(65.8333359f, 19651.334f); pathB.lineTo(65.8333359f, 19651.5f); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_educationalcraft_com_4(skiatest::Reporter* reporter, const char* filename) { @@ -3211,7 +3147,7 @@ pathB.cubicTo(980.968994f, 1478.18005f, 979.718018f, 1475.66199f, 983.632019f, 1473.87805f); pathB.cubicTo(983.632019f, 1473.87805f, 984.229004f, 1477.80103f, 980.968994f, 1478.18005f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_narayana_publishers_com_194(skiatest::Reporter* reporter, const char* filename) { @@ -3239,7 +3175,7 @@ pathB.lineTo(1082, 440); pathB.lineTo(1090.01001f, 448); pathB.lineTo(1081, 448); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_cooksnaps_com_17(skiatest::Reporter* reporter, const char* filename) { @@ -3288,7 +3224,7 @@ pathB.quadTo(170.696686f, 241.450104f, 158.293594f, 238.126709f); pathB.quadTo(145.890503f, 234.803314f, 139.4702f, 223.683014f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_swapspacesystems_com_5(skiatest::Reporter* reporter, const char* filename) { @@ -3315,7 +3251,7 @@ pathB.lineTo(1186.5199f, 5809.85059f); pathB.lineTo(811.648376f, 5800.03418f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_kitcheninspirations_wordpress_com_66(skiatest::Reporter* reporter, const char* filename) { @@ -3333,7 +3269,7 @@ pathB.lineTo(47.1666679f, 27820.498f); pathB.lineTo(60.8333359f, 27820.5f); pathB.lineTo(60.8333359f, 27820.668f); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_etiqadd_com_2464(skiatest::Reporter* reporter, const char* filename) { @@ -3360,7 +3296,7 @@ pathB.lineTo(632.5f, 1309.69238f); pathB.lineTo(623.307617f, 1300.5f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_narayana_verlag_de_194(skiatest::Reporter* reporter, const char* filename) { @@ -3388,7 +3324,7 @@ pathB.lineTo(1082, 508); pathB.lineTo(1090.01001f, 516); pathB.lineTo(1081, 516); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_americascup_com_108(skiatest::Reporter* reporter, const char* filename) { @@ -3411,7 +3347,7 @@ pathB.lineTo(1002.17114f, 713); pathB.lineTo(987.171143f, 713); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_vantageproduction_com_109(skiatest::Reporter* reporter, const char* filename) { @@ -3439,7 +3375,7 @@ pathB.lineTo(792, 751); pathB.lineTo(804.01001f, 763); pathB.lineTo(791, 763); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_aceinfographics_com_106(skiatest::Reporter* reporter, const char* filename) { @@ -3463,7 +3399,7 @@ pathB.lineTo(168.020004f, 7635.97998f); pathB.lineTo(168, 11578); pathB.lineTo(166, 11580); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_tcmevents_org_13(skiatest::Reporter* reporter, const char* filename) { @@ -3490,7 +3426,7 @@ pathB.lineTo(468.507751f, 560.724426f); pathB.lineTo(325.968597f, 560.475708f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_paseoitaigara_com_br_56(skiatest::Reporter* reporter, const char* filename) { @@ -3518,7 +3454,7 @@ pathB.quadTo(634.389832f, 1248.24268f, 634.389832f, 1247); pathB.quadTo(634.389832f, 1245.75732f, 635.268494f, 1244.87866f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_mortgagemarketguide_com_109(skiatest::Reporter* reporter, const char* filename) { @@ -3546,7 +3482,7 @@ pathB.lineTo(814, 773); pathB.lineTo(826.01001f, 785); pathB.lineTo(813, 785); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_9to5mac_com_64(skiatest::Reporter* reporter, const char* filename) { @@ -3568,7 +3504,7 @@ pathB.lineTo(365.848175f, 5081.15186f); pathB.lineTo(368, 5103); pathB.lineTo(365, 5106); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } static void skpwww_googleventures_com_32(skiatest::Reporter* reporter, const char* filename) { @@ -3587,10 +3523,9 @@ pathB.lineTo(738.767395f, 914.088379f); pathB.lineTo(713.055908f, 883.446594f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -#if TEST_NEW_FAILURES static void skpwww_devbridge_com_22(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -3616,10 +3551,9 @@ pathB.quadTo(4942.75146f, 1523, 4962.375f, 1542.6239f); pathB.quadTo(4981.99902f, 1562.24768f, 4981.99902f, 1590); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// cubic/quad intersection static void skpwww_alamdi_com_3(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -3649,10 +3583,9 @@ pathB.lineTo(10210, 5318); pathB.cubicTo(10210, 5316.34326f, 10211.3428f, 5315, 10213, 5315); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// bumpSpan failed assertion "span->fOppValue >= 0" static void skpwww_familysurvivalprotocol_wordpress_com_61(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -3668,9 +3601,8 @@ pathB.lineTo(143, 14555.9902f); pathB.lineTo(165, 14556); pathB.lineTo(165, 14557); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -#endif static void skpwww_firstunitedbank_com_19(skiatest::Reporter* reporter, const char* filename) { SkPath path; @@ -3705,10 +3637,9 @@ pathB.lineTo(809.5f, 11701.5f); pathB.lineTo(1062.91907f, 11687.0811f); pathB.lineTo(1047, 11703); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// addSimpleAngle: failed assertion "index == count() - 2" static void skpwww_shinydemos_com_5(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -3725,10 +3656,9 @@ pathB.lineTo(545.296204f, 987.615051f); pathB.lineTo(205.884949f, 648.203796f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// addTCoincident oPeek = &other->fTs[++oPeekIndex]; static void skpwww_lptemp_com_3(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -3758,11 +3688,9 @@ pathB.lineTo(77.6666718f, 1396.66675f); pathB.cubicTo(77.6666718f, 1394.82568f, 79.15905f, 1393.33337f, 81, 1393.33337f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -#if TEST_NEW_FAILURES -// SkOpSegment.cpp:3915: failed assertion "otherEnd >= 0" static void skpwww_shinydemos_com_15(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -3781,10 +3709,9 @@ pathB.lineTo(545.296204f, 987.615051f); pathB.lineTo(205.884949f, 648.203796f); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -// SkOpSegment.cpp:4398: failed assertion "!span->fDone" static void skpwww_lptemp_com_5(skiatest::Reporter* reporter, const char* filename) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -3814,22 +3741,917 @@ pathB.lineTo(77.6666718f, 3153.3335f); pathB.cubicTo(77.6666718f, 3151.49268f, 79.15905f, 3150, 81, 3150); pathB.close(); - testPathOp(reporter, path, pathB, kIntersect_PathOp, filename); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); } -#endif +static void skpwww_educationalcraft_com_4a(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(941, 1494); + path.lineTo(941, 1464); + path.lineTo(985, 1464); + path.lineTo(985, 1494); + path.lineTo(941, 1494); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kWinding_FillType); + +pathB.moveTo(984.546021f, 1478.31494f); +pathB.cubicTo(984.546021f, 1478.31494f, 984.543213f, 1478.32239f, 984.537598f, 1478.33655f); +pathB.cubicTo(984.419006f, 1478.63477f, 983.044373f, 1481.90405f, 980.026001f, 1481.276f); +pathB.cubicTo(980.026001f, 1481.276f, 980.02594f, 1481.27576f, 980.025879f, 1481.27527f); +pathB.cubicTo(980.018494f, 1481.22131f, 979.602478f, 1478.38831f, 984.546021f, 1478.31494f); + testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename); + +} + +static void skpwww_woothemes_com_1(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x44472795), SkBits2Float(0x455cdb8d)); // 796.618f, 3533.72f +path.lineTo(SkBits2Float(0x44467c27), SkBits2Float(0x455cdb8d)); // 793.94f, 3533.72f +path.lineTo(SkBits2Float(0x44467c27), SkBits2Float(0x455d055d)); // 793.94f, 3536.34f +path.lineTo(SkBits2Float(0x44472795), SkBits2Float(0x455d055d)); // 796.618f, 3536.34f +path.lineTo(SkBits2Float(0x44472795), SkBits2Float(0x455cdb8d)); // 796.618f, 3533.72f + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x4446861c), SkBits2Float(0x455cdb8d)); // 794.095f, 3533.72f +path.cubicTo(SkBits2Float(0x4446a0d8), SkBits2Float(0x455cefbb), SkBits2Float(0x444727a5), SkBits2Float(0x455d055d), SkBits2Float(0x444727a5), SkBits2Float(0x455d055d)); // 794.513f, 3534.98f, 796.619f, 3536.34f, 796.619f, 3536.34f +path.cubicTo(SkBits2Float(0x4446c5b0), SkBits2Float(0x455cf8a4), SkBits2Float(0x444693af), SkBits2Float(0x455cedad), SkBits2Float(0x44467c1b), SkBits2Float(0x455ce4b8)); // 795.089f, 3535.54f, 794.308f, 3534.85f, 793.939f, 3534.29f +path.lineTo(SkBits2Float(0x44467d70), SkBits2Float(0x455ce016)); // 793.96f, 3534.01f +path.cubicTo(SkBits2Float(0x44467fa9), SkBits2Float(0x455cde82), SkBits2Float(0x444682b5), SkBits2Float(0x455cdd03), SkBits2Float(0x4446861c), SkBits2Float(0x455cdb8d)); // 793.995f, 3533.91f, 794.042f, 3533.81f, 794.095f, 3533.72f + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 1, filename); +} + +static void skpwww_gorcraft_ru_1(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x44924000), SkBits2Float(0x458e7800)); // 1170, 4559 +path.conicTo(SkBits2Float(0x44930000), SkBits2Float(0x458e7800), SkBits2Float(0x44930000), SkBits2Float(0x458ea800), SkBits2Float(0x3f3504f3)); // 1176, 4559, 1176, 4565, 0.707107f +path.lineTo(SkBits2Float(0x44930000), SkBits2Float(0x458f7000)); // 1176, 4590 +path.conicTo(SkBits2Float(0x44930000), SkBits2Float(0x458f9800), SkBits2Float(0x44926000), SkBits2Float(0x458f9800), SkBits2Float(0x3f3504f3)); // 1176, 4595, 1171, 4595, 0.707107f +path.lineTo(SkBits2Float(0x42a60000), SkBits2Float(0x458f9800)); // 83, 4595 +path.conicTo(SkBits2Float(0x429c0471), SkBits2Float(0x458f9800), SkBits2Float(0x429c0000), SkBits2Float(0x458f700c), SkBits2Float(0x3f352d2d)); // 78.0087f, 4595, 78, 4590.01f, 0.707721f +path.lineTo(SkBits2Float(0x429c0000), SkBits2Float(0x458ea800)); // 78, 4565 +path.conicTo(SkBits2Float(0x429c0000), SkBits2Float(0x458e7800), SkBits2Float(0x42a80000), SkBits2Float(0x458e7800), SkBits2Float(0x3f3504f3)); // 78, 4559, 84, 4559, 0.707107f +path.lineTo(SkBits2Float(0x44924000), SkBits2Float(0x458e7800)); // 1170, 4559 +path.close(); + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x429c0000), SkBits2Float(0x458f7000)); // 78, 4590 +path.lineTo(SkBits2Float(0x429c0000), SkBits2Float(0x458ea800)); // 78, 4565 +path.conicTo(SkBits2Float(0x429c0000), SkBits2Float(0x458e7800), SkBits2Float(0x42a80000), SkBits2Float(0x458e7800), SkBits2Float(0x3f3504f3)); // 78, 4559, 84, 4559, 0.707107f +path.lineTo(SkBits2Float(0x431e0000), SkBits2Float(0x458e7800)); // 158, 4559 +path.conicTo(SkBits2Float(0x431e0000), SkBits2Float(0x458e7800), SkBits2Float(0x431e0000), SkBits2Float(0x458e7800), SkBits2Float(0x3f3504f3)); // 158, 4559, 158, 4559, 0.707107f +path.lineTo(SkBits2Float(0x431e0000), SkBits2Float(0x458fa000)); // 158, 4596 +path.conicTo(SkBits2Float(0x431e0000), SkBits2Float(0x458fa000), SkBits2Float(0x431e0000), SkBits2Float(0x458fa000), SkBits2Float(0x3f3504f3)); // 158, 4596, 158, 4596, 0.707107f +path.lineTo(SkBits2Float(0x42a80000), SkBits2Float(0x458fa000)); // 84, 4596 +path.conicTo(SkBits2Float(0x429c0000), SkBits2Float(0x458fa000), SkBits2Float(0x429c0000), SkBits2Float(0x458f7000), SkBits2Float(0x3f3504f3)); // 78, 4596, 78, 4590, 0.707107f +path.close(); + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 2, filename); +} + +static void skpwww_neda_net_1(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x447a0000), SkBits2Float(0x00000000)); // 1000, 0 +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 +path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x44b6e000)); // 0, 1463 +path.lineTo(SkBits2Float(0x447a0000), SkBits2Float(0x44b6e000)); // 1000, 1463 +path.lineTo(SkBits2Float(0x447a0000), SkBits2Float(0x00000000)); // 1000, 0 +path.close(); + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x366a410f), SkBits2Float(0x43a38000)); // 3.49066e-06f, 327 +path.lineTo(SkBits2Float(0x447a0000), SkBits2Float(0x43a38001)); // 1000, 327 +path.lineTo(SkBits2Float(0x447a0000), SkBits2Float(0x4435c000)); // 1000, 727 +path.lineTo(SkBits2Float(0xb66a410d), SkBits2Float(0x4435c000)); // -3.49066e-06f, 727 +path.lineTo(SkBits2Float(0x366a410f), SkBits2Float(0x43a38000)); // 3.49066e-06f, 327 +path.close(); + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 1, filename); +} + +// "http___www_neda_net.skp" dir=87 +static void skpwww_neda_net_2(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x442fc000), SkBits2Float(0x4546a000)); // 703, 3178 +path.lineTo(SkBits2Float(0x441f4000), SkBits2Float(0x4546a000)); // 637, 3178 +path.lineTo(SkBits2Float(0x441f4000), SkBits2Float(0x454ab000)); // 637, 3243 +path.lineTo(SkBits2Float(0x442fc000), SkBits2Float(0x454ab000)); // 703, 3243 +path.lineTo(SkBits2Float(0x442fc000), SkBits2Float(0x4546a000)); // 703, 3178 +path.close(); + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x44220e6e), SkBits2Float(0x45469c4c)); // 648.225f, 3177.77f +path.lineTo(SkBits2Float(0x442fc01c), SkBits2Float(0x45475696)); // 703.002f, 3189.41f +path.lineTo(SkBits2Float(0x442cf191), SkBits2Float(0x454aa3b5)); // 691.774f, 3242.23f +path.lineTo(SkBits2Float(0x441f3fe3), SkBits2Float(0x4549e96b)); // 636.998f, 3230.59f +path.lineTo(SkBits2Float(0x44220e6e), SkBits2Float(0x45469c4c)); // 648.225f, 3177.77f +path.close(); + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 1, filename); +} + +static void skpwww_mybuilder_com_1(skiatest::Reporter* reporter, const char* filename) { + SkPath path; +path.setFillType(SkPath::kEvenOdd_FillType); +path.moveTo(1000, 659); +path.lineTo(1000, 377); +path.lineTo(455, 377); +path.lineTo(455, 659); +path.lineTo(1000, 659); +path.close(); + SkPath path1(path); + path.reset(); + path.setFillType(SkPath::kEvenOdd_FillType); +path.moveTo(921.472f, 414.086f); +path.lineTo(968.815f, 386.754f); +path.lineTo(993.069f, 428.761f); +path.lineTo(945.726f, 456.096f); +path.lineTo(921.471f, 414.086f); +path.lineTo(921.472f, 414.086f); +path.close(); +path.moveTo(971.151f, 422.889f); +path.cubicTo(966.509f, 414.848f, 957.649f, 411.727f, 950.181f, 416.038f); +path.lineTo(947.224f, 417.746f); +path.lineTo(946.979f, 417.887f); +path.lineTo(947.838f, 419.371f); +path.lineTo(947.844f, 419.367f); +path.lineTo(947.868f, 419.353f); +path.lineTo(947.945f, 419.309f); +path.cubicTo(947.988f, 419.285f, 947.988f, 419.285f, 948.023f, 419.263f); +path.cubicTo(948.039f, 419.255f, 948.039f, 419.255f, 948.047f, 419.25f); +path.lineTo(948.052f, 419.247f); +path.lineTo(947.196f, 417.762f); +path.lineTo(947.195f, 417.762f); +path.lineTo(946.888f, 417.939f); +path.lineTo(943.39f, 419.959f); +path.lineTo(944.249f, 421.443f); +path.lineTo(947.745f, 419.424f); +path.lineTo(948.05f, 419.247f); +path.lineTo(948.052f, 419.247f); +path.lineTo(947.195f, 417.763f); +path.cubicTo(947.193f, 417.763f, 947.193f, 417.763f, 947.19f, 417.766f); +path.lineTo(947.166f, 417.779f); +path.lineTo(947.087f, 417.825f); +path.lineTo(947.011f, 417.868f); +path.lineTo(946.987f, 417.883f); +path.lineTo(946.982f, 417.886f); +path.lineTo(946.98f, 417.886f); +path.lineTo(947.839f, 419.37f); +path.lineTo(948.083f, 419.229f); +path.lineTo(951.039f, 417.522f); +path.cubicTo(957.631f, 413.716f, 965.471f, 416.477f, 969.669f, 423.746f); +path.lineTo(971.153f, 422.889f); +path.lineTo(971.151f, 422.889f); +path.close(); + SkPath path2(path); + testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename); +} + +static void skpwww_nimble_com_au_1(skiatest::Reporter* reporter, const char* filename) { + SkPath path; +path.setFillType(SkPath::kEvenOdd_FillType); +path.moveTo(188.6f, 1988.8f); +path.lineTo(188.6f, 2041.6f); +path.cubicTo(188.6f, 2065.4f, 208, 2084.8f, 231.8f, 2084.8f); +path.cubicTo(255.6f, 2084.8f, 275, 2065.4f, 275, 2041.6f); +path.lineTo(275.2f, 2041.6f); +path.lineTo(275.2f, 1988.8f); +path.lineTo(188.6f, 1988.8f); +path.close(); + SkPath path1(path); + path.reset(); +path.setFillType(SkPath::kWinding_FillType); +path.moveTo(275, 2041.6f); +path.conicTo(275, 2084.8f, 231.8f, 2084.8f, 0.707107f); +path.conicTo(188.6f, 2084.8f, 188.6f, 2041.6f, 0.707107f); +path.conicTo(188.6f, 1998.4f, 231.8f, 1998.4f, 0.707107f); +path.conicTo(275, 1998.4f, 275, 2041.6f, 0.707107f); +path.close(); + SkPath path2(path); + testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename); +} + +static void skpwww_tinytots_com_1(skiatest::Reporter* reporter, const char* filename) { + SkPath path; +path.setFillType(SkPath::kEvenOdd_FillType); +path.moveTo(75.96f, 26.318f); +path.lineTo(70.337f, 26.318f); +path.lineTo(70.337f, 32.376f); +path.lineTo(75.96f, 32.376f); +path.lineTo(75.96f, 26.318f); +path.close(); + SkPath path1(path); + path.reset(); +path.setFillType(SkPath::kWinding_FillType); +path.moveTo(75.88f, 27.873f); +path.cubicTo(75.929f, 28.138f, 75.956f, 29.196f, 75.96f, 31.046f); +path.lineTo(72.766f, 32.376f); +path.cubicTo(72.763f, 30.525f, 72.735f, 29.468f, 72.686f, 29.203f); +path.cubicTo(72.636f, 28.94f, 72.519f, 28.722f, 72.335f, 28.552f); +path.cubicTo(72.248f, 28.472f, 72.058f, 28.364f, 71.763f, 28.228f); +path.cubicTo(72.425f, 27.933f, 72.425f, 27.933f, 73.395f, 27.498f); +path.cubicTo(72.425f, 27.933f, 72.425f, 27.933f, 71.763f, 28.228f); +path.cubicTo(71.425f, 28.072f, 70.95f, 27.878f, 70.337f, 27.647f); +path.lineTo(73.531f, 26.317f); +path.cubicTo(74.144f, 26.547f, 74.619f, 26.741f, 74.957f, 26.898f); +path.cubicTo(74.475f, 27.113f, 73.993f, 27.329f, 73.511f, 27.544f); +path.cubicTo(73.993f, 27.329f, 74.475f, 27.114f, 74.957f, 26.898f); +path.cubicTo(75.252f, 27.034f, 75.442f, 27.142f, 75.529f, 27.222f); +path.cubicTo(75.713f, 27.393f, 75.83f, 27.61f, 75.88f, 27.873f); + SkPath path2(path); + testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename); +} + +static void http___www_emuleteca_cl_26(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x44370000)); // 1, 732 +path.conicTo(SkBits2Float(0x3f800000), SkBits2Float(0x4428c000), SkBits2Float(0x42680000), SkBits2Float(0x4428c000), SkBits2Float(0x3f3504f3)); // 1, 675, 58, 675, 0.707107f +path.conicTo(SkBits2Float(0x40400000), SkBits2Float(0x4428c000), SkBits2Float(0x40400000), SkBits2Float(0x44370000), SkBits2Float(0x3f3504f3)); // 3, 675, 3, 732, 0.707107f +path.conicTo(SkBits2Float(0x40400000), SkBits2Float(0x44444000), SkBits2Float(0x42680000), SkBits2Float(0x44444000), SkBits2Float(0x3f3504f3)); // 3, 785, 58, 785, 0.707107f +path.lineTo(SkBits2Float(0x446d0000), SkBits2Float(0x44444000)); // 948, 785 +path.conicTo(SkBits2Float(0x447ac000), SkBits2Float(0x44444000), SkBits2Float(0x447ac000), SkBits2Float(0x44370000), SkBits2Float(0x3f3504f3)); // 1003, 785, 1003, 732, 0.707107f +path.conicTo(SkBits2Float(0x447ac000), SkBits2Float(0x4428c000), SkBits2Float(0x446d0000), SkBits2Float(0x4428c000), SkBits2Float(0x3f3504f3)); // 1003, 675, 948, 675, 0.707107f +path.conicTo(SkBits2Float(0x447b4000), SkBits2Float(0x4428c000), SkBits2Float(0x447b4000), SkBits2Float(0x44370000), SkBits2Float(0x3f3504f3)); // 1005, 675, 1005, 732, 0.707107f +path.conicTo(SkBits2Float(0x447b4000), SkBits2Float(0x44454000), SkBits2Float(0x446d0000), SkBits2Float(0x44454000), SkBits2Float(0x3f3504f3)); // 1005, 789, 948, 789, 0.707107f +path.lineTo(SkBits2Float(0x42680000), SkBits2Float(0x44454000)); // 58, 789 +path.conicTo(SkBits2Float(0x3f800000), SkBits2Float(0x44454000), SkBits2Float(0x3f800000), SkBits2Float(0x44370000), SkBits2Float(0x3f3504f3)); // 1, 789, 1, 732, 0.707107f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x4428c000)); // 1, 675 +path.lineTo(SkBits2Float(0x42680000), SkBits2Float(0x4428c000)); // 58, 675 +path.lineTo(SkBits2Float(0x3fc8f676), SkBits2Float(0x44454000)); // 1.57002f, 789 +path.lineTo(SkBits2Float(0x3f800000), SkBits2Float(0x44454000)); // 1, 789 + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 1, filename); +} + +static void http___www_emuleteca_cl_27(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType((SkPath::FillType) 1); +path.moveTo(SkBits2Float(0x42680000), SkBits2Float(0x4428c000)); // 58, 675 +path.conicTo(SkBits2Float(0x3f800000), SkBits2Float(0x4428c000), SkBits2Float(0x3f800000), SkBits2Float(0x44370000), SkBits2Float(0x3f3504f3)); // 1, 675, 1, 732, 0.707107f +path.conicTo(SkBits2Float(0x3f800000), SkBits2Float(0x443bd045), SkBits2Float(0x414acf56), SkBits2Float(0x443fa420), SkBits2Float(0x3f778612)); // 1, 751.254f, 12.6756f, 766.564f, 0.96689f +path.lineTo(SkBits2Float(0x41606e3d), SkBits2Float(0x443ef569)); // 14.0269f, 763.835f +path.conicTo(SkBits2Float(0x40400000), SkBits2Float(0x443b6c34), SkBits2Float(0x40400000), SkBits2Float(0x44370000), SkBits2Float(0x3f77ac46)); // 3, 749.691f, 3, 732, 0.967472f +path.conicTo(SkBits2Float(0x40400000), SkBits2Float(0x4428c000), SkBits2Float(0x42680000), SkBits2Float(0x4428c000), SkBits2Float(0x3f3504f3)); // 3, 675, 58, 675, 0.707107f +path.close(); + + SkPath path1(path); + path.reset(); + path.setFillType((SkPath::FillType) 0); +path.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x4428c000)); // 1, 675 +path.lineTo(SkBits2Float(0x4c4a3de6), SkBits2Float(0xccca3d89)); // 5.30165e+07f, -1.06032e+08f +path.lineTo(SkBits2Float(0x41a71147), SkBits2Float(0x443b4eec)); // 20.8834f, 749.233f +path.lineTo(SkBits2Float(0x3f800000), SkBits2Float(0x44454000)); // 1, 789 + + SkPath path2(path); + testPathOp(reporter, path1, path2, (SkPathOp) 1, filename); +} + +static void http___www_emuleteca_cl_28(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + + SkPath path1(path); + path.reset(); + path.setFillType(SkPath::kWinding_FillType); +path.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x4428c000)); // 1, 675 +path.lineTo(SkBits2Float(0x4c4a3de6), SkBits2Float(0xccca3d89)); // 5.30165e+07f, -1.06032e+08f +path.lineTo(SkBits2Float(0x41a71147), SkBits2Float(0x443b4eec)); // 20.8834f, 749.233f +path.lineTo(SkBits2Float(0x3f800000), SkBits2Float(0x44454000)); // 1, 789 + SkPath path2(path); + testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename); +} + +static void http___www_project2061_org(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); +path.moveTo(751, 62); +path.lineTo(497, 62); +path.lineTo(497, 138); +path.lineTo(751, 138); +path.lineTo(751, 62); +path.close(); + SkPath path1(path); + path.reset(); + path.setFillType(SkPath::kWinding_FillType); +path.moveTo(699.889f, 115.29f); +path.cubicTo(699.889f, 114.913f, 699.627f, 114.723f, 699.111f, 114.723f); +path.cubicTo(698.604f, 114.723f, 697.843f, 114.671f, 697.096f, 114.423f); +path.cubicTo(695.94f, 114.094f, 694.53f, 113.168f, 692.746f, 108.775f); +path.cubicTo(690.469f, 103.12f, 685.644f, 90.563f, 682.761f, 83.061f); +path.lineTo(680.541f, 77.301f); +path.cubicTo(679.927f, 75.721f, 679.67f, 75.261f, 679.151f, 75.261f); +path.cubicTo(678.636f, 75.261f, 678.392f, 75.73f, 677.759f, 77.464f); +path.lineTo(665.868f, 109.881f); +path.cubicTo(664.861f, 112.669f, 663.852f, 114.339f, 661.675f, 114.622f); +path.cubicTo(661.675f, 114.622f, 661.551f, 114.631f, 661.378f, 114.642f); +path.cubicTo(661.072f, 114.597f, 660.75f, 114.531f, 660.427f, 114.424f); +path.cubicTo(659.272f, 114.095f, 657.861f, 113.169f, 656.078f, 108.776f); +path.cubicTo(653.797f, 103.112f, 648.967f, 90.541f, 646.08f, 83.029f); +path.lineTo(643.873f, 77.302f); +path.cubicTo(643.259f, 75.722f, 643.002f, 75.262f, 642.484f, 75.262f); +path.cubicTo(641.968f, 75.262f, 641.724f, 75.731f, 641.092f, 77.465f); +path.lineTo(629.2f, 109.879f); +path.cubicTo(628.193f, 112.667f, 627.184f, 114.337f, 625.007f, 114.62f); +path.cubicTo(625.007f, 114.62f, 624.884f, 114.629f, 624.71f, 114.64f); +path.cubicTo(624.404f, 114.595f, 624.081f, 114.529f, 623.759f, 114.422f); +path.cubicTo(622.603f, 114.093f, 621.193f, 113.167f, 619.409f, 108.774f); +path.cubicTo(617.132f, 103.119f, 612.307f, 90.562f, 609.424f, 83.06f); +path.lineTo(607.204f, 77.3f); +path.cubicTo(606.59f, 75.72f, 606.333f, 75.26f, 605.815f, 75.26f); +path.cubicTo(605.3f, 75.26f, 605.055f, 75.729f, 604.423f, 77.463f); +path.lineTo(592.531f, 109.879f); +path.cubicTo(591.524f, 112.667f, 590.514f, 114.337f, 588.338f, 114.62f); +path.cubicTo(588.337f, 114.62f, 586.852f, 114.722f, 586.852f, 114.722f); +path.cubicTo(586.418f, 114.722f, 586.126f, 114.95f, 586.126f, 115.289f); +path.cubicTo(586.126f, 115.96f, 586.97f, 115.96f, 587.376f, 115.96f); +path.lineTo(592.101f, 115.843f); +path.lineTo(593.295f, 115.804f); +path.lineTo(594.624f, 115.86f); +path.lineTo(597.381f, 115.961f); +path.cubicTo(598.241f, 115.961f, 598.422f, 115.596f, 598.422f, 115.29f); +path.cubicTo(598.422f, 115.078f, 598.321f, 114.723f, 597.643f, 114.723f); +path.lineTo(597.119f, 114.723f); +path.cubicTo(596.448f, 114.723f, 595.383f, 114.381f, 595.383f, 113.463f); +path.cubicTo(595.383f, 112.545f, 595.638f, 111.334f, 596.101f, 110.052f); +path.cubicTo(596.103f, 110.048f, 599.246f, 100.809f, 599.246f, 100.809f); +path.cubicTo(599.337f, 100.583f, 599.435f, 100.564f, 599.528f, 100.564f); +path.lineTo(610.791f, 100.564f); +path.cubicTo(610.929f, 100.564f, 610.958f, 100.585f, 611.035f, 100.737f); +path.cubicTo(611.035f, 100.741f, 615.896f, 113.554f, 615.896f, 113.554f); +path.cubicTo(615.972f, 113.754f, 616.01f, 113.942f, 616.01f, 114.105f); +path.cubicTo(616.01f, 114.228f, 615.988f, 114.338f, 615.945f, 114.429f); +path.cubicTo(615.876f, 114.573f, 615.779f, 114.619f, 615.741f, 114.631f); +path.cubicTo(615.272f, 114.748f, 615.2f, 115.061f, 615.2f, 115.237f); +path.cubicTo(615.2f, 115.766f, 615.792f, 115.796f, 616.772f, 115.844f); +path.lineTo(617.012f, 115.857f); +path.cubicTo(618.978f, 115.913f, 621.359f, 115.948f, 623.835f, 115.958f); +path.cubicTo(623.912f, 115.961f, 623.984f, 115.961f, 624.045f, 115.961f); +path.lineTo(624.118f, 115.959f); +path.cubicTo(624.494f, 115.961f, 624.871f, 115.961f, 625.25f, 115.961f); +path.cubicTo(625.47f, 115.961f, 625.662f, 115.946f, 625.827f, 115.917f); +path.lineTo(628.77f, 115.844f); +path.lineTo(629.965f, 115.805f); +path.lineTo(631.293f, 115.861f); +path.lineTo(634.051f, 115.962f); +path.cubicTo(634.911f, 115.962f, 635.092f, 115.597f, 635.092f, 115.291f); +path.cubicTo(635.092f, 115.079f, 634.991f, 114.724f, 634.313f, 114.724f); +path.lineTo(633.789f, 114.724f); +path.cubicTo(633.118f, 114.724f, 632.053f, 114.382f, 632.053f, 113.464f); +path.cubicTo(632.053f, 112.546f, 632.308f, 111.335f, 632.771f, 110.053f); +path.cubicTo(632.773f, 110.049f, 635.916f, 100.81f, 635.916f, 100.81f); +path.cubicTo(636.007f, 100.584f, 636.105f, 100.565f, 636.198f, 100.565f); +path.lineTo(647.46f, 100.565f); +path.cubicTo(647.598f, 100.565f, 647.627f, 100.586f, 647.704f, 100.737f); +path.cubicTo(647.704f, 100.741f, 652.565f, 113.554f, 652.565f, 113.554f); +path.cubicTo(652.641f, 113.754f, 652.679f, 113.942f, 652.679f, 114.105f); +path.cubicTo(652.679f, 114.228f, 652.657f, 114.338f, 652.613f, 114.43f); +path.cubicTo(652.544f, 114.574f, 652.447f, 114.619f, 652.409f, 114.632f); +path.cubicTo(651.94f, 114.749f, 651.868f, 115.062f, 651.868f, 115.238f); +path.cubicTo(651.868f, 115.767f, 652.46f, 115.797f, 653.44f, 115.845f); +path.lineTo(653.681f, 115.858f); +path.cubicTo(655.647f, 115.914f, 658.028f, 115.949f, 660.503f, 115.959f); +path.cubicTo(660.58f, 115.962f, 660.652f, 115.962f, 660.713f, 115.962f); +path.lineTo(660.787f, 115.96f); +path.cubicTo(661.162f, 115.962f, 661.54f, 115.962f, 661.918f, 115.962f); +path.cubicTo(662.139f, 115.962f, 662.33f, 115.947f, 662.496f, 115.918f); +path.lineTo(665.439f, 115.845f); +path.lineTo(666.633f, 115.806f); +path.lineTo(667.962f, 115.862f); +path.lineTo(670.719f, 115.963f); +path.cubicTo(671.579f, 115.963f, 671.76f, 115.598f, 671.76f, 115.292f); +path.cubicTo(671.76f, 115.08f, 671.659f, 114.725f, 670.981f, 114.725f); +path.lineTo(670.457f, 114.725f); +path.cubicTo(669.786f, 114.725f, 668.721f, 114.383f, 668.721f, 113.465f); +path.cubicTo(668.721f, 112.547f, 668.976f, 111.336f, 669.439f, 110.054f); +path.cubicTo(669.441f, 110.05f, 672.584f, 100.811f, 672.584f, 100.811f); +path.cubicTo(672.675f, 100.585f, 672.773f, 100.566f, 672.866f, 100.566f); +path.lineTo(684.128f, 100.566f); +path.cubicTo(684.266f, 100.566f, 684.295f, 100.587f, 684.372f, 100.739f); +path.cubicTo(684.372f, 100.743f, 689.233f, 113.556f, 689.233f, 113.556f); +path.cubicTo(689.309f, 113.756f, 689.347f, 113.944f, 689.347f, 114.107f); +path.cubicTo(689.347f, 114.23f, 689.325f, 114.34f, 689.281f, 114.431f); +path.cubicTo(689.212f, 114.575f, 689.115f, 114.621f, 689.077f, 114.633f); +path.cubicTo(688.608f, 114.75f, 688.536f, 115.063f, 688.536f, 115.239f); +path.cubicTo(688.536f, 115.768f, 689.128f, 115.798f, 690.108f, 115.846f); +path.lineTo(690.348f, 115.859f); +path.cubicTo(692.687f, 115.926f, 695.611f, 115.963f, 698.586f, 115.963f); +path.cubicTo(699.451f, 115.961f, 699.889f, 115.735f, 699.889f, 115.29f); +path.close(); +path.moveTo(600.18f, 98.176f); +path.cubicTo(600.181f, 98.171f, 600.185f, 98.158f, 600.185f, 98.158f); +path.cubicTo(600.191f, 98.142f, 604.801f, 84.049f, 604.801f, 84.049f); +path.cubicTo(604.865f, 83.857f, 604.915f, 83.756f, 604.951f, 83.697f); +path.cubicTo(604.987f, 83.756f, 605.037f, 83.858f, 605.102f, 84.051f); +path.cubicTo(605.103f, 84.054f, 610.08f, 98.149f, 610.08f, 98.149f); +path.cubicTo(610.079f, 98.145f, 610.079f, 98.145f, 610.079f, 98.147f); +path.cubicTo(610.079f, 98.149f, 610.081f, 98.164f, 610.083f, 98.176f); +path.lineTo(600.18f, 98.176f); +path.close(); +path.moveTo(636.849f, 98.176f); +path.cubicTo(636.851f, 98.171f, 636.854f, 98.158f, 636.854f, 98.158f); +path.cubicTo(636.859f, 98.142f, 641.469f, 84.049f, 641.469f, 84.049f); +path.cubicTo(641.533f, 83.857f, 641.584f, 83.756f, 641.62f, 83.697f); +path.cubicTo(641.656f, 83.756f, 641.706f, 83.857f, 641.771f, 84.051f); +path.cubicTo(641.773f, 84.054f, 646.749f, 98.149f, 646.749f, 98.149f); +path.cubicTo(646.749f, 98.145f, 646.748f, 98.145f, 646.748f, 98.147f); +path.cubicTo(646.748f, 98.15f, 646.75f, 98.165f, 646.751f, 98.176f); +path.lineTo(636.849f, 98.176f); +path.close(); +path.moveTo(673.517f, 98.176f); +path.cubicTo(673.519f, 98.171f, 673.522f, 98.158f, 673.522f, 98.158f); +path.cubicTo(673.528f, 98.142f, 678.138f, 84.049f, 678.138f, 84.049f); +path.cubicTo(678.202f, 83.857f, 678.252f, 83.756f, 678.288f, 83.697f); +path.cubicTo(678.324f, 83.756f, 678.375f, 83.858f, 678.439f, 84.051f); +path.cubicTo(678.44f, 84.054f, 683.417f, 98.149f, 683.417f, 98.149f); +path.cubicTo(683.416f, 98.145f, 683.416f, 98.145f, 683.416f, 98.147f); +path.cubicTo(683.416f, 98.149f, 683.418f, 98.164f, 683.42f, 98.176f); +path.lineTo(673.517f, 98.176f); +path.close(); +path.moveTo(702.086f, 115.256f); +path.lineTo(702.089f, 115.257f); +path.cubicTo(704.075f, 116.223f, 706.408f, 116.692f, 709.22f, 116.692f); +path.cubicTo(712.384f, 116.692f, 715.17f, 115.918f, 717.275f, 114.454f); +path.cubicTo(720.639f, 112.074f, 721.837f, 108.31f, 721.837f, 105.581f); +path.cubicTo(721.837f, 101.538f, 720.57f, 98.203f, 714.092f, 93.115f); +path.lineTo(712.625f, 91.966f); +path.cubicTo(707.567f, 87.838f, 706.331f, 86.155f, 706.331f, 83.396f); +path.cubicTo(706.331f, 79.927f, 708.819f, 77.595f, 712.519f, 77.595f); +path.cubicTo(716.925f, 77.595f, 718.41f, 79.437f, 718.843f, 80.229f); +path.cubicTo(719.131f, 80.754f, 719.442f, 82.122f, 719.494f, 82.739f); +path.cubicTo(719.543f, 83.13f, 719.608f, 83.65f, 720.167f, 83.65f); +path.cubicTo(720.789f, 83.65f, 720.789f, 82.704f, 720.789f, 82.195f); +path.cubicTo(720.789f, 79.413f, 720.918f, 77.758f, 720.973f, 77.052f); +path.lineTo(720.998f, 76.662f); +path.cubicTo(720.998f, 76.113f, 720.581f, 76.043f, 720.219f, 76.043f); +path.cubicTo(719.847f, 76.043f, 719.469f, 76.003f, 718.438f, 75.788f); +path.cubicTo(716.936f, 75.428f, 715.369f, 75.26f, 713.514f, 75.26f); +path.cubicTo(706.771f, 75.26f, 702.415f, 79.048f, 702.415f, 84.91f); +path.cubicTo(702.415f, 88.438f, 703.589f, 91.748f, 709.319f, 96.434f); +path.lineTo(711.676f, 98.365f); +path.cubicTo(716.332f, 102.196f, 717.607f, 104.172f, 717.607f, 107.564f); +path.cubicTo(717.607f, 110.888f, 715.121f, 114.253f, 710.371f, 114.253f); +path.cubicTo(708.428f, 114.253f, 703.808f, 113.754f, 702.868f, 109.132f); +path.cubicTo(702.716f, 108.424f, 702.716f, 107.718f, 702.716f, 107.25f); +path.cubicTo(702.716f, 106.94f, 702.716f, 106.422f, 702.094f, 106.422f); +path.cubicTo(701.519f, 106.422f, 701.455f, 107.116f, 701.421f, 107.488f); +path.cubicTo(701.421f, 107.495f, 701.375f, 108.117f, 701.375f, 108.117f); +path.cubicTo(701.293f, 109.171f, 701.158f, 110.933f, 701.158f, 113.044f); +path.cubicTo(701.16f, 114.432f, 701.197f, 114.813f, 702.086f, 115.256f); +path.close(); +path.moveTo(501.274f, 129.973f); +path.lineTo(498.058f, 136.963f); +path.lineTo(498.62f, 136.963f); +path.lineTo(499.723f, 134.561f); +path.lineTo(503.124f, 134.561f); +path.lineTo(504.227f, 136.963f); +path.lineTo(504.788f, 136.963f); +path.lineTo(501.571f, 129.973f); +path.lineTo(501.274f, 129.973f); +path.close(); +path.moveTo(499.926f, 134.114f); +path.lineTo(501.417f, 130.848f); +path.lineTo(502.918f, 134.114f); +path.lineTo(499.926f, 134.114f); +path.close(); +path.moveTo(510.117f, 130.023f); +path.lineTo(507.677f, 130.023f); +path.lineTo(507.677f, 136.963f); +path.lineTo(510.209f, 136.963f); +path.cubicTo(512.966f, 136.963f, 513.916f, 135.101f, 513.916f, 133.493f); +path.cubicTo(513.916f, 131.967f, 513.078f, 130.023f, 510.117f, 130.023f); +path.close(); +path.moveTo(510.178f, 136.515f); +path.lineTo(508.217f, 136.515f); +path.lineTo(508.217f, 130.471f); +path.lineTo(510.147f, 130.471f); +path.cubicTo(512.036f, 130.471f, 513.333f, 131.712f, 513.333f, 133.493f); +path.cubicTo(513.333f, 135.447f, 511.853f, 136.515f, 510.178f, 136.515f); +path.close(); +path.moveTo(519.105f, 135.894f); +path.lineTo(516.634f, 130.023f); +path.lineTo(516.093f, 130.023f); +path.lineTo(519.024f, 137.034f); +path.lineTo(519.177f, 137.034f); +path.lineTo(522.098f, 130.023f); +path.lineTo(521.557f, 130.023f); +path.lineTo(519.105f, 135.894f); +path.close(); +path.moveTo(526.817f, 129.973f); +path.lineTo(523.601f, 136.963f); +path.lineTo(524.162f, 136.963f); +path.lineTo(525.265f, 134.561f); +path.lineTo(528.665f, 134.561f); +path.lineTo(529.768f, 136.963f); +path.lineTo(530.33f, 136.963f); +path.lineTo(527.113f, 129.973f); +path.lineTo(526.817f, 129.973f); +path.close(); +path.moveTo(525.469f, 134.114f); +path.lineTo(526.96f, 130.848f); +path.lineTo(528.461f, 134.114f); +path.lineTo(525.469f, 134.114f); +path.close(); +path.moveTo(538.947f, 136.088f); +path.lineTo(533.668f, 130.024f); +path.lineTo(533.208f, 130.024f); +path.lineTo(533.208f, 136.964f); +path.lineTo(533.749f, 136.964f); +path.lineTo(533.749f, 130.899f); +path.lineTo(539.038f, 136.964f); +path.lineTo(539.487f, 136.964f); +path.lineTo(539.487f, 130.024f); +path.lineTo(538.946f, 130.024f); +path.lineTo(538.946f, 136.088f); +path.lineTo(538.947f, 136.088f); +path.close(); +path.moveTo(543.41f, 133.503f); +path.cubicTo(543.41f, 131.743f, 544.717f, 130.43f, 546.453f, 130.43f); +path.cubicTo(547.28f, 130.43f, 548.067f, 130.644f, 548.934f, 131.102f); +path.lineTo(548.934f, 130.512f); +path.cubicTo(548.087f, 130.125f, 547.26f, 129.932f, 546.483f, 129.932f); +path.cubicTo(544.574f, 129.932f, 542.828f, 131.377f, 542.828f, 133.503f); +path.cubicTo(542.828f, 135.538f, 544.411f, 137.054f, 546.534f, 137.054f); +path.cubicTo(547.341f, 137.054f, 548.198f, 136.83f, 549.005f, 136.402f); +path.lineTo(549.005f, 135.843f); +path.cubicTo(548.055f, 136.341f, 547.31f, 136.555f, 546.523f, 136.555f); +path.cubicTo(544.707f, 136.556f, 543.41f, 135.294f, 543.41f, 133.503f); +path.close(); +path.moveTo(552.079f, 136.963f); +path.lineTo(552.62f, 136.963f); +path.lineTo(552.62f, 130.023f); +path.lineTo(552.079f, 130.023f); +path.lineTo(552.079f, 136.963f); +path.close(); +path.moveTo(561.984f, 136.088f); +path.lineTo(556.705f, 130.024f); +path.lineTo(556.245f, 130.024f); +path.lineTo(556.245f, 136.964f); +path.lineTo(556.786f, 136.964f); +path.lineTo(556.786f, 130.899f); +path.lineTo(562.075f, 136.964f); +path.lineTo(562.524f, 136.964f); +path.lineTo(562.524f, 130.024f); +path.lineTo(561.983f, 130.024f); +path.lineTo(561.983f, 136.088f); +path.lineTo(561.984f, 136.088f); +path.close(); +path.moveTo(570.122f, 134.257f); +path.lineTo(571.592f, 134.257f); +path.lineTo(571.592f, 136.129f); +path.cubicTo(571.041f, 136.403f, 570.489f, 136.556f, 569.683f, 136.556f); +path.cubicTo(567.488f, 136.556f, 566.456f, 135.05f, 566.456f, 133.493f); +path.cubicTo(566.456f, 131.733f, 567.763f, 130.43f, 569.529f, 130.43f); +path.cubicTo(570.366f, 130.43f, 571.153f, 130.654f, 572, 131.132f); +path.lineTo(572, 130.522f); +path.cubicTo(571.02f, 130.084f, 570.397f, 129.932f, 569.61f, 129.932f); +path.cubicTo(567.455f, 129.932f, 565.873f, 131.438f, 565.873f, 133.473f); +path.cubicTo(565.873f, 135.529f, 567.496f, 137.055f, 569.661f, 137.055f); +path.cubicTo(570.468f, 137.055f, 571.305f, 136.862f, 572.132f, 136.465f); +path.lineTo(572.132f, 133.809f); +path.lineTo(570.121f, 133.809f); +path.lineTo(570.121f, 134.257f); +path.lineTo(570.122f, 134.257f); +path.close(); +path.moveTo(580.681f, 131.753f); +path.cubicTo(580.681f, 131.001f, 581.314f, 130.43f, 582.151f, 130.43f); +path.cubicTo(582.631f, 130.43f, 582.998f, 130.552f, 583.713f, 130.959f); +path.lineTo(583.713f, 130.37f); +path.cubicTo(583.172f, 130.075f, 582.641f, 129.933f, 582.099f, 129.933f); +path.cubicTo(580.996f, 129.933f, 580.098f, 130.747f, 580.098f, 131.775f); +path.cubicTo(580.098f, 134.065f, 583.417f, 133.2f, 583.417f, 135.173f); +path.cubicTo(583.417f, 135.956f, 582.774f, 136.557f, 581.947f, 136.557f); +path.cubicTo(581.406f, 136.557f, 580.966f, 136.374f, 580.191f, 135.814f); +path.lineTo(580.191f, 136.445f); +path.cubicTo(580.804f, 136.852f, 581.406f, 137.055f, 581.978f, 137.055f); +path.cubicTo(583.111f, 137.055f, 584, 136.221f, 584, 135.152f); +path.cubicTo(584, 132.72f, 580.681f, 133.605f, 580.681f, 131.753f); +path.close(); +path.moveTo(587.543f, 133.503f); +path.cubicTo(587.543f, 131.743f, 588.85f, 130.43f, 590.586f, 130.43f); +path.cubicTo(591.413f, 130.43f, 592.199f, 130.644f, 593.067f, 131.102f); +path.lineTo(593.067f, 130.512f); +path.cubicTo(592.22f, 130.125f, 591.393f, 129.932f, 590.616f, 129.932f); +path.cubicTo(588.707f, 129.932f, 586.96f, 131.377f, 586.96f, 133.503f); +path.cubicTo(586.96f, 135.538f, 588.543f, 137.054f, 590.667f, 137.054f); +path.cubicTo(591.473f, 137.054f, 592.331f, 136.83f, 593.138f, 136.402f); +path.lineTo(593.138f, 135.843f); +path.cubicTo(592.188f, 136.341f, 591.443f, 136.555f, 590.657f, 136.555f); +path.cubicTo(588.84f, 136.556f, 587.543f, 135.294f, 587.543f, 133.503f); +path.close(); +path.moveTo(596.212f, 136.963f); +path.lineTo(596.753f, 136.963f); +path.lineTo(596.753f, 130.023f); +path.lineTo(596.212f, 130.023f); +path.lineTo(596.212f, 136.963f); +path.close(); +path.moveTo(600.94f, 133.697f); +path.lineTo(604.116f, 133.697f); +path.lineTo(604.116f, 133.25f); +path.lineTo(600.94f, 133.25f); +path.lineTo(600.94f, 130.472f); +path.lineTo(604.198f, 130.472f); +path.lineTo(604.198f, 130.024f); +path.lineTo(600.399f, 130.024f); +path.lineTo(600.399f, 136.964f); +path.lineTo(604.279f, 136.964f); +path.lineTo(604.279f, 136.516f); +path.lineTo(600.94f, 136.516f); +path.lineTo(600.94f, 133.697f); +path.close(); +path.moveTo(613.163f, 136.088f); +path.lineTo(607.884f, 130.024f); +path.lineTo(607.424f, 130.024f); +path.lineTo(607.424f, 136.964f); +path.lineTo(607.965f, 136.964f); +path.lineTo(607.965f, 130.899f); +path.lineTo(613.254f, 136.964f); +path.lineTo(613.703f, 136.964f); +path.lineTo(613.703f, 130.024f); +path.lineTo(613.162f, 130.024f); +path.lineTo(613.162f, 136.088f); +path.lineTo(613.163f, 136.088f); +path.close(); +path.moveTo(617.625f, 133.503f); +path.cubicTo(617.625f, 131.743f, 618.932f, 130.43f, 620.668f, 130.43f); +path.cubicTo(621.495f, 130.43f, 622.281f, 130.644f, 623.149f, 131.102f); +path.lineTo(623.149f, 130.512f); +path.cubicTo(622.301f, 130.125f, 621.474f, 129.932f, 620.698f, 129.932f); +path.cubicTo(618.789f, 129.932f, 617.042f, 131.377f, 617.042f, 133.503f); +path.cubicTo(617.042f, 135.538f, 618.625f, 137.054f, 620.749f, 137.054f); +path.cubicTo(621.556f, 137.054f, 622.413f, 136.83f, 623.22f, 136.402f); +path.lineTo(623.22f, 135.843f); +path.cubicTo(622.27f, 136.341f, 621.525f, 136.555f, 620.739f, 136.555f); +path.cubicTo(618.922f, 136.556f, 617.625f, 135.294f, 617.625f, 133.503f); +path.close(); +path.moveTo(626.856f, 133.697f); +path.lineTo(630.032f, 133.697f); +path.lineTo(630.032f, 133.25f); +path.lineTo(626.856f, 133.25f); +path.lineTo(626.856f, 130.472f); +path.lineTo(630.114f, 130.472f); +path.lineTo(630.114f, 130.024f); +path.lineTo(626.316f, 130.024f); +path.lineTo(626.316f, 136.964f); +path.lineTo(630.196f, 136.964f); +path.lineTo(630.196f, 136.516f); +path.lineTo(626.857f, 136.516f); +path.lineTo(626.857f, 133.697f); +path.lineTo(626.856f, 133.697f); +path.close(); +path.moveTo(633.115f, 136.963f); +path.lineTo(633.33f, 136.963f); +path.cubicTo(633.33f, 137.309f, 633.299f, 137.523f, 633.115f, 137.797f); +path.lineTo(633.115f, 138.154f); +path.cubicTo(633.565f, 137.828f, 633.779f, 137.269f, 633.779f, 136.75f); +path.lineTo(633.779f, 135.976f); +path.lineTo(633.115f, 135.976f); +path.lineTo(633.115f, 136.963f); +path.close(); +path.moveTo(641.511f, 131.753f); +path.cubicTo(641.511f, 131.001f, 642.144f, 130.43f, 642.981f, 130.43f); +path.cubicTo(643.461f, 130.43f, 643.828f, 130.552f, 644.544f, 130.959f); +path.lineTo(644.544f, 130.37f); +path.cubicTo(644.003f, 130.075f, 643.472f, 129.933f, 642.931f, 129.933f); +path.cubicTo(641.828f, 129.933f, 640.929f, 130.747f, 640.929f, 131.775f); +path.cubicTo(640.929f, 134.065f, 644.247f, 133.2f, 644.247f, 135.173f); +path.cubicTo(644.247f, 135.956f, 643.604f, 136.557f, 642.777f, 136.557f); +path.cubicTo(642.236f, 136.557f, 641.797f, 136.374f, 641.02f, 135.814f); +path.lineTo(641.02f, 136.445f); +path.cubicTo(641.633f, 136.852f, 642.235f, 137.055f, 642.807f, 137.055f); +path.cubicTo(643.941f, 137.055f, 644.829f, 136.221f, 644.829f, 135.152f); +path.cubicTo(644.829f, 132.72f, 641.511f, 133.605f, 641.511f, 131.753f); +path.close(); +path.moveTo(648.649f, 133.697f); +path.lineTo(651.824f, 133.697f); +path.lineTo(651.824f, 133.25f); +path.lineTo(648.649f, 133.25f); +path.lineTo(648.649f, 130.472f); +path.lineTo(651.906f, 130.472f); +path.lineTo(651.906f, 130.024f); +path.lineTo(648.107f, 130.024f); +path.lineTo(648.107f, 136.964f); +path.lineTo(651.988f, 136.964f); +path.lineTo(651.988f, 136.516f); +path.lineTo(648.649f, 136.516f); +path.lineTo(648.649f, 133.697f); +path.close(); +path.moveTo(657.91f, 133.503f); +path.cubicTo(658.799f, 133.177f, 659.237f, 132.618f, 659.237f, 131.814f); +path.cubicTo(659.237f, 130.888f, 658.635f, 130.023f, 656.929f, 130.023f); +path.lineTo(655.142f, 130.023f); +path.lineTo(655.142f, 136.963f); +path.lineTo(655.683f, 136.963f); +path.lineTo(655.683f, 133.635f); +path.lineTo(656.623f, 133.635f); +path.cubicTo(657.46f, 133.635f, 657.726f, 133.747f, 658.563f, 135.06f); +path.lineTo(659.778f, 136.963f); +path.lineTo(660.411f, 136.963f); +path.lineTo(658.838f, 134.561f); +path.cubicTo(658.513f, 134.073f, 658.237f, 133.676f, 657.91f, 133.503f); +path.close(); +path.moveTo(656.838f, 133.188f); +path.lineTo(655.684f, 133.188f); +path.lineTo(655.684f, 130.472f); +path.lineTo(656.817f, 130.472f); +path.cubicTo(658.094f, 130.472f, 658.655f, 131.041f, 658.655f, 131.825f); +path.cubicTo(658.655f, 132.659f, 658.063f, 133.188f, 656.838f, 133.188f); +path.close(); +path.moveTo(664.968f, 135.894f); +path.lineTo(662.497f, 130.023f); +path.lineTo(661.956f, 130.023f); +path.lineTo(664.887f, 137.034f); +path.lineTo(665.04f, 137.034f); +path.lineTo(667.961f, 130.023f); +path.lineTo(667.42f, 130.023f); +path.lineTo(664.968f, 135.894f); +path.close(); +path.moveTo(670.829f, 136.963f); +path.lineTo(671.37f, 136.963f); +path.lineTo(671.37f, 130.023f); +path.lineTo(670.829f, 130.023f); +path.lineTo(670.829f, 136.963f); +path.close(); +path.moveTo(680.734f, 136.088f); +path.lineTo(675.455f, 130.024f); +path.lineTo(674.995f, 130.024f); +path.lineTo(674.995f, 136.964f); +path.lineTo(675.536f, 136.964f); +path.lineTo(675.536f, 130.899f); +path.lineTo(680.826f, 136.964f); +path.lineTo(681.275f, 136.964f); +path.lineTo(681.275f, 130.024f); +path.lineTo(680.733f, 130.024f); +path.lineTo(680.733f, 136.088f); +path.lineTo(680.734f, 136.088f); +path.close(); +path.moveTo(688.873f, 134.257f); +path.lineTo(690.343f, 134.257f); +path.lineTo(690.343f, 136.129f); +path.cubicTo(689.791f, 136.403f, 689.24f, 136.556f, 688.433f, 136.556f); +path.cubicTo(686.238f, 136.556f, 685.206f, 135.05f, 685.206f, 133.493f); +path.cubicTo(685.206f, 131.733f, 686.514f, 130.43f, 688.28f, 130.43f); +path.cubicTo(689.117f, 130.43f, 689.903f, 130.654f, 690.751f, 131.132f); +path.lineTo(690.751f, 130.522f); +path.cubicTo(689.771f, 130.084f, 689.148f, 129.932f, 688.362f, 129.932f); +path.cubicTo(686.207f, 129.932f, 684.624f, 131.438f, 684.624f, 133.473f); +path.cubicTo(684.624f, 135.529f, 686.247f, 137.055f, 688.413f, 137.055f); +path.cubicTo(689.219f, 137.055f, 690.057f, 136.862f, 690.884f, 136.465f); +path.lineTo(690.884f, 133.809f); +path.lineTo(688.873f, 133.809f); +path.lineTo(688.873f, 134.257f); +path.close(); +path.moveTo(699.432f, 131.753f); +path.cubicTo(699.432f, 131.001f, 700.065f, 130.43f, 700.902f, 130.43f); +path.cubicTo(701.381f, 130.43f, 701.749f, 130.552f, 702.464f, 130.959f); +path.lineTo(702.464f, 130.37f); +path.cubicTo(701.923f, 130.075f, 701.391f, 129.933f, 700.85f, 129.933f); +path.cubicTo(699.747f, 129.933f, 698.849f, 130.747f, 698.849f, 131.775f); +path.cubicTo(698.849f, 134.065f, 702.168f, 133.2f, 702.168f, 135.173f); +path.cubicTo(702.168f, 135.956f, 701.525f, 136.557f, 700.698f, 136.557f); +path.cubicTo(700.156f, 136.557f, 699.718f, 136.374f, 698.941f, 135.814f); +path.lineTo(698.941f, 136.445f); +path.cubicTo(699.553f, 136.852f, 700.156f, 137.055f, 700.728f, 137.055f); +path.cubicTo(701.862f, 137.055f, 702.75f, 136.221f, 702.75f, 135.152f); +path.cubicTo(702.75f, 132.72f, 699.432f, 133.605f, 699.432f, 131.753f); +path.close(); +path.moveTo(709.407f, 129.932f); +path.cubicTo(707.345f, 129.932f, 705.731f, 131.499f, 705.731f, 133.493f); +path.cubicTo(705.731f, 135.488f, 707.345f, 137.054f, 709.407f, 137.054f); +path.cubicTo(711.48f, 137.054f, 713.104f, 135.487f, 713.104f, 133.493f); +path.cubicTo(713.104f, 131.499f, 711.481f, 129.932f, 709.407f, 129.932f); +path.close(); +path.moveTo(709.428f, 136.556f); +path.cubicTo(707.683f, 136.556f, 706.314f, 135.223f, 706.314f, 133.493f); +path.cubicTo(706.314f, 131.763f, 707.682f, 130.43f, 709.428f, 130.43f); +path.cubicTo(711.175f, 130.43f, 712.523f, 131.763f, 712.523f, 133.493f); +path.cubicTo(712.522f, 135.223f, 711.175f, 136.556f, 709.428f, 136.556f); +path.close(); +path.moveTo(716.739f, 133.503f); +path.cubicTo(716.739f, 131.743f, 718.046f, 130.43f, 719.782f, 130.43f); +path.cubicTo(720.609f, 130.43f, 721.395f, 130.644f, 722.264f, 131.102f); +path.lineTo(722.264f, 130.512f); +path.cubicTo(721.416f, 130.125f, 720.589f, 129.932f, 719.813f, 129.932f); +path.cubicTo(717.903f, 129.932f, 716.157f, 131.377f, 716.157f, 133.503f); +path.cubicTo(716.157f, 135.538f, 717.74f, 137.054f, 719.863f, 137.054f); +path.cubicTo(720.67f, 137.054f, 721.528f, 136.83f, 722.335f, 136.402f); +path.lineTo(722.335f, 135.843f); +path.cubicTo(721.385f, 136.341f, 720.64f, 136.555f, 719.854f, 136.555f); +path.cubicTo(718.037f, 136.556f, 716.739f, 135.294f, 716.739f, 133.503f); +path.close(); +path.moveTo(725.409f, 136.963f); +path.lineTo(725.95f, 136.963f); +path.lineTo(725.95f, 130.023f); +path.lineTo(725.409f, 130.023f); +path.lineTo(725.409f, 136.963f); +path.close(); +path.moveTo(730.136f, 133.697f); +path.lineTo(733.312f, 133.697f); +path.lineTo(733.312f, 133.25f); +path.lineTo(730.136f, 133.25f); +path.lineTo(730.136f, 130.472f); +path.lineTo(733.393f, 130.472f); +path.lineTo(733.393f, 130.024f); +path.lineTo(729.595f, 130.024f); +path.lineTo(729.595f, 136.964f); +path.lineTo(733.475f, 136.964f); +path.lineTo(733.475f, 136.516f); +path.lineTo(730.136f, 136.516f); +path.lineTo(730.136f, 133.697f); +path.close(); +path.moveTo(735.865f, 130.472f); +path.lineTo(738.479f, 130.472f); +path.lineTo(738.479f, 136.964f); +path.lineTo(739.02f, 136.964f); +path.lineTo(739.02f, 130.472f); +path.lineTo(741.634f, 130.472f); +path.lineTo(741.634f, 130.024f); +path.lineTo(735.865f, 130.024f); +path.lineTo(735.865f, 130.472f); +path.close(); +path.moveTo(749.017f, 130.023f); +path.lineTo(746.699f, 132.771f); +path.lineTo(744.371f, 130.023f); +path.lineTo(743.717f, 130.023f); +path.lineTo(746.423f, 133.239f); +path.lineTo(746.423f, 136.963f); +path.lineTo(746.964f, 136.963f); +path.lineTo(746.964f, 133.239f); +path.lineTo(749.67f, 130.023f); +path.lineTo(749.017f, 130.023f); +path.close(); +path.moveTo(578.773f, 115.549f); +path.lineTo(578.773f, 62.773f); +path.lineTo(557.571f, 62.773f); +path.cubicTo(562.752f, 67.658f, 575.797f, 106.652f, 578.773f, 115.549f); +path.close(); +path.moveTo(544.754f, 62.861f); +path.lineTo(524.496f, 62.861f); +path.lineTo(524.496f, 115.55f); +path.lineTo(524.934f, 115.55f); +path.lineTo(544.544f, 64.169f); +path.lineTo(528.157f, 115.549f); +path.lineTo(528.682f, 115.549f); +path.lineTo(546.557f, 67.658f); +path.lineTo(531.675f, 115.548f); +path.lineTo(532.376f, 115.548f); +path.lineTo(548.57f, 71.846f); +path.lineTo(535.293f, 115.507f); +path.lineTo(535.906f, 115.507f); +path.lineTo(544.281f, 97.58f); +path.lineTo(542.267f, 97.58f); +path.lineTo(550.409f, 76.033f); +path.lineTo(544.281f, 97.578f); +path.lineTo(546.119f, 97.578f); +path.lineTo(538.678f, 115.551f); +path.lineTo(565.029f, 115.551f); +path.cubicTo(562.052f, 106.477f, 550.759f, 67.92f, 544.754f, 62.861f); +path.close(); + SkPath path2(path); + testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename); +} + +static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0; static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0; +static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0; static struct TestDesc tests[] = { + TEST(skpwww_gorcraft_ru_1), + TEST(http___www_project2061_org), + TEST(http___www_emuleteca_cl_27), + TEST(http___www_emuleteca_cl_26), + TEST(http___www_emuleteca_cl_28), + TEST(skpwww_nimble_com_au_1), + TEST(skpwww_mybuilder_com_1), + TEST(skpwww_neda_net_2), + TEST(skpwww_woothemes_com_1), + TEST(skpwww_neda_net_1), + TEST(skpwww_tinytots_com_1), + TEST(skpwww_educationalcraft_com_4a), TEST(skpwww_lptemp_com_3), TEST(skpwww_shinydemos_com_5), -#if TEST_NEW_FAILURES TEST(skpwww_lptemp_com_5), TEST(skpwww_shinydemos_com_15), TEST(skpwww_familysurvivalprotocol_wordpress_com_61), TEST(skpwww_alamdi_com_3), TEST(skpwww_devbridge_com_22), -#endif TEST(skpwww_firstunitedbank_com_19), TEST(skpwww_googleventures_com_32), TEST(skpwww_9to5mac_com_64), @@ -3942,11 +4764,10 @@ static const size_t testCount = SK_ARRAY_COUNT(tests); static bool runReverse = false; -static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0; DEF_TEST(PathOpsSkp, reporter) { #if DEBUG_SHOW_TEST_NAME strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH); #endif - RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse); + RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse); }
diff --git a/src/third_party/skia/tests/PathOpsTSectDebug.h b/src/third_party/skia/tests/PathOpsTSectDebug.h new file mode 100644 index 0000000..20915210 --- /dev/null +++ b/src/third_party/skia/tests/PathOpsTSectDebug.h
@@ -0,0 +1,224 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef PathOpsTSectDebug_DEFINED +#define PathOpsTSectDebug_DEFINED + +#include "SkPathOpsTSect.h" + +template<typename TCurve, typename OppCurve> +char SkTCoincident<TCurve, OppCurve>::dumpIsCoincidentStr() const { + if (!!fMatch != fMatch) { + return '?'; + } + return fMatch ? '*' : 0; +} + +template<typename TCurve, typename OppCurve> +void SkTCoincident<TCurve, OppCurve>::dump() const { + SkDebugf("t=%1.9g pt=(%1.9g,%1.9g)%s\n", fPerpT, fPerpPt.fX, fPerpPt.fY, + fMatch ? " match" : ""); +} + +template<typename TCurve, typename OppCurve> +const SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::debugSpan(int id) const { + const SkTSpan<TCurve, OppCurve>* test = fHead; + do { + if (test->debugID() == id) { + return test; + } + } while ((test = test->next())); + return nullptr; +} + +template<typename TCurve, typename OppCurve> +const SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::debugT(double t) const { + const SkTSpan<TCurve, OppCurve>* test = fHead; + const SkTSpan<TCurve, OppCurve>* closest = nullptr; + double bestDist = DBL_MAX; + do { + if (between(test->fStartT, t, test->fEndT)) { + return test; + } + double testDist = SkTMin(fabs(test->fStartT - t), fabs(test->fEndT - t)); + if (bestDist > testDist) { + bestDist = testDist; + closest = test; + } + } while ((test = test->next())); + SkASSERT(closest); + return closest; +} + +template<typename TCurve, typename OppCurve> +void SkTSect<TCurve, OppCurve>::dump() const { + dumpCommon(fHead); +} + +extern int gDumpTSectNum; + +template<typename TCurve, typename OppCurve> +void SkTSect<TCurve, OppCurve>::dumpBoth(SkTSect<OppCurve, TCurve>* opp) const { +#if DEBUG_T_SECT_DUMP <= 2 +#if DEBUG_T_SECT_DUMP == 2 + SkDebugf("%d ", ++gDumpTSectNum); +#endif + this->dump(); + SkDebugf(" "); + opp->dump(); + SkDebugf("\n"); +#elif DEBUG_T_SECT_DUMP == 3 + SkDebugf("<div id=\"sect%d\">\n", ++gDumpTSectNum); + if (this->fHead) { + this->dumpCurves(); + } + if (opp->fHead) { + opp->dumpCurves(); + } + SkDebugf("</div>\n\n"); +#endif +} + +template<typename TCurve, typename OppCurve> +void SkTSect<TCurve, OppCurve>::dumpBounded(int id) const { + const SkTSpan<TCurve, OppCurve>* bounded = debugSpan(id); + if (!bounded) { + SkDebugf("no span matches %d\n", id); + return; + } + const SkTSpan<OppCurve, TCurve>* test = bounded->debugOpp()->fHead; + do { + if (test->findOppSpan(bounded)) { + test->dump(); + SkDebugf(" "); + } + } while ((test = test->next())); + SkDebugf("\n"); +} + +template<typename TCurve, typename OppCurve> +void SkTSect<TCurve, OppCurve>::dumpBounds() const { + const SkTSpan<TCurve, OppCurve>* test = fHead; + do { + test->dumpBounds(); + } while ((test = test->next())); +} + +template<typename TCurve, typename OppCurve> +void SkTSect<TCurve, OppCurve>::dumpCoin() const { + dumpCommon(fCoincident); +} + +template<typename TCurve, typename OppCurve> +void SkTSect<TCurve, OppCurve>::dumpCoinCurves() const { + dumpCommonCurves(fCoincident); +} + +template<typename TCurve, typename OppCurve> +void SkTSect<TCurve, OppCurve>::dumpCommon(const SkTSpan<TCurve, OppCurve>* test) const { + SkDebugf("id=%d", debugID()); + if (!test) { + SkDebugf(" (empty)"); + return; + } + do { + SkDebugf(" "); + test->dump(); + } while ((test = test->next())); +} + +template<typename TCurve, typename OppCurve> +void SkTSect<TCurve, OppCurve>::dumpCommonCurves(const SkTSpan<TCurve, OppCurve>* test) const { + do { + test->fPart.dumpID(test->debugID()); + } while ((test = test->next())); +} + +template<typename TCurve, typename OppCurve> +void SkTSect<TCurve, OppCurve>::dumpCurves() const { + dumpCommonCurves(fHead); +} + +template<typename TCurve, typename OppCurve> +const SkTSpan<TCurve, OppCurve>* SkTSpan<TCurve, OppCurve>::debugSpan(int id) const { + return SkDEBUGRELEASE(fDebugSect->debugSpan(id), nullptr); +} + +template<typename TCurve, typename OppCurve> +const SkTSpan<TCurve, OppCurve>* SkTSpan<TCurve, OppCurve>::debugT(double t) const { + return SkDEBUGRELEASE(fDebugSect->debugT(t), nullptr); +} + +template<typename TCurve, typename OppCurve> +void SkTSpan<TCurve, OppCurve>::dumpAll() const { + dumpID(); + SkDebugf("=(%g,%g) [", fStartT, fEndT); + const SkTSpanBounded<OppCurve, TCurve>* testBounded = fBounded; + while (testBounded) { + const SkTSpan<OppCurve, TCurve>* span = testBounded->fBounded; + const SkTSpanBounded<OppCurve, TCurve>* next = testBounded->fNext; + span->dumpID(); + SkDebugf("=(%g,%g)", span->fStartT, span->fEndT); + if (next) { + SkDebugf(" "); + } + testBounded = next; + } + SkDebugf("]\n"); +} + +template<typename TCurve, typename OppCurve> +void SkTSpan<TCurve, OppCurve>::dump() const { + dumpID(); + SkDebugf("=(%g,%g) [", fStartT, fEndT); + const SkTSpanBounded<OppCurve, TCurve>* testBounded = fBounded; + while (testBounded) { + const SkTSpan<OppCurve, TCurve>* span = testBounded->fBounded; + const SkTSpanBounded<OppCurve, TCurve>* next = testBounded->fNext; + span->dumpID(); + if (next) { + SkDebugf(","); + } + testBounded = next; + } + SkDebugf("]"); +} + +template<typename TCurve, typename OppCurve> +void SkTSpan<TCurve, OppCurve>::dumpBounded(int id) const { + SkDEBUGCODE(fDebugSect->dumpBounded(id)); +} + +template<typename TCurve, typename OppCurve> +void SkTSpan<TCurve, OppCurve>::dumpBounds() const { + dumpID(); + SkDebugf(" bounds=(%1.9g,%1.9g, %1.9g,%1.9g) boundsMax=%1.9g%s\n", + fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom, fBoundsMax, + fCollapsed ? " collapsed" : ""); +} + +template<typename TCurve, typename OppCurve> +void SkTSpan<TCurve, OppCurve>::dumpCoin() const { + dumpID(); + SkDebugf(" coinStart "); + fCoinStart.dump(); + SkDebugf(" coinEnd "); + fCoinEnd.dump(); +} + +template<typename TCurve, typename OppCurve> +void SkTSpan<TCurve, OppCurve>::dumpID() const { + char cS = fCoinStart.dumpIsCoincidentStr(); + if (cS) { + SkDebugf("%c", cS); + } + SkDebugf("%d", debugID()); + char cE = fCoinEnd.dumpIsCoincidentStr(); + if (cE) { + SkDebugf("%c", cE); + } +} +#endif // PathOpsTSectDebug_DEFINED
diff --git a/src/third_party/skia/tests/PathOpsTestCommon.cpp b/src/third_party/skia/tests/PathOpsTestCommon.cpp index 60a12ee..8cc8fe8 100644 --- a/src/third_party/skia/tests/PathOpsTestCommon.cpp +++ b/src/third_party/skia/tests/PathOpsTestCommon.cpp
@@ -6,14 +6,133 @@ */ #include "PathOpsTestCommon.h" #include "SkPathOpsBounds.h" +#include "SkPathOpsConic.h" #include "SkPathOpsCubic.h" #include "SkPathOpsLine.h" #include "SkPathOpsQuad.h" -#include "SkPathOpsTriangle.h" +#include "SkReduceOrder.h" +#include "SkTSort.h" + +static double calc_t_div(const SkDCubic& cubic, double precision, double start) { + const double adjust = sqrt(3.) / 36; + SkDCubic sub; + const SkDCubic* cPtr; + if (start == 0) { + cPtr = &cubic; + } else { + // OPTIMIZE: special-case half-split ? + sub = cubic.subDivide(start, 1); + cPtr = ⊂ + } + const SkDCubic& c = *cPtr; + double dx = c[3].fX - 3 * (c[2].fX - c[1].fX) - c[0].fX; + double dy = c[3].fY - 3 * (c[2].fY - c[1].fY) - c[0].fY; + double dist = sqrt(dx * dx + dy * dy); + double tDiv3 = precision / (adjust * dist); + double t = SkDCubeRoot(tDiv3); + if (start > 0) { + t = start + (1 - start) * t; + } + return t; +} + +static bool add_simple_ts(const SkDCubic& cubic, double precision, SkTArray<double, true>* ts) { + double tDiv = calc_t_div(cubic, precision, 0); + if (tDiv >= 1) { + return true; + } + if (tDiv >= 0.5) { + ts->push_back(0.5); + return true; + } + return false; +} + +static void addTs(const SkDCubic& cubic, double precision, double start, double end, + SkTArray<double, true>* ts) { + double tDiv = calc_t_div(cubic, precision, 0); + double parts = ceil(1.0 / tDiv); + for (double index = 0; index < parts; ++index) { + double newT = start + (index / parts) * (end - start); + if (newT > 0 && newT < 1) { + ts->push_back(newT); + } + } +} + +static void toQuadraticTs(const SkDCubic* cubic, double precision, SkTArray<double, true>* ts) { + SkReduceOrder reducer; + int order = reducer.reduce(*cubic, SkReduceOrder::kAllow_Quadratics); + if (order < 3) { + return; + } + double inflectT[5]; + int inflections = cubic->findInflections(inflectT); + SkASSERT(inflections <= 2); + if (!cubic->endsAreExtremaInXOrY()) { + inflections += cubic->findMaxCurvature(&inflectT[inflections]); + SkASSERT(inflections <= 5); + } + SkTQSort<double>(inflectT, &inflectT[inflections - 1]); + // OPTIMIZATION: is this filtering common enough that it needs to be pulled out into its + // own subroutine? + while (inflections && approximately_less_than_zero(inflectT[0])) { + memmove(inflectT, &inflectT[1], sizeof(inflectT[0]) * --inflections); + } + int start = 0; + int next = 1; + while (next < inflections) { + if (!approximately_equal(inflectT[start], inflectT[next])) { + ++start; + ++next; + continue; + } + memmove(&inflectT[start], &inflectT[next], sizeof(inflectT[0]) * (--inflections - start)); + } + + while (inflections && approximately_greater_than_one(inflectT[inflections - 1])) { + --inflections; + } + SkDCubicPair pair; + if (inflections == 1) { + pair = cubic->chopAt(inflectT[0]); + int orderP1 = reducer.reduce(pair.first(), SkReduceOrder::kNo_Quadratics); + if (orderP1 < 2) { + --inflections; + } else { + int orderP2 = reducer.reduce(pair.second(), SkReduceOrder::kNo_Quadratics); + if (orderP2 < 2) { + --inflections; + } + } + } + if (inflections == 0 && add_simple_ts(*cubic, precision, ts)) { + return; + } + if (inflections == 1) { + pair = cubic->chopAt(inflectT[0]); + addTs(pair.first(), precision, 0, inflectT[0], ts); + addTs(pair.second(), precision, inflectT[0], 1, ts); + return; + } + if (inflections > 1) { + SkDCubic part = cubic->subDivide(0, inflectT[0]); + addTs(part, precision, 0, inflectT[0], ts); + int last = inflections - 1; + for (int idx = 0; idx < last; ++idx) { + part = cubic->subDivide(inflectT[idx], inflectT[idx + 1]); + addTs(part, precision, inflectT[idx], inflectT[idx + 1], ts); + } + part = cubic->subDivide(inflectT[last], 1); + addTs(part, precision, inflectT[last], 1, ts); + return; + } + addTs(*cubic, precision, 0, 1, ts); +} void CubicToQuads(const SkDCubic& cubic, double precision, SkTArray<SkDQuad, true>& quads) { SkTArray<double, true> ts; - cubic.toQuadraticTs(precision, &ts); + toQuadraticTs(&cubic, precision, &ts); if (ts.count() <= 0) { SkDQuad quad = cubic.toQuad(); quads.push_back(quad); @@ -22,8 +141,20 @@ double tStart = 0; for (int i1 = 0; i1 <= ts.count(); ++i1) { const double tEnd = i1 < ts.count() ? ts[i1] : 1; + SkDRect bounds; + bounds.setBounds(cubic); SkDCubic part = cubic.subDivide(tStart, tEnd); SkDQuad quad = part.toQuad(); + if (quad[1].fX < bounds.fLeft) { + quad[1].fX = bounds.fLeft; + } else if (quad[1].fX > bounds.fRight) { + quad[1].fX = bounds.fRight; + } + if (quad[1].fY < bounds.fTop) { + quad[1].fY = bounds.fTop; + } else if (quad[1].fY > bounds.fBottom) { + quad[1].fY = bounds.fBottom; + } quads.push_back(quad); tStart = tEnd; } @@ -106,7 +237,7 @@ lo = hi; } break; - } + } case SkPath::kClose_Verb: simplePath->close(); break; @@ -134,6 +265,18 @@ return !SkScalarIsNaN(bounds.fBottom); } +bool ValidConic(const SkDConic& conic) { + for (int index = 0; index < SkDConic::kPointCount; ++index) { + if (!ValidPoint(conic[index])) { + return false; + } + } + if (SkDoubleIsNaN(conic.fWeight)) { + return false; + } + return true; +} + bool ValidCubic(const SkDCubic& cubic) { for (int index = 0; index < 4; ++index) { if (!ValidPoint(cubic[index])) { @@ -180,15 +323,6 @@ return true; } -bool ValidTriangle(const SkDTriangle& triangle) { - for (int index = 0; index < 3; ++index) { - if (!ValidPoint(triangle.fPts[index])) { - return false; - } - } - return true; -} - bool ValidVector(const SkDVector& v) { if (SkDoubleIsNaN(v.fX)) { return false;
diff --git a/src/third_party/skia/tests/PathOpsTestCommon.h b/src/third_party/skia/tests/PathOpsTestCommon.h index 0c42bfb..ba64d93 100644 --- a/src/third_party/skia/tests/PathOpsTestCommon.h +++ b/src/third_party/skia/tests/PathOpsTestCommon.h
@@ -12,16 +12,31 @@ struct SkPathOpsBounds; +struct QuadPts { + static const int kPointCount = 3; + SkDPoint fPts[kPointCount]; +}; + +struct ConicPts { + QuadPts fPts; + SkScalar fWeight; +}; + +struct CubicPts { + static const int kPointCount = 4; + SkDPoint fPts[kPointCount]; +}; + void CubicPathToQuads(const SkPath& cubicPath, SkPath* quadPath); void CubicPathToSimple(const SkPath& cubicPath, SkPath* simplePath); void CubicToQuads(const SkDCubic& cubic, double precision, SkTArray<SkDQuad, true>& quads); -bool ValidBounds(const SkPathOpsBounds&); +bool ValidBounds(const SkPathOpsBounds& ); +bool ValidConic(const SkDConic& cubic); bool ValidCubic(const SkDCubic& cubic); bool ValidLine(const SkDLine& line); bool ValidPoint(const SkDPoint& pt); bool ValidPoints(const SkPoint* pts, int count); bool ValidQuad(const SkDQuad& quad); -bool ValidTriangle(const SkDTriangle& triangle); bool ValidVector(const SkDVector& v); #endif
diff --git a/src/third_party/skia/tests/PathOpsThreadedCommon.cpp b/src/third_party/skia/tests/PathOpsThreadedCommon.cpp index 0adde91..a1a65b7 100644 --- a/src/third_party/skia/tests/PathOpsThreadedCommon.cpp +++ b/src/third_party/skia/tests/PathOpsThreadedCommon.cpp
@@ -11,13 +11,12 @@ PathOpsThreadedTestRunner::~PathOpsThreadedTestRunner() { for (int index = 0; index < fRunnables.count(); index++) { - SkDELETE(fRunnables[index]); + delete fRunnables[index]; } } void PathOpsThreadedTestRunner::render() { - SkTaskGroup tg; - for (int index = 0; index < fRunnables.count(); ++ index) { - tg.add(fRunnables[index]); - } + SkTaskGroup().batch(fRunnables.count(), [&](int i) { + (*fRunnables[i])(); + }); }
diff --git a/src/third_party/skia/tests/PathOpsThreadedCommon.h b/src/third_party/skia/tests/PathOpsThreadedCommon.h index 124921e..706da6b 100644 --- a/src/third_party/skia/tests/PathOpsThreadedCommon.h +++ b/src/third_party/skia/tests/PathOpsThreadedCommon.h
@@ -7,10 +7,14 @@ #ifndef PathOpsThreadedCommon_DEFINED #define PathOpsThreadedCommon_DEFINED +#include "SkBitmap.h" #include "SkGraphics.h" -#include "SkRunnable.h" +#include "SkPath.h" +#include "SkPathOps.h" #include "SkTDArray.h" +#include <string> + #define PATH_STR_SIZE 512 class PathOpsThreadedRunnable; @@ -24,11 +28,14 @@ unsigned char fB; unsigned char fC; unsigned char fD; - char* fPathStr; + std::string fPathStr; const char* fKey; char fSerialNo[256]; skiatest::Reporter* fReporter; SkBitmap* fBitmap; + + void outputProgress(const char* pathStr, SkPath::FillType); + void outputProgress(const char* pathStr, SkPathOp); }; class PathOpsThreadedTestRunner { @@ -44,7 +51,7 @@ skiatest::Reporter* fReporter; }; -class PathOpsThreadedRunnable : public SkRunnable { +class PathOpsThreadedRunnable { public: PathOpsThreadedRunnable(void (*testFun)(PathOpsThreadState*), int a, int b, int c, int d, PathOpsThreadedTestRunner* runner) { @@ -73,11 +80,9 @@ fTestFun = testFun; } - virtual void run() SK_OVERRIDE { + void operator()() { SkBitmap bitmap; fState.fBitmap = &bitmap; - char pathStr[PATH_STR_SIZE]; - fState.fPathStr = pathStr; SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024); (*fTestFun)(&fState); }
diff --git a/src/third_party/skia/tests/PathOpsThreeWayTest.cpp b/src/third_party/skia/tests/PathOpsThreeWayTest.cpp new file mode 100644 index 0000000..b86ff65 --- /dev/null +++ b/src/third_party/skia/tests/PathOpsThreeWayTest.cpp
@@ -0,0 +1,84 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "PathOpsTestCommon.h" +#include "SkIntersections.h" +#include "SkTDArray.h" +#include "Test.h" + +// check intersections for consistency + +struct Curve { + int ptCount; + CubicPts curve; // largest can hold lines / quads/ cubics +}; + +static const Curve testSet0[] = { // extracted from skpClip2 + {4, {{{134,11414}, {131.990234,11414}, {130.32666,11415.4824}, {130.042755,11417.4131}}} }, + {4, {{{130.042755,11417.4131}, {130.233124,11418.3193}, {131.037079,11419}, {132,11419}}} }, + {4, {{{132,11419}, {130.895432,11419}, {130,11418.1045}, {130,11417}}} }, +}; + +static const Curve testSet1[] = { // extracted from cubicOp85i + {4, {{{3,4}, {1,5}, {4,3}, {6,4}}} }, + {1, {{{6,4}, {3,4}}} }, + {4, {{{3,4}, {4,6}, {4,3}, {5,1}}} }, + {1, {{{5,1}, {3,4}}} }, +}; + +static const struct TestSet { + const Curve* tests; + int testCount; +} testSets[] = { + { testSet0, (int) SK_ARRAY_COUNT(testSet0) }, + { testSet1, (int) SK_ARRAY_COUNT(testSet1) }, +}; + +static const int testSetsCount = (int) SK_ARRAY_COUNT(testSets); + +static void testSetTest(skiatest::Reporter* reporter, int index) { + const TestSet& testSet = testSets[index]; + int testCount = testSet.testCount; + SkASSERT(testCount > 1); + SkTDArray<SkIntersections> combos; + for (int outer = 0; outer < testCount - 1; ++outer) { + const Curve& oTest = testSet.tests[outer]; + for (int inner = outer + 1; inner < testCount; ++inner) { + const Curve& iTest = testSet.tests[inner]; + SkIntersections* i = combos.append(); + sk_bzero(i, sizeof(SkIntersections)); + SkDLine oLine = {{ oTest.curve.fPts[0], oTest.curve.fPts[1] }}; + SkDLine iLine = {{ iTest.curve.fPts[0], iTest.curve.fPts[1] }}; + SkDCubic iCurve, oCurve; + iCurve.debugSet(iTest.curve.fPts); + oCurve.debugSet(oTest.curve.fPts); + if (oTest.ptCount == 1 && iTest.ptCount == 1) { + i->intersect(oLine, iLine); + } else if (oTest.ptCount == 1 && iTest.ptCount == 4) { + i->intersect(iCurve, oLine); + } else if (oTest.ptCount == 4 && iTest.ptCount == 1) { + i->intersect(oCurve, iLine); + } else if (oTest.ptCount == 4 && iTest.ptCount == 4) { + i->intersect(oCurve, iCurve); + } else { + SkASSERT(0); + } +// i->dump(); + } + } +} + +DEF_TEST(PathOpsThreeWay, reporter) { + for (int index = 0; index < testSetsCount; ++index) { + testSetTest(reporter, index); + reporter->bumpTestCount(); + } +} + +DEF_TEST(PathOpsThreeWayOneOff, reporter) { + int index = 0; + testSetTest(reporter, index); +}
diff --git a/src/third_party/skia/tests/PathOpsTigerTest.cpp b/src/third_party/skia/tests/PathOpsTigerTest.cpp new file mode 100644 index 0000000..01c3d4e --- /dev/null +++ b/src/third_party/skia/tests/PathOpsTigerTest.cpp
@@ -0,0 +1,334 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "PathOpsExtendedTest.h" +#include "PathOpsThreadedCommon.h" + +#define TEST(name) { name, #name } + +static void tiger8(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.moveTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a)); // 492.451f, 225.216f +path.quadTo(SkBits2Float(0x43f58ce4), SkBits2Float(0x435d2a04), SkBits2Float(0x43f71bd9), SkBits2Float(0x435ac7d8)); // 491.101f, 221.164f, 494.218f, 218.781f +path.quadTo(SkBits2Float(0x43f7d69d), SkBits2Float(0x4359aa35), SkBits2Float(0x43f8b3b3), SkBits2Float(0x435951c5)); // 495.677f, 217.665f, 497.404f, 217.319f +path.conicTo(SkBits2Float(0x43f8ba67), SkBits2Float(0x43594f16), SkBits2Float(0x43f8c136), SkBits2Float(0x43594dd9), SkBits2Float(0x3f7fa2b1)); // 497.456f, 217.309f, 497.509f, 217.304f, 0.998576f +path.quadTo(SkBits2Float(0x43fcc3a8), SkBits2Float(0x43589340), SkBits2Float(0x43ff01dc), SkBits2Float(0x4352e191)); // 505.529f, 216.575f, 510.015f, 210.881f +path.conicTo(SkBits2Float(0x43ff5113), SkBits2Float(0x4352187b), SkBits2Float(0x43ffb59e), SkBits2Float(0x4352b6e9), SkBits2Float(0x3f3504f3)); // 510.633f, 210.096f, 511.419f, 210.714f, 0.707107f +path.conicTo(SkBits2Float(0x43ffdc85), SkBits2Float(0x4352f435), SkBits2Float(0x43ffe4a9), SkBits2Float(0x435355e9), SkBits2Float(0x3f6ec0ae)); // 511.723f, 210.954f, 511.786f, 211.336f, 0.932628f +path.quadTo(SkBits2Float(0x4400461c), SkBits2Float(0x435b3080), SkBits2Float(0x4400b692), SkBits2Float(0x4360b229)); // 513.095f, 219.189f, 514.853f, 224.696f +path.conicTo(SkBits2Float(0x4400c662), SkBits2Float(0x43617856), SkBits2Float(0x44009920), SkBits2Float(0x4361decb), SkBits2Float(0x3f46ad5b)); // 515.1f, 225.47f, 514.393f, 225.87f, 0.776083f +path.quadTo(SkBits2Float(0x43fb4920), SkBits2Float(0x43688f50), SkBits2Float(0x43f8340f), SkBits2Float(0x4365b887)); // 502.571f, 232.56f, 496.407f, 229.721f +path.quadTo(SkBits2Float(0x43f72cd2), SkBits2Float(0x4364c612), SkBits2Float(0x43f69888), SkBits2Float(0x4362e330)); // 494.35f, 228.774f, 493.192f, 226.887f +path.quadTo(SkBits2Float(0x43f66a00), SkBits2Float(0x43624bae), SkBits2Float(0x43f64c73), SkBits2Float(0x4361ad04)); // 492.828f, 226.296f, 492.597f, 225.676f +path.quadTo(SkBits2Float(0x43f642ea), SkBits2Float(0x436179d2), SkBits2Float(0x43f63c1c), SkBits2Float(0x43614abe)); // 492.523f, 225.476f, 492.47f, 225.292f +path.quadTo(SkBits2Float(0x43f639c9), SkBits2Float(0x43613aa5), SkBits2Float(0x43f63809), SkBits2Float(0x43612cda)); // 492.451f, 225.229f, 492.438f, 225.175f +path.quadTo(SkBits2Float(0x43f63777), SkBits2Float(0x43612855), SkBits2Float(0x43f636df), SkBits2Float(0x43612357)); // 492.433f, 225.158f, 492.429f, 225.138f +path.quadTo(SkBits2Float(0x43f6368f), SkBits2Float(0x436120b2), SkBits2Float(0x43f6367b), SkBits2Float(0x43612005)); // 492.426f, 225.128f, 492.426f, 225.125f +path.lineTo(SkBits2Float(0x43f63656), SkBits2Float(0x43611ebc)); // 492.424f, 225.12f +path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e34)); // 492.424f, 225.118f +path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611df3)); // 492.424f, 225.117f +path.lineTo(SkBits2Float(0x43f6363e), SkBits2Float(0x43611de5)); // 492.424f, 225.117f +path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611deb)); // 492.424f, 225.117f +path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e37)); // 492.424f, 225.118f +path.lineTo(SkBits2Float(0x43f63644), SkBits2Float(0x43611e19)); // 492.424f, 225.118f +path.quadTo(SkBits2Float(0x43f6365c), SkBits2Float(0x43611ee7), SkBits2Float(0x43f6365d), SkBits2Float(0x43611ef9)); // 492.425f, 225.121f, 492.425f, 225.121f +path.quadTo(SkBits2Float(0x43f63666), SkBits2Float(0x43611f4b), SkBits2Float(0x43f63672), SkBits2Float(0x43611fb1)); // 492.425f, 225.122f, 492.425f, 225.124f +path.quadTo(SkBits2Float(0x43f636ab), SkBits2Float(0x436121a4), SkBits2Float(0x43f636e3), SkBits2Float(0x4361236a)); // 492.427f, 225.131f, 492.429f, 225.138f +path.quadTo(SkBits2Float(0x43f636fd), SkBits2Float(0x43612443), SkBits2Float(0x43f63705), SkBits2Float(0x4361247e)); // 492.43f, 225.142f, 492.43f, 225.143f +path.quadTo(SkBits2Float(0x43f637d7), SkBits2Float(0x43612b15), SkBits2Float(0x43f638dc), SkBits2Float(0x436131b0)); // 492.436f, 225.168f, 492.444f, 225.194f +path.quadTo(SkBits2Float(0x43f63b88), SkBits2Float(0x43614303), SkBits2Float(0x43f63f62), SkBits2Float(0x43615368)); // 492.465f, 225.262f, 492.495f, 225.326f +path.quadTo(SkBits2Float(0x43f6436f), SkBits2Float(0x4361649f), SkBits2Float(0x43f648b2), SkBits2Float(0x43617468)); // 492.527f, 225.393f, 492.568f, 225.455f +path.quadTo(SkBits2Float(0x43f68760), SkBits2Float(0x43623072), SkBits2Float(0x43f6ec71), SkBits2Float(0x4361cb60)); // 493.058f, 226.189f, 493.847f, 225.794f +path.quadTo(SkBits2Float(0x43f722ef), SkBits2Float(0x436194e0), SkBits2Float(0x43f73027), SkBits2Float(0x43611df0)); // 494.273f, 225.582f, 494.376f, 225.117f +path.quadTo(SkBits2Float(0x43f73334), SkBits2Float(0x43610284), SkBits2Float(0x43f73333), SkBits2Float(0x4360e667)); // 494.4f, 225.01f, 494.4f, 224.9f +path.lineTo(SkBits2Float(0x43f63638), SkBits2Float(0x43611daf)); // 492.424f, 225.116f +path.lineTo(SkBits2Float(0x43f6b333), SkBits2Float(0x4360e666)); // 493.4f, 224.9f +path.lineTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a)); // 492.451f, 225.216f +path.close(); +path.moveTo(SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572)); // 494.349f, 224.584f +path.conicTo(SkBits2Float(0x43f72ebd), SkBits2Float(0x4360a219), SkBits2Float(0x43f7302e), SkBits2Float(0x4360af1f), SkBits2Float(0x3f7fa741)); // 494.365f, 224.633f, 494.376f, 224.684f, 0.998646f +path.lineTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360e667)); // 492.4f, 224.9f +path.quadTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360ca4b), SkBits2Float(0x43f6363f), SkBits2Float(0x4360aede)); // 492.4f, 224.79f, 492.424f, 224.683f +path.quadTo(SkBits2Float(0x43f64377), SkBits2Float(0x436037ee), SkBits2Float(0x43f679f5), SkBits2Float(0x4360016e)); // 492.527f, 224.218f, 492.953f, 224.006f +path.quadTo(SkBits2Float(0x43f6df06), SkBits2Float(0x435f9c5c), SkBits2Float(0x43f71db4), SkBits2Float(0x43605866)); // 493.742f, 223.611f, 494.232f, 224.345f +path.quadTo(SkBits2Float(0x43f722f8), SkBits2Float(0x43606830), SkBits2Float(0x43f72704), SkBits2Float(0x43607966)); // 494.273f, 224.407f, 494.305f, 224.474f +path.quadTo(SkBits2Float(0x43f72ae0), SkBits2Float(0x436089cd), SkBits2Float(0x43f72d8a), SkBits2Float(0x43609b1e)); // 494.335f, 224.538f, 494.356f, 224.606f +path.quadTo(SkBits2Float(0x43f72e8e), SkBits2Float(0x4360a1b8), SkBits2Float(0x43f72f61), SkBits2Float(0x4360a850)); // 494.364f, 224.632f, 494.37f, 224.657f +path.quadTo(SkBits2Float(0x43f72f68), SkBits2Float(0x4360a88a), SkBits2Float(0x43f72f83), SkBits2Float(0x4360a964)); // 494.37f, 224.658f, 494.371f, 224.662f +path.quadTo(SkBits2Float(0x43f72fbb), SkBits2Float(0x4360ab2a), SkBits2Float(0x43f72ff4), SkBits2Float(0x4360ad1d)); // 494.373f, 224.669f, 494.375f, 224.676f +path.quadTo(SkBits2Float(0x43f73000), SkBits2Float(0x4360ad83), SkBits2Float(0x43f73009), SkBits2Float(0x4360add5)); // 494.375f, 224.678f, 494.375f, 224.679f +path.quadTo(SkBits2Float(0x43f7300b), SkBits2Float(0x4360ade9), SkBits2Float(0x43f73022), SkBits2Float(0x4360aeb5)); // 494.375f, 224.679f, 494.376f, 224.682f +path.lineTo(SkBits2Float(0x43f7301f), SkBits2Float(0x4360ae97)); // 494.376f, 224.682f +path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aee3)); // 494.376f, 224.683f +path.lineTo(SkBits2Float(0x43f73028), SkBits2Float(0x4360aeeb)); // 494.376f, 224.683f +path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aedf)); // 494.376f, 224.683f +path.lineTo(SkBits2Float(0x43f73021), SkBits2Float(0x4360aeaa)); // 494.376f, 224.682f +path.lineTo(SkBits2Float(0x43f73016), SkBits2Float(0x4360ae50)); // 494.376f, 224.681f +path.lineTo(SkBits2Float(0x43f73007), SkBits2Float(0x4360adc1)); // 494.375f, 224.679f +path.lineTo(SkBits2Float(0x43f72ff9), SkBits2Float(0x4360ad4d)); // 494.375f, 224.677f +path.quadTo(SkBits2Float(0x43f7300d), SkBits2Float(0x4360adf7), SkBits2Float(0x43f73031), SkBits2Float(0x4360af12)); // 494.375f, 224.68f, 494.376f, 224.684f +path.quadTo(SkBits2Float(0x43f730f0), SkBits2Float(0x4360b4f1), SkBits2Float(0x43f7320a), SkBits2Float(0x4360bc94)); // 494.382f, 224.707f, 494.391f, 224.737f +path.quadTo(SkBits2Float(0x43f73625), SkBits2Float(0x4360d8fe), SkBits2Float(0x43f73c59), SkBits2Float(0x4360fa4a)); // 494.423f, 224.848f, 494.471f, 224.978f +path.quadTo(SkBits2Float(0x43f75132), SkBits2Float(0x43616a36), SkBits2Float(0x43f772ac), SkBits2Float(0x4361d738)); // 494.634f, 225.415f, 494.896f, 225.841f +path.quadTo(SkBits2Float(0x43f7de60), SkBits2Float(0x436335ea), SkBits2Float(0x43f89f25), SkBits2Float(0x4363e779)); // 495.737f, 227.211f, 497.243f, 227.904f +path.quadTo(SkBits2Float(0x43fb3d30), SkBits2Float(0x436650a0), SkBits2Float(0x44005a14), SkBits2Float(0x43602133)); // 502.478f, 230.315f, 513.407f, 224.13f +path.lineTo(SkBits2Float(0x4400799a), SkBits2Float(0x4360ffff)); // 513.9f, 225 +path.lineTo(SkBits2Float(0x44003ca2), SkBits2Float(0x43614dd5)); // 512.947f, 225.304f +path.quadTo(SkBits2Float(0x43ff92b8), SkBits2Float(0x435ba8f8), SkBits2Float(0x43fee825), SkBits2Float(0x4353aa15)); // 511.146f, 219.66f, 509.814f, 211.664f +path.lineTo(SkBits2Float(0x43ff6667), SkBits2Float(0x43537fff)); // 510.8f, 211.5f +path.lineTo(SkBits2Float(0x43ffcaf2), SkBits2Float(0x43541e6d)); // 511.586f, 212.119f +path.quadTo(SkBits2Float(0x43fd4888), SkBits2Float(0x435a7d38), SkBits2Float(0x43f8d864), SkBits2Float(0x435b4bbf)); // 506.567f, 218.489f, 497.691f, 219.296f +path.lineTo(SkBits2Float(0x43f8cccd), SkBits2Float(0x435a4ccc)); // 497.6f, 218.3f +path.lineTo(SkBits2Float(0x43f8e5e7), SkBits2Float(0x435b47d3)); // 497.796f, 219.281f +path.quadTo(SkBits2Float(0x43f84300), SkBits2Float(0x435b88fd), SkBits2Float(0x43f7b75b), SkBits2Float(0x435c5e8e)); // 496.523f, 219.535f, 495.432f, 220.369f +path.quadTo(SkBits2Float(0x43f6b984), SkBits2Float(0x435de2c4), SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572)); // 493.449f, 221.886f, 494.349f, 224.584f +path.close(); +testSimplify(reporter, path, filename); +} + +// fails to include a line of edges, probably mis-sorting +static void tiger8a(skiatest::Reporter* reporter, const char* filename) { + SkPath path; + path.moveTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a)); // 492.451f, 225.216f +path.quadTo(SkBits2Float(0x43f58ce4), SkBits2Float(0x435d2a04), SkBits2Float(0x43f71bd9), SkBits2Float(0x435ac7d8)); // 491.101f, 221.164f, 494.218f, 218.781f +path.quadTo(SkBits2Float(0x43f7d69d), SkBits2Float(0x4359aa35), SkBits2Float(0x43f8b3b3), SkBits2Float(0x435951c5)); // 495.677f, 217.665f, 497.404f, 217.319f +path.conicTo(SkBits2Float(0x43f8ba67), SkBits2Float(0x43594f16), SkBits2Float(0x43f8c136), SkBits2Float(0x43594dd9), SkBits2Float(0x3f7fa2b1)); // 497.456f, 217.309f, 497.509f, 217.304f, 0.998576f +path.quadTo(SkBits2Float(0x43fcc3a8), SkBits2Float(0x43589340), SkBits2Float(0x43ff01dc), SkBits2Float(0x4352e191)); // 505.529f, 216.575f, 510.015f, 210.881f +path.conicTo(SkBits2Float(0x43ff5113), SkBits2Float(0x4352187b), SkBits2Float(0x43ffb59e), SkBits2Float(0x4352b6e9), SkBits2Float(0x3f3504f3)); // 510.633f, 210.096f, 511.419f, 210.714f, 0.707107f +path.conicTo(SkBits2Float(0x43ffdc85), SkBits2Float(0x4352f435), SkBits2Float(0x43ffe4a9), SkBits2Float(0x435355e9), SkBits2Float(0x3f6ec0ae)); // 511.723f, 210.954f, 511.786f, 211.336f, 0.932628f +path.quadTo(SkBits2Float(0x4400461c), SkBits2Float(0x435b3080), SkBits2Float(0x4400b692), SkBits2Float(0x4360b229)); // 513.095f, 219.189f, 514.853f, 224.696f +path.conicTo(SkBits2Float(0x4400c662), SkBits2Float(0x43617856), SkBits2Float(0x44009920), SkBits2Float(0x4361decb), SkBits2Float(0x3f46ad5b)); // 515.1f, 225.47f, 514.393f, 225.87f, 0.776083f +path.quadTo(SkBits2Float(0x43fb4920), SkBits2Float(0x43688f50), SkBits2Float(0x43f8340f), SkBits2Float(0x4365b887)); // 502.571f, 232.56f, 496.407f, 229.721f +path.quadTo(SkBits2Float(0x43f72cd2), SkBits2Float(0x4364c612), SkBits2Float(0x43f69888), SkBits2Float(0x4362e330)); // 494.35f, 228.774f, 493.192f, 226.887f +path.quadTo(SkBits2Float(0x43f66a00), SkBits2Float(0x43624bae), SkBits2Float(0x43f64c73), SkBits2Float(0x4361ad04)); // 492.828f, 226.296f, 492.597f, 225.676f +path.quadTo(SkBits2Float(0x43f642ea), SkBits2Float(0x436179d2), SkBits2Float(0x43f63c1c), SkBits2Float(0x43614abe)); // 492.523f, 225.476f, 492.47f, 225.292f +path.quadTo(SkBits2Float(0x43f639c9), SkBits2Float(0x43613aa5), SkBits2Float(0x43f63809), SkBits2Float(0x43612cda)); // 492.451f, 225.229f, 492.438f, 225.175f +path.quadTo(SkBits2Float(0x43f63777), SkBits2Float(0x43612855), SkBits2Float(0x43f636df), SkBits2Float(0x43612357)); // 492.433f, 225.158f, 492.429f, 225.138f +path.quadTo(SkBits2Float(0x43f6368f), SkBits2Float(0x436120b2), SkBits2Float(0x43f6367b), SkBits2Float(0x43612005)); // 492.426f, 225.128f, 492.426f, 225.125f +path.lineTo(SkBits2Float(0x43f63656), SkBits2Float(0x43611ebc)); // 492.424f, 225.12f +path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e34)); // 492.424f, 225.118f +path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611df3)); // 492.424f, 225.117f +path.lineTo(SkBits2Float(0x43f6363e), SkBits2Float(0x43611de5)); // 492.424f, 225.117f +path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611deb)); // 492.424f, 225.117f +path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e37)); // 492.424f, 225.118f +path.lineTo(SkBits2Float(0x43f63644), SkBits2Float(0x43611e19)); // 492.424f, 225.118f +path.quadTo(SkBits2Float(0x43f6365c), SkBits2Float(0x43611ee7), SkBits2Float(0x43f6365d), SkBits2Float(0x43611ef9)); // 492.425f, 225.121f, 492.425f, 225.121f +path.quadTo(SkBits2Float(0x43f63666), SkBits2Float(0x43611f4b), SkBits2Float(0x43f63672), SkBits2Float(0x43611fb1)); // 492.425f, 225.122f, 492.425f, 225.124f +path.quadTo(SkBits2Float(0x43f636ab), SkBits2Float(0x436121a4), SkBits2Float(0x43f636e3), SkBits2Float(0x4361236a)); // 492.427f, 225.131f, 492.429f, 225.138f +path.quadTo(SkBits2Float(0x43f636fd), SkBits2Float(0x43612443), SkBits2Float(0x43f63705), SkBits2Float(0x4361247e)); // 492.43f, 225.142f, 492.43f, 225.143f +path.quadTo(SkBits2Float(0x43f637d7), SkBits2Float(0x43612b15), SkBits2Float(0x43f638dc), SkBits2Float(0x436131b0)); // 492.436f, 225.168f, 492.444f, 225.194f +path.quadTo(SkBits2Float(0x43f63b88), SkBits2Float(0x43614303), SkBits2Float(0x43f63f62), SkBits2Float(0x43615368)); // 492.465f, 225.262f, 492.495f, 225.326f +path.quadTo(SkBits2Float(0x43f6436f), SkBits2Float(0x4361649f), SkBits2Float(0x43f648b2), SkBits2Float(0x43617468)); // 492.527f, 225.393f, 492.568f, 225.455f +path.quadTo(SkBits2Float(0x43f68760), SkBits2Float(0x43623072), SkBits2Float(0x43f6ec71), SkBits2Float(0x4361cb60)); // 493.058f, 226.189f, 493.847f, 225.794f +path.quadTo(SkBits2Float(0x43f722ef), SkBits2Float(0x436194e0), SkBits2Float(0x43f73027), SkBits2Float(0x43611df0)); // 494.273f, 225.582f, 494.376f, 225.117f +path.quadTo(SkBits2Float(0x43f73334), SkBits2Float(0x43610284), SkBits2Float(0x43f73333), SkBits2Float(0x4360e667)); // 494.4f, 225.01f, 494.4f, 224.9f +path.lineTo(SkBits2Float(0x43f63638), SkBits2Float(0x43611daf)); // 492.424f, 225.116f +path.lineTo(SkBits2Float(0x43f6b333), SkBits2Float(0x4360e666)); // 493.4f, 224.9f +path.lineTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a)); // 492.451f, 225.216f +path.close(); +testSimplify(reporter, path, filename); +} + +static void tiger8a_x(skiatest::Reporter* reporter, uint64_t testlines) { + SkPath path; +uint64_t i = 0; +if (testlines & (1LL << i++)) path.moveTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a)); // 492.451f, 225.216f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f58ce4), SkBits2Float(0x435d2a04), SkBits2Float(0x43f71bd9), SkBits2Float(0x435ac7d8)); // 491.101f, 221.164f, 494.218f, 218.781f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f7d69d), SkBits2Float(0x4359aa35), SkBits2Float(0x43f8b3b3), SkBits2Float(0x435951c5)); // 495.677f, 217.665f, 497.404f, 217.319f +if (testlines & (1LL << i++)) path.conicTo(SkBits2Float(0x43f8ba67), SkBits2Float(0x43594f16), SkBits2Float(0x43f8c136), SkBits2Float(0x43594dd9), SkBits2Float(0x3f7fa2b1)); // 497.456f, 217.309f, 497.509f, 217.304f, 0.998576f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43fcc3a8), SkBits2Float(0x43589340), SkBits2Float(0x43ff01dc), SkBits2Float(0x4352e191)); // 505.529f, 216.575f, 510.015f, 210.881f +if (testlines & (1LL << i++)) path.conicTo(SkBits2Float(0x43ff5113), SkBits2Float(0x4352187b), SkBits2Float(0x43ffb59e), SkBits2Float(0x4352b6e9), SkBits2Float(0x3f3504f3)); // 510.633f, 210.096f, 511.419f, 210.714f, 0.707107f +if (testlines & (1LL << i++)) path.conicTo(SkBits2Float(0x43ffdc85), SkBits2Float(0x4352f435), SkBits2Float(0x43ffe4a9), SkBits2Float(0x435355e9), SkBits2Float(0x3f6ec0ae)); // 511.723f, 210.954f, 511.786f, 211.336f, 0.932628f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x4400461c), SkBits2Float(0x435b3080), SkBits2Float(0x4400b692), SkBits2Float(0x4360b229)); // 513.095f, 219.189f, 514.853f, 224.696f +if (testlines & (1LL << i++)) path.conicTo(SkBits2Float(0x4400c662), SkBits2Float(0x43617856), SkBits2Float(0x44009920), SkBits2Float(0x4361decb), SkBits2Float(0x3f46ad5b)); // 515.1f, 225.47f, 514.393f, 225.87f, 0.776083f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43fb4920), SkBits2Float(0x43688f50), SkBits2Float(0x43f8340f), SkBits2Float(0x4365b887)); // 502.571f, 232.56f, 496.407f, 229.721f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f72cd2), SkBits2Float(0x4364c612), SkBits2Float(0x43f69888), SkBits2Float(0x4362e330)); // 494.35f, 228.774f, 493.192f, 226.887f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f66a00), SkBits2Float(0x43624bae), SkBits2Float(0x43f64c73), SkBits2Float(0x4361ad04)); // 492.828f, 226.296f, 492.597f, 225.676f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f642ea), SkBits2Float(0x436179d2), SkBits2Float(0x43f63c1c), SkBits2Float(0x43614abe)); // 492.523f, 225.476f, 492.47f, 225.292f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f639c9), SkBits2Float(0x43613aa5), SkBits2Float(0x43f63809), SkBits2Float(0x43612cda)); // 492.451f, 225.229f, 492.438f, 225.175f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f63777), SkBits2Float(0x43612855), SkBits2Float(0x43f636df), SkBits2Float(0x43612357)); // 492.433f, 225.158f, 492.429f, 225.138f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f6368f), SkBits2Float(0x436120b2), SkBits2Float(0x43f6367b), SkBits2Float(0x43612005)); // 492.426f, 225.128f, 492.426f, 225.125f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63656), SkBits2Float(0x43611ebc)); // 492.424f, 225.12f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e34)); // 492.424f, 225.118f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611df3)); // 492.424f, 225.117f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f6363e), SkBits2Float(0x43611de5)); // 492.424f, 225.117f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611deb)); // 492.424f, 225.117f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e37)); // 492.424f, 225.118f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63644), SkBits2Float(0x43611e19)); // 492.424f, 225.118f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f6365c), SkBits2Float(0x43611ee7), SkBits2Float(0x43f6365d), SkBits2Float(0x43611ef9)); // 492.425f, 225.121f, 492.425f, 225.121f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f63666), SkBits2Float(0x43611f4b), SkBits2Float(0x43f63672), SkBits2Float(0x43611fb1)); // 492.425f, 225.122f, 492.425f, 225.124f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f636ab), SkBits2Float(0x436121a4), SkBits2Float(0x43f636e3), SkBits2Float(0x4361236a)); // 492.427f, 225.131f, 492.429f, 225.138f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f636fd), SkBits2Float(0x43612443), SkBits2Float(0x43f63705), SkBits2Float(0x4361247e)); // 492.43f, 225.142f, 492.43f, 225.143f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f637d7), SkBits2Float(0x43612b15), SkBits2Float(0x43f638dc), SkBits2Float(0x436131b0)); // 492.436f, 225.168f, 492.444f, 225.194f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f63b88), SkBits2Float(0x43614303), SkBits2Float(0x43f63f62), SkBits2Float(0x43615368)); // 492.465f, 225.262f, 492.495f, 225.326f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f6436f), SkBits2Float(0x4361649f), SkBits2Float(0x43f648b2), SkBits2Float(0x43617468)); // 492.527f, 225.393f, 492.568f, 225.455f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f68760), SkBits2Float(0x43623072), SkBits2Float(0x43f6ec71), SkBits2Float(0x4361cb60)); // 493.058f, 226.189f, 493.847f, 225.794f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f722ef), SkBits2Float(0x436194e0), SkBits2Float(0x43f73027), SkBits2Float(0x43611df0)); // 494.273f, 225.582f, 494.376f, 225.117f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f73334), SkBits2Float(0x43610284), SkBits2Float(0x43f73333), SkBits2Float(0x4360e667)); // 494.4f, 225.01f, 494.4f, 224.9f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63638), SkBits2Float(0x43611daf)); // 492.424f, 225.116f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f6b333), SkBits2Float(0x4360e666)); // 493.4f, 224.9f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a)); // 492.451f, 225.216f +if (testlines & (1LL << i++)) path.close(); +testSimplify(reporter, path, "tiger"); +} + +#include "SkRandom.h" + +static void tiger8a_h_1(skiatest::Reporter* reporter, const char* ) { + uint64_t testlines = 0x0000000000002008; // best so far: 0x0000001d14c14bb1; + tiger8a_x(reporter, testlines); +} + +static void tiger8b_x(skiatest::Reporter* reporter, uint64_t testlines) { + SkPath path; +uint64_t i = 0; +if (testlines & (1LL << i++)) path.moveTo(SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572)); // 494.349f, 224.584f +if (testlines & (1LL << i++)) path.conicTo(SkBits2Float(0x43f72ebd), SkBits2Float(0x4360a219), SkBits2Float(0x43f7302e), SkBits2Float(0x4360af1f), SkBits2Float(0x3f7fa741)); // 494.365f, 224.633f, 494.376f, 224.684f, 0.998646f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360e667)); // 492.4f, 224.9f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360ca4b), SkBits2Float(0x43f6363f), SkBits2Float(0x4360aede)); // 492.4f, 224.79f, 492.424f, 224.683f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f64377), SkBits2Float(0x436037ee), SkBits2Float(0x43f679f5), SkBits2Float(0x4360016e)); // 492.527f, 224.218f, 492.953f, 224.006f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f6df06), SkBits2Float(0x435f9c5c), SkBits2Float(0x43f71db4), SkBits2Float(0x43605866)); // 493.742f, 223.611f, 494.232f, 224.345f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f722f8), SkBits2Float(0x43606830), SkBits2Float(0x43f72704), SkBits2Float(0x43607966)); // 494.273f, 224.407f, 494.305f, 224.474f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f72ae0), SkBits2Float(0x436089cd), SkBits2Float(0x43f72d8a), SkBits2Float(0x43609b1e)); // 494.335f, 224.538f, 494.356f, 224.606f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f72e8e), SkBits2Float(0x4360a1b8), SkBits2Float(0x43f72f61), SkBits2Float(0x4360a850)); // 494.364f, 224.632f, 494.37f, 224.657f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f72f68), SkBits2Float(0x4360a88a), SkBits2Float(0x43f72f83), SkBits2Float(0x4360a964)); // 494.37f, 224.658f, 494.371f, 224.662f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f72fbb), SkBits2Float(0x4360ab2a), SkBits2Float(0x43f72ff4), SkBits2Float(0x4360ad1d)); // 494.373f, 224.669f, 494.375f, 224.676f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f73000), SkBits2Float(0x4360ad83), SkBits2Float(0x43f73009), SkBits2Float(0x4360add5)); // 494.375f, 224.678f, 494.375f, 224.679f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f7300b), SkBits2Float(0x4360ade9), SkBits2Float(0x43f73022), SkBits2Float(0x4360aeb5)); // 494.375f, 224.679f, 494.376f, 224.682f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f7301f), SkBits2Float(0x4360ae97)); // 494.376f, 224.682f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aee3)); // 494.376f, 224.683f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f73028), SkBits2Float(0x4360aeeb)); // 494.376f, 224.683f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aedf)); // 494.376f, 224.683f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f73021), SkBits2Float(0x4360aeaa)); // 494.376f, 224.682f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f73016), SkBits2Float(0x4360ae50)); // 494.376f, 224.681f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f73007), SkBits2Float(0x4360adc1)); // 494.375f, 224.679f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f72ff9), SkBits2Float(0x4360ad4d)); // 494.375f, 224.677f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f7300d), SkBits2Float(0x4360adf7), SkBits2Float(0x43f73031), SkBits2Float(0x4360af12)); // 494.375f, 224.68f, 494.376f, 224.684f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f730f0), SkBits2Float(0x4360b4f1), SkBits2Float(0x43f7320a), SkBits2Float(0x4360bc94)); // 494.382f, 224.707f, 494.391f, 224.737f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f73625), SkBits2Float(0x4360d8fe), SkBits2Float(0x43f73c59), SkBits2Float(0x4360fa4a)); // 494.423f, 224.848f, 494.471f, 224.978f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f75132), SkBits2Float(0x43616a36), SkBits2Float(0x43f772ac), SkBits2Float(0x4361d738)); // 494.634f, 225.415f, 494.896f, 225.841f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f7de60), SkBits2Float(0x436335ea), SkBits2Float(0x43f89f25), SkBits2Float(0x4363e779)); // 495.737f, 227.211f, 497.243f, 227.904f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43fb3d30), SkBits2Float(0x436650a0), SkBits2Float(0x44005a14), SkBits2Float(0x43602133)); // 502.478f, 230.315f, 513.407f, 224.13f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4400799a), SkBits2Float(0x4360ffff)); // 513.9f, 225 +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44003ca2), SkBits2Float(0x43614dd5)); // 512.947f, 225.304f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43ff92b8), SkBits2Float(0x435ba8f8), SkBits2Float(0x43fee825), SkBits2Float(0x4353aa15)); // 511.146f, 219.66f, 509.814f, 211.664f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43ff6667), SkBits2Float(0x43537fff)); // 510.8f, 211.5f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43ffcaf2), SkBits2Float(0x43541e6d)); // 511.586f, 212.119f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43fd4888), SkBits2Float(0x435a7d38), SkBits2Float(0x43f8d864), SkBits2Float(0x435b4bbf)); // 506.567f, 218.489f, 497.691f, 219.296f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f8cccd), SkBits2Float(0x435a4ccc)); // 497.6f, 218.3f +if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f8e5e7), SkBits2Float(0x435b47d3)); // 497.796f, 219.281f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f84300), SkBits2Float(0x435b88fd), SkBits2Float(0x43f7b75b), SkBits2Float(0x435c5e8e)); // 496.523f, 219.535f, 495.432f, 220.369f +if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f6b984), SkBits2Float(0x435de2c4), SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572)); // 493.449f, 221.886f, 494.349f, 224.584f +if (testlines & (1LL << i++)) path.close(); +testSimplify(reporter, path, "tiger"); +} + +static void testTiger(PathOpsThreadState* data) { + uint64_t testlines = ((uint64_t) data->fB << 32) | (unsigned int) data->fA; + if (data->fC) { + tiger8b_x(data->fReporter, testlines); + } else { + tiger8a_x(data->fReporter, testlines); + } +} + +static void tiger_threaded(skiatest::Reporter* reporter, const char* filename) { + initializeTests(reporter, "tigerb"); + PathOpsThreadedTestRunner testRunner(reporter); + for (int ab = 0; ab < 2; ++ab) { + SkRandom r; + int testCount = reporter->allowExtendedTest() ? 10000 : 100; + for (int samples = 2; samples < 37; ++samples) { + for (int tests = 0; tests < testCount; ++tests) { + uint64_t testlines = 0; + for (int i = 0; i < samples; ++i) { + int bit; + do { + bit = r.nextRangeU(0, 38); + } while (testlines & (1LL << bit)); + testlines |= 1LL << bit; + } + *testRunner.fRunnables.append() = + new PathOpsThreadedRunnable(&testTiger, + (int) (unsigned) (testlines & 0xFFFFFFFF), + (int) (unsigned) (testlines >> 32), + ab, 0, &testRunner); + } + } + } + testRunner.render(); +} + +static void tiger8b_h_1(skiatest::Reporter* reporter, const char* filename) { + uint64_t testlines = 0x000000000f27b9e3; // best so far: 0x000000201304b4a3 + tiger8b_x(reporter, testlines); +} + +// tries to add same edge twice +static void tiger8b(skiatest::Reporter* reporter, const char* filename) { + SkPath path; +path.moveTo(SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572)); // 494.349f, 224.584f +path.conicTo(SkBits2Float(0x43f72ebd), SkBits2Float(0x4360a219), SkBits2Float(0x43f7302e), SkBits2Float(0x4360af1f), SkBits2Float(0x3f7fa741)); // 494.365f, 224.633f, 494.376f, 224.684f, 0.998646f +path.lineTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360e667)); // 492.4f, 224.9f +path.quadTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360ca4b), SkBits2Float(0x43f6363f), SkBits2Float(0x4360aede)); // 492.4f, 224.79f, 492.424f, 224.683f +path.quadTo(SkBits2Float(0x43f64377), SkBits2Float(0x436037ee), SkBits2Float(0x43f679f5), SkBits2Float(0x4360016e)); // 492.527f, 224.218f, 492.953f, 224.006f +path.quadTo(SkBits2Float(0x43f6df06), SkBits2Float(0x435f9c5c), SkBits2Float(0x43f71db4), SkBits2Float(0x43605866)); // 493.742f, 223.611f, 494.232f, 224.345f +path.quadTo(SkBits2Float(0x43f722f8), SkBits2Float(0x43606830), SkBits2Float(0x43f72704), SkBits2Float(0x43607966)); // 494.273f, 224.407f, 494.305f, 224.474f +path.quadTo(SkBits2Float(0x43f72ae0), SkBits2Float(0x436089cd), SkBits2Float(0x43f72d8a), SkBits2Float(0x43609b1e)); // 494.335f, 224.538f, 494.356f, 224.606f +path.quadTo(SkBits2Float(0x43f72e8e), SkBits2Float(0x4360a1b8), SkBits2Float(0x43f72f61), SkBits2Float(0x4360a850)); // 494.364f, 224.632f, 494.37f, 224.657f +path.quadTo(SkBits2Float(0x43f72f68), SkBits2Float(0x4360a88a), SkBits2Float(0x43f72f83), SkBits2Float(0x4360a964)); // 494.37f, 224.658f, 494.371f, 224.662f +path.quadTo(SkBits2Float(0x43f72fbb), SkBits2Float(0x4360ab2a), SkBits2Float(0x43f72ff4), SkBits2Float(0x4360ad1d)); // 494.373f, 224.669f, 494.375f, 224.676f +path.quadTo(SkBits2Float(0x43f73000), SkBits2Float(0x4360ad83), SkBits2Float(0x43f73009), SkBits2Float(0x4360add5)); // 494.375f, 224.678f, 494.375f, 224.679f +path.quadTo(SkBits2Float(0x43f7300b), SkBits2Float(0x4360ade9), SkBits2Float(0x43f73022), SkBits2Float(0x4360aeb5)); // 494.375f, 224.679f, 494.376f, 224.682f +path.lineTo(SkBits2Float(0x43f7301f), SkBits2Float(0x4360ae97)); // 494.376f, 224.682f +path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aee3)); // 494.376f, 224.683f +path.lineTo(SkBits2Float(0x43f73028), SkBits2Float(0x4360aeeb)); // 494.376f, 224.683f +path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aedf)); // 494.376f, 224.683f +path.lineTo(SkBits2Float(0x43f73021), SkBits2Float(0x4360aeaa)); // 494.376f, 224.682f +path.lineTo(SkBits2Float(0x43f73016), SkBits2Float(0x4360ae50)); // 494.376f, 224.681f +path.lineTo(SkBits2Float(0x43f73007), SkBits2Float(0x4360adc1)); // 494.375f, 224.679f +path.lineTo(SkBits2Float(0x43f72ff9), SkBits2Float(0x4360ad4d)); // 494.375f, 224.677f +path.quadTo(SkBits2Float(0x43f7300d), SkBits2Float(0x4360adf7), SkBits2Float(0x43f73031), SkBits2Float(0x4360af12)); // 494.375f, 224.68f, 494.376f, 224.684f +path.quadTo(SkBits2Float(0x43f730f0), SkBits2Float(0x4360b4f1), SkBits2Float(0x43f7320a), SkBits2Float(0x4360bc94)); // 494.382f, 224.707f, 494.391f, 224.737f +path.quadTo(SkBits2Float(0x43f73625), SkBits2Float(0x4360d8fe), SkBits2Float(0x43f73c59), SkBits2Float(0x4360fa4a)); // 494.423f, 224.848f, 494.471f, 224.978f +path.quadTo(SkBits2Float(0x43f75132), SkBits2Float(0x43616a36), SkBits2Float(0x43f772ac), SkBits2Float(0x4361d738)); // 494.634f, 225.415f, 494.896f, 225.841f +path.quadTo(SkBits2Float(0x43f7de60), SkBits2Float(0x436335ea), SkBits2Float(0x43f89f25), SkBits2Float(0x4363e779)); // 495.737f, 227.211f, 497.243f, 227.904f +path.quadTo(SkBits2Float(0x43fb3d30), SkBits2Float(0x436650a0), SkBits2Float(0x44005a14), SkBits2Float(0x43602133)); // 502.478f, 230.315f, 513.407f, 224.13f +path.lineTo(SkBits2Float(0x4400799a), SkBits2Float(0x4360ffff)); // 513.9f, 225 +path.lineTo(SkBits2Float(0x44003ca2), SkBits2Float(0x43614dd5)); // 512.947f, 225.304f +path.quadTo(SkBits2Float(0x43ff92b8), SkBits2Float(0x435ba8f8), SkBits2Float(0x43fee825), SkBits2Float(0x4353aa15)); // 511.146f, 219.66f, 509.814f, 211.664f +path.lineTo(SkBits2Float(0x43ff6667), SkBits2Float(0x43537fff)); // 510.8f, 211.5f +path.lineTo(SkBits2Float(0x43ffcaf2), SkBits2Float(0x43541e6d)); // 511.586f, 212.119f +path.quadTo(SkBits2Float(0x43fd4888), SkBits2Float(0x435a7d38), SkBits2Float(0x43f8d864), SkBits2Float(0x435b4bbf)); // 506.567f, 218.489f, 497.691f, 219.296f +path.lineTo(SkBits2Float(0x43f8cccd), SkBits2Float(0x435a4ccc)); // 497.6f, 218.3f +path.lineTo(SkBits2Float(0x43f8e5e7), SkBits2Float(0x435b47d3)); // 497.796f, 219.281f +path.quadTo(SkBits2Float(0x43f84300), SkBits2Float(0x435b88fd), SkBits2Float(0x43f7b75b), SkBits2Float(0x435c5e8e)); // 496.523f, 219.535f, 495.432f, 220.369f +path.quadTo(SkBits2Float(0x43f6b984), SkBits2Float(0x435de2c4), SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572)); // 493.449f, 221.886f, 494.349f, 224.584f +path.close(); +testSimplify(reporter, path, filename); +} + + + +static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0; +static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0; +static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0; + +static TestDesc tests[] = { + TEST(tiger8a_h_1), + TEST(tiger8a), + TEST(tiger8b_h_1), + TEST(tiger8b), + TEST(tiger8), + TEST(tiger_threaded), +}; + +static const size_t testCount = SK_ARRAY_COUNT(tests); +static bool runReverse = false; + +DEF_TEST(PathOpsTiger, reporter) { + RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse); +}
diff --git a/src/third_party/skia/tests/PathOpsTightBoundsTest.cpp b/src/third_party/skia/tests/PathOpsTightBoundsTest.cpp index cea3752..a2e7bca 100644 --- a/src/third_party/skia/tests/PathOpsTightBoundsTest.cpp +++ b/src/third_party/skia/tests/PathOpsTightBoundsTest.cpp
@@ -8,7 +8,6 @@ #include "PathOpsThreadedCommon.h" #include "SkCanvas.h" #include "SkRandom.h" -#include "SkTArray.h" #include "SkTSort.h" #include "Test.h" @@ -40,8 +39,8 @@ int outerCount = reporter->allowExtendedTest() ? 100 : 1; for (int index = 0; index < outerCount; ++index) { for (int idx2 = 0; idx2 < 10; ++idx2) { - *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable, - (&testTightBoundsLines, 0, 0, 0, 0, &testRunner)); + *testRunner.fRunnables.append() = + new PathOpsThreadedRunnable(&testTightBoundsLines, 0, 0, 0, 0, &testRunner); } } testRunner.render(); @@ -115,9 +114,88 @@ int outerCount = reporter->allowExtendedTest() ? 100 : 1; for (int index = 0; index < outerCount; ++index) { for (int idx2 = 0; idx2 < 10; ++idx2) { - *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable, - (&testTightBoundsQuads, 0, 0, 0, 0, &testRunner)); + *testRunner.fRunnables.append() = + new PathOpsThreadedRunnable(&testTightBoundsQuads, 0, 0, 0, 0, &testRunner); } } testRunner.render(); } + +DEF_TEST(PathOpsTightBoundsMove, reporter) { + SkPath path; + path.moveTo(10, 10); + path.close(); + path.moveTo(20, 20); + path.lineTo(20, 20); + path.close(); + path.moveTo(15, 15); + path.lineTo(15, 15); + path.close(); + const SkRect& bounds = path.getBounds(); + SkRect tight; + REPORTER_ASSERT(reporter, TightBounds(path, &tight)); + REPORTER_ASSERT(reporter, bounds == tight); +} + +DEF_TEST(PathOpsTightBoundsMoveOne, reporter) { + SkPath path; + path.moveTo(20, 20); + const SkRect& bounds = path.getBounds(); + SkRect tight; + REPORTER_ASSERT(reporter, TightBounds(path, &tight)); + REPORTER_ASSERT(reporter, bounds == tight); +} + +DEF_TEST(PathOpsTightBoundsMoveTwo, reporter) { + SkPath path; + path.moveTo(20, 20); + path.moveTo(40, 40); + const SkRect& bounds = path.getBounds(); + SkRect tight; + REPORTER_ASSERT(reporter, TightBounds(path, &tight)); + REPORTER_ASSERT(reporter, bounds == tight); +} + +DEF_TEST(PathOpsTightBoundsTiny, reporter) { + SkPath path; + path.moveTo(1, 1); + path.quadTo(1.000001f, 1, 1, 1); + const SkRect& bounds = path.getBounds(); + SkRect tight; + REPORTER_ASSERT(reporter, TightBounds(path, &tight)); + SkRect moveBounds = {1, 1, 1, 1}; + REPORTER_ASSERT(reporter, bounds != tight); + REPORTER_ASSERT(reporter, moveBounds == tight); +} + +DEF_TEST(PathOpsTightBoundsWellBehaved, reporter) { + SkPath path; + path.moveTo(1, 1); + path.quadTo(2, 3, 4, 5); + const SkRect& bounds = path.getBounds(); + SkRect tight; + REPORTER_ASSERT(reporter, TightBounds(path, &tight)); + REPORTER_ASSERT(reporter, bounds == tight); +} + +DEF_TEST(PathOpsTightBoundsIllBehaved, reporter) { + SkPath path; + path.moveTo(1, 1); + path.quadTo(4, 3, 2, 2); + const SkRect& bounds = path.getBounds(); + SkRect tight; + REPORTER_ASSERT(reporter, TightBounds(path, &tight)); + REPORTER_ASSERT(reporter, bounds != tight); +} + +DEF_TEST(PathOpsTightBoundsIllBehavedScaled, reporter) { + SkPath path; + path.moveTo(0, 0); + path.quadTo(1048578, 1048577, 1048576, 1048576); + const SkRect& bounds = path.getBounds(); + SkRect tight; + REPORTER_ASSERT(reporter, TightBounds(path, &tight)); + REPORTER_ASSERT(reporter, bounds != tight); + REPORTER_ASSERT(reporter, tight.right() == 1048576); + REPORTER_ASSERT(reporter, tight.bottom() == 1048576); +}
diff --git a/src/third_party/skia/tests/PathOpsTypesTest.cpp b/src/third_party/skia/tests/PathOpsTypesTest.cpp old mode 100755 new mode 100644
diff --git a/src/third_party/skia/tests/PathTest.cpp b/src/third_party/skia/tests/PathTest.cpp index 1f0422d..117da5b 100644 --- a/src/third_party/skia/tests/PathTest.cpp +++ b/src/third_party/skia/tests/PathTest.cpp
@@ -5,21 +5,69 @@ * found in the LICENSE file. */ +#include "SkAutoMalloc.h" #include "SkCanvas.h" +#include "SkGeometry.h" +#include "SkNullCanvas.h" #include "SkPaint.h" #include "SkParse.h" #include "SkParsePath.h" -#include "SkPath.h" #include "SkPathEffect.h" +#include "SkPathPriv.h" #include "SkRRect.h" #include "SkRandom.h" #include "SkReader32.h" #include "SkSize.h" #include "SkStream.h" +#include "SkStrokeRec.h" #include "SkSurface.h" -#include "SkTypes.h" #include "SkWriter32.h" #include "Test.h" +#include <cmath> + + +static void set_radii(SkVector radii[4], int index, float rad) { + sk_bzero(radii, sizeof(SkVector) * 4); + radii[index].set(rad, rad); +} + +static void test_add_rrect(skiatest::Reporter* reporter, const SkRect& bounds, + const SkVector radii[4]) { + SkRRect rrect; + rrect.setRectRadii(bounds, radii); + REPORTER_ASSERT(reporter, bounds == rrect.rect()); + + SkPath path; + // this line should not assert in the debug build (from validate) + path.addRRect(rrect); + REPORTER_ASSERT(reporter, bounds == path.getBounds()); +} + +static void test_skbug_3469(skiatest::Reporter* reporter) { + SkPath path; + path.moveTo(20, 20); + path.quadTo(20, 50, 80, 50); + path.quadTo(20, 50, 20, 80); + REPORTER_ASSERT(reporter, !path.isConvex()); +} + +static void test_skbug_3239(skiatest::Reporter* reporter) { + const float min = SkBits2Float(0xcb7f16c8); /* -16717512.000000 */ + const float max = SkBits2Float(0x4b7f1c1d); /* 16718877.000000 */ + const float big = SkBits2Float(0x4b7f1bd7); /* 16718807.000000 */ + + const float rad = 33436320; + + const SkRect rectx = SkRect::MakeLTRB(min, min, max, big); + const SkRect recty = SkRect::MakeLTRB(min, min, big, max); + + SkVector radii[4]; + for (int i = 0; i < 4; ++i) { + set_radii(radii, i, rad); + test_add_rrect(reporter, rectx, radii); + test_add_rrect(reporter, recty, radii); + } +} static void make_path_crbug364224(SkPath* path) { path->reset(); @@ -51,10 +99,24 @@ path->close(); } +static void test_sect_with_horizontal_needs_pinning() { + // Test that sect_with_horizontal in SkLineClipper.cpp needs to pin after computing the + // intersection. + SkPath path; + path.reset(); + path.moveTo(-540000, -720000); + path.lineTo(-9.10000017e-05f, 9.99999996e-13f); + path.lineTo(1, 1); + + // Without the pinning code in sect_with_horizontal(), this would assert in the lineclipper + SkPaint paint; + SkSurface::MakeRasterN32Premul(10, 10)->getCanvas()->drawPath(path, paint); +} + static void test_path_crbug364224() { SkPath path; SkPaint paint; - SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(84, 88)); + auto surface(SkSurface::MakeRasterN32Premul(84, 88)); SkCanvas* canvas = surface->getCanvas(); make_path_crbug364224_simplified(&path); @@ -64,6 +126,112 @@ canvas->drawPath(path, paint); } +// this is a unit test instead of a GM because it doesn't draw anything +static void test_fuzz_crbug_638223() { + auto surface(SkSurface::MakeRasterN32Premul(250, 250)); + SkCanvas* canvas = surface->getCanvas(); + SkPath path; + path.moveTo(SkBits2Float(0x47452a00), SkBits2Float(0x43211d01)); // 50474, 161.113f + path.conicTo(SkBits2Float(0x401c0000), SkBits2Float(0x40680000), + SkBits2Float(0x02c25a81), SkBits2Float(0x981a1fa0), + SkBits2Float(0x6bf9abea)); // 2.4375f, 3.625f, 2.85577e-37f, -1.992e-24f, 6.03669e+26f + SkPaint paint; + paint.setAntiAlias(true); + canvas->drawPath(path, paint); +} + +static void test_fuzz_crbug_643933() { + auto surface(SkSurface::MakeRasterN32Premul(250, 250)); + SkCanvas* canvas = surface->getCanvas(); + SkPaint paint; + paint.setAntiAlias(true); + SkPath path; + path.moveTo(0, 0); + path.conicTo(SkBits2Float(0x002001f2), SkBits2Float(0x4161ffff), // 2.93943e-39f, 14.125f + SkBits2Float(0x49f7224d), SkBits2Float(0x45eec8df), // 2.02452e+06f, 7641.11f + SkBits2Float(0x721aee0c)); // 3.0687e+30f + canvas->drawPath(path, paint); + path.reset(); + path.moveTo(0, 0); + path.conicTo(SkBits2Float(0x00007ff2), SkBits2Float(0x4169ffff), // 4.58981e-41f, 14.625f + SkBits2Float(0x43ff2261), SkBits2Float(0x41eeea04), // 510.269f, 29.8643f + SkBits2Float(0x5d06eff8)); // 6.07704e+17f + canvas->drawPath(path, paint); +} + +static void test_fuzz_crbug_647922() { + auto surface(SkSurface::MakeRasterN32Premul(250, 250)); + SkCanvas* canvas = surface->getCanvas(); + SkPaint paint; + paint.setAntiAlias(true); + SkPath path; + path.moveTo(0, 0); + path.conicTo(SkBits2Float(0x00003939), SkBits2Float(0x42487fff), // 2.05276e-41f, 50.125f + SkBits2Float(0x48082361), SkBits2Float(0x4408e8e9), // 139406, 547.639f + SkBits2Float(0x4d1ade0f)); // 1.6239e+08f + canvas->drawPath(path, paint); +} + +static void test_fuzz_crbug_662780() { + auto surface(SkSurface::MakeRasterN32Premul(250, 250)); + SkCanvas* canvas = surface->getCanvas(); + SkPaint paint; + paint.setAntiAlias(true); + SkPath path; + path.moveTo(SkBits2Float(0x41000000), SkBits2Float(0x431e0000)); // 8, 158 + path.lineTo(SkBits2Float(0x41000000), SkBits2Float(0x42f00000)); // 8, 120 + // 8, 8, 8.00002f, 8, 0.707107f + path.conicTo(SkBits2Float(0x41000000), SkBits2Float(0x41000000), + SkBits2Float(0x41000010), SkBits2Float(0x41000000), SkBits2Float(0x3f3504f3)); + path.lineTo(SkBits2Float(0x439a0000), SkBits2Float(0x41000000)); // 308, 8 + // 308, 8, 308, 8, 0.707107f + path.conicTo(SkBits2Float(0x439a0000), SkBits2Float(0x41000000), + SkBits2Float(0x439a0000), SkBits2Float(0x41000000), SkBits2Float(0x3f3504f3)); + path.lineTo(SkBits2Float(0x439a0000), SkBits2Float(0x431e0000)); // 308, 158 + // 308, 158, 308, 158, 0.707107f + path.conicTo(SkBits2Float(0x439a0000), SkBits2Float(0x431e0000), + SkBits2Float(0x439a0000), SkBits2Float(0x431e0000), SkBits2Float(0x3f3504f3)); + path.lineTo(SkBits2Float(0x41000000), SkBits2Float(0x431e0000)); // 8, 158 + // 8, 158, 8, 158, 0.707107f + path.conicTo(SkBits2Float(0x41000000), SkBits2Float(0x431e0000), + SkBits2Float(0x41000000), SkBits2Float(0x431e0000), SkBits2Float(0x3f3504f3)); + path.close(); + canvas->clipPath(path, true); + canvas->drawRect(SkRect::MakeWH(250, 250), paint); +} + +static void test_mask_overflow() { + auto surface(SkSurface::MakeRasterN32Premul(500, 500)); + SkCanvas* canvas = surface->getCanvas(); + SkPaint paint; + paint.setAntiAlias(true); + SkPath path; + path.moveTo(SkBits2Float(0x43e28000), SkBits2Float(0x43aa8000)); // 453, 341 + path.lineTo(SkBits2Float(0x43de6000), SkBits2Float(0x43aa8000)); // 444.75f, 341 + // 440.47f, 341, 437, 344.47f, 437, 348.75f + path.cubicTo(SkBits2Float(0x43dc3c29), SkBits2Float(0x43aa8000), + SkBits2Float(0x43da8000), SkBits2Float(0x43ac3c29), + SkBits2Float(0x43da8000), SkBits2Float(0x43ae6000)); + path.lineTo(SkBits2Float(0x43da8000), SkBits2Float(0x43b18000)); // 437, 355 + path.lineTo(SkBits2Float(0x43e28000), SkBits2Float(0x43b18000)); // 453, 355 + path.lineTo(SkBits2Float(0x43e28000), SkBits2Float(0x43aa8000)); // 453, 341 + canvas->drawPath(path, paint); +} + +static void test_fuzz_crbug_668907() { + auto surface(SkSurface::MakeRasterN32Premul(400, 500)); + SkCanvas* canvas = surface->getCanvas(); + SkPaint paint; + paint.setAntiAlias(true); + SkPath path; + path.moveTo(SkBits2Float(0x46313741), SkBits2Float(0x3b00e540)); // 11341.8f, 0.00196679f + path.quadTo(SkBits2Float(0x41410041), SkBits2Float(0xc1414141), SkBits2Float(0x41414141), + SkBits2Float(0x414100ff)); // 12.0626f, -12.0784f, 12.0784f, 12.0627f + path.lineTo(SkBits2Float(0x46313741), SkBits2Float(0x3b00e540)); // 11341.8f, 0.00196679f + path.close(); + canvas->drawPath(path, paint); +} + /** * In debug mode, this path was causing an assertion to fail in * SkPathStroker::preJoinTo() and, in Release, the use of an unitialized value. @@ -90,6 +258,19 @@ stroke.applyToPath(&path, path); } +static void test_path_crbugskia5995() { + auto surface(SkSurface::MakeRasterN32Premul(500, 500)); + SkCanvas* canvas = surface->getCanvas(); + SkPaint paint; + paint.setAntiAlias(true); + SkPath path; + path.moveTo(SkBits2Float(0x40303030), SkBits2Float(0x3e303030)); // 2.75294f, 0.172059f + path.quadTo(SkBits2Float(0x41d63030), SkBits2Float(0x30303030), SkBits2Float(0x41013030), + SkBits2Float(0x00000000)); // 26.7735f, 6.40969e-10f, 8.07426f, 0 + path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 + canvas->drawPath(path, paint); +} + static void make_path0(SkPath* path) { // from * https://code.google.com/p/skia/issues/detail?id=1706 @@ -134,7 +315,7 @@ } } -#if defined(WIN32) +#ifdef SK_BUILD_FOR_WIN #define SUPPRESS_VISIBILITY_WARNING #else #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden"))) @@ -190,57 +371,6 @@ REPORTER_ASSERT(reporter, 95 == last.fY); } -static void test_android_specific_behavior(skiatest::Reporter* reporter) { -#ifdef SK_BUILD_FOR_ANDROID - // Make sure we treat fGenerationID and fSourcePath correctly for each of - // copy, assign, rewind, reset, and swap. - SkPath original, source, anotherSource; - original.setSourcePath(&source); - original.moveTo(0, 0); - original.lineTo(1, 1); - REPORTER_ASSERT(reporter, original.getSourcePath() == &source); - - uint32_t copyID, assignID; - - // Test copy constructor. Copy generation ID, copy source path. - SkPath copy(original); - REPORTER_ASSERT(reporter, copy.getGenerationID() == original.getGenerationID()); - REPORTER_ASSERT(reporter, copy.getSourcePath() == original.getSourcePath()); - - // Test assigment operator. Change generation ID, copy source path. - SkPath assign; - assignID = assign.getGenerationID(); - assign = original; - REPORTER_ASSERT(reporter, assign.getGenerationID() != assignID); - REPORTER_ASSERT(reporter, assign.getSourcePath() == original.getSourcePath()); - - // Test rewind. Change generation ID, don't touch source path. - copyID = copy.getGenerationID(); - copy.rewind(); - REPORTER_ASSERT(reporter, copy.getGenerationID() != copyID); - REPORTER_ASSERT(reporter, copy.getSourcePath() == original.getSourcePath()); - - // Test reset. Change generation ID, don't touch source path. - assignID = assign.getGenerationID(); - assign.reset(); - REPORTER_ASSERT(reporter, assign.getGenerationID() != assignID); - REPORTER_ASSERT(reporter, assign.getSourcePath() == original.getSourcePath()); - - // Test swap. Swap the generation IDs, swap source paths. - copy.reset(); - copy.moveTo(2, 2); - copy.setSourcePath(&anotherSource); - copyID = copy.getGenerationID(); - assign.moveTo(3, 3); - assignID = assign.getGenerationID(); - copy.swap(assign); - REPORTER_ASSERT(reporter, copy.getGenerationID() != copyID); - REPORTER_ASSERT(reporter, assign.getGenerationID() != assignID); - REPORTER_ASSERT(reporter, copy.getSourcePath() == original.getSourcePath()); - REPORTER_ASSERT(reporter, assign.getSourcePath() == &anotherSource); -#endif -} - static void test_gen_id(skiatest::Reporter* reporter) { SkPath a, b; REPORTER_ASSERT(reporter, a.getGenerationID() == b.getGenerationID()); @@ -275,7 +405,7 @@ REPORTER_ASSERT(reporter, a.getGenerationID() == x); REPORTER_ASSERT(reporter, w != x); -#ifdef SK_BUILD_FOR_ANDROID +#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK static bool kExpectGenIDToIgnoreFill = false; #else static bool kExpectGenIDToIgnoreFill = true; @@ -299,7 +429,7 @@ SkPaint paint; paint.setAntiAlias(true); - SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(84, 88)); + auto surface(SkSurface::MakeRasterN32Premul(84, 88)); surface->getCanvas()->drawPath(path, paint); } @@ -322,7 +452,7 @@ SkPath dst; // Before the fix, this would infinite-recurse, and run out of stack // because we would keep trying to subdivide a degenerate cubic segment. - paint.getFillPath(path, &dst, NULL); + paint.getFillPath(path, &dst, nullptr); } static void build_path_170666(SkPath& path) { @@ -418,7 +548,7 @@ SkPaint paint; paint.setAntiAlias(true); - SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(1000, 1000)); + auto surface(SkSurface::MakeRasterN32Premul(1000, 1000)); build_path_simple_170666(path); surface->getCanvas()->drawPath(path, paint); @@ -427,31 +557,104 @@ surface->getCanvas()->drawPath(path, paint); } + +static void test_tiny_path_convexity(skiatest::Reporter* reporter, const char* pathBug, + SkScalar tx, SkScalar ty, SkScalar scale) { + SkPath smallPath; + SkAssertResult(SkParsePath::FromSVGString(pathBug, &smallPath)); + bool smallConvex = smallPath.isConvex(); + SkPath largePath; + SkAssertResult(SkParsePath::FromSVGString(pathBug, &largePath)); + SkMatrix matrix; + matrix.reset(); + matrix.preTranslate(100, 100); + matrix.preScale(scale, scale); + largePath.transform(matrix); + bool largeConvex = largePath.isConvex(); + REPORTER_ASSERT(reporter, smallConvex == largeConvex); +} + +static void test_crbug_493450(skiatest::Reporter* reporter) { + const char reducedCase[] = + "M0,0" + "L0.0002, 0" + "L0.0002, 0.0002" + "L0.0001, 0.0001" + "L0,0.0002" + "Z"; + test_tiny_path_convexity(reporter, reducedCase, 100, 100, 100000); + const char originalFiddleData[] = + "M-0.3383152268862998,-0.11217565719203619L-0.33846085183212765,-0.11212264406895281" + "L-0.338509393480737,-0.11210607966681395L-0.33857792286700894,-0.1121889121487573" + "L-0.3383866116636664,-0.11228834570924921L-0.33842087635680235,-0.11246078673250548" + "L-0.33809536177201055,-0.11245415228342878L-0.33797257995493996,-0.11216571641452182" + "L-0.33802112160354925,-0.11201996164188659L-0.33819815585141844,-0.11218559834671019Z"; + test_tiny_path_convexity(reporter, originalFiddleData, 280081.4116670522f, 93268.04618493588f, + 826357.3384828606f); +} + +static void test_crbug_495894(skiatest::Reporter* reporter) { + const char originalFiddleData[] = + "M-0.34004273849857214,-0.11332803232216355L-0.34008271397389744,-0.11324483772714951" + "L-0.3401940742265893,-0.11324483772714951L-0.34017694188002134,-0.11329807920275889" + "L-0.3402026403998733,-0.11333468903941245L-0.34029972369709194,-0.11334134592705701" + "L-0.3403054344792813,-0.11344121970007795L-0.3403140006525653,-0.11351115418399343" + "L-0.34024261587519866,-0.11353446986281181L-0.3402197727464413,-0.11360442946144192" + "L-0.34013696640469604,-0.11359110237029302L-0.34009128014718143,-0.1135877707043939" + "L-0.3400598708451401,-0.11360776134112742L-0.34004273849857214,-0.11355112520064405" + "L-0.3400113291965308,-0.11355112520064405L-0.3399970522410575,-0.11359110237029302" + "L-0.33997135372120546,-0.11355112520064405L-0.3399627875479215,-0.11353780084493197" + "L-0.3399485105924481,-0.11350782354357004L-0.3400027630232468,-0.11346452910331437" + "L-0.3399485105924481,-0.11340126558629839L-0.33993994441916414,-0.11340126558629839" + "L-0.33988283659727087,-0.11331804756574679L-0.33989140277055485,-0.11324483772714951" + "L-0.33997991989448945,-0.11324483772714951L-0.3399856306766788,-0.11324483772714951" + "L-0.34002560615200417,-0.11334467443478255ZM-0.3400684370184241,-0.11338461985124307" + "L-0.340154098751264,-0.11341791238732665L-0.340162664924548,-0.1134378899559977" + "L-0.34017979727111597,-0.11340126558629839L-0.3401655203156427,-0.11338129083212668" + "L-0.34012268944922275,-0.11332137577529414L-0.34007414780061346,-0.11334467443478255Z" + "M-0.3400027630232468,-0.11290567901106024L-0.3400113291965308,-0.11298876531245433" + "L-0.33997991989448945,-0.11301535852306784L-0.33990282433493346,-0.11296217481488612" + "L-0.33993994441916414,-0.11288906492739594Z"; + test_tiny_path_convexity(reporter, originalFiddleData, 22682.240000000005f,7819.72220766405f, + 65536); +} + +static void test_crbug_613918() { + SkPath path; + path.conicTo(-6.62478e-08f, 4.13885e-08f, -6.36935e-08f, 3.97927e-08f, 0.729058f); + path.quadTo(2.28206e-09f, -1.42572e-09f, 3.91919e-09f, -2.44852e-09f); + path.cubicTo(-16752.2f, -26792.9f, -21.4673f, 10.9347f, -8.57322f, -7.22739f); + + // This call could lead to an assert or uninitialized read due to a failure + // to check the return value from SkCubicClipper::ChopMonoAtY. + path.contains(-1.84817e-08f, 1.15465e-08f); +} + static void test_addrect(skiatest::Reporter* reporter) { SkPath path; path.lineTo(0, 0); path.addRect(SkRect::MakeWH(50, 100)); - REPORTER_ASSERT(reporter, path.isRect(NULL)); + REPORTER_ASSERT(reporter, path.isRect(nullptr)); path.reset(); path.lineTo(FLT_EPSILON, FLT_EPSILON); path.addRect(SkRect::MakeWH(50, 100)); - REPORTER_ASSERT(reporter, !path.isRect(NULL)); + REPORTER_ASSERT(reporter, !path.isRect(nullptr)); path.reset(); path.quadTo(0, 0, 0, 0); path.addRect(SkRect::MakeWH(50, 100)); - REPORTER_ASSERT(reporter, !path.isRect(NULL)); + REPORTER_ASSERT(reporter, !path.isRect(nullptr)); path.reset(); path.conicTo(0, 0, 0, 0, 0.5f); path.addRect(SkRect::MakeWH(50, 100)); - REPORTER_ASSERT(reporter, !path.isRect(NULL)); + REPORTER_ASSERT(reporter, !path.isRect(nullptr)); path.reset(); path.cubicTo(0, 0, 0, 0, 0, 0); path.addRect(SkRect::MakeWH(50, 100)); - REPORTER_ASSERT(reporter, !path.isRect(NULL)); + REPORTER_ASSERT(reporter, !path.isRect(nullptr)); } // Make sure we stay non-finite once we get there (unless we reset or rewind). @@ -497,7 +700,7 @@ } static void test_clipped_cubic() { - SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(640, 480)); + auto surface(SkSurface::MakeRasterN32Premul(640, 480)); // This path used to assert, because our cubic-chopping code incorrectly // moved control points after the chop. This test should be run in SK_DEBUG @@ -514,6 +717,56 @@ } } +static void dump_if_ne(skiatest::Reporter* reporter, const SkRect& expected, const SkRect& bounds) { + if (expected != bounds) { + ERRORF(reporter, "path.getBounds() returned [%g %g %g %g], but expected [%g %g %g %g]", + bounds.left(), bounds.top(), bounds.right(), bounds.bottom(), + expected.left(), expected.top(), expected.right(), expected.bottom()); + } +} + +static void test_bounds_crbug_513799(skiatest::Reporter* reporter) { + SkPath path; +#if 0 + // As written these tests were failing on LLVM 4.2 MacMini Release mysteriously, so we've + // rewritten them to avoid this (compiler-bug?). + REPORTER_ASSERT(reporter, SkRect::MakeLTRB(0, 0, 0, 0) == path.getBounds()); + + path.moveTo(-5, -8); + REPORTER_ASSERT(reporter, SkRect::MakeLTRB(-5, -8, -5, -8) == path.getBounds()); + + path.addRect(SkRect::MakeLTRB(1, 2, 3, 4)); + REPORTER_ASSERT(reporter, SkRect::MakeLTRB(-5, -8, 3, 4) == path.getBounds()); + + path.moveTo(1, 2); + REPORTER_ASSERT(reporter, SkRect::MakeLTRB(-5, -8, 3, 4) == path.getBounds()); +#else + dump_if_ne(reporter, SkRect::MakeLTRB(0, 0, 0, 0), path.getBounds()); + + path.moveTo(-5, -8); // should set the bounds + dump_if_ne(reporter, SkRect::MakeLTRB(-5, -8, -5, -8), path.getBounds()); + + path.addRect(SkRect::MakeLTRB(1, 2, 3, 4)); // should extend the bounds + dump_if_ne(reporter, SkRect::MakeLTRB(-5, -8, 3, 4), path.getBounds()); + + path.moveTo(1, 2); // don't expect this to have changed the bounds + dump_if_ne(reporter, SkRect::MakeLTRB(-5, -8, 3, 4), path.getBounds()); +#endif +} + +#include "SkSurface.h" +static void test_fuzz_crbug_627414(skiatest::Reporter* reporter) { + SkPath path; + path.moveTo(0, 0); + path.conicTo(3.58732e-43f, 2.72084f, 3.00392f, 3.00392f, 8.46e+37f); + + SkPaint paint; + paint.setAntiAlias(true); + + auto surf = SkSurface::MakeRasterN32Premul(100, 100); + surf->getCanvas()->drawPath(path, paint); +} + // Inspired by http://ie.microsoft.com/testdrive/Performance/Chalkboard/ // which triggered an assert, from a tricky cubic. This test replicates that // example, so we can ensure that we handle it (in SkEdge.cpp), and don't @@ -533,9 +786,7 @@ SkPaint paint; paint.setAntiAlias(true); - SkSurface* surface = SkSurface::NewRasterPMColor(19, 130); - surface->getCanvas()->drawPath(path, paint); - surface->unref(); + SkSurface::MakeRasterN32Premul(19, 130)->getCanvas()->drawPath(path, paint); } // Inspired by http://code.google.com/p/chromium/issues/detail?id=141651 @@ -725,6 +976,23 @@ test_path_isfinite(reporter); } +static void test_islastcontourclosed(skiatest::Reporter* reporter) { + SkPath path; + REPORTER_ASSERT(reporter, !path.isLastContourClosed()); + path.moveTo(0, 0); + REPORTER_ASSERT(reporter, !path.isLastContourClosed()); + path.close(); + REPORTER_ASSERT(reporter, path.isLastContourClosed()); + path.lineTo(100, 100); + REPORTER_ASSERT(reporter, !path.isLastContourClosed()); + path.moveTo(200, 200); + REPORTER_ASSERT(reporter, !path.isLastContourClosed()); + path.close(); + REPORTER_ASSERT(reporter, path.isLastContourClosed()); + path.moveTo(0, 0); + REPORTER_ASSERT(reporter, !path.isLastContourClosed()); +} + // assert that we always // start with a moveTo // only have 1 moveTo @@ -787,7 +1055,7 @@ for (int doClose = 0; doClose <= 1; ++doClose) { for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) { SkPath path; - path.addPoly(pts, count, SkToBool(doClose)); + path.addPoly(pts, SkToInt(count), SkToBool(doClose)); test_poly(reporter, path, pts, SkToBool(doClose)); } } @@ -815,30 +1083,30 @@ // Set this for paths that don't have a consistent direction such as a bowtie. // (cheapComputeDirection is not expected to catch these.) -static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1); +const SkPathPriv::FirstDirection kDontCheckDir = static_cast<SkPathPriv::FirstDirection>(-1); static void check_direction(skiatest::Reporter* reporter, const SkPath& path, - SkPath::Direction expected) { + SkPathPriv::FirstDirection expected) { if (expected == kDontCheckDir) { return; } SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path. - SkPath::Direction dir; - if (copy.cheapComputeDirection(&dir)) { + SkPathPriv::FirstDirection dir; + if (SkPathPriv::CheapComputeFirstDirection(copy, &dir)) { REPORTER_ASSERT(reporter, dir == expected); } else { - REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected); + REPORTER_ASSERT(reporter, SkPathPriv::kUnknown_FirstDirection == expected); } } static void test_direction(skiatest::Reporter* reporter) { size_t i; SkPath path; - REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL)); - REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction)); - REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction)); - REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction)); + REPORTER_ASSERT(reporter, !SkPathPriv::CheapComputeFirstDirection(path, nullptr)); + REPORTER_ASSERT(reporter, !SkPathPriv::CheapIsFirstDirection(path, SkPathPriv::kCW_FirstDirection)); + REPORTER_ASSERT(reporter, !SkPathPriv::CheapIsFirstDirection(path, SkPathPriv::kCCW_FirstDirection)); + REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(path, SkPathPriv::kUnknown_FirstDirection)); static const char* gDegen[] = { "M 10 10", @@ -852,7 +1120,7 @@ path.reset(); bool valid = SkParsePath::FromSVGString(gDegen[i], &path); REPORTER_ASSERT(reporter, valid); - REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL)); + REPORTER_ASSERT(reporter, !SkPathPriv::CheapComputeFirstDirection(path, nullptr)); } static const char* gCW[] = { @@ -868,7 +1136,7 @@ path.reset(); bool valid = SkParsePath::FromSVGString(gCW[i], &path); REPORTER_ASSERT(reporter, valid); - check_direction(reporter, path, SkPath::kCW_Direction); + check_direction(reporter, path, SkPathPriv::kCW_FirstDirection); } static const char* gCCW[] = { @@ -884,7 +1152,7 @@ path.reset(); bool valid = SkParsePath::FromSVGString(gCCW[i], &path); REPORTER_ASSERT(reporter, valid); - check_direction(reporter, path, SkPath::kCCW_Direction); + check_direction(reporter, path, SkPathPriv::kCCW_FirstDirection); } // Test two donuts, each wound a different direction. Only the outer contour @@ -892,12 +1160,12 @@ path.reset(); path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction); path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction); - check_direction(reporter, path, SkPath::kCW_Direction); + check_direction(reporter, path, SkPathPriv::kCW_FirstDirection); path.reset(); path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction); path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction); - check_direction(reporter, path, SkPath::kCCW_Direction); + check_direction(reporter, path, SkPathPriv::kCCW_FirstDirection); // triangle with one point really far from the origin. path.reset(); @@ -905,19 +1173,19 @@ path.moveTo(SkBits2Float(0x501c7652), SkBits2Float(0x501c7652)); path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1); path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1); - check_direction(reporter, path, SkPath::kCCW_Direction); + check_direction(reporter, path, SkPathPriv::kCCW_FirstDirection); path.reset(); path.conicTo(20, 0, 20, 20, 0.5f); path.close(); - check_direction(reporter, path, SkPath::kCW_Direction); + check_direction(reporter, path, SkPathPriv::kCW_FirstDirection); path.reset(); path.lineTo(1, 1e7f); path.lineTo(1e7f, 2e7f); path.close(); REPORTER_ASSERT(reporter, SkPath::kConvex_Convexity == path.getConvexity()); - check_direction(reporter, path, SkPath::kCCW_Direction); + check_direction(reporter, path, SkPathPriv::kCCW_FirstDirection); } static void add_rect(SkPath* path, const SkRect& r) { @@ -1084,19 +1352,47 @@ REPORTER_ASSERT(reporter, c == expected); } +static void test_path_crbug389050(skiatest::Reporter* reporter) { + SkPath tinyConvexPolygon; + tinyConvexPolygon.moveTo(600.131559f, 800.112512f); + tinyConvexPolygon.lineTo(600.161735f, 800.118627f); + tinyConvexPolygon.lineTo(600.148962f, 800.142338f); + tinyConvexPolygon.lineTo(600.134891f, 800.137724f); + tinyConvexPolygon.close(); + tinyConvexPolygon.getConvexity(); + check_convexity(reporter, tinyConvexPolygon, SkPath::kConvex_Convexity); + check_direction(reporter, tinyConvexPolygon, SkPathPriv::kCW_FirstDirection); + + SkPath platTriangle; + platTriangle.moveTo(0, 0); + platTriangle.lineTo(200, 0); + platTriangle.lineTo(100, 0.04f); + platTriangle.close(); + platTriangle.getConvexity(); + check_direction(reporter, platTriangle, SkPathPriv::kCW_FirstDirection); + + platTriangle.reset(); + platTriangle.moveTo(0, 0); + platTriangle.lineTo(200, 0); + platTriangle.lineTo(100, 0.03f); + platTriangle.close(); + platTriangle.getConvexity(); + check_direction(reporter, platTriangle, SkPathPriv::kCW_FirstDirection); +} + static void test_convexity2(skiatest::Reporter* reporter) { SkPath pt; pt.moveTo(0, 0); pt.close(); check_convexity(reporter, pt, SkPath::kConvex_Convexity); - check_direction(reporter, pt, SkPath::kUnknown_Direction); + check_direction(reporter, pt, SkPathPriv::kUnknown_FirstDirection); SkPath line; line.moveTo(12*SK_Scalar1, 20*SK_Scalar1); line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1); line.close(); check_convexity(reporter, line, SkPath::kConvex_Convexity); - check_direction(reporter, line, SkPath::kUnknown_Direction); + check_direction(reporter, line, SkPathPriv::kUnknown_FirstDirection); SkPath triLeft; triLeft.moveTo(0, 0); @@ -1104,7 +1400,7 @@ triLeft.lineTo(SK_Scalar1, SK_Scalar1); triLeft.close(); check_convexity(reporter, triLeft, SkPath::kConvex_Convexity); - check_direction(reporter, triLeft, SkPath::kCW_Direction); + check_direction(reporter, triLeft, SkPathPriv::kCW_FirstDirection); SkPath triRight; triRight.moveTo(0, 0); @@ -1112,7 +1408,7 @@ triRight.lineTo(SK_Scalar1, SK_Scalar1); triRight.close(); check_convexity(reporter, triRight, SkPath::kConvex_Convexity); - check_direction(reporter, triRight, SkPath::kCCW_Direction); + check_direction(reporter, triRight, SkPathPriv::kCCW_FirstDirection); SkPath square; square.moveTo(0, 0); @@ -1121,7 +1417,7 @@ square.lineTo(0, SK_Scalar1); square.close(); check_convexity(reporter, square, SkPath::kConvex_Convexity); - check_direction(reporter, square, SkPath::kCW_Direction); + check_direction(reporter, square, SkPathPriv::kCW_FirstDirection); SkPath redundantSquare; redundantSquare.moveTo(0, 0); @@ -1138,7 +1434,7 @@ redundantSquare.lineTo(0, SK_Scalar1); redundantSquare.close(); check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity); - check_direction(reporter, redundantSquare, SkPath::kCW_Direction); + check_direction(reporter, redundantSquare, SkPathPriv::kCW_FirstDirection); SkPath bowTie; bowTie.moveTo(0, 0); @@ -1177,9 +1473,9 @@ dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1); dent.close(); check_convexity(reporter, dent, SkPath::kConcave_Convexity); - check_direction(reporter, dent, SkPath::kCW_Direction); + check_direction(reporter, dent, SkPathPriv::kCW_FirstDirection); - // http://skbug.com/2235 + // https://bug.skia.org/2235 SkPath strokedSin; for (int i = 0; i < 2000; i++) { SkScalar x = SkIntToScalar(i) / 2; @@ -1205,7 +1501,21 @@ degenerateConcave.lineTo(-55.971577f, 460.0f); degenerateConcave.lineTo(41.446522f, 376.25f); check_convexity(reporter, degenerateConcave, SkPath::kConcave_Convexity); - check_direction(reporter, degenerateConcave, SkPath::kUnknown_Direction); + check_direction(reporter, degenerateConcave, SkPathPriv::kUnknown_FirstDirection); + + // http://crbug.com/433683 + SkPath badFirstVector; + badFirstVector.moveTo(501.087708f, 319.610352f); + badFirstVector.lineTo(501.087708f, 319.610352f); + badFirstVector.cubicTo(501.087677f, 319.610321f, 449.271606f, 258.078674f, 395.084564f, 198.711182f); + badFirstVector.cubicTo(358.967072f, 159.140717f, 321.910553f, 120.650436f, 298.442322f, 101.955399f); + badFirstVector.lineTo(301.557678f, 98.044601f); + badFirstVector.cubicTo(325.283844f, 116.945084f, 362.615204f, 155.720825f, 398.777557f, 195.340454f); + badFirstVector.cubicTo(453.031860f, 254.781662f, 504.912262f, 316.389618f, 504.912292f, 316.389648f); + badFirstVector.lineTo(504.912292f, 316.389648f); + badFirstVector.lineTo(501.087708f, 319.610352f); + badFirstVector.close(); + check_convexity(reporter, badFirstVector, SkPath::kConcave_Convexity); } static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p, @@ -1228,7 +1538,7 @@ while (str) { SkScalar x, y; str = SkParse::FindScalar(str, &x); - if (NULL == str) { + if (nullptr == str) { break; } str = SkParse::FindScalar(str, &y); @@ -1254,26 +1564,30 @@ path.reset(); path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction); check_convexity(reporter, path, SkPath::kConvex_Convexity); - REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction)); + REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(path, SkPathPriv::kCCW_FirstDirection)); path.reset(); path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction); check_convexity(reporter, path, SkPath::kConvex_Convexity); - REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction)); + REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(path, SkPathPriv::kCW_FirstDirection)); + + path.reset(); + path.quadTo(100, 100, 50, 50); // This is a convex path from GM:convexpaths + check_convexity(reporter, path, SkPath::kConvex_Convexity); static const struct { - const char* fPathStr; - SkPath::Convexity fExpectedConvexity; - SkPath::Direction fExpectedDirection; + const char* fPathStr; + SkPath::Convexity fExpectedConvexity; + SkPathPriv::FirstDirection fExpectedDirection; } gRec[] = { - { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction }, - { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction }, - { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction }, - { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction }, - { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction }, - { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction }, + { "", SkPath::kConvex_Convexity, SkPathPriv::kUnknown_FirstDirection }, + { "0 0", SkPath::kConvex_Convexity, SkPathPriv::kUnknown_FirstDirection }, + { "0 0 10 10", SkPath::kConvex_Convexity, SkPathPriv::kUnknown_FirstDirection }, + { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPathPriv::kUnknown_FirstDirection }, + { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPathPriv::kCW_FirstDirection }, + { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPathPriv::kCCW_FirstDirection }, { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir }, - { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction }, + { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPathPriv::kCW_FirstDirection }, }; for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { @@ -1284,9 +1598,9 @@ // check after setting the initial convex and direction if (kDontCheckDir != gRec[i].fExpectedDirection) { SkPath copy(path); - SkPath::Direction dir; - bool foundDir = copy.cheapComputeDirection(&dir); - REPORTER_ASSERT(reporter, (gRec[i].fExpectedDirection == SkPath::kUnknown_Direction) + SkPathPriv::FirstDirection dir; + bool foundDir = SkPathPriv::CheapComputeFirstDirection(copy, &dir); + REPORTER_ASSERT(reporter, (gRec[i].fExpectedDirection == SkPathPriv::kUnknown_FirstDirection) ^ foundDir); REPORTER_ASSERT(reporter, !foundDir || gRec[i].fExpectedDirection == dir); check_convexity(reporter, copy, gRec[i].fExpectedConvexity); @@ -1294,6 +1608,79 @@ REPORTER_ASSERT(reporter, gRec[i].fExpectedConvexity == path.getConvexity()); check_direction(reporter, path, gRec[i].fExpectedDirection); } + + static const SkPoint nonFinitePts[] = { + { SK_ScalarInfinity, 0 }, + { 0, SK_ScalarInfinity }, + { SK_ScalarInfinity, SK_ScalarInfinity }, + { SK_ScalarNegativeInfinity, 0}, + { 0, SK_ScalarNegativeInfinity }, + { SK_ScalarNegativeInfinity, SK_ScalarNegativeInfinity }, + { SK_ScalarNegativeInfinity, SK_ScalarInfinity }, + { SK_ScalarInfinity, SK_ScalarNegativeInfinity }, + { SK_ScalarNaN, 0 }, + { 0, SK_ScalarNaN }, + { SK_ScalarNaN, SK_ScalarNaN }, + }; + + const size_t nonFinitePtsCount = sizeof(nonFinitePts) / sizeof(nonFinitePts[0]); + + static const SkPoint finitePts[] = { + { SK_ScalarMax, 0 }, + { 0, SK_ScalarMax }, + { SK_ScalarMax, SK_ScalarMax }, + { SK_ScalarMin, 0 }, + { 0, SK_ScalarMin }, + { SK_ScalarMin, SK_ScalarMin }, + }; + + const size_t finitePtsCount = sizeof(finitePts) / sizeof(finitePts[0]); + + for (int index = 0; index < (int) (13 * nonFinitePtsCount * finitePtsCount); ++index) { + int i = (int) (index % nonFinitePtsCount); + int f = (int) (index % finitePtsCount); + int g = (int) ((f + 1) % finitePtsCount); + path.reset(); + switch (index % 13) { + case 0: path.lineTo(nonFinitePts[i]); break; + case 1: path.quadTo(nonFinitePts[i], nonFinitePts[i]); break; + case 2: path.quadTo(nonFinitePts[i], finitePts[f]); break; + case 3: path.quadTo(finitePts[f], nonFinitePts[i]); break; + case 4: path.cubicTo(nonFinitePts[i], finitePts[f], finitePts[f]); break; + case 5: path.cubicTo(finitePts[f], nonFinitePts[i], finitePts[f]); break; + case 6: path.cubicTo(finitePts[f], finitePts[f], nonFinitePts[i]); break; + case 7: path.cubicTo(nonFinitePts[i], nonFinitePts[i], finitePts[f]); break; + case 8: path.cubicTo(nonFinitePts[i], finitePts[f], nonFinitePts[i]); break; + case 9: path.cubicTo(finitePts[f], nonFinitePts[i], nonFinitePts[i]); break; + case 10: path.cubicTo(nonFinitePts[i], nonFinitePts[i], nonFinitePts[i]); break; + case 11: path.cubicTo(nonFinitePts[i], finitePts[f], finitePts[g]); break; + case 12: path.moveTo(nonFinitePts[i]); break; + } + check_convexity(reporter, path, SkPath::kUnknown_Convexity); + } + + for (int index = 0; index < (int) (11 * finitePtsCount); ++index) { + int f = (int) (index % finitePtsCount); + int g = (int) ((f + 1) % finitePtsCount); + path.reset(); + int curveSelect = index % 11; + switch (curveSelect) { + case 0: path.moveTo(finitePts[f]); break; + case 1: path.lineTo(finitePts[f]); break; + case 2: path.quadTo(finitePts[f], finitePts[f]); break; + case 3: path.quadTo(finitePts[f], finitePts[g]); break; + case 4: path.quadTo(finitePts[g], finitePts[f]); break; + case 5: path.cubicTo(finitePts[f], finitePts[f], finitePts[f]); break; + case 6: path.cubicTo(finitePts[f], finitePts[f], finitePts[g]); break; + case 7: path.cubicTo(finitePts[f], finitePts[g], finitePts[f]); break; + case 8: path.cubicTo(finitePts[f], finitePts[g], finitePts[g]); break; + case 9: path.cubicTo(finitePts[g], finitePts[f], finitePts[f]); break; + case 10: path.cubicTo(finitePts[g], finitePts[f], finitePts[g]); break; + } + check_convexity(reporter, path, curveSelect == 0 ? SkPath::kConvex_Convexity + : SkPath::kUnknown_Convexity); + } + } static void test_isLine(skiatest::Reporter* reporter) { @@ -1301,7 +1688,7 @@ SkPoint pts[2]; const SkScalar value = SkIntToScalar(5); - REPORTER_ASSERT(reporter, !path.isLine(NULL)); + REPORTER_ASSERT(reporter, !path.isLine(nullptr)); // set some non-zero values pts[0].set(value, value); @@ -1316,7 +1703,7 @@ REPORTER_ASSERT(reporter, value != moveX && value != moveY); path.moveTo(moveX, moveY); - REPORTER_ASSERT(reporter, !path.isLine(NULL)); + REPORTER_ASSERT(reporter, !path.isLine(nullptr)); REPORTER_ASSERT(reporter, !path.isLine(pts)); // check that pts was untouched REPORTER_ASSERT(reporter, pts[0].equals(value, value)); @@ -1327,7 +1714,7 @@ REPORTER_ASSERT(reporter, value != lineX && value != lineY); path.lineTo(lineX, lineY); - REPORTER_ASSERT(reporter, path.isLine(NULL)); + REPORTER_ASSERT(reporter, path.isLine(nullptr)); REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY)); REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY)); @@ -1336,14 +1723,14 @@ REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY)); path.lineTo(0, 0); // too many points/verbs - REPORTER_ASSERT(reporter, !path.isLine(NULL)); + REPORTER_ASSERT(reporter, !path.isLine(nullptr)); REPORTER_ASSERT(reporter, !path.isLine(pts)); REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY)); REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY)); path.reset(); path.quadTo(1, 1, 2, 2); - REPORTER_ASSERT(reporter, !path.isLine(NULL)); + REPORTER_ASSERT(reporter, !path.isLine(nullptr)); } static void test_conservativelyContains(skiatest::Reporter* reporter) { @@ -1354,7 +1741,7 @@ // A circle that bounds kBaseRect (with a significant amount of slop) SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height()); - circleR = SkScalarMul(circleR, 1.75f) / 2; + circleR *= 1.75f / 2; static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()}; // round-rect radii @@ -1519,7 +1906,7 @@ // Test that multiple move commands do not cause asserts. // At the time of writing, this would not modify cached convexity. This caused an assert while - // checking conservative containment again. http://skbug.com/1460 + // checking conservative containment again. https://bug.skia.org/1460 path.moveTo(SkIntToScalar(100), SkIntToScalar(100)); #if 0 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0, @@ -1538,6 +1925,18 @@ SkIntToScalar(10), SkIntToScalar(10)))); + // Same as above path and first test but with the extra moveTo making a degenerate sub-path + // following the non-empty sub-path. Verifies that this does not trigger assertions. + path.reset(); + path.moveTo(0, 0); + path.lineTo(SkIntToScalar(100), 0); + path.lineTo(0, SkIntToScalar(100)); + path.moveTo(100, 100); + + REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0, + SkIntToScalar(10), + SkIntToScalar(10)))); + // Test that multiple move commands do not cause asserts and that the function // is not confused by the multiple moves. path.reset(); @@ -1565,10 +1964,8 @@ path.moveTo(0, 0); path.lineTo(1, 0); path.lineTo(1, 1); path.lineTo(0, 1); path.close(); - REPORTER_ASSERT(reporter, path.isRect(NULL, NULL)); - REPORTER_ASSERT(reporter, path.isRect(&isClosed, NULL)); + REPORTER_ASSERT(reporter, path.isRect(nullptr, &isClosed, nullptr)); REPORTER_ASSERT(reporter, isClosed); - REPORTER_ASSERT(reporter, SkPath::kStroke_PathAsRect == path.asRect(NULL)); } // Simple isRect test is inline TestPath, below. @@ -1619,7 +2016,7 @@ struct IsRectTest { SkPoint *fPoints; - size_t fPointCount; + int fPointCount; bool fClose; bool fIsRect; } tests[] = { @@ -1661,7 +2058,7 @@ }; const size_t testCount = SK_ARRAY_COUNT(tests); - size_t index; + int index; for (size_t testIndex = 0; testIndex < testCount; ++testIndex) { SkPath path; path.moveTo(tests[testIndex].fPoints[0].fX, tests[testIndex].fPoints[0].fY); @@ -1671,105 +2068,222 @@ if (tests[testIndex].fClose) { path.close(); } - REPORTER_ASSERT(reporter, tests[testIndex].fIsRect == path.isRect(NULL)); - REPORTER_ASSERT(reporter, tests[testIndex].fIsRect == path.isRect(NULL, NULL)); + REPORTER_ASSERT(reporter, tests[testIndex].fIsRect == path.isRect(nullptr)); if (tests[testIndex].fIsRect) { SkRect computed, expected; - expected.set(tests[testIndex].fPoints, tests[testIndex].fPointCount); - REPORTER_ASSERT(reporter, path.isRect(&computed)); - REPORTER_ASSERT(reporter, expected == computed); - bool isClosed; - SkPath::Direction direction, cheapDirection; - REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection)); - REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction)); + SkPath::Direction direction; + SkPathPriv::FirstDirection cheapDirection; + expected.set(tests[testIndex].fPoints, tests[testIndex].fPointCount); + REPORTER_ASSERT(reporter, SkPathPriv::CheapComputeFirstDirection(path, &cheapDirection)); + REPORTER_ASSERT(reporter, path.isRect(&computed, &isClosed, &direction)); + REPORTER_ASSERT(reporter, expected == computed); REPORTER_ASSERT(reporter, isClosed == tests[testIndex].fClose); - REPORTER_ASSERT(reporter, direction == cheapDirection); - direction = (SkPath::Direction) -1; - if (!tests[testIndex].fClose) { - REPORTER_ASSERT(reporter, SkPath::kFill_PathAsRect == path.asRect()); - REPORTER_ASSERT(reporter, SkPath::kFill_PathAsRect == path.asRect(&direction)); - } else { - REPORTER_ASSERT(reporter, SkPath::kStroke_PathAsRect == path.asRect()); - REPORTER_ASSERT(reporter, SkPath::kStroke_PathAsRect == path.asRect(&direction)); - } - REPORTER_ASSERT(reporter, direction == cheapDirection); + REPORTER_ASSERT(reporter, SkPathPriv::AsFirstDirection(direction) == cheapDirection); } else { SkRect computed; computed.set(123, 456, 789, 1011); - REPORTER_ASSERT(reporter, !path.isRect(&computed)); + bool isClosed = (bool)-1; + SkPath::Direction direction = (SkPath::Direction) - 1; + REPORTER_ASSERT(reporter, !path.isRect(&computed, &isClosed, &direction)); REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456); REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011); - - bool isClosed = (bool) -1; - SkPath::Direction direction = (SkPath::Direction) -1; - REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction)); REPORTER_ASSERT(reporter, isClosed == (bool) -1); REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1); - REPORTER_ASSERT(reporter, SkPath::kNone_PathAsRect == path.asRect()); - REPORTER_ASSERT(reporter, SkPath::kNone_PathAsRect == path.asRect(&direction)); - REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1); } } // fail, close then line SkPath path1; path1.moveTo(r1[0].fX, r1[0].fY); - for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) { + for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) { path1.lineTo(r1[index].fX, r1[index].fY); } path1.close(); path1.lineTo(1, 0); - REPORTER_ASSERT(reporter, !path1.isRect(NULL)); + REPORTER_ASSERT(reporter, !path1.isRect(nullptr)); // fail, move in the middle path1.reset(); path1.moveTo(r1[0].fX, r1[0].fY); - for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) { + for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) { if (index == 2) { path1.moveTo(1, .5f); } path1.lineTo(r1[index].fX, r1[index].fY); } path1.close(); - REPORTER_ASSERT(reporter, !path1.isRect(NULL)); + REPORTER_ASSERT(reporter, !path1.isRect(nullptr)); // fail, move on the edge path1.reset(); - for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) { + for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) { path1.moveTo(r1[index - 1].fX, r1[index - 1].fY); path1.lineTo(r1[index].fX, r1[index].fY); } path1.close(); - REPORTER_ASSERT(reporter, !path1.isRect(NULL)); + REPORTER_ASSERT(reporter, !path1.isRect(nullptr)); // fail, quad path1.reset(); path1.moveTo(r1[0].fX, r1[0].fY); - for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) { + for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) { if (index == 2) { path1.quadTo(1, .5f, 1, .5f); } path1.lineTo(r1[index].fX, r1[index].fY); } path1.close(); - REPORTER_ASSERT(reporter, !path1.isRect(NULL)); + REPORTER_ASSERT(reporter, !path1.isRect(nullptr)); // fail, cubic path1.reset(); path1.moveTo(r1[0].fX, r1[0].fY); - for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) { + for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) { if (index == 2) { path1.cubicTo(1, .5f, 1, .5f, 1, .5f); } path1.lineTo(r1[index].fX, r1[index].fY); } path1.close(); - REPORTER_ASSERT(reporter, !path1.isRect(NULL)); + REPORTER_ASSERT(reporter, !path1.isRect(nullptr)); } -static void test_isNestedRects(skiatest::Reporter* reporter) { +static void check_simple_closed_rect(skiatest::Reporter* reporter, const SkPath& path, + const SkRect& rect, SkPath::Direction dir, unsigned start) { + SkRect r = SkRect::MakeEmpty(); + SkPath::Direction d = SkPath::kCCW_Direction; + unsigned s = ~0U; + + REPORTER_ASSERT(reporter, SkPathPriv::IsSimpleClosedRect(path, &r, &d, &s)); + REPORTER_ASSERT(reporter, r == rect); + REPORTER_ASSERT(reporter, d == dir); + REPORTER_ASSERT(reporter, s == start); +} + +static void test_is_simple_closed_rect(skiatest::Reporter* reporter) { + SkRect r = SkRect::MakeEmpty(); + SkPath::Direction d = SkPath::kCCW_Direction; + unsigned s = ~0U; + + const SkRect testRect = SkRect::MakeXYWH(10, 10, 50, 70); + const SkRect emptyRect = SkRect::MakeEmpty(); + SkPath path; + for (int start = 0; start < 4; ++start) { + for (auto dir : {SkPath::kCCW_Direction, SkPath::kCW_Direction}) { + SkPath path; + path.addRect(testRect, dir, start); + check_simple_closed_rect(reporter, path, testRect, dir, start); + path.close(); + check_simple_closed_rect(reporter, path, testRect, dir, start); + SkPath path2 = path; + path2.lineTo(10, 10); + REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s)); + path2 = path; + path2.moveTo(10, 10); + REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s)); + path2 = path; + path2.addRect(testRect, dir, start); + REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s)); + // Make the path by hand, manually closing it. + path2.reset(); + SkPath::RawIter iter(path); + SkPath::Verb v; + SkPoint verbPts[4]; + SkPoint firstPt = {0.f, 0.f}; + while ((v = iter.next(verbPts)) != SkPath::kDone_Verb) { + switch(v) { + case SkPath::kMove_Verb: + firstPt = verbPts[0]; + path2.moveTo(verbPts[0]); + break; + case SkPath::kLine_Verb: + path2.lineTo(verbPts[1]); + break; + default: + break; + } + } + // We haven't closed it yet... + REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s)); + // ... now we do and test again. + path2.lineTo(firstPt); + check_simple_closed_rect(reporter, path2, testRect, dir, start); + // A redundant close shouldn't cause a failure. + path2.close(); + check_simple_closed_rect(reporter, path2, testRect, dir, start); + // Degenerate point and line rects are not allowed + path2.reset(); + path2.addRect(emptyRect, dir, start); + REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s)); + SkRect degenRect = testRect; + degenRect.fLeft = degenRect.fRight; + path2.reset(); + path2.addRect(degenRect, dir, start); + REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s)); + degenRect = testRect; + degenRect.fTop = degenRect.fBottom; + path2.reset(); + path2.addRect(degenRect, dir, start); + REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s)); + // An inverted rect makes a rect path, but changes the winding dir and start point. + SkPath::Direction swapDir = (dir == SkPath::kCW_Direction) + ? SkPath::kCCW_Direction + : SkPath::kCW_Direction; + static constexpr unsigned kXSwapStarts[] = { 1, 0, 3, 2 }; + static constexpr unsigned kYSwapStarts[] = { 3, 2, 1, 0 }; + SkRect swapRect = testRect; + SkTSwap(swapRect.fLeft, swapRect.fRight); + path2.reset(); + path2.addRect(swapRect, dir, start); + check_simple_closed_rect(reporter, path2, testRect, swapDir, kXSwapStarts[start]); + swapRect = testRect; + SkTSwap(swapRect.fTop, swapRect.fBottom); + path2.reset(); + path2.addRect(swapRect, dir, start); + check_simple_closed_rect(reporter, path2, testRect, swapDir, kYSwapStarts[start]); + } + } + // down, up, left, close + path.reset(); + path.moveTo(1, 1); + path.lineTo(1, 2); + path.lineTo(1, 1); + path.lineTo(0, 1); + SkRect rect; + SkPath::Direction dir; + unsigned start; + path.close(); + REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path, &rect, &dir, &start)); + // right, left, up, close + path.reset(); + path.moveTo(1, 1); + path.lineTo(2, 1); + path.lineTo(1, 1); + path.lineTo(1, 0); + path.close(); + REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path, &rect, &dir, &start)); + // parallelogram with horizontal edges + path.reset(); + path.moveTo(1, 0); + path.lineTo(3, 0); + path.lineTo(2, 1); + path.lineTo(0, 1); + path.close(); + REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path, &rect, &dir, &start)); + // parallelogram with vertical edges + path.reset(); + path.moveTo(0, 1); + path.lineTo(0, 3); + path.lineTo(1, 2); + path.lineTo(1, 0); + path.close(); + REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path, &rect, &dir, &start)); + +} + +static void test_isNestedFillRects(skiatest::Reporter* reporter) { // passing tests (all moveTo / lineTo... SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}}; @@ -1796,47 +2310,47 @@ SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L' - // failing, no close + // success, no close is OK SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto struct IsNestedRectTest { SkPoint *fPoints; - size_t fPointCount; - SkPath::Direction fDirection; + int fPointCount; + SkPathPriv::FirstDirection fDirection; bool fClose; bool fIsNestedRect; // nests with path.addRect(-1, -1, 2, 2); } tests[] = { - { r1, SK_ARRAY_COUNT(r1), SkPath::kCW_Direction , true, true }, - { r2, SK_ARRAY_COUNT(r2), SkPath::kCW_Direction , true, true }, - { r3, SK_ARRAY_COUNT(r3), SkPath::kCW_Direction , true, true }, - { r4, SK_ARRAY_COUNT(r4), SkPath::kCW_Direction , true, true }, - { r5, SK_ARRAY_COUNT(r5), SkPath::kCCW_Direction, true, true }, - { r6, SK_ARRAY_COUNT(r6), SkPath::kCCW_Direction, true, true }, - { r7, SK_ARRAY_COUNT(r7), SkPath::kCCW_Direction, true, true }, - { r8, SK_ARRAY_COUNT(r8), SkPath::kCCW_Direction, true, true }, - { r9, SK_ARRAY_COUNT(r9), SkPath::kCCW_Direction, true, true }, - { ra, SK_ARRAY_COUNT(ra), SkPath::kCCW_Direction, true, true }, - { rb, SK_ARRAY_COUNT(rb), SkPath::kCW_Direction, true, true }, - { rc, SK_ARRAY_COUNT(rc), SkPath::kCW_Direction, true, true }, - { rd, SK_ARRAY_COUNT(rd), SkPath::kCCW_Direction, true, true }, - { re, SK_ARRAY_COUNT(re), SkPath::kCW_Direction, true, true }, + { r1, SK_ARRAY_COUNT(r1), SkPathPriv::kCW_FirstDirection , true, true }, + { r2, SK_ARRAY_COUNT(r2), SkPathPriv::kCW_FirstDirection , true, true }, + { r3, SK_ARRAY_COUNT(r3), SkPathPriv::kCW_FirstDirection , true, true }, + { r4, SK_ARRAY_COUNT(r4), SkPathPriv::kCW_FirstDirection , true, true }, + { r5, SK_ARRAY_COUNT(r5), SkPathPriv::kCCW_FirstDirection, true, true }, + { r6, SK_ARRAY_COUNT(r6), SkPathPriv::kCCW_FirstDirection, true, true }, + { r7, SK_ARRAY_COUNT(r7), SkPathPriv::kCCW_FirstDirection, true, true }, + { r8, SK_ARRAY_COUNT(r8), SkPathPriv::kCCW_FirstDirection, true, true }, + { r9, SK_ARRAY_COUNT(r9), SkPathPriv::kCCW_FirstDirection, true, true }, + { ra, SK_ARRAY_COUNT(ra), SkPathPriv::kCCW_FirstDirection, true, true }, + { rb, SK_ARRAY_COUNT(rb), SkPathPriv::kCW_FirstDirection, true, true }, + { rc, SK_ARRAY_COUNT(rc), SkPathPriv::kCW_FirstDirection, true, true }, + { rd, SK_ARRAY_COUNT(rd), SkPathPriv::kCCW_FirstDirection, true, true }, + { re, SK_ARRAY_COUNT(re), SkPathPriv::kCW_FirstDirection, true, true }, - { f1, SK_ARRAY_COUNT(f1), SkPath::kUnknown_Direction, true, false }, - { f2, SK_ARRAY_COUNT(f2), SkPath::kUnknown_Direction, true, false }, - { f3, SK_ARRAY_COUNT(f3), SkPath::kUnknown_Direction, true, false }, - { f4, SK_ARRAY_COUNT(f4), SkPath::kUnknown_Direction, true, false }, - { f5, SK_ARRAY_COUNT(f5), SkPath::kUnknown_Direction, true, false }, - { f6, SK_ARRAY_COUNT(f6), SkPath::kUnknown_Direction, true, false }, - { f7, SK_ARRAY_COUNT(f7), SkPath::kUnknown_Direction, true, false }, - { f8, SK_ARRAY_COUNT(f8), SkPath::kUnknown_Direction, true, false }, + { f1, SK_ARRAY_COUNT(f1), SkPathPriv::kUnknown_FirstDirection, true, false }, + { f2, SK_ARRAY_COUNT(f2), SkPathPriv::kUnknown_FirstDirection, true, false }, + { f3, SK_ARRAY_COUNT(f3), SkPathPriv::kUnknown_FirstDirection, true, false }, + { f4, SK_ARRAY_COUNT(f4), SkPathPriv::kUnknown_FirstDirection, true, false }, + { f5, SK_ARRAY_COUNT(f5), SkPathPriv::kUnknown_FirstDirection, true, false }, + { f6, SK_ARRAY_COUNT(f6), SkPathPriv::kUnknown_FirstDirection, true, false }, + { f7, SK_ARRAY_COUNT(f7), SkPathPriv::kUnknown_FirstDirection, true, false }, + { f8, SK_ARRAY_COUNT(f8), SkPathPriv::kUnknown_FirstDirection, true, false }, - { c1, SK_ARRAY_COUNT(c1), SkPath::kUnknown_Direction, false, false }, - { c2, SK_ARRAY_COUNT(c2), SkPath::kUnknown_Direction, false, false }, + { c1, SK_ARRAY_COUNT(c1), SkPathPriv::kCW_FirstDirection, false, true }, + { c2, SK_ARRAY_COUNT(c2), SkPathPriv::kCW_FirstDirection, false, true }, }; const size_t testCount = SK_ARRAY_COUNT(tests); - size_t index; + int index; for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) { for (size_t testIndex = 0; testIndex < testCount; ++testIndex) { SkPath path; @@ -1853,25 +2367,27 @@ if (!rectFirst) { path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction); } - REPORTER_ASSERT(reporter, tests[testIndex].fIsNestedRect == path.isNestedRects(NULL)); + REPORTER_ASSERT(reporter, + tests[testIndex].fIsNestedRect == path.isNestedFillRects(nullptr)); if (tests[testIndex].fIsNestedRect) { SkRect expected[2], computed[2]; - SkPath::Direction expectedDirs[2], computedDirs[2]; + SkPathPriv::FirstDirection expectedDirs[2]; + SkPath::Direction computedDirs[2]; SkRect testBounds; testBounds.set(tests[testIndex].fPoints, tests[testIndex].fPointCount); expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2); expected[1] = testBounds; if (rectFirst) { - expectedDirs[0] = SkPath::kCW_Direction; + expectedDirs[0] = SkPathPriv::kCW_FirstDirection; } else { - expectedDirs[0] = SkPath::kCCW_Direction; + expectedDirs[0] = SkPathPriv::kCCW_FirstDirection; } expectedDirs[1] = tests[testIndex].fDirection; - REPORTER_ASSERT(reporter, path.isNestedRects(computed, computedDirs)); + REPORTER_ASSERT(reporter, path.isNestedFillRects(computed, computedDirs)); REPORTER_ASSERT(reporter, expected[0] == computed[0]); REPORTER_ASSERT(reporter, expected[1] == computed[1]); - REPORTER_ASSERT(reporter, expectedDirs[0] == computedDirs[0]); - REPORTER_ASSERT(reporter, expectedDirs[1] == computedDirs[1]); + REPORTER_ASSERT(reporter, expectedDirs[0] == SkPathPriv::AsFirstDirection(computedDirs[0])); + REPORTER_ASSERT(reporter, expectedDirs[1] == SkPathPriv::AsFirstDirection(computedDirs[1])); } } @@ -1881,7 +2397,7 @@ path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction); } path1.moveTo(r1[0].fX, r1[0].fY); - for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) { + for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) { path1.lineTo(r1[index].fX, r1[index].fY); } path1.close(); @@ -1889,7 +2405,7 @@ if (!rectFirst) { path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction); } - REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL)); + REPORTER_ASSERT(reporter, !path1.isNestedFillRects(nullptr)); // fail, move in the middle path1.reset(); @@ -1897,7 +2413,7 @@ path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction); } path1.moveTo(r1[0].fX, r1[0].fY); - for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) { + for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) { if (index == 2) { path1.moveTo(1, .5f); } @@ -1907,14 +2423,14 @@ if (!rectFirst) { path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction); } - REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL)); + REPORTER_ASSERT(reporter, !path1.isNestedFillRects(nullptr)); // fail, move on the edge path1.reset(); if (rectFirst) { path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction); } - for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) { + for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) { path1.moveTo(r1[index - 1].fX, r1[index - 1].fY); path1.lineTo(r1[index].fX, r1[index].fY); } @@ -1922,7 +2438,7 @@ if (!rectFirst) { path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction); } - REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL)); + REPORTER_ASSERT(reporter, !path1.isNestedFillRects(nullptr)); // fail, quad path1.reset(); @@ -1930,7 +2446,7 @@ path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction); } path1.moveTo(r1[0].fX, r1[0].fY); - for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) { + for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) { if (index == 2) { path1.quadTo(1, .5f, 1, .5f); } @@ -1940,7 +2456,7 @@ if (!rectFirst) { path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction); } - REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL)); + REPORTER_ASSERT(reporter, !path1.isNestedFillRects(nullptr)); // fail, cubic path1.reset(); @@ -1948,7 +2464,7 @@ path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction); } path1.moveTo(r1[0].fX, r1[0].fY); - for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) { + for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) { if (index == 2) { path1.cubicTo(1, .5f, 1, .5f, 1, .5f); } @@ -1958,15 +2474,29 @@ if (!rectFirst) { path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction); } - REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL)); + REPORTER_ASSERT(reporter, !path1.isNestedFillRects(nullptr)); // fail, not nested path1.reset(); path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction); path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction); - REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL)); + REPORTER_ASSERT(reporter, !path1.isNestedFillRects(nullptr)); } + // pass, constructed explicitly from manually closed rects specified as moves/lines. + SkPath path; + path.moveTo(0, 0); + path.lineTo(10, 0); + path.lineTo(10, 10); + path.lineTo(0, 10); + path.lineTo(0, 0); + path.moveTo(1, 1); + path.lineTo(9, 1); + path.lineTo(9, 9); + path.lineTo(1, 9); + path.lineTo(1, 1); + REPORTER_ASSERT(reporter, path.isNestedFillRects(nullptr)); + // pass, stroke rect SkPath src, dst; src.addRect(1, 1, 7, 7, SkPath::kCW_Direction); @@ -1974,7 +2504,7 @@ strokePaint.setStyle(SkPaint::kStroke_Style); strokePaint.setStrokeWidth(2); strokePaint.getFillPath(src, &dst); - REPORTER_ASSERT(reporter, dst.isNestedRects(NULL)); + REPORTER_ASSERT(reporter, dst.isNestedFillRects(nullptr)); } static void write_and_read_back(skiatest::Reporter* reporter, @@ -1994,14 +2524,91 @@ REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() == p.getConvexityOrUnknown()); - REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL)); - + SkRect oval0, oval1; + SkPath::Direction dir0, dir1; + unsigned start0, start1; + REPORTER_ASSERT(reporter, readBack.isOval(nullptr) == p.isOval(nullptr)); + if (p.isOval(&oval0, &dir0, &start0) && readBack.isOval(&oval1, &dir1, &start1)) { + REPORTER_ASSERT(reporter, oval0 == oval1); + REPORTER_ASSERT(reporter, dir0 == dir1); + REPORTER_ASSERT(reporter, start0 == start1); + } + REPORTER_ASSERT(reporter, readBack.isRRect(nullptr) == p.isRRect(nullptr)); + SkRRect rrect0, rrect1; + if (p.isRRect(&rrect0, &dir0, &start0) && readBack.isRRect(&rrect1, &dir1, &start1)) { + REPORTER_ASSERT(reporter, rrect0 == rrect1); + REPORTER_ASSERT(reporter, dir0 == dir1); + REPORTER_ASSERT(reporter, start0 == start1); + } const SkRect& origBounds = p.getBounds(); const SkRect& readBackBounds = readBack.getBounds(); REPORTER_ASSERT(reporter, origBounds == readBackBounds); } +static void test_corrupt_flattening(skiatest::Reporter* reporter) { + SkPath path; + path.moveTo(1, 2); + path.lineTo(1, 2); + path.quadTo(1, 2, 3, 4); + path.conicTo(1, 2, 3, 4, 0.5f); + path.cubicTo(1, 2, 3, 4, 5, 6); + uint8_t buffer[1024]; + SkDEBUGCODE(size_t size =) path.writeToMemory(buffer); + SkASSERT(size <= sizeof(buffer)); + + // find where the counts and verbs are stored : from the impl in SkPathRef.cpp + int32_t* vCount = (int32_t*)&buffer[16]; + SkASSERT(*vCount == 5); + int32_t* pCount = (int32_t*)&buffer[20]; + SkASSERT(*pCount == 9); + int32_t* cCount = (int32_t*)&buffer[24]; + SkASSERT(*cCount == 1); + uint8_t* verbs = &buffer[28]; + + REPORTER_ASSERT(reporter, path.readFromMemory(buffer, sizeof(buffer))); + + // check that we detect under/over-flow of counts + + *vCount += 1; + REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer))); + *vCount -= 1; // restore + + *pCount += 1; + REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer))); + *pCount -= 2; + REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer))); + *pCount += 1; // restore + + *cCount += 1; + REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer))); + *cCount -= 2; + REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer))); + *cCount += 1; // restore + + // Check that we detect when the verbs indicate more or fewer pts/conics + + uint8_t save = verbs[0]; + SkASSERT(save == SkPath::kCubic_Verb); + verbs[0] = SkPath::kQuad_Verb; + REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer))); + verbs[0] = save; + + save = verbs[1]; + SkASSERT(save == SkPath::kConic_Verb); + verbs[1] = SkPath::kQuad_Verb; + REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer))); + verbs[1] = SkPath::kCubic_Verb; + REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer))); + verbs[1] = save; + + // Check that we detect invalid verbs + save = verbs[1]; + verbs[1] = 17; + REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer))); + verbs[1] = save; +} + static void test_flattening(skiatest::Reporter* reporter) { SkPath p; @@ -2021,7 +2628,7 @@ // create a buffer that should be much larger than the path so we don't // kill our stack if writer goes too far. char buffer[1024]; - size_t size1 = p.writeToMemory(NULL); + size_t size1 = p.writeToMemory(nullptr); size_t size2 = p.writeToMemory(buffer); REPORTER_ASSERT(reporter, size1 == size2); @@ -2050,6 +2657,8 @@ write_and_read_back(reporter, oval); } + + test_corrupt_flattening(reporter); } static void test_transform(skiatest::Reporter* reporter) { @@ -2104,14 +2713,14 @@ { SkMatrix matrix; matrix.reset(); - matrix.setPerspX(SkScalarToPersp(4)); + matrix.setPerspX(4); SkPath p1; p1.moveTo(SkPoint::Make(0, 0)); p.transform(matrix, &p1); REPORTER_ASSERT(reporter, matrix.invert(&matrix)); - p1.transform(matrix, NULL); + p1.transform(matrix, nullptr); SkRect pBounds = p.getBounds(); SkRect p1Bounds = p1.getBounds(); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(pBounds.fLeft, p1Bounds.fLeft)); @@ -2130,7 +2739,7 @@ p1.moveTo(SkPoint::Make(0, 0)); p.transform(matrix, &p1); - REPORTER_ASSERT(reporter, p1.cheapIsDirection(SkPath::kCW_Direction)); + REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(p1, SkPathPriv::kCW_FirstDirection)); } @@ -2142,7 +2751,7 @@ p1.moveTo(SkPoint::Make(0, 0)); // Make p1 unique (i.e., not empty path) p.transform(matrix, &p1); - REPORTER_ASSERT(reporter, p1.cheapIsDirection(SkPath::kCCW_Direction)); + REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(p1, SkPathPriv::kCCW_FirstDirection)); } { @@ -2152,7 +2761,7 @@ p1.moveTo(SkPoint::Make(0, 0)); // Make p1 unique (i.e., not empty path) p.transform(matrix, &p1); - REPORTER_ASSERT(reporter, p1.cheapIsDirection(SkPath::kUnknown_Direction)); + REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(p1, SkPathPriv::kUnknown_FirstDirection)); } } @@ -2191,9 +2800,9 @@ SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb }; static const struct zeroPathTestData gZeroLengthTests[] = { - { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, + { "M 1 1", 1, {1, 1, 1, 1}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, { "M 1 1 M 2 1", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) }, - { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) }, + { "M 1 1 z", 1, {1, 1, 1, 1}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) }, { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) }, { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) }, { "M 1 1 L 1 1 M 2 1 L 2 1", 4, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs6, SK_ARRAY_COUNT(resultVerbs6) }, @@ -2423,6 +3032,20 @@ REPORTER_ASSERT(reporter, SkPath::kDone_Verb == iter.next(pts, true)); // The GM degeneratesegments.cpp test is more extensive + + // Test out mixed degenerate and non-degenerate geometry with Conics + const SkVector radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 100, 100 } }; + SkRect r = SkRect::MakeWH(100, 100); + SkRRect rr; + rr.setRectRadii(r, radii); + p.reset(); + p.addRRect(rr); + iter.setPath(p, false); + REPORTER_ASSERT(reporter, SkPath::kMove_Verb == iter.next(pts)); + REPORTER_ASSERT(reporter, SkPath::kLine_Verb == iter.next(pts)); + REPORTER_ASSERT(reporter, SkPath::kLine_Verb == iter.next(pts)); + REPORTER_ASSERT(reporter, SkPath::kConic_Verb == iter.next(pts)); + REPORTER_ASSERT(reporter, SK_ScalarRoot2Over2 == iter.conicWeight()); } static void test_raw_iter(skiatest::Reporter* reporter) { @@ -2485,14 +3108,10 @@ REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1); REPORTER_ASSERT(reporter, pts[0].fY == 0); REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb); - REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1); - REPORTER_ASSERT(reporter, pts[0].fY == 0); REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2); REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1); REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb); - REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2); - REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1); REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3); REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2); @@ -2500,8 +3119,6 @@ REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4); REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3); REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb); - REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4); - REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3); REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); // Generate random paths and verify @@ -2644,7 +3261,6 @@ numIterPts += 3; break; case SkPath::kClose_Verb: - REPORTER_ASSERT(reporter, pts[0] == lastMoveTo); lastPt = lastMoveTo; break; default: @@ -2659,37 +3275,42 @@ static void check_for_circle(skiatest::Reporter* reporter, const SkPath& path, bool expectedCircle, - SkPath::Direction expectedDir) { + SkPathPriv::FirstDirection expectedDir) { SkRect rect = SkRect::MakeEmpty(); REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle); - REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir)); - - if (expectedCircle) { + SkPath::Direction isOvalDir; + unsigned isOvalStart; + if (path.isOval(&rect, &isOvalDir, &isOvalStart)) { REPORTER_ASSERT(reporter, rect.height() == rect.width()); + REPORTER_ASSERT(reporter, SkPathPriv::AsFirstDirection(isOvalDir) == expectedDir); + SkPath tmpPath; + tmpPath.addOval(rect, isOvalDir, isOvalStart); + REPORTER_ASSERT(reporter, path == tmpPath); } + REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(path, expectedDir)); } static void test_circle_skew(skiatest::Reporter* reporter, const SkPath& path, - SkPath::Direction dir) { + SkPathPriv::FirstDirection dir) { SkPath tmp; SkMatrix m; m.setSkew(SkIntToScalar(3), SkIntToScalar(5)); path.transform(m, &tmp); // this matrix reverses the direction. - if (SkPath::kCCW_Direction == dir) { - dir = SkPath::kCW_Direction; + if (SkPathPriv::kCCW_FirstDirection == dir) { + dir = SkPathPriv::kCW_FirstDirection; } else { - REPORTER_ASSERT(reporter, SkPath::kCW_Direction == dir); - dir = SkPath::kCCW_Direction; + REPORTER_ASSERT(reporter, SkPathPriv::kCW_FirstDirection == dir); + dir = SkPathPriv::kCCW_FirstDirection; } check_for_circle(reporter, tmp, false, dir); } static void test_circle_translate(skiatest::Reporter* reporter, const SkPath& path, - SkPath::Direction dir) { + SkPathPriv::FirstDirection dir) { SkPath tmp; // translate at small offset @@ -2709,7 +3330,7 @@ static void test_circle_rotate(skiatest::Reporter* reporter, const SkPath& path, - SkPath::Direction dir) { + SkPathPriv::FirstDirection dir) { for (int angle = 0; angle < 360; ++angle) { SkPath tmp; SkMatrix m; @@ -2729,37 +3350,35 @@ static void test_circle_mirror_x(skiatest::Reporter* reporter, const SkPath& path, - SkPath::Direction dir) { + SkPathPriv::FirstDirection dir) { SkPath tmp; SkMatrix m; m.reset(); m.setScaleX(-SK_Scalar1); path.transform(m, &tmp); - - if (SkPath::kCW_Direction == dir) { - dir = SkPath::kCCW_Direction; + if (SkPathPriv::kCW_FirstDirection == dir) { + dir = SkPathPriv::kCCW_FirstDirection; } else { - REPORTER_ASSERT(reporter, SkPath::kCCW_Direction == dir); - dir = SkPath::kCW_Direction; + REPORTER_ASSERT(reporter, SkPathPriv::kCCW_FirstDirection == dir); + dir = SkPathPriv::kCW_FirstDirection; } - check_for_circle(reporter, tmp, true, dir); } static void test_circle_mirror_y(skiatest::Reporter* reporter, const SkPath& path, - SkPath::Direction dir) { + SkPathPriv::FirstDirection dir) { SkPath tmp; SkMatrix m; m.reset(); m.setScaleY(-SK_Scalar1); path.transform(m, &tmp); - if (SkPath::kCW_Direction == dir) { - dir = SkPath::kCCW_Direction; + if (SkPathPriv::kCW_FirstDirection == dir) { + dir = SkPathPriv::kCCW_FirstDirection; } else { - REPORTER_ASSERT(reporter, SkPath::kCCW_Direction == dir); - dir = SkPath::kCW_Direction; + REPORTER_ASSERT(reporter, SkPathPriv::kCCW_FirstDirection == dir); + dir = SkPathPriv::kCW_FirstDirection; } check_for_circle(reporter, tmp, true, dir); @@ -2767,7 +3386,7 @@ static void test_circle_mirror_xy(skiatest::Reporter* reporter, const SkPath& path, - SkPath::Direction dir) { + SkPathPriv::FirstDirection dir) { SkPath tmp; SkMatrix m; m.reset(); @@ -2779,20 +3398,13 @@ } static void test_circle_with_direction(skiatest::Reporter* reporter, - SkPath::Direction dir) { + SkPath::Direction inDir) { + const SkPathPriv::FirstDirection dir = SkPathPriv::AsFirstDirection(inDir); SkPath path; // circle at origin - path.addCircle(0, 0, SkIntToScalar(20), dir); - check_for_circle(reporter, path, true, dir); - test_circle_rotate(reporter, path, dir); - test_circle_translate(reporter, path, dir); - test_circle_skew(reporter, path, dir); + path.addCircle(0, 0, SkIntToScalar(20), inDir); - // circle at an offset at (10, 10) - path.reset(); - path.addCircle(SkIntToScalar(10), SkIntToScalar(10), - SkIntToScalar(20), dir); check_for_circle(reporter, path, true, dir); test_circle_rotate(reporter, path, dir); test_circle_translate(reporter, path, dir); @@ -2800,6 +3412,31 @@ test_circle_mirror_x(reporter, path, dir); test_circle_mirror_y(reporter, path, dir); test_circle_mirror_xy(reporter, path, dir); + + // circle at an offset at (10, 10) + path.reset(); + path.addCircle(SkIntToScalar(10), SkIntToScalar(10), + SkIntToScalar(20), inDir); + + check_for_circle(reporter, path, true, dir); + test_circle_rotate(reporter, path, dir); + test_circle_translate(reporter, path, dir); + test_circle_skew(reporter, path, dir); + test_circle_mirror_x(reporter, path, dir); + test_circle_mirror_y(reporter, path, dir); + test_circle_mirror_xy(reporter, path, dir); + + // Try different starting points for the contour. + for (unsigned start = 0; start < 4; ++start) { + path.reset(); + path.addOval(SkRect::MakeXYWH(20, 10, 5, 5), inDir, start); + test_circle_rotate(reporter, path, dir); + test_circle_translate(reporter, path, dir); + test_circle_skew(reporter, path, dir); + test_circle_mirror_x(reporter, path, dir); + test_circle_mirror_y(reporter, path, dir); + test_circle_mirror_xy(reporter, path, dir); + } } static void test_circle_with_add_paths(skiatest::Reporter* reporter) { @@ -2808,8 +3445,8 @@ SkPath rect; SkPath empty; - static const SkPath::Direction kCircleDir = SkPath::kCW_Direction; - static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction; + const SkPath::Direction kCircleDir = SkPath::kCW_Direction; + const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction; circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir); rect.addRect(SkIntToScalar(5), SkIntToScalar(5), @@ -2824,17 +3461,18 @@ // empty + circle (translate) path = empty; path.addPath(circle, translate); - check_for_circle(reporter, path, false, kCircleDir); + check_for_circle(reporter, path, false, SkPathPriv::AsFirstDirection(kCircleDir)); // circle + empty (translate) path = circle; path.addPath(empty, translate); - check_for_circle(reporter, path, true, kCircleDir); + + check_for_circle(reporter, path, true, SkPathPriv::AsFirstDirection(kCircleDir)); // test reverseAddPath path = circle; path.reverseAddPath(rect); - check_for_circle(reporter, path, false, kCircleDirOpposite); + check_for_circle(reporter, path, false, SkPathPriv::AsFirstDirection(kCircleDirOpposite)); } static void test_circle(skiatest::Reporter* reporter) { @@ -2845,19 +3483,19 @@ SkPath path; path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction); path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction); - check_for_circle(reporter, path, false, SkPath::kCW_Direction); + check_for_circle(reporter, path, false, SkPathPriv::kCW_FirstDirection); // some extra lineTo() would make isOval() fail path.reset(); path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction); path.lineTo(0, 0); - check_for_circle(reporter, path, false, SkPath::kCW_Direction); + check_for_circle(reporter, path, false, SkPathPriv::kCW_FirstDirection); // not back to the original point path.reset(); path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction); path.setLastPt(SkIntToScalar(5), SkIntToScalar(5)); - check_for_circle(reporter, path, false, SkPath::kCW_Direction); + check_for_circle(reporter, path, false, SkPathPriv::kCW_FirstDirection); test_circle_with_add_paths(reporter); @@ -2871,55 +3509,63 @@ SkRect rect; SkMatrix m; SkPath path; + unsigned start = 0; + SkPath::Direction dir = SkPath::kCCW_Direction; rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50)); path.addOval(rect); - REPORTER_ASSERT(reporter, path.isOval(NULL)); + // Defaults to dir = CW and start = 1 + REPORTER_ASSERT(reporter, path.isOval(nullptr)); m.setRotate(SkIntToScalar(90)); SkPath tmp; path.transform(m, &tmp); - // an oval rotated 90 degrees is still an oval. - REPORTER_ASSERT(reporter, tmp.isOval(NULL)); + // an oval rotated 90 degrees is still an oval. The start index changes from 1 to 2. Direction + // is unchanged. + REPORTER_ASSERT(reporter, tmp.isOval(nullptr, &dir, &start)); + REPORTER_ASSERT(reporter, 2 == start); + REPORTER_ASSERT(reporter, SkPath::kCW_Direction == dir); m.reset(); m.setRotate(SkIntToScalar(30)); tmp.reset(); path.transform(m, &tmp); // an oval rotated 30 degrees is not an oval anymore. - REPORTER_ASSERT(reporter, !tmp.isOval(NULL)); + REPORTER_ASSERT(reporter, !tmp.isOval(nullptr)); // since empty path being transformed. path.reset(); tmp.reset(); m.reset(); path.transform(m, &tmp); - REPORTER_ASSERT(reporter, !tmp.isOval(NULL)); + REPORTER_ASSERT(reporter, !tmp.isOval(nullptr)); // empty path is not an oval tmp.reset(); - REPORTER_ASSERT(reporter, !tmp.isOval(NULL)); + REPORTER_ASSERT(reporter, !tmp.isOval(nullptr)); // only has moveTo()s tmp.reset(); tmp.moveTo(0, 0); tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10)); - REPORTER_ASSERT(reporter, !tmp.isOval(NULL)); + REPORTER_ASSERT(reporter, !tmp.isOval(nullptr)); // mimic WebKit's calling convention, // call moveTo() first and then call addOval() path.reset(); path.moveTo(0, 0); path.addOval(rect); - REPORTER_ASSERT(reporter, path.isOval(NULL)); + REPORTER_ASSERT(reporter, path.isOval(nullptr)); // copy path path.reset(); tmp.reset(); tmp.addOval(rect); path = tmp; - REPORTER_ASSERT(reporter, path.isOval(NULL)); + REPORTER_ASSERT(reporter, path.isOval(nullptr, &dir, &start)); + REPORTER_ASSERT(reporter, SkPath::kCW_Direction == dir); + REPORTER_ASSERT(reporter, 1 == start); } static void test_empty(skiatest::Reporter* reporter, const SkPath& p) { @@ -2939,12 +3585,21 @@ static void test_rrect_is_convex(skiatest::Reporter* reporter, SkPath* path, SkPath::Direction dir) { REPORTER_ASSERT(reporter, path->isConvex()); - REPORTER_ASSERT(reporter, path->cheapIsDirection(dir)); + REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(*path, SkPathPriv::AsFirstDirection(dir))); path->setConvexity(SkPath::kUnknown_Convexity); REPORTER_ASSERT(reporter, path->isConvex()); path->reset(); } +static void test_rrect_convexity_is_unknown(skiatest::Reporter* reporter, SkPath* path, + SkPath::Direction dir) { + REPORTER_ASSERT(reporter, path->isConvex()); + REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(*path, SkPathPriv::AsFirstDirection(dir))); + path->setConvexity(SkPath::kUnknown_Convexity); + REPORTER_ASSERT(reporter, path->getConvexity() == SkPath::kUnknown_Convexity); + path->reset(); +} + static void test_rrect(skiatest::Reporter* reporter) { SkPath p; SkRRect rr; @@ -2981,7 +3636,7 @@ p.addRRect(rr); bool closed; SkPath::Direction dir; - REPORTER_ASSERT(reporter, p.isRect(&closed, &dir)); + REPORTER_ASSERT(reporter, p.isRect(nullptr, &closed, &dir)); REPORTER_ASSERT(reporter, closed); REPORTER_ASSERT(reporter, SkPath::kCW_Direction == dir); test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction); @@ -3000,11 +3655,13 @@ SkRect largeR = {0, 0, SK_ScalarMax, SK_ScalarMax}; rr.setRectRadii(largeR, radii); p.addRRect(rr); - test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction); + test_rrect_convexity_is_unknown(reporter, &p, SkPath::kCW_Direction); + + // we check for non-finites SkRect infR = {0, 0, SK_ScalarMax, SK_ScalarInfinity}; rr.setRectRadii(infR, radii); - p.addRRect(rr); - test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction); + REPORTER_ASSERT(reporter, rr.isEmpty()); + SkRect tinyR = {0, 0, 1e-9f, 1e-9f}; p.addRoundRect(tinyR, 5e-11f, 5e-11f); test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction); @@ -3023,21 +3680,85 @@ p.reset(); SkPath cwOval; cwOval.addOval(oval); - p.addArc(oval, 1, 360); + p.addArc(oval, 0, 360); REPORTER_ASSERT(reporter, p == cwOval); p.reset(); SkPath ccwOval; ccwOval.addOval(oval, SkPath::kCCW_Direction); - p.addArc(oval, 1, -360); + p.addArc(oval, 0, -360); REPORTER_ASSERT(reporter, p == ccwOval); p.reset(); p.addArc(oval, 1, 180); REPORTER_ASSERT(reporter, p.isConvex()); - REPORTER_ASSERT(reporter, p.cheapIsDirection(SkPath::kCW_Direction)); + REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(p, SkPathPriv::kCW_FirstDirection)); p.setConvexity(SkPath::kUnknown_Convexity); REPORTER_ASSERT(reporter, p.isConvex()); } +static inline SkScalar oval_start_index_to_angle(unsigned start) { + switch (start) { + case 0: + return 270.f; + case 1: + return 0.f; + case 2: + return 90.f; + case 3: + return 180.f; + default: + return -1.f; + } +} + +static inline SkScalar canonical_start_angle(float angle) { + while (angle < 0.f) { + angle += 360.f; + } + while (angle >= 360.f) { + angle -= 360.f; + } + return angle; +} + +static void check_oval_arc(skiatest::Reporter* reporter, SkScalar start, SkScalar sweep, + const SkPath& path) { + SkRect r = SkRect::MakeEmpty(); + SkPath::Direction d = SkPath::kCCW_Direction; + unsigned s = ~0U; + bool isOval = path.isOval(&r, &d, &s); + REPORTER_ASSERT(reporter, isOval); + SkPath recreatedPath; + recreatedPath.addOval(r, d, s); + REPORTER_ASSERT(reporter, path == recreatedPath); + REPORTER_ASSERT(reporter, oval_start_index_to_angle(s) == canonical_start_angle(start)); + REPORTER_ASSERT(reporter, (SkPath::kCW_Direction == d) == (sweep > 0.f)); +} + +static void test_arc_ovals(skiatest::Reporter* reporter) { + SkRect oval = SkRect::MakeWH(10, 20); + for (SkScalar sweep : {-720.f, -540.f, -360.f, 360.f, 432.f, 720.f}) { + for (SkScalar start = -360.f; start <= 360.f; start += 1.f) { + SkPath path; + path.addArc(oval, start, sweep); + // SkPath's interfaces for inserting and extracting ovals only allow contours + // to start at multiples of 90 degrees. + if (std::fmod(start, 90.f) == 0) { + check_oval_arc(reporter, start, sweep, path); + } else { + REPORTER_ASSERT(reporter, !path.isOval(nullptr)); + } + } + // Test start angles that are nearly at valid oval start angles. + for (float start : {-180.f, -90.f, 90.f, 180.f}) { + for (float delta : {-SK_ScalarNearlyZero, SK_ScalarNearlyZero}) { + SkPath path; + path.addArc(oval, start + delta, sweep); + check_oval_arc(reporter, start, sweep, path); + } + } + } +} + static void check_move(skiatest::Reporter* reporter, SkPath::RawIter* iter, SkScalar x0, SkScalar y0) { SkPoint pts[4]; @@ -3118,6 +3839,13 @@ check_done_and_reset(reporter, p, &iter); } +static bool nearly_equal(const SkRect& a, const SkRect& b) { + return SkScalarNearlyEqual(a.fLeft, b.fLeft) && + SkScalarNearlyEqual(a.fTop, b.fTop) && + SkScalarNearlyEqual(a.fRight, b.fRight) && + SkScalarNearlyEqual(a.fBottom, b.fBottom); +} + static void test_arcTo(skiatest::Reporter* reporter) { SkPath p; p.arcTo(0, 0, 1, 2, 1); @@ -3144,15 +3872,16 @@ check_path_is_move_and_reset(reporter, &p, oval.fRight, oval.centerY()); p.arcTo(oval, 360, 0, false); check_path_is_move_and_reset(reporter, &p, oval.fRight, oval.centerY()); + for (float sweep = 359, delta = 0.5f; sweep != (float) (sweep + delta); ) { p.arcTo(oval, 0, sweep, false); - REPORTER_ASSERT(reporter, p.getBounds() == oval); + REPORTER_ASSERT(reporter, nearly_equal(p.getBounds(), oval)); sweep += delta; delta /= 2; } for (float sweep = 361, delta = 0.5f; sweep != (float) (sweep - delta);) { p.arcTo(oval, 0, sweep, false); - REPORTER_ASSERT(reporter, p.getBounds() == oval); + REPORTER_ASSERT(reporter, nearly_equal(p.getBounds(), oval)); sweep -= delta; delta /= 2; } @@ -3261,18 +3990,28 @@ SkPath p; SkPoint pt = p.getPoint(0); REPORTER_ASSERT(reporter, pt == SkPoint::Make(0, 0)); - REPORTER_ASSERT(reporter, !p.getLastPt(NULL)); + REPORTER_ASSERT(reporter, !p.getLastPt(nullptr)); REPORTER_ASSERT(reporter, !p.getLastPt(&pt) && pt == SkPoint::Make(0, 0)); p.setLastPt(10, 10); pt = p.getPoint(0); REPORTER_ASSERT(reporter, pt == SkPoint::Make(10, 10)); - REPORTER_ASSERT(reporter, p.getLastPt(NULL)); + REPORTER_ASSERT(reporter, p.getLastPt(nullptr)); p.rMoveTo(10, 10); REPORTER_ASSERT(reporter, p.getLastPt(&pt) && pt == SkPoint::Make(20, 20)); } static void test_contains(skiatest::Reporter* reporter) { SkPath p; + p.moveTo(SkBits2Float(0xe085e7b1), SkBits2Float(0x5f512c00)); // -7.7191e+19f, 1.50724e+19f + p.conicTo(SkBits2Float(0xdfdaa221), SkBits2Float(0x5eaac338), SkBits2Float(0x60342f13), SkBits2Float(0xdf0cbb58), SkBits2Float(0x3f3504f3)); // -3.15084e+19f, 6.15237e+18f, 5.19345e+19f, -1.01408e+19f, 0.707107f + p.conicTo(SkBits2Float(0x60ead799), SkBits2Float(0xdfb76c24), SkBits2Float(0x609b9872), SkBits2Float(0xdf730de8), SkBits2Float(0x3f3504f4)); // 1.35377e+20f, -2.6434e+19f, 8.96947e+19f, -1.75139e+19f, 0.707107f + p.lineTo(SkBits2Float(0x609b9872), SkBits2Float(0xdf730de8)); // 8.96947e+19f, -1.75139e+19f + p.conicTo(SkBits2Float(0x6018b296), SkBits2Float(0xdeee870d), SkBits2Float(0xe008cd8e), SkBits2Float(0x5ed5b2db), SkBits2Float(0x3f3504f3)); // 4.40121e+19f, -8.59386e+18f, -3.94308e+19f, 7.69931e+18f, 0.707107f + p.conicTo(SkBits2Float(0xe0d526d9), SkBits2Float(0x5fa67b31), SkBits2Float(0xe085e7b2), SkBits2Float(0x5f512c01), SkBits2Float(0x3f3504f3)); // -1.22874e+20f, 2.39925e+19f, -7.7191e+19f, 1.50724e+19f, 0.707107f + // this may return true or false, depending on the platform's numerics, but it should not crash + (void) p.contains(-77.2027664f, 15.3066053f); + + p.reset(); p.setFillType(SkPath::kInverseWinding_FillType); REPORTER_ASSERT(reporter, p.contains(0, 0)); p.setFillType(SkPath::kWinding_FillType); @@ -3280,6 +4019,10 @@ p.moveTo(4, 4); p.lineTo(6, 8); p.lineTo(8, 4); + // test on edge + REPORTER_ASSERT(reporter, p.contains(6, 4)); + REPORTER_ASSERT(reporter, p.contains(5, 6)); + REPORTER_ASSERT(reporter, p.contains(7, 6)); // test quick reject REPORTER_ASSERT(reporter, !p.contains(4, 0)); REPORTER_ASSERT(reporter, !p.contains(0, 4)); @@ -3293,10 +4036,53 @@ p.moveTo(4, 4); p.lineTo(8, 6); p.lineTo(4, 8); + // test on edge + REPORTER_ASSERT(reporter, p.contains(4, 6)); + REPORTER_ASSERT(reporter, p.contains(6, 5)); + REPORTER_ASSERT(reporter, p.contains(6, 7)); // test various crossings in y REPORTER_ASSERT(reporter, !p.contains(7, 5)); REPORTER_ASSERT(reporter, p.contains(7, 6)); REPORTER_ASSERT(reporter, !p.contains(7, 7)); + p.reset(); + p.moveTo(4, 4); + p.lineTo(8, 4); + p.lineTo(8, 8); + p.lineTo(4, 8); + // test on vertices + REPORTER_ASSERT(reporter, p.contains(4, 4)); + REPORTER_ASSERT(reporter, p.contains(8, 4)); + REPORTER_ASSERT(reporter, p.contains(8, 8)); + REPORTER_ASSERT(reporter, p.contains(4, 8)); + p.reset(); + p.moveTo(4, 4); + p.lineTo(6, 8); + p.lineTo(2, 8); + // test on edge + REPORTER_ASSERT(reporter, p.contains(5, 6)); + REPORTER_ASSERT(reporter, p.contains(4, 8)); + REPORTER_ASSERT(reporter, p.contains(3, 6)); + p.reset(); + p.moveTo(4, 4); + p.lineTo(0, 6); + p.lineTo(4, 8); + // test on edge + REPORTER_ASSERT(reporter, p.contains(2, 5)); + REPORTER_ASSERT(reporter, p.contains(2, 7)); + REPORTER_ASSERT(reporter, p.contains(4, 6)); + // test canceling coincident edge (a smaller triangle is coincident with a larger one) + p.reset(); + p.moveTo(4, 0); + p.lineTo(6, 4); + p.lineTo(2, 4); + p.moveTo(4, 0); + p.lineTo(0, 8); + p.lineTo(8, 8); + REPORTER_ASSERT(reporter, !p.contains(1, 2)); + REPORTER_ASSERT(reporter, !p.contains(3, 2)); + REPORTER_ASSERT(reporter, !p.contains(4, 0)); + REPORTER_ASSERT(reporter, p.contains(4, 4)); + // test quads p.reset(); p.moveTo(4, 4); @@ -3305,25 +4091,49 @@ p.quadTo(4, 6, 4, 4); REPORTER_ASSERT(reporter, p.contains(5, 6)); REPORTER_ASSERT(reporter, !p.contains(6, 5)); + // test quad edge + REPORTER_ASSERT(reporter, p.contains(5, 5)); + REPORTER_ASSERT(reporter, p.contains(5, 8)); + REPORTER_ASSERT(reporter, p.contains(4, 5)); + // test quad endpoints + REPORTER_ASSERT(reporter, p.contains(4, 4)); + REPORTER_ASSERT(reporter, p.contains(8, 8)); + REPORTER_ASSERT(reporter, p.contains(4, 8)); p.reset(); - p.moveTo(6, 6); - p.quadTo(8, 8, 6, 8); - p.quadTo(4, 8, 4, 6); - p.quadTo(4, 4, 6, 6); + const SkPoint qPts[] = {{6, 6}, {8, 8}, {6, 8}, {4, 8}, {4, 6}, {4, 4}, {6, 6}}; + p.moveTo(qPts[0]); + for (int index = 1; index < (int) SK_ARRAY_COUNT(qPts); index += 2) { + p.quadTo(qPts[index], qPts[index + 1]); + } REPORTER_ASSERT(reporter, p.contains(5, 6)); REPORTER_ASSERT(reporter, !p.contains(6, 5)); + // test quad edge + SkPoint halfway; + for (int index = 0; index < (int) SK_ARRAY_COUNT(qPts) - 2; index += 2) { + SkEvalQuadAt(&qPts[index], 0.5f, &halfway, nullptr); + REPORTER_ASSERT(reporter, p.contains(halfway.fX, halfway.fY)); + } -#define CONIC_CONTAINS_BUG_FIXED 0 -#if CONIC_CONTAINS_BUG_FIXED + // test conics p.reset(); - p.moveTo(4, 4); - p.conicTo(6, 6, 8, 8, 0.5f); - p.conicTo(6, 8, 4, 8, 0.5f); - p.conicTo(4, 6, 4, 4, 0.5f); + const SkPoint kPts[] = {{4, 4}, {6, 6}, {8, 8}, {6, 8}, {4, 8}, {4, 6}, {4, 4}}; + p.moveTo(kPts[0]); + for (int index = 1; index < (int) SK_ARRAY_COUNT(kPts); index += 2) { + p.conicTo(kPts[index], kPts[index + 1], 0.5f); + } REPORTER_ASSERT(reporter, p.contains(5, 6)); REPORTER_ASSERT(reporter, !p.contains(6, 5)); -#endif + // test conic edge + for (int index = 0; index < (int) SK_ARRAY_COUNT(kPts) - 2; index += 2) { + SkConic conic(&kPts[index], 0.5f); + halfway = conic.evalAt(0.5f); + REPORTER_ASSERT(reporter, p.contains(halfway.fX, halfway.fY)); + } + // test conic end points + REPORTER_ASSERT(reporter, p.contains(4, 4)); + REPORTER_ASSERT(reporter, p.contains(8, 8)); + REPORTER_ASSERT(reporter, p.contains(4, 8)); // test cubics SkPoint pts[] = {{5, 4}, {6, 5}, {7, 6}, {6, 6}, {4, 6}, {5, 7}, {5, 5}, {5, 4}, {6, 5}, {7, 6}}; @@ -3336,6 +4146,15 @@ p.close(); REPORTER_ASSERT(reporter, p.contains(5.5f, 5.5f)); REPORTER_ASSERT(reporter, !p.contains(4.5f, 5.5f)); + // test cubic edge + SkEvalCubicAt(&pts[i], 0.5f, &halfway, nullptr, nullptr); + REPORTER_ASSERT(reporter, p.contains(halfway.fX, halfway.fY)); + SkEvalCubicAt(&pts[i + 3], 0.5f, &halfway, nullptr, nullptr); + REPORTER_ASSERT(reporter, p.contains(halfway.fX, halfway.fY)); + // test cubic end points + REPORTER_ASSERT(reporter, p.contains(pts[i].fX, pts[i].fY)); + REPORTER_ASSERT(reporter, p.contains(pts[i + 3].fX, pts[i + 3].fY)); + REPORTER_ASSERT(reporter, p.contains(pts[i + 6].fX, pts[i + 6].fY)); } } @@ -3344,7 +4163,7 @@ static void TestPathRef(skiatest::Reporter* reporter) { static const int kRepeatCnt = 10; - SkAutoTUnref<SkPathRef> pathRef(SkNEW(SkPathRef)); + sk_sp<SkPathRef> pathRef(new SkPathRef); SkPathRef::Editor ed(&pathRef); @@ -3382,7 +4201,7 @@ } { - SkScalar* weights = NULL; + SkScalar* weights = nullptr; ed.growForRepeatedVerb(SkPath::kConic_Verb, kRepeatCnt, &weights); REPORTER_ASSERT(reporter, kRepeatCnt == pathRef->countVerbs()); REPORTER_ASSERT(reporter, 2*kRepeatCnt == pathRef->countPoints()); @@ -3433,50 +4252,81 @@ bool dumpAsHex, const char* str) { SkDynamicMemoryWStream wStream; path.dump(&wStream, force, dumpAsHex); - SkAutoDataUnref data(wStream.copyToData()); + sk_sp<SkData> data = wStream.detachAsData(); REPORTER_ASSERT(reporter, data->size() == strlen(str)); - REPORTER_ASSERT(reporter, !memcmp(data->data(), str, strlen(str))); + if (strlen(str) > 0) { + REPORTER_ASSERT(reporter, !memcmp(data->data(), str, strlen(str))); + } else { + REPORTER_ASSERT(reporter, data->data() == nullptr || !memcmp(data->data(), str, strlen(str))); + } } static void test_dump(skiatest::Reporter* reporter) { SkPath p; - compare_dump(reporter, p, false, false, ""); - compare_dump(reporter, p, true, false, ""); + compare_dump(reporter, p, false, false, "path.setFillType(SkPath::kWinding_FillType);\n"); + compare_dump(reporter, p, true, false, "path.setFillType(SkPath::kWinding_FillType);\n"); p.moveTo(1, 2); p.lineTo(3, 4); - compare_dump(reporter, p, false, false, "path.moveTo(1, 2);\n" + compare_dump(reporter, p, false, false, "path.setFillType(SkPath::kWinding_FillType);\n" + "path.moveTo(1, 2);\n" "path.lineTo(3, 4);\n"); - compare_dump(reporter, p, true, false, "path.moveTo(1, 2);\n" + compare_dump(reporter, p, true, false, "path.setFillType(SkPath::kWinding_FillType);\n" + "path.moveTo(1, 2);\n" "path.lineTo(3, 4);\n" "path.lineTo(1, 2);\n" "path.close();\n"); p.reset(); + p.setFillType(SkPath::kEvenOdd_FillType); p.moveTo(1, 2); p.quadTo(3, 4, 5, 6); - compare_dump(reporter, p, false, false, "path.moveTo(1, 2);\n" + compare_dump(reporter, p, false, false, "path.setFillType(SkPath::kEvenOdd_FillType);\n" + "path.moveTo(1, 2);\n" "path.quadTo(3, 4, 5, 6);\n"); p.reset(); + p.setFillType(SkPath::kInverseWinding_FillType); p.moveTo(1, 2); p.conicTo(3, 4, 5, 6, 0.5f); - compare_dump(reporter, p, false, false, "path.moveTo(1, 2);\n" + compare_dump(reporter, p, false, false, "path.setFillType(SkPath::kInverseWinding_FillType);\n" + "path.moveTo(1, 2);\n" "path.conicTo(3, 4, 5, 6, 0.5f);\n"); p.reset(); + p.setFillType(SkPath::kInverseEvenOdd_FillType); p.moveTo(1, 2); p.cubicTo(3, 4, 5, 6, 7, 8); - compare_dump(reporter, p, false, false, "path.moveTo(1, 2);\n" + compare_dump(reporter, p, false, false, "path.setFillType(SkPath::kInverseEvenOdd_FillType);\n" + "path.moveTo(1, 2);\n" "path.cubicTo(3, 4, 5, 6, 7, 8);\n"); p.reset(); + p.setFillType(SkPath::kWinding_FillType); p.moveTo(1, 2); p.lineTo(3, 4); - compare_dump(reporter, p, false, true, "path.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x40000000));\n" - "path.lineTo(SkBits2Float(0x40400000), SkBits2Float(0x40800000));\n"); + compare_dump(reporter, p, false, true, + "path.setFillType(SkPath::kWinding_FillType);\n" + "path.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x40000000)); // 1, 2\n" + "path.lineTo(SkBits2Float(0x40400000), SkBits2Float(0x40800000)); // 3, 4\n"); p.reset(); p.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x40000000)); p.lineTo(SkBits2Float(0x40400000), SkBits2Float(0x40800000)); - compare_dump(reporter, p, false, false, "path.moveTo(1, 2);\n" + compare_dump(reporter, p, false, false, "path.setFillType(SkPath::kWinding_FillType);\n" + "path.moveTo(1, 2);\n" "path.lineTo(3, 4);\n"); } +namespace { + +class ChangeListener : public SkPathRef::GenIDChangeListener { +public: + ChangeListener(bool *changed) : fChanged(changed) { *fChanged = false; } + ~ChangeListener() override {} + void onChange() override { + *fChanged = true; + } +private: + bool* fChanged; +}; + +} + class PathTest_Private { public: static void TestPathTo(skiatest::Reporter* reporter) { @@ -3496,12 +4346,194 @@ SkRect reverseExpected = {-4, -4, 8, 8}; REPORTER_ASSERT(reporter, p.getBounds() == reverseExpected); } + + static void TestPathrefListeners(skiatest::Reporter* reporter) { + SkPath p; + + bool changed = false; + p.moveTo(0, 0); + + // Check that listener is notified on moveTo(). + + SkPathPriv::AddGenIDChangeListener(p, new ChangeListener(&changed)); + REPORTER_ASSERT(reporter, !changed); + p.moveTo(10, 0); + REPORTER_ASSERT(reporter, changed); + + // Check that listener is notified on lineTo(). + SkPathPriv::AddGenIDChangeListener(p, new ChangeListener(&changed)); + REPORTER_ASSERT(reporter, !changed); + p.lineTo(20, 0); + REPORTER_ASSERT(reporter, changed); + + // Check that listener is notified on reset(). + SkPathPriv::AddGenIDChangeListener(p, new ChangeListener(&changed)); + REPORTER_ASSERT(reporter, !changed); + p.reset(); + REPORTER_ASSERT(reporter, changed); + + p.moveTo(0, 0); + + // Check that listener is notified on rewind(). + SkPathPriv::AddGenIDChangeListener(p, new ChangeListener(&changed)); + REPORTER_ASSERT(reporter, !changed); + p.rewind(); + REPORTER_ASSERT(reporter, changed); + + // Check that listener is notified when pathref is deleted. + { + SkPath q; + q.moveTo(10, 10); + SkPathPriv::AddGenIDChangeListener(q, new ChangeListener(&changed)); + REPORTER_ASSERT(reporter, !changed); + } + // q went out of scope. + REPORTER_ASSERT(reporter, changed); + } }; -DEF_TEST(Paths, reporter) { - test_path_crbug364224(); +static void test_crbug_629455(skiatest::Reporter* reporter) { + SkPath path; + path.moveTo(0, 0); + path.cubicTo(SkBits2Float(0xcdcdcd00), SkBits2Float(0xcdcdcdcd), + SkBits2Float(0xcdcdcdcd), SkBits2Float(0xcdcdcdcd), + SkBits2Float(0x423fcdcd), SkBits2Float(0x40ed9341)); +// AKA: cubicTo(-4.31596e+08f, -4.31602e+08f, -4.31602e+08f, -4.31602e+08f, 47.951f, 7.42423f); + path.lineTo(0, 0); - SkTSize<SkScalar>::Make(3,4); + auto surface = SkSurface::MakeRasterN32Premul(100, 100); + SkPaint paint; + paint.setAntiAlias(true); + surface->getCanvas()->drawPath(path, paint); +} + +static void test_fuzz_crbug_662952(skiatest::Reporter* reporter) { + SkPath path; + path.moveTo(SkBits2Float(0x4109999a), SkBits2Float(0x411c0000)); // 8.6f, 9.75f + path.lineTo(SkBits2Float(0x410a6666), SkBits2Float(0x411c0000)); // 8.65f, 9.75f + path.lineTo(SkBits2Float(0x410a6666), SkBits2Float(0x411e6666)); // 8.65f, 9.9f + path.lineTo(SkBits2Float(0x4109999a), SkBits2Float(0x411e6666)); // 8.6f, 9.9f + path.lineTo(SkBits2Float(0x4109999a), SkBits2Float(0x411c0000)); // 8.6f, 9.75f + path.close(); + + auto surface = SkSurface::MakeRasterN32Premul(100, 100); + SkPaint paint; + paint.setAntiAlias(true); + surface->getCanvas()->clipPath(path, true); + surface->getCanvas()->drawRect(SkRect::MakeWH(100, 100), paint); +} + +static void test_path_crbugskia6003() { + auto surface(SkSurface::MakeRasterN32Premul(500, 500)); + SkCanvas* canvas = surface->getCanvas(); + SkPaint paint; + paint.setAntiAlias(true); + SkPath path; + path.moveTo(SkBits2Float(0x4325e666), SkBits2Float(0x42a1999a)); // 165.9f, 80.8f + path.lineTo(SkBits2Float(0x4325e666), SkBits2Float(0x42a2999a)); // 165.9f, 81.3f + path.lineTo(SkBits2Float(0x4325b333), SkBits2Float(0x42a2999a)); // 165.7f, 81.3f + path.lineTo(SkBits2Float(0x4325b333), SkBits2Float(0x42a16666)); // 165.7f, 80.7f + path.lineTo(SkBits2Float(0x4325b333), SkBits2Float(0x429f6666)); // 165.7f, 79.7f + // 165.7f, 79.7f, 165.8f, 79.7f, 165.8f, 79.7f + path.cubicTo(SkBits2Float(0x4325b333), SkBits2Float(0x429f6666), SkBits2Float(0x4325cccc), + SkBits2Float(0x429f6666), SkBits2Float(0x4325cccc), SkBits2Float(0x429f6666)); + // 165.8f, 79.7f, 165.8f, 79.7f, 165.9f, 79.7f + path.cubicTo(SkBits2Float(0x4325cccc), SkBits2Float(0x429f6666), SkBits2Float(0x4325cccc), + SkBits2Float(0x429f6666), SkBits2Float(0x4325e666), SkBits2Float(0x429f6666)); + path.lineTo(SkBits2Float(0x4325e666), SkBits2Float(0x42a1999a)); // 165.9f, 80.8f + path.close(); + canvas->clipPath(path, true); + canvas->drawRect(SkRect::MakeWH(500, 500), paint); +} + +static void test_fuzz_crbug_662730(skiatest::Reporter* reporter) { + SkPath path; + path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 + path.lineTo(SkBits2Float(0xd5394437), SkBits2Float(0x37373737)); // -1.2731e+13f, 1.09205e-05f + path.lineTo(SkBits2Float(0x37373737), SkBits2Float(0x37373737)); // 1.09205e-05f, 1.09205e-05f + path.lineTo(SkBits2Float(0x37373745), SkBits2Float(0x0001b800)); // 1.09205e-05f, 1.57842e-40f + path.close(); + + auto surface = SkSurface::MakeRasterN32Premul(100, 100); + SkPaint paint; + paint.setAntiAlias(true); + surface->getCanvas()->drawPath(path, paint); +} + +static void test_interp(skiatest::Reporter* reporter) { + SkPath p1, p2, out; + REPORTER_ASSERT(reporter, p1.isInterpolatable(p2)); + REPORTER_ASSERT(reporter, p1.interpolate(p2, 0, &out)); + REPORTER_ASSERT(reporter, p1 == out); + REPORTER_ASSERT(reporter, p1.interpolate(p2, 1, &out)); + REPORTER_ASSERT(reporter, p1 == out); + p1.moveTo(0, 2); + p1.lineTo(0, 4); + REPORTER_ASSERT(reporter, !p1.isInterpolatable(p2)); + REPORTER_ASSERT(reporter, !p1.interpolate(p2, 1, &out)); + p2.moveTo(6, 0); + p2.lineTo(8, 0); + REPORTER_ASSERT(reporter, p1.isInterpolatable(p2)); + REPORTER_ASSERT(reporter, p1.interpolate(p2, 0, &out)); + REPORTER_ASSERT(reporter, p2 == out); + REPORTER_ASSERT(reporter, p1.interpolate(p2, 1, &out)); + REPORTER_ASSERT(reporter, p1 == out); + REPORTER_ASSERT(reporter, p1.interpolate(p2, 0.5f, &out)); + REPORTER_ASSERT(reporter, out.getBounds() == SkRect::MakeLTRB(3, 1, 4, 2)); + p1.reset(); + p1.moveTo(4, 4); + p1.conicTo(5, 4, 5, 5, 1 / SkScalarSqrt(2)); + p2.reset(); + p2.moveTo(4, 2); + p2.conicTo(7, 2, 7, 5, 1 / SkScalarSqrt(2)); + REPORTER_ASSERT(reporter, p1.isInterpolatable(p2)); + REPORTER_ASSERT(reporter, p1.interpolate(p2, 0.5f, &out)); + REPORTER_ASSERT(reporter, out.getBounds() == SkRect::MakeLTRB(4, 3, 6, 5)); + p2.reset(); + p2.moveTo(4, 2); + p2.conicTo(6, 3, 6, 5, 1); + REPORTER_ASSERT(reporter, !p1.isInterpolatable(p2)); + p2.reset(); + p2.moveTo(4, 4); + p2.conicTo(5, 4, 5, 5, 0.5f); + REPORTER_ASSERT(reporter, !p1.isInterpolatable(p2)); +} + +DEF_TEST(PathInterp, reporter) { + test_interp(reporter); +} + +#include "SkSurface.h" +DEF_TEST(PathBigCubic, reporter) { + SkPath path; + path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 + path.moveTo(SkBits2Float(0x44000000), SkBits2Float(0x373938b8)); // 512, 1.10401e-05f + path.cubicTo(SkBits2Float(0x00000001), SkBits2Float(0xdf000052), SkBits2Float(0x00000100), SkBits2Float(0x00000000), SkBits2Float(0x00000100), SkBits2Float(0x00000000)); // 1.4013e-45f, -9.22346e+18f, 3.58732e-43f, 0, 3.58732e-43f, 0 + path.moveTo(0, 512); + + // this call should not assert + SkSurface::MakeRasterN32Premul(255, 255, nullptr)->getCanvas()->drawPath(path, SkPaint()); +} + +DEF_TEST(PathContains, reporter) { + test_contains(reporter); +} + +DEF_TEST(Paths, reporter) { + test_fuzz_crbug_647922(); + test_fuzz_crbug_643933(); + test_sect_with_horizontal_needs_pinning(); + test_crbug_629455(reporter); + test_fuzz_crbug_627414(reporter); + test_path_crbug364224(); + test_fuzz_crbug_662952(reporter); + test_fuzz_crbug_662730(reporter); + test_fuzz_crbug_662780(); + test_mask_overflow(); + test_path_crbugskia6003(); + test_fuzz_crbug_668907(); + + SkSize::Make(3, 4); SkPath p, empty; SkRect bounds, bounds2; @@ -3521,7 +4553,8 @@ p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1); check_convex_bounds(reporter, p, bounds); // we have quads or cubics - REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask); + REPORTER_ASSERT(reporter, + p.getSegmentMasks() & (kCurveSegmentMask | SkPath::kConic_SegmentMask)); REPORTER_ASSERT(reporter, !p.isEmpty()); p.reset(); @@ -3544,8 +4577,8 @@ REPORTER_ASSERT(reporter, !(p == empty)); // do getPoints and getVerbs return the right result - REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4); - REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5); + REPORTER_ASSERT(reporter, p.getPoints(nullptr, 0) == 4); + REPORTER_ASSERT(reporter, p.getVerbs(nullptr, 0) == 5); SkPoint pts[4]; int count = p.getPoints(pts, 4); REPORTER_ASSERT(reporter, count == 4); @@ -3565,7 +4598,7 @@ p.offset(SK_Scalar1*3, SK_Scalar1*4); REPORTER_ASSERT(reporter, bounds == p.getBounds()); - REPORTER_ASSERT(reporter, p.isRect(NULL)); + REPORTER_ASSERT(reporter, p.isRect(nullptr)); bounds2.setEmpty(); REPORTER_ASSERT(reporter, p.isRect(&bounds2)); REPORTER_ASSERT(reporter, bounds == bounds2); @@ -3573,12 +4606,27 @@ // now force p to not be a rect bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2); p.addRect(bounds); - REPORTER_ASSERT(reporter, !p.isRect(NULL)); + REPORTER_ASSERT(reporter, !p.isRect(nullptr)); + + // Test an edge case w.r.t. the bound returned by isRect (i.e., the + // path has a trailing moveTo. Please see crbug.com\445368) + { + SkRect r; + p.reset(); + p.addRect(bounds); + REPORTER_ASSERT(reporter, p.isRect(&r)); + REPORTER_ASSERT(reporter, r == bounds); + // add a moveTo outside of our bounds + p.moveTo(bounds.fLeft + 10, bounds.fBottom + 10); + REPORTER_ASSERT(reporter, p.isRect(&r)); + REPORTER_ASSERT(reporter, r == bounds); + } test_operatorEqual(reporter); test_isLine(reporter); test_isRect(reporter); - test_isNestedRects(reporter); + test_is_simple_closed_rect(reporter); + test_isNestedFillRects(reporter); test_zero_length_paths(reporter); test_direction(reporter); test_convexity(reporter); @@ -3597,6 +4645,7 @@ test_addPoly(reporter); test_isfinite(reporter); test_isfinite_after_transform(reporter); + test_islastcontourclosed(reporter); test_arb_round_rect_is_convex(reporter); test_arb_zero_rad_round_rect_is_rect(reporter); test_addrect(reporter); @@ -3604,14 +4653,17 @@ test_tricky_cubic(); test_clipped_cubic(); test_crbug_170666(); + test_crbug_493450(reporter); + test_crbug_495894(reporter); + test_crbug_613918(); test_bad_cubic_crbug229478(); test_bad_cubic_crbug234190(); - test_android_specific_behavior(reporter); test_gen_id(reporter); test_path_close_issue1474(reporter); test_path_to_region(reporter); test_rrect(reporter); test_arc(reporter); + test_arc_ovals(reporter); test_arcTo(reporter); test_addPath(reporter); test_addPathMode(reporter, false, false); @@ -3626,6 +4678,100 @@ test_contains(reporter); PathTest_Private::TestPathTo(reporter); PathRefTest_Private::TestPathRef(reporter); + PathTest_Private::TestPathrefListeners(reporter); test_dump(reporter); + test_path_crbug389050(reporter); test_path_crbugskia2820(reporter); + test_path_crbugskia5995(); + test_skbug_3469(reporter); + test_skbug_3239(reporter); + test_bounds_crbug_513799(reporter); + test_fuzz_crbug_638223(); +} + +DEF_TEST(conservatively_contains_rect, reporter) { + SkPath path; + + path.moveTo(SkBits2Float(0x44000000), SkBits2Float(0x373938b8)); // 512, 1.10401e-05f + // 1.4013e-45f, -9.22346e+18f, 3.58732e-43f, 0, 3.58732e-43f, 0 + path.cubicTo(SkBits2Float(0x00000001), SkBits2Float(0xdf000052), + SkBits2Float(0x00000100), SkBits2Float(0x00000000), + SkBits2Float(0x00000100), SkBits2Float(0x00000000)); + path.moveTo(0, 0); + + // this guy should not assert + path.conservativelyContainsRect({ -211747, 12.1115f, -197893, 25.0321f }); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static void rand_path(SkPath* path, SkRandom& rand, SkPath::Verb verb, int n) { + for (int i = 0; i < n; ++i) { + switch (verb) { + case SkPath::kLine_Verb: + path->lineTo(rand.nextF()*100, rand.nextF()*100); + break; + case SkPath::kQuad_Verb: + path->quadTo(rand.nextF()*100, rand.nextF()*100, + rand.nextF()*100, rand.nextF()*100); + break; + case SkPath::kConic_Verb: + path->conicTo(rand.nextF()*100, rand.nextF()*100, + rand.nextF()*100, rand.nextF()*100, rand.nextF()*10); + break; + case SkPath::kCubic_Verb: + path->cubicTo(rand.nextF()*100, rand.nextF()*100, + rand.nextF()*100, rand.nextF()*100, + rand.nextF()*100, rand.nextF()*100); + break; + default: + SkASSERT(false); + } + } +} + +#include "SkPathOps.h" +DEF_TEST(path_tight_bounds, reporter) { + SkRandom rand; + + const SkPath::Verb verbs[] = { + SkPath::kLine_Verb, SkPath::kQuad_Verb, SkPath::kConic_Verb, SkPath::kCubic_Verb, + }; + for (int i = 0; i < 1000; ++i) { + for (int n = 1; n <= 10; n += 9) { + for (SkPath::Verb verb : verbs) { + SkPath path; + rand_path(&path, rand, verb, n); + SkRect bounds = path.getBounds(); + SkRect tight = path.computeTightBounds(); + REPORTER_ASSERT(reporter, bounds.contains(tight)); + + SkRect tight2; + TightBounds(path, &tight2); + REPORTER_ASSERT(reporter, nearly_equal(tight, tight2)); + } + } + } +} + +DEF_TEST(skbug_6450, r) { + SkRect ri = { 0.18554693f, 195.26283f, 0.185784385f, 752.644409f }; + SkVector rdi[4] = { + { 1.81159976e-09f, 7.58768801e-05f }, + { 0.000118725002f, 0.000118725002f }, + { 0.000118725002f, 0.000118725002f }, + { 0.000118725002f, 0.486297607f } + }; + SkRRect irr; + irr.setRectRadii(ri, rdi); + SkRect ro = { 9.18354821e-39f, 2.1710848e+9f, 2.16945843e+9f, 3.47808128e+9f }; + SkVector rdo[4] = { + { 0, 0 }, + { 0.0103298295f, 0.185887396f }, + { 2.52999727e-29f, 169.001938f }, + { 195.262741f, 195.161255f } + }; + SkRRect orr; + orr.setRectRadii(ro, rdo); + SkMakeNullCanvas()->drawDRRect(orr, irr, SkPaint()); }
diff --git a/src/third_party/skia/tests/PathUtilsTest.cpp b/src/third_party/skia/tests/PathUtilsTest.cpp deleted file mode 100644 index 4dfeda4..0000000 --- a/src/third_party/skia/tests/PathUtilsTest.cpp +++ /dev/null
@@ -1,152 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkBitmap.h" -#include "SkCanvas.h" -#include "SkPathUtils.h" -#include "SkRandom.h" -#include "SkTime.h" -#include "Test.h" - -const int kNumIt = 100; - -static void fill_random_bits(int chars, char* bits){ - SkRandom rand(SkTime::GetMSecs()); - - for (int i = 0; i < chars; ++i){ - bits[i] = rand.nextU(); - } -} - -static int get_bit(const char* buffer, int x) { - int byte = x >> 3; - int bit = x & 7; - - return buffer[byte] & (128 >> bit); -} - -/* // useful for debugging errors - #include <iostream> -static void print_bits( const char* bits, int w, int h) { - - for (int y = 0; y < h; ++y) { - for (int x = 0; x < w; ++x){ - bool bit = get_bit(&bits[y], x)!=0; - std::cout << bit; - } - std::cout << std::endl; - } -} - -static void print_bmp( SkBitmap* bmp, int w, int h){ - - for (int y = 0; y < h; ++y) { - for (int x = 0; x < w; ++x) { - int d = *bmp->getAddr32(x,y); - if (d == -1) - std::cout << 0; - else - std::cout << 1; - } - std::cout << std::endl; - } -} -*/ - -static void binary_to_skbitmap(const char* bin_bmp, SkBitmap* sk_bmp, - int w, int h, int rowBytes){ - //init the SkBitmap - sk_bmp->allocN32Pixels(w, h); - - for (int y = 0; y < h; ++y) { // for every row - - const char* curLine = &bin_bmp[y * rowBytes]; - for (int x = 0; x < w; ++x) {// for every pixel - if (get_bit(curLine, x)) { - *sk_bmp->getAddr32(x,y) = SK_ColorBLACK; - } - else { - *sk_bmp->getAddr32(x,y) = SK_ColorWHITE; - } - } - } -} - -static bool test_bmp(skiatest::Reporter* reporter, - const SkBitmap* bmp1, const SkBitmap* bmp2, - int w, int h) { - for (int y = 0; y < h; ++y) { // loop through all pixels - for (int x = 0; x < w; ++x) { - REPORTER_ASSERT( reporter, *bmp1->getAddr32(x,y) == *bmp2->getAddr32(x,y) ); - } - } - return true; -} - -static void test_path_eq(skiatest::Reporter* reporter, const SkPath* path, - const SkBitmap* truth, int w, int h){ - // make paint - SkPaint bmpPaint; - bmpPaint.setAntiAlias(true); // Black paint for bitmap - bmpPaint.setStyle(SkPaint::kFill_Style); - bmpPaint.setColor(SK_ColorBLACK); - - // make bmp - SkBitmap bmp; - bmp.allocN32Pixels(w, h); - SkCanvas canvas(bmp); - canvas.clear(SK_ColorWHITE); - canvas.drawPath(*path, bmpPaint); - - // test bmp - test_bmp(reporter, truth, &bmp, w, h); -} - -static void test_path(skiatest::Reporter* reporter, const SkBitmap* truth, - const char* bin_bmp, int w, int h, int rowBytes){ - // make path - SkPath path; - SkPathUtils::BitsToPath_Path(&path, bin_bmp, w, h, rowBytes); - - //test for correctness - test_path_eq(reporter, &path, truth, w, h); -} - -static void test_region(skiatest::Reporter* reporter, const SkBitmap* truth, - const char* bin_bmp, int w, int h, int rowBytes){ - //generate bitmap - SkPath path; - SkPathUtils::BitsToPath_Region(&path, bin_bmp, w, h, rowBytes); - - //test for correctness - test_path_eq(reporter, &path, truth, w, h); -} - -DEF_TEST(PathUtils, reporter) { - const int w[] = {4, 8, 12, 16}; - const int h = 8, rowBytes = 4; - - char bits[ h * rowBytes ]; - static char* binBmp = &bits[0]; - - //loop to run randomized test lots of times - for (int it = 0; it < kNumIt; ++it) - { - // generate a random binary bitmap - fill_random_bits( h * rowBytes, binBmp); // generate random bitmap - - // for each bitmap width, use subset of binary bitmap - for (unsigned int i = 0; i < SK_ARRAY_COUNT(w); ++i) { - // generate truth bitmap - SkBitmap bmpTruth; - binary_to_skbitmap(binBmp, &bmpTruth, w[i], h, rowBytes); - - test_path(reporter, &bmpTruth, binBmp, w[i], h, rowBytes); - test_region(reporter, &bmpTruth, binBmp, w[i], h, rowBytes); - } - } -}
diff --git a/src/third_party/skia/tests/PictureBBHTest.cpp b/src/third_party/skia/tests/PictureBBHTest.cpp new file mode 100644 index 0000000..0bc81c2 --- /dev/null +++ b/src/third_party/skia/tests/PictureBBHTest.cpp
@@ -0,0 +1,106 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkBBoxHierarchy.h" +#include "SkPaint.h" +#include "SkPicture.h" +#include "SkPictureRecorder.h" + +#include "Test.h" + +class PictureBBHTestBase { +public: + PictureBBHTestBase(int playbackWidth, int playbackHeight, + int recordWidth, int recordHeight) { + + fResultBitmap.allocN32Pixels(playbackWidth, playbackHeight); + fPictureWidth = recordWidth; + fPictureHeight = recordHeight; + } + + virtual ~PictureBBHTestBase() { } + + virtual void doTest(SkCanvas& playbackCanvas, SkCanvas& recordingCanvas) = 0; + + void run(skiatest::Reporter* reporter) { + // No BBH + this->run(nullptr, reporter); + + // With an R-Tree + SkRTreeFactory RTreeFactory; + this->run(&RTreeFactory, reporter); + } + +private: + void run(SkBBHFactory* factory, skiatest::Reporter* reporter) { + SkCanvas playbackCanvas(fResultBitmap); + playbackCanvas.clear(SK_ColorGREEN); + SkPictureRecorder recorder; + SkCanvas* recordCanvas = recorder.beginRecording(SkIntToScalar(fPictureWidth), + SkIntToScalar(fPictureHeight), + factory); + this->doTest(playbackCanvas, *recordCanvas); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); + playbackCanvas.drawPicture(picture); + REPORTER_ASSERT(reporter, SK_ColorGREEN == fResultBitmap.getColor(0, 0)); + } + + SkBitmap fResultBitmap; + int fPictureWidth, fPictureHeight; +}; + +// Test to verify the playback of an empty picture +// +class DrawEmptyPictureBBHTest : public PictureBBHTestBase { +public: + DrawEmptyPictureBBHTest() + : PictureBBHTestBase(2, 2, 1, 1) {} + ~DrawEmptyPictureBBHTest() override {} + + void doTest(SkCanvas&, SkCanvas&) override {} +}; + +// Test to verify the playback of a picture into a canvas that has +// an empty clip. +// +class EmptyClipPictureBBHTest : public PictureBBHTestBase { +public: + EmptyClipPictureBBHTest() + : PictureBBHTestBase(2, 2, 3, 3) {} + + void doTest(SkCanvas& playbackCanvas, SkCanvas& recordingCanvas) override { + // intersect with out of bounds rect -> empty clip. + playbackCanvas.clipRect(SkRect::MakeXYWH(10, 10, 1, 1)); + SkPaint paint; + recordingCanvas.drawRect(SkRect::MakeWH(3, 3), paint); + } + + ~EmptyClipPictureBBHTest() override {} +}; + +DEF_TEST(PictureBBH, reporter) { + + DrawEmptyPictureBBHTest emptyPictureTest; + emptyPictureTest.run(reporter); + + EmptyClipPictureBBHTest emptyClipPictureTest; + emptyClipPictureTest.run(reporter); +} + +DEF_TEST(RTreeMakeLargest, r) { + // A call to insert() with 2 or more rects and a bounds of SkRect::MakeLargest() + // used to fall into an infinite loop. + + SkRTreeFactory factory; + std::unique_ptr<SkBBoxHierarchy> bbh{ factory(SkRect::MakeLargest()) }; + + SkRect rects[] = { {0,0, 10,10}, {5,5,15,15} }; + bbh->insert(rects, SK_ARRAY_COUNT(rects)); + REPORTER_ASSERT(r, bbh->getRootBound() == SkRect::MakeWH(15,15)); +}
diff --git a/src/third_party/skia/tests/PictureShaderTest.cpp b/src/third_party/skia/tests/PictureShaderTest.cpp index 8d933db..742b72d 100644 --- a/src/third_party/skia/tests/PictureShaderTest.cpp +++ b/src/third_party/skia/tests/PictureShaderTest.cpp
@@ -5,22 +5,76 @@ * found in the LICENSE file. */ +#include "SkCanvas.h" #include "SkPicture.h" #include "SkPictureRecorder.h" +#include "SkPictureShader.h" #include "SkShader.h" +#include "SkSurface.h" #include "Test.h" -// Test that attempting to create a picture shader with a NULL picture or -// empty picture returns NULL. +// Test that attempting to create a picture shader with a nullptr picture or +// empty picture returns a shader that draws nothing. DEF_TEST(PictureShader_empty, reporter) { - SkShader* shader = SkShader::CreatePictureShader(NULL, - SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, NULL, NULL); - REPORTER_ASSERT(reporter, NULL == shader); + SkPaint paint; + + SkBitmap bitmap; + bitmap.allocN32Pixels(1,1); + + SkCanvas canvas(bitmap); + canvas.clear(SK_ColorGREEN); + + paint.setShader(SkShader::MakePictureShader( + nullptr, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, nullptr, nullptr)); + + canvas.drawRect(SkRect::MakeWH(1,1), paint); + REPORTER_ASSERT(reporter, *bitmap.getAddr32(0,0) == SK_ColorGREEN); + SkPictureRecorder factory; - factory.beginRecording(0, 0, NULL, 0); - SkAutoTUnref<SkPicture> picture(factory.endRecording()); - shader = SkShader::CreatePictureShader(picture.get(), - SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, NULL, NULL); - REPORTER_ASSERT(reporter, NULL == shader); + factory.beginRecording(0, 0, nullptr, 0); + paint.setShader(SkShader::MakePictureShader(factory.finishRecordingAsPicture(), + SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, nullptr, nullptr)); + + canvas.drawRect(SkRect::MakeWH(1,1), paint); + REPORTER_ASSERT(reporter, *bitmap.getAddr32(0,0) == SK_ColorGREEN); +} + +// Test that the SkPictureShader cache is purged on shader deletion. +DEF_TEST(PictureShader_caching, reporter) { + auto makePicture = [] () { + SkPictureRecorder recorder; + recorder.beginRecording(100, 100)->drawColor(SK_ColorGREEN); + return recorder.finishRecordingAsPicture(); + }; + + sk_sp<SkPicture> picture = makePicture(); + REPORTER_ASSERT(reporter, picture->unique()); + + sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(100, 100); + + { + SkPaint paint; + paint.setShader(SkPictureShader::Make(picture, + SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode, nullptr, nullptr)); + surface->getCanvas()->drawPaint(paint); + + // We should have about 3 refs by now: local + shader + shader cache. + REPORTER_ASSERT(reporter, !picture->unique()); + } + + // Draw another picture shader to have a chance to purge. + { + SkPaint paint; + paint.setShader(SkPictureShader::Make(makePicture(), + SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode, nullptr, nullptr)); + surface->getCanvas()->drawPaint(paint); + + } + + // All but the local ref should be gone now. + REPORTER_ASSERT(reporter, picture->unique()); }
diff --git a/src/third_party/skia/tests/PictureStateTreeTest.cpp b/src/third_party/skia/tests/PictureStateTreeTest.cpp deleted file mode 100644 index cb154de..0000000 --- a/src/third_party/skia/tests/PictureStateTreeTest.cpp +++ /dev/null
@@ -1,123 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkBBHFactory.h" -#include "SkCanvas.h" -#include "SkPictureRecorder.h" -#include "SkPictureStateTree.h" -#include "Test.h" - -static SkPicture* draw_scene(SkBBHFactory* bbhFactory) { - SkPictureRecorder recorder; - SkCanvas* canvas = recorder.beginRecording(200, 200, bbhFactory, 0); - - SkPaint p1, p2; - p1.setStyle(SkPaint::kFill_Style); - p1.setARGB(0x80, 0, 0xff, 0); - p2.setStyle(SkPaint::kFill_Style); - p2.setARGB(0x80, 0xff, 0, 0); - - canvas->drawColor(SK_ColorWHITE); - - // This is intended to exercise some tricky SkPictureStateTree code paths when - // played back with various clips: - // - // * cleanup/rewind when the last draw is not at the root of the state tree. - // * state nodes with both kSave_Flag & kSaveLayer_Flag set. - // * state tree transitions which implicitly reset the matrix via. restore(). - - canvas->save(); - canvas->translate(10, 10); - - canvas->drawRect(SkRect::MakeWH(100, 50), p1); - canvas->drawRect(SkRect::MakeWH(50, 100), p2); - - SkRect layerBounds = SkRect::MakeXYWH(0, 0, 90, 90); - canvas->saveLayer(&layerBounds, NULL); - canvas->save(); - canvas->clipRect(layerBounds); - - canvas->save(); - canvas->clipRect(SkRect::MakeWH(25, 25)); - canvas->rotate(90); - canvas->drawRect(SkRect::MakeWH(100, 50), p1); - canvas->restore(); - - canvas->save(); - canvas->clipRect(SkRect::MakeWH(25, 25)); - canvas->save(); - canvas->rotate(90); - canvas->drawRect(SkRect::MakeWH(50, 100), p2); - canvas->restore(); - canvas->drawRect(SkRect::MakeWH(100, 50), p1); - canvas->restore(); - canvas->drawRect(SkRect::MakeXYWH(99, 99, 1, 1), p1); - canvas->restore(); - canvas->restore(); - - canvas->restore(); - - return recorder.endRecording(); -} - -static void check_bms(skiatest::Reporter* reporter, const SkBitmap& bm1, const SkBitmap& bm2) { - SkASSERT(bm1.getSize() == bm2.getSize()); - REPORTER_ASSERT(reporter, 0 == memcmp(bm1.getAddr(0, 0), bm2.getAddr(0, 0), bm1.getSize())); -} - -static void test_reference_picture(skiatest::Reporter* reporter) { - SkRTreeFactory bbhFactory; - - SkAutoTUnref<SkPicture> bbhPicture(draw_scene(&bbhFactory)); - SkAutoTUnref<SkPicture> referencePicture(draw_scene(NULL)); - - SkBitmap referenceBitmap; - referenceBitmap.allocN32Pixels(100, 100); - SkCanvas referenceCanvas(referenceBitmap); - - SkBitmap bbhBitmap; - bbhBitmap.allocN32Pixels(100, 100); - SkCanvas bbhCanvas(bbhBitmap); - - referenceCanvas.drawColor(SK_ColorTRANSPARENT); - referenceCanvas.drawPicture(referencePicture.get()); - bbhCanvas.drawColor(SK_ColorTRANSPARENT); - bbhCanvas.drawPicture(bbhPicture.get()); - REPORTER_ASSERT(reporter, - referenceCanvas.getSaveCount() == bbhCanvas.getSaveCount()); - REPORTER_ASSERT(reporter, - referenceCanvas.getTotalMatrix() == bbhCanvas.getTotalMatrix()); - check_bms(reporter, referenceBitmap, bbhBitmap); - - referenceCanvas.drawColor(SK_ColorTRANSPARENT); - referenceCanvas.clipRect(SkRect::MakeWH(50, 50)); - referenceCanvas.drawPicture(referencePicture.get()); - bbhCanvas.drawColor(SK_ColorTRANSPARENT); - bbhCanvas.clipRect(SkRect::MakeWH(50, 50)); - bbhCanvas.drawPicture(bbhPicture.get()); - REPORTER_ASSERT(reporter, - referenceCanvas.getSaveCount() == bbhCanvas.getSaveCount()); - REPORTER_ASSERT(reporter, - referenceCanvas.getTotalMatrix() == bbhCanvas.getTotalMatrix()); - check_bms(reporter, referenceBitmap, bbhBitmap); - - referenceCanvas.drawColor(SK_ColorTRANSPARENT); - referenceCanvas.clipRect(SkRect::MakeWH(10, 10)); - referenceCanvas.drawPicture(referencePicture.get()); - bbhCanvas.drawColor(SK_ColorTRANSPARENT); - bbhCanvas.clipRect(SkRect::MakeWH(10, 10)); - bbhCanvas.drawPicture(bbhPicture.get()); - REPORTER_ASSERT(reporter, - referenceCanvas.getSaveCount() == bbhCanvas.getSaveCount()); - REPORTER_ASSERT(reporter, - referenceCanvas.getTotalMatrix() == bbhCanvas.getTotalMatrix()); - check_bms(reporter, referenceBitmap, bbhBitmap); -} - -DEF_TEST(PictureStateTree, reporter) { - test_reference_picture(reporter); -}
diff --git a/src/third_party/skia/tests/PictureTest.cpp b/src/third_party/skia/tests/PictureTest.cpp index 9cd63df..0dc3bfe 100644 --- a/src/third_party/skia/tests/PictureTest.cpp +++ b/src/third_party/skia/tests/PictureTest.cpp
@@ -5,39 +5,37 @@ * found in the LICENSE file. */ +#include "SkBigPicture.h" #include "SkBBoxHierarchy.h" #include "SkBlurImageFilter.h" #include "SkCanvas.h" +#include "SkColorMatrixFilter.h" #include "SkColorPriv.h" #include "SkDashPathEffect.h" #include "SkData.h" -#include "SkDecodingImageGenerator.h" -#include "SkError.h" +#include "SkImageGenerator.h" #include "SkImageEncoder.h" #include "SkImageGenerator.h" +#include "SkMD5.h" #include "SkPaint.h" #include "SkPicture.h" +#include "SkPictureAnalyzer.h" #include "SkPictureRecorder.h" -#include "SkPictureUtils.h" #include "SkPixelRef.h" +#include "SkPixelSerializer.h" +#include "SkMiniRecorder.h" #include "SkRRect.h" #include "SkRandom.h" +#include "SkRecord.h" #include "SkShader.h" #include "SkStream.h" +#include "sk_tool_utils.h" -#if SK_SUPPORT_GPU -#include "SkSurface.h" -#include "GrContextFactory.h" -#include "GrPictureUtils.h" -#endif #include "Test.h" #include "SkLumaColorFilter.h" #include "SkColorFilterImageFilter.h" -static const int gColorScale = 30; -static const int gColorOffset = 60; - static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) { bm->allocN32Pixels(w, h); bm->eraseColor(color); @@ -46,555 +44,32 @@ } } -static void make_checkerboard(SkBitmap* bm, int w, int h, bool immutable) { - SkASSERT(w % 2 == 0); - SkASSERT(h % 2 == 0); - bm->allocPixels(SkImageInfo::Make(w, h, kAlpha_8_SkColorType, - kPremul_SkAlphaType)); - SkAutoLockPixels lock(*bm); - for (int y = 0; y < h; y += 2) { - uint8_t* s = bm->getAddr8(0, y); - for (int x = 0; x < w; x += 2) { - *s++ = 0xFF; - *s++ = 0x00; - } - s = bm->getAddr8(0, y + 1); - for (int x = 0; x < w; x += 2) { - *s++ = 0x00; - *s++ = 0xFF; - } - } - if (immutable) { - bm->setImmutable(); - } -} +// For a while willPlayBackBitmaps() ignored SkImages and just looked for SkBitmaps. +static void test_images_are_found_by_willPlayBackBitmaps(skiatest::Reporter* reporter) { + // We just need _some_ SkImage + const SkPMColor pixel = 0; + const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1); + sk_sp<SkImage> image(SkImage::MakeRasterCopy(SkPixmap(info, &pixel, sizeof(pixel)))); -static void init_paint(SkPaint* paint, const SkBitmap &bm) { - SkShader* shader = SkShader::CreateBitmapShader(bm, - SkShader::kClamp_TileMode, - SkShader::kClamp_TileMode); - paint->setShader(shader)->unref(); -} - -typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, - const SkBitmap&, const SkPoint&, - SkTDArray<SkPixelRef*>* usedPixRefs); - -static void drawpaint_proc(SkCanvas* canvas, const SkBitmap& bm, - const SkBitmap& altBM, const SkPoint& pos, - SkTDArray<SkPixelRef*>* usedPixRefs) { - SkPaint paint; - init_paint(&paint, bm); - - canvas->drawPaint(paint); - *usedPixRefs->append() = bm.pixelRef(); -} - -static void drawpoints_proc(SkCanvas* canvas, const SkBitmap& bm, - const SkBitmap& altBM, const SkPoint& pos, - SkTDArray<SkPixelRef*>* usedPixRefs) { - SkPaint paint; - init_paint(&paint, bm); - - // draw a rect - SkPoint points[5] = { - { pos.fX, pos.fY }, - { pos.fX + bm.width() - 1, pos.fY }, - { pos.fX + bm.width() - 1, pos.fY + bm.height() - 1 }, - { pos.fX, pos.fY + bm.height() - 1 }, - { pos.fX, pos.fY }, - }; - - canvas->drawPoints(SkCanvas::kPolygon_PointMode, 5, points, paint); - *usedPixRefs->append() = bm.pixelRef(); -} - -static void drawrect_proc(SkCanvas* canvas, const SkBitmap& bm, - const SkBitmap& altBM, const SkPoint& pos, - SkTDArray<SkPixelRef*>* usedPixRefs) { - SkPaint paint; - init_paint(&paint, bm); - - SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; - r.offset(pos.fX, pos.fY); - - canvas->drawRect(r, paint); - *usedPixRefs->append() = bm.pixelRef(); -} - -static void drawoval_proc(SkCanvas* canvas, const SkBitmap& bm, - const SkBitmap& altBM, const SkPoint& pos, - SkTDArray<SkPixelRef*>* usedPixRefs) { - SkPaint paint; - init_paint(&paint, bm); - - SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; - r.offset(pos.fX, pos.fY); - - canvas->drawOval(r, paint); - *usedPixRefs->append() = bm.pixelRef(); -} - -static void drawrrect_proc(SkCanvas* canvas, const SkBitmap& bm, - const SkBitmap& altBM, const SkPoint& pos, - SkTDArray<SkPixelRef*>* usedPixRefs) { - SkPaint paint; - init_paint(&paint, bm); - - SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; - r.offset(pos.fX, pos.fY); - - SkRRect rr; - rr.setRectXY(r, SkIntToScalar(bm.width())/4, SkIntToScalar(bm.height())/4); - canvas->drawRRect(rr, paint); - *usedPixRefs->append() = bm.pixelRef(); -} - -static void drawpath_proc(SkCanvas* canvas, const SkBitmap& bm, - const SkBitmap& altBM, const SkPoint& pos, - SkTDArray<SkPixelRef*>* usedPixRefs) { - SkPaint paint; - init_paint(&paint, bm); - - SkPath path; - path.lineTo(bm.width()/2.0f, SkIntToScalar(bm.height())); - path.lineTo(SkIntToScalar(bm.width()), 0); - path.close(); - path.offset(pos.fX, pos.fY); - - canvas->drawPath(path, paint); - *usedPixRefs->append() = bm.pixelRef(); -} - -static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm, - const SkBitmap& altBM, const SkPoint& pos, - SkTDArray<SkPixelRef*>* usedPixRefs) { - canvas->drawBitmap(bm, pos.fX, pos.fY, NULL); - *usedPixRefs->append() = bm.pixelRef(); -} - -static void drawbitmap_withshader_proc(SkCanvas* canvas, const SkBitmap& bm, - const SkBitmap& altBM, const SkPoint& pos, - SkTDArray<SkPixelRef*>* usedPixRefs) { - SkPaint paint; - init_paint(&paint, bm); - - // The bitmap in the paint is ignored unless we're drawing an A8 bitmap - canvas->drawBitmap(altBM, pos.fX, pos.fY, &paint); - *usedPixRefs->append() = bm.pixelRef(); - *usedPixRefs->append() = altBM.pixelRef(); -} - -static void drawsprite_proc(SkCanvas* canvas, const SkBitmap& bm, - const SkBitmap& altBM, const SkPoint& pos, - SkTDArray<SkPixelRef*>* usedPixRefs) { - const SkMatrix& ctm = canvas->getTotalMatrix(); - - SkPoint p(pos); - ctm.mapPoints(&p, 1); - - canvas->drawSprite(bm, (int)p.fX, (int)p.fY, NULL); - *usedPixRefs->append() = bm.pixelRef(); -} - -#if 0 -// Although specifiable, this case doesn't seem to make sense (i.e., the -// bitmap in the shader is never used). -static void drawsprite_withshader_proc(SkCanvas* canvas, const SkBitmap& bm, - const SkBitmap& altBM, const SkPoint& pos, - SkTDArray<SkPixelRef*>* usedPixRefs) { - SkPaint paint; - init_paint(&paint, bm); - - const SkMatrix& ctm = canvas->getTotalMatrix(); - - SkPoint p(pos); - ctm.mapPoints(&p, 1); - - canvas->drawSprite(altBM, (int)p.fX, (int)p.fY, &paint); - *usedPixRefs->append() = bm.pixelRef(); - *usedPixRefs->append() = altBM.pixelRef(); -} -#endif - -static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm, - const SkBitmap& altBM, const SkPoint& pos, - SkTDArray<SkPixelRef*>* usedPixRefs) { - SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; - - r.offset(pos.fX, pos.fY); - canvas->drawBitmapRectToRect(bm, NULL, r, NULL); - *usedPixRefs->append() = bm.pixelRef(); -} - -static void drawbitmaprect_withshader_proc(SkCanvas* canvas, - const SkBitmap& bm, - const SkBitmap& altBM, - const SkPoint& pos, - SkTDArray<SkPixelRef*>* usedPixRefs) { - SkPaint paint; - init_paint(&paint, bm); - - SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; - r.offset(pos.fX, pos.fY); - - // The bitmap in the paint is ignored unless we're drawing an A8 bitmap - canvas->drawBitmapRectToRect(altBM, NULL, r, &paint); - *usedPixRefs->append() = bm.pixelRef(); - *usedPixRefs->append() = altBM.pixelRef(); -} - -static void drawtext_proc(SkCanvas* canvas, const SkBitmap& bm, - const SkBitmap& altBM, const SkPoint& pos, - SkTDArray<SkPixelRef*>* usedPixRefs) { - SkPaint paint; - init_paint(&paint, bm); - paint.setTextSize(SkIntToScalar(1.5*bm.width())); - - canvas->drawText("0", 1, pos.fX, pos.fY+bm.width(), paint); - *usedPixRefs->append() = bm.pixelRef(); -} - -static void drawpostext_proc(SkCanvas* canvas, const SkBitmap& bm, - const SkBitmap& altBM, const SkPoint& pos, - SkTDArray<SkPixelRef*>* usedPixRefs) { - SkPaint paint; - init_paint(&paint, bm); - paint.setTextSize(SkIntToScalar(1.5*bm.width())); - - SkPoint point = { pos.fX, pos.fY + bm.height() }; - canvas->drawPosText("O", 1, &point, paint); - *usedPixRefs->append() = bm.pixelRef(); -} - -static void drawtextonpath_proc(SkCanvas* canvas, const SkBitmap& bm, - const SkBitmap& altBM, const SkPoint& pos, - SkTDArray<SkPixelRef*>* usedPixRefs) { - SkPaint paint; - - init_paint(&paint, bm); - paint.setTextSize(SkIntToScalar(1.5*bm.width())); - - SkPath path; - path.lineTo(SkIntToScalar(bm.width()), 0); - path.offset(pos.fX, pos.fY+bm.height()); - - canvas->drawTextOnPath("O", 1, path, NULL, paint); - *usedPixRefs->append() = bm.pixelRef(); -} - -static void drawverts_proc(SkCanvas* canvas, const SkBitmap& bm, - const SkBitmap& altBM, const SkPoint& pos, - SkTDArray<SkPixelRef*>* usedPixRefs) { - SkPaint paint; - init_paint(&paint, bm); - - SkPoint verts[4] = { - { pos.fX, pos.fY }, - { pos.fX + bm.width(), pos.fY }, - { pos.fX + bm.width(), pos.fY + bm.height() }, - { pos.fX, pos.fY + bm.height() } - }; - SkPoint texs[4] = { { 0, 0 }, - { SkIntToScalar(bm.width()), 0 }, - { SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }, - { 0, SkIntToScalar(bm.height()) } }; - uint16_t indices[6] = { 0, 1, 2, 0, 2, 3 }; - - canvas->drawVertices(SkCanvas::kTriangles_VertexMode, 4, verts, texs, NULL, NULL, - indices, 6, paint); - *usedPixRefs->append() = bm.pixelRef(); -} - -// Return a picture with the bitmaps drawn at the specified positions. -static SkPicture* record_bitmaps(const SkBitmap bm[], - const SkPoint pos[], - SkTDArray<SkPixelRef*> analytic[], - int count, - DrawBitmapProc proc) { SkPictureRecorder recorder; - SkCanvas* canvas = recorder.beginRecording(1000, 1000); - for (int i = 0; i < count; ++i) { - analytic[i].rewind(); - canvas->save(); - SkRect clipRect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY, - SkIntToScalar(bm[i].width()), - SkIntToScalar(bm[i].height())); - canvas->clipRect(clipRect, SkRegion::kIntersect_Op); - proc(canvas, bm[i], bm[count+i], pos[i], &analytic[i]); - canvas->restore(); - } - return recorder.endRecording(); + recorder.beginRecording(100,100)->drawImage(image, 0,0); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); + + REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps()); } -static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) { - rect->fLeft = rand.nextRangeScalar(-W, 2*W); - rect->fTop = rand.nextRangeScalar(-H, 2*H); - rect->fRight = rect->fLeft + rand.nextRangeScalar(0, W); - rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H); - - // we integralize rect to make our tests more predictable, since Gather is - // a little sloppy. - SkIRect ir; - rect->round(&ir); - rect->set(ir); -} - -static void draw(SkPicture* pic, int width, int height, SkBitmap* result) { - make_bm(result, width, height, SK_ColorBLACK, false); - - SkCanvas canvas(*result); - canvas.drawPicture(pic); -} - -template <typename T> int find_index(const T* array, T elem, int count) { - for (int i = 0; i < count; ++i) { - if (array[i] == elem) { - return i; - } - } - return -1; -} - -// Return true if 'ref' is found in array[] -static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) { - return find_index<const SkPixelRef*>(array, ref, count) >= 0; -} - -// Look at each pixel that is inside 'subset', and if its color appears in -// colors[], find the corresponding value in refs[] and append that ref into -// array, skipping duplicates of the same value. -// Note that gathering pixelRefs from rendered colors suffers from the problem -// that multiple simultaneous textures (e.g., A8 for alpha and 8888 for color) -// isn't easy to reconstruct. -static void gather_from_image(const SkBitmap& bm, SkPixelRef* const refs[], - int count, SkTDArray<SkPixelRef*>* array, - const SkRect& subset) { - SkIRect ir; - subset.roundOut(&ir); - - if (!ir.intersect(0, 0, bm.width()-1, bm.height()-1)) { - return; - } - - // Since we only want to return unique values in array, when we scan we just - // set a bit for each index'd color found. In practice we only have a few - // distinct colors, so we just use an int's bits as our array. Hence the - // assert that count <= number-of-bits-in-our-int. - SkASSERT((unsigned)count <= 32); - uint32_t bitarray = 0; - - SkAutoLockPixels alp(bm); - - for (int y = ir.fTop; y < ir.fBottom; ++y) { - for (int x = ir.fLeft; x < ir.fRight; ++x) { - SkPMColor pmc = *bm.getAddr32(x, y); - // the only good case where the color is not found would be if - // the color is transparent, meaning no bitmap was drawn in that - // pixel. - if (pmc) { - uint32_t index = SkGetPackedR32(pmc); - SkASSERT(SkGetPackedG32(pmc) == index); - SkASSERT(SkGetPackedB32(pmc) == index); - if (0 == index) { - continue; // background color - } - SkASSERT(0 == (index - gColorOffset) % gColorScale); - index = (index - gColorOffset) / gColorScale; - SkASSERT(static_cast<int>(index) < count); - bitarray |= 1 << index; - } - } - } - - for (int i = 0; i < count; ++i) { - if (bitarray & (1 << i)) { - *array->append() = refs[i]; - } - } -} - -static void gather_from_analytic(const SkPoint pos[], SkScalar w, SkScalar h, - const SkTDArray<SkPixelRef*> analytic[], - int count, - SkTDArray<SkPixelRef*>* result, - const SkRect& subset) { - for (int i = 0; i < count; ++i) { - SkRect rect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY, w, h); - - if (SkRect::Intersects(subset, rect)) { - result->append(analytic[i].count(), analytic[i].begin()); - } - } -} - - -static const struct { - const DrawBitmapProc proc; - const char* const desc; -} gProcs[] = { - {drawpaint_proc, "drawpaint"}, - {drawpoints_proc, "drawpoints"}, - {drawrect_proc, "drawrect"}, - {drawoval_proc, "drawoval"}, - {drawrrect_proc, "drawrrect"}, - {drawpath_proc, "drawpath"}, - {drawbitmap_proc, "drawbitmap"}, - {drawbitmap_withshader_proc, "drawbitmap_withshader"}, - {drawsprite_proc, "drawsprite"}, -#if 0 - {drawsprite_withshader_proc, "drawsprite_withshader"}, -#endif - {drawbitmaprect_proc, "drawbitmaprect"}, - {drawbitmaprect_withshader_proc, "drawbitmaprect_withshader"}, - {drawtext_proc, "drawtext"}, - {drawpostext_proc, "drawpostext"}, - {drawtextonpath_proc, "drawtextonpath"}, - {drawverts_proc, "drawverts"}, -}; - -static void create_textures(SkBitmap* bm, SkPixelRef** refs, int num, int w, int h) { - // Our convention is that the color components contain an encoding of - // the index of their corresponding bitmap/pixelref. (0,0,0,0) is - // reserved for the background - for (int i = 0; i < num; ++i) { - make_bm(&bm[i], w, h, - SkColorSetARGB(0xFF, - gColorScale*i+gColorOffset, - gColorScale*i+gColorOffset, - gColorScale*i+gColorOffset), - true); - refs[i] = bm[i].pixelRef(); - } - - // The A8 alternate bitmaps are all BW checkerboards - for (int i = 0; i < num; ++i) { - make_checkerboard(&bm[num+i], w, h, true); - refs[num+i] = bm[num+i].pixelRef(); - } -} - -static void test_gatherpixelrefs(skiatest::Reporter* reporter) { - const int IW = 32; - const int IH = IW; - const SkScalar W = SkIntToScalar(IW); - const SkScalar H = W; - - static const int N = 4; - SkBitmap bm[2*N]; - SkPixelRef* refs[2*N]; - SkTDArray<SkPixelRef*> analytic[N]; - - const SkPoint pos[N] = { - { 0, 0 }, { W, 0 }, { 0, H }, { W, H } - }; - - create_textures(bm, refs, N, IW, IH); - - SkRandom rand; - for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) { - SkAutoTUnref<SkPicture> pic( - record_bitmaps(bm, pos, analytic, N, gProcs[k].proc)); - - REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0); - // quick check for a small piece of each quadrant, which should just - // contain 1 or 2 bitmaps. - for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) { - SkRect r; - r.set(2, 2, W - 2, H - 2); - r.offset(pos[i].fX, pos[i].fY); - SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r)); - if (!data) { - ERRORF(reporter, "SkPictureUtils::GatherPixelRefs returned " - "NULL for %s.", gProcs[k].desc); - continue; - } - SkPixelRef** gatheredRefs = (SkPixelRef**)data->data(); - int count = static_cast<int>(data->size() / sizeof(SkPixelRef*)); - REPORTER_ASSERT(reporter, 1 == count || 2 == count); - if (1 == count) { - REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]); - } else if (2 == count) { - REPORTER_ASSERT(reporter, - (gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) || - (gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N])); - } - } - - SkBitmap image; - draw(pic, 2*IW, 2*IH, &image); - - // Test a bunch of random (mostly) rects, and compare the gather results - // with a deduced list of refs by looking at the colors drawn. - for (int j = 0; j < 100; ++j) { - SkRect r; - rand_rect(&r, rand, 2*W, 2*H); - - SkTDArray<SkPixelRef*> fromImage; - gather_from_image(image, refs, N, &fromImage, r); - - SkTDArray<SkPixelRef*> fromAnalytic; - gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r); - - SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); - size_t dataSize = data ? data->size() : 0; - int gatherCount = static_cast<int>(dataSize / sizeof(SkPixelRef*)); - SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize); - SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL; - SkAutoDataUnref adu(data); - - // Everything that we saw drawn should appear in the analytic list - // but the analytic list may contain some pixelRefs that were not - // seen in the image (e.g., A8 textures used as masks) - for (int i = 0; i < fromImage.count(); ++i) { - if (-1 == fromAnalytic.find(fromImage[i])) { - ERRORF(reporter, "PixelRef missing %d %s", - i, gProcs[k].desc); - } - } - - /* - * GatherPixelRefs is conservative, so it can return more bitmaps - * than are strictly required. Thus our check here is only that - * Gather didn't miss any that we actually needed. Even that isn't - * a strict requirement on Gather, which is meant to be quick and - * only mostly-correct, but at the moment this test should work. - */ - for (int i = 0; i < fromAnalytic.count(); ++i) { - bool found = find(gatherRefs, fromAnalytic[i], gatherCount); - if (!found) { - ERRORF(reporter, "PixelRef missing %d %s", - i, gProcs[k].desc); - } -#if 0 - // enable this block of code to debug failures, as it will rerun - // the case that failed. - if (!found) { - SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); - size_t dataSize = data ? data->size() : 0; - } -#endif - } - } - } -} - -#define GENERATE_CANVAS(recorder, x) \ - (x) ? recorder.EXPERIMENTAL_beginRecording(100, 100) \ - : recorder. DEPRECATED_beginRecording(100,100); - /* Hit a few SkPicture::Analysis cases not handled elsewhere. */ -static void test_analysis(skiatest::Reporter* reporter, bool useNewPath) { +static void test_analysis(skiatest::Reporter* reporter) { SkPictureRecorder recorder; - SkCanvas* canvas = GENERATE_CANVAS(recorder, useNewPath); + SkCanvas* canvas = recorder.beginRecording(100, 100); { canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ()); } - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps()); - canvas = GENERATE_CANVAS(recorder, useNewPath); + canvas = recorder.beginRecording(100, 100); { SkPaint paint; // CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader @@ -603,103 +78,16 @@ bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2)); bitmap.eraseColor(SK_ColorBLUE); *(bitmap.getAddr32(0, 0)) = SK_ColorGREEN; - SkShader* shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode, - SkShader::kClamp_TileMode); - paint.setShader(shader)->unref(); - REPORTER_ASSERT(reporter, - shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType); + paint.setShader(SkShader::MakeBitmapShader(bitmap, SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode)); + REPORTER_ASSERT(reporter, paint.getShader()->isAImage()); canvas->drawRect(SkRect::MakeWH(10, 10), paint); } - picture.reset(recorder.endRecording()); - REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps()); + REPORTER_ASSERT(reporter, recorder.finishRecordingAsPicture()->willPlayBackBitmaps()); } -static void test_gatherpixelrefsandrects(skiatest::Reporter* reporter) { - const int IW = 32; - const int IH = IW; - const SkScalar W = SkIntToScalar(IW); - const SkScalar H = W; - - static const int N = 4; - SkBitmap bm[2*N]; - SkPixelRef* refs[2*N]; - SkTDArray<SkPixelRef*> analytic[N]; - - const SkPoint pos[N] = { - { 0, 0 }, { W, 0 }, { 0, H }, { W, H } - }; - - create_textures(bm, refs, N, IW, IH); - - SkRandom rand; - for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) { - SkAutoTUnref<SkPicture> pic( - record_bitmaps(bm, pos, analytic, N, gProcs[k].proc)); - - REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0); - - SkAutoTUnref<SkPictureUtils::SkPixelRefContainer> prCont( - new SkPictureUtils::SkPixelRefsAndRectsList); - - SkPictureUtils::GatherPixelRefsAndRects(pic, prCont); - - // quick check for a small piece of each quadrant, which should just - // contain 1 or 2 bitmaps. - for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) { - SkRect r; - r.set(2, 2, W - 2, H - 2); - r.offset(pos[i].fX, pos[i].fY); - - SkTDArray<SkPixelRef*> gatheredRefs; - prCont->query(r, &gatheredRefs); - - int count = gatheredRefs.count(); - REPORTER_ASSERT(reporter, 1 == count || 2 == count); - if (1 == count) { - REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]); - } else if (2 == count) { - REPORTER_ASSERT(reporter, - (gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) || - (gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N])); - } - } - - SkBitmap image; - draw(pic, 2*IW, 2*IH, &image); - - // Test a bunch of random (mostly) rects, and compare the gather results - // with the analytic results and the pixel refs seen in a rendering. - for (int j = 0; j < 100; ++j) { - SkRect r; - rand_rect(&r, rand, 2*W, 2*H); - - SkTDArray<SkPixelRef*> fromImage; - gather_from_image(image, refs, N, &fromImage, r); - - SkTDArray<SkPixelRef*> fromAnalytic; - gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r); - - SkTDArray<SkPixelRef*> gatheredRefs; - prCont->query(r, &gatheredRefs); - - // Everything that we saw drawn should appear in the analytic list - // but the analytic list may contain some pixelRefs that were not - // seen in the image (e.g., A8 textures used as masks) - for (int i = 0; i < fromImage.count(); ++i) { - REPORTER_ASSERT(reporter, -1 != fromAnalytic.find(fromImage[i])); - } - - // Everything in the analytic list should appear in the gathered - // list. - for (int i = 0; i < fromAnalytic.count(); ++i) { - REPORTER_ASSERT(reporter, -1 != gatheredRefs.find(fromAnalytic[i])); - } - } - } -} - #ifdef SK_DEBUG // Ensure that deleting an empty SkPicture does not assert. Asserts only fire // in debug mode, so only run in debug mode. @@ -708,7 +96,7 @@ // Creates an SkPictureRecord recorder.beginRecording(0, 0); // Turns that into an SkPicture - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); // Ceates a new SkPictureRecord recorder.beginRecording(0, 0); } @@ -717,7 +105,7 @@ static void test_serializing_empty_picture() { SkPictureRecorder recorder; recorder.beginRecording(0, 0); - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); SkDynamicMemoryWStream stream; picture->serialize(&stream); } @@ -745,34 +133,54 @@ #if SK_SUPPORT_GPU -static void test_gpu_veto(skiatest::Reporter* reporter, - bool useNewPath) { +static SkPath make_convex_path() { + SkPath path; + path.lineTo(100, 0); + path.lineTo(50, 100); + path.close(); + return path; +} + +static SkPath make_concave_path() { + SkPath path; + path.lineTo(50, 50); + path.lineTo(100, 0); + path.lineTo(50, 100); + path.close(); + + return path; +} + +static void test_gpu_veto(skiatest::Reporter* reporter) { SkPictureRecorder recorder; - SkCanvas* canvas = GENERATE_CANVAS(recorder, useNewPath); + SkCanvas* canvas = recorder.beginRecording(100, 100); { SkPath path; path.moveTo(0, 0); path.lineTo(50, 50); SkScalar intervals[] = { 1.0f, 1.0f }; - SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, 2, 0)); + sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, 2, 0)); SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); paint.setPathEffect(dash); - canvas->drawPath(path, paint); + for (int i = 0; i < 50; ++i) { + canvas->drawPath(path, paint); + } } - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); // path effects currently render an SkPicture undesireable for GPU rendering - const char *reason = NULL; - REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL, &reason)); + const char *reason = nullptr; + REPORTER_ASSERT(reporter, + !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization(&reason)); REPORTER_ASSERT(reporter, reason); - canvas = GENERATE_CANVAS(recorder, useNewPath); + canvas = recorder.beginRecording(100, 100); { SkPath path; @@ -790,11 +198,33 @@ canvas->drawPath(path, paint); } } - picture.reset(recorder.endRecording()); - // A lot of AA concave paths currently render an SkPicture undesireable for GPU rendering - REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL)); + picture = recorder.finishRecordingAsPicture(); + // A lot of small AA concave paths should be fine for GPU rendering + REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); - canvas = GENERATE_CANVAS(recorder, useNewPath); + canvas = recorder.beginRecording(100, 100); + { + SkPath path; + + path.moveTo(0, 0); + path.lineTo(0, 100); + path.lineTo(50, 50); + path.lineTo(100, 100); + path.lineTo(100, 0); + path.close(); + REPORTER_ASSERT(reporter, !path.isConvex()); + + SkPaint paint; + paint.setAntiAlias(true); + for (int i = 0; i < 50; ++i) { + canvas->drawPath(path, paint); + } + } + picture = recorder.finishRecordingAsPicture(); + // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering + REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); + + canvas = recorder.beginRecording(100, 100); { SkPath path; @@ -814,332 +244,78 @@ canvas->drawPath(path, paint); } } - picture.reset(recorder.endRecording()); + picture = recorder.finishRecordingAsPicture(); // hairline stroked AA concave paths are fine for GPU rendering - REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL)); + REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); - canvas = GENERATE_CANVAS(recorder, useNewPath); + canvas = recorder.beginRecording(100, 100); { SkPaint paint; SkScalar intervals [] = { 10, 20 }; - SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25); - paint.setPathEffect(pe)->unref(); + paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25)); SkPoint points [2] = { { 0, 0 }, { 100, 0 } }; - canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint); - } - picture.reset(recorder.endRecording()); - // fast-path dashed effects are fine for GPU rendering ... - REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL)); - canvas = GENERATE_CANVAS(recorder, useNewPath); + for (int i = 0; i < 50; ++i) { + canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint); + } + } + picture = recorder.finishRecordingAsPicture(); + // fast-path dashed effects are fine for GPU rendering ... + REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); + + canvas = recorder.beginRecording(100, 100); { SkPaint paint; SkScalar intervals [] = { 10, 20 }; - SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25); - paint.setPathEffect(pe)->unref(); + paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25)); - canvas->drawRect(SkRect::MakeWH(10, 10), paint); + for (int i = 0; i < 50; ++i) { + canvas->drawRect(SkRect::MakeWH(10, 10), paint); + } } - picture.reset(recorder.endRecording()); + picture = recorder.finishRecordingAsPicture(); // ... but only when applied to drawPoint() calls - REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL)); + REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); + + canvas = recorder.beginRecording(100, 100); + { + const SkPath convexClip = make_convex_path(); + const SkPath concaveClip = make_concave_path(); + + for (int i = 0; i < 50; ++i) { + canvas->clipPath(convexClip); + canvas->clipPath(concaveClip); + canvas->clipPath(convexClip, kIntersect_SkClipOp, true); + canvas->drawRect(SkRect::MakeWH(100, 100), SkPaint()); + } + } + picture = recorder.finishRecordingAsPicture(); + // Convex clips and non-AA concave clips are fine on the GPU. + REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); + + canvas = recorder.beginRecording(100, 100); + { + const SkPath concaveClip = make_concave_path(); + for (int i = 0; i < 50; ++i) { + canvas->clipPath(concaveClip, kIntersect_SkClipOp, true); + canvas->drawRect(SkRect::MakeWH(100, 100), SkPaint()); + } + } + picture = recorder.finishRecordingAsPicture(); + // ... but AA concave clips are not. + REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); // Nest the previous picture inside a new one. - // This doesn't work in the old backend. - if (useNewPath) { - canvas = GENERATE_CANVAS(recorder, useNewPath); - { - canvas->drawPicture(picture.get()); - } - picture.reset(recorder.endRecording()); - REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL)); + canvas = recorder.beginRecording(100, 100); + { + canvas->drawPicture(picture); } + picture = recorder.finishRecordingAsPicture(); + REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization()); } -#undef GENERATE_CANVAS - -static void test_gpu_picture_optimization(skiatest::Reporter* reporter, - GrContextFactory* factory) { - for (int i= 0; i < GrContextFactory::kGLContextTypeCnt; ++i) { - GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i; - - if (!GrContextFactory::IsRenderingGLContext(glCtxType)) { - continue; - } - - GrContext* context = factory->get(glCtxType); - - if (NULL == context) { - continue; - } - - static const int kWidth = 100; - static const int kHeight = 100; - - SkAutoTUnref<SkPicture> pict, child; - - { - SkPictureRecorder recorder; - - SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth), SkIntToScalar(kHeight)); - - c->saveLayer(NULL, NULL); - c->restore(); - - child.reset(recorder.endRecording()); - } - - // create a picture with the structure: - // 1) - // SaveLayer - // Restore - // 2) - // SaveLayer - // Translate - // SaveLayer w/ bound - // Restore - // Restore - // 3) - // SaveLayer w/ copyable paint - // Restore - // 4) - // SaveLayer - // DrawPicture (which has a SaveLayer/Restore pair) - // Restore - // 5) - // SaveLayer - // DrawPicture with Matrix & Paint (with SaveLayer/Restore pair) - // Restore - { - SkPictureRecorder recorder; - - SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth), - SkIntToScalar(kHeight)); - // 1) - c->saveLayer(NULL, NULL); // layer #0 - c->restore(); - - // 2) - c->saveLayer(NULL, NULL); // layer #1 - c->translate(kWidth/2.0f, kHeight/2.0f); - SkRect r = SkRect::MakeXYWH(0, 0, kWidth/2, kHeight/2); - c->saveLayer(&r, NULL); // layer #2 - c->restore(); - c->restore(); - - // 3) - { - SkPaint p; - p.setColor(SK_ColorRED); - c->saveLayer(NULL, &p); // layer #3 - c->restore(); - } - - SkPaint layerPaint; - layerPaint.setColor(SK_ColorRED); // Non-alpha only to avoid SaveLayerDrawRestoreNooper - // 4) - { - c->saveLayer(NULL, &layerPaint); // layer #4 - c->drawPicture(child); // layer #5 inside picture - c->restore(); - } - // 5 - { - SkPaint picturePaint; - SkMatrix trans; - trans.setTranslate(10, 10); - - c->saveLayer(NULL, &layerPaint); // layer #6 - c->drawPicture(child, &trans, &picturePaint); // layer #7 inside picture - c->restore(); - } - - pict.reset(recorder.endRecording()); - } - - // Now test out the SaveLayer extraction - { - SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight); - - SkAutoTUnref<SkSurface> surface(SkSurface::NewScratchRenderTarget(context, info)); - - SkCanvas* canvas = surface->getCanvas(); - - canvas->EXPERIMENTAL_optimize(pict); - - SkPicture::AccelData::Key key = GrAccelData::ComputeAccelDataKey(); - - const SkPicture::AccelData* data = pict->EXPERIMENTAL_getAccelData(key); - REPORTER_ASSERT(reporter, data); - - const GrAccelData *gpuData = static_cast<const GrAccelData*>(data); - REPORTER_ASSERT(reporter, 8 == gpuData->numSaveLayers()); - - const GrAccelData::SaveLayerInfo& info0 = gpuData->saveLayerInfo(0); - // The parent/child layers appear in reverse order - const GrAccelData::SaveLayerInfo& info1 = gpuData->saveLayerInfo(2); - const GrAccelData::SaveLayerInfo& info2 = gpuData->saveLayerInfo(1); - - const GrAccelData::SaveLayerInfo& info3 = gpuData->saveLayerInfo(3); - - // The parent/child layers appear in reverse order - const GrAccelData::SaveLayerInfo& info4 = gpuData->saveLayerInfo(5); - const GrAccelData::SaveLayerInfo& info5 = gpuData->saveLayerInfo(4); - - // The parent/child layers appear in reverse order - const GrAccelData::SaveLayerInfo& info6 = gpuData->saveLayerInfo(7); - const GrAccelData::SaveLayerInfo& info7 = gpuData->saveLayerInfo(6); - - REPORTER_ASSERT(reporter, info0.fValid); - REPORTER_ASSERT(reporter, NULL == info0.fPicture); - REPORTER_ASSERT(reporter, kWidth == info0.fSize.fWidth && - kHeight == info0.fSize.fHeight); - REPORTER_ASSERT(reporter, info0.fOriginXform.isIdentity()); - REPORTER_ASSERT(reporter, 0 == info0.fOffset.fX && 0 == info0.fOffset.fY); - REPORTER_ASSERT(reporter, NULL == info0.fPaint); - REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers); - - REPORTER_ASSERT(reporter, info1.fValid); - REPORTER_ASSERT(reporter, NULL == info1.fPicture); - REPORTER_ASSERT(reporter, kWidth == info1.fSize.fWidth && - kHeight == info1.fSize.fHeight); - REPORTER_ASSERT(reporter, info1.fOriginXform.isIdentity()); - REPORTER_ASSERT(reporter, 0 == info1.fOffset.fX && 0 == info1.fOffset.fY); - REPORTER_ASSERT(reporter, NULL == info1.fPaint); - REPORTER_ASSERT(reporter, !info1.fIsNested && - info1.fHasNestedLayers); // has a nested SL - - REPORTER_ASSERT(reporter, info2.fValid); - REPORTER_ASSERT(reporter, NULL == info2.fPicture); - REPORTER_ASSERT(reporter, kWidth / 2 == info2.fSize.fWidth && - kHeight/2 == info2.fSize.fHeight); // bound reduces size - REPORTER_ASSERT(reporter, !info2.fOriginXform.isIdentity()); - REPORTER_ASSERT(reporter, kWidth/2 == info2.fOffset.fX && // translated - kHeight/2 == info2.fOffset.fY); - REPORTER_ASSERT(reporter, NULL == info1.fPaint); - REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested - - REPORTER_ASSERT(reporter, info3.fValid); - REPORTER_ASSERT(reporter, NULL == info3.fPicture); - REPORTER_ASSERT(reporter, kWidth == info3.fSize.fWidth && - kHeight == info3.fSize.fHeight); - REPORTER_ASSERT(reporter, info3.fOriginXform.isIdentity()); - REPORTER_ASSERT(reporter, 0 == info3.fOffset.fX && 0 == info3.fOffset.fY); - REPORTER_ASSERT(reporter, info3.fPaint); - REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers); - - REPORTER_ASSERT(reporter, info4.fValid); - REPORTER_ASSERT(reporter, NULL == info4.fPicture); - REPORTER_ASSERT(reporter, kWidth == info4.fSize.fWidth && - kHeight == info4.fSize.fHeight); - REPORTER_ASSERT(reporter, 0 == info4.fOffset.fX && 0 == info4.fOffset.fY); - REPORTER_ASSERT(reporter, info4.fOriginXform.isIdentity()); - REPORTER_ASSERT(reporter, info4.fPaint); - REPORTER_ASSERT(reporter, !info4.fIsNested && - info4.fHasNestedLayers); // has a nested SL - - REPORTER_ASSERT(reporter, info5.fValid); - REPORTER_ASSERT(reporter, child == info5.fPicture); // in a child picture - REPORTER_ASSERT(reporter, kWidth == info5.fSize.fWidth && - kHeight == info5.fSize.fHeight); - REPORTER_ASSERT(reporter, 0 == info5.fOffset.fX && 0 == info5.fOffset.fY); - REPORTER_ASSERT(reporter, info5.fOriginXform.isIdentity()); - REPORTER_ASSERT(reporter, NULL == info5.fPaint); - REPORTER_ASSERT(reporter, info5.fIsNested && !info5.fHasNestedLayers); // is nested - - REPORTER_ASSERT(reporter, info6.fValid); - REPORTER_ASSERT(reporter, NULL == info6.fPicture); - REPORTER_ASSERT(reporter, kWidth == info6.fSize.fWidth && - kHeight == info6.fSize.fHeight); - REPORTER_ASSERT(reporter, 0 == info6.fOffset.fX && 0 == info6.fOffset.fY); - REPORTER_ASSERT(reporter, info6.fOriginXform.isIdentity()); - REPORTER_ASSERT(reporter, info6.fPaint); - REPORTER_ASSERT(reporter, !info6.fIsNested && - info6.fHasNestedLayers); // has a nested SL - - REPORTER_ASSERT(reporter, info7.fValid); - REPORTER_ASSERT(reporter, child == info7.fPicture); // in a child picture - REPORTER_ASSERT(reporter, kWidth == info7.fSize.fWidth && - kHeight == info7.fSize.fHeight); - REPORTER_ASSERT(reporter, 0 == info7.fOffset.fX && 0 == info7.fOffset.fY); - REPORTER_ASSERT(reporter, info7.fOriginXform.isIdentity()); - REPORTER_ASSERT(reporter, NULL == info7.fPaint); - REPORTER_ASSERT(reporter, info7.fIsNested && !info7.fHasNestedLayers); // is nested - } - } -} - -#endif - -static void test_has_text(skiatest::Reporter* reporter, bool useNewPath) { - SkPictureRecorder recorder; -#define BEGIN_RECORDING useNewPath ? recorder.EXPERIMENTAL_beginRecording(100, 100) \ - : recorder. DEPRECATED_beginRecording(100, 100) - - SkCanvas* canvas = BEGIN_RECORDING; - { - canvas->drawRect(SkRect::MakeWH(20, 20), SkPaint()); - } - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); - REPORTER_ASSERT(reporter, !picture->hasText()); - - SkPoint point = SkPoint::Make(10, 10); - canvas = BEGIN_RECORDING; - { - canvas->drawText("Q", 1, point.fX, point.fY, SkPaint()); - } - picture.reset(recorder.endRecording()); - REPORTER_ASSERT(reporter, picture->hasText()); - - canvas = BEGIN_RECORDING; - { - canvas->drawPosText("Q", 1, &point, SkPaint()); - } - picture.reset(recorder.endRecording()); - REPORTER_ASSERT(reporter, picture->hasText()); - - canvas = BEGIN_RECORDING; - { - canvas->drawPosTextH("Q", 1, &point.fX, point.fY, SkPaint()); - } - picture.reset(recorder.endRecording()); - REPORTER_ASSERT(reporter, picture->hasText()); - - canvas = BEGIN_RECORDING; - { - SkPath path; - path.moveTo(0, 0); - path.lineTo(50, 50); - - canvas->drawTextOnPathHV("Q", 1, path, point.fX, point.fY, SkPaint()); - } - picture.reset(recorder.endRecording()); - REPORTER_ASSERT(reporter, picture->hasText()); - - canvas = BEGIN_RECORDING; - { - SkPath path; - path.moveTo(0, 0); - path.lineTo(50, 50); - - canvas->drawTextOnPath("Q", 1, path, NULL, SkPaint()); - } - picture.reset(recorder.endRecording()); - REPORTER_ASSERT(reporter, picture->hasText()); - - // Nest the previous picture inside a new one. - // This doesn't work in the old backend. - if (useNewPath) { - canvas = BEGIN_RECORDING; - { - canvas->drawPicture(picture.get()); - } - picture.reset(recorder.endRecording()); - REPORTER_ASSERT(reporter, picture->hasText()); - } -#undef BEGIN_RECORDING -} +#endif // SK_SUPPORT_GPU static void set_canvas_to_save_count_4(SkCanvas* canvas) { canvas->restoreToCount(1); @@ -1160,18 +336,17 @@ , fRestoreCount(0){ } - virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint, - SaveFlags flags) SK_OVERRIDE { + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override { ++fSaveLayerCount; - return this->INHERITED::willSaveLayer(bounds, paint, flags); + return this->INHERITED::getSaveLayerStrategy(rec); } - virtual void willSave() SK_OVERRIDE { + void willSave() override { ++fSaveCount; this->INHERITED::willSave(); } - virtual void willRestore() SK_OVERRIDE { + void willRestore() override { ++fRestoreCount; this->INHERITED::willRestore(); } @@ -1207,14 +382,14 @@ // the 'partialReplay' method. class SkPictureRecorderReplayTester { public: - static SkPicture* Copy(SkPictureRecorder* recorder) { + static sk_sp<SkPicture> Copy(SkPictureRecorder* recorder) { SkPictureRecorder recorder2; SkCanvas* canvas = recorder2.beginRecording(10, 10); recorder->partialReplay(canvas); - return recorder2.endRecording(); + return recorder2.finishRecordingAsPicture(); } }; @@ -1222,7 +397,7 @@ SkRect clipRect = SkRect::MakeWH(2, 2); SkRect drawRect = SkRect::MakeWH(10, 10); canvas->save(); - canvas->clipRect(clipRect, SkRegion::kReplace_Op); + canvas->clipRect(clipRect, kReplace_SkClipOp); canvas->translate(1.0f, 1.0f); SkPaint p; p.setColor(SK_ColorGREEN); @@ -1241,18 +416,14 @@ SkMatrix beforeMatrix = canvas.getTotalMatrix(); - SkRect beforeClip; - - canvas.getClipBounds(&beforeClip); + SkRect beforeClip = canvas.getLocalClipBounds(); canvas.drawPicture(picture); REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount()); REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix()); - SkRect afterClip; - - canvas.getClipBounds(&afterClip); + SkRect afterClip = canvas.getLocalClipBounds(); REPORTER_ASSERT(reporter, afterClip == beforeClip); } @@ -1265,21 +436,21 @@ SkCanvas* canvas = recorder.beginRecording(10, 10); - canvas->saveLayer(NULL, NULL); + canvas->saveLayer(nullptr, nullptr); - SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); + sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); // The extra save and restore comes from the Copy process. - check_save_state(reporter, copy, 2, 1, 3); + check_save_state(reporter, copy.get(), 2, 1, 3); - canvas->saveLayer(NULL, NULL); + canvas->saveLayer(nullptr, nullptr); - SkAutoTUnref<SkPicture> final(recorder.endRecording()); + sk_sp<SkPicture> final(recorder.finishRecordingAsPicture()); - check_save_state(reporter, final, 1, 2, 3); + check_save_state(reporter, final.get(), 1, 2, 3); // The copy shouldn't pick up any operations added after it was made - check_save_state(reporter, copy, 2, 1, 3); + check_save_state(reporter, copy.get(), 2, 1, 3); } // (partially) check leakage of draw ops @@ -1293,7 +464,7 @@ canvas->drawRect(r, p); - SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); + sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps()); @@ -1301,9 +472,9 @@ make_bm(&bm, 10, 10, SK_ColorRED, true); r.offset(5.0f, 5.0f); - canvas->drawBitmapRectToRect(bm, NULL, r); + canvas->drawBitmapRect(bm, r, nullptr); - SkAutoTUnref<SkPicture> final(recorder.endRecording()); + sk_sp<SkPicture> final(recorder.finishRecordingAsPicture()); REPORTER_ASSERT(reporter, final->willPlayBackBitmaps()); REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID()); @@ -1316,19 +487,19 @@ { SkPictureRecorder recorder; - SkCanvas* canvas = recorder.beginRecording(4, 3, NULL, 0); + SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0); create_imbalance(canvas); int expectedSaveCount = canvas->getSaveCount(); - SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); - check_balance(reporter, copy); + sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); + check_balance(reporter, copy.get()); REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount()); // End the recording of source to test the picture finalization // process isn't complicated by the partialReplay step - SkAutoTUnref<SkPicture> final(recorder.endRecording()); + sk_sp<SkPicture> final(recorder.finishRecordingAsPicture()); } } @@ -1352,7 +523,7 @@ canvas->save(); canvas->translate(10, 10); canvas->drawRect(rect, paint); - SkAutoTUnref<SkPicture> extraSavePicture(recorder.endRecording()); + sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture()); testCanvas.drawPicture(extraSavePicture); REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); @@ -1373,7 +544,7 @@ canvas->restore(); canvas->restore(); canvas->restore(); - SkAutoTUnref<SkPicture> extraRestorePicture(recorder.endRecording()); + sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture()); testCanvas.drawPicture(extraRestorePicture); REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); @@ -1385,7 +556,7 @@ SkCanvas* canvas = recorder.beginRecording(100, 100); canvas->translate(10, 10); canvas->drawRect(rect, paint); - SkAutoTUnref<SkPicture> noSavePicture(recorder.endRecording()); + sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture()); testCanvas.drawPicture(noSavePicture); REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); @@ -1406,7 +577,7 @@ for (int i = 0; i < 1000; ++i) { rand_op(canvas, rand); } - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); rand = rand2; } @@ -1422,7 +593,7 @@ canvas->clipRect(rect); canvas->restore(); } - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); } } @@ -1437,121 +608,13 @@ SkPictureRecorder recorder; SkCanvas* recordingCanvas = recorder.beginRecording(100, 100); recordingCanvas->drawBitmap(bm, 0, 0); - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); SkCanvas canvas; canvas.drawPicture(picture); } #endif -static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) { - return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100); -} - -static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) { - SkPictureRecorder recorder; - SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(bitmap.width()), - SkIntToScalar(bitmap.height())); - canvas->drawBitmap(bitmap, 0, 0); - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); - - SkDynamicMemoryWStream wStream; - picture->serialize(&wStream, &encode_bitmap_to_data); - return wStream.copyToData(); -} - -struct ErrorContext { - int fErrors; - skiatest::Reporter* fReporter; -}; - -static void assert_one_parse_error_cb(SkError error, void* context) { - ErrorContext* errorContext = static_cast<ErrorContext*>(context); - errorContext->fErrors++; - // This test only expects one error, and that is a kParseError. If there are others, - // there is some unknown problem. - REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors, - "This threw more errors than expected."); - REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error, - SkGetLastErrorString()); -} - -static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) { - // Create a bitmap that will be encoded. - SkBitmap original; - make_bm(&original, 100, 100, SK_ColorBLUE, true); - SkDynamicMemoryWStream wStream; - if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) { - return; - } - SkAutoDataUnref data(wStream.copyToData()); - - SkBitmap bm; - bool installSuccess = SkInstallDiscardablePixelRef( - SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()), &bm); - REPORTER_ASSERT(reporter, installSuccess); - - // Write both bitmaps to pictures, and ensure that the resulting data streams are the same. - // Flattening original will follow the old path of performing an encode, while flattening bm - // will use the already encoded data. - SkAutoDataUnref picture1(serialized_picture_from_bitmap(original)); - SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm)); - REPORTER_ASSERT(reporter, picture1->equals(picture2)); - // Now test that a parse error was generated when trying to create a new SkPicture without - // providing a function to decode the bitmap. - ErrorContext context; - context.fErrors = 0; - context.fReporter = reporter; - SkSetErrorCallback(assert_one_parse_error_cb, &context); - SkMemoryStream pictureStream(picture1); - SkClearLastError(); - SkAutoUnref pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL)); - REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL); - SkClearLastError(); - SkSetErrorCallback(NULL, NULL); -} - -static void test_draw_empty(skiatest::Reporter* reporter) { - SkBitmap result; - make_bm(&result, 2, 2, SK_ColorBLACK, false); - - SkCanvas canvas(result); - - { - // stock SkPicture - SkPictureRecorder recorder; - recorder.beginRecording(1, 1); - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); - - canvas.drawPicture(picture); - } - - { - // tile grid - SkTileGridFactory::TileGridInfo gridInfo; - gridInfo.fMargin.setEmpty(); - gridInfo.fOffset.setZero(); - gridInfo.fTileInterval.set(1, 1); - - SkTileGridFactory factory(gridInfo); - SkPictureRecorder recorder; - recorder.beginRecording(1, 1, &factory); - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); - - canvas.drawPicture(picture); - } - - { - // RTree - SkRTreeFactory factory; - SkPictureRecorder recorder; - recorder.beginRecording(1, 1, &factory); - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); - - canvas.drawPicture(picture); - } -} - static void test_clip_bound_opt(skiatest::Reporter* reporter) { // Test for crbug.com/229011 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4), @@ -1574,9 +637,8 @@ // Testing conservative-raster-clip that is enabled by PictureRecord { SkCanvas* canvas = recorder.beginRecording(10, 10); - canvas->clipPath(invPath, SkRegion::kIntersect_Op); - bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); - REPORTER_ASSERT(reporter, true == nonEmpty); + canvas->clipPath(invPath); + clipBounds = canvas->getDeviceClipBounds(); REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); @@ -1584,10 +646,9 @@ } { SkCanvas* canvas = recorder.beginRecording(10, 10); - canvas->clipPath(path, SkRegion::kIntersect_Op); - canvas->clipPath(invPath, SkRegion::kIntersect_Op); - bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); - REPORTER_ASSERT(reporter, true == nonEmpty); + canvas->clipPath(path); + canvas->clipPath(invPath); + clipBounds = canvas->getDeviceClipBounds(); REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); @@ -1595,10 +656,9 @@ } { SkCanvas* canvas = recorder.beginRecording(10, 10); - canvas->clipPath(path, SkRegion::kIntersect_Op); - canvas->clipPath(invPath, SkRegion::kUnion_Op); - bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); - REPORTER_ASSERT(reporter, true == nonEmpty); + canvas->clipPath(path); + canvas->clipPath(invPath, kUnion_SkClipOp); + clipBounds = canvas->getDeviceClipBounds(); REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); @@ -1606,9 +666,8 @@ } { SkCanvas* canvas = recorder.beginRecording(10, 10); - canvas->clipPath(path, SkRegion::kDifference_Op); - bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); - REPORTER_ASSERT(reporter, true == nonEmpty); + canvas->clipPath(path, kDifference_SkClipOp); + clipBounds = canvas->getDeviceClipBounds(); REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); @@ -1616,12 +675,11 @@ } { SkCanvas* canvas = recorder.beginRecording(10, 10); - canvas->clipPath(path, SkRegion::kReverseDifference_Op); - bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); + canvas->clipPath(path, kReverseDifference_SkClipOp); + clipBounds = canvas->getDeviceClipBounds(); // True clip is actually empty in this case, but the best // determination we can make using only bounds as input is that the // clip is included in the bounds of 'path'. - REPORTER_ASSERT(reporter, true == nonEmpty); REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); @@ -1629,10 +687,9 @@ } { SkCanvas* canvas = recorder.beginRecording(10, 10); - canvas->clipPath(path, SkRegion::kIntersect_Op); - canvas->clipPath(path2, SkRegion::kXOR_Op); - bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); - REPORTER_ASSERT(reporter, true == nonEmpty); + canvas->clipPath(path, kIntersect_SkClipOp); + canvas->clipPath(path2, kXOR_SkClipOp); + clipBounds = canvas->getDeviceClipBounds(); REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft); REPORTER_ASSERT(reporter, 6 == clipBounds.fTop); REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); @@ -1640,6 +697,34 @@ } } +static void test_cull_rect_reset(skiatest::Reporter* reporter) { + SkPictureRecorder recorder; + SkRect bounds = SkRect::MakeWH(10, 10); + SkRTreeFactory factory; + SkCanvas* canvas = recorder.beginRecording(bounds, &factory); + bounds = SkRect::MakeWH(100, 100); + SkPaint paint; + canvas->drawRect(bounds, paint); + canvas->drawRect(bounds, paint); + sk_sp<SkPicture> p(recorder.finishRecordingAsPictureWithCull(bounds)); + const SkBigPicture* picture = p->asSkBigPicture(); + REPORTER_ASSERT(reporter, picture); + + SkRect finalCullRect = picture->cullRect(); + REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft); + REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop); + REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom); + REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight); + + const SkBBoxHierarchy* pictureBBH = picture->bbh(); + SkRect bbhCullRect = pictureBBH->getRootBound(); + REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft); + REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop); + REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom); + REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight); +} + + /** * A canvas that records the number of clip commands. */ @@ -1650,28 +735,22 @@ , fClipCount(0){ } - virtual void onClipRect(const SkRect& r, - SkRegion::Op op, - ClipEdgeStyle edgeStyle) SK_OVERRIDE { + void onClipRect(const SkRect& r, SkClipOp op, ClipEdgeStyle edgeStyle) override { fClipCount += 1; this->INHERITED::onClipRect(r, op, edgeStyle); } - virtual void onClipRRect(const SkRRect& rrect, - SkRegion::Op op, - ClipEdgeStyle edgeStyle)SK_OVERRIDE { + void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle)override { fClipCount += 1; this->INHERITED::onClipRRect(rrect, op, edgeStyle); } - virtual void onClipPath(const SkPath& path, - SkRegion::Op op, - ClipEdgeStyle edgeStyle) SK_OVERRIDE { + void onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) override { fClipCount += 1; this->INHERITED::onClipPath(path, op, edgeStyle); } - virtual void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) SK_OVERRIDE { + void onClipRegion(const SkRegion& deviceRgn, SkClipOp op) override { fClipCount += 1; this->INHERITED::onClipRegion(deviceRgn, op); } @@ -1688,14 +767,14 @@ SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(10, 10); - canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op); + canvas->clipRect(SkRect::MakeEmpty(), kReplace_SkClipOp); // The following expanding clip should not be skipped. - canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op); + canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), kUnion_SkClipOp); // Draw something so the optimizer doesn't just fold the world. SkPaint p; p.setColor(SK_ColorBLUE); canvas->drawPaint(p); - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); ClipCountingCanvas testCanvas(10, 10); picture->playback(&testCanvas); @@ -1711,37 +790,37 @@ SkPictureRecorder recorder; recorder.beginRecording(10, 10); - SkAutoTUnref<SkPicture> childPlain(recorder.endRecording()); + sk_sp<SkPicture> childPlain(recorder.finishRecordingAsPicture()); REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0); - SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording()); + sk_sp<SkPicture> childWithBitmap(recorder.finishRecordingAsPicture()); REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1 { SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->drawPicture(childPlain); - SkAutoTUnref<SkPicture> parentPP(recorder.endRecording()); + sk_sp<SkPicture> parentPP(recorder.finishRecordingAsPicture()); REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0 } { SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->drawPicture(childWithBitmap); - SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording()); + sk_sp<SkPicture> parentPWB(recorder.finishRecordingAsPicture()); REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1 } { SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->drawBitmap(bm, 0, 0); canvas->drawPicture(childPlain); - SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording()); + sk_sp<SkPicture> parentWBP(recorder.finishRecordingAsPicture()); REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1 } { SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->drawBitmap(bm, 0, 0); canvas->drawPicture(childWithBitmap); - SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording()); + sk_sp<SkPicture> parentWBWB(recorder.finishRecordingAsPicture()); REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2 } } @@ -1750,14 +829,14 @@ SkPictureRecorder recorder; recorder.beginRecording(0, 0); - SkAutoTUnref<SkPicture> empty(recorder.endRecording()); + sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture()); // Empty pictures should still have a valid ID REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID); SkCanvas* canvas = recorder.beginRecording(1, 1); - canvas->drawARGB(255, 255, 255, 255); - SkAutoTUnref<SkPicture> hasData(recorder.endRecording()); + canvas->drawColor(SK_ColorWHITE); + sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture()); // picture should have a non-zero id after recording REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID); @@ -1765,7 +844,20 @@ REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID()); } +static void test_typeface(skiatest::Reporter* reporter) { + SkPictureRecorder recorder; + SkCanvas* canvas = recorder.beginRecording(10, 10); + SkPaint paint; + paint.setTypeface(SkTypeface::MakeFromName("Arial", + SkFontStyle::FromOldStyle(SkTypeface::kItalic))); + canvas->drawString("Q", 0, 10, paint); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); + SkDynamicMemoryWStream stream; + picture->serialize(&stream); +} + DEF_TEST(Picture, reporter) { + test_typeface(reporter); #ifdef SK_DEBUG test_deleting_empty_picture(); test_serializing_empty_picture(); @@ -1775,40 +867,32 @@ test_unbalanced_save_restores(reporter); test_peephole(); #if SK_SUPPORT_GPU - test_gpu_veto(reporter, false); - test_gpu_veto(reporter, true); + test_gpu_veto(reporter); #endif - test_has_text(reporter, false); - test_has_text(reporter, true); - test_analysis(reporter, false); - test_analysis(reporter, true); - test_gatherpixelrefs(reporter); - test_gatherpixelrefsandrects(reporter); - test_bitmap_with_encoded_data(reporter); - test_draw_empty(reporter); + test_images_are_found_by_willPlayBackBitmaps(reporter); + test_analysis(reporter); test_clip_bound_opt(reporter); test_clip_expansion(reporter); test_hierarchical(reporter); test_gen_id(reporter); + test_cull_rect_reset(reporter); } -#if SK_SUPPORT_GPU -DEF_GPUTEST(GPUPicture, reporter, factory) { - test_gpu_picture_optimization(reporter, factory); -} -#endif - static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) { const SkPaint paint; const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f }; const SkIRect irect = { 2, 2, 3, 3 }; + int divs[] = { 2, 3 }; + SkCanvas::Lattice lattice; + lattice.fXCount = lattice.fYCount = 2; + lattice.fXDivs = lattice.fYDivs = divs; // Don't care what these record, as long as they're legal. canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint); - canvas->drawBitmapRectToRect(bitmap, &rect, rect, &paint, SkCanvas::kNone_DrawBitmapRectFlag); - canvas->drawBitmapMatrix(bitmap, SkMatrix::I(), &paint); + canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint); canvas->drawBitmapNine(bitmap, irect, rect, &paint); - canvas->drawSprite(bitmap, 1, 1); + canvas->drawBitmap(bitmap, 1, 1); // drawSprite + canvas->drawBitmapLattice(bitmap, lattice, rect, &paint); } static void test_draw_bitmaps(SkCanvas* canvas) { @@ -1821,7 +905,7 @@ DEF_TEST(Picture_EmptyBitmap, r) { SkPictureRecorder recorder; test_draw_bitmaps(recorder.beginRecording(10, 10)); - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); } DEF_TEST(Canvas_EmptyBitmap, r) { @@ -1836,8 +920,8 @@ // This test is from crbug.com/344987. // The commands are: // saveLayer with paint that modifies alpha - // drawBitmapRectToRect - // drawBitmapRectToRect + // drawBitmapRect + // drawBitmapRect // restore // The bug was that this structure was modified so that: // - The saveLayer and restore were eliminated @@ -1857,14 +941,14 @@ SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(100, 100); - canvas->drawARGB(0, 0, 0, 0); + canvas->drawColor(0); canvas->saveLayer(0, &semiTransparent); canvas->drawBitmap(blueBM, 25, 25); canvas->drawBitmap(redBM, 50, 50); canvas->restore(); - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); // Now replay the picture back on another canvas // and check a couple of its pixels. @@ -1882,26 +966,23 @@ struct CountingBBH : public SkBBoxHierarchy { mutable int searchCalls; + SkRect rootBound; - CountingBBH() : searchCalls(0) {} + CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {} - virtual void search(const SkRect& query, SkTDArray<void*>* results) const { + void search(const SkRect& query, SkTDArray<int>* results) const override { this->searchCalls++; } - // All other methods unimplemented. - virtual void insert(void* data, const SkRect& bounds, bool defer) {} - virtual void flushDeferredInserts() {} - virtual void clear() {} - virtual int getCount() const { return 0; } - virtual int getDepth() const { return 0; } - virtual void rewindInserts() {} + void insert(const SkRect[], int) override {} + virtual size_t bytesUsed() const override { return 0; } + SkRect getRootBound() const override { return rootBound; } }; class SpoonFedBBHFactory : public SkBBHFactory { public: explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {} - virtual SkBBoxHierarchy* operator()(int width, int height) const { + SkBBoxHierarchy* operator()(const SkRect&) const override { return SkRef(fBBH); } private: @@ -1910,12 +991,16 @@ // When the canvas clip covers the full picture, we don't need to call the BBH. DEF_TEST(Picture_SkipBBH, r) { - CountingBBH bbh; + SkRect bound = SkRect::MakeWH(320, 240); + CountingBBH bbh(bound); SpoonFedBBHFactory factory(&bbh); SkPictureRecorder recorder; - recorder.beginRecording(320, 240, &factory); - SkAutoTUnref<const SkPicture> picture(recorder.endRecording()); + SkCanvas* c = recorder.beginRecording(bound, &factory); + // Record a few ops so we don't hit a small- or empty- picture optimization. + c->drawRect(bound, SkPaint()); + c->drawRect(bound, SkPaint()); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); SkCanvas big(640, 480), small(300, 200); @@ -1938,18 +1023,152 @@ REPORTER_ASSERT(r, mut.pixelRef()->unique()); REPORTER_ASSERT(r, immut.pixelRef()->unique()); - SkPictureRecorder rec; - SkCanvas* canvas = rec.beginRecording(1920, 1200); - canvas->drawBitmap(mut, 0, 0); - canvas->drawBitmap(immut, 800, 600); - SkAutoTDelete<const SkPicture> pic(rec.endRecording()); + sk_sp<SkPicture> pic; + { + // we want the recorder to go out of scope before our subsequent checks, so we + // place it inside local braces. + SkPictureRecorder rec; + SkCanvas* canvas = rec.beginRecording(1920, 1200); + canvas->drawBitmap(mut, 0, 0); + canvas->drawBitmap(immut, 800, 600); + pic = rec.finishRecordingAsPicture(); + } // The picture shares the immutable pixels but copies the mutable ones. REPORTER_ASSERT(r, mut.pixelRef()->unique()); REPORTER_ASSERT(r, !immut.pixelRef()->unique()); // When the picture goes away, it's just our bitmaps holding the refs. - pic.reset(NULL); + pic = nullptr; REPORTER_ASSERT(r, mut.pixelRef()->unique()); REPORTER_ASSERT(r, immut.pixelRef()->unique()); } + +// getRecordingCanvas() should return a SkCanvas when recording, null when not recording. +DEF_TEST(Picture_getRecordingCanvas, r) { + SkPictureRecorder rec; + REPORTER_ASSERT(r, !rec.getRecordingCanvas()); + for (int i = 0; i < 3; i++) { + rec.beginRecording(100, 100); + REPORTER_ASSERT(r, rec.getRecordingCanvas()); + rec.finishRecordingAsPicture(); + REPORTER_ASSERT(r, !rec.getRecordingCanvas()); + } +} + +DEF_TEST(MiniRecorderLeftHanging, r) { + // Any shader or other ref-counted effect will do just fine here. + SkPaint paint; + paint.setShader(SkShader::MakeColorShader(SK_ColorRED)); + + SkMiniRecorder rec; + REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint)); + // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader. +} + +DEF_TEST(Picture_preserveCullRect, r) { + SkPictureRecorder recorder; + + SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4)); + c->clear(SK_ColorCYAN); + + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); + SkDynamicMemoryWStream wstream; + picture->serialize(&wstream); + + std::unique_ptr<SkStream> rstream(wstream.detachAsStream()); + sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream.get())); + + REPORTER_ASSERT(r, deserializedPicture != nullptr); + REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1); + REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2); + REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3); + REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4); +} + +#if SK_SUPPORT_GPU + +DEF_TEST(PictureGpuAnalyzer, r) { + SkPictureRecorder recorder; + + { + SkCanvas* canvas = recorder.beginRecording(10, 10); + SkPaint paint; + SkScalar intervals [] = { 10, 20 }; + paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25)); + + for (int i = 0; i < 50; ++i) { + canvas->drawRect(SkRect::MakeWH(10, 10), paint); + } + } + sk_sp<SkPicture> vetoPicture(recorder.finishRecordingAsPicture()); + + SkPictureGpuAnalyzer analyzer; + REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization()); + + analyzer.analyzePicture(vetoPicture.get()); + REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization()); + + analyzer.reset(); + REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization()); + + recorder.beginRecording(10, 10)->drawPicture(vetoPicture); + sk_sp<SkPicture> nestedVetoPicture(recorder.finishRecordingAsPicture()); + + analyzer.analyzePicture(nestedVetoPicture.get()); + REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization()); + + analyzer.reset(); + + const SkPath convexClip = make_convex_path(); + const SkPath concaveClip = make_concave_path(); + for (int i = 0; i < 50; ++i) { + analyzer.analyzeClipPath(convexClip, kIntersect_SkClipOp, false); + analyzer.analyzeClipPath(convexClip, kIntersect_SkClipOp, true); + analyzer.analyzeClipPath(concaveClip, kIntersect_SkClipOp, false); + } + REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization()); + + for (int i = 0; i < 50; ++i) { + analyzer.analyzeClipPath(concaveClip, kIntersect_SkClipOp, true); + } + REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization()); +} + +#endif // SK_SUPPORT_GPU + +// If we record bounded ops into a picture with a big cull and calculate the +// bounds of those ops, we should trim down the picture cull to the ops' bounds. +// If we're not using an SkBBH, we shouldn't change it. +DEF_TEST(Picture_UpdatedCull_1, r) { + // Testing 1 draw exercises SkMiniPicture. + SkRTreeFactory factory; + SkPictureRecorder recorder; + + auto canvas = recorder.beginRecording(SkRect::MakeLargest(), &factory); + canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{}); + auto pic = recorder.finishRecordingAsPicture(); + REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,20)); + + canvas = recorder.beginRecording(SkRect::MakeLargest()); + canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{}); + pic = recorder.finishRecordingAsPicture(); + REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeLargest()); +} +DEF_TEST(Picture_UpdatedCull_2, r) { + // Testing >1 draw exercises SkBigPicture. + SkRTreeFactory factory; + SkPictureRecorder recorder; + + auto canvas = recorder.beginRecording(SkRect::MakeLargest(), &factory); + canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{}); + canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{}); + auto pic = recorder.finishRecordingAsPicture(); + REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,40)); + + canvas = recorder.beginRecording(SkRect::MakeLargest()); + canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{}); + canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{}); + pic = recorder.finishRecordingAsPicture(); + REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeLargest()); +}
diff --git a/src/third_party/skia/tests/PipeTest.cpp b/src/third_party/skia/tests/PipeTest.cpp index 00a1a3f..e5d2f09 100644 --- a/src/third_party/skia/tests/PipeTest.cpp +++ b/src/third_party/skia/tests/PipeTest.cpp
@@ -1,56 +1,187 @@ /* - * Copyright 2012 Google Inc. + * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ -#include "SamplePipeControllers.h" -#include "SkBitmap.h" +#include "Resources.h" #include "SkCanvas.h" -#include "SkGPipe.h" +#include "SkPipe.h" #include "SkPaint.h" -#include "SkShader.h" +#include "SkStream.h" +#include "SkSurface.h" #include "Test.h" -// Ensures that the pipe gracefully handles drawing an invalid bitmap. -static void testDrawingBadBitmap(SkCanvas* pipeCanvas) { - SkBitmap badBitmap; - badBitmap.setInfo(SkImageInfo::MakeUnknown(5, 5)); - pipeCanvas->drawBitmap(badBitmap, 0, 0); +#include "SkNullCanvas.h" +#include "SkAutoPixmapStorage.h" +#include "SkPictureRecorder.h" + +static void drain(SkPipeDeserializer* deserial, SkDynamicMemoryWStream* stream) { + std::unique_ptr<SkCanvas> canvas = SkMakeNullCanvas(); + sk_sp<SkData> data = stream->detachAsData(); + deserial->playback(data->data(), data->size(), canvas.get()); } -// Ensure that pipe gracefully handles attempting to draw after endRecording is called on the -// SkGPipeWriter. -static void testDrawingAfterEndRecording(SkCanvas* canvas) { - PipeController pc(canvas); - SkGPipeWriter writer; - SkCanvas* pipeCanvas = writer.startRecording(&pc, SkGPipeWriter::kCrossProcess_Flag); - writer.endRecording(); +static bool deep_equal(SkImage* a, SkImage* b) { + if (a->width() != b->width() || a->height() != b->height()) { + return false; + } - SkBitmap bm; - bm.allocN32Pixels(2, 2); - bm.eraseColor(SK_ColorTRANSPARENT); + const SkImageInfo info = SkImageInfo::MakeN32Premul(a->width(), a->height()); + SkAutoPixmapStorage pmapA, pmapB; + pmapA.alloc(info); + pmapB.alloc(info); - SkShader* shader = SkShader::CreateBitmapShader(bm, SkShader::kClamp_TileMode, - SkShader::kClamp_TileMode); - SkPaint paint; - paint.setShader(shader)->unref(); - pipeCanvas->drawPaint(paint); + if (!a->readPixels(pmapA, 0, 0) || !b->readPixels(pmapB, 0, 0)) { + return false; + } - pipeCanvas->drawBitmap(bm, 0, 0); + for (int y = 0; y < info.height(); ++y) { + if (memcmp(pmapA.addr32(0, y), pmapB.addr32(0, y), info.width() * sizeof(SkPMColor))) { + return false; + } + } + return true; } -DEF_TEST(Pipe, reporter) { - SkBitmap bitmap; - bitmap.setInfo(SkImageInfo::MakeN32Premul(64, 64)); - SkCanvas canvas(bitmap); +DEF_TEST(Pipe_image_draw_first, reporter) { + sk_sp<SkImage> img = GetResourceAsImage("mandrill_128.png"); + SkASSERT(img.get()); - PipeController pipeController(&canvas); - SkGPipeWriter writer; - SkCanvas* pipeCanvas = writer.startRecording(&pipeController); - testDrawingBadBitmap(pipeCanvas); - writer.endRecording(); + SkPipeSerializer serializer; + SkPipeDeserializer deserializer; - testDrawingAfterEndRecording(&canvas); + SkDynamicMemoryWStream stream; + SkCanvas* wc = serializer.beginWrite(SkRect::MakeWH(100, 100), &stream); + wc->drawImage(img, 0, 0, nullptr); + serializer.endWrite(); + size_t offset0 = stream.bytesWritten(); + REPORTER_ASSERT(reporter, offset0 > 100); // the raw image must be sorta big + drain(&deserializer, &stream); + + // try drawing the same image again -- it should be much smaller + wc = serializer.beginWrite(SkRect::MakeWH(100, 100), &stream); + wc->drawImage(img, 0, 0, nullptr); + size_t offset1 = stream.bytesWritten(); + serializer.endWrite(); + REPORTER_ASSERT(reporter, offset1 <= 32); + drain(&deserializer, &stream); + + // try serializing the same image directly, again it should be small + sk_sp<SkData> data = serializer.writeImage(img.get()); + size_t offset2 = data->size(); + REPORTER_ASSERT(reporter, offset2 <= 32); + auto img1 = deserializer.readImage(data.get()); + REPORTER_ASSERT(reporter, deep_equal(img.get(), img1.get())); + + // try serializing the same image directly (again), check that it is the same! + data = serializer.writeImage(img.get()); + size_t offset3 = data->size(); + REPORTER_ASSERT(reporter, offset3 <= 32); + auto img2 = deserializer.readImage(data.get()); + REPORTER_ASSERT(reporter, img1.get() == img2.get()); +} + +DEF_TEST(Pipe_image_draw_second, reporter) { + sk_sp<SkImage> img = GetResourceAsImage("mandrill_128.png"); + SkASSERT(img.get()); + + SkPipeSerializer serializer; + SkPipeDeserializer deserializer; + SkDynamicMemoryWStream stream; + + sk_sp<SkData> data = serializer.writeImage(img.get()); + size_t offset0 = data->size(); + REPORTER_ASSERT(reporter, offset0 > 100); // the raw image must be sorta big + auto img1 = deserializer.readImage(data.get()); + + // The 2nd image should be nice and small + data = serializer.writeImage(img.get()); + size_t offset1 = data->size(); + REPORTER_ASSERT(reporter, offset1 <= 16); + auto img2 = deserializer.readImage(data.get()); + REPORTER_ASSERT(reporter, img1.get() == img2.get()); + + // Now try drawing the image, it should also be small + SkCanvas* wc = serializer.beginWrite(SkRect::MakeWH(100, 100), &stream); + wc->drawImage(img, 0, 0, nullptr); + serializer.endWrite(); + size_t offset2 = stream.bytesWritten(); + REPORTER_ASSERT(reporter, offset2 <= 16); +} + +DEF_TEST(Pipe_picture_draw_first, reporter) { + sk_sp<SkPicture> picture = []() { + SkPictureRecorder rec; + SkCanvas* c = rec.beginRecording(SkRect::MakeWH(100, 100)); + for (int i = 0; i < 100; ++i) { + c->drawColor(i); + } + return rec.finishRecordingAsPicture(); + }(); + SkPipeSerializer serializer; + SkPipeDeserializer deserializer; + + SkDynamicMemoryWStream stream; + SkCanvas* wc = serializer.beginWrite(SkRect::MakeWH(100, 100), &stream); + wc->drawPicture(picture); + serializer.endWrite(); + size_t offset0 = stream.bytesWritten(); + REPORTER_ASSERT(reporter, offset0 > 100); // the raw picture must be sorta big + drain(&deserializer, &stream); + + // try drawing the same picture again -- it should be much smaller + wc = serializer.beginWrite(SkRect::MakeWH(100, 100), &stream); + wc->drawPicture(picture); + size_t offset1 = stream.bytesWritten(); + serializer.endWrite(); + REPORTER_ASSERT(reporter, offset1 <= 16); + drain(&deserializer, &stream); + + // try writing the picture directly, it should also be small + sk_sp<SkData> data = serializer.writePicture(picture.get()); + size_t offset2 = data->size(); + REPORTER_ASSERT(reporter, offset2 <= 16); + auto pic1 = deserializer.readPicture(data.get()); + + // try writing the picture directly, it should also be small + data = serializer.writePicture(picture.get()); + size_t offset3 = data->size(); + REPORTER_ASSERT(reporter, offset3 == offset2); + auto pic2 = deserializer.readPicture(data.get()); + REPORTER_ASSERT(reporter, pic1.get() == pic2.get()); +} + +DEF_TEST(Pipe_picture_draw_second, reporter) { + sk_sp<SkPicture> picture = []() { + SkPictureRecorder rec; + SkCanvas* c = rec.beginRecording(SkRect::MakeWH(100, 100)); + for (int i = 0; i < 100; ++i) { + c->drawColor(i); + } + return rec.finishRecordingAsPicture(); + }(); + SkPipeSerializer serializer; + SkPipeDeserializer deserializer; + SkDynamicMemoryWStream stream; + + sk_sp<SkData> data = serializer.writePicture(picture.get()); + size_t offset0 = data->size(); + REPORTER_ASSERT(reporter, offset0 > 100); // the raw picture must be sorta big + auto pic1 = deserializer.readPicture(data.get()); + + // The 2nd picture should be nice and small + data = serializer.writePicture(picture.get()); + size_t offset1 = data->size(); + REPORTER_ASSERT(reporter, offset1 <= 16); + auto pic2 = deserializer.readPicture(data.get()); + SkASSERT(pic1.get() == pic2.get()); + + // Now try drawing the image, it should also be small + SkCanvas* wc = serializer.beginWrite(SkRect::MakeWH(100, 100), &stream); + wc->drawPicture(picture); + serializer.endWrite(); + size_t offset2 = stream.bytesWritten(); + REPORTER_ASSERT(reporter, offset2 <= 16); }
diff --git a/src/third_party/skia/tests/PixelRefTest.cpp b/src/third_party/skia/tests/PixelRefTest.cpp index d1d3026..c978d81 100644 --- a/src/third_party/skia/tests/PixelRefTest.cpp +++ b/src/third_party/skia/tests/PixelRefTest.cpp
@@ -1,12 +1,67 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + #include "Test.h" #include "SkMallocPixelRef.h" #include "SkPixelRef.h" +static void decrement_counter_proc(void* pixels, void* ctx) { + int* counter = (int*)ctx; + *counter -= 1; +} + +static void test_dont_leak_install(skiatest::Reporter* reporter) { + bool success; + int release_counter; + SkImageInfo info; + SkBitmap bm; + + info = SkImageInfo::MakeN32Premul(0, 0); + release_counter = 1; + success = bm.installPixels(info, nullptr, 0, decrement_counter_proc, &release_counter); + REPORTER_ASSERT(reporter, true == success); + bm.reset(); + REPORTER_ASSERT(reporter, 0 == release_counter); + + info = SkImageInfo::MakeN32Premul(10, 10); + release_counter = 1; + success = bm.installPixels(info, nullptr, 0, decrement_counter_proc, &release_counter); + REPORTER_ASSERT(reporter, true == success); + bm.reset(); + REPORTER_ASSERT(reporter, 0 == release_counter); + + info = SkImageInfo::MakeN32Premul(-10, -10); + release_counter = 1; + success = bm.installPixels(info, nullptr, 0, decrement_counter_proc, &release_counter); + REPORTER_ASSERT(reporter, false == success); + bm.reset(); + REPORTER_ASSERT(reporter, 0 == release_counter); +} + +static void test_install(skiatest::Reporter* reporter) { + bool success; + SkImageInfo info = SkImageInfo::MakeN32Premul(0, 0); + SkBitmap bm; + // make sure we don't assert on an empty install + success = bm.installPixels(info, nullptr, 0); + REPORTER_ASSERT(reporter, success); + + // no pixels should be the same as setInfo() + info = SkImageInfo::MakeN32Premul(10, 10); + success = bm.installPixels(info, nullptr, 0); + REPORTER_ASSERT(reporter, success); + +} + class TestListener : public SkPixelRef::GenIDChangeListener { public: explicit TestListener(int* ptr) : fPtr(ptr) {} - virtual void onChange() SK_OVERRIDE { (*fPtr)++; } + void onChange() override { (*fPtr)++; } private: int* fPtr; }; @@ -14,11 +69,11 @@ DEF_TEST(PixelRef_GenIDChange, r) { SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10); - SkAutoTUnref<SkPixelRef> pixelRef(SkMallocPixelRef::NewAllocate(info, 0, NULL)); + sk_sp<SkPixelRef> pixelRef = SkMallocPixelRef::MakeAllocate(info, 0); // Register a listener. int count = 0; - pixelRef->addGenIDChangeListener(SkNEW_ARGS(TestListener, (&count))); + pixelRef->addGenIDChangeListener(new TestListener(&count)); REPORTER_ASSERT(r, 0 == count); // No one has looked at our pixelRef's generation ID, so invalidating it doesn't make sense. @@ -35,12 +90,15 @@ // Force the generation ID to be recalculated, then add a listener. REPORTER_ASSERT(r, 0 != pixelRef->getGenerationID()); - pixelRef->addGenIDChangeListener(SkNEW_ARGS(TestListener, (&count))); + pixelRef->addGenIDChangeListener(new TestListener(&count)); pixelRef->notifyPixelsChanged(); REPORTER_ASSERT(r, 1 == count); - // Quick check that NULL is safe. + // Quick check that nullptr is safe. REPORTER_ASSERT(r, 0 != pixelRef->getGenerationID()); - pixelRef->addGenIDChangeListener(NULL); + pixelRef->addGenIDChangeListener(nullptr); pixelRef->notifyPixelsChanged(); + + test_install(r); + test_dont_leak_install(r); }
diff --git a/src/third_party/skia/tests/Point3Test.cpp b/src/third_party/skia/tests/Point3Test.cpp new file mode 100644 index 0000000..2dedd6b --- /dev/null +++ b/src/third_party/skia/tests/Point3Test.cpp
@@ -0,0 +1,130 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// Unit tests for src/core/SkPoint3.cpp and its header + +#include "SkPoint3.h" +#include "Test.h" + +static void test_eq_ops(skiatest::Reporter* reporter) { + const SkPoint3 p0 = SkPoint3::Make(0, 0, 0); + const SkPoint3 p1 = SkPoint3::Make(1, 1, 1); + const SkPoint3 p2 = SkPoint3::Make(1, 1, 1); + + REPORTER_ASSERT(reporter, p0 != p1); + REPORTER_ASSERT(reporter, p1 == p2); +} + +static void test_ops(skiatest::Reporter* reporter) { + SkPoint3 v = SkPoint3::Make(1, 1, 1); + v.normalize(); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(v.length(), SK_Scalar1)); + + // scale + SkPoint3 p = v.makeScale(3.0f); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.length(), 3.0f)); + + p.scale(1.0f/3.0f); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.length(), SK_Scalar1)); + + SkPoint3 p1 = SkPoint3::Make(20.0f, 2.0f, 10.0f); + SkPoint3 p2 = -p1; + + // - + p = p1 - p1; + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.x(), 0.0f)); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.y(), 0.0f)); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.z(), 0.0f)); + + // + + p = p1 + p2; + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.x(), 0.0f)); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.y(), 0.0f)); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.z(), 0.0f)); +} + +static void test_dot(skiatest::Reporter* reporter) { + const SkPoint3 xAxis = SkPoint3::Make(1.0f, 0.0f, 0.0f); + const SkPoint3 yAxis = SkPoint3::Make(0.0f, 1.0f, 0.0f); + const SkPoint3 zAxis = SkPoint3::Make(0.0f, 0.0f, 1.0f); + + SkScalar dot = xAxis.dot(yAxis); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dot, 0.0f)); + + dot = yAxis.dot(zAxis); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dot, 0.0f)); + + dot = zAxis.dot(xAxis); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dot, 0.0f)); + + SkPoint3 v = SkPoint3::Make(13.0f, 2.0f, 7.0f); + v.normalize(); + + dot = v.dot(v); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dot, 1.0f)); + + v = SkPoint3::Make(SK_ScalarRoot2Over2, SK_ScalarRoot2Over2, 0.0f); + + dot = xAxis.dot(v); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dot, SK_ScalarRoot2Over2)); + + dot = yAxis.dot(v); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dot, SK_ScalarRoot2Over2)); +} + +static void test_length(skiatest::Reporter* reporter, + SkScalar x, SkScalar y, SkScalar z, SkScalar expectedLen) { + SkPoint3 point = SkPoint3::Make(x, y, z); + + SkScalar s1 = point.length(); + SkScalar s2 = SkPoint3::Length(x, y, z); + + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(s1, s2)); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(s1, expectedLen)); +} + +static void test_normalize(skiatest::Reporter* reporter, + SkScalar x, SkScalar y, SkScalar z, SkScalar expectedLen) { + SkPoint3 point = SkPoint3::Make(x, y, z); + + bool result = point.normalize(); + SkScalar newLength = point.length(); + + if (0 == expectedLen) { + const SkPoint3 empty = SkPoint3::Make(0.0f, 0.0f, 0.0f); + + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(newLength, 0)); + REPORTER_ASSERT(reporter, !result); + REPORTER_ASSERT(reporter, point == empty); + } else { + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(newLength, SK_Scalar1)); + REPORTER_ASSERT(reporter, result); + } +} + +DEF_TEST(Point3, reporter) { + test_eq_ops(reporter); + test_ops(reporter); + test_dot(reporter); + + static const struct { + SkScalar fX; + SkScalar fY; + SkScalar fZ; + SkScalar fLength; + } gRec[] = { + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.3f, 0.4f, 0.5f, SK_ScalarRoot2Over2 }, + { 1.0e-37f, 1.0e-37f, 1.0e-37f, 0.0f }, // underflows + { 3.4e38f, 0.0f, 0.0f, 3.4e38f } // overflows + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { + test_length(reporter, gRec[i].fX, gRec[i].fY, gRec[i].fZ, gRec[i].fLength); + test_normalize(reporter, gRec[i].fX, gRec[i].fY, gRec[i].fZ, gRec[i].fLength); + } +}
diff --git a/src/third_party/skia/tests/PointTest.cpp b/src/third_party/skia/tests/PointTest.cpp index fed443a..c0d1f26 100644 --- a/src/third_party/skia/tests/PointTest.cpp +++ b/src/third_party/skia/tests/PointTest.cpp
@@ -1,4 +1,3 @@ - /* * Copyright 2011 Google Inc. * @@ -74,7 +73,7 @@ // even the pair of memcpy calls are not sufficient, since those seem to // be no-op'd, so we add a runtime tests (just like get_value) to force // the compiler to give us an actual float. - if (NULL == reporter) { + if (nullptr == reporter) { storage = ~storage; } memcpy(&value, &storage, 4); @@ -108,13 +107,13 @@ // report failure if we try to normalize them. static void test_underflow(skiatest::Reporter* reporter) { SkPoint pt = { 1.0e-37f, 1.0e-37f }; - SkPoint copy = pt; + const SkPoint empty = { 0, 0 }; REPORTER_ASSERT(reporter, 0 == SkPoint::Normalize(&pt)); - REPORTER_ASSERT(reporter, pt == copy); // pt is unchanged + REPORTER_ASSERT(reporter, pt == empty); REPORTER_ASSERT(reporter, !pt.setLength(SK_Scalar1)); - REPORTER_ASSERT(reporter, pt == copy); // pt is unchanged + REPORTER_ASSERT(reporter, pt == empty); } DEF_TEST(Point, reporter) {
diff --git a/src/third_party/skia/tests/PremulAlphaRoundTripTest.cpp b/src/third_party/skia/tests/PremulAlphaRoundTripTest.cpp index ce45f16..7719ad8 100644 --- a/src/third_party/skia/tests/PremulAlphaRoundTripTest.cpp +++ b/src/third_party/skia/tests/PremulAlphaRoundTripTest.cpp
@@ -5,15 +5,13 @@ * found in the LICENSE file. */ -#include "SkBitmapDevice.h" #include "SkCanvas.h" -#include "SkConfig8888.h" +#include "SkSurface.h" #include "Test.h" #include "sk_tool_utils.h" #if SK_SUPPORT_GPU -#include "GrContextFactory.h" -#include "SkGpuDevice.h" +#include "GrContext.h" #endif static uint32_t pack_unpremul_rgba(SkColor c) { @@ -46,7 +44,7 @@ { kBGRA_8888_SkColorType, pack_unpremul_bgra }, }; -static void fillCanvas(SkCanvas* canvas, SkColorType colorType, PackUnpremulProc proc) { +static void fill_canvas(SkCanvas* canvas, SkColorType colorType, PackUnpremulProc proc) { // Don't strictly need a bitmap, but its a handy way to allocate the pixels SkBitmap bmp; bmp.allocN32Pixels(256, 256); @@ -63,72 +61,55 @@ canvas->writePixels(info, bmp.getPixels(), bmp.rowBytes(), 0, 0); } -DEF_GPUTEST(PremulAlphaRoundTrip, reporter, factory) { - const SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256); +static void test_premul_alpha_roundtrip(skiatest::Reporter* reporter, SkSurface* surf) { + SkCanvas* canvas = surf->getCanvas(); + for (size_t upmaIdx = 0; upmaIdx < SK_ARRAY_COUNT(gUnpremul); ++upmaIdx) { + fill_canvas(canvas, gUnpremul[upmaIdx].fColorType, gUnpremul[upmaIdx].fPackProc); - for (int dtype = 0; dtype < 2; ++dtype) { + const SkImageInfo info = SkImageInfo::Make(256, 256, gUnpremul[upmaIdx].fColorType, + kUnpremul_SkAlphaType); + SkBitmap readBmp1; + readBmp1.allocPixels(info); + SkBitmap readBmp2; + readBmp2.allocPixels(info); - int glCtxTypeCnt = 1; -#if SK_SUPPORT_GPU - if (0 != dtype) { - glCtxTypeCnt = GrContextFactory::kGLContextTypeCnt; - } -#endif - for (int glCtxType = 0; glCtxType < glCtxTypeCnt; ++glCtxType) { - SkAutoTUnref<SkBaseDevice> device; - if (0 == dtype) { - device.reset(SkBitmapDevice::Create(info)); - } else { -#if SK_SUPPORT_GPU - GrContextFactory::GLContextType type = - static_cast<GrContextFactory::GLContextType>(glCtxType); - if (!GrContextFactory::IsRenderingGLContext(type)) { - continue; + readBmp1.eraseColor(0); + readBmp2.eraseColor(0); + + canvas->readPixels(readBmp1, 0, 0); + sk_tool_utils::write_pixels(canvas, readBmp1, 0, 0, gUnpremul[upmaIdx].fColorType, + kUnpremul_SkAlphaType); + canvas->readPixels(readBmp2, 0, 0); + + bool success = true; + for (int y = 0; y < 256 && success; ++y) { + const uint32_t* pixels1 = readBmp1.getAddr32(0, y); + const uint32_t* pixels2 = readBmp2.getAddr32(0, y); + for (int x = 0; x < 256 && success; ++x) { + // We see sporadic failures here. May help to see where it goes wrong. + if (pixels1[x] != pixels2[x]) { + SkDebugf("%x != %x, x = %d, y = %d\n", pixels1[x], pixels2[x], x, y); } - GrContext* context = factory->get(type); - if (NULL == context) { - continue; - } - - device.reset(SkGpuDevice::Create(context, info, - SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType), 0)); -#else - continue; -#endif - } - SkCanvas canvas(device); - - for (size_t upmaIdx = 0; upmaIdx < SK_ARRAY_COUNT(gUnpremul); ++upmaIdx) { - fillCanvas(&canvas, gUnpremul[upmaIdx].fColorType, gUnpremul[upmaIdx].fPackProc); - - const SkImageInfo info = SkImageInfo::Make(256, 256, gUnpremul[upmaIdx].fColorType, - kUnpremul_SkAlphaType); - SkBitmap readBmp1; - readBmp1.allocPixels(info); - SkBitmap readBmp2; - readBmp2.allocPixels(info); - - readBmp1.eraseColor(0); - readBmp2.eraseColor(0); - - canvas.readPixels(&readBmp1, 0, 0); - sk_tool_utils::write_pixels(&canvas, readBmp1, 0, 0, gUnpremul[upmaIdx].fColorType, - kUnpremul_SkAlphaType); - canvas.readPixels(&readBmp2, 0, 0); - - bool success = true; - for (int y = 0; y < 256 && success; ++y) { - const uint32_t* pixels1 = readBmp1.getAddr32(0, y); - const uint32_t* pixels2 = readBmp2.getAddr32(0, y); - for (int x = 0; x < 256 && success; ++x) { - // We see sporadic failures here. May help to see where it goes wrong. - if (pixels1[x] != pixels2[x]) { - SkDebugf("%x != %x, x = %d, y = %d\n", pixels1[x], pixels2[x], x, y); - } - REPORTER_ASSERT(reporter, success = pixels1[x] == pixels2[x]); - } - } + REPORTER_ASSERT(reporter, success = pixels1[x] == pixels2[x]); } } } } + +DEF_TEST(PremulAlphaRoundTrip, reporter) { + const SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256); + + sk_sp<SkSurface> surf(SkSurface::MakeRaster(info)); + + test_premul_alpha_roundtrip(reporter, surf.get()); +} +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PremulAlphaRoundTrip_Gpu, reporter, ctxInfo) { + const SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256); + + sk_sp<SkSurface> surf(SkSurface::MakeRenderTarget(ctxInfo.grContext(), + SkBudgeted::kNo, + info)); + test_premul_alpha_roundtrip(reporter, surf.get()); +} +#endif
diff --git a/src/third_party/skia/tests/PrimitiveProcessorTest.cpp b/src/third_party/skia/tests/PrimitiveProcessorTest.cpp new file mode 100644 index 0000000..75bdb83 --- /dev/null +++ b/src/third_party/skia/tests/PrimitiveProcessorTest.cpp
@@ -0,0 +1,144 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// This is a GPU-backend specific test. It relies on static intializers to work + +#include "SkTypes.h" +#include "Test.h" + +#if SK_SUPPORT_GPU +#include "GrContext.h" +#include "GrGeometryProcessor.h" +#include "GrGpu.h" +#include "GrOpFlushState.h" +#include "GrRenderTargetContext.h" +#include "GrRenderTargetContextPriv.h" +#include "SkString.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "glsl/GrGLSLGeometryProcessor.h" +#include "glsl/GrGLSLVarying.h" +#include "ops/GrMeshDrawOp.h" + +namespace { +class Op : public GrMeshDrawOp { +public: + DEFINE_OP_CLASS_ID + + const char* name() const override { return "Dummy Op"; } + + static std::unique_ptr<GrDrawOp> Make(int numAttribs) { + return std::unique_ptr<GrDrawOp>(new Op(numAttribs)); + } + + FixedFunctionFlags fixedFunctionFlags() const override { + return FixedFunctionFlags::kNone; + } + + RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override { + return RequiresDstTexture::kNo; + } + +private: + Op(int numAttribs) : INHERITED(ClassID()), fNumAttribs(numAttribs) { + this->setBounds(SkRect::MakeWH(1.f, 1.f), HasAABloat::kNo, IsZeroArea::kNo); + } + + bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; } + + void onPrepareDraws(Target* target) const override { + class GP : public GrGeometryProcessor { + public: + GP(int numAttribs) { + this->initClassID<GP>(); + SkASSERT(numAttribs > 1); + for (auto i = 0; i < numAttribs; ++i) { + fAttribNames.push_back().printf("attr%d", i); + } + for (auto i = 0; i < numAttribs; ++i) { + this->addVertexAttrib(fAttribNames[i].c_str(), kVec2f_GrVertexAttribType); + } + } + const char* name() const override { return "Dummy GP"; } + + GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override { + class GLSLGP : public GrGLSLGeometryProcessor { + public: + void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { + const GP& gp = args.fGP.cast<GP>(); + args.fVaryingHandler->emitAttributes(gp); + this->setupPosition(args.fVertBuilder, gpArgs, gp.getAttrib(0).fName); + GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; + fragBuilder->codeAppendf("%s = vec4(1);", args.fOutputColor); + fragBuilder->codeAppendf("%s = vec4(1);", args.fOutputCoverage); + } + void setData(const GrGLSLProgramDataManager& pdman, + const GrPrimitiveProcessor& primProc, + FPCoordTransformIter&&) override {} + }; + return new GLSLGP(); + } + void getGLSLProcessorKey(const GrShaderCaps&, + GrProcessorKeyBuilder* builder) const override { + builder->add32(this->numAttribs()); + } + + private: + SkTArray<SkString> fAttribNames; + }; + sk_sp<GrGeometryProcessor> gp(new GP(fNumAttribs)); + QuadHelper helper; + size_t vertexStride = gp->getVertexStride(); + SkPoint* vertices = reinterpret_cast<SkPoint*>(helper.init(target, vertexStride, 1)); + vertices->setRectFan(0.f, 0.f, 1.f, 1.f, vertexStride); + helper.recordDraw(target, gp.get(), target->makePipeline(0, &GrProcessorSet::EmptySet())); + } + + int fNumAttribs; + + typedef GrMeshDrawOp INHERITED; +}; +} + +DEF_GPUTEST_FOR_ALL_CONTEXTS(VertexAttributeCount, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + + sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext( + SkBackingFit::kApprox, + 1, 1, kRGBA_8888_GrPixelConfig, + nullptr)); + if (!renderTargetContext) { + ERRORF(reporter, "Could not create render target context."); + return; + } + int attribCnt = context->caps()->maxVertexAttributes(); + if (!attribCnt) { + ERRORF(reporter, "No attributes allowed?!"); + return; + } + context->flush(); + context->resetGpuStats(); +#if GR_GPU_STATS + REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 0); + REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 0); +#endif + GrPaint grPaint; + // This one should succeed. + renderTargetContext->priv().testingOnly_addDrawOp(Op::Make(attribCnt)); + context->flush(); +#if GR_GPU_STATS + REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 1); + REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 0); +#endif + context->resetGpuStats(); + renderTargetContext->priv().testingOnly_addDrawOp(Op::Make(attribCnt + 1)); + context->flush(); +#if GR_GPU_STATS + REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 0); + REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 1); +#endif +} +#endif
diff --git a/src/third_party/skia/tests/ProcessorTest.cpp b/src/third_party/skia/tests/ProcessorTest.cpp new file mode 100644 index 0000000..fd025a0 --- /dev/null +++ b/src/third_party/skia/tests/ProcessorTest.cpp
@@ -0,0 +1,459 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTypes.h" +#include "Test.h" + +#if SK_SUPPORT_GPU +#include <random> +#include "GrClip.h" +#include "GrContext.h" +#include "GrGpuResource.h" +#include "GrRenderTargetContext.h" +#include "GrRenderTargetContextPriv.h" +#include "GrResourceProvider.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "ops/GrMeshDrawOp.h" +#include "ops/GrRectOpFactory.h" + +namespace { +class TestOp : public GrMeshDrawOp { +public: + DEFINE_OP_CLASS_ID + const char* name() const override { return "TestOp"; } + + static std::unique_ptr<GrDrawOp> Make(sk_sp<GrFragmentProcessor> fp) { + return std::unique_ptr<GrDrawOp>(new TestOp(std::move(fp))); + } + + FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } + + RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override { + static constexpr GrProcessorAnalysisColor kUnknownColor; + GrColor overrideColor; + fProcessors.finalize(kUnknownColor, GrProcessorAnalysisCoverage::kNone, clip, false, caps, + &overrideColor); + return RequiresDstTexture::kNo; + } + +private: + TestOp(sk_sp<GrFragmentProcessor> fp) : INHERITED(ClassID()), fProcessors(std::move(fp)) { + this->setBounds(SkRect::MakeWH(100, 100), HasAABloat::kNo, IsZeroArea::kNo); + } + + void onPrepareDraws(Target* target) const override { return; } + + bool onCombineIfPossible(GrOp* op, const GrCaps& caps) override { return false; } + + GrProcessorSet fProcessors; + + typedef GrMeshDrawOp INHERITED; +}; + +/** + * FP used to test ref/IO counts on owned GrGpuResources. Can also be a parent FP to test counts + * of resources owned by child FPs. + */ +class TestFP : public GrFragmentProcessor { +public: + struct Image { + Image(sk_sp<GrTextureProxy> proxy, GrIOType ioType) : fProxy(proxy), fIOType(ioType) {} + sk_sp<GrTextureProxy> fProxy; + GrIOType fIOType; + }; + static sk_sp<GrFragmentProcessor> Make(sk_sp<GrFragmentProcessor> child) { + return sk_sp<GrFragmentProcessor>(new TestFP(std::move(child))); + } + static sk_sp<GrFragmentProcessor> Make(const SkTArray<sk_sp<GrTextureProxy>>& proxies, + const SkTArray<sk_sp<GrBuffer>>& buffers, + const SkTArray<Image>& images) { + return sk_sp<GrFragmentProcessor>(new TestFP(proxies, buffers, images)); + } + + const char* name() const override { return "test"; } + + void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override { + // We don't really care about reusing these. + static int32_t gKey = 0; + b->add32(sk_atomic_inc(&gKey)); + } + +private: + TestFP(const SkTArray<sk_sp<GrTextureProxy>>& proxies, + const SkTArray<sk_sp<GrBuffer>>& buffers, + const SkTArray<Image>& images) + : INHERITED(kNone_OptimizationFlags), fSamplers(4), fBuffers(4), fImages(4) { + for (const auto& proxy : proxies) { + this->addTextureSampler(&fSamplers.emplace_back(proxy)); + } + for (const auto& buffer : buffers) { + this->addBufferAccess(&fBuffers.emplace_back(kRGBA_8888_GrPixelConfig, buffer.get())); + } + for (const Image& image : images) { + fImages.emplace_back(image.fProxy, image.fIOType, + GrSLMemoryModel::kNone, GrSLRestrict::kNo); + this->addImageStorageAccess(&fImages.back()); + } + } + + TestFP(sk_sp<GrFragmentProcessor> child) + : INHERITED(kNone_OptimizationFlags), fSamplers(4), fBuffers(4), fImages(4) { + this->registerChildProcessor(std::move(child)); + } + + virtual GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { + class TestGLSLFP : public GrGLSLFragmentProcessor { + public: + TestGLSLFP() {} + void emitCode(EmitArgs& args) override { + GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; + fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, args.fInputColor); + } + + private: + }; + return new TestGLSLFP(); + } + + bool onIsEqual(const GrFragmentProcessor&) const override { return false; } + + GrTAllocator<TextureSampler> fSamplers; + GrTAllocator<BufferAccess> fBuffers; + GrTAllocator<ImageStorageAccess> fImages; + typedef GrFragmentProcessor INHERITED; +}; +} + +template <typename T> +inline void testingOnly_getIORefCnts(const T* resource, int* refCnt, int* readCnt, int* writeCnt) { + *refCnt = resource->fRefCnt; + *readCnt = resource->fPendingReads; + *writeCnt = resource->fPendingWrites; +} + +void testingOnly_getIORefCnts(GrTextureProxy* proxy, int* refCnt, int* readCnt, int* writeCnt) { + *refCnt = proxy->getBackingRefCnt_TestOnly(); + *readCnt = proxy->getPendingReadCnt_TestOnly(); + *writeCnt = proxy->getPendingWriteCnt_TestOnly(); +} + +DEF_GPUTEST_FOR_ALL_CONTEXTS(ProcessorRefTest, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + + GrTextureDesc desc; + desc.fConfig = kRGBA_8888_GrPixelConfig; + desc.fWidth = 10; + desc.fHeight = 10; + + for (int parentCnt = 0; parentCnt < 2; parentCnt++) { + sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext( + SkBackingFit::kApprox, 1, 1, kRGBA_8888_GrPixelConfig, nullptr)); + { + bool texelBufferSupport = context->caps()->shaderCaps()->texelBufferSupport(); + bool imageLoadStoreSupport = context->caps()->shaderCaps()->imageLoadStoreSupport(); + sk_sp<GrTextureProxy> proxy1(GrSurfaceProxy::MakeDeferred(context->resourceProvider(), + desc, SkBackingFit::kExact, + SkBudgeted::kYes)); + sk_sp<GrTextureProxy> proxy2(GrSurfaceProxy::MakeDeferred(context->resourceProvider(), + desc, SkBackingFit::kExact, + SkBudgeted::kYes)); + sk_sp<GrTextureProxy> proxy3(GrSurfaceProxy::MakeDeferred(context->resourceProvider(), + desc, SkBackingFit::kExact, + SkBudgeted::kYes)); + sk_sp<GrTextureProxy> proxy4(GrSurfaceProxy::MakeDeferred(context->resourceProvider(), + desc, SkBackingFit::kExact, + SkBudgeted::kYes)); + sk_sp<GrBuffer> buffer(texelBufferSupport + ? context->resourceProvider()->createBuffer( + 1024, GrBufferType::kTexel_GrBufferType, + GrAccessPattern::kStatic_GrAccessPattern, 0) + : nullptr); + { + SkTArray<sk_sp<GrTextureProxy>> proxies; + SkTArray<sk_sp<GrBuffer>> buffers; + SkTArray<TestFP::Image> images; + proxies.push_back(proxy1); + if (texelBufferSupport) { + buffers.push_back(buffer); + } + if (imageLoadStoreSupport) { + images.emplace_back(proxy2, GrIOType::kRead_GrIOType); + images.emplace_back(proxy3, GrIOType::kWrite_GrIOType); + images.emplace_back(proxy4, GrIOType::kRW_GrIOType); + } + auto fp = TestFP::Make(std::move(proxies), std::move(buffers), std::move(images)); + for (int i = 0; i < parentCnt; ++i) { + fp = TestFP::Make(std::move(fp)); + } + std::unique_ptr<GrDrawOp> op(TestOp::Make(std::move(fp))); + renderTargetContext->priv().testingOnly_addDrawOp(std::move(op)); + } + int refCnt, readCnt, writeCnt; + + testingOnly_getIORefCnts(proxy1.get(), &refCnt, &readCnt, &writeCnt); + REPORTER_ASSERT(reporter, 1 == refCnt); + REPORTER_ASSERT(reporter, 1 == readCnt); + REPORTER_ASSERT(reporter, 0 == writeCnt); + + if (texelBufferSupport) { + testingOnly_getIORefCnts(buffer.get(), &refCnt, &readCnt, &writeCnt); + REPORTER_ASSERT(reporter, 1 == refCnt); + REPORTER_ASSERT(reporter, 1 == readCnt); + REPORTER_ASSERT(reporter, 0 == writeCnt); + } + + if (imageLoadStoreSupport) { + testingOnly_getIORefCnts(proxy2.get(), &refCnt, &readCnt, &writeCnt); + REPORTER_ASSERT(reporter, 1 == refCnt); + REPORTER_ASSERT(reporter, 1 == readCnt); + REPORTER_ASSERT(reporter, 0 == writeCnt); + + testingOnly_getIORefCnts(proxy3.get(), &refCnt, &readCnt, &writeCnt); + REPORTER_ASSERT(reporter, 1 == refCnt); + REPORTER_ASSERT(reporter, 0 == readCnt); + REPORTER_ASSERT(reporter, 1 == writeCnt); + + testingOnly_getIORefCnts(proxy4.get(), &refCnt, &readCnt, &writeCnt); + REPORTER_ASSERT(reporter, 1 == refCnt); + REPORTER_ASSERT(reporter, 1 == readCnt); + REPORTER_ASSERT(reporter, 1 == writeCnt); + } + + context->flush(); + + testingOnly_getIORefCnts(proxy1.get(), &refCnt, &readCnt, &writeCnt); + REPORTER_ASSERT(reporter, 1 == refCnt); + REPORTER_ASSERT(reporter, 0 == readCnt); + REPORTER_ASSERT(reporter, 0 == writeCnt); + + if (texelBufferSupport) { + testingOnly_getIORefCnts(buffer.get(), &refCnt, &readCnt, &writeCnt); + REPORTER_ASSERT(reporter, 1 == refCnt); + REPORTER_ASSERT(reporter, 0 == readCnt); + REPORTER_ASSERT(reporter, 0 == writeCnt); + } + + if (texelBufferSupport) { + testingOnly_getIORefCnts(proxy2.get(), &refCnt, &readCnt, &writeCnt); + REPORTER_ASSERT(reporter, 1 == refCnt); + REPORTER_ASSERT(reporter, 0 == readCnt); + REPORTER_ASSERT(reporter, 0 == writeCnt); + + testingOnly_getIORefCnts(proxy3.get(), &refCnt, &readCnt, &writeCnt); + REPORTER_ASSERT(reporter, 1 == refCnt); + REPORTER_ASSERT(reporter, 0 == readCnt); + REPORTER_ASSERT(reporter, 0 == writeCnt); + + testingOnly_getIORefCnts(proxy4.get(), &refCnt, &readCnt, &writeCnt); + REPORTER_ASSERT(reporter, 1 == refCnt); + REPORTER_ASSERT(reporter, 0 == readCnt); + REPORTER_ASSERT(reporter, 0 == writeCnt); + } + } + } +} + +// This test uses the random GrFragmentProcessor test factory, which relies on static initializers. +#if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS + +static GrColor texel_color(int i, int j) { + SkASSERT((unsigned)i < 256 && (unsigned)j < 256); + GrColor color = GrColorPackRGBA(j, (uint8_t)(i + j), (uint8_t)(2 * j - i), i); + return GrPremulColor(color); +} + +static GrColor4f texel_color4f(int i, int j) { return GrColor4f::FromGrColor(texel_color(i, j)); } + +void test_draw_op(GrRenderTargetContext* rtc, sk_sp<GrFragmentProcessor> fp, + sk_sp<GrTextureProxy> inputDataProxy) { + GrPaint paint; + paint.addColorTextureProcessor(std::move(inputDataProxy), nullptr, SkMatrix::I()); + paint.addColorFragmentProcessor(std::move(fp)); + paint.setPorterDuffXPFactory(SkBlendMode::kSrc); + + auto op = GrRectOpFactory::MakeNonAAFill(std::move(paint), SkMatrix::I(), + SkRect::MakeWH(rtc->width(), rtc->height()), + GrAAType::kNone); + rtc->addDrawOp(GrNoClip(), std::move(op)); +} + +#include "SkCommandLineFlags.h" +DEFINE_bool(randomProcessorTest, false, "Use non-deterministic seed for random processor tests?"); + +#if GR_TEST_UTILS +DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + using FPFactory = GrProcessorTestFactory<GrFragmentProcessor>; + + uint32_t seed = 0; + if (FLAGS_randomProcessorTest) { + std::random_device rd; + seed = rd(); + } + // If a non-deterministic bot fails this test, check the output to see what seed it used, then + // hard-code that value here: + SkRandom random(seed); + + sk_sp<GrRenderTargetContext> rtc = context->makeDeferredRenderTargetContext( + SkBackingFit::kExact, 256, 256, kRGBA_8888_GrPixelConfig, nullptr); + GrSurfaceDesc desc; + desc.fWidth = 256; + desc.fHeight = 256; + desc.fFlags = kRenderTarget_GrSurfaceFlag; + desc.fConfig = kRGBA_8888_GrPixelConfig; + + sk_sp<GrTextureProxy> proxies[2]; + + // Put premul data into the RGBA texture that the test FPs can optionally use. + std::unique_ptr<GrColor[]> rgbaData(new GrColor[256 * 256]); + for (int y = 0; y < 256; ++y) { + for (int x = 0; x < 256; ++x) { + rgbaData.get()[256 * y + x] = + texel_color(random.nextULessThan(256), random.nextULessThan(256)); + } + } + proxies[0] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kYes, + rgbaData.get(), 256 * sizeof(GrColor)); + + // Put random values into the alpha texture that the test FPs can optionally use. + desc.fConfig = kAlpha_8_GrPixelConfig; + std::unique_ptr<uint8_t[]> alphaData(new uint8_t[256 * 256]); + for (int y = 0; y < 256; ++y) { + for (int x = 0; x < 256; ++x) { + alphaData.get()[256 * y + x] = random.nextULessThan(256); + } + } + proxies[1] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kYes, + alphaData.get(), 256); + + if (!proxies[0] || !proxies[1]) { + return; + } + + GrProcessorTestData testData(&random, context, rtc.get(), proxies); + + // Use a different array of premul colors for the output of the fragment processor that preceeds + // the fragment processor under test. + for (int y = 0; y < 256; ++y) { + for (int x = 0; x < 256; ++x) { + rgbaData.get()[256 * y + x] = texel_color(x, y); + } + } + desc.fConfig = kRGBA_8888_GrPixelConfig; + + sk_sp<GrTextureProxy> dataProxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), + desc, SkBudgeted::kYes, + rgbaData.get(), + 256 * sizeof(GrColor)); + + // Because processors factories configure themselves in random ways, this is not exhaustive. + for (int i = 0; i < FPFactory::Count(); ++i) { + int timesToInvokeFactory = 5; + // Increase the number of attempts if the FP has child FPs since optimizations likely depend + // on child optimizations being present. + sk_sp<GrFragmentProcessor> fp = FPFactory::MakeIdx(i, &testData); + for (int j = 0; j < fp->numChildProcessors(); ++j) { + // This value made a reasonable trade off between time and coverage when this test was + // written. + timesToInvokeFactory *= FPFactory::Count() / 2; + } + for (int j = 0; j < timesToInvokeFactory; ++j) { + fp = FPFactory::MakeIdx(i, &testData); + if (!fp->instantiate(context->resourceProvider())) { + continue; + } + + if (!fp->hasConstantOutputForConstantInput() && !fp->preservesOpaqueInput() && + !fp->compatibleWithCoverageAsAlpha()) { + continue; + } + test_draw_op(rtc.get(), fp, dataProxy); + memset(rgbaData.get(), 0x0, sizeof(GrColor) * 256 * 256); + rtc->readPixels( + SkImageInfo::Make(256, 256, kRGBA_8888_SkColorType, kPremul_SkAlphaType), + rgbaData.get(), 0, 0, 0); + bool passing = true; + if (0) { // Useful to see what FPs are being tested. + SkString children; + for (int c = 0; c < fp->numChildProcessors(); ++c) { + if (!c) { + children.append("("); + } + children.append(fp->childProcessor(c).name()); + children.append(c == fp->numChildProcessors() - 1 ? ")" : ", "); + } + SkDebugf("%s %s\n", fp->name(), children.c_str()); + } + for (int y = 0; y < 256 && passing; ++y) { + for (int x = 0; x < 256 && passing; ++x) { + GrColor input = texel_color(x, y); + GrColor output = rgbaData.get()[y * 256 + x]; + if (fp->compatibleWithCoverageAsAlpha()) { + // A modulating processor is allowed to modulate either the input color or + // just the input alpha. + bool legalColorModulation = + GrColorUnpackA(output) <= GrColorUnpackA(input) && + GrColorUnpackR(output) <= GrColorUnpackR(input) && + GrColorUnpackG(output) <= GrColorUnpackG(input) && + GrColorUnpackB(output) <= GrColorUnpackB(input); + bool legalAlphaModulation = + GrColorUnpackA(output) <= GrColorUnpackA(input) && + GrColorUnpackR(output) <= GrColorUnpackA(input) && + GrColorUnpackG(output) <= GrColorUnpackA(input) && + GrColorUnpackB(output) <= GrColorUnpackA(input); + if (!legalColorModulation && !legalAlphaModulation) { + ERRORF(reporter, + "\"Modulating\" processor %s made color/alpha value larger. " + "Input: 0x%08x, Output: 0x%08x.", + fp->name(), input, output); + passing = false; + } + } + GrColor4f input4f = texel_color4f(x, y); + GrColor4f output4f = GrColor4f::FromGrColor(output); + GrColor4f expected4f; + if (fp->hasConstantOutputForConstantInput(input4f, &expected4f)) { + float rDiff = fabsf(output4f.fRGBA[0] - expected4f.fRGBA[0]); + float gDiff = fabsf(output4f.fRGBA[1] - expected4f.fRGBA[1]); + float bDiff = fabsf(output4f.fRGBA[2] - expected4f.fRGBA[2]); + float aDiff = fabsf(output4f.fRGBA[3] - expected4f.fRGBA[3]); + static constexpr float kTol = 4 / 255.f; + if (rDiff > kTol || gDiff > kTol || bDiff > kTol || aDiff > kTol) { + ERRORF(reporter, + "Processor %s claimed output for const input doesn't match " + "actual output. Error: %f, Tolerance: %f, input: (%f, %f, %f, " + "%f), actual: (%f, %f, %f, %f), expected(%f, %f, %f, %f)", + fp->name(), SkTMax(rDiff, SkTMax(gDiff, SkTMax(bDiff, aDiff))), + kTol, input4f.fRGBA[0], input4f.fRGBA[1], input4f.fRGBA[2], + input4f.fRGBA[3], output4f.fRGBA[0], output4f.fRGBA[1], + output4f.fRGBA[2], output4f.fRGBA[3], expected4f.fRGBA[0], + expected4f.fRGBA[1], expected4f.fRGBA[2], expected4f.fRGBA[3]); + passing = false; + } + } + if (GrColorIsOpaque(input) && fp->preservesOpaqueInput() && + !GrColorIsOpaque(output)) { + ERRORF(reporter, + "Processor %s claimed opaqueness is preserved but it is not. Input: " + "0x%08x, Output: 0x%08x.", + fp->name(), input, output); + passing = false; + } + if (!passing) { + ERRORF(reporter, "Seed: 0x%08x, Processor details: %s", + seed, fp->dumpInfo().c_str()); + } + } + } + } + } +} + +#endif // GR_TEST_UTILS +#endif // SK_ALLOW_STATIC_GLOBAL_INITIALIZERS +#endif // SK_SUPPORT_GPU
diff --git a/src/third_party/skia/tests/ProxyConversionTest.cpp b/src/third_party/skia/tests/ProxyConversionTest.cpp new file mode 100644 index 0000000..513b680 --- /dev/null +++ b/src/third_party/skia/tests/ProxyConversionTest.cpp
@@ -0,0 +1,171 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// This is a GPU-backend specific test. + +#include "Test.h" + +#if SK_SUPPORT_GPU +#include "GrBackendSurface.h" +#include "GrRenderTarget.h" +#include "GrRenderTargetProxy.h" +#include "GrResourceProvider.h" +#include "GrSurfaceProxy.h" +#include "GrTexture.h" +#include "GrTextureProxy.h" + +static sk_sp<GrSurfaceProxy> make_wrapped_FBO0(GrResourceProvider* provider, + skiatest::Reporter* reporter, + const GrSurfaceDesc& desc) { + GrGLFramebufferInfo fboInfo; + fboInfo.fFBOID = 0; + GrBackendRenderTarget backendRT(desc.fWidth, desc.fHeight, desc.fSampleCnt, 8, + desc.fConfig, fboInfo); + + sk_sp<GrRenderTarget> defaultFBO(provider->wrapBackendRenderTarget(backendRT, desc.fOrigin)); + SkASSERT(!defaultFBO->asTexture()); + + return GrSurfaceProxy::MakeWrapped(std::move(defaultFBO)); +} + +static sk_sp<GrSurfaceProxy> make_wrapped_offscreen_rt(GrResourceProvider* provider, + skiatest::Reporter* reporter, + const GrSurfaceDesc& desc, + SkBudgeted budgeted) { + SkASSERT(kRenderTarget_GrSurfaceFlag == desc.fFlags); + + sk_sp<GrTexture> tex(provider->createTexture(desc, budgeted)); + + return GrSurfaceProxy::MakeWrapped(std::move(tex)); +} + +static sk_sp<GrSurfaceProxy> make_wrapped_texture(GrResourceProvider* provider, + const GrSurfaceDesc& desc, + SkBudgeted budgeted) { + sk_sp<GrTexture> tex(provider->createTexture(desc, budgeted)); + + return GrSurfaceProxy::MakeWrapped(std::move(tex)); +} + +// Test converting between RenderTargetProxies and TextureProxies for wrapped +// Proxies +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WrappedProxyConversionTest, reporter, ctxInfo) { + GrResourceProvider* provider = ctxInfo.grContext()->resourceProvider(); + + GrSurfaceDesc desc; + desc.fFlags = kRenderTarget_GrSurfaceFlag; + desc.fWidth = 64; + desc.fHeight = 64; + desc.fConfig = kRGBA_8888_GrPixelConfig; + desc.fOrigin = kBottomLeft_GrSurfaceOrigin; + + if (kOpenGL_GrBackend == ctxInfo.backend()) { + // External on-screen render target. + sk_sp<GrSurfaceProxy> sProxy(make_wrapped_FBO0(provider, reporter, desc)); + + // RenderTarget-only + GrRenderTargetProxy* rtProxy = sProxy->asRenderTargetProxy(); + REPORTER_ASSERT(reporter, rtProxy); + REPORTER_ASSERT(reporter, !rtProxy->asTextureProxy()); + REPORTER_ASSERT(reporter, rtProxy->asRenderTargetProxy() == rtProxy); + } + + { + // Internal offscreen render target. + sk_sp<GrSurfaceProxy> sProxy(make_wrapped_offscreen_rt(provider, + reporter, desc, + SkBudgeted::kYes)); + + // Both RenderTarget and Texture + GrRenderTargetProxy* rtProxy = sProxy->asRenderTargetProxy(); + REPORTER_ASSERT(reporter, rtProxy); + GrTextureProxy* tProxy = rtProxy->asTextureProxy(); + REPORTER_ASSERT(reporter, tProxy); + REPORTER_ASSERT(reporter, tProxy->asRenderTargetProxy() == rtProxy); + REPORTER_ASSERT(reporter, rtProxy->asRenderTargetProxy() == rtProxy); + } + + { + // Internal offscreen render target - but through GrTextureProxy + sk_sp<GrSurfaceProxy> sProxy(make_wrapped_texture(provider, desc, SkBudgeted::kYes)); + + // Both RenderTarget and Texture + GrTextureProxy* tProxy = sProxy->asTextureProxy(); + REPORTER_ASSERT(reporter, tProxy); + GrRenderTargetProxy* rtProxy = tProxy->asRenderTargetProxy(); + REPORTER_ASSERT(reporter, rtProxy); + REPORTER_ASSERT(reporter, rtProxy->asTextureProxy() == tProxy); + REPORTER_ASSERT(reporter, tProxy->asTextureProxy() == tProxy); + } + + { + desc.fFlags = kNone_GrSurfaceFlags; // force no-RT + + sk_sp<GrSurfaceProxy> sProxy(make_wrapped_texture(provider, desc, SkBudgeted::kYes)); + + // Texture-only + GrTextureProxy* tProxy = sProxy->asTextureProxy(); + REPORTER_ASSERT(reporter, tProxy); + REPORTER_ASSERT(reporter, tProxy->asTextureProxy() == tProxy); + REPORTER_ASSERT(reporter, !tProxy->asRenderTargetProxy()); + } +} + +// Test converting between RenderTargetProxies and TextureProxies for deferred +// Proxies +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DefferredProxyConversionTest, reporter, ctxInfo) { + GrResourceProvider* resourceProvider = ctxInfo.grContext()->resourceProvider(); + + GrSurfaceDesc desc; + desc.fFlags = kRenderTarget_GrSurfaceFlag; + desc.fWidth = 64; + desc.fHeight = 64; + desc.fConfig = kRGBA_8888_GrPixelConfig; + + { + sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(resourceProvider, desc, + SkBackingFit::kApprox, + SkBudgeted::kYes)); + + // Both RenderTarget and Texture + GrRenderTargetProxy* rtProxy = proxy->asRenderTargetProxy(); + REPORTER_ASSERT(reporter, rtProxy); + GrTextureProxy* tProxy = rtProxy->asTextureProxy(); + REPORTER_ASSERT(reporter, tProxy); + REPORTER_ASSERT(reporter, tProxy->asRenderTargetProxy() == rtProxy); + REPORTER_ASSERT(reporter, rtProxy->asRenderTargetProxy() == rtProxy); + } + + { + sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(resourceProvider, desc, + SkBackingFit::kApprox, + SkBudgeted::kYes)); + + // Both RenderTarget and Texture - but via GrTextureProxy + GrTextureProxy* tProxy = proxy->asTextureProxy(); + REPORTER_ASSERT(reporter, tProxy); + GrRenderTargetProxy* rtProxy = tProxy->asRenderTargetProxy(); + REPORTER_ASSERT(reporter, rtProxy); + REPORTER_ASSERT(reporter, rtProxy->asTextureProxy() == tProxy); + REPORTER_ASSERT(reporter, tProxy->asTextureProxy() == tProxy); + } + + { + desc.fFlags = kNone_GrSurfaceFlags; // force no-RT + + sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(resourceProvider, desc, + SkBackingFit::kApprox, + SkBudgeted::kYes)); + + // Texture-only + GrTextureProxy* tProxy = proxy->asTextureProxy(); + REPORTER_ASSERT(reporter, tProxy); + REPORTER_ASSERT(reporter, tProxy->asTextureProxy() == tProxy); + REPORTER_ASSERT(reporter, !tProxy->asRenderTargetProxy()); + } +} +#endif
diff --git a/src/third_party/skia/tests/ProxyRefTest.cpp b/src/third_party/skia/tests/ProxyRefTest.cpp new file mode 100644 index 0000000..04510b3 --- /dev/null +++ b/src/third_party/skia/tests/ProxyRefTest.cpp
@@ -0,0 +1,192 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// This is a GPU-backend specific test. + +#include "Test.h" + +#if SK_SUPPORT_GPU +#include "GrContextPriv.h" +#include "GrGpuResourceRef.h" +#include "GrRenderTargetProxy.h" +#include "GrResourceProvider.h" +#include "GrSurfaceProxy.h" +#include "GrTexture.h" +#include "GrTextureProxy.h" + +int32_t GrIORefProxy::getProxyRefCnt_TestOnly() const { + return fRefCnt; +} + +int32_t GrIORefProxy::getBackingRefCnt_TestOnly() const { + if (fTarget) { + return fTarget->fRefCnt; + } + + return fRefCnt; +} + +int32_t GrIORefProxy::getPendingReadCnt_TestOnly() const { + if (fTarget) { + return fTarget->fPendingReads; + } + + return fPendingReads; +} + +int32_t GrIORefProxy::getPendingWriteCnt_TestOnly() const { + if (fTarget) { + return fTarget->fPendingWrites; + } + + return fPendingWrites; +} + +static const int kWidthHeight = 128; + +static void check_refs(skiatest::Reporter* reporter, + GrTextureProxy* proxy, + int32_t expectedProxyRefs, + int32_t expectedBackingRefs, + int32_t expectedNumReads, + int32_t expectedNumWrites) { + REPORTER_ASSERT(reporter, proxy->getProxyRefCnt_TestOnly() == expectedProxyRefs); + REPORTER_ASSERT(reporter, proxy->getBackingRefCnt_TestOnly() == expectedBackingRefs); + REPORTER_ASSERT(reporter, proxy->getPendingReadCnt_TestOnly() == expectedNumReads); + REPORTER_ASSERT(reporter, proxy->getPendingWriteCnt_TestOnly() == expectedNumWrites); + + SkASSERT(proxy->getProxyRefCnt_TestOnly() == expectedProxyRefs); + SkASSERT(proxy->getBackingRefCnt_TestOnly() == expectedBackingRefs); + SkASSERT(proxy->getPendingReadCnt_TestOnly() == expectedNumReads); + SkASSERT(proxy->getPendingWriteCnt_TestOnly() == expectedNumWrites); +} + +static sk_sp<GrTextureProxy> make_deferred(GrContext* context) { + GrSurfaceDesc desc; + desc.fFlags = kRenderTarget_GrSurfaceFlag; + desc.fWidth = kWidthHeight; + desc.fHeight = kWidthHeight; + desc.fConfig = kRGBA_8888_GrPixelConfig; + + return GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, + SkBackingFit::kApprox, SkBudgeted::kYes, + GrResourceProvider::kNoPendingIO_Flag); +} + +static sk_sp<GrTextureProxy> make_wrapped(GrContext* context) { + GrSurfaceDesc desc; + desc.fFlags = kRenderTarget_GrSurfaceFlag; + desc.fWidth = kWidthHeight; + desc.fHeight = kWidthHeight; + desc.fConfig = kRGBA_8888_GrPixelConfig; + + sk_sp<GrTexture> tex(context->resourceProvider()->createTexture(desc, SkBudgeted::kNo)); + + return GrSurfaceProxy::MakeWrapped(std::move(tex)); +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ProxyRefTest, reporter, ctxInfo) { + GrResourceProvider* provider = ctxInfo.grContext()->resourceProvider(); + + for (auto make : { make_deferred, make_wrapped }) { + // A single write + { + sk_sp<GrTextureProxy> proxy((*make)(ctxInfo.grContext())); + + GrPendingIOResource<GrSurfaceProxy, kWrite_GrIOType> fWrite(proxy.get()); + + static const int kExpectedReads = 0; + static const int kExpectedWrites = 1; + + check_refs(reporter, proxy.get(), 1, 1, kExpectedReads, kExpectedWrites); + + proxy->instantiate(provider); + + // In the deferred case, this checks that the refs transfered to the GrSurface + check_refs(reporter, proxy.get(), 1, 1, kExpectedReads, kExpectedWrites); + } + + // A single read + { + sk_sp<GrTextureProxy> proxy((*make)(ctxInfo.grContext())); + + GrPendingIOResource<GrSurfaceProxy, kRead_GrIOType> fRead(proxy.get()); + + static const int kExpectedReads = 1; + static const int kExpectedWrites = 0; + + check_refs(reporter, proxy.get(), 1, 1, kExpectedReads, kExpectedWrites); + + proxy->instantiate(provider); + + // In the deferred case, this checks that the refs transfered to the GrSurface + check_refs(reporter, proxy.get(), 1, 1, kExpectedReads, kExpectedWrites); + } + + // A single read/write pair + { + sk_sp<GrTextureProxy> proxy((*make)(ctxInfo.grContext())); + + GrPendingIOResource<GrSurfaceProxy, kRW_GrIOType> fRW(proxy.get()); + + static const int kExpectedReads = 1; + static const int kExpectedWrites = 1; + + check_refs(reporter, proxy.get(), 1, 1, kExpectedReads, kExpectedWrites); + + proxy->instantiate(provider); + + // In the deferred case, this checks that the refs transferred to the GrSurface + check_refs(reporter, proxy.get(), 1, 1, kExpectedReads, kExpectedWrites); + } + + // Multiple normal refs + { + sk_sp<GrTextureProxy> proxy((*make)(ctxInfo.grContext())); + proxy->ref(); + proxy->ref(); + + static const int kExpectedReads = 0; + static const int kExpectedWrites = 0; + + check_refs(reporter, proxy.get(), 3, 3,kExpectedReads, kExpectedWrites); + + proxy->instantiate(provider); + + // In the deferred case, this checks that the refs transferred to the GrSurface + check_refs(reporter, proxy.get(), 3, 3, kExpectedReads, kExpectedWrites); + + proxy->unref(); + proxy->unref(); + } + + // Continue using (reffing) proxy after instantiation + { + sk_sp<GrTextureProxy> proxy((*make)(ctxInfo.grContext())); + proxy->ref(); + + GrPendingIOResource<GrSurfaceProxy, kWrite_GrIOType> fWrite(proxy.get()); + + static const int kExpectedWrites = 1; + + check_refs(reporter, proxy.get(), 2, 2, 0, kExpectedWrites); + + proxy->instantiate(provider); + + // In the deferred case, this checks that the refs transfered to the GrSurface + check_refs(reporter, proxy.get(), 2, 2, 0, kExpectedWrites); + + proxy->unref(); + check_refs(reporter, proxy.get(), 1, 1, 0, kExpectedWrites); + + GrPendingIOResource<GrSurfaceProxy, kRead_GrIOType> fRead(proxy.get()); + check_refs(reporter, proxy.get(), 1, 1, 1, kExpectedWrites); + } + } +} + +#endif
diff --git a/src/third_party/skia/tests/ProxyTest.cpp b/src/third_party/skia/tests/ProxyTest.cpp new file mode 100644 index 0000000..dd1f4cb --- /dev/null +++ b/src/third_party/skia/tests/ProxyTest.cpp
@@ -0,0 +1,311 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// This is a GPU-backend specific test. + +#include "Test.h" + +#if SK_SUPPORT_GPU + +#include "GrBackendSurface.h" +#include "GrRenderTargetPriv.h" +#include "GrRenderTargetProxy.h" +#include "GrResourceProvider.h" +#include "GrSurfaceProxy.h" +#include "GrTexture.h" +#include "GrTextureProxy.h" + +// Check that the surface proxy's member vars are set as expected +static void check_surface(skiatest::Reporter* reporter, + GrSurfaceProxy* proxy, + GrSurfaceOrigin origin, + int width, int height, + GrPixelConfig config, + const GrGpuResource::UniqueID& uniqueID, + SkBudgeted budgeted) { + REPORTER_ASSERT(reporter, proxy->origin() == origin); + REPORTER_ASSERT(reporter, proxy->width() == width); + REPORTER_ASSERT(reporter, proxy->height() == height); + REPORTER_ASSERT(reporter, proxy->config() == config); + if (!uniqueID.isInvalid()) { + REPORTER_ASSERT(reporter, proxy->uniqueID().asUInt() == uniqueID.asUInt()); + } else { + REPORTER_ASSERT(reporter, !proxy->uniqueID().isInvalid()); + } + REPORTER_ASSERT(reporter, proxy->isBudgeted() == budgeted); +} + +static void check_rendertarget(skiatest::Reporter* reporter, + const GrCaps& caps, + GrResourceProvider* provider, + GrRenderTargetProxy* rtProxy, + int numSamples, + SkBackingFit fit, + int expectedMaxWindowRects, + bool wasWrapped) { + REPORTER_ASSERT(reporter, rtProxy->maxWindowRectangles(caps) == expectedMaxWindowRects); + REPORTER_ASSERT(reporter, rtProxy->numStencilSamples() == numSamples); + + GrSurfaceProxy::UniqueID idBefore = rtProxy->uniqueID(); + REPORTER_ASSERT(reporter, rtProxy->instantiate(provider)); + GrRenderTarget* rt = rtProxy->priv().peekRenderTarget(); + + REPORTER_ASSERT(reporter, rtProxy->uniqueID() == idBefore); + if (wasWrapped) { + // Wrapped resources share their uniqueID with the wrapping RenderTargetProxy + REPORTER_ASSERT(reporter, rtProxy->uniqueID().asUInt() == rt->uniqueID().asUInt()); + } else { + // Deferred resources should always have a different ID from their instantiated rendertarget + REPORTER_ASSERT(reporter, rtProxy->uniqueID().asUInt() != rt->uniqueID().asUInt()); + } + + REPORTER_ASSERT(reporter, rt->origin() == rtProxy->origin()); + if (SkBackingFit::kExact == fit) { + REPORTER_ASSERT(reporter, rt->width() == rtProxy->width()); + REPORTER_ASSERT(reporter, rt->height() == rtProxy->height()); + } else { + REPORTER_ASSERT(reporter, rt->width() >= rtProxy->width()); + REPORTER_ASSERT(reporter, rt->height() >= rtProxy->height()); + } + REPORTER_ASSERT(reporter, rt->config() == rtProxy->config()); + + REPORTER_ASSERT(reporter, rt->fsaaType() == rtProxy->fsaaType()); + REPORTER_ASSERT(reporter, rt->numColorSamples() == rtProxy->numColorSamples()); + REPORTER_ASSERT(reporter, rt->numStencilSamples() == rtProxy->numStencilSamples()); + REPORTER_ASSERT(reporter, rt->renderTargetPriv().flags() == rtProxy->testingOnly_getFlags()); +} + +static void check_texture(skiatest::Reporter* reporter, + GrResourceProvider* provider, + GrTextureProxy* texProxy, + SkBackingFit fit, + bool wasWrapped) { + GrSurfaceProxy::UniqueID idBefore = texProxy->uniqueID(); + + REPORTER_ASSERT(reporter, texProxy->instantiate(provider)); + GrTexture* tex = texProxy->priv().peekTexture(); + + REPORTER_ASSERT(reporter, texProxy->uniqueID() == idBefore); + if (wasWrapped) { + // Wrapped resources share their uniqueID with the wrapping TextureProxy + REPORTER_ASSERT(reporter, texProxy->uniqueID().asUInt() == tex->uniqueID().asUInt()); + } else { + // Deferred resources should always have a different ID from their instantiated texture + REPORTER_ASSERT(reporter, texProxy->uniqueID().asUInt() != tex->uniqueID().asUInt()); + } + + REPORTER_ASSERT(reporter, tex->origin() == texProxy->origin()); + if (SkBackingFit::kExact == fit) { + REPORTER_ASSERT(reporter, tex->width() == texProxy->width()); + REPORTER_ASSERT(reporter, tex->height() == texProxy->height()); + } else { + REPORTER_ASSERT(reporter, tex->width() >= texProxy->width()); + REPORTER_ASSERT(reporter, tex->height() >= texProxy->height()); + } + REPORTER_ASSERT(reporter, tex->config() == texProxy->config()); +} + + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DeferredProxyTest, reporter, ctxInfo) { + GrResourceProvider* provider = ctxInfo.grContext()->resourceProvider(); + const GrCaps& caps = *ctxInfo.grContext()->caps(); + + const GrGpuResource::UniqueID kInvalidResourceID = GrGpuResource::UniqueID::InvalidID(); + + int attempt = 0; // useful for debugging + + for (auto origin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) { + for (auto widthHeight : { 100, 128, 1048576 }) { + for (auto config : { kAlpha_8_GrPixelConfig, kRGB_565_GrPixelConfig, + kRGBA_8888_GrPixelConfig }) { + for (auto fit : { SkBackingFit::kExact, SkBackingFit::kApprox }) { + for (auto budgeted : { SkBudgeted::kYes, SkBudgeted::kNo }) { + for (auto numSamples : { 0, 4, 16, 128 }) { + GrSurfaceDesc desc; + desc.fFlags = kRenderTarget_GrSurfaceFlag; + desc.fOrigin = origin; + desc.fWidth = widthHeight; + desc.fHeight = widthHeight; + desc.fConfig = config; + desc.fSampleCnt = numSamples; + + { + sk_sp<GrTexture> tex; + if (SkBackingFit::kApprox == fit) { + tex = provider->createApproxTexture(desc, 0); + } else { + tex = provider->createTexture(desc, budgeted); + } + + sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred( + provider, desc, + fit, budgeted)); + REPORTER_ASSERT(reporter, SkToBool(tex) == SkToBool(proxy)); + if (proxy) { + REPORTER_ASSERT(reporter, proxy->asRenderTargetProxy()); + // This forces the proxy to compute and cache its + // pre-instantiation size guess. Later, when it is actually + // instantiated, it checks that the instantiated size is <= to + // the pre-computation. If the proxy never computed its + // pre-instantiation size then the check is skipped. + proxy->gpuMemorySize(); + + check_surface(reporter, proxy.get(), origin, + widthHeight, widthHeight, config, + kInvalidResourceID, budgeted); + int supportedSamples = caps.getSampleCount(numSamples, config); + check_rendertarget(reporter, caps, provider, + proxy->asRenderTargetProxy(), + supportedSamples, + fit, caps.maxWindowRectangles(), false); + } + } + + desc.fFlags = kNone_GrSurfaceFlags; + + { + sk_sp<GrTexture> tex; + if (SkBackingFit::kApprox == fit) { + tex = provider->createApproxTexture(desc, 0); + } else { + tex = provider->createTexture(desc, budgeted); + } + + sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(provider, + desc, + fit, + budgeted)); + REPORTER_ASSERT(reporter, SkToBool(tex) == SkToBool(proxy)); + if (proxy) { + // This forces the proxy to compute and cache its pre-instantiation + // size guess. Later, when it is actually instantiated, it checks + // that the instantiated size is <= to the pre-computation. + // If the proxy never computed its pre-instantiation size then the + // check is skipped. + proxy->gpuMemorySize(); + + check_surface(reporter, proxy.get(), origin, + widthHeight, widthHeight, config, + kInvalidResourceID, budgeted); + check_texture(reporter, provider, proxy->asTextureProxy(), + fit, false); + } + } + + attempt++; + } + } + } + } + } + } +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WrappedProxyTest, reporter, ctxInfo) { + GrResourceProvider* provider = ctxInfo.grContext()->resourceProvider(); + const GrCaps& caps = *ctxInfo.grContext()->caps(); + + static const int kWidthHeight = 100; + + for (auto origin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) { + for (auto config : { kAlpha_8_GrPixelConfig, kRGBA_8888_GrPixelConfig }) { + for (auto budgeted : { SkBudgeted::kYes, SkBudgeted::kNo }) { + for (auto numSamples: { 0, 4}) { + int supportedNumSamples = caps.getSampleCount(numSamples, config); + + bool renderable = caps.isConfigRenderable(config, numSamples > 0); + + GrSurfaceDesc desc; + desc.fOrigin = origin; + desc.fWidth = kWidthHeight; + desc.fHeight = kWidthHeight; + desc.fConfig = config; + desc.fSampleCnt = supportedNumSamples; + + // External on-screen render target. + if (renderable && kOpenGL_GrBackend == ctxInfo.backend()) { + GrGLFramebufferInfo fboInfo; + fboInfo.fFBOID = 0; + GrBackendRenderTarget backendRT(kWidthHeight, kWidthHeight, numSamples, 8, + config, fboInfo); + + sk_sp<GrRenderTarget> defaultFBO( + provider->wrapBackendRenderTarget(backendRT, origin)); + + sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeWrapped(defaultFBO)); + check_surface(reporter, sProxy.get(), origin, + kWidthHeight, kWidthHeight, config, + defaultFBO->uniqueID(), SkBudgeted::kNo); + check_rendertarget(reporter, caps, provider, sProxy->asRenderTargetProxy(), + supportedNumSamples, SkBackingFit::kExact, 0, true); + } + + sk_sp<GrTexture> tex; + + // Internal offscreen render target. + if (renderable) { + desc.fFlags = kRenderTarget_GrSurfaceFlag; + tex = provider->createTexture(desc, budgeted); + sk_sp<GrRenderTarget> rt(sk_ref_sp(tex->asRenderTarget())); + + sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeWrapped(rt)); + check_surface(reporter, sProxy.get(), origin, + kWidthHeight, kWidthHeight, config, + rt->uniqueID(), budgeted); + check_rendertarget(reporter, caps, provider, sProxy->asRenderTargetProxy(), + supportedNumSamples, SkBackingFit::kExact, + caps.maxWindowRectangles(), true); + } + + if (!tex) { + SkASSERT(kNone_GrSurfaceFlags == desc.fFlags ); + desc.fSampleCnt = 0; + tex = provider->createTexture(desc, budgeted); + } + + sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeWrapped(tex)); + check_surface(reporter, sProxy.get(), origin, + kWidthHeight, kWidthHeight, config, tex->uniqueID(), budgeted); + check_texture(reporter, provider, sProxy->asTextureProxy(), + SkBackingFit::kExact, true); + } + } + } + } +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ZeroSizedProxyTest, reporter, ctxInfo) { + GrResourceProvider* provider = ctxInfo.grContext()->resourceProvider(); + + for (auto flags : { kRenderTarget_GrSurfaceFlag, kNone_GrSurfaceFlags }) { + for (auto fit : { SkBackingFit::kExact, SkBackingFit::kApprox }) { + for (int width : { 0, 100 }) { + for (int height : { 0, 100}) { + if (width && height) { + continue; // not zero-sized + } + + GrSurfaceDesc desc; + desc.fFlags = flags; + desc.fOrigin = kBottomLeft_GrSurfaceOrigin; + desc.fWidth = width; + desc.fHeight = height; + desc.fConfig = kRGBA_8888_GrPixelConfig; + desc.fSampleCnt = 0; + + sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(provider, + desc, + fit, + SkBudgeted::kNo)); + REPORTER_ASSERT(reporter, !proxy); + } + } + } + } +} + +#endif
diff --git a/src/third_party/skia/tests/QuickRejectTest.cpp b/src/third_party/skia/tests/QuickRejectTest.cpp index 8f4357e..d7d330f 100644 --- a/src/third_party/skia/tests/QuickRejectTest.cpp +++ b/src/third_party/skia/tests/QuickRejectTest.cpp
@@ -5,8 +5,12 @@ * found in the LICENSE file. */ +#include "SkArenaAlloc.h" +#include "SkBitmap.h" #include "SkCanvas.h" +#include "SkColorSpaceXformer.h" #include "SkDrawLooper.h" +#include "SkLightingImageFilter.h" #include "SkTypes.h" #include "Test.h" @@ -16,25 +20,29 @@ class TestLooper : public SkDrawLooper { public: - virtual SkDrawLooper::Context* createContext(SkCanvas*, void* storage) const SK_OVERRIDE { - return SkNEW_PLACEMENT(storage, TestDrawLooperContext); + SkDrawLooper::Context* makeContext(SkCanvas*, SkArenaAlloc* alloc) const override { + return alloc->make<TestDrawLooperContext>(); } - virtual size_t contextSize() const SK_OVERRIDE { return sizeof(TestDrawLooperContext); } + sk_sp<SkDrawLooper> onMakeColorSpace(SkColorSpaceXformer*) const override { + return nullptr; + } #ifndef SK_IGNORE_TO_STRING - virtual void toString(SkString* str) const SK_OVERRIDE { + void toString(SkString* str) const override { str->append("TestLooper:"); } #endif + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(TestLooper) + private: class TestDrawLooperContext : public SkDrawLooper::Context { public: TestDrawLooperContext() : fOnce(true) {} - virtual ~TestDrawLooperContext() {} + ~TestDrawLooperContext() override {} - virtual bool next(SkCanvas* canvas, SkPaint*) SK_OVERRIDE { + bool next(SkCanvas* canvas, SkPaint*) override { if (fOnce) { fOnce = false; canvas->translate(SkIntToScalar(10), 0); @@ -42,13 +50,14 @@ } return false; } + private: bool fOnce; }; - - SK_DECLARE_UNFLATTENABLE_OBJECT() }; +sk_sp<SkFlattenable> TestLooper::CreateProc(SkReadBuffer&) { return sk_make_sp<TestLooper>(); } + static void test_drawBitmap(skiatest::Reporter* reporter) { SkBitmap src; src.allocN32Pixels(10, 10); @@ -80,11 +89,89 @@ // to the left. The test is to ensure that canvas' quickReject machinary // allows us through, even though sans-looper we would look like we should // be clipped out. - paint.setLooper(new TestLooper)->unref(); + paint.setLooper(sk_make_sp<TestLooper>()); canvas.drawBitmap(src, SkIntToScalar(-10), 0, &paint); REPORTER_ASSERT(reporter, 0xFFFFFFFF == *dst.getAddr32(5, 5)); } +static void test_layers(skiatest::Reporter* reporter) { + SkCanvas canvas(100, 100); + + SkRect r = SkRect::MakeWH(10, 10); + REPORTER_ASSERT(reporter, false == canvas.quickReject(r)); + + r.offset(300, 300); + REPORTER_ASSERT(reporter, true == canvas.quickReject(r)); + + // Test that saveLayer updates quickReject + SkRect bounds = SkRect::MakeLTRB(50, 50, 70, 70); + canvas.saveLayer(&bounds, nullptr); + REPORTER_ASSERT(reporter, true == canvas.quickReject(SkRect::MakeWH(10, 10))); + REPORTER_ASSERT(reporter, false == canvas.quickReject(SkRect::MakeWH(60, 60))); +} + +static void test_quick_reject(skiatest::Reporter* reporter) { + SkCanvas canvas(100, 100); + SkRect r0 = SkRect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f); + SkRect r1 = SkRect::MakeLTRB(-50.0f, 110.0f, 50.0f, 120.0f); + SkRect r2 = SkRect::MakeLTRB(110.0f, -50.0f, 120.0f, 50.0f); + SkRect r3 = SkRect::MakeLTRB(-120.0f, -50.0f, 120.0f, 50.0f); + SkRect r4 = SkRect::MakeLTRB(-50.0f, -120.0f, 50.0f, 120.0f); + SkRect r5 = SkRect::MakeLTRB(-120.0f, -120.0f, 120.0f, 120.0f); + SkRect r6 = SkRect::MakeLTRB(-120.0f, -120.0f, -110.0f, -110.0f); + SkRect r7 = SkRect::MakeLTRB(SK_ScalarNaN, -50.0f, 50.0f, 50.0f); + SkRect r8 = SkRect::MakeLTRB(-50.0f, SK_ScalarNaN, 50.0f, 50.0f); + SkRect r9 = SkRect::MakeLTRB(-50.0f, -50.0f, SK_ScalarNaN, 50.0f); + SkRect r10 = SkRect::MakeLTRB(-50.0f, -50.0f, 50.0f, SK_ScalarNaN); + REPORTER_ASSERT(reporter, false == canvas.quickReject(r0)); + REPORTER_ASSERT(reporter, true == canvas.quickReject(r1)); + REPORTER_ASSERT(reporter, true == canvas.quickReject(r2)); + REPORTER_ASSERT(reporter, false == canvas.quickReject(r3)); + REPORTER_ASSERT(reporter, false == canvas.quickReject(r4)); + REPORTER_ASSERT(reporter, false == canvas.quickReject(r5)); + REPORTER_ASSERT(reporter, true == canvas.quickReject(r6)); + REPORTER_ASSERT(reporter, true == canvas.quickReject(r7)); + REPORTER_ASSERT(reporter, true == canvas.quickReject(r8)); + REPORTER_ASSERT(reporter, true == canvas.quickReject(r9)); + REPORTER_ASSERT(reporter, true == canvas.quickReject(r10)); + + SkMatrix m = SkMatrix::MakeScale(2.0f); + m.setTranslateX(10.0f); + m.setTranslateY(10.0f); + canvas.setMatrix(m); + SkRect r11 = SkRect::MakeLTRB(5.0f, 5.0f, 100.0f, 100.0f); + SkRect r12 = SkRect::MakeLTRB(5.0f, 50.0f, 100.0f, 100.0f); + SkRect r13 = SkRect::MakeLTRB(50.0f, 5.0f, 100.0f, 100.0f); + REPORTER_ASSERT(reporter, false == canvas.quickReject(r11)); + REPORTER_ASSERT(reporter, true == canvas.quickReject(r12)); + REPORTER_ASSERT(reporter, true == canvas.quickReject(r13)); +} + DEF_TEST(QuickReject, reporter) { test_drawBitmap(reporter); + test_layers(reporter); + test_quick_reject(reporter); +} + +// Regression test to make sure that we keep fIsScaleTranslate up to date on the canvas. +// It is possible to set a new matrix on the canvas without calling setMatrix(). This tests +// that code path. +DEF_TEST(QuickReject_MatrixState, reporter) { + SkCanvas canvas(100, 100); + + SkMatrix matrix; + matrix.setRotate(45.0f); + canvas.setMatrix(matrix); + + SkPaint paint; + sk_sp<SkImageFilter> filter = SkLightingImageFilter::MakeDistantLitDiffuse( + SkPoint3::Make(1.0f, 1.0f, 1.0f), 0xFF0000FF, 2.0f, 0.5f, nullptr); + REPORTER_ASSERT(reporter, filter); + paint.setImageFilter(filter); + SkCanvas::SaveLayerRec rec; + rec.fPaint = &paint; + canvas.saveLayer(rec); + + // quickReject() will assert if the matrix is out of sync. + canvas.quickReject(SkRect::MakeWH(100.0f, 100.0f)); }
diff --git a/src/third_party/skia/tests/RRectInPathTest.cpp b/src/third_party/skia/tests/RRectInPathTest.cpp new file mode 100644 index 0000000..4aa535e --- /dev/null +++ b/src/third_party/skia/tests/RRectInPathTest.cpp
@@ -0,0 +1,472 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkMatrix.h" +#include "SkPath.h" +//#include "SkPathRef.h" +#include "SkRRect.h" +#include "Test.h" + +static SkRRect path_contains_rrect(skiatest::Reporter* reporter, const SkPath& path, + SkPath::Direction* dir, unsigned* start) { + SkRRect out; + REPORTER_ASSERT(reporter, path.isRRect(&out, dir, start)); + SkPath recreatedPath; + recreatedPath.addRRect(out, *dir, *start); + REPORTER_ASSERT(reporter, path == recreatedPath); + // Test that rotations/mirrors of the rrect path are still rrect paths and the returned + // parameters for the transformed paths are correct. + static const SkMatrix kMatrices[] = { + SkMatrix::MakeScale(1, 1), + SkMatrix::MakeScale(-1, 1), + SkMatrix::MakeScale(1, -1), + SkMatrix::MakeScale(-1, -1), + }; + for (auto& m : kMatrices) { + SkPath xformed; + path.transform(m, &xformed); + SkRRect xrr = SkRRect::MakeRect(SkRect::MakeEmpty()); + SkPath::Direction xd = SkPath::kCCW_Direction; + unsigned xs = ~0U; + REPORTER_ASSERT(reporter, xformed.isRRect(&xrr, &xd, &xs)); + recreatedPath.reset(); + recreatedPath.addRRect(xrr, xd, xs); + REPORTER_ASSERT(reporter, recreatedPath == xformed); + } + return out; +} + +static SkRRect inner_path_contains_rrect(skiatest::Reporter* reporter, const SkRRect& in, + SkPath::Direction dir, unsigned start) { + switch (in.getType()) { + case SkRRect::kEmpty_Type: + case SkRRect::kRect_Type: + case SkRRect::kOval_Type: + return in; + default: + break; + } + SkPath path; + path.addRRect(in, dir, start); + SkPath::Direction outDir; + unsigned outStart; + SkRRect rrect = path_contains_rrect(reporter, path, &outDir, &outStart); + REPORTER_ASSERT(reporter, outDir == dir && outStart == start); + return rrect; +} + +static void path_contains_rrect_check(skiatest::Reporter* reporter, const SkRRect& in, + SkPath::Direction dir, unsigned start) { + SkRRect out = inner_path_contains_rrect(reporter, in, dir, start); + if (in != out) { + SkDebugf(""); + } + REPORTER_ASSERT(reporter, in == out); +} + +static void path_contains_rrect_nocheck(skiatest::Reporter* reporter, const SkRRect& in, + SkPath::Direction dir, unsigned start) { + SkRRect out = inner_path_contains_rrect(reporter, in, dir, start); + if (in == out) { + SkDebugf(""); + } +} + +static void path_contains_rrect_check(skiatest::Reporter* reporter, const SkRect& r, + SkVector v[4], SkPath::Direction dir, unsigned start) { + SkRRect rrect; + rrect.setRectRadii(r, v); + path_contains_rrect_check(reporter, rrect, dir, start); +} + +class ForceIsRRect_Private { +public: + ForceIsRRect_Private(SkPath* path, SkPath::Direction dir, unsigned start) { + path->fPathRef->setIsRRect(true, dir == SkPath::kCCW_Direction, start); + } +}; + +static void force_path_contains_rrect(skiatest::Reporter* reporter, SkPath& path, + SkPath::Direction dir, unsigned start) { + ForceIsRRect_Private force_rrect(&path, dir, start); + SkPath::Direction outDir; + unsigned outStart; + path_contains_rrect(reporter, path, &outDir, &outStart); + REPORTER_ASSERT(reporter, outDir == dir && outStart == start); +} + +static void test_undetected_paths(skiatest::Reporter* reporter) { + // We use a dummy path to get the exact conic weight used by SkPath for a circular arc. This + // allows our local, hand-crafted, artisanal round rect paths below to exactly match the + // factory made corporate paths produced by SkPath. + SkPath dummyPath; + dummyPath.addCircle(0, 0, 10); + SkPath::RawIter iter(dummyPath); + SkPoint dummyPts[4]; + SkPath::Verb v = iter.next(dummyPts); + REPORTER_ASSERT(reporter, SkPath::kMove_Verb == v); + v = iter.next(dummyPts); + REPORTER_ASSERT(reporter, SkPath::kConic_Verb == v); + const SkScalar weight = iter.conicWeight(); + + SkPath path; + path.moveTo(0, 62.5f); + path.lineTo(0, 3.5f); + path.conicTo(0, 0, 3.5f, 0, weight); + path.lineTo(196.5f, 0); + path.conicTo(200, 0, 200, 3.5f, weight); + path.lineTo(200, 62.5f); + path.conicTo(200, 66, 196.5f, 66, weight); + path.lineTo(3.5f, 66); + path.conicTo(0, 66, 0, 62.5, weight); + path.close(); + force_path_contains_rrect(reporter, path, SkPath::kCW_Direction, 6); + + path.reset(); + path.moveTo(0, 81.5f); + path.lineTo(0, 3.5f); + path.conicTo(0, 0, 3.5f, 0, weight); + path.lineTo(149.5, 0); + path.conicTo(153, 0, 153, 3.5f, weight); + path.lineTo(153, 81.5f); + path.conicTo(153, 85, 149.5f, 85, weight); + path.lineTo(3.5f, 85); + path.conicTo(0, 85, 0, 81.5f, weight); + path.close(); + force_path_contains_rrect(reporter, path, SkPath::kCW_Direction, 6); + + path.reset(); + path.moveTo(14, 1189); + path.lineTo(14, 21); + path.conicTo(14, 14, 21, 14, weight); + path.lineTo(1363, 14); + path.conicTo(1370, 14, 1370, 21, weight); + path.lineTo(1370, 1189); + path.conicTo(1370, 1196, 1363, 1196, weight); + path.lineTo(21, 1196); + path.conicTo(14, 1196, 14, 1189, weight); + path.close(); + force_path_contains_rrect(reporter, path, SkPath::kCW_Direction, 6); + + path.reset(); + path.moveTo(14, 1743); + path.lineTo(14, 21); + path.conicTo(14, 14, 21, 14, weight); + path.lineTo(1363, 14); + path.conicTo(1370, 14, 1370, 21, weight); + path.lineTo(1370, 1743); + path.conicTo(1370, 1750, 1363, 1750, weight); + path.lineTo(21, 1750); + path.conicTo(14, 1750, 14, 1743, weight); + path.close(); + force_path_contains_rrect(reporter, path, SkPath::kCW_Direction, 6); +} + +static const SkScalar kWidth = 100.0f; +static const SkScalar kHeight = 100.0f; + +static void test_tricky_radii(skiatest::Reporter* reporter) { + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + { + // crbug.com/458522 + SkRRect rr; + const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 }; + const SkScalar rad = 12814; + const SkVector vec[] = { { rad, rad }, { 0, rad }, { rad, rad }, { 0, rad } }; + rr.setRectRadii(bounds, vec); + path_contains_rrect_check(reporter, rr, dir, start); + } + + { + // crbug.com//463920 + SkRect r = SkRect::MakeLTRB(0, 0, 1009, 33554432.0); + SkVector radii[4] = { + { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554432.0 }, { 110.0f, 5.0f } + }; + SkRRect rr; + rr.setRectRadii(r, radii); + path_contains_rrect_nocheck(reporter, rr, dir, start); + } + } + } +} + +static void test_empty_crbug_458524(skiatest::Reporter* reporter) { + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + SkRRect rr; + const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 }; + const SkScalar rad = 40; + rr.setRectXY(bounds, rad, rad); + path_contains_rrect_check(reporter, rr, dir, start); + + SkRRect other; + SkMatrix matrix; + matrix.setScale(0, 1); + rr.transform(matrix, &other); + path_contains_rrect_check(reporter, rr, dir, start); + } + } +} + +static void test_inset(skiatest::Reporter* reporter) { + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + SkRRect rr, rr2; + SkRect r = { 0, 0, 100, 100 }; + + rr.setRect(r); + rr.inset(-20, -20, &rr2); + path_contains_rrect_check(reporter, rr, dir, start); + + rr.inset(20, 20, &rr2); + path_contains_rrect_check(reporter, rr, dir, start); + + rr.inset(r.width()/2, r.height()/2, &rr2); + path_contains_rrect_check(reporter, rr, dir, start); + + rr.setRectXY(r, 20, 20); + rr.inset(19, 19, &rr2); + path_contains_rrect_check(reporter, rr, dir, start); + rr.inset(20, 20, &rr2); + path_contains_rrect_check(reporter, rr, dir, start); + } + } +} + + +static void test_9patch_rrect(skiatest::Reporter* reporter, + const SkRect& rect, + SkScalar l, SkScalar t, SkScalar r, SkScalar b, + bool checkRadii) { + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + SkRRect rr; + rr.setNinePatch(rect, l, t, r, b); + if (checkRadii) { + path_contains_rrect_check(reporter, rr, dir, start); + } else { + path_contains_rrect_nocheck(reporter, rr, dir, start); + } + + SkRRect rr2; // construct the same RR using the most general set function + SkVector radii[4] = { { l, t }, { r, t }, { r, b }, { l, b } }; + rr2.setRectRadii(rect, radii); + if (checkRadii) { + path_contains_rrect_check(reporter, rr, dir, start); + } else { + path_contains_rrect_nocheck(reporter, rr, dir, start); + } + } + } +} + +// Test out the basic API entry points +static void test_round_rect_basic(skiatest::Reporter* reporter) { + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + //---- + SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); + + SkRRect rr1; + rr1.setRect(rect); + path_contains_rrect_check(reporter, rr1, dir, start); + + SkRRect rr1_2; // construct the same RR using the most general set function + SkVector rr1_2_radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }; + rr1_2.setRectRadii(rect, rr1_2_radii); + path_contains_rrect_check(reporter, rr1_2, dir, start); + SkRRect rr1_3; // construct the same RR using the nine patch set function + rr1_3.setNinePatch(rect, 0, 0, 0, 0); + path_contains_rrect_check(reporter, rr1_2, dir, start); + + //---- + SkPoint halfPoint = { SkScalarHalf(kWidth), SkScalarHalf(kHeight) }; + SkRRect rr2; + rr2.setOval(rect); + path_contains_rrect_check(reporter, rr2, dir, start); + + SkRRect rr2_2; // construct the same RR using the most general set function + SkVector rr2_2_radii[4] = { { halfPoint.fX, halfPoint.fY }, + { halfPoint.fX, halfPoint.fY }, + { halfPoint.fX, halfPoint.fY }, + { halfPoint.fX, halfPoint.fY } }; + rr2_2.setRectRadii(rect, rr2_2_radii); + path_contains_rrect_check(reporter, rr2_2, dir, start); + SkRRect rr2_3; // construct the same RR using the nine patch set function + rr2_3.setNinePatch(rect, halfPoint.fX, halfPoint.fY, halfPoint.fX, halfPoint.fY); + path_contains_rrect_check(reporter, rr2_3, dir, start); + + //---- + SkPoint p = { 5, 5 }; + SkRRect rr3; + rr3.setRectXY(rect, p.fX, p.fY); + path_contains_rrect_check(reporter, rr3, dir, start); + + SkRRect rr3_2; // construct the same RR using the most general set function + SkVector rr3_2_radii[4] = { { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 } }; + rr3_2.setRectRadii(rect, rr3_2_radii); + path_contains_rrect_check(reporter, rr3_2, dir, start); + SkRRect rr3_3; // construct the same RR using the nine patch set function + rr3_3.setNinePatch(rect, 5, 5, 5, 5); + path_contains_rrect_check(reporter, rr3_3, dir, start); + + //---- + test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true); + + { + // Test out the rrect from skia:3466 + SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f, + 0.806214333f); + + test_9patch_rrect(reporter, + rect2, + 0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f, + false); + } + + //---- + SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } }; + + SkRRect rr5; + rr5.setRectRadii(rect, radii2); + path_contains_rrect_check(reporter, rr5, dir, start); + } + } +} + +// Test out the cases when the RR degenerates to a rect +static void test_round_rect_rects(skiatest::Reporter* reporter) { + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + //---- + SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); + SkRRect rr1; + rr1.setRectXY(rect, 0, 0); + + path_contains_rrect_check(reporter, rr1, dir, start); + + //---- + SkPoint radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }; + + SkRRect rr2; + rr2.setRectRadii(rect, radii); + + path_contains_rrect_check(reporter, rr2, dir, start); + + //---- + SkPoint radii2[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } }; + + SkRRect rr3; + rr3.setRectRadii(rect, radii2); + path_contains_rrect_check(reporter, rr3, dir, start); + } + } +} + +// Test out the cases when the RR degenerates to an oval +static void test_round_rect_ovals(skiatest::Reporter* reporter) { + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + //---- + SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); + SkRRect rr1; + rr1.setRectXY(rect, SkScalarHalf(kWidth), SkScalarHalf(kHeight)); + + path_contains_rrect_check(reporter, rr1, dir, start); + } + } +} + +// Test out the non-degenerate RR cases +static void test_round_rect_general(skiatest::Reporter* reporter) { + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + //---- + SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); + SkRRect rr1; + rr1.setRectXY(rect, 20, 20); + + path_contains_rrect_check(reporter, rr1, dir, start); + + //---- + SkPoint radii[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } }; + + SkRRect rr2; + rr2.setRectRadii(rect, radii); + + path_contains_rrect_check(reporter, rr2, dir, start); + } + } +} + +static void test_round_rect_iffy_parameters(skiatest::Reporter* reporter) { + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); + SkPoint radii[4] = { { 50, 100 }, { 100, 50 }, { 50, 100 }, { 100, 50 } }; + SkRRect rr1; + rr1.setRectRadii(rect, radii); + path_contains_rrect_nocheck(reporter, rr1, dir, start); + } + } +} + +static void set_radii(SkVector radii[4], int index, float rad) { + sk_bzero(radii, sizeof(SkVector) * 4); + radii[index].set(rad, rad); +} + +static void test_skbug_3239(skiatest::Reporter* reporter) { + const float min = SkBits2Float(0xcb7f16c8); /* -16717512.000000 */ + const float max = SkBits2Float(0x4b7f1c1d); /* 16718877.000000 */ + const float big = SkBits2Float(0x4b7f1bd7); /* 16718807.000000 */ + + const float rad = 33436320; + + const SkRect rectx = SkRect::MakeLTRB(min, min, max, big); + const SkRect recty = SkRect::MakeLTRB(min, min, big, max); + + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + SkVector radii[4]; + for (int i = 0; i < 4; ++i) { + set_radii(radii, i, rad); + path_contains_rrect_check(reporter, rectx, radii, dir, start); + path_contains_rrect_check(reporter, recty, radii, dir, start); + } + } + } +} + +static void test_mix(skiatest::Reporter* reporter) { + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + // Test out mixed degenerate and non-degenerate geometry with Conics + const SkVector radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 100, 100 } }; + SkRect r = SkRect::MakeWH(100, 100); + SkRRect rr; + rr.setRectRadii(r, radii); + path_contains_rrect_check(reporter, rr, dir, start); + } + } +} + +DEF_TEST(RoundRectInPath, reporter) { + test_tricky_radii(reporter); + test_empty_crbug_458524(reporter); + test_inset(reporter); + test_round_rect_basic(reporter); + test_round_rect_rects(reporter); + test_round_rect_ovals(reporter); + test_round_rect_general(reporter); + test_undetected_paths(reporter); + test_round_rect_iffy_parameters(reporter); + test_skbug_3239(reporter); + test_mix(reporter); +}
diff --git a/src/third_party/skia/tests/RTConfRegistryTest.cpp b/src/third_party/skia/tests/RTConfRegistryTest.cpp deleted file mode 100644 index 59fb3e9..0000000 --- a/src/third_party/skia/tests/RTConfRegistryTest.cpp +++ /dev/null
@@ -1,32 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkRTConf.h" -#include "Test.h" - -// Friended proxy for SkRTConfRegistry::parse() -template <typename T> -bool test_rt_conf_parse(SkRTConfRegistry* reg, const char* key, T* value) { - return reg->parse(key, value); -} - -static void portable_setenv(const char* key, const char* value) { -#ifdef SK_BUILD_FOR_WIN32 - _putenv_s(key, value); -#else - setenv(key, value, 1); -#endif -} - -DEF_TEST(SkRTConfRegistry, reporter) { - SkRTConfRegistry reg; - - portable_setenv("skia_nonexistent_item", "132"); - int result = 0; - test_rt_conf_parse(®, "nonexistent.item", &result); - REPORTER_ASSERT(reporter, result == 132); -}
diff --git a/src/third_party/skia/tests/RTreeTest.cpp b/src/third_party/skia/tests/RTreeTest.cpp index 40af5fe..b39af67 100644 --- a/src/third_party/skia/tests/RTreeTest.cpp +++ b/src/third_party/skia/tests/RTreeTest.cpp
@@ -7,21 +7,12 @@ #include "SkRTree.h" #include "SkRandom.h" -#include "SkTSort.h" #include "Test.h" -static const size_t MIN_CHILDREN = 6; -static const size_t MAX_CHILDREN = 11; - static const int NUM_RECTS = 200; static const size_t NUM_ITERATIONS = 100; static const size_t NUM_QUERIES = 50; -struct DataRect { - SkRect rect; - void* data; -}; - static SkRect random_rect(SkRandom& rand) { SkRect rect = {0,0,0,0}; while (rect.isEmpty()) { @@ -34,121 +25,67 @@ return rect; } -static void random_data_rects(SkRandom& rand, DataRect out[], int n) { - for (int i = 0; i < n; ++i) { - out[i].rect = random_rect(rand); - out[i].data = reinterpret_cast<void*>(i); - } -} - -static bool verify_query(SkRect query, DataRect rects[], - SkTDArray<void*>& found) { - // TODO(mtklein): no need to do this after everything's SkRects - query.roundOut(); - - SkTDArray<void*> expected; - +static bool verify_query(SkRect query, SkRect rects[], SkTDArray<int>& found) { + SkTDArray<int> expected; // manually intersect with every rectangle for (int i = 0; i < NUM_RECTS; ++i) { - if (SkRect::Intersects(query, rects[i].rect)) { - expected.push(rects[i].data); + if (SkRect::Intersects(query, rects[i])) { + expected.push(i); } } if (expected.count() != found.count()) { return false; } - if (0 == expected.count()) { return true; } - - // Just cast to long since sorting by the value of the void*'s was being problematic... - SkTQSort(reinterpret_cast<long*>(expected.begin()), - reinterpret_cast<long*>(expected.end() - 1)); - SkTQSort(reinterpret_cast<long*>(found.begin()), - reinterpret_cast<long*>(found.end() - 1)); return found == expected; } -static void run_queries(skiatest::Reporter* reporter, SkRandom& rand, DataRect rects[], - SkRTree& tree) { +static void run_queries(skiatest::Reporter* reporter, SkRandom& rand, SkRect rects[], + const SkRTree& tree) { for (size_t i = 0; i < NUM_QUERIES; ++i) { - SkTDArray<void*> hits; + SkTDArray<int> hits; SkRect query = random_rect(rand); tree.search(query, &hits); REPORTER_ASSERT(reporter, verify_query(query, rects, hits)); } } -static void rtree_test_main(SkRTree* rtree, skiatest::Reporter* reporter) { - DataRect rects[NUM_RECTS]; - SkRandom rand; - REPORTER_ASSERT(reporter, rtree); - +DEF_TEST(RTree, reporter) { int expectedDepthMin = -1; - int expectedDepthMax = -1; - int tmp = NUM_RECTS; while (tmp > 0) { - tmp -= static_cast<int>(pow(static_cast<double>(MAX_CHILDREN), - static_cast<double>(expectedDepthMin + 1))); + tmp -= static_cast<int>(pow(static_cast<double>(SkRTree::kMaxChildren), + static_cast<double>(expectedDepthMin + 1))); ++expectedDepthMin; } + int expectedDepthMax = -1; tmp = NUM_RECTS; while (tmp > 0) { - tmp -= static_cast<int>(pow(static_cast<double>(MIN_CHILDREN), - static_cast<double>(expectedDepthMax + 1))); + tmp -= static_cast<int>(pow(static_cast<double>(SkRTree::kMinChildren), + static_cast<double>(expectedDepthMax + 1))); ++expectedDepthMax; } + SkRandom rand; + SkAutoTMalloc<SkRect> rects(NUM_RECTS); for (size_t i = 0; i < NUM_ITERATIONS; ++i) { - random_data_rects(rand, rects, NUM_RECTS); + SkRTree rtree; + REPORTER_ASSERT(reporter, 0 == rtree.getCount()); - // First try bulk-loaded inserts - for (int i = 0; i < NUM_RECTS; ++i) { - rtree->insert(rects[i].data, rects[i].rect, true); + for (int j = 0; j < NUM_RECTS; j++) { + rects[j] = random_rect(rand); } - rtree->flushDeferredInserts(); - run_queries(reporter, rand, rects, *rtree); - REPORTER_ASSERT(reporter, NUM_RECTS == rtree->getCount()); - REPORTER_ASSERT(reporter, expectedDepthMin <= rtree->getDepth() && - expectedDepthMax >= rtree->getDepth()); - rtree->clear(); - REPORTER_ASSERT(reporter, 0 == rtree->getCount()); - // Then try immediate inserts - for (int i = 0; i < NUM_RECTS; ++i) { - rtree->insert(rects[i].data, rects[i].rect); - } - run_queries(reporter, rand, rects, *rtree); - REPORTER_ASSERT(reporter, NUM_RECTS == rtree->getCount()); - REPORTER_ASSERT(reporter, expectedDepthMin <= rtree->getDepth() && - expectedDepthMax >= rtree->getDepth()); - rtree->clear(); - REPORTER_ASSERT(reporter, 0 == rtree->getCount()); + rtree.insert(rects.get(), NUM_RECTS); + SkASSERT(rects); // SkRTree doesn't take ownership of rects. - // And for good measure try immediate inserts, but in reversed order - for (int i = NUM_RECTS - 1; i >= 0; --i) { - rtree->insert(rects[i].data, rects[i].rect); - } - run_queries(reporter, rand, rects, *rtree); - REPORTER_ASSERT(reporter, NUM_RECTS == rtree->getCount()); - REPORTER_ASSERT(reporter, expectedDepthMin <= rtree->getDepth() && - expectedDepthMax >= rtree->getDepth()); - rtree->clear(); - REPORTER_ASSERT(reporter, 0 == rtree->getCount()); + run_queries(reporter, rand, rects, rtree); + REPORTER_ASSERT(reporter, NUM_RECTS == rtree.getCount()); + REPORTER_ASSERT(reporter, expectedDepthMin <= rtree.getDepth() && + expectedDepthMax >= rtree.getDepth()); } } - -DEF_TEST(RTree, reporter) { - SkRTree* rtree = SkRTree::Create(MIN_CHILDREN, MAX_CHILDREN); - SkAutoUnref au(rtree); - rtree_test_main(rtree, reporter); - - // Rtree that orders input rectangles on deferred insert. - SkRTree* unsortedRtree = SkRTree::Create(MIN_CHILDREN, MAX_CHILDREN, 1, false); - SkAutoUnref auo(unsortedRtree); - rtree_test_main(unsortedRtree, reporter); -}
diff --git a/src/third_party/skia/tests/RandomTest.cpp b/src/third_party/skia/tests/RandomTest.cpp index 8d93d87..448f073 100644 --- a/src/third_party/skia/tests/RandomTest.cpp +++ b/src/third_party/skia/tests/RandomTest.cpp
@@ -119,7 +119,7 @@ // now make some strings and track them for (int i = 0; i < kN; ++i) { - value <<= 1; + value = SkLeftShift(value, 1); unsigned int rnd = rand.nextU(); value |= ((rnd >> shift) & 0x1);
diff --git a/src/third_party/skia/tests/ReadPixelsTest.cpp b/src/third_party/skia/tests/ReadPixelsTest.cpp index 77aac1f..f426e5f 100644 --- a/src/third_party/skia/tests/ReadPixelsTest.cpp +++ b/src/third_party/skia/tests/ReadPixelsTest.cpp
@@ -5,25 +5,30 @@ * found in the LICENSE file. */ -#include "SkBitmapDevice.h" #include "SkCanvas.h" #include "SkColorPriv.h" +#include "SkColorSpace_Base.h" +#include "SkHalf.h" +#include "SkImageInfoPriv.h" #include "SkMathPriv.h" -#include "SkRegion.h" #include "SkSurface.h" #include "Test.h" #if SK_SUPPORT_GPU +#include "GrContext.h" #include "GrContextFactory.h" -#include "SkGpuDevice.h" +#include "GrContextPriv.h" +#include "SkGr.h" #endif +#include <initializer_list> + static const int DEV_W = 100, DEV_H = 100; static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H); static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1, DEV_H * SK_Scalar1); -static SkPMColor getCanvasColor(int x, int y) { +static SkPMColor get_src_color(int x, int y) { SkASSERT(x >= 0 && x < DEV_W); SkASSERT(y >= 0 && y < DEV_H); @@ -52,7 +57,7 @@ return SkPremultiplyARGBInline(a, r, g, b); } -static SkPMColor getBitmapColor(int x, int y, int w) { +static SkPMColor get_dst_bmp_init_color(int x, int y, int w) { int n = y * w + x; U8CPU b = n & 0xff; @@ -61,8 +66,8 @@ return SkPackARGB32(0xff, r, g , b); } -static SkPMColor convertToPMColor(SkColorType ct, SkAlphaType at, const uint32_t* addr, - bool* doUnpremul) { +static SkPMColor convert_to_pmcolor(SkColorType ct, SkAlphaType at, const uint32_t* addr, + bool* doUnpremul) { *doUnpremul = (kUnpremul_SkAlphaType == at); const uint8_t* c = reinterpret_cast<const uint8_t*>(addr); @@ -93,7 +98,7 @@ return SkPackARGB32(a, r, g, b); } -static void fillCanvas(SkCanvas* canvas) { +static SkBitmap make_src_bitmap() { static SkBitmap bmp; if (bmp.isNull()) { bmp.allocN32Pixels(DEV_W, DEV_H); @@ -101,34 +106,42 @@ for (int y = 0; y < DEV_H; ++y) { for (int x = 0; x < DEV_W; ++x) { SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bmp.rowBytes() + x * bmp.bytesPerPixel()); - *pixel = getCanvasColor(x, y); + *pixel = get_src_color(x, y); } } } + return bmp; +} + +static void fill_src_canvas(SkCanvas* canvas) { canvas->save(); canvas->setMatrix(SkMatrix::I()); - canvas->clipRect(DEV_RECT_S, SkRegion::kReplace_Op); + canvas->clipRect(DEV_RECT_S, kReplace_SkClipOp); SkPaint paint; - paint.setXfermodeMode(SkXfermode::kSrc_Mode); - canvas->drawBitmap(bmp, 0, 0, &paint); + paint.setBlendMode(SkBlendMode::kSrc); + canvas->drawBitmap(make_src_bitmap(), 0, 0, &paint); canvas->restore(); } -static void fillBitmap(SkBitmap* bitmap) { - SkASSERT(bitmap->lockPixelsAreWritable()); - SkAutoLockPixels alp(*bitmap); +static void fill_dst_bmp_with_init_data(SkBitmap* bitmap) { int w = bitmap->width(); int h = bitmap->height(); intptr_t pixels = reinterpret_cast<intptr_t>(bitmap->getPixels()); for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { - SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bitmap->rowBytes() + x * bitmap->bytesPerPixel()); - *pixel = getBitmapColor(x, y, w); + SkPMColor initColor = get_dst_bmp_init_color(x, y, w); + if (kAlpha_8_SkColorType == bitmap->colorType()) { + uint8_t* alpha = reinterpret_cast<uint8_t*>(pixels + y * bitmap->rowBytes() + x); + *alpha = SkGetPackedA32(initColor); + } else { + SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bitmap->rowBytes() + x * bitmap->bytesPerPixel()); + *pixel = initColor; + } } } } -static bool checkPixel(SkPMColor a, SkPMColor b, bool didPremulConversion) { +static bool check_read_pixel(SkPMColor a, SkPMColor b, bool didPremulConversion) { if (!didPremulConversion) { return a == b; } @@ -151,18 +164,17 @@ // checks the bitmap contains correct pixels after the readPixels // if the bitmap was prefilled with pixels it checks that these weren't // overwritten in the area outside the readPixels. -static bool checkRead(skiatest::Reporter* reporter, - const SkBitmap& bitmap, - int x, int y, - bool checkCanvasPixels, - bool checkBitmapPixels) { - SkASSERT(4 == bitmap.bytesPerPixel()); +static bool check_read(skiatest::Reporter* reporter, + const SkBitmap& bitmap, + int x, int y, + bool checkCanvasPixels, + bool checkBitmapPixels, + SkColorType ct, + SkAlphaType at) { + SkASSERT(ct == bitmap.colorType() && at == bitmap.alphaType()); SkASSERT(!bitmap.isNull()); SkASSERT(checkCanvasPixels || checkBitmapPixels); - const SkColorType ct = bitmap.colorType(); - const SkAlphaType at = bitmap.alphaType(); - int bw = bitmap.width(); int bh = bitmap.height(); @@ -171,7 +183,34 @@ if (!clippedSrcRect.intersect(srcRect)) { clippedSrcRect.setEmpty(); } - SkAutoLockPixels alp(bitmap); + if (kAlpha_8_SkColorType == ct) { + for (int by = 0; by < bh; ++by) { + for (int bx = 0; bx < bw; ++bx) { + int devx = bx + srcRect.fLeft; + int devy = by + srcRect.fTop; + const uint8_t* alpha = bitmap.getAddr8(bx, by); + + if (clippedSrcRect.contains(devx, devy)) { + if (checkCanvasPixels) { + uint8_t canvasAlpha = SkGetPackedA32(get_src_color(devx, devy)); + if (canvasAlpha != *alpha) { + ERRORF(reporter, "Expected readback alpha (%d, %d) value 0x%02x, got 0x%02x. ", + bx, by, canvasAlpha, *alpha); + return false; + } + } + } else if (checkBitmapPixels) { + uint32_t origDstAlpha = SkGetPackedA32(get_dst_bmp_init_color(bx, by, bw)); + if (origDstAlpha != *alpha) { + ERRORF(reporter, "Expected clipped out area of readback to be unchanged. " + "Expected 0x%02x, got 0x%02x", origDstAlpha, *alpha); + return false; + } + } + } + } + return true; + } for (int by = 0; by < bh; ++by) { for (int bx = 0; bx < bw; ++bx) { int devx = bx + srcRect.fLeft; @@ -181,18 +220,20 @@ if (clippedSrcRect.contains(devx, devy)) { if (checkCanvasPixels) { - SkPMColor canvasPixel = getCanvasColor(devx, devy); + SkPMColor canvasPixel = get_src_color(devx, devy); bool didPremul; - SkPMColor pmPixel = convertToPMColor(ct, at, pixel, &didPremul); - bool check; - REPORTER_ASSERT(reporter, check = checkPixel(pmPixel, canvasPixel, didPremul)); - if (!check) { + SkPMColor pmPixel = convert_to_pmcolor(ct, at, pixel, &didPremul); + if (!check_read_pixel(pmPixel, canvasPixel, didPremul)) { + ERRORF(reporter, "Expected readback pixel (%d, %d) value 0x%08x, got 0x%08x. " + "Readback was unpremul: %d", bx, by, canvasPixel, pmPixel, didPremul); return false; } } } else if (checkBitmapPixels) { - REPORTER_ASSERT(reporter, getBitmapColor(bx, by, bw) == *pixel); - if (getBitmapColor(bx, by, bw) != *pixel) { + uint32_t origDstPixel = get_dst_bmp_init_color(bx, by, bw); + if (origDstPixel != *pixel) { + ERRORF(reporter, "Expected clipped out area of readback to be unchanged. " + "Expected 0x%08x, got 0x%08x", origDstPixel, *pixel); return false; } } @@ -204,11 +245,17 @@ enum BitmapInit { kFirstBitmapInit = 0, - kNoPixels_BitmapInit = kFirstBitmapInit, - kTight_BitmapInit, + kTight_BitmapInit = kFirstBitmapInit, kRowBytes_BitmapInit, + kRowBytesOdd_BitmapInit, - kBitmapInitCnt + kLastAligned_BitmapInit = kRowBytes_BitmapInit, + +#if 0 // THIS CAUSES ERRORS ON WINDOWS AND SOME ANDROID DEVICES + kLast_BitmapInit = kRowBytesOdd_BitmapInit +#else + kLast_BitmapInit = kLastAligned_BitmapInit +#endif }; static BitmapInit nextBMI(BitmapInit bmi) { @@ -220,171 +267,345 @@ SkAlphaType at) { SkImageInfo info = SkImageInfo::Make(rect.width(), rect.height(), ct, at); size_t rowBytes = 0; - bool alloc = true; switch (init) { - case kNoPixels_BitmapInit: - alloc = false; case kTight_BitmapInit: break; case kRowBytes_BitmapInit: - rowBytes = (info.width() + 16) * sizeof(SkPMColor); + rowBytes = SkAlign4((info.width() + 16) * info.bytesPerPixel()); + break; + case kRowBytesOdd_BitmapInit: + rowBytes = SkAlign4(info.width() * info.bytesPerPixel()) + 3; break; default: SkASSERT(0); break; } + bitmap->allocPixels(info, rowBytes); +} - if (alloc) { - bitmap->allocPixels(info); - } else { - bitmap->setInfo(info, rowBytes); +static const struct { + SkColorType fColorType; + SkAlphaType fAlphaType; +} gReadPixelsConfigs[] = { + { kRGBA_8888_SkColorType, kPremul_SkAlphaType }, + { kRGBA_8888_SkColorType, kUnpremul_SkAlphaType }, + { kBGRA_8888_SkColorType, kPremul_SkAlphaType }, + { kBGRA_8888_SkColorType, kUnpremul_SkAlphaType }, + { kAlpha_8_SkColorType, kPremul_SkAlphaType }, +}; +const SkIRect gReadPixelsTestRects[] = { + // entire thing + DEV_RECT, + // larger on all sides + SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10), + // fully contained + SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4), + // outside top left + SkIRect::MakeLTRB(-10, -10, -1, -1), + // touching top left corner + SkIRect::MakeLTRB(-10, -10, 0, 0), + // overlapping top left corner + SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4), + // overlapping top left and top right corners + SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H / 4), + // touching entire top edge + SkIRect::MakeLTRB(-10, -10, DEV_W + 10, 0), + // overlapping top right corner + SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H / 4), + // contained in x, overlapping top edge + SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W / 4, DEV_H / 4), + // outside top right corner + SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1), + // touching top right corner + SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0), + // overlapping top left and bottom left corners + SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10), + // touching entire left edge + SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10), + // overlapping bottom left corner + SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10), + // contained in y, overlapping left edge + SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4), + // outside bottom left corner + SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10), + // touching bottom left corner + SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10), + // overlapping bottom left and bottom right corners + SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10), + // touching entire left edge + SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10), + // overlapping bottom right corner + SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10), + // overlapping top right and bottom right corners + SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10), +}; + +static void test_readpixels(skiatest::Reporter* reporter, const sk_sp<SkSurface>& surface, + BitmapInit lastBitmapInit) { + SkCanvas* canvas = surface->getCanvas(); + fill_src_canvas(canvas); + for (size_t rect = 0; rect < SK_ARRAY_COUNT(gReadPixelsTestRects); ++rect) { + const SkIRect& srcRect = gReadPixelsTestRects[rect]; + for (BitmapInit bmi = kFirstBitmapInit; bmi <= lastBitmapInit; bmi = nextBMI(bmi)) { + for (size_t c = 0; c < SK_ARRAY_COUNT(gReadPixelsConfigs); ++c) { + SkBitmap bmp; + init_bitmap(&bmp, srcRect, bmi, + gReadPixelsConfigs[c].fColorType, gReadPixelsConfigs[c].fAlphaType); + + // if the bitmap has pixels allocated before the readPixels, + // note that and fill them with pattern + bool startsWithPixels = !bmp.isNull(); + if (startsWithPixels) { + fill_dst_bmp_with_init_data(&bmp); + } + uint32_t idBefore = surface->generationID(); + bool success = canvas->readPixels(bmp, srcRect.fLeft, srcRect.fTop); + uint32_t idAfter = surface->generationID(); + + // we expect to succeed when the read isn't fully clipped + // out. + bool expectSuccess = SkIRect::Intersects(srcRect, DEV_RECT); + // determine whether we expected the read to succeed. + REPORTER_ASSERT(reporter, success == expectSuccess); + // read pixels should never change the gen id + REPORTER_ASSERT(reporter, idBefore == idAfter); + + if (success || startsWithPixels) { + check_read(reporter, bmp, srcRect.fLeft, srcRect.fTop, + success, startsWithPixels, + gReadPixelsConfigs[c].fColorType, gReadPixelsConfigs[c].fAlphaType); + } else { + // if we had no pixels beforehand and the readPixels + // failed then our bitmap should still not have pixels + REPORTER_ASSERT(reporter, bmp.isNull()); + } + } + } + } +} +DEF_TEST(ReadPixels, reporter) { + const SkImageInfo info = SkImageInfo::MakeN32Premul(DEV_W, DEV_H); + auto surface(SkSurface::MakeRaster(info)); + // SW readback fails a premul check when reading back to an unaligned rowbytes. + test_readpixels(reporter, surface, kLastAligned_BitmapInit); +} +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReadPixels_Gpu, reporter, ctxInfo) { + if (ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_D3D9_ES2_ContextType || + ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_GL_ES2_ContextType || + ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_D3D11_ES2_ContextType) { + // skbug.com/6742 ReadPixels_Texture & _Gpu don't work with ANGLE ES2 configs + return; + } + + const SkImageInfo ii = SkImageInfo::MakeN32Premul(DEV_W, DEV_H); + for (auto& origin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) { + sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctxInfo.grContext(), SkBudgeted::kNo, + ii, 0, origin, nullptr)); + test_readpixels(reporter, surface, kLast_BitmapInit); + } +} +#endif + +#if SK_SUPPORT_GPU +static void test_readpixels_texture(skiatest::Reporter* reporter, + sk_sp<GrSurfaceContext> sContext) { + for (size_t rect = 0; rect < SK_ARRAY_COUNT(gReadPixelsTestRects); ++rect) { + const SkIRect& srcRect = gReadPixelsTestRects[rect]; + for (BitmapInit bmi = kFirstBitmapInit; bmi <= kLast_BitmapInit; bmi = nextBMI(bmi)) { + for (size_t c = 0; c < SK_ARRAY_COUNT(gReadPixelsConfigs); ++c) { + SkBitmap bmp; + init_bitmap(&bmp, srcRect, bmi, + gReadPixelsConfigs[c].fColorType, gReadPixelsConfigs[c].fAlphaType); + + // if the bitmap has pixels allocated before the readPixels, + // note that and fill them with pattern + bool startsWithPixels = !bmp.isNull(); + // Try doing the read directly from a non-renderable texture + if (startsWithPixels) { + fill_dst_bmp_with_init_data(&bmp); + uint32_t flags = 0; + if (gReadPixelsConfigs[c].fAlphaType == kUnpremul_SkAlphaType) { + flags = GrContextPriv::kUnpremul_PixelOpsFlag; + } + bool success = sContext->readPixels(bmp.info(), bmp.getPixels(), + bmp.rowBytes(), + srcRect.fLeft, srcRect.fTop, flags); + check_read(reporter, bmp, srcRect.fLeft, srcRect.fTop, + success, true, + gReadPixelsConfigs[c].fColorType, gReadPixelsConfigs[c].fAlphaType); + } + } + } } } -DEF_GPUTEST(ReadPixels, reporter, factory) { - const SkIRect testRects[] = { - // entire thing - DEV_RECT, - // larger on all sides - SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10), - // fully contained - SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4), - // outside top left - SkIRect::MakeLTRB(-10, -10, -1, -1), - // touching top left corner - SkIRect::MakeLTRB(-10, -10, 0, 0), - // overlapping top left corner - SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4), - // overlapping top left and top right corners - SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H / 4), - // touching entire top edge - SkIRect::MakeLTRB(-10, -10, DEV_W + 10, 0), - // overlapping top right corner - SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H / 4), - // contained in x, overlapping top edge - SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W / 4, DEV_H / 4), - // outside top right corner - SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1), - // touching top right corner - SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0), - // overlapping top left and bottom left corners - SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10), - // touching entire left edge - SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10), - // overlapping bottom left corner - SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10), - // contained in y, overlapping left edge - SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4), - // outside bottom left corner - SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10), - // touching bottom left corner - SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10), - // overlapping bottom left and bottom right corners - SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10), - // touching entire left edge - SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10), - // overlapping bottom right corner - SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10), - // overlapping top right and bottom right corners - SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10), +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReadPixels_Texture, reporter, ctxInfo) { + if (ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_D3D9_ES2_ContextType || + ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_GL_ES2_ContextType || + ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_D3D11_ES2_ContextType) { + // skbug.com/6742 ReadPixels_Texture & _Gpu don't work with ANGLE ES2 configs + return; + } + + GrContext* context = ctxInfo.grContext(); + + SkBitmap bmp = make_src_bitmap(); + + // On the GPU we will also try reading back from a non-renderable texture. + for (auto origin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) { + for (auto flags : {kNone_GrSurfaceFlags, kRenderTarget_GrSurfaceFlag}) { + GrSurfaceDesc desc; + desc.fFlags = flags; + desc.fWidth = DEV_W; + desc.fHeight = DEV_H; + desc.fConfig = kSkia8888_GrPixelConfig; + desc.fOrigin = origin; + + sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), + desc, SkBudgeted::kNo, + bmp.getPixels(), + bmp.rowBytes()); + + sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext( + std::move(proxy), nullptr); + + test_readpixels_texture(reporter, std::move(sContext)); + } + } +} +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static const uint32_t kNumPixels = 5; + +// The five reference pixels are: red, green, blue, white, black. +// Five is an interesting number to test because we'll exercise a full 4-wide SIMD vector +// plus a tail pixel. +static const uint32_t rgba[kNumPixels] = { + 0xFF0000FF, 0xFF00FF00, 0xFFFF0000, 0xFFFFFFFF, 0xFF000000 +}; +static const uint32_t bgra[kNumPixels] = { + 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFFFFFFFF, 0xFF000000 +}; +static const uint16_t rgb565[kNumPixels] = { + SK_R16_MASK_IN_PLACE, SK_G16_MASK_IN_PLACE, SK_B16_MASK_IN_PLACE, 0xFFFF, 0x0 +}; + +static const uint16_t rgba4444[kNumPixels] = { 0xF00F, 0x0F0F, 0x00FF, 0xFFFF, 0x000F }; + +static const uint64_t kRed = (uint64_t) SK_Half1 << 0; +static const uint64_t kGreen = (uint64_t) SK_Half1 << 16; +static const uint64_t kBlue = (uint64_t) SK_Half1 << 32; +static const uint64_t kAlpha = (uint64_t) SK_Half1 << 48; +static const uint64_t f16[kNumPixels] = { + kAlpha | kRed, kAlpha | kGreen, kAlpha | kBlue, kAlpha | kBlue | kGreen | kRed, kAlpha +}; + +static const uint8_t alpha8[kNumPixels] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const uint8_t gray8[kNumPixels] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + +static const void* five_reference_pixels(SkColorType colorType) { + switch (colorType) { + case kUnknown_SkColorType: + return nullptr; + case kAlpha_8_SkColorType: + return alpha8; + case kRGB_565_SkColorType: + return rgb565; + case kARGB_4444_SkColorType: + return rgba4444; + case kRGBA_8888_SkColorType: + return rgba; + case kBGRA_8888_SkColorType: + return bgra; + case kGray_8_SkColorType: + return gray8; + case kRGBA_F16_SkColorType: + return f16; + default: + return nullptr; // remove me when kIndex_8 is removed from the enum + } + + SkASSERT(false); + return nullptr; +} + +static void test_conversion(skiatest::Reporter* r, const SkImageInfo& dstInfo, + const SkImageInfo& srcInfo) { + if (!SkImageInfoIsValidRenderingCS(srcInfo)) { + return; + } + + const void* srcPixels = five_reference_pixels(srcInfo.colorType()); + SkPixmap srcPixmap(srcInfo, srcPixels, srcInfo.minRowBytes()); + sk_sp<SkImage> src = SkImage::MakeFromRaster(srcPixmap, nullptr, nullptr); + REPORTER_ASSERT(r, src); + + // Enough space for 5 pixels when color type is F16, more than enough space in other cases. + uint64_t dstPixels[kNumPixels]; + SkPixmap dstPixmap(dstInfo, dstPixels, dstInfo.minRowBytes()); + bool success = src->readPixels(dstPixmap, 0, 0); + REPORTER_ASSERT(r, success == SkImageInfoValidConversion(dstInfo, srcInfo)); + + if (success) { + if (kGray_8_SkColorType == srcInfo.colorType() && + kGray_8_SkColorType != dstInfo.colorType()) + { + // This conversion is legal, but we won't get the "reference" pixels since we cannot + // represent colors in kGray8. + return; + } + + REPORTER_ASSERT(r, 0 == memcmp(dstPixels, five_reference_pixels(dstInfo.colorType()), + kNumPixels * SkColorTypeBytesPerPixel(dstInfo.colorType()))); + + } +} + +DEF_TEST(ReadPixels_ValidConversion, reporter) { + const SkColorType kColorTypes[] = { + kUnknown_SkColorType, + kAlpha_8_SkColorType, + kRGB_565_SkColorType, + kARGB_4444_SkColorType, + kRGBA_8888_SkColorType, + kBGRA_8888_SkColorType, + kGray_8_SkColorType, + kRGBA_F16_SkColorType, }; - for (int dtype = 0; dtype < 3; ++dtype) { - int glCtxTypeCnt = 1; -#if SK_SUPPORT_GPU - if (0 != dtype) { - glCtxTypeCnt = GrContextFactory::kGLContextTypeCnt; - } -#endif - const SkImageInfo info = SkImageInfo::MakeN32Premul(DEV_W, DEV_H); - for (int glCtxType = 0; glCtxType < glCtxTypeCnt; ++glCtxType) { - SkAutoTUnref<SkSurface> surface; - if (0 == dtype) { - surface.reset(SkSurface::NewRaster(info)); - } else { -#if SK_SUPPORT_GPU - GrContextFactory::GLContextType type = - static_cast<GrContextFactory::GLContextType>(glCtxType); - if (!GrContextFactory::IsRenderingGLContext(type)) { - continue; - } - GrContext* context = factory->get(type); - if (NULL == context) { - continue; - } - GrTextureDesc desc; - desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; - desc.fWidth = DEV_W; - desc.fHeight = DEV_H; - desc.fConfig = kSkia8888_GrPixelConfig; - desc.fOrigin = 1 == dtype ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin; - GrAutoScratchTexture ast(context, desc, GrContext::kExact_ScratchTexMatch); - SkAutoTUnref<GrTexture> tex(ast.detach()); - surface.reset(SkSurface::NewRenderTargetDirect(tex->asRenderTarget())); -#else - continue; -#endif - } - SkCanvas& canvas = *surface->getCanvas(); - fillCanvas(&canvas); + const SkAlphaType kAlphaTypes[] = { + kUnknown_SkAlphaType, + kOpaque_SkAlphaType, + kPremul_SkAlphaType, + kUnpremul_SkAlphaType, + }; - static const struct { - SkColorType fColorType; - SkAlphaType fAlphaType; - } gReadConfigs[] = { - { kRGBA_8888_SkColorType, kPremul_SkAlphaType }, - { kRGBA_8888_SkColorType, kUnpremul_SkAlphaType }, - { kBGRA_8888_SkColorType, kPremul_SkAlphaType }, - { kBGRA_8888_SkColorType, kUnpremul_SkAlphaType }, - }; - for (size_t rect = 0; rect < SK_ARRAY_COUNT(testRects); ++rect) { - const SkIRect& srcRect = testRects[rect]; - for (BitmapInit bmi = kFirstBitmapInit; bmi < kBitmapInitCnt; bmi = nextBMI(bmi)) { - for (size_t c = 0; c < SK_ARRAY_COUNT(gReadConfigs); ++c) { - SkBitmap bmp; - init_bitmap(&bmp, srcRect, bmi, - gReadConfigs[c].fColorType, gReadConfigs[c].fAlphaType); + const sk_sp<SkColorSpace> kColorSpaces[] = { + nullptr, + SkColorSpace::MakeSRGB(), + }; - // if the bitmap has pixels allocated before the readPixels, - // note that and fill them with pattern - bool startsWithPixels = !bmp.isNull(); - if (startsWithPixels) { - fillBitmap(&bmp); + for (SkColorType dstCT : kColorTypes) { + for (SkAlphaType dstAT: kAlphaTypes) { + for (sk_sp<SkColorSpace> dstCS : kColorSpaces) { + for (SkColorType srcCT : kColorTypes) { + for (SkAlphaType srcAT: kAlphaTypes) { + for (sk_sp<SkColorSpace> srcCS : kColorSpaces) { + if (kRGBA_F16_SkColorType == dstCT && dstCS) { + dstCS = as_CSB(dstCS)->makeLinearGamma(); + } + + if (kRGBA_F16_SkColorType == srcCT && srcCS) { + srcCS = as_CSB(srcCS)->makeLinearGamma(); + } + + test_conversion(reporter, + SkImageInfo::Make(kNumPixels, 1, dstCT, dstAT, dstCS), + SkImageInfo::Make(kNumPixels, 1, srcCT, srcAT, srcCS)); } - uint32_t idBefore = surface->generationID(); - bool success = canvas.readPixels(&bmp, srcRect.fLeft, srcRect.fTop); - uint32_t idAfter = surface->generationID(); - - // we expect to succeed when the read isn't fully clipped - // out. - bool expectSuccess = SkIRect::Intersects(srcRect, DEV_RECT); - // determine whether we expected the read to succeed. - REPORTER_ASSERT(reporter, success == expectSuccess); - // read pixels should never change the gen id - REPORTER_ASSERT(reporter, idBefore == idAfter); - - if (success || startsWithPixels) { - checkRead(reporter, bmp, srcRect.fLeft, srcRect.fTop, - success, startsWithPixels); - } else { - // if we had no pixels beforehand and the readPixels - // failed then our bitmap should still not have pixels - REPORTER_ASSERT(reporter, bmp.isNull()); - } - } - // check the old webkit version of readPixels that clips the - // bitmap size - SkBitmap wkbmp; - bool success = canvas.readPixels(srcRect, &wkbmp); - SkIRect clippedRect = DEV_RECT; - if (clippedRect.intersect(srcRect)) { - REPORTER_ASSERT(reporter, success); - REPORTER_ASSERT(reporter, kN32_SkColorType == wkbmp.colorType()); - REPORTER_ASSERT(reporter, kPremul_SkAlphaType == wkbmp.alphaType()); - checkRead(reporter, wkbmp, clippedRect.fLeft, - clippedRect.fTop, true, false); - } else { - REPORTER_ASSERT(reporter, !success); } } }
diff --git a/src/third_party/skia/tests/ReadWriteAlphaTest.cpp b/src/third_party/skia/tests/ReadWriteAlphaTest.cpp index 385a17e..344400c 100644 --- a/src/third_party/skia/tests/ReadWriteAlphaTest.cpp +++ b/src/third_party/skia/tests/ReadWriteAlphaTest.cpp
@@ -5,108 +5,197 @@ * found in the LICENSE file. */ -// This test is specific to the GPU backend. -#if SK_SUPPORT_GPU && !defined(SK_BUILD_FOR_ANDROID) - -#include "GrContextFactory.h" -#include "SkGpuDevice.h" #include "Test.h" -static const int X_SIZE = 12; -static const int Y_SIZE = 12; +// This test is specific to the GPU backend. +#if SK_SUPPORT_GPU -DEF_GPUTEST(ReadWriteAlpha, reporter, factory) { - for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) { - GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type); - if (!GrContextFactory::IsRenderingGLContext(glType)) { - continue; +#include "GrContext.h" +#include "GrContextPriv.h" +#include "GrResourceProvider.h" +#include "GrSurfaceContext.h" +#include "GrSurfaceProxy.h" +#include "GrTextureProxy.h" +#include "SkCanvas.h" +#include "SkSurface.h" + +// This was made indivisible by 4 to ensure we test setting GL_PACK_ALIGNMENT properly. +static const int X_SIZE = 13; +static const int Y_SIZE = 13; + +static void validate_alpha_data(skiatest::Reporter* reporter, int w, int h, const uint8_t* actual, + size_t actualRowBytes, const uint8_t* expected, SkString extraMsg) { + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + uint8_t a = actual[y * actualRowBytes + x]; + uint8_t e = expected[y * w + x]; + if (e != a) { + ERRORF(reporter, + "Failed alpha readback. Expected: 0x%02x, Got: 0x%02x at (%d,%d), %s", + e, a, x, y, extraMsg.c_str()); + return; + } } - GrContext* context = factory->get(glType); - if (NULL == context) { - continue; - } + } +} - unsigned char textureData[X_SIZE][Y_SIZE]; +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReadWriteAlpha, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + unsigned char alphaData[X_SIZE * Y_SIZE]; - memset(textureData, 0, X_SIZE * Y_SIZE); + static const int kClearValue = 0x2; - GrTextureDesc desc; - - // let Skia know we will be using this texture as a render target - desc.fFlags = kRenderTarget_GrTextureFlagBit; - // it is a single channel texture - desc.fConfig = kAlpha_8_GrPixelConfig; + bool match; + static const size_t kRowBytes[] = {0, X_SIZE, X_SIZE + 1, 2 * X_SIZE - 1}; + { + GrSurfaceDesc desc; + desc.fFlags = kNone_GrSurfaceFlags; + desc.fConfig = kAlpha_8_GrPixelConfig; // it is a single channel texture desc.fWidth = X_SIZE; desc.fHeight = Y_SIZE; // We are initializing the texture with zeros here - GrTexture* texture = context->createUncachedTexture(desc, textureData, 0); - if (!texture) { + memset(alphaData, 0, X_SIZE * Y_SIZE); + + sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(context->resourceProvider(), + desc, + SkBudgeted::kNo, + alphaData, 0)); + if (!proxy) { + ERRORF(reporter, "Could not create alpha texture."); return; } + sk_sp<GrSurfaceContext> sContext(context->contextPriv().makeWrappedSurfaceContext( + std::move(proxy), nullptr)); - SkAutoTUnref<GrTexture> au(texture); + const SkImageInfo ii = SkImageInfo::MakeA8(X_SIZE, Y_SIZE); + sk_sp<SkSurface> surf(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, ii)); // create a distinctive texture for (int y = 0; y < Y_SIZE; ++y) { for (int x = 0; x < X_SIZE; ++x) { - textureData[x][y] = x*Y_SIZE+y; + alphaData[y * X_SIZE + x] = y*X_SIZE+x; } } - // upload the texture - texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig, - textureData, 0); + for (auto rowBytes : kRowBytes) { - unsigned char readback[X_SIZE][Y_SIZE]; + // upload the texture (do per-rowbytes iteration because we may overwrite below). + bool result = sContext->writePixels(ii, alphaData, 0, 0, 0); + REPORTER_ASSERT_MESSAGE(reporter, result, "Initial A8 writePixels failed"); - // clear readback to something non-zero so we can detect readback failures - memset(readback, 0x1, X_SIZE * Y_SIZE); + size_t nonZeroRowBytes = rowBytes ? rowBytes : X_SIZE; + std::unique_ptr<uint8_t[]> readback(new uint8_t[nonZeroRowBytes * Y_SIZE]); + // clear readback to something non-zero so we can detect readback failures + memset(readback.get(), kClearValue, nonZeroRowBytes * Y_SIZE); - // read the texture back - texture->readPixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig, - readback, 0); + // read the texture back + result = sContext->readPixels(ii, readback.get(), rowBytes, 0, 0); + REPORTER_ASSERT_MESSAGE(reporter, result, "Initial A8 readPixels failed"); - // make sure the original & read back versions match - bool match = true; + // make sure the original & read back versions match + SkString msg; + msg.printf("rb:%d A8", SkToU32(rowBytes)); + validate_alpha_data(reporter, X_SIZE, Y_SIZE, readback.get(), nonZeroRowBytes, + alphaData, msg); - for (int y = 0; y < Y_SIZE; ++y) { - for (int x = 0; x < X_SIZE; ++x) { - if (textureData[x][y] != readback[x][y]) { - match = false; + // Now try writing to a single channel surface (if we could create one). + if (surf) { + SkCanvas* canvas = surf->getCanvas(); + + SkPaint paint; + + const SkRect rect = SkRect::MakeLTRB(-10, -10, X_SIZE + 10, Y_SIZE + 10); + + paint.setColor(SK_ColorWHITE); + + canvas->drawRect(rect, paint); + + memset(readback.get(), kClearValue, nonZeroRowBytes * Y_SIZE); + result = surf->readPixels(ii, readback.get(), nonZeroRowBytes, 0, 0); + REPORTER_ASSERT_MESSAGE(reporter, result, "A8 readPixels after clear failed"); + + match = true; + for (int y = 0; y < Y_SIZE && match; ++y) { + for (int x = 0; x < X_SIZE && match; ++x) { + uint8_t rbValue = readback.get()[y * nonZeroRowBytes + x]; + if (0xFF != rbValue) { + ERRORF(reporter, + "Failed alpha readback after clear. Expected: 0xFF, Got: 0x%02x" + " at (%d,%d), rb:%d", rbValue, x, y, SkToU32(rowBytes)); + match = false; + } + } } } } + } - REPORTER_ASSERT(reporter, match); + static const GrPixelConfig kRGBAConfigs[] { + kRGBA_8888_GrPixelConfig, + kBGRA_8888_GrPixelConfig, + kSRGBA_8888_GrPixelConfig + }; - // Now try writing on the single channel texture - SkAutoTUnref<SkBaseDevice> device(SkGpuDevice::Create(texture->asRenderTarget(), - SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType))); - SkCanvas canvas(device); + for (int y = 0; y < Y_SIZE; ++y) { + for (int x = 0; x < X_SIZE; ++x) { + alphaData[y * X_SIZE + x] = y*X_SIZE+x; + } + } - SkPaint paint; + const SkImageInfo dstInfo = SkImageInfo::Make(X_SIZE, Y_SIZE, + kAlpha_8_SkColorType, + kPremul_SkAlphaType); - const SkRect rect = SkRect::MakeLTRB(-10, -10, X_SIZE + 10, Y_SIZE + 10); + // Attempt to read back just alpha from a RGBA/BGRA texture. Once with a texture-only src and + // once with a render target. + for (auto config : kRGBAConfigs) { + for (int rt = 0; rt < 2; ++rt) { + GrSurfaceDesc desc; + desc.fFlags = rt ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags; + desc.fConfig = config; + desc.fWidth = X_SIZE; + desc.fHeight = Y_SIZE; - paint.setColor(SK_ColorWHITE); - - canvas.drawRect(rect, paint); - - texture->readPixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig, - readback, 0); - - match = true; - - for (int y = 0; y < Y_SIZE; ++y) { - for (int x = 0; x < X_SIZE; ++x) { - if (0xFF != readback[x][y]) { - match = false; + uint32_t rgbaData[X_SIZE * Y_SIZE]; + // Make the alpha channel of the rgba texture come from alphaData. + for (int y = 0; y < Y_SIZE; ++y) { + for (int x = 0; x < X_SIZE; ++x) { + rgbaData[y * X_SIZE + x] = GrColorPackRGBA(6, 7, 8, alphaData[y * X_SIZE + x]); } } - } + sk_sp<GrTextureProxy> proxy = + GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kNo, + rgbaData, 0); + if (!proxy) { + // We always expect to be able to create a RGBA texture + if (!rt && kRGBA_8888_GrPixelConfig == desc.fConfig) { + ERRORF(reporter, "Failed to create RGBA texture."); + } + continue; + } - REPORTER_ASSERT(reporter, match); + sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext( + std::move(proxy), nullptr); + + for (auto rowBytes : kRowBytes) { + size_t nonZeroRowBytes = rowBytes ? rowBytes : X_SIZE; + + std::unique_ptr<uint8_t[]> readback(new uint8_t[nonZeroRowBytes * Y_SIZE]); + // Clear so we don't accidentally see values from previous iteration. + memset(readback.get(), kClearValue, nonZeroRowBytes * Y_SIZE); + + // read the texture back + bool result = sContext->readPixels(dstInfo, readback.get(), rowBytes, 0, 0); + REPORTER_ASSERT_MESSAGE(reporter, result, "8888 readPixels failed"); + + // make sure the original & read back versions match + SkString msg; + msg.printf("rt:%d, rb:%d 8888", rt, SkToU32(rowBytes)); + validate_alpha_data(reporter, X_SIZE, Y_SIZE, readback.get(), nonZeroRowBytes, + alphaData, msg); + } + } } }
diff --git a/src/third_party/skia/tests/Reader32Test.cpp b/src/third_party/skia/tests/Reader32Test.cpp index cfa08b3..301f67c 100644 --- a/src/third_party/skia/tests/Reader32Test.cpp +++ b/src/third_party/skia/tests/Reader32Test.cpp
@@ -35,8 +35,8 @@ DEF_TEST(Reader32, reporter) { SkReader32 reader; assert_empty(reporter, reader); - REPORTER_ASSERT(reporter, NULL == reader.base()); - REPORTER_ASSERT(reporter, NULL == reader.peek()); + REPORTER_ASSERT(reporter, nullptr == reader.base()); + REPORTER_ASSERT(reporter, nullptr == reader.peek()); size_t i; @@ -74,8 +74,17 @@ reader.read(buffer, sizeof(data2)); REPORTER_ASSERT(reporter, !memcmp(data2, buffer, sizeof(data2))); - reader.setMemory(NULL, 0); + reader.setMemory(nullptr, 0); assert_empty(reporter, reader); - REPORTER_ASSERT(reporter, NULL == reader.base()); - REPORTER_ASSERT(reporter, NULL == reader.peek()); + REPORTER_ASSERT(reporter, nullptr == reader.base()); + REPORTER_ASSERT(reporter, nullptr == reader.peek()); + + // need to handle read(null, 0) and not get undefined behavior from memcpy + { + char storage[100]; + reader.setMemory(storage, sizeof(storage)); + char buffer[10]; + reader.read(buffer, 0); // easy case, since we pass a ptr + reader.read(nullptr, 0); // undef case, read() can't blindly call memcpy + } }
diff --git a/src/third_party/skia/tests/RecordDrawTest.cpp b/src/third_party/skia/tests/RecordDrawTest.cpp index 33efbd8..1dbd945 100644 --- a/src/third_party/skia/tests/RecordDrawTest.cpp +++ b/src/third_party/skia/tests/RecordDrawTest.cpp
@@ -9,25 +9,53 @@ #include "RecordTestUtils.h" #include "SkDebugCanvas.h" -#include "SkDrawPictureCallback.h" #include "SkDropShadowImageFilter.h" +#include "SkImagePriv.h" #include "SkRecord.h" #include "SkRecordDraw.h" #include "SkRecordOpts.h" #include "SkRecorder.h" #include "SkRecords.h" +#include "SkSurface.h" static const int W = 1920, H = 1080; -class JustOneDraw : public SkDrawPictureCallback { +class JustOneDraw : public SkPicture::AbortCallback { public: JustOneDraw() : fCalls(0) {} - virtual bool abortDrawing() SK_OVERRIDE { return fCalls++ > 0; } + bool abort() override { return fCalls++ > 0; } private: int fCalls; }; +DEF_TEST(RecordDraw_LazySaves, r) { + // Record two commands. + SkRecord record; + SkRecorder recorder(&record, W, H); + + REPORTER_ASSERT(r, 0 == record.count()); + recorder.save(); + REPORTER_ASSERT(r, 0 == record.count()); // the save was not recorded (yet) + recorder.drawColor(SK_ColorRED); + REPORTER_ASSERT(r, 1 == record.count()); + recorder.scale(2, 2); + REPORTER_ASSERT(r, 3 == record.count()); // now we see the save + recorder.restore(); + REPORTER_ASSERT(r, 4 == record.count()); + + assert_type<SkRecords::DrawPaint>(r, record, 0); + assert_type<SkRecords::Save> (r, record, 1); + assert_type<SkRecords::Concat> (r, record, 2); + assert_type<SkRecords::Restore> (r, record, 3); + + recorder.save(); + recorder.save(); + recorder.restore(); + recorder.restore(); + REPORTER_ASSERT(r, 4 == record.count()); +} + DEF_TEST(RecordDraw_Abort, r) { // Record two commands. SkRecord record; @@ -39,28 +67,25 @@ SkRecorder canvas(&rerecord, W, H); JustOneDraw callback; - SkRecordDraw(record, &canvas, NULL/*bbh*/, &callback); + SkRecordDraw(record, &canvas, nullptr, nullptr, 0, nullptr/*bbh*/, &callback); - REPORTER_ASSERT(r, 3 == rerecord.count()); - assert_type<SkRecords::Save> (r, rerecord, 0); - assert_type<SkRecords::DrawRect>(r, rerecord, 1); - assert_type<SkRecords::Restore> (r, rerecord, 2); + REPORTER_ASSERT(r, 1 == count_instances_of_type<SkRecords::DrawRect>(rerecord)); + REPORTER_ASSERT(r, 0 == count_instances_of_type<SkRecords::ClipRect>(rerecord)); } DEF_TEST(RecordDraw_Unbalanced, r) { SkRecord record; SkRecorder recorder(&record, W, H); recorder.save(); // We won't balance this, but SkRecordDraw will for us. + recorder.scale(2, 2); SkRecord rerecord; SkRecorder canvas(&rerecord, W, H); - SkRecordDraw(record, &canvas, NULL/*bbh*/, NULL/*callback*/); + SkRecordDraw(record, &canvas, nullptr, nullptr, 0, nullptr/*bbh*/, nullptr/*callback*/); - REPORTER_ASSERT(r, 4 == rerecord.count()); - assert_type<SkRecords::Save> (r, rerecord, 0); - assert_type<SkRecords::Save> (r, rerecord, 1); - assert_type<SkRecords::Restore> (r, rerecord, 2); - assert_type<SkRecords::Restore> (r, rerecord, 3); + int save_count = count_instances_of_type<SkRecords::Save>(rerecord); + int restore_count = count_instances_of_type<SkRecords::Save>(rerecord); + REPORTER_ASSERT(r, save_count == restore_count); } DEF_TEST(RecordDraw_SetMatrixClobber, r) { @@ -78,7 +103,7 @@ translate.setTranslate(20, 20); translateCanvas.setMatrix(translate); - SkRecordDraw(scaleRecord, &translateCanvas, NULL/*bbh*/, NULL/*callback*/); + SkRecordDraw(scaleRecord, &translateCanvas, nullptr, nullptr, 0, nullptr/*bbh*/, nullptr/*callback*/); REPORTER_ASSERT(r, 4 == translateRecord.count()); assert_type<SkRecords::SetMatrix>(r, translateRecord, 0); assert_type<SkRecords::Save> (r, translateRecord, 1); @@ -97,27 +122,6 @@ REPORTER_ASSERT(r, setMatrix->matrix == expected); } -struct TestBBH : public SkBBoxHierarchy { - virtual void insert(void* data, const SkRect& bounds, bool defer) SK_OVERRIDE { - Entry e = { (uintptr_t)data, bounds }; - entries.push(e); - } - virtual int getCount() const SK_OVERRIDE { return entries.count(); } - - virtual void flushDeferredInserts() SK_OVERRIDE {} - - virtual void search(const SkRect& query, SkTDArray<void*>* results) const SK_OVERRIDE {} - virtual void clear() SK_OVERRIDE {} - virtual void rewindInserts() SK_OVERRIDE {} - virtual int getDepth() const SK_OVERRIDE { return -1; } - - struct Entry { - uintptr_t data; - SkRect bounds; - }; - SkTDArray<Entry> entries; -}; - // Like a==b, with a little slop recognizing that float equality can be weird. static bool sloppy_rect_eq(SkRect a, SkRect b) { SkRect inset(a), outset(a); @@ -126,9 +130,7 @@ return outset.contains(b) && !inset.contains(b); } -// This test is not meant to make total sense yet. It's testing the status quo -// of SkRecordFillBounds(), which itself doesn't make total sense yet. -DEF_TEST(RecordDraw_BBH, r) { +DEF_TEST(RecordDraw_BasicBounds, r) { SkRecord record; SkRecorder recorder(&record, W, H); recorder.save(); @@ -137,14 +139,11 @@ recorder.drawRect(SkRect::MakeWH(320, 240), SkPaint()); recorder.restore(); - TestBBH bbh; - SkRecordFillBounds(record, &bbh); + SkAutoTMalloc<SkRect> bounds(record.count()); + SkRecordFillBounds(SkRect::MakeWH(SkIntToScalar(W), SkIntToScalar(H)), record, bounds); - REPORTER_ASSERT(r, bbh.entries.count() == 5); - for (int i = 0; i < bbh.entries.count(); i++) { - REPORTER_ASSERT(r, bbh.entries[i].data == (uintptr_t)i); - - REPORTER_ASSERT(r, sloppy_rect_eq(SkRect::MakeWH(400, 480), bbh.entries[i].bounds)); + for (int i = 0; i < record.count(); i++) { + REPORTER_ASSERT(r, sloppy_rect_eq(SkRect::MakeWH(400, 480), bounds[i])); } } @@ -163,15 +162,14 @@ const SkPoint pos[] = { {40, 50}, {60, 70} }; recorder.drawPosText(text, bytes, pos, SkPaint()); - TestBBH bbh; - SkRecordFillBounds(record, &bbh); - REPORTER_ASSERT(r, bbh.entries.count() == 2); + SkAutoTMalloc<SkRect> bounds(record.count()); + SkRecordFillBounds(SkRect::MakeWH(SkIntToScalar(W), SkIntToScalar(H)), record, bounds); // We can make these next assertions confidently because SkRecordFillBounds // builds its bounds by overestimating font metrics in a platform-independent way. // If that changes, these tests will need to be more flexible. - REPORTER_ASSERT(r, sloppy_rect_eq(bbh.entries[0].bounds, SkRect::MakeLTRB(-86, 6, 116, 54))); - REPORTER_ASSERT(r, sloppy_rect_eq(bbh.entries[1].bounds, SkRect::MakeLTRB(-56, 26, 156, 94))); + REPORTER_ASSERT(r, sloppy_rect_eq(bounds[0], SkRect::MakeLTRB(0, 0, 140, 60))); + REPORTER_ASSERT(r, sloppy_rect_eq(bounds[1], SkRect::MakeLTRB(0, 20, 180, 100))); } // Base test to ensure start/stop range is respected @@ -191,42 +189,15 @@ SkRecord rerecord; SkRecorder canvas(&rerecord, kWidth, kHeight); - SkRecordPartialDraw(record, &canvas, r1, 1, 2, SkMatrix::I()); // replay just drawRect of r2 + SkRecordPartialDraw(record, &canvas, nullptr, 0, 1, 2, SkMatrix::I()); // replay just drawRect of r2 - REPORTER_ASSERT(r, 3 == rerecord.count()); - assert_type<SkRecords::Save> (r, rerecord, 0); - assert_type<SkRecords::DrawRect> (r, rerecord, 1); - assert_type<SkRecords::Restore> (r, rerecord, 2); - - const SkRecords::DrawRect* drawRect = assert_type<SkRecords::DrawRect>(r, rerecord, 1); + REPORTER_ASSERT(r, 1 == count_instances_of_type<SkRecords::DrawRect>(rerecord)); + int index = find_first_instances_of_type<SkRecords::DrawRect>(rerecord); + const SkRecords::DrawRect* drawRect = assert_type<SkRecords::DrawRect>(r, rerecord, index); REPORTER_ASSERT(r, drawRect->rect == r2); } -// Check that clears are converted to drawRects -DEF_TEST(RecordDraw_PartialClear, r) { - static const int kWidth = 10, kHeight = 10; - - SkRect rect = { 0, 0, kWidth, kHeight }; - - SkRecord record; - SkRecorder recorder(&record, kWidth, kHeight); - recorder.clear(SK_ColorRED); - - SkRecord rerecord; - SkRecorder canvas(&rerecord, kWidth, kHeight); - SkRecordPartialDraw(record, &canvas, rect, 0, 1, SkMatrix::I()); // replay just the clear - - REPORTER_ASSERT(r, 3 == rerecord.count()); - assert_type<SkRecords::Save> (r, rerecord, 0); - assert_type<SkRecords::DrawRect>(r, rerecord, 1); - assert_type<SkRecords::Restore> (r, rerecord, 2); - - const SkRecords::DrawRect* drawRect = assert_type<SkRecords::DrawRect>(r, rerecord, 1); - REPORTER_ASSERT(r, drawRect->rect == rect); - REPORTER_ASSERT(r, drawRect->paint.getColor() == SK_ColorRED); -} - -// A regression test for crbug.com/415468 and skbug.com/2957. +// A regression test for crbug.com/415468 and https://bug.skia.org/2957 . // // This also now serves as a regression test for crbug.com/418417. We used to adjust the // bounds for the saveLayer, clip, and restore to be greater than the bounds of the picture. @@ -238,9 +209,12 @@ // We draw a rectangle with a long drop shadow. We used to not update the clip // bounds based on SaveLayer paints, so the drop shadow could be cut off. SkPaint paint; - paint.setImageFilter(SkDropShadowImageFilter::Create(20, 0, 0, 0, SK_ColorBLACK))->unref(); + paint.setImageFilter(SkDropShadowImageFilter::Make( + 20, 0, 0, 0, SK_ColorBLACK, + SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode, + nullptr)); - recorder.saveLayer(NULL, &paint); + recorder.saveLayer(nullptr, &paint); recorder.clipRect(SkRect::MakeWH(20, 40)); recorder.drawRect(SkRect::MakeWH(20, 40), SkPaint()); recorder.restore(); @@ -250,11 +224,84 @@ // // The second bug showed up as adjusting the picture bounds (0,0,50,50) by the drop shadow too. // The saveLayer, clipRect, and restore bounds were incorrectly (0,0,70,50). - TestBBH bbh; - SkRecordFillBounds(record, &bbh); - REPORTER_ASSERT(r, bbh.entries.count() == 4); - REPORTER_ASSERT(r, sloppy_rect_eq(bbh.entries[0].bounds, SkRect::MakeLTRB(0, 0, 50, 50))); - REPORTER_ASSERT(r, sloppy_rect_eq(bbh.entries[1].bounds, SkRect::MakeLTRB(0, 0, 50, 50))); - REPORTER_ASSERT(r, sloppy_rect_eq(bbh.entries[2].bounds, SkRect::MakeLTRB(0, 0, 40, 40))); - REPORTER_ASSERT(r, sloppy_rect_eq(bbh.entries[3].bounds, SkRect::MakeLTRB(0, 0, 50, 50))); + SkAutoTMalloc<SkRect> bounds(record.count()); + SkRecordFillBounds(SkRect::MakeWH(50, 50), record, bounds); + REPORTER_ASSERT(r, sloppy_rect_eq(bounds[0], SkRect::MakeLTRB(0, 0, 50, 50))); + REPORTER_ASSERT(r, sloppy_rect_eq(bounds[1], SkRect::MakeLTRB(0, 0, 50, 50))); + REPORTER_ASSERT(r, sloppy_rect_eq(bounds[2], SkRect::MakeLTRB(0, 0, 40, 40))); + REPORTER_ASSERT(r, sloppy_rect_eq(bounds[3], SkRect::MakeLTRB(0, 0, 50, 50))); +} + +// When a saveLayer provides an explicit bound and has a complex paint (e.g., one that +// affects transparent black), that bound should serve to shrink the area of the required +// backing store. +DEF_TEST(RecordDraw_SaveLayerBoundsAffectsClipBounds, r) { + SkRecord record; + SkRecorder recorder(&record, 50, 50); + + SkPaint p; + p.setBlendMode(SkBlendMode::kSrc); + + SkRect layerBounds = SkRect::MakeLTRB(10, 10, 40, 40); + recorder.saveLayer(&layerBounds, &p); + recorder.drawRect(SkRect::MakeLTRB(20, 20, 30, 30), SkPaint()); + recorder.restore(); + + SkAutoTMalloc<SkRect> bounds(record.count()); + SkRecordFillBounds(SkRect::MakeWH(50, 50), record, bounds); + if (!SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds()) { + REPORTER_ASSERT(r, sloppy_rect_eq(bounds[0], SkRect::MakeLTRB(10, 10, 40, 40))); + REPORTER_ASSERT(r, sloppy_rect_eq(bounds[1], SkRect::MakeLTRB(20, 20, 30, 30))); + REPORTER_ASSERT(r, sloppy_rect_eq(bounds[2], SkRect::MakeLTRB(10, 10, 40, 40))); + } +} + +DEF_TEST(RecordDraw_drawImage, r){ + class SkCanvasMock : public SkCanvas { + public: + SkCanvasMock(int width, int height) : SkCanvas(width, height) { + this->resetTestValues(); + } + + void onDrawImage(const SkImage* image, SkScalar left, SkScalar top, + const SkPaint* paint) override { + fDrawImageCalled = true; + } + + void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, + const SkPaint* paint, SrcRectConstraint) override { + fDrawImageRectCalled = true; + } + + void resetTestValues() { + fDrawImageCalled = fDrawImageRectCalled = false; + } + + bool fDrawImageCalled; + bool fDrawImageRectCalled; + }; + + auto surface(SkSurface::MakeRasterN32Premul(10, 10)); + surface->getCanvas()->clear(SK_ColorGREEN); + sk_sp<SkImage> image(surface->makeImageSnapshot()); + + SkCanvasMock canvas(10, 10); + + { + SkRecord record; + SkRecorder recorder(&record, 10, 10); + recorder.drawImage(image, 0, 0); + SkRecordDraw(record, &canvas, nullptr, nullptr, 0, nullptr, 0); + } + REPORTER_ASSERT(r, canvas.fDrawImageCalled); + canvas.resetTestValues(); + + { + SkRecord record; + SkRecorder recorder(&record, 10, 10); + recorder.drawImageRect(image, SkRect::MakeWH(10, 10), nullptr); + SkRecordDraw(record, &canvas, nullptr, nullptr, 0, nullptr, 0); + } + REPORTER_ASSERT(r, canvas.fDrawImageRectCalled); + }
diff --git a/src/third_party/skia/tests/RecordOptsTest.cpp b/src/third_party/skia/tests/RecordOptsTest.cpp index c5c4471..cb80987 100644 --- a/src/third_party/skia/tests/RecordOptsTest.cpp +++ b/src/third_party/skia/tests/RecordOptsTest.cpp
@@ -8,34 +8,31 @@ #include "Test.h" #include "RecordTestUtils.h" +#include "SkBlurImageFilter.h" +#include "SkColorFilter.h" #include "SkRecord.h" #include "SkRecordOpts.h" #include "SkRecorder.h" #include "SkRecords.h" -#include "SkXfermode.h" +#include "SkPictureRecorder.h" +#include "SkPictureImageFilter.h" +#include "SkSurface.h" static const int W = 1920, H = 1080; -DEF_TEST(RecordOpts_NoopDrawSaveRestore, r) { +DEF_TEST(RecordOpts_NoopDraw, r) { SkRecord record; SkRecorder recorder(&record, W, H); - // The save and restore are pointless if there's only draw commands in the middle. - recorder.save(); - recorder.drawRect(SkRect::MakeWH(200, 200), SkPaint()); - recorder.drawRect(SkRect::MakeWH(300, 300), SkPaint()); - recorder.drawRect(SkRect::MakeWH(100, 100), SkPaint()); - recorder.restore(); + recorder.drawRect(SkRect::MakeWH(200, 200), SkPaint()); + recorder.drawRect(SkRect::MakeWH(300, 300), SkPaint()); + recorder.drawRect(SkRect::MakeWH(100, 100), SkPaint()); - record.replace<SkRecords::NoOp>(2); // NoOps should be allowed. + record.replace<SkRecords::NoOp>(1); // NoOps should be allowed. SkRecordNoopSaveRestores(&record); - assert_type<SkRecords::NoOp>(r, record, 0); - assert_type<SkRecords::DrawRect>(r, record, 1); - assert_type<SkRecords::NoOp>(r, record, 2); - assert_type<SkRecords::DrawRect>(r, record, 3); - assert_type<SkRecords::NoOp>(r, record, 4); + REPORTER_ASSERT(r, 2 == count_instances_of_type<SkRecords::DrawRect>(record)); } DEF_TEST(RecordOpts_SingleNoopSaveRestore, r) { @@ -47,7 +44,7 @@ recorder.restore(); SkRecordNoopSaveRestores(&record); - for (unsigned i = 0; i < 3; i++) { + for (int i = 0; i < 3; i++) { assert_type<SkRecords::NoOp>(r, record, i); } } @@ -70,7 +67,7 @@ recorder.restore(); SkRecordNoopSaveRestores(&record); - for (unsigned index = 0; index < 8; index++) { + for (int index = 0; index < record.count(); index++) { assert_type<SkRecords::NoOp>(r, record, index); } } @@ -81,24 +78,51 @@ // A previous bug NoOp'd away the first 3 commands. recorder.save(); - recorder.saveLayer(NULL, NULL); + recorder.saveLayer(nullptr, nullptr); recorder.restore(); recorder.restore(); SkRecordNoopSaveRestores(&record); - assert_type<SkRecords::Save> (r, record, 0); - assert_type<SkRecords::SaveLayer>(r, record, 1); - assert_type<SkRecords::Restore> (r, record, 2); - assert_type<SkRecords::Restore> (r, record, 3); + switch (record.count()) { + case 4: + assert_type<SkRecords::Save> (r, record, 0); + assert_type<SkRecords::SaveLayer>(r, record, 1); + assert_type<SkRecords::Restore> (r, record, 2); + assert_type<SkRecords::Restore> (r, record, 3); + break; + case 2: + assert_type<SkRecords::SaveLayer>(r, record, 0); + assert_type<SkRecords::Restore> (r, record, 1); + break; + case 0: + break; + default: + REPORTER_ASSERT(r, false); + } } +#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK static void assert_savelayer_restore(skiatest::Reporter* r, SkRecord* record, - unsigned i, + int i, bool shouldBeNoOped) { SkRecordNoopSaveLayerDrawRestores(record); if (shouldBeNoOped) { assert_type<SkRecords::NoOp>(r, *record, i); + assert_type<SkRecords::NoOp>(r, *record, i+1); + } else { + assert_type<SkRecords::SaveLayer>(r, *record, i); + assert_type<SkRecords::Restore>(r, *record, i+1); + } +} + +static void assert_savelayer_draw_restore(skiatest::Reporter* r, + SkRecord* record, + int i, + bool shouldBeNoOped) { + SkRecordNoopSaveLayerDrawRestores(record); + if (shouldBeNoOped) { + assert_type<SkRecords::NoOp>(r, *record, i); assert_type<SkRecords::NoOp>(r, *record, i+2); } else { assert_type<SkRecords::SaveLayer>(r, *record, i); @@ -113,54 +137,293 @@ SkRect bounds = SkRect::MakeWH(100, 200); SkRect draw = SkRect::MakeWH(50, 60); - SkPaint goodLayerPaint, badLayerPaint, worseLayerPaint; - goodLayerPaint.setColor(0x03000000); // Only alpha. - badLayerPaint.setColor( 0x03040506); // Not only alpha. - worseLayerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode); // Any effect will do. + SkPaint alphaOnlyLayerPaint, translucentLayerPaint, xfermodeLayerPaint; + alphaOnlyLayerPaint.setColor(0x03000000); // Only alpha. + translucentLayerPaint.setColor(0x03040506); // Not only alpha. + xfermodeLayerPaint.setBlendMode(SkBlendMode::kDstIn); // Any effect will do. - SkPaint goodDrawPaint, badDrawPaint; - goodDrawPaint.setColor(0xFF020202); // Opaque. - badDrawPaint.setColor( 0x0F020202); // Not opaque. + SkPaint opaqueDrawPaint, translucentDrawPaint; + opaqueDrawPaint.setColor(0xFF020202); // Opaque. + translucentDrawPaint.setColor(0x0F020202); // Not opaque. - // No change: optimization can't handle bounds. - recorder.saveLayer(&bounds, NULL); - recorder.drawRect(draw, goodDrawPaint); + // SaveLayer/Restore removed: No paint = no point. + recorder.saveLayer(nullptr, nullptr); + recorder.drawRect(draw, opaqueDrawPaint); recorder.restore(); - assert_savelayer_restore(r, &record, 0, false); + assert_savelayer_draw_restore(r, &record, 0, true); - // SaveLayer/Restore removed: no bounds + no paint = no point. - recorder.saveLayer(NULL, NULL); - recorder.drawRect(draw, goodDrawPaint); + // Bounds don't matter. + recorder.saveLayer(&bounds, nullptr); + recorder.drawRect(draw, opaqueDrawPaint); recorder.restore(); - assert_savelayer_restore(r, &record, 3, true); + assert_savelayer_draw_restore(r, &record, 3, true); // TODO(mtklein): test case with null draw paint // No change: layer paint isn't alpha-only. - recorder.saveLayer(NULL, &badLayerPaint); - recorder.drawRect(draw, goodDrawPaint); + recorder.saveLayer(nullptr, &translucentLayerPaint); + recorder.drawRect(draw, opaqueDrawPaint); recorder.restore(); - assert_savelayer_restore(r, &record, 6, false); + assert_savelayer_draw_restore(r, &record, 6, false); // No change: layer paint has an effect. - recorder.saveLayer(NULL, &worseLayerPaint); - recorder.drawRect(draw, goodDrawPaint); + recorder.saveLayer(nullptr, &xfermodeLayerPaint); + recorder.drawRect(draw, opaqueDrawPaint); recorder.restore(); - assert_savelayer_restore(r, &record, 9, false); - - // No change: draw paint isn't opaque. - recorder.saveLayer(NULL, &goodLayerPaint); - recorder.drawRect(draw, badDrawPaint); - recorder.restore(); - assert_savelayer_restore(r, &record, 12, false); + assert_savelayer_draw_restore(r, &record, 9, false); // SaveLayer/Restore removed: we can fold in the alpha! - recorder.saveLayer(NULL, &goodLayerPaint); - recorder.drawRect(draw, goodDrawPaint); + recorder.saveLayer(nullptr, &alphaOnlyLayerPaint); + recorder.drawRect(draw, translucentDrawPaint); recorder.restore(); - assert_savelayer_restore(r, &record, 15, true); + assert_savelayer_draw_restore(r, &record, 12, true); + + // SaveLayer/Restore removed: we can fold in the alpha! + recorder.saveLayer(nullptr, &alphaOnlyLayerPaint); + recorder.drawRect(draw, opaqueDrawPaint); + recorder.restore(); + assert_savelayer_draw_restore(r, &record, 15, true); const SkRecords::DrawRect* drawRect = assert_type<SkRecords::DrawRect>(r, record, 16); - REPORTER_ASSERT(r, drawRect != NULL); + REPORTER_ASSERT(r, drawRect != nullptr); REPORTER_ASSERT(r, drawRect->paint.getColor() == 0x03020202); + + // saveLayer w/ backdrop should NOT go away + sk_sp<SkImageFilter> filter(SkBlurImageFilter::Make(3, 3, nullptr)); + recorder.saveLayer({ nullptr, nullptr, filter.get(), nullptr, nullptr, 0}); + recorder.drawRect(draw, opaqueDrawPaint); + recorder.restore(); + assert_savelayer_draw_restore(r, &record, 18, false); + + // saveLayer w/ clip mask should also NOT go away + { + sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(10, 10)); + recorder.saveLayer({ nullptr, nullptr, nullptr, surface->makeImageSnapshot().get(), + nullptr, 0}); + recorder.drawRect(draw, opaqueDrawPaint); + recorder.restore(); + assert_savelayer_draw_restore(r, &record, 21, false); + } } +#endif + +static void assert_merge_svg_opacity_and_filter_layers(skiatest::Reporter* r, + SkRecord* record, + int i, + bool shouldBeNoOped) { + SkRecordMergeSvgOpacityAndFilterLayers(record); + if (shouldBeNoOped) { + assert_type<SkRecords::NoOp>(r, *record, i); + assert_type<SkRecords::NoOp>(r, *record, i + 6); + } else { + assert_type<SkRecords::SaveLayer>(r, *record, i); + assert_type<SkRecords::Restore>(r, *record, i + 6); + } +} + +DEF_TEST(RecordOpts_MergeSvgOpacityAndFilterLayers, r) { + SkRecord record; + SkRecorder recorder(&record, W, H); + + SkRect bounds = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(200)); + SkRect clip = SkRect::MakeWH(SkIntToScalar(50), SkIntToScalar(60)); + + SkPaint alphaOnlyLayerPaint; + alphaOnlyLayerPaint.setColor(0x03000000); // Only alpha. + SkPaint translucentLayerPaint; + translucentLayerPaint.setColor(0x03040506); // Not only alpha. + SkPaint xfermodePaint; + xfermodePaint.setBlendMode(SkBlendMode::kDstIn); + SkPaint colorFilterPaint; + colorFilterPaint.setColorFilter( + SkColorFilter::MakeModeFilter(SK_ColorLTGRAY, SkBlendMode::kSrcIn)); + + SkPaint opaqueFilterLayerPaint; + opaqueFilterLayerPaint.setColor(0xFF020202); // Opaque. + SkPaint translucentFilterLayerPaint; + translucentFilterLayerPaint.setColor(0x0F020202); // Not opaque. + sk_sp<SkPicture> shape; + { + SkPictureRecorder recorder; + SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(100), SkIntToScalar(100)); + SkPaint shapePaint; + shapePaint.setColor(SK_ColorWHITE); + canvas->drawRect(SkRect::MakeWH(SkIntToScalar(50), SkIntToScalar(50)), shapePaint); + shape = recorder.finishRecordingAsPicture(); + } + translucentFilterLayerPaint.setImageFilter(SkPictureImageFilter::Make(shape)); + + int index = 0; + + { + sk_sp<SkImageFilter> filter(SkBlurImageFilter::Make(3, 3, nullptr)); + // first (null) should be optimized, 2nd should not + SkImageFilter* filters[] = { nullptr, filter.get() }; + + // Any combination of these should cause the pattern to be optimized. + SkRect* firstBounds[] = { nullptr, &bounds }; + SkPaint* firstPaints[] = { nullptr, &alphaOnlyLayerPaint }; + SkRect* secondBounds[] = { nullptr, &bounds }; + SkPaint* secondPaints[] = { &opaqueFilterLayerPaint, &translucentFilterLayerPaint }; + + for (auto outerF : filters) { + bool outerNoOped = !outerF; + for (auto innerF : filters) { + for (size_t i = 0; i < SK_ARRAY_COUNT(firstBounds); ++ i) { + for (size_t j = 0; j < SK_ARRAY_COUNT(firstPaints); ++j) { + for (size_t k = 0; k < SK_ARRAY_COUNT(secondBounds); ++k) { + for (size_t m = 0; m < SK_ARRAY_COUNT(secondPaints); ++m) { + bool innerNoOped = !secondBounds[k] && !secondPaints[m] && !innerF; + + recorder.saveLayer({firstBounds[i], firstPaints[j], outerF, + nullptr, nullptr, 0}); + recorder.save(); + recorder.clipRect(clip); + recorder.saveLayer({secondBounds[k], secondPaints[m], innerF, + nullptr, nullptr, 0}); + recorder.restore(); + recorder.restore(); + recorder.restore(); + assert_merge_svg_opacity_and_filter_layers(r, &record, index, + outerNoOped); + #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK + assert_savelayer_restore(r, &record, index + 3, innerNoOped); + #endif + index += 7; + } + } + } + } + } + } + } + + // These should cause the pattern to stay unoptimized: + struct { + SkPaint* firstPaint; + SkPaint* secondPaint; + } noChangeTests[] = { + // No change: nullptr filter layer paint not implemented. + { &alphaOnlyLayerPaint, nullptr }, + // No change: layer paint is not alpha-only. + { &translucentLayerPaint, &opaqueFilterLayerPaint }, + // No change: layer paint has an xfereffect. + { &xfermodePaint, &opaqueFilterLayerPaint }, + // No change: filter layer paint has an xfereffect. + { &alphaOnlyLayerPaint, &xfermodePaint }, + // No change: layer paint has a color filter. + { &colorFilterPaint, &opaqueFilterLayerPaint }, + // No change: filter layer paint has a color filter (until the optimization accounts for + // constant color draws that can filter the color). + { &alphaOnlyLayerPaint, &colorFilterPaint } + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(noChangeTests); ++i) { + recorder.saveLayer(nullptr, noChangeTests[i].firstPaint); + recorder.save(); + recorder.clipRect(clip); + recorder.saveLayer(nullptr, noChangeTests[i].secondPaint); + recorder.restore(); + recorder.restore(); + recorder.restore(); + assert_merge_svg_opacity_and_filter_layers(r, &record, index, false); + index += 7; + } + + // Test the folded alpha value. + recorder.saveLayer(nullptr, &alphaOnlyLayerPaint); + recorder.save(); + recorder.clipRect(clip); + recorder.saveLayer(nullptr, &opaqueFilterLayerPaint); + recorder.restore(); + recorder.restore(); + recorder.restore(); + assert_merge_svg_opacity_and_filter_layers(r, &record, index, true); + + const SkRecords::SaveLayer* saveLayer = assert_type<SkRecords::SaveLayer>(r, record, index + 3); + REPORTER_ASSERT(r, saveLayer != nullptr); + REPORTER_ASSERT(r, saveLayer->paint->getColor() == 0x03020202); + + index += 7; + + // Test that currently we do not fold alphas for patterns without the clip. This is just not + // implemented. + recorder.saveLayer(nullptr, &alphaOnlyLayerPaint); + recorder.saveLayer(nullptr, &opaqueFilterLayerPaint); + recorder.restore(); + recorder.restore(); + SkRecordMergeSvgOpacityAndFilterLayers(&record); + assert_type<SkRecords::SaveLayer>(r, record, index); + assert_type<SkRecords::SaveLayer>(r, record, index + 1); + assert_type<SkRecords::Restore>(r, record, index + 2); + assert_type<SkRecords::Restore>(r, record, index + 3); + index += 4; +} + +static void do_draw(SkCanvas* canvas, SkColor color, bool doLayer) { + canvas->drawColor(SK_ColorWHITE); + + SkPaint p; + p.setColor(color); + + if (doLayer) { + canvas->saveLayer(nullptr, nullptr); + p.setBlendMode(SkBlendMode::kSrc); + canvas->drawPaint(p); + canvas->restore(); + } else { + canvas->drawPaint(p); + } +} + +static bool is_equal(SkSurface* a, SkSurface* b) { + const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1); + SkPMColor ca, cb; + a->readPixels(info, &ca, sizeof(SkPMColor), 0, 0); + b->readPixels(info, &cb, sizeof(SkPMColor), 0, 0); + return ca == cb; +} + +// Test drawing w/ and w/o a simple layer (no bounds or paint), so see that drawing ops +// that *should* draw the same in fact do. +// +// Perform this test twice : once directly, and once via a picture +// +static void do_savelayer_srcmode(skiatest::Reporter* r, SkColor color) { + for (int doPicture = 0; doPicture <= 1; ++doPicture) { + sk_sp<SkSurface> surf0 = SkSurface::MakeRasterN32Premul(10, 10); + sk_sp<SkSurface> surf1 = SkSurface::MakeRasterN32Premul(10, 10); + SkCanvas* c0 = surf0->getCanvas(); + SkCanvas* c1 = surf1->getCanvas(); + + SkPictureRecorder rec0, rec1; + if (doPicture) { + c0 = rec0.beginRecording(10, 10); + c1 = rec1.beginRecording(10, 10); + } + + do_draw(c0, color, false); + do_draw(c1, color, true); + + if (doPicture) { + surf0->getCanvas()->drawPicture(rec0.finishRecordingAsPicture()); + surf1->getCanvas()->drawPicture(rec1.finishRecordingAsPicture()); + } + + // we replicate the assert so we can see which line is reported if there is a failure + if (doPicture) { + REPORTER_ASSERT(r, is_equal(surf0.get(), surf1.get())); + } else { + REPORTER_ASSERT(r, is_equal(surf0.get(), surf1.get())); + } + } +} + +DEF_TEST(savelayer_srcmode_opaque, r) { + do_savelayer_srcmode(r, SK_ColorRED); +} + +DEF_TEST(savelayer_srcmode_alpha, r) { + do_savelayer_srcmode(r, 0x80FF0000); +} +
diff --git a/src/third_party/skia/tests/RecordPatternTest.cpp b/src/third_party/skia/tests/RecordPatternTest.cpp index 5f4d006..33a0114 100644 --- a/src/third_party/skia/tests/RecordPatternTest.cpp +++ b/src/third_party/skia/tests/RecordPatternTest.cpp
@@ -1,3 +1,10 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + #include "Test.h" #include "SkRecord.h" @@ -6,9 +13,9 @@ #include "SkRecords.h" using namespace SkRecords; -typedef Pattern3<Is<Save>, - Is<ClipRect>, - Is<Restore> > +typedef Pattern<Is<Save>, + Is<ClipRect>, + Is<Restore>> SaveClipRectRestore; DEF_TEST(RecordPattern_Simple, r) { @@ -28,9 +35,9 @@ recorder.restore(); REPORTER_ASSERT(r, pattern.match(&record, 0)); - REPORTER_ASSERT(r, pattern.first<Save>() != NULL); - REPORTER_ASSERT(r, pattern.second<ClipRect>() != NULL); - REPORTER_ASSERT(r, pattern.third<Restore>() != NULL); + REPORTER_ASSERT(r, pattern.first<Save>() != nullptr); + REPORTER_ASSERT(r, pattern.second<ClipRect>() != nullptr); + REPORTER_ASSERT(r, pattern.third<Restore>() != nullptr); } DEF_TEST(RecordPattern_StartingIndex, r) { @@ -47,7 +54,7 @@ } // We should match only at 0 and 3. Going over the length should fail gracefully. - for (unsigned i = 0; i < 8; i++) { + for (int i = 0; i < 8; i++) { if (i == 0 || i == 3) { REPORTER_ASSERT(r, pattern.match(&record, i) == i + 3); } else { @@ -70,130 +77,76 @@ REPORTER_ASSERT(r, !pattern.match(&record, 0)); } -DEF_TEST(RecordPattern_Star, r) { - Pattern3<Is<Save>, Star<Is<ClipRect> >, Is<Restore> > pattern; +DEF_TEST(RecordPattern_Greedy, r) { + Pattern<Is<Save>, Greedy<Is<ClipRect>>, Is<Restore>> pattern; SkRecord record; SkRecorder recorder(&record, 1920, 1200); - - recorder.save(); - recorder.restore(); - REPORTER_ASSERT(r, pattern.match(&record, 0)); + int index = 0; recorder.save(); recorder.clipRect(SkRect::MakeWH(300, 200)); recorder.restore(); - REPORTER_ASSERT(r, pattern.match(&record, 2)); + REPORTER_ASSERT(r, pattern.match(&record, index)); + index += 3; recorder.save(); recorder.clipRect(SkRect::MakeWH(300, 200)); recorder.clipRect(SkRect::MakeWH(100, 100)); recorder.restore(); - REPORTER_ASSERT(r, pattern.match(&record, 5)); -} - -DEF_TEST(RecordPattern_IsDraw, r) { - Pattern3<Is<Save>, IsDraw, Is<Restore> > pattern; - - SkRecord record; - SkRecorder recorder(&record, 1920, 1200); - - recorder.save(); - recorder.clipRect(SkRect::MakeWH(300, 200)); - recorder.restore(); - - REPORTER_ASSERT(r, !pattern.match(&record, 0)); - - SkPaint paint; - - recorder.save(); - paint.setColor(0xEEAA8822); - recorder.drawRect(SkRect::MakeWH(300, 200), paint); - recorder.restore(); - - recorder.save(); - paint.setColor(0xFACEFACE); - recorder.drawPaint(paint); - recorder.restore(); - - REPORTER_ASSERT(r, pattern.match(&record, 3)); - REPORTER_ASSERT(r, pattern.first<Save>() != NULL); - REPORTER_ASSERT(r, pattern.second<SkPaint>()->getColor() == 0xEEAA8822); - REPORTER_ASSERT(r, pattern.third<Restore>() != NULL); - - REPORTER_ASSERT(r, pattern.match(&record, 6)); - REPORTER_ASSERT(r, pattern.first<Save>() != NULL); - REPORTER_ASSERT(r, pattern.second<SkPaint>()->getColor() == 0xFACEFACE); - REPORTER_ASSERT(r, pattern.third<Restore>() != NULL); + REPORTER_ASSERT(r, pattern.match(&record, index)); } DEF_TEST(RecordPattern_Complex, r) { - Pattern3<Is<Save>, - Star<Not<Or3<Is<Save>, + Pattern<Is<Save>, + Greedy<Not<Or<Is<Save>, Is<Restore>, - IsDraw> > >, - Is<Restore> > pattern; + IsDraw>>>, + Is<Restore>> pattern; SkRecord record; SkRecorder recorder(&record, 1920, 1200); + int start, begin, end; - recorder.save(); - recorder.restore(); - REPORTER_ASSERT(r, pattern.match(&record, 0) == 2); - - recorder.save(); - recorder.save(); - recorder.restore(); - recorder.restore(); - REPORTER_ASSERT(r, !pattern.match(&record, 2)); - REPORTER_ASSERT(r, pattern.match(&record, 3) == 5); - + start = record.count(); recorder.save(); recorder.clipRect(SkRect::MakeWH(300, 200)); recorder.restore(); - REPORTER_ASSERT(r, pattern.match(&record, 6) == 9); + REPORTER_ASSERT(r, pattern.match(&record, 0) == record.count()); + end = start; + REPORTER_ASSERT(r, pattern.search(&record, &begin, &end)); + REPORTER_ASSERT(r, begin == start); + REPORTER_ASSERT(r, end == record.count()); + start = record.count(); recorder.save(); recorder.clipRect(SkRect::MakeWH(300, 200)); recorder.drawRect(SkRect::MakeWH(100, 3000), SkPaint()); recorder.restore(); - REPORTER_ASSERT(r, !pattern.match(&record, 9)); + REPORTER_ASSERT(r, !pattern.match(&record, start)); + end = start; + REPORTER_ASSERT(r, !pattern.search(&record, &begin, &end)); + start = record.count(); recorder.save(); - recorder.pushCull(SkRect::MakeWH(300, 200)); recorder.clipRect(SkRect::MakeWH(300, 200)); recorder.clipRect(SkRect::MakeWH(100, 400)); - recorder.popCull(); recorder.restore(); - REPORTER_ASSERT(r, pattern.match(&record, 13) == 19); - - // Same as above, but using pattern.search to step through matches. - unsigned begin, end = 0; + REPORTER_ASSERT(r, pattern.match(&record, start) == record.count()); + end = start; REPORTER_ASSERT(r, pattern.search(&record, &begin, &end)); - REPORTER_ASSERT(r, begin == 0); - REPORTER_ASSERT(r, end == 2); - - REPORTER_ASSERT(r, pattern.search(&record, &begin, &end)); - REPORTER_ASSERT(r, begin == 3); - REPORTER_ASSERT(r, end == 5); - - REPORTER_ASSERT(r, pattern.search(&record, &begin, &end)); - REPORTER_ASSERT(r, begin == 6); - REPORTER_ASSERT(r, end == 9); - - REPORTER_ASSERT(r, pattern.search(&record, &begin, &end)); - REPORTER_ASSERT(r, begin == 13); - REPORTER_ASSERT(r, end == 19); + REPORTER_ASSERT(r, begin == start); + REPORTER_ASSERT(r, end == record.count()); REPORTER_ASSERT(r, !pattern.search(&record, &begin, &end)); } DEF_TEST(RecordPattern_SaveLayerIsNotADraw, r) { - Pattern1<IsDraw> pattern; + Pattern<IsDraw> pattern; SkRecord record; SkRecorder recorder(&record, 1920, 1200); - recorder.saveLayer(NULL, NULL); + recorder.saveLayer(nullptr, nullptr); REPORTER_ASSERT(r, !pattern.match(&record, 0)); }
diff --git a/src/third_party/skia/tests/RecordReplaceDrawTest.cpp b/src/third_party/skia/tests/RecordReplaceDrawTest.cpp deleted file mode 100644 index 618be72..0000000 --- a/src/third_party/skia/tests/RecordReplaceDrawTest.cpp +++ /dev/null
@@ -1,128 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#if SK_SUPPORT_GPU - -#include "Test.h" -#include "RecordTestUtils.h" - -#include "SkBBHFactory.h" -#include "SkRecordDraw.h" -#include "SkRecorder.h" -#include "SkUtils.h" -#include "GrRecordReplaceDraw.h" - -static const int kWidth = 100; -static const int kHeight = 100; - -class JustOneDraw : public SkDrawPictureCallback { -public: - JustOneDraw() : fCalls(0) {} - - virtual bool abortDrawing() SK_OVERRIDE { return fCalls++ > 0; } -private: - int fCalls; -}; - -// Make sure the abort callback works -DEF_TEST(RecordReplaceDraw_Abort, r) { - // Record two commands. - SkRecord record; - SkRecorder recorder(&record, kWidth, kHeight); - recorder.drawRect(SkRect::MakeWH(SkIntToScalar(kWidth), SkIntToScalar(kHeight)), SkPaint()); - recorder.clipRect(SkRect::MakeWH(SkIntToScalar(kWidth), SkIntToScalar(kHeight))); - - SkRecord rerecord; - SkRecorder canvas(&rerecord, kWidth, kHeight); - - GrReplacements replacements; - JustOneDraw callback; - GrRecordReplaceDraw(record, &canvas, NULL/*bbh*/, &replacements, &callback); - - REPORTER_ASSERT(r, 3 == rerecord.count()); - assert_type<SkRecords::Save>(r, rerecord, 0); - assert_type<SkRecords::DrawRect>(r, rerecord, 1); - assert_type<SkRecords::Restore>(r, rerecord, 2); -} - -// Make sure GrRecordReplaceDraw balances unbalanced saves -DEF_TEST(RecordReplaceDraw_Unbalanced, r) { - SkRecord record; - SkRecorder recorder(&record, kWidth, kHeight); - recorder.save(); // We won't balance this, but GrRecordReplaceDraw will for us. - - SkRecord rerecord; - SkRecorder canvas(&rerecord, kWidth, kHeight); - - GrReplacements replacements; - GrRecordReplaceDraw(record, &canvas, NULL/*bbh*/, &replacements, NULL/*callback*/); - - REPORTER_ASSERT(r, 4 == rerecord.count()); - assert_type<SkRecords::Save>(r, rerecord, 0); - assert_type<SkRecords::Save>(r, rerecord, 1); - assert_type<SkRecords::Restore>(r, rerecord, 2); - assert_type<SkRecords::Restore>(r, rerecord, 3); -} - -static SkImage* make_image(SkColor color) { - const SkPMColor pmcolor = SkPreMultiplyColor(color); - const SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight); - const size_t rowBytes = info.minRowBytes(); - const size_t size = rowBytes * info.height(); - - SkAutoMalloc addr(size); - sk_memset32((SkPMColor*)addr.get(), pmcolor, SkToInt(size >> 2)); - - return SkImage::NewRasterCopy(info, addr.get(), rowBytes); -} - -// Test out the layer replacement functionality with and w/o a BBH -void test_replacements(skiatest::Reporter* r, bool useBBH) { - SkRecord record; - SkRecorder recorder(&record, kWidth, kHeight); - SkAutoTDelete<SkPaint> paint(SkNEW(SkPaint)); - recorder.saveLayer(NULL, paint); - recorder.clear(SK_ColorRED); - recorder.restore(); - recorder.drawRect(SkRect::MakeWH(SkIntToScalar(kWidth/2), SkIntToScalar(kHeight/2)), - SkPaint()); - - GrReplacements replacements; - GrReplacements::ReplacementInfo* ri = replacements.push(); - ri->fStart = 0; - ri->fStop = 2; - ri->fPos.set(0, 0); - ri->fImage = make_image(SK_ColorRED); - ri->fPaint = SkNEW(SkPaint); - ri->fSrcRect = SkIRect::MakeWH(kWidth, kHeight); - - SkAutoTUnref<SkBBoxHierarchy> bbh; - - if (useBBH) { - SkRTreeFactory factory; - bbh.reset((factory)(kWidth, kHeight)); - SkRecordFillBounds(record, bbh); - } - - SkRecord rerecord; - SkRecorder canvas(&rerecord, kWidth, kHeight); - GrRecordReplaceDraw(record, &canvas, bbh, &replacements, NULL/*callback*/); - - REPORTER_ASSERT(r, 7 == rerecord.count()); - assert_type<SkRecords::Save>(r, rerecord, 0); - assert_type<SkRecords::Save>(r, rerecord, 1); - assert_type<SkRecords::SetMatrix>(r, rerecord, 2); - assert_type<SkRecords::DrawBitmapRectToRect>(r, rerecord, 3); - assert_type<SkRecords::Restore>(r, rerecord, 4); - assert_type<SkRecords::DrawRect>(r, rerecord, 5); - assert_type<SkRecords::Restore>(r, rerecord, 6); -} - -DEF_TEST(RecordReplaceDraw_Replace, r) { test_replacements(r, false); } -DEF_TEST(RecordReplaceDraw_ReplaceWithBBH, r) { test_replacements(r, true); } - -#endif
diff --git a/src/third_party/skia/tests/RecordTest.cpp b/src/third_party/skia/tests/RecordTest.cpp index 2a0e615..a67d7e8 100644 --- a/src/third_party/skia/tests/RecordTest.cpp +++ b/src/third_party/skia/tests/RecordTest.cpp
@@ -5,13 +5,14 @@ * found in the LICENSE file. */ -#include "Test.h" - +#include "RecordTestUtils.h" #include "SkBitmap.h" #include "SkImageInfo.h" -#include "SkShader.h" #include "SkRecord.h" #include "SkRecords.h" +#include "SkShader.h" +#include "Test.h" + // Sums the area of any DrawRect command it sees. class AreaSummer { @@ -27,8 +28,8 @@ int area() const { return fArea; } void apply(const SkRecord& record) { - for (unsigned i = 0; i < record.count(); i++) { - record.visit<void>(i, *this); + for (int i = 0; i < record.count(); i++) { + record.visit(i, *this); } } @@ -45,13 +46,13 @@ } void apply(SkRecord* record) { - for (unsigned i = 0; i < record->count(); i++) { - record->mutate<void>(i, *this); + for (int i = 0; i < record->count(); i++) { + record->mutate(i, *this); } } }; -#define APPEND(record, type, ...) SkNEW_PLACEMENT_ARGS(record.append<type>(), type, (__VA_ARGS__)) +#define APPEND(record, type, ...) new (record.append<type>()) type{__VA_ARGS__} // Basic tests for the low-level SkRecord code. DEF_TEST(Record, r) { @@ -76,6 +77,25 @@ REPORTER_ASSERT(r, summer.area() == 500); } +DEF_TEST(Record_defrag, r) { + SkRecord record; + APPEND(record, SkRecords::Save); + APPEND(record, SkRecords::ClipRect); + APPEND(record, SkRecords::NoOp); + APPEND(record, SkRecords::DrawRect); + APPEND(record, SkRecords::NoOp); + APPEND(record, SkRecords::NoOp); + APPEND(record, SkRecords::Restore); + REPORTER_ASSERT(r, record.count() == 7); + + record.defrag(); + REPORTER_ASSERT(r, record.count() == 4); + assert_type<SkRecords::Save >(r, record, 0); + assert_type<SkRecords::ClipRect>(r, record, 1); + assert_type<SkRecords::DrawRect>(r, record, 2); + assert_type<SkRecords::Restore >(r, record, 3); +} + #undef APPEND template <typename T> @@ -85,21 +105,14 @@ DEF_TEST(Record_Alignment, r) { SkRecord record; - - // Of course a byte's always aligned. REPORTER_ASSERT(r, is_aligned(record.alloc<uint8_t>())); - - // (If packed tightly, the rest below here would be off by one.) - - // It happens that the first implementation always aligned to 4 bytes, - // so these two were always correct. REPORTER_ASSERT(r, is_aligned(record.alloc<uint16_t>())); REPORTER_ASSERT(r, is_aligned(record.alloc<uint32_t>())); - - // These two are regression tests (void* only on 64-bit machines). - REPORTER_ASSERT(r, is_aligned(record.alloc<uint64_t>())); REPORTER_ASSERT(r, is_aligned(record.alloc<void*>())); - // We're not testing beyond sizeof(void*), which is where the current implementation will break. + // It's not clear if we care that 8-byte values are aligned on 32-bit machines. + if (sizeof(void*) == 8) { + REPORTER_ASSERT(r, is_aligned(record.alloc<double>())); + REPORTER_ASSERT(r, is_aligned(record.alloc<uint64_t>())); + } } -
diff --git a/src/third_party/skia/tests/RecordTestUtils.h b/src/third_party/skia/tests/RecordTestUtils.h index 0575b83..853ee21 100644 --- a/src/third_party/skia/tests/RecordTestUtils.h +++ b/src/third_party/skia/tests/RecordTestUtils.h
@@ -1,13 +1,21 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + #ifndef RecordTestUtils_DEFINED #define RecordTestUtils_DEFINED #include "SkRecord.h" #include "SkRecords.h" +#include "Test.h" -// If the command we're reading is a U, set ptr to it, otherwise set it to NULL. +// If the command we're reading is a U, set ptr to it, otherwise set it to nullptr. template <typename U> struct ReadAs { - ReadAs() : ptr(NULL), type(SkRecords::Type(~0)) {} + ReadAs() : ptr(nullptr), type(SkRecords::Type(~0)) {} const U* ptr; SkRecords::Type type; @@ -20,12 +28,36 @@ // Assert that the ith command in record is of type T, and return it. template <typename T> -static const T* assert_type(skiatest::Reporter* r, const SkRecord& record, unsigned index) { +static const T* assert_type(skiatest::Reporter* r, const SkRecord& record, int index) { ReadAs<T> reader; - record.visit<void>(index, reader); + record.visit(index, reader); REPORTER_ASSERT(r, T::kType == reader.type); REPORTER_ASSERT(r, SkToBool(reader.ptr)); return reader.ptr; } +template <typename DrawT> struct MatchType { + template <typename T> int operator()(const T&) { return 0; } + int operator()(const DrawT&) { return 1; } +}; + +template <typename DrawT> int count_instances_of_type(const SkRecord& record) { + MatchType<DrawT> matcher; + int counter = 0; + for (int i = 0; i < record.count(); i++) { + counter += record.visit(i, matcher); + } + return counter; +} + +template <typename DrawT> int find_first_instances_of_type(const SkRecord& record) { + MatchType<DrawT> matcher; + for (int i = 0; i < record.count(); i++) { + if (record.visit(i, matcher)) { + return i; + } + } + return -1; +} + #endif//RecordTestUtils_DEFINED
diff --git a/src/third_party/skia/tests/RecorderTest.cpp b/src/third_party/skia/tests/RecorderTest.cpp index aced54f..a709a51 100644 --- a/src/third_party/skia/tests/RecorderTest.cpp +++ b/src/third_party/skia/tests/RecorderTest.cpp
@@ -12,6 +12,7 @@ #include "SkRecorder.h" #include "SkRecords.h" #include "SkShader.h" +#include "SkSurface.h" #define COUNT(T) + 1 static const int kRecordTypes = SK_RECORD_TYPES(COUNT); @@ -29,8 +30,8 @@ int count() const { return fHistogram[T::kType]; } void apply(const SkRecord& record) { - for (unsigned i = 0; i < record.count(); i++) { - record.visit<void>(i, *this); + for (int i = 0; i < record.count(); i++) { + record.visit(i, *this); } } @@ -49,39 +50,6 @@ REPORTER_ASSERT(r, 1 == tally.count<SkRecords::DrawRect>()); } -// All of Skia will work fine without support for comment groups, but -// Chrome's inspector can break. This serves as a simple regression test. -DEF_TEST(Recorder_CommentGroups, r) { - SkRecord record; - SkRecorder recorder(&record, 1920, 1080); - - recorder.beginCommentGroup("test"); - recorder.addComment("foo", "bar"); - recorder.addComment("baz", "quux"); - recorder.endCommentGroup(); - - Tally tally; - tally.apply(record); - - REPORTER_ASSERT(r, 1 == tally.count<SkRecords::BeginCommentGroup>()); - REPORTER_ASSERT(r, 2 == tally.count<SkRecords::AddComment>()); - REPORTER_ASSERT(r, 1 == tally.count<SkRecords::EndCommentGroup>()); -} - -// DrawData is similar to comment groups. It doesn't affect drawing, but -// it's a pass-through we provide to the client. Again, a simple reg. test. -DEF_TEST(Recorder_DrawData, r) { - SkRecord record; - SkRecorder recorder(&record, 100, 100); - - const char* data = "This sure is some data, eh?"; - recorder.drawData(data, strlen(data)); - - Tally tally; - tally.apply(record); - REPORTER_ASSERT(r, 1 == tally.count<SkRecords::DrawData>()); -} - // Regression test for leaking refs held by optional arguments. DEF_TEST(Recorder_RefLeaking, r) { // We use SaveLayer to test: @@ -90,7 +58,7 @@ SkRect bounds = SkRect::MakeWH(320, 240); SkPaint paint; - paint.setShader(SkShader::CreateEmptyShader())->unref(); + paint.setShader(SkShader::MakeEmptyShader()); REPORTER_ASSERT(r, paint.getShader()->unique()); { @@ -102,51 +70,41 @@ REPORTER_ASSERT(r, paint.getShader()->unique()); } -DEF_TEST(Recorder_RefPictures, r) { - SkAutoTUnref<SkPicture> pic; +DEF_TEST(Recorder_drawImage_takeReference, reporter) { + sk_sp<SkImage> image; { - SkPictureRecorder pr; - SkCanvas* canvas = pr.beginRecording(100, 100); - canvas->drawColor(SK_ColorRED); - pic.reset(pr.endRecording()); + auto surface(SkSurface::MakeRasterN32Premul(100, 100)); + surface->getCanvas()->clear(SK_ColorGREEN); + image = surface->makeImageSnapshot(); } - REPORTER_ASSERT(r, pic->unique()); + { + SkRecord record; + SkRecorder recorder(&record, 100, 100); + + // DrawImage is supposed to take a reference + recorder.drawImage(image, 0, 0); + REPORTER_ASSERT(reporter, !image->unique()); + + Tally tally; + tally.apply(record); + + REPORTER_ASSERT(reporter, 1 == tally.count<SkRecords::DrawImage>()); + } + REPORTER_ASSERT(reporter, image->unique()); { SkRecord record; SkRecorder recorder(&record, 100, 100); - recorder.drawPicture(pic); - // the recorder should now also be an owner - REPORTER_ASSERT(r, !pic->unique()); + + // DrawImageRect is supposed to take a reference + recorder.drawImageRect(image, SkRect::MakeWH(100, 100), nullptr); + REPORTER_ASSERT(reporter, !image->unique()); + + Tally tally; + tally.apply(record); + + REPORTER_ASSERT(reporter, 1 == tally.count<SkRecords::DrawImageRect>()); } - // the recorder destructor should have released us (back to unique) - REPORTER_ASSERT(r, pic->unique()); + REPORTER_ASSERT(reporter, image->unique()); } - -DEF_TEST(Recorder_IsDrawingToLayer, r) { - SkRecord record; - SkRecorder recorder(&record, 100, 100); - - // We'll save, saveLayer, save, and saveLayer, then restore them all, - // checking that isDrawingToLayer() is correct at each step. - - REPORTER_ASSERT(r, !recorder.isDrawingToLayer()); - recorder.save(); - REPORTER_ASSERT(r, !recorder.isDrawingToLayer()); - recorder.saveLayer(NULL, NULL); - REPORTER_ASSERT(r, recorder.isDrawingToLayer()); - recorder.save(); - REPORTER_ASSERT(r, recorder.isDrawingToLayer()); - recorder.saveLayer(NULL, NULL); - REPORTER_ASSERT(r, recorder.isDrawingToLayer()); - recorder.restore(); - REPORTER_ASSERT(r, recorder.isDrawingToLayer()); - recorder.restore(); - REPORTER_ASSERT(r, recorder.isDrawingToLayer()); - recorder.restore(); - REPORTER_ASSERT(r, !recorder.isDrawingToLayer()); - recorder.restore(); - REPORTER_ASSERT(r, !recorder.isDrawingToLayer()); -} -
diff --git a/src/third_party/skia/tests/RecordingTest.cpp b/src/third_party/skia/tests/RecordingTest.cpp deleted file mode 100644 index 0066556..0000000 --- a/src/third_party/skia/tests/RecordingTest.cpp +++ /dev/null
@@ -1,29 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Test.h" - -#include "../include/record/SkRecording.h" - -// Minimally exercise the public SkRecording API. - -DEF_TEST(SkRecording, r) { - EXPERIMENTAL::SkRecording recording(1920, 1080); - - // Some very exciting commands here. - recording.canvas()->clipRect(SkRect::MakeWH(320, 240)); - - SkAutoTDelete<const EXPERIMENTAL::SkPlayback> playback(recording.releasePlayback()); - - SkCanvas target; - playback->draw(&target); - - // Here's another recording we never call releasePlayback(). - // However pointless, this should be safe. - EXPERIMENTAL::SkRecording pointless(1920, 1080); - pointless.canvas()->clipRect(SkRect::MakeWH(320, 240)); -}
diff --git a/src/third_party/skia/tests/RecordingXfermodeTest.cpp b/src/third_party/skia/tests/RecordingXfermodeTest.cpp index 8da81b3..20dccc6 100644 --- a/src/third_party/skia/tests/RecordingXfermodeTest.cpp +++ b/src/third_party/skia/tests/RecordingXfermodeTest.cpp
@@ -7,12 +7,13 @@ #include "Test.h" +#include "../include/core/SkBitmap.h" #include "../include/core/SkCanvas.h" #include "../include/core/SkPicture.h" #include "../include/core/SkStream.h" #include "../include/core/SkString.h" -#include "../include/record/SkRecording.h" #include "../include/core/SkPictureRecorder.h" +#include "../src/core/SkBlendModePriv.h" #include <cstring> // Verify that replay of a recording into a clipped canvas @@ -20,25 +21,22 @@ // This arose from http://crbug.com/401593 which has // https://code.google.com/p/skia/issues/detail?id=1291 as its root cause. - namespace { class Drawer { public: - explicit Drawer() - : fImageInfo(SkImageInfo::MakeN32Premul(200,100)) - { - fCircleBM.allocPixels( SkImageInfo::MakeN32Premul(100,100) ); + explicit Drawer() : fImageInfo(SkImageInfo::MakeN32Premul(200, 100)) { + fCircleBM.allocPixels(SkImageInfo::MakeN32Premul(100, 100)); SkCanvas canvas(fCircleBM); canvas.clear(0xffffffff); SkPaint circlePaint; circlePaint.setColor(0xff000000); - canvas.drawCircle(50,50,50,circlePaint); + canvas.drawCircle(50, 50, 50, circlePaint); } const SkImageInfo& imageInfo() const { return fImageInfo; } - void draw(SkCanvas* canvas, const SkRect& clipRect, SkXfermode::Mode mode) const { + void draw(SkCanvas* canvas, const SkRect& clipRect, SkBlendMode mode) const { SkPaint greenPaint; greenPaint.setColor(0xff008000); SkPaint blackPaint; @@ -47,16 +45,17 @@ whitePaint.setColor(0xffffffff); SkPaint layerPaint; layerPaint.setColor(0xff000000); - layerPaint.setXfermodeMode(mode); - SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fImageInfo.width()),SkIntToScalar(fImageInfo.height()))); + layerPaint.setBlendMode(mode); + SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fImageInfo.width()), + SkIntToScalar(fImageInfo.height()))); canvas->clipRect(clipRect); canvas->clear(0xff000000); - canvas->saveLayer(NULL,&blackPaint); - canvas->drawRect(canvasRect,greenPaint); - canvas->saveLayer(NULL,&layerPaint); - canvas->drawBitmapRect(fCircleBM,SkRect::MakeXYWH(20,20,60,60),&blackPaint); + canvas->saveLayer(nullptr, &blackPaint); + canvas->drawRect(canvasRect, greenPaint); + canvas->saveLayer(nullptr, &layerPaint); + canvas->drawBitmapRect(fCircleBM, SkRect::MakeXYWH(20,20,60,60), &blackPaint); canvas->restore(); canvas->restore(); } @@ -69,24 +68,20 @@ class RecordingStrategy { public: virtual ~RecordingStrategy() {} - virtual void init(const SkImageInfo&) = 0; virtual const SkBitmap& recordAndReplay(const Drawer& drawer, const SkRect& intoClip, - SkXfermode::Mode) = 0; + SkBlendMode) = 0; }; class BitmapBackedCanvasStrategy : public RecordingStrategy { // This version just draws into a bitmap-backed canvas. public: - BitmapBackedCanvasStrategy() {} - - virtual void init(const SkImageInfo& imageInfo) { + BitmapBackedCanvasStrategy(const SkImageInfo& imageInfo) { fBitmap.allocPixels(imageInfo); } - virtual const SkBitmap& recordAndReplay(const Drawer& drawer, - const SkRect& intoClip, - SkXfermode::Mode mode) { + const SkBitmap& recordAndReplay(const Drawer& drawer, const SkRect& intoClip, + SkBlendMode mode) override { SkCanvas canvas(fBitmap); canvas.clear(0xffffffff); // Note that the scene is drawn just into the clipped region! @@ -99,70 +94,27 @@ SkBitmap fBitmap; }; -class DeprecatedRecorderStrategy : public RecordingStrategy { - // This version draws the entire scene into an SkPictureRecorder, - // using the deprecated recording backend. +class PictureStrategy : public RecordingStrategy { + // This version draws the entire scene into an SkPictureRecorder. // Then it then replays the scene through a clip rectangle. // This backend proved to be buggy. public: - DeprecatedRecorderStrategy() {} - - virtual void init(const SkImageInfo& imageInfo) { + PictureStrategy(const SkImageInfo& imageInfo) { fBitmap.allocPixels(imageInfo); - fWidth = imageInfo.width(); - fHeight= imageInfo.height(); + fWidth = imageInfo.width(); + fHeight = imageInfo.height(); } - virtual const SkBitmap& recordAndReplay(const Drawer& drawer, - const SkRect& intoClip, - SkXfermode::Mode mode) { - SkTileGridFactory::TileGridInfo tileGridInfo = { {100,100}, {0,0}, {0,0} }; - SkTileGridFactory factory(tileGridInfo); + const SkBitmap& recordAndReplay(const Drawer& drawer, const SkRect& intoClip, + SkBlendMode mode) override { + SkRTreeFactory factory; SkPictureRecorder recorder; SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fWidth),SkIntToScalar(fHeight))); - SkCanvas* canvas = recorder.DEPRECATED_beginRecording( SkIntToScalar(fWidth), SkIntToScalar(fHeight), &factory); + SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(fWidth), + SkIntToScalar(fHeight), + &factory); drawer.draw(canvas, canvasRect, mode); - SkAutoTDelete<SkPicture> picture(recorder.endRecording()); - - SkCanvas replayCanvas(fBitmap); - replayCanvas.clear(0xffffffff); - replayCanvas.clipRect(intoClip); - picture->playback(&replayCanvas); - - return fBitmap; - } - - private: - SkBitmap fBitmap; - int fWidth; - int fHeight; -}; - -class NewRecordingStrategy : public RecordingStrategy { - // This version draws the entire scene into an SkPictureRecorder, - // using the new recording backend. - // Then it then replays the scene through a clip rectangle. - // This backend proved to be buggy. - public: - NewRecordingStrategy() {} - - virtual void init(const SkImageInfo& imageInfo) { - fBitmap.allocPixels(imageInfo); - fWidth = imageInfo.width(); - fHeight= imageInfo.height(); - } - - virtual const SkBitmap& recordAndReplay(const Drawer& drawer, - const SkRect& intoClip, - SkXfermode::Mode mode) { - SkTileGridFactory::TileGridInfo tileGridInfo = { {100,100}, {0,0}, {0,0} }; - SkTileGridFactory factory(tileGridInfo); - SkPictureRecorder recorder; - SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fWidth),SkIntToScalar(fHeight))); - SkCanvas* canvas = recorder.EXPERIMENTAL_beginRecording( SkIntToScalar(fWidth), SkIntToScalar(fHeight), &factory); - - drawer.draw(canvas, canvasRect, mode); - SkAutoTDelete<SkPicture> picture(recorder.endRecording()); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); SkCanvas replayCanvas(fBitmap); replayCanvas.clear(0xffffffff); @@ -177,64 +129,45 @@ int fHeight; }; -} - +} // namespace DEF_TEST(SkRecordingAccuracyXfermode, reporter) { #define FINEGRAIN 0 - const Drawer drawer; - BitmapBackedCanvasStrategy golden; // This is the expected result. - DeprecatedRecorderStrategy deprecatedRecording; - NewRecordingStrategy newRecording; - - golden.init(drawer.imageInfo()); - deprecatedRecording.init(drawer.imageInfo()); - newRecording.init(drawer.imageInfo()); + BitmapBackedCanvasStrategy golden(drawer.imageInfo()); + PictureStrategy picture(drawer.imageInfo()); #if !FINEGRAIN unsigned numErrors = 0; SkString errors; #endif - for (int iMode = 0; iMode < int(SkXfermode::kLastMode) ; iMode++ ) { - const SkRect& clip = SkRect::MakeXYWH(100,0,100,100); - SkXfermode::Mode mode = SkXfermode::Mode(iMode); + for (int iMode = 0; iMode < int(SkBlendMode::kLastMode); iMode++) { + const SkRect& clip = SkRect::MakeXYWH(100, 0, 100, 100); + SkBlendMode mode = SkBlendMode(iMode); const SkBitmap& goldenBM = golden.recordAndReplay(drawer, clip, mode); - const SkBitmap& deprecatedBM = deprecatedRecording.recordAndReplay(drawer, clip, mode); - const SkBitmap& newRecordingBM = newRecording.recordAndReplay(drawer, clip, mode); + const SkBitmap& pictureBM = picture.recordAndReplay(drawer, clip, mode); size_t pixelsSize = goldenBM.getSize(); - REPORTER_ASSERT( reporter, pixelsSize == deprecatedBM.getSize() ); - REPORTER_ASSERT( reporter, pixelsSize == newRecordingBM.getSize() ); + REPORTER_ASSERT(reporter, pixelsSize == pictureBM.getSize()); // The pixel arrays should match. #if FINEGRAIN - REPORTER_ASSERT_MESSAGE( reporter, - 0==memcmp( goldenBM.getPixels(), deprecatedBM.getPixels(), pixelsSize ), - "Tiled bitmap is wrong"); - REPORTER_ASSERT_MESSAGE( reporter, - 0==memcmp( goldenBM.getPixels(), recordingBM.getPixels(), pixelsSize ), - "SkRecorder bitmap is wrong"); + REPORTER_ASSERT(reporter, + 0 == memcmp(goldenBM.getPixels(), pictureBM.getPixels(), pixelsSize)); #else - if ( memcmp( goldenBM.getPixels(), deprecatedBM.getPixels(), pixelsSize ) ) { + if (memcmp(goldenBM.getPixels(), pictureBM.getPixels(), pixelsSize)) { numErrors++; - SkString str; - str.printf("For SkXfermode %d %s: Deprecated recorder bitmap is wrong\n", iMode, SkXfermode::ModeName(mode)); - errors.append(str); - } - if ( memcmp( goldenBM.getPixels(), newRecordingBM.getPixels(), pixelsSize ) ) { - numErrors++; - SkString str; - str.printf("For SkXfermode %d %s: SkPictureRecorder bitmap is wrong\n", iMode, SkXfermode::ModeName(mode)); - errors.append(str); + errors.appendf("For SkXfermode %d %s: SkPictureRecorder bitmap is wrong\n", + iMode, SkBlendMode_Name(mode)); } #endif } + #if !FINEGRAIN - REPORTER_ASSERT_MESSAGE( reporter, 0==numErrors, errors.c_str() ); + REPORTER_ASSERT_MESSAGE(reporter, 0 == numErrors, errors.c_str()); #endif }
diff --git a/src/third_party/skia/tests/RectTest.cpp b/src/third_party/skia/tests/RectTest.cpp new file mode 100644 index 0000000..820586c --- /dev/null +++ b/src/third_party/skia/tests/RectTest.cpp
@@ -0,0 +1,90 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkRect.h" +#include "Test.h" + +static bool has_green_pixels(const SkBitmap& bm) { + for (int j = 0; j < bm.height(); ++j) { + for (int i = 0; i < bm.width(); ++i) { + if (SkColorGetG(bm.getColor(i, j))) { + return true; + } + } + } + + return false; +} + +static void test_stroke_width_clipping(skiatest::Reporter* reporter) { + SkBitmap bm; + bm.allocN32Pixels(100, 10); + bm.eraseColor(SK_ColorTRANSPARENT); + + SkCanvas canvas(bm); + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(10); + paint.setColor(0xff00ff00); + + // clip out the left half of our canvas + canvas.clipRect(SkRect::MakeXYWH(51, 0, 49, 100)); + + // no stroke bleed should be visible + canvas.drawRect(SkRect::MakeWH(44, 100), paint); + REPORTER_ASSERT(reporter, !has_green_pixels(bm)); + + // right stroke edge should bleed into the visible area + canvas.scale(2, 2); + canvas.drawRect(SkRect::MakeWH(22, 50), paint); + REPORTER_ASSERT(reporter, has_green_pixels(bm)); +} + +static void test_skbug4406(skiatest::Reporter* reporter) { + SkBitmap bm; + bm.allocN32Pixels(10, 10); + bm.eraseColor(SK_ColorTRANSPARENT); + + SkCanvas canvas(bm); + const SkRect r = { 1.5f, 1, 3.5f, 3 }; + // draw filled green rect first + SkPaint paint; + paint.setStyle(SkPaint::kFill_Style); + paint.setColor(0xff00ff00); + paint.setStrokeWidth(1); + paint.setAntiAlias(true); + canvas.drawRect(r, paint); + + // paint black with stroke rect (that asserts in bug 4406) + // over the filled rect, it should cover it + paint.setStyle(SkPaint::kStroke_Style); + paint.setColor(0xff000000); + paint.setStrokeWidth(1); + canvas.drawRect(r, paint); + REPORTER_ASSERT(reporter, !has_green_pixels(bm)); + + // do it again with thinner stroke + paint.setStyle(SkPaint::kFill_Style); + paint.setColor(0xff00ff00); + paint.setStrokeWidth(1); + paint.setAntiAlias(true); + canvas.drawRect(r, paint); + // paint black with stroke rect (that asserts in bug 4406) + // over the filled rect, it doesnt cover it completelly with thinner stroke + paint.setStyle(SkPaint::kStroke_Style); + paint.setColor(0xff000000); + paint.setStrokeWidth(0.99f); + canvas.drawRect(r, paint); + REPORTER_ASSERT(reporter, has_green_pixels(bm)); +} + +DEF_TEST(Rect, reporter) { + test_stroke_width_clipping(reporter); + test_skbug4406(reporter); +}
diff --git a/src/third_party/skia/tests/RectangleTextureTest.cpp b/src/third_party/skia/tests/RectangleTextureTest.cpp new file mode 100644 index 0000000..705f585 --- /dev/null +++ b/src/third_party/skia/tests/RectangleTextureTest.cpp
@@ -0,0 +1,163 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" +#include "TestUtils.h" + +#if SK_SUPPORT_GPU +#include "GrClip.h" +#include "GrContext.h" +#include "GrContextPriv.h" +#include "GrRenderTargetContext.h" +#include "GrTest.h" +#include "gl/GLTestContext.h" +#include "gl/GrGLGpu.h" +#include "gl/GrGLUtil.h" + +// skbug.com/5932 +static void test_basic_draw_as_src(skiatest::Reporter* reporter, GrContext* context, + sk_sp<GrTextureProxy> rectProxy, uint32_t expectedPixelValues[]) { + sk_sp<GrRenderTargetContext> rtContext( + context->makeDeferredRenderTargetContext(SkBackingFit::kExact, rectProxy->width(), + rectProxy->height(), rectProxy->config(), + nullptr)); + for (auto filter : {GrSamplerParams::kNone_FilterMode, + GrSamplerParams::kBilerp_FilterMode, + GrSamplerParams::kMipMap_FilterMode}) { + rtContext->clear(nullptr, 0xDDCCBBAA, true); + sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make( + rectProxy, + nullptr, + SkMatrix::I(), filter)); + GrPaint paint; + paint.setPorterDuffXPFactory(SkBlendMode::kSrc); + paint.addColorFragmentProcessor(std::move(fp)); + rtContext->drawPaint(GrNoClip(), std::move(paint), SkMatrix::I()); + test_read_pixels(reporter, rtContext.get(), expectedPixelValues, + "RectangleTexture-basic-draw"); + } +} + +static void test_clear(skiatest::Reporter* reporter, GrSurfaceContext* rectContext) { + if (GrRenderTargetContext* rtc = rectContext->asRenderTargetContext()) { + // Clear the whole thing. + GrColor color0 = GrColorPackRGBA(0xA, 0xB, 0xC, 0xD); + rtc->clear(nullptr, color0, false); + + int w = rtc->width(); + int h = rtc->height(); + int pixelCnt = w * h; + SkAutoTMalloc<uint32_t> expectedPixels(pixelCnt); + + // The clear color is a GrColor, our readback is to kRGBA_8888, which may be different. + uint32_t expectedColor0 = 0; + uint8_t* expectedBytes0 = SkTCast<uint8_t*>(&expectedColor0); + expectedBytes0[0] = GrColorUnpackR(color0); + expectedBytes0[1] = GrColorUnpackG(color0); + expectedBytes0[2] = GrColorUnpackB(color0); + expectedBytes0[3] = GrColorUnpackA(color0); + for (int i = 0; i < rtc->width() * rtc->height(); ++i) { + expectedPixels.get()[i] = expectedColor0; + } + + // Clear the the top to a different color. + GrColor color1 = GrColorPackRGBA(0x1, 0x2, 0x3, 0x4); + SkIRect rect = SkIRect::MakeWH(w, h/2); + rtc->clear(&rect, color1, false); + + uint32_t expectedColor1 = 0; + uint8_t* expectedBytes1 = SkTCast<uint8_t*>(&expectedColor1); + expectedBytes1[0] = GrColorUnpackR(color1); + expectedBytes1[1] = GrColorUnpackG(color1); + expectedBytes1[2] = GrColorUnpackB(color1); + expectedBytes1[3] = GrColorUnpackA(color1); + + for (int y = 0; y < h/2; ++y) { + for (int x = 0; x < w; ++x) { + expectedPixels.get()[y * h + x] = expectedColor1; + } + } + + test_read_pixels(reporter, rtc, expectedPixels.get(), "RectangleTexture-clear"); + } +} + +DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(RectangleTexture, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + sk_gpu_test::GLTestContext* glContext = ctxInfo.glContext(); + static const int kWidth = 13; + static const int kHeight = 13; + + GrColor pixels[kWidth * kHeight]; + for (int y = 0; y < kHeight; ++y) { + for (int x = 0; x < kWidth; ++x) { + pixels[y * kWidth + x] = y * kWidth + x; + } + } + + for (auto origin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) { + bool useBLOrigin = kBottomLeft_GrSurfaceOrigin == origin; + + GrGLuint rectTexID = glContext->createTextureRectangle(kWidth, kHeight, GR_GL_RGBA, + GR_GL_RGBA, GR_GL_UNSIGNED_BYTE, + pixels); + + if (!rectTexID) { + return; + } + + // Let GrContext know that we messed with the GL context directly. + context->resetContext(); + + // Wrap the rectangle texture ID in a GrTexture + GrGLTextureInfo rectangleInfo; + rectangleInfo.fID = rectTexID; + rectangleInfo.fTarget = GR_GL_TEXTURE_RECTANGLE; + + GrBackendTexture rectangleTex(kWidth, kHeight, kRGBA_8888_GrPixelConfig, rectangleInfo); + + GrColor refPixels[kWidth * kHeight]; + for (int y = 0; y < kHeight; ++y) { + for (int x = 0; x < kWidth; ++x) { + int y0 = useBLOrigin ? kHeight - y - 1 : y; + refPixels[y * kWidth + x] = pixels[y0 * kWidth + x]; + } + } + + sk_sp<GrTextureProxy> rectProxy = GrSurfaceProxy::MakeWrappedBackend(context, + rectangleTex, + origin); + if (!rectProxy) { + ERRORF(reporter, "Error creating proxy for rectangle texture."); + GR_GL_CALL(glContext->gl(), DeleteTextures(1, &rectTexID)); + continue; + } + + test_basic_draw_as_src(reporter, context, rectProxy, refPixels); + + // Test copy to both a texture and RT + test_copy_from_surface(reporter, context, rectProxy.get(), refPixels, + false, "RectangleTexture-copy-from"); + + sk_sp<GrSurfaceContext> rectContext = context->contextPriv().makeWrappedSurfaceContext( + std::move(rectProxy), nullptr); + SkASSERT(rectContext); + + test_read_pixels(reporter, rectContext.get(), refPixels, "RectangleTexture-read"); + + test_copy_to_surface(reporter, context->resourceProvider(), + rectContext.get(), "RectangleTexture-copy-to"); + + test_write_pixels(reporter, rectContext.get(), true, "RectangleTexture-write"); + + test_clear(reporter, rectContext.get()); + + GR_GL_CALL(glContext->gl(), DeleteTextures(1, &rectTexID)); + } +} + +#endif
diff --git a/src/third_party/skia/tests/RefCntTest.cpp b/src/third_party/skia/tests/RefCntTest.cpp index bd4f348..4bad342 100644 --- a/src/third_party/skia/tests/RefCntTest.cpp +++ b/src/third_party/skia/tests/RefCntTest.cpp
@@ -6,68 +6,11 @@ */ #include "SkRefCnt.h" -#include "SkTRefArray.h" #include "SkThreadUtils.h" #include "SkTypes.h" #include "SkWeakRefCnt.h" #include "Test.h" -class InstCounterClass { -public: - InstCounterClass() { fCount = gInstCounter++; } - InstCounterClass(const InstCounterClass& src) { - fCount = src.fCount; - gInstCounter += 1; - } - virtual ~InstCounterClass() { gInstCounter -= 1; } - - static int gInstCounter; - int fCount; -}; - -int InstCounterClass::gInstCounter; - -static void test_refarray(skiatest::Reporter* reporter) { - REPORTER_ASSERT(reporter, 0 == InstCounterClass::gInstCounter); - - const int N = 10; - SkTRefArray<InstCounterClass>* array = SkTRefArray<InstCounterClass>::Create(N); - - REPORTER_ASSERT(reporter, 1 == array->getRefCnt()); - REPORTER_ASSERT(reporter, N == array->count()); - - REPORTER_ASSERT(reporter, N == InstCounterClass::gInstCounter); - array->unref(); - REPORTER_ASSERT(reporter, 0 == InstCounterClass::gInstCounter); - - // Now test the copy factory - - int i; - InstCounterClass* src = new InstCounterClass[N]; - REPORTER_ASSERT(reporter, N == InstCounterClass::gInstCounter); - for (i = 0; i < N; ++i) { - REPORTER_ASSERT(reporter, i == src[i].fCount); - } - - array = SkTRefArray<InstCounterClass>::Create(src, N); - REPORTER_ASSERT(reporter, 1 == array->getRefCnt()); - REPORTER_ASSERT(reporter, N == array->count()); - - REPORTER_ASSERT(reporter, 2*N == InstCounterClass::gInstCounter); - for (i = 0; i < N; ++i) { - REPORTER_ASSERT(reporter, i == (*array)[i].fCount); - } - - delete[] src; - REPORTER_ASSERT(reporter, N == InstCounterClass::gInstCounter); - - for (i = 0; i < N; ++i) { - REPORTER_ASSERT(reporter, i == (*array)[i].fCount); - } - array->unref(); - REPORTER_ASSERT(reporter, 0 == InstCounterClass::gInstCounter); -} - static void bounce_ref(void* data) { SkRefCnt* ref = static_cast<SkRefCnt*>(data); for (int i = 0; i < 100000; ++i) { @@ -82,16 +25,13 @@ SkThread thing1(bounce_ref, ref); SkThread thing2(bounce_ref, ref); - thing1.setProcessorAffinity(0); - thing2.setProcessorAffinity(23); - - SkASSERT(thing1.start()); - SkASSERT(thing2.start()); + SkAssertResult(thing1.start()); + SkAssertResult(thing2.start()); thing1.join(); thing2.join(); - REPORTER_ASSERT(reporter, ref->getRefCnt() == 1); + REPORTER_ASSERT(reporter, ref->unique()); ref->unref(); } @@ -120,28 +60,341 @@ SkThread thing3(bounce_weak_ref, ref); SkThread thing4(bounce_weak_weak_ref, ref); - thing1.setProcessorAffinity(0); - thing2.setProcessorAffinity(23); - thing3.setProcessorAffinity(2); - thing4.setProcessorAffinity(17); - - SkASSERT(thing1.start()); - SkASSERT(thing2.start()); - SkASSERT(thing3.start()); - SkASSERT(thing4.start()); + SkAssertResult(thing1.start()); + SkAssertResult(thing2.start()); + SkAssertResult(thing3.start()); + SkAssertResult(thing4.start()); thing1.join(); thing2.join(); thing3.join(); thing4.join(); - REPORTER_ASSERT(reporter, ref->getRefCnt() == 1); - REPORTER_ASSERT(reporter, ref->getWeakCnt() == 1); + REPORTER_ASSERT(reporter, ref->unique()); + SkDEBUGCODE(REPORTER_ASSERT(reporter, ref->getWeakCnt() == 1)); ref->unref(); } DEF_TEST(RefCnt, reporter) { test_refCnt(reporter); test_weakRefCnt(reporter); - test_refarray(reporter); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static int gRefCounter; +static int gUnrefCounter; +static int gNewCounter; +static int gDeleteCounter; + +#define check(reporter, ref, unref, make, kill) \ + REPORTER_ASSERT(reporter, gRefCounter == ref); \ + REPORTER_ASSERT(reporter, gUnrefCounter == unref); \ + REPORTER_ASSERT(reporter, gNewCounter == make); \ + REPORTER_ASSERT(reporter, gDeleteCounter == kill); + + +class Effect { +public: + Effect() : fRefCnt(1) { + gNewCounter += 1; + } + virtual ~Effect() {} + + int fRefCnt; + + void ref() { + gRefCounter += 1; + fRefCnt += 1; + } + void unref() { + gUnrefCounter += 1; + + SkASSERT(fRefCnt > 0); + if (0 == --fRefCnt) { + gDeleteCounter += 1; + delete this; + } + } + + int* method() const { return new int; } +}; + +static sk_sp<Effect> Create() { + return sk_make_sp<Effect>(); +} + +class Paint { +public: + sk_sp<Effect> fEffect; + + const sk_sp<Effect>& get() const { return fEffect; } + + void set(sk_sp<Effect> value) { + fEffect = std::move(value); + } +}; + +struct EffectImpl : public Effect { + ~EffectImpl() override {} + + static sk_sp<EffectImpl> Create() { + return sk_sp<EffectImpl>(new EffectImpl); + } + int fValue; +}; +static sk_sp<Effect> make_effect() { + auto foo = EffectImpl::Create(); + foo->fValue = 42; + return std::move(foo); +} + +static void reset_counters() { + gRefCounter = 0; + gUnrefCounter = 0; + gNewCounter = 0; + gDeleteCounter = 0; +} +DEF_TEST(sk_sp, reporter) { + reset_counters(); + + Paint paint; + REPORTER_ASSERT(reporter, paint.fEffect.get() == nullptr); + REPORTER_ASSERT(reporter, !paint.get()); + check(reporter, 0, 0, 0, 0); + + paint.set(Create()); + check(reporter, 0, 0, 1, 0); + REPORTER_ASSERT(reporter, paint.fEffect.get()->fRefCnt == 1); + + if (paint.get()) { + REPORTER_ASSERT(reporter, true); + } else { + REPORTER_ASSERT(reporter, false); + } + if (!paint.get()) { + REPORTER_ASSERT(reporter, false); + } else { + REPORTER_ASSERT(reporter, true); + } + + paint.set(nullptr); + check(reporter, 0, 1, 1, 1); + + if (paint.get()) { + REPORTER_ASSERT(reporter, false); + } else { + REPORTER_ASSERT(reporter, true); + } + if (!paint.get()) { + REPORTER_ASSERT(reporter, true); + } else { + REPORTER_ASSERT(reporter, false); + } + + auto e = Create(); + REPORTER_ASSERT(reporter, sizeof(e) == sizeof(void*)); + + check(reporter, 0, 1, 2, 1); + paint.set(e); + check(reporter, 1, 1, 2, 1); + REPORTER_ASSERT(reporter, paint.fEffect.get()->fRefCnt == 2); + + Paint paint2; + paint2.set(paint.get()); + check(reporter, 2, 1, 2, 1); + REPORTER_ASSERT(reporter, paint.fEffect.get()->fRefCnt == 3); + + // Test sk_sp::operator-> + delete paint.get()->method(); + check(reporter, 2, 1, 2, 1); + + // Test sk_sp::operator* + delete (*paint.get()).method(); + check(reporter, 2, 1, 2, 1); + + paint.set(nullptr); + e = nullptr; + paint2.set(nullptr); + check(reporter, 2, 4, 2, 2); + + reset_counters(); + { + // Test convertible sk_sp assignment. + check(reporter, 0, 0, 0, 0); + sk_sp<Effect> foo(nullptr); + REPORTER_ASSERT(reporter, !foo); + foo = make_effect(); + REPORTER_ASSERT(reporter, foo); + check(reporter, 0, 0, 1, 0); + } + check(reporter, 0, 1, 1, 1); + + // Test passing convertible rvalue into funtion. + reset_counters(); + paint.set(EffectImpl::Create()); + check(reporter, 0, 0, 1, 0); + paint.set(nullptr); + check(reporter, 0, 1, 1, 1); + + reset_counters(); + auto baz = EffectImpl::Create(); + check(reporter, 0, 0, 1, 0); + paint.set(std::move(baz)); + check(reporter, 0, 0, 1, 0); + REPORTER_ASSERT(reporter, !baz); + paint.set(nullptr); + check(reporter, 0, 1, 1, 1); + + reset_counters(); + { + // test comparison operator with convertible type. + sk_sp<EffectImpl> bar1 = EffectImpl::Create(); + sk_sp<Effect> bar2(bar1); // convertible copy constructor + check(reporter, 1, 0, 1, 0); + REPORTER_ASSERT(reporter, bar1); + REPORTER_ASSERT(reporter, bar2); + REPORTER_ASSERT(reporter, bar1 == bar2); + REPORTER_ASSERT(reporter, bar2 == bar1); + REPORTER_ASSERT(reporter, !(bar1 != bar2)); + REPORTER_ASSERT(reporter, !(bar2 != bar1)); + sk_sp<Effect> bar3(nullptr); + bar3 = bar1; // convertible copy assignment + check(reporter, 2, 0, 1, 0); + + } + check(reporter, 2, 3, 1, 1); + + // test passing convertible copy into funtion. + reset_counters(); + baz = EffectImpl::Create(); + check(reporter, 0, 0, 1, 0); + paint.set(baz); + check(reporter, 1, 0, 1, 0); + baz = nullptr; + check(reporter, 1, 1, 1, 0); + paint.set(nullptr); + check(reporter, 1, 2, 1, 1); + + { + sk_sp<SkRefCnt> empty; + sk_sp<SkRefCnt> notEmpty = sk_make_sp<SkRefCnt>(); + REPORTER_ASSERT(reporter, empty == sk_sp<SkRefCnt>()); + + REPORTER_ASSERT(reporter, notEmpty != empty); + REPORTER_ASSERT(reporter, empty != notEmpty); + + REPORTER_ASSERT(reporter, nullptr == empty); + REPORTER_ASSERT(reporter, empty == nullptr); + REPORTER_ASSERT(reporter, empty == empty); + + REPORTER_ASSERT(reporter, nullptr <= empty); + REPORTER_ASSERT(reporter, empty <= nullptr); + REPORTER_ASSERT(reporter, empty <= empty); + + REPORTER_ASSERT(reporter, nullptr >= empty); + REPORTER_ASSERT(reporter, empty >= nullptr); + REPORTER_ASSERT(reporter, empty >= empty); + } + + { + sk_sp<SkRefCnt> a = sk_make_sp<SkRefCnt>(); + sk_sp<SkRefCnt> b = sk_make_sp<SkRefCnt>(); + REPORTER_ASSERT(reporter, a != b); + REPORTER_ASSERT(reporter, (a < b) != (b < a)); + REPORTER_ASSERT(reporter, (b > a) != (a > b)); + REPORTER_ASSERT(reporter, (a <= b) != (b <= a)); + REPORTER_ASSERT(reporter, (b >= a) != (a >= b)); + + REPORTER_ASSERT(reporter, a == a); + REPORTER_ASSERT(reporter, a <= a); + REPORTER_ASSERT(reporter, a >= a); + } + + // http://wg21.cmeerw.net/lwg/issue998 + { + class foo : public SkRefCnt { + public: + foo() : bar(this) {} + void reset() { bar.reset(); } + private: + sk_sp<foo> bar; + }; + // The following should properly delete the object and not cause undefined behavior. + // This is an ugly example, but the same issue can arise in more subtle ways. + (new foo)->reset(); + } + + // https://crrev.com/0d4ef2583a6f19c3e61be04d36eb1a60b133832c + { + struct StructB; + struct StructA : public SkRefCnt { + sk_sp<StructB> b; + }; + + struct StructB : public SkRefCnt { + sk_sp<StructA> a; + ~StructB() override {} // Some clang versions don't emit this implicitly. + }; + + // Create a reference cycle. + StructA* a = new StructA; + a->b.reset(new StructB); + a->b->a.reset(a); + + // Break the cycle by calling reset(). This will cause |a| (and hence, |a.b|) + // to be deleted before the call to reset() returns. This tests that the + // implementation of sk_sp::reset() doesn't access |this| after it + // deletes the underlying pointer. This behaviour is consistent with the + // definition of unique_ptr::reset in C++11. + a->b.reset(); + } +} + +namespace { +struct FooAbstract : public SkRefCnt { + virtual void f() = 0; +}; +struct FooConcrete : public FooAbstract { + void f() override {} +}; +} +static sk_sp<FooAbstract> make_foo() { + // can not cast FooConcrete to FooAbstract. + // can cast FooConcrete* to FooAbstract*. + return sk_make_sp<FooConcrete>(); +} +DEF_TEST(sk_make_sp, r) { + auto x = make_foo(); +} + +// Test that reset() "adopts" ownership from the caller, even if we are given the same ptr twice +// +DEF_TEST(sk_sp_reset, r) { + SkRefCnt* rc = new SkRefCnt; + REPORTER_ASSERT(r, rc->unique()); + + sk_sp<SkRefCnt> sp; + sp.reset(rc); + // We have transfered our ownership over to sp + REPORTER_ASSERT(r, rc->unique()); + + rc->ref(); // now "rc" is also an owner + REPORTER_ASSERT(r, !rc->unique()); + + sp.reset(rc); // this should transfer our ownership over to sp + REPORTER_ASSERT(r, rc->unique()); +} + +DEF_TEST(sk_sp_ref, r) { + SkRefCnt* rc = new SkRefCnt; + REPORTER_ASSERT(r, rc->unique()); + + { + sk_sp<SkRefCnt> sp = sk_ref_sp(rc); + REPORTER_ASSERT(r, !rc->unique()); + } + + REPORTER_ASSERT(r, rc->unique()); + rc->unref(); }
diff --git a/src/third_party/skia/tests/RefDictTest.cpp b/src/third_party/skia/tests/RefDictTest.cpp index 1e18a68..f3ba657 100644 --- a/src/third_party/skia/tests/RefDictTest.cpp +++ b/src/third_party/skia/tests/RefDictTest.cpp
@@ -10,7 +10,7 @@ class TestRC : public SkRefCnt { public: - SK_DECLARE_INST_COUNT(TestRC) + private: typedef SkRefCnt INHERITED; }; @@ -19,56 +19,56 @@ TestRC data0, data1; SkRefDict dict; - REPORTER_ASSERT(reporter, NULL == dict.find(NULL)); - REPORTER_ASSERT(reporter, NULL == dict.find("foo")); - REPORTER_ASSERT(reporter, NULL == dict.find("bar")); + REPORTER_ASSERT(reporter, nullptr == dict.find(nullptr)); + REPORTER_ASSERT(reporter, nullptr == dict.find("foo")); + REPORTER_ASSERT(reporter, nullptr == dict.find("bar")); dict.set("foo", &data0); REPORTER_ASSERT(reporter, &data0 == dict.find("foo")); - REPORTER_ASSERT(reporter, 2 == data0.getRefCnt()); + REPORTER_ASSERT(reporter, !data0.unique()); dict.set("foo", &data0); REPORTER_ASSERT(reporter, &data0 == dict.find("foo")); - REPORTER_ASSERT(reporter, 2 == data0.getRefCnt()); + REPORTER_ASSERT(reporter, !data0.unique()); dict.set("foo", &data1); REPORTER_ASSERT(reporter, &data1 == dict.find("foo")); - REPORTER_ASSERT(reporter, 1 == data0.getRefCnt()); - REPORTER_ASSERT(reporter, 2 == data1.getRefCnt()); + REPORTER_ASSERT(reporter, data0.unique()); + REPORTER_ASSERT(reporter, !data1.unique()); - dict.set("foo", NULL); - REPORTER_ASSERT(reporter, NULL == dict.find("foo")); - REPORTER_ASSERT(reporter, 1 == data0.getRefCnt()); - REPORTER_ASSERT(reporter, 1 == data1.getRefCnt()); + dict.set("foo", nullptr); + REPORTER_ASSERT(reporter, nullptr == dict.find("foo")); + REPORTER_ASSERT(reporter, data0.unique()); + REPORTER_ASSERT(reporter, data1.unique()); dict.set("foo", &data0); dict.set("bar", &data1); REPORTER_ASSERT(reporter, &data0 == dict.find("foo")); REPORTER_ASSERT(reporter, &data1 == dict.find("bar")); - REPORTER_ASSERT(reporter, 2 == data0.getRefCnt()); - REPORTER_ASSERT(reporter, 2 == data1.getRefCnt()); + REPORTER_ASSERT(reporter, !data0.unique()); + REPORTER_ASSERT(reporter, !data1.unique()); dict.set("foo", &data1); REPORTER_ASSERT(reporter, &data1 == dict.find("foo")); REPORTER_ASSERT(reporter, &data1 == dict.find("bar")); - REPORTER_ASSERT(reporter, 1 == data0.getRefCnt()); - REPORTER_ASSERT(reporter, 3 == data1.getRefCnt()); + REPORTER_ASSERT(reporter, data0.unique()); + REPORTER_ASSERT(reporter, !data1.unique()); dict.removeAll(); - REPORTER_ASSERT(reporter, NULL == dict.find("foo")); - REPORTER_ASSERT(reporter, NULL == dict.find("bar")); - REPORTER_ASSERT(reporter, 1 == data0.getRefCnt()); - REPORTER_ASSERT(reporter, 1 == data1.getRefCnt()); + REPORTER_ASSERT(reporter, nullptr == dict.find("foo")); + REPORTER_ASSERT(reporter, nullptr == dict.find("bar")); + REPORTER_ASSERT(reporter, data0.unique()); + REPORTER_ASSERT(reporter, data1.unique()); { SkRefDict d; - REPORTER_ASSERT(reporter, NULL == d.find("foo")); - REPORTER_ASSERT(reporter, 1 == data0.getRefCnt()); + REPORTER_ASSERT(reporter, nullptr == d.find("foo")); + REPORTER_ASSERT(reporter, data0.unique()); d.set("foo", &data0); REPORTER_ASSERT(reporter, &data0 == d.find("foo")); - REPORTER_ASSERT(reporter, 2 == data0.getRefCnt()); + REPORTER_ASSERT(reporter, !data0.unique()); // let d go out of scope still with a ref on data0 } // be sure d's destructor lowered data0's owner count back to 1 - REPORTER_ASSERT(reporter, 1 == data0.getRefCnt()); + REPORTER_ASSERT(reporter, data0.unique()); }
diff --git a/src/third_party/skia/tests/RegionTest.cpp b/src/third_party/skia/tests/RegionTest.cpp index ae58ae6..7fcf101 100644 --- a/src/third_party/skia/tests/RegionTest.cpp +++ b/src/third_party/skia/tests/RegionTest.cpp
@@ -5,6 +5,8 @@ * found in the LICENSE file. */ +#include "SkAutoMalloc.h" +#include "SkPath.h" #include "SkRandom.h" #include "SkRegion.h" #include "Test.h" @@ -91,6 +93,13 @@ REPORTER_ASSERT(reporter, !empty.contains(empty2)); REPORTER_ASSERT(reporter, !valid.contains(empty)); REPORTER_ASSERT(reporter, !empty.contains(valid)); + + SkPath emptyPath; + emptyPath.moveTo(1, 5); + emptyPath.close(); + SkRegion openClip; + openClip.setRect(-16000, -16000, 16000, 16000); + empty.setPath(emptyPath, openClip); // should not assert } enum { @@ -254,3 +263,136 @@ test_empties(reporter); test_fromchrome(reporter); } + +// Test that writeToMemory reports the same number of bytes whether there was a +// buffer to write to or not. +static void test_write(const SkRegion& region, skiatest::Reporter* r) { + const size_t bytesNeeded = region.writeToMemory(nullptr); + SkAutoMalloc storage(bytesNeeded); + const size_t bytesWritten = region.writeToMemory(storage.get()); + REPORTER_ASSERT(r, bytesWritten == bytesNeeded); + + // Also check that the bytes are meaningful. + SkRegion copy; + REPORTER_ASSERT(r, copy.readFromMemory(storage.get(), bytesNeeded)); + REPORTER_ASSERT(r, region == copy); +} + +DEF_TEST(Region_writeToMemory, r) { + // Test an empty region. + SkRegion region; + REPORTER_ASSERT(r, region.isEmpty()); + test_write(region, r); + + // Test a rectangular region + bool nonEmpty = region.setRect(0, 0, 50, 50); + REPORTER_ASSERT(r, nonEmpty); + REPORTER_ASSERT(r, region.isRect()); + test_write(region, r); + + // Test a complex region + nonEmpty = region.op(50, 50, 100, 100, SkRegion::kUnion_Op); + REPORTER_ASSERT(r, nonEmpty); + REPORTER_ASSERT(r, region.isComplex()); + test_write(region, r); + + SkRegion complexRegion; + Union(&complexRegion, SkIRect::MakeXYWH(0, 0, 1, 1)); + Union(&complexRegion, SkIRect::MakeXYWH(0, 0, 3, 3)); + Union(&complexRegion, SkIRect::MakeXYWH(10, 0, 3, 3)); + Union(&complexRegion, SkIRect::MakeXYWH(0, 10, 13, 3)); + test_write(complexRegion, r); + + Union(&complexRegion, SkIRect::MakeXYWH(10, 20, 3, 3)); + Union(&complexRegion, SkIRect::MakeXYWH(0, 20, 3, 3)); + test_write(complexRegion, r); +} + +DEF_TEST(Region_readFromMemory_bad, r) { + // These assume what our binary format is: conceivably we could change it + // and might need to remove or change some of these tests. + SkRegion region; + + { + // invalid boundary rectangle + int32_t data[5] = {0, 4, 4, 8, 2}; + REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); + } + // Region Layout, Serialized Format: + // COUNT LEFT TOP RIGHT BOTTOM Y_SPAN_COUNT TOTAL_INTERVAL_COUNT + // Top ( Bottom Span_Interval_Count ( Left Right )* Sentinel )+ Sentinel + { + // Example of valid data + int32_t data[] = {9, 0, 0, 10, 10, 1, 2, 0, 10, 2, 0, 4, 6, 10, + 2147483647, 2147483647}; + REPORTER_ASSERT(r, 0 != region.readFromMemory(data, sizeof(data))); + } + { + // Example of valid data with 4 intervals + int32_t data[] = {19, 0, 0, 30, 30, 3, 4, 0, 10, 2, 0, 10, 20, 30, + 2147483647, 20, 0, 2147483647, 30, 2, 0, 10, 20, 30, + 2147483647, 2147483647}; + REPORTER_ASSERT(r, 0 != region.readFromMemory(data, sizeof(data))); + } + { + // Short count + int32_t data[] = {8, 0, 0, 10, 10, 1, 2, 0, 10, 2, 0, 4, 6, 10, + 2147483647, 2147483647}; + REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); + } + { + // bounds don't match + int32_t data[] = {9, 0, 0, 10, 11, 1, 2, 0, 10, 2, 0, 4, 6, 10, + 2147483647, 2147483647}; + REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); + } + { + // bad yspan count + int32_t data[] = {9, 0, 0, 10, 10, 2, 2, 0, 10, 2, 0, 4, 6, 10, + 2147483647, 2147483647}; + REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); + } + { + // bad int count + int32_t data[] = {9, 0, 0, 10, 10, 1, 3, 0, 10, 2, 0, 4, 6, 10, + 2147483647, 2147483647}; + REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); + } + { + // bad final sentinal + int32_t data[] = {9, 0, 0, 10, 10, 1, 2, 0, 10, 2, 0, 4, 6, 10, + 2147483647, -1}; + REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); + } + { + // bad row sentinal + int32_t data[] = {9, 0, 0, 10, 10, 1, 2, 0, 10, 2, 0, 4, 6, 10, + -1, 2147483647}; + REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); + } + { + // starts with empty yspan + int32_t data[] = {12, 0, 0, 10, 10, 2, 2, -5, 0, 0, 2147483647, 10, + 2, 0, 4, 6, 10, 2147483647, 2147483647}; + REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); + } + { + // ends with empty yspan + int32_t data[] = {12, 0, 0, 10, 10, 2, 2, 0, 10, 2, 0, 4, 6, 10, + 2147483647, 15, 0, 2147483647, 2147483647}; + REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); + } + { + // y intervals out of order + int32_t data[] = {19, 0, -20, 30, 10, 3, 4, 0, 10, 2, 0, 10, 20, 30, + 2147483647, -20, 0, 2147483647, -10, 2, 0, 10, 20, 30, + 2147483647, 2147483647}; + REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); + } + { + // x intervals out of order + int32_t data[] = {9, 0, 0, 10, 10, 1, 2, 0, 10, 2, 6, 10, 0, 4, + 2147483647, 2147483647}; + REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); + } +}
diff --git a/src/third_party/skia/tests/RenderTargetContextTest.cpp b/src/third_party/skia/tests/RenderTargetContextTest.cpp new file mode 100644 index 0000000..311643e --- /dev/null +++ b/src/third_party/skia/tests/RenderTargetContextTest.cpp
@@ -0,0 +1,78 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// This is a GPU-backend specific test. + +#include "Test.h" + +// MDB TODO: the early instantiation of the renderTargetContext's backing GrRenderTargetProxy +// mixes this test up. Re-enable once backing GPU resources are distributed by MDB at flush time. +#if 0 + +#if SK_SUPPORT_GPU +#include "GrTextureProxy.h" +#include "GrRenderTargetContext.h" + +static const int kSize = 64; + +static sk_sp<GrRenderTargetContext> get_rtc(GrContext* ctx) { + return ctx->makeDeferredRenderTargetContext(SkBackingFit::kExact, + kSize, kSize, + kRGBA_8888_GrPixelConfig, nullptr); +} + +static void check_is_wrapped_status(skiatest::Reporter* reporter, + GrRenderTargetContext* rtCtx, + bool wrappedExpectation) { + REPORTER_ASSERT(reporter, rtCtx->isWrapped_ForTesting() == wrappedExpectation); + + GrTextureProxy* tProxy = rtCtx->asTextureProxy(); + REPORTER_ASSERT(reporter, tProxy); + + REPORTER_ASSERT(reporter, tProxy->isWrapped_ForTesting() == wrappedExpectation); +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(RenderTargetContextTest, reporter, ctxInfo) { + GrContext* ctx = ctxInfo.grContext(); + + // Calling instantiate on a GrRenderTargetContext's textureProxy also instantiates the + // GrRenderTargetContext + { + sk_sp<GrRenderTargetContext> rtCtx(get_rtc(ctx)); + + check_is_wrapped_status(reporter, rtCtx.get(), false); + + GrTextureProxy* tProxy = rtCtx->asTextureProxy(); + REPORTER_ASSERT(reporter, tProxy); + + REPORTER_ASSERT(reporter, tProxy->instantiate(ctx->resourceProvider())); + + check_is_wrapped_status(reporter, rtCtx.get(), true); + } + + // readPixels switches a deferred rtCtx to wrapped + { + sk_sp<GrRenderTargetContext> rtCtx(get_rtc(ctx)); + + check_is_wrapped_status(reporter, rtCtx.get(), false); + + SkImageInfo dstInfo = SkImageInfo::MakeN32Premul(kSize, kSize); + SkAutoTMalloc<uint32_t> dstBuffer(kSize * kSize); + static const size_t kRowBytes = sizeof(uint32_t) * kSize; + + bool result = rtCtx->readPixels(dstInfo, dstBuffer.get(), kRowBytes, 0, 0); + REPORTER_ASSERT(reporter, result); + + check_is_wrapped_status(reporter, rtCtx.get(), true); + } + + // TODO: in a future world we should be able to add a test that the majority of + // GrRenderTargetContext calls do not force the instantiation of a deferred + // GrRenderTargetContext +} +#endif +#endif
diff --git a/src/third_party/skia/tests/ResourceAllocatorTest.cpp b/src/third_party/skia/tests/ResourceAllocatorTest.cpp new file mode 100644 index 0000000..2398d68 --- /dev/null +++ b/src/third_party/skia/tests/ResourceAllocatorTest.cpp
@@ -0,0 +1,51 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// Include here to ensure SK_SUPPORT_GPU is set correctly before it is examined. +#include "SkTypes.h" + +#if SK_SUPPORT_GPU +#include "Test.h" + +#include "GrResourceAllocator.h" +#include "GrSurfaceProxyPriv.h" +#include "GrTextureProxy.h" + +// Basic test that two proxies with overlapping intervals and compatible descriptors are +// assigned different GrSurfaces. +static void overlap_test(skiatest::Reporter* reporter, GrResourceProvider* resourceProvider) { + GrSurfaceDesc desc; + desc.fConfig = kRGBA_8888_GrPixelConfig; + desc.fWidth = 64; + desc.fHeight = 64; + + sk_sp<GrSurfaceProxy> p1 = GrSurfaceProxy::MakeDeferred(resourceProvider, desc, + SkBackingFit::kApprox, + SkBudgeted::kNo); + sk_sp<GrSurfaceProxy> p2 = GrSurfaceProxy::MakeDeferred(resourceProvider, desc, + SkBackingFit::kApprox, + SkBudgeted::kNo); + + GrResourceAllocator alloc(resourceProvider); + + alloc.addInterval(p1.get(), 0, 4); + alloc.addInterval(p2.get(), 1, 2); + + alloc.assign(); + + REPORTER_ASSERT(reporter, p1->priv().peekSurface()); + REPORTER_ASSERT(reporter, p2->priv().peekSurface()); + REPORTER_ASSERT(reporter, p1->underlyingUniqueID() != p2->underlyingUniqueID()); +} + +DEF_GPUTEST_FOR_ALL_CONTEXTS(ResourceAllocatorTest, reporter, ctxInfo) { + GrResourceProvider* resourceProvider = ctxInfo.grContext()->resourceProvider(); + + overlap_test(reporter, resourceProvider); +} + +#endif
diff --git a/src/third_party/skia/tests/ResourceCacheTest.cpp b/src/third_party/skia/tests/ResourceCacheTest.cpp index f9f94d1..3cfa157 100644 --- a/src/third_party/skia/tests/ResourceCacheTest.cpp +++ b/src/third_party/skia/tests/ResourceCacheTest.cpp
@@ -5,11 +5,26 @@ * found in the LICENSE file. */ +// Include here to ensure SK_SUPPORT_GPU is set correctly before it is examined. +#include "SkTypes.h" + #if SK_SUPPORT_GPU +#include <thread> +#include "GrContext.h" +#include "GrContextPriv.h" +#include "GrContextFactory.h" +#include "GrGpu.h" +#include "GrGpuResourceCacheAccess.h" +#include "GrGpuResourcePriv.h" +#include "GrResourceCache.h" +#include "GrResourceProvider.h" +#include "GrTest.h" +#include "GrTexture.h" #include "SkCanvas.h" -#include "GrContextFactory.h" -#include "GrResourceCache.h" +#include "SkGr.h" +#include "SkMessageBus.h" +#include "SkMipMap.h" #include "SkSurface.h" #include "Test.h" @@ -17,9 +32,17 @@ static const int gHeight = 480; //////////////////////////////////////////////////////////////////////////////// -static void test_cache(skiatest::Reporter* reporter, - GrContext* context, - SkCanvas* canvas) { +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ResourceCacheCache, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + GrSurfaceDesc desc; + desc.fConfig = kRGBA_8888_GrPixelConfig; + desc.fFlags = kRenderTarget_GrSurfaceFlag; + desc.fWidth = gWidth; + desc.fHeight = gHeight; + SkImageInfo info = SkImageInfo::MakeN32Premul(gWidth, gHeight); + auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info)); + SkCanvas* canvas = surface->getCanvas(); + const SkIRect size = SkIRect::MakeWH(gWidth, gHeight); SkBitmap src; @@ -28,7 +51,7 @@ size_t srcSize = src.getSize(); size_t initialCacheSize; - context->getResourceCacheUsage(NULL, &initialCacheSize); + context->getResourceCacheUsage(nullptr, &initialCacheSize); int oldMaxNum; size_t oldMaxBytes; @@ -44,13 +67,13 @@ for (int i = 0; i < 100; ++i) { canvas->drawBitmap(src, 0, 0); - canvas->readPixels(size, &readback); + canvas->readPixels(readback, 0, 0); // "modify" the src texture src.notifyPixelsChanged(); size_t curCacheSize; - context->getResourceCacheUsage(NULL, &curCacheSize); + context->getResourceCacheUsage(nullptr, &curCacheSize); // we should never go over the size limit REPORTER_ASSERT(reporter, curCacheSize <= maxCacheSize); @@ -59,28 +82,214 @@ context->setResourceCacheLimits(oldMaxNum, oldMaxBytes); } -class TestResource : public GrGpuResource { - static const size_t kDefaultSize = 100; +static bool is_rendering_and_not_angle_es3(sk_gpu_test::GrContextFactory::ContextType type) { + if (type == sk_gpu_test::GrContextFactory::kANGLE_D3D11_ES3_ContextType || + type == sk_gpu_test::GrContextFactory::kANGLE_GL_ES3_ContextType) { + return false; + } + return sk_gpu_test::GrContextFactory::IsRenderingContext(type); +} -public: - SK_DECLARE_INST_COUNT(TestResource); - TestResource(GrGpu* gpu, size_t size = kDefaultSize) - : INHERITED(gpu, false) - , fCache(NULL) - , fToDelete(NULL) - , fSize(size) { - ++fAlive; - this->registerWithCache(); +// This currently fails on ES3 ANGLE contexts +DEF_GPUTEST_FOR_CONTEXTS(ResourceCacheStencilBuffers, &is_rendering_and_not_angle_es3, reporter, + ctxInfo) { + GrContext* context = ctxInfo.grContext(); + GrSurfaceDesc smallDesc; + smallDesc.fFlags = kRenderTarget_GrSurfaceFlag; + smallDesc.fConfig = kRGBA_8888_GrPixelConfig; + smallDesc.fWidth = 4; + smallDesc.fHeight = 4; + smallDesc.fSampleCnt = 0; + + if (context->caps()->avoidStencilBuffers()) { + return; + } + GrResourceProvider* resourceProvider = context->resourceProvider(); + // Test that two budgeted RTs with the same desc share a stencil buffer. + sk_sp<GrTexture> smallRT0(resourceProvider->createTexture(smallDesc, SkBudgeted::kYes)); + if (smallRT0 && smallRT0->asRenderTarget()) { + resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget()); } - ~TestResource() { - --fAlive; - if (fToDelete) { - // Breaks our little 2-element cycle below. - fToDelete->setDeleteWhenDestroyed(NULL, NULL); - fCache->deleteResource(fToDelete->getCacheEntry()); + sk_sp<GrTexture> smallRT1(resourceProvider->createTexture(smallDesc, SkBudgeted::kYes)); + if (smallRT1 && smallRT1->asRenderTarget()) { + resourceProvider->attachStencilAttachment(smallRT1->asRenderTarget()); + } + + REPORTER_ASSERT(reporter, + smallRT0 && smallRT1 && + smallRT0->asRenderTarget() && smallRT1->asRenderTarget() && + resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget()) == + resourceProvider->attachStencilAttachment(smallRT1->asRenderTarget())); + + // An unbudgeted RT with the same desc should also share. + sk_sp<GrTexture> smallRT2(resourceProvider->createTexture(smallDesc, SkBudgeted::kNo)); + if (smallRT2 && smallRT2->asRenderTarget()) { + resourceProvider->attachStencilAttachment(smallRT2->asRenderTarget()); + } + REPORTER_ASSERT(reporter, + smallRT0 && smallRT2 && + smallRT0->asRenderTarget() && smallRT2->asRenderTarget() && + resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget()) == + resourceProvider->attachStencilAttachment(smallRT2->asRenderTarget())); + + // An RT with a much larger size should not share. + GrSurfaceDesc bigDesc; + bigDesc.fFlags = kRenderTarget_GrSurfaceFlag; + bigDesc.fConfig = kRGBA_8888_GrPixelConfig; + bigDesc.fWidth = 400; + bigDesc.fHeight = 200; + bigDesc.fSampleCnt = 0; + sk_sp<GrTexture> bigRT(resourceProvider->createTexture(bigDesc, SkBudgeted::kNo)); + if (bigRT && bigRT->asRenderTarget()) { + resourceProvider->attachStencilAttachment(bigRT->asRenderTarget()); + } + REPORTER_ASSERT(reporter, + smallRT0 && bigRT && + smallRT0->asRenderTarget() && bigRT->asRenderTarget() && + resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget()) != + resourceProvider->attachStencilAttachment(bigRT->asRenderTarget())); + + int supportedSampleCount = context->caps()->getSampleCount(4, smallDesc.fConfig); + if (supportedSampleCount > 0) { + // An RT with a different sample count should not share. + GrSurfaceDesc smallMSAADesc = smallDesc; + smallMSAADesc.fSampleCnt = supportedSampleCount; + sk_sp<GrTexture> smallMSAART0(resourceProvider->createTexture(smallMSAADesc, + SkBudgeted::kNo)); + if (smallMSAART0 && smallMSAART0->asRenderTarget()) { + resourceProvider->attachStencilAttachment(smallMSAART0->asRenderTarget()); } - this->release(); +#ifdef SK_BUILD_FOR_ANDROID + if (!smallMSAART0) { + // The nexus player seems to fail to create MSAA textures. + return; + } +#endif + REPORTER_ASSERT(reporter, + smallRT0 && smallMSAART0 && + smallRT0->asRenderTarget() && smallMSAART0->asRenderTarget() && + resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget()) != + resourceProvider->attachStencilAttachment(smallMSAART0->asRenderTarget())); + // A second MSAA RT should share with the first MSAA RT. + sk_sp<GrTexture> smallMSAART1(resourceProvider->createTexture(smallMSAADesc, + SkBudgeted::kNo)); + if (smallMSAART1 && smallMSAART1->asRenderTarget()) { + resourceProvider->attachStencilAttachment(smallMSAART1->asRenderTarget()); + } + REPORTER_ASSERT(reporter, + smallMSAART0 && smallMSAART1 && + smallMSAART0->asRenderTarget() && + smallMSAART1->asRenderTarget() && + resourceProvider->attachStencilAttachment(smallMSAART0->asRenderTarget()) == + resourceProvider->attachStencilAttachment(smallMSAART1->asRenderTarget())); + // But not one with a larger sample count should not. (Also check that the request for 4 + // samples didn't get rounded up to >= 8 or else they could share.). + supportedSampleCount = context->caps()->getSampleCount(8, smallDesc.fConfig); + if (supportedSampleCount != smallMSAADesc.fSampleCnt && + smallMSAART0 && smallMSAART0->asRenderTarget()) { + smallMSAADesc.fSampleCnt = supportedSampleCount; + smallMSAART1 = resourceProvider->createTexture(smallMSAADesc, SkBudgeted::kNo); + sk_sp<GrTexture> smallMSAART1( + resourceProvider->createTexture(smallMSAADesc, SkBudgeted::kNo)); + if (smallMSAART1 && smallMSAART1->asRenderTarget()) { + resourceProvider->attachStencilAttachment(smallMSAART1->asRenderTarget()); + } + REPORTER_ASSERT(reporter, + smallMSAART0 && smallMSAART1 && + smallMSAART0->asRenderTarget() && + smallMSAART1->asRenderTarget() && + resourceProvider->attachStencilAttachment(smallMSAART0->asRenderTarget()) != + resourceProvider->attachStencilAttachment(smallMSAART1->asRenderTarget())); + } + } +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ResourceCacheWrappedResources, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + GrGpu* gpu = context->getGpu(); + // this test is only valid for GL + if (!gpu || !gpu->glContextForTesting()) { + return; + } + + GrBackendObject texHandles[2]; + static const int kW = 100; + static const int kH = 100; + + texHandles[0] = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kRGBA_8888_GrPixelConfig); + texHandles[1] = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kRGBA_8888_GrPixelConfig); + + context->resetContext(); + + GrBackendTexture backendTex1 = GrTest::CreateBackendTexture(context->contextPriv().getBackend(), + kW, + kH, + kRGBA_8888_GrPixelConfig, + texHandles[0]); + sk_sp<GrTexture> borrowed(context->resourceProvider()->wrapBackendTexture( + backendTex1, kTopLeft_GrSurfaceOrigin, kBorrow_GrWrapOwnership)); + + GrBackendTexture backendTex2 = GrTest::CreateBackendTexture(context->contextPriv().getBackend(), + kW, + kH, + kRGBA_8888_GrPixelConfig, + texHandles[1]); + sk_sp<GrTexture> adopted(context->resourceProvider()->wrapBackendTexture( + backendTex2, kTopLeft_GrSurfaceOrigin, kAdopt_GrWrapOwnership)); + + REPORTER_ASSERT(reporter, borrowed != nullptr && adopted != nullptr); + if (!borrowed || !adopted) { + return; + } + + borrowed.reset(nullptr); + adopted.reset(nullptr); + + context->flush(); + + bool borrowedIsAlive = gpu->isTestingOnlyBackendTexture(texHandles[0]); + bool adoptedIsAlive = gpu->isTestingOnlyBackendTexture(texHandles[1]); + + REPORTER_ASSERT(reporter, borrowedIsAlive); + REPORTER_ASSERT(reporter, !adoptedIsAlive); + + gpu->deleteTestingOnlyBackendTexture(texHandles[0], !borrowedIsAlive); + gpu->deleteTestingOnlyBackendTexture(texHandles[1], !adoptedIsAlive); + + context->resetContext(); +} + +class TestResource : public GrGpuResource { + enum ScratchConstructor { kScratchConstructor }; +public: + static const size_t kDefaultSize = 100; + + /** Property that distinctly categorizes the resource. + * For example, textures have width, height, ... */ + enum SimulatedProperty { kA_SimulatedProperty, kB_SimulatedProperty }; + + TestResource(GrGpu* gpu, SkBudgeted budgeted = SkBudgeted::kYes, size_t size = kDefaultSize) + : INHERITED(gpu) + , fToDelete(nullptr) + , fSize(size) + , fProperty(kA_SimulatedProperty) + , fIsScratch(false) { + ++fNumAlive; + this->registerWithCache(budgeted); + } + + static TestResource* CreateScratch(GrGpu* gpu, SkBudgeted budgeted, + SimulatedProperty property) { + return new TestResource(gpu, budgeted, property, kScratchConstructor); + } + static TestResource* CreateWrapped(GrGpu* gpu, size_t size = kDefaultSize) { + return new TestResource(gpu, size); + } + + ~TestResource() override { + --fNumAlive; + SkSafeUnref(fToDelete); } void setSize(size_t size) { @@ -88,222 +297,1447 @@ this->didChangeGpuMemorySize(); } - size_t gpuMemorySize() const SK_OVERRIDE { return fSize; } + static int NumAlive() { return fNumAlive; } - static int alive() { return fAlive; } - - void setDeleteWhenDestroyed(GrResourceCache* cache, TestResource* resource) { - fCache = cache; - fToDelete = resource; + void setUnrefWhenDestroyed(TestResource* resource) { + SkRefCnt_SafeAssign(fToDelete, resource); } + static void ComputeScratchKey(SimulatedProperty property, GrScratchKey* key) { + static GrScratchKey::ResourceType t = GrScratchKey::GenerateResourceType(); + GrScratchKey::Builder builder(key, t, kScratchKeyFieldCnt); + for (int i = 0; i < kScratchKeyFieldCnt; ++i) { + builder[i] = static_cast<uint32_t>(i + property); + } + } + + static size_t ExpectedScratchKeySize() { + return sizeof(uint32_t) * (kScratchKeyFieldCnt + GrScratchKey::kMetaDataCnt); + } private: - GrResourceCache* fCache; + static const int kScratchKeyFieldCnt = 6; + + TestResource(GrGpu* gpu, SkBudgeted budgeted, SimulatedProperty property, ScratchConstructor) + : INHERITED(gpu) + , fToDelete(nullptr) + , fSize(kDefaultSize) + , fProperty(property) + , fIsScratch(true) { + ++fNumAlive; + this->registerWithCache(budgeted); + } + + // Constructor for simulating resources that wrap backend objects. + TestResource(GrGpu* gpu, size_t size) + : INHERITED(gpu) + , fToDelete(nullptr) + , fSize(size) + , fProperty(kA_SimulatedProperty) + , fIsScratch(false) { + ++fNumAlive; + this->registerWithCacheWrapped(); + } + + void computeScratchKey(GrScratchKey* key) const override { + if (fIsScratch) { + ComputeScratchKey(fProperty, key); + } + } + + size_t onGpuMemorySize() const override { return fSize; } + TestResource* fToDelete; size_t fSize; - static int fAlive; - + static int fNumAlive; + SimulatedProperty fProperty; + bool fIsScratch; typedef GrGpuResource INHERITED; }; -int TestResource::fAlive = 0; +int TestResource::fNumAlive = 0; -static void test_purge_invalidated(skiatest::Reporter* reporter, GrContext* context) { - GrCacheID::Domain domain = GrCacheID::GenerateDomain(); - GrCacheID::Key keyData; - keyData.fData64[0] = 5; - keyData.fData64[1] = 18; - GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType(); - GrResourceKey key(GrCacheID(domain, keyData), t, 0); +class Mock { +public: + Mock(int maxCnt, size_t maxBytes) { + fContext.reset(GrContext::Create(kMock_GrBackend, (GrBackendContext) nullptr)); + SkASSERT(fContext); + fContext->setResourceCacheLimits(maxCnt, maxBytes); + GrResourceCache* cache = fContext->getResourceCache(); + cache->purgeAllUnlocked(); + SkASSERT(0 == cache->getResourceCount() && 0 == cache->getResourceBytes()); + } - GrResourceCache cache(5, 30000); + GrResourceCache* cache() { return fContext->getResourceCache(); } - // Add two resources with the same key that delete each other from the cache when destroyed. + GrContext* context() { return fContext.get(); } + +private: + sk_sp<GrContext> fContext; +}; + +static void test_no_key(skiatest::Reporter* reporter) { + Mock mock(10, 30000); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); + + // Create a bunch of resources with no keys TestResource* a = new TestResource(context->getGpu()); TestResource* b = new TestResource(context->getGpu()); - cache.addResource(key, a); - cache.addResource(key, b); - // Circle back. - a->setDeleteWhenDestroyed(&cache, b); - b->setDeleteWhenDestroyed(&cache, a); + TestResource* c = new TestResource(context->getGpu()); + TestResource* d = new TestResource(context->getGpu()); + a->setSize(11); + b->setSize(12); + c->setSize(13); + d->setSize(14); + + REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive()); + REPORTER_ASSERT(reporter, 4 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() + c->gpuMemorySize() + + d->gpuMemorySize() == cache->getResourceBytes()); + + // Should be safe to purge without deleting the resources since we still have refs. + cache->purgeAllUnlocked(); + REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive()); + + // Since the resources have neither unique nor scratch keys, delete immediately upon unref. + + a->unref(); + REPORTER_ASSERT(reporter, 3 == TestResource::NumAlive()); + REPORTER_ASSERT(reporter, 3 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, b->gpuMemorySize() + c->gpuMemorySize() + d->gpuMemorySize() == + cache->getResourceBytes()); + + c->unref(); + REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, b->gpuMemorySize() + d->gpuMemorySize() == + cache->getResourceBytes()); + + d->unref(); + REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, b->gpuMemorySize() == cache->getResourceBytes()); + + b->unref(); + REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); +} + +// Each integer passed as a template param creates a new domain. +template <int> +static void make_unique_key(GrUniqueKey* key, int data, const char* tag = nullptr) { + static GrUniqueKey::Domain d = GrUniqueKey::GenerateDomain(); + GrUniqueKey::Builder builder(key, d, 1, tag); + builder[0] = data; +} + +static void test_budgeting(skiatest::Reporter* reporter) { + Mock mock(10, 300); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); + + GrUniqueKey uniqueKey; + make_unique_key<0>(&uniqueKey, 0); + + // Create a scratch, a unique, and a wrapped resource + TestResource* scratch = + TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, TestResource::kB_SimulatedProperty); + scratch->setSize(10); + TestResource* unique = new TestResource(context->getGpu()); + unique->setSize(11); + unique->resourcePriv().setUniqueKey(uniqueKey); + TestResource* wrapped = TestResource::CreateWrapped(context->getGpu()); + wrapped->setSize(12); + TestResource* unbudgeted = + new TestResource(context->getGpu(), SkBudgeted::kNo); + unbudgeted->setSize(13); + + // Make sure we can add a unique key to the wrapped resource + GrUniqueKey uniqueKey2; + make_unique_key<0>(&uniqueKey2, 1); + wrapped->resourcePriv().setUniqueKey(uniqueKey2); + GrGpuResource* wrappedViaKey = cache->findAndRefUniqueResource(uniqueKey2); + REPORTER_ASSERT(reporter, wrappedViaKey != nullptr); + + // Remove the extra ref we just added. + wrappedViaKey->unref(); + + // Make sure sizes are as we expect + REPORTER_ASSERT(reporter, 4 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() + + wrapped->gpuMemorySize() + unbudgeted->gpuMemorySize() == + cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() == + cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); + + // Our refs mean that the resources are non purgeable. + cache->purgeAllUnlocked(); + REPORTER_ASSERT(reporter, 4 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() + + wrapped->gpuMemorySize() + unbudgeted->gpuMemorySize() == + cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() == + cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); + + // Unreffing the wrapped resource should free it right away. + wrapped->unref(); + REPORTER_ASSERT(reporter, 3 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() + + unbudgeted->gpuMemorySize() == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); + + // Now try freeing the budgeted resources first + wrapped = TestResource::CreateWrapped(context->getGpu()); + scratch->setSize(12); + unique->unref(); + REPORTER_ASSERT(reporter, 11 == cache->getPurgeableBytes()); + cache->purgeAllUnlocked(); + REPORTER_ASSERT(reporter, 3 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + wrapped->gpuMemorySize() + + unbudgeted->gpuMemorySize() == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, scratch->gpuMemorySize() == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); + + scratch->unref(); + REPORTER_ASSERT(reporter, 12 == cache->getPurgeableBytes()); + cache->purgeAllUnlocked(); + REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, unbudgeted->gpuMemorySize() + wrapped->gpuMemorySize() == + cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); + + wrapped->unref(); + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, unbudgeted->gpuMemorySize() == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); + + unbudgeted->unref(); + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); +} + +static void test_unbudgeted(skiatest::Reporter* reporter) { + Mock mock(10, 30000); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); + + GrUniqueKey uniqueKey; + make_unique_key<0>(&uniqueKey, 0); + + TestResource* scratch; + TestResource* unique; + TestResource* wrapped; + TestResource* unbudgeted; + + // A large uncached or wrapped resource shouldn't evict anything. + scratch = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, + TestResource::kB_SimulatedProperty); + + scratch->setSize(10); + scratch->unref(); + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, 10 == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 10 == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 10 == cache->getPurgeableBytes()); + + unique = new TestResource(context->getGpu()); + unique->setSize(11); + unique->resourcePriv().setUniqueKey(uniqueKey); + unique->unref(); + REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes()); + + size_t large = 2 * cache->getResourceBytes(); + unbudgeted = new TestResource(context->getGpu(), SkBudgeted::kNo, large); + REPORTER_ASSERT(reporter, 3 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, 21 + large == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes()); + + unbudgeted->unref(); + REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes()); + + wrapped = TestResource::CreateWrapped(context->getGpu(), large); + REPORTER_ASSERT(reporter, 3 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, 21 + large == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes()); + + wrapped->unref(); + REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes()); + + cache->purgeAllUnlocked(); + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); +} + +// This method can't be static because it needs to friended in GrGpuResource::CacheAccess. +void test_unbudgeted_to_scratch(skiatest::Reporter* reporter); +/*static*/ void test_unbudgeted_to_scratch(skiatest::Reporter* reporter) { + Mock mock(10, 300); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); + + TestResource* resource = + TestResource::CreateScratch(context->getGpu(), SkBudgeted::kNo, + TestResource::kA_SimulatedProperty); + GrScratchKey key; + TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &key); + + size_t size = resource->gpuMemorySize(); + for (int i = 0; i < 2; ++i) { + // Since this resource is unbudgeted, it should not be reachable as scratch. + REPORTER_ASSERT(reporter, resource->resourcePriv().getScratchKey() == key); + REPORTER_ASSERT(reporter, !resource->cacheAccess().isScratch()); + REPORTER_ASSERT(reporter, SkBudgeted::kNo == resource->resourcePriv().isBudgeted()); + REPORTER_ASSERT(reporter, nullptr == cache->findAndRefScratchResource(key, TestResource::kDefaultSize, 0)); + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, size == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); + + // Once it is unrefed, it should become available as scratch. + resource->unref(); + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, size == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, size == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, size == cache->getPurgeableBytes()); + resource = static_cast<TestResource*>(cache->findAndRefScratchResource(key, TestResource::kDefaultSize, 0)); + REPORTER_ASSERT(reporter, resource); + REPORTER_ASSERT(reporter, resource->resourcePriv().getScratchKey() == key); + REPORTER_ASSERT(reporter, resource->cacheAccess().isScratch()); + REPORTER_ASSERT(reporter, SkBudgeted::kYes == resource->resourcePriv().isBudgeted()); + + if (0 == i) { + // If made unbudgeted, it should return to original state: ref'ed and unbudgeted. Try + // the above tests again. + resource->resourcePriv().makeUnbudgeted(); + } else { + // After the second time around, try removing the scratch key + resource->resourcePriv().removeScratchKey(); + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, size == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, size == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); + REPORTER_ASSERT(reporter, !resource->resourcePriv().getScratchKey().isValid()); + REPORTER_ASSERT(reporter, !resource->cacheAccess().isScratch()); + REPORTER_ASSERT(reporter, SkBudgeted::kYes == resource->resourcePriv().isBudgeted()); + + // now when it is unrefed it should die since it has no key. + resource->unref(); + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); + } + } +} + +static void test_duplicate_scratch_key(skiatest::Reporter* reporter) { + Mock mock(5, 30000); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); + + // Create two resources that have the same scratch key. + TestResource* a = TestResource::CreateScratch(context->getGpu(), + SkBudgeted::kYes, + TestResource::kB_SimulatedProperty); + TestResource* b = TestResource::CreateScratch(context->getGpu(), + SkBudgeted::kYes, + TestResource::kB_SimulatedProperty); + a->setSize(11); + b->setSize(12); + GrScratchKey scratchKey1; + TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey1); + // Check for negative case consistency. (leaks upon test failure.) + REPORTER_ASSERT(reporter, nullptr == cache->findAndRefScratchResource(scratchKey1, TestResource::kDefaultSize, 0)); + + GrScratchKey scratchKey; + TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey); + + // Scratch resources are registered with GrResourceCache just by existing. There are 2. + REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->countScratchEntriesForKey(scratchKey));) + REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() == + cache->getResourceBytes()); + + // Our refs mean that the resources are non purgeable. + cache->purgeAllUnlocked(); + REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); + + // Unref but don't purge + a->unref(); + b->unref(); + REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->countScratchEntriesForKey(scratchKey));) + + // Purge again. This time resources should be purgeable. + cache->purgeAllUnlocked(); + REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));) +} + +static void test_remove_scratch_key(skiatest::Reporter* reporter) { + Mock mock(5, 30000); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); + + // Create two resources that have the same scratch key. + TestResource* a = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, + TestResource::kB_SimulatedProperty); + TestResource* b = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, + TestResource::kB_SimulatedProperty); a->unref(); b->unref(); - // Add a third independent resource also with the same key. - GrGpuResource* r = new TestResource(context->getGpu()); - cache.addResource(key, r); - r->unref(); + GrScratchKey scratchKey; + // Ensure that scratch key lookup is correct for negative case. + TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey); + // (following leaks upon test failure). + REPORTER_ASSERT(reporter, cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0) == nullptr); - // Invalidate all three, all three should be purged and destroyed. - REPORTER_ASSERT(reporter, 3 == TestResource::alive()); - const GrResourceInvalidatedMessage msg = { key }; - SkMessageBus<GrResourceInvalidatedMessage>::Post(msg); - cache.purgeAsNeeded(); - REPORTER_ASSERT(reporter, 0 == TestResource::alive()); + // Scratch resources are registered with GrResourceCache just by existing. There are 2. + TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey); + REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->countScratchEntriesForKey(scratchKey));) + REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); + + // Find the first resource and remove its scratch key + GrGpuResource* find; + find = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0); + find->resourcePriv().removeScratchKey(); + // It's still alive, but not cached by scratch key anymore + REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + SkDEBUGCODE(REPORTER_ASSERT(reporter, 1 == cache->countScratchEntriesForKey(scratchKey));) + REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); + + // The cache should immediately delete it when it's unrefed since it isn't accessible. + find->unref(); + REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); + SkDEBUGCODE(REPORTER_ASSERT(reporter, 1 == cache->countScratchEntriesForKey(scratchKey));) + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + + // Repeat for the second resource. + find = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0); + find->resourcePriv().removeScratchKey(); + REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); + SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));) + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + + // Should be able to call this multiple times with no problem. + find->resourcePriv().removeScratchKey(); + REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); + SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));) + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + + find->unref(); + REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); + SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));) + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); } -static void test_cache_delete_on_destruction(skiatest::Reporter* reporter, - GrContext* context) { - GrCacheID::Domain domain = GrCacheID::GenerateDomain(); - GrCacheID::Key keyData; - keyData.fData64[0] = 5; - keyData.fData64[1] = 0; - GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType(); +static void test_scratch_key_consistency(skiatest::Reporter* reporter) { + Mock mock(5, 30000); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); - GrResourceKey key(GrCacheID(domain, keyData), t, 0); + // Create two resources that have the same scratch key. + TestResource* a = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, + TestResource::kB_SimulatedProperty); + TestResource* b = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, + TestResource::kB_SimulatedProperty); + a->unref(); + b->unref(); - { - { - GrResourceCache cache(3, 30000); - TestResource* a = new TestResource(context->getGpu()); - TestResource* b = new TestResource(context->getGpu()); - cache.addResource(key, a); - cache.addResource(key, b); + GrScratchKey scratchKey; + // Ensure that scratch key comparison and assignment is consistent. + GrScratchKey scratchKey1; + TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey1); + GrScratchKey scratchKey2; + TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey2); + REPORTER_ASSERT(reporter, scratchKey1.size() == TestResource::ExpectedScratchKeySize()); + REPORTER_ASSERT(reporter, scratchKey1 != scratchKey2); + REPORTER_ASSERT(reporter, scratchKey2 != scratchKey1); + scratchKey = scratchKey1; + REPORTER_ASSERT(reporter, scratchKey.size() == TestResource::ExpectedScratchKeySize()); + REPORTER_ASSERT(reporter, scratchKey1 == scratchKey); + REPORTER_ASSERT(reporter, scratchKey == scratchKey1); + REPORTER_ASSERT(reporter, scratchKey2 != scratchKey); + REPORTER_ASSERT(reporter, scratchKey != scratchKey2); + scratchKey = scratchKey2; + REPORTER_ASSERT(reporter, scratchKey.size() == TestResource::ExpectedScratchKeySize()); + REPORTER_ASSERT(reporter, scratchKey1 != scratchKey); + REPORTER_ASSERT(reporter, scratchKey != scratchKey1); + REPORTER_ASSERT(reporter, scratchKey2 == scratchKey); + REPORTER_ASSERT(reporter, scratchKey == scratchKey2); - a->setDeleteWhenDestroyed(&cache, b); - b->setDeleteWhenDestroyed(&cache, a); + // Ensure that scratch key lookup is correct for negative case. + TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey); + // (following leaks upon test failure). + REPORTER_ASSERT(reporter, cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0) == nullptr); - a->unref(); - b->unref(); - REPORTER_ASSERT(reporter, 2 == TestResource::alive()); - } - REPORTER_ASSERT(reporter, 0 == TestResource::alive()); - } - { - GrResourceCache cache(3, 30000); - TestResource* a = new TestResource(context->getGpu()); - TestResource* b = new TestResource(context->getGpu()); - cache.addResource(key, a); - cache.addResource(key, b); + // Find the first resource with a scratch key and a copy of a scratch key. + TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey); + GrGpuResource* find = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0); + REPORTER_ASSERT(reporter, find != nullptr); + find->unref(); - a->setDeleteWhenDestroyed(&cache, b); - b->setDeleteWhenDestroyed(&cache, a); + scratchKey2 = scratchKey; + find = cache->findAndRefScratchResource(scratchKey2, TestResource::kDefaultSize, 0); + REPORTER_ASSERT(reporter, find != nullptr); + REPORTER_ASSERT(reporter, find == a || find == b); - a->unref(); - b->unref(); - - cache.deleteResource(a->getCacheEntry()); - - REPORTER_ASSERT(reporter, 0 == TestResource::alive()); - } + GrGpuResource* find2 = cache->findAndRefScratchResource(scratchKey2, TestResource::kDefaultSize, 0); + REPORTER_ASSERT(reporter, find2 != nullptr); + REPORTER_ASSERT(reporter, find2 == a || find2 == b); + REPORTER_ASSERT(reporter, find2 != find); + find2->unref(); + find->unref(); } -static void test_resource_size_changed(skiatest::Reporter* reporter, - GrContext* context) { - GrCacheID::Domain domain = GrCacheID::GenerateDomain(); - GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType(); +static void test_duplicate_unique_key(skiatest::Reporter* reporter) { + Mock mock(5, 30000); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); - GrCacheID::Key key1Data; - key1Data.fData64[0] = 0; - key1Data.fData64[1] = 0; - GrResourceKey key1(GrCacheID(domain, key1Data), t, 0); + GrUniqueKey key; + make_unique_key<0>(&key, 0); - GrCacheID::Key key2Data; - key2Data.fData64[0] = 1; - key2Data.fData64[1] = 0; - GrResourceKey key2(GrCacheID(domain, key2Data), t, 0); + // Create two resources that we will attempt to register with the same unique key. + TestResource* a = new TestResource(context->getGpu()); + a->setSize(11); + + // Set key on resource a. + a->resourcePriv().setUniqueKey(key); + REPORTER_ASSERT(reporter, a == cache->findAndRefUniqueResource(key)); + a->unref(); + + // Make sure that redundantly setting a's key works. + a->resourcePriv().setUniqueKey(key); + REPORTER_ASSERT(reporter, a == cache->findAndRefUniqueResource(key)); + a->unref(); + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); + + // Create resource b and set the same key. It should replace a's unique key cache entry. + TestResource* b = new TestResource(context->getGpu()); + b->setSize(12); + b->resourcePriv().setUniqueKey(key); + REPORTER_ASSERT(reporter, b == cache->findAndRefUniqueResource(key)); + b->unref(); + + // Still have two resources because a is still reffed. + REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + + a->unref(); + // Now a should be gone. + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, b->gpuMemorySize() == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); + + // Now replace b with c, but make sure c can start with one unique key and change it to b's key. + // Also make b be unreffed when replacement occurs. + b->unref(); + TestResource* c = new TestResource(context->getGpu()); + GrUniqueKey differentKey; + make_unique_key<0>(&differentKey, 1); + c->setSize(13); + c->resourcePriv().setUniqueKey(differentKey); + REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, b->gpuMemorySize() + c->gpuMemorySize() == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + // c replaces b and b should be immediately purged. + c->resourcePriv().setUniqueKey(key); + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); + + // c shouldn't be purged because it is ref'ed. + cache->purgeAllUnlocked(); + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); + + // Drop the ref on c, it should be kept alive because it has a unique key. + c->unref(); + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); + + // Verify that we can find c, then remove its unique key. It should get purged immediately. + REPORTER_ASSERT(reporter, c == cache->findAndRefUniqueResource(key)); + c->resourcePriv().removeUniqueKey(); + c->unref(); + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); + + { + GrUniqueKey key2; + make_unique_key<0>(&key2, 0); + sk_sp<TestResource> d(new TestResource(context->getGpu())); + int foo = 4132; + key2.setCustomData(SkData::MakeWithCopy(&foo, sizeof(foo))); + d->resourcePriv().setUniqueKey(key2); + } + + GrUniqueKey key3; + make_unique_key<0>(&key3, 0); + sk_sp<GrGpuResource> d2(cache->findAndRefUniqueResource(key3)); + REPORTER_ASSERT(reporter, *(int*) d2->getUniqueKey().getCustomData()->data() == 4132); +} + +static void test_purge_invalidated(skiatest::Reporter* reporter) { + Mock mock(5, 30000); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); + + GrUniqueKey key1, key2, key3; + make_unique_key<0>(&key1, 1); + make_unique_key<0>(&key2, 2); + make_unique_key<0>(&key3, 3); + + // Add three resources to the cache. Only c is usable as scratch. + TestResource* a = new TestResource(context->getGpu()); + TestResource* b = new TestResource(context->getGpu()); + TestResource* c = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, + TestResource::kA_SimulatedProperty); + a->resourcePriv().setUniqueKey(key1); + b->resourcePriv().setUniqueKey(key2); + c->resourcePriv().setUniqueKey(key3); + a->unref(); + // hold b until *after* the message is sent. + c->unref(); + + REPORTER_ASSERT(reporter, cache->hasUniqueKey(key1)); + REPORTER_ASSERT(reporter, cache->hasUniqueKey(key2)); + REPORTER_ASSERT(reporter, cache->hasUniqueKey(key3)); + REPORTER_ASSERT(reporter, 3 == TestResource::NumAlive()); + + typedef GrUniqueKeyInvalidatedMessage Msg; + typedef SkMessageBus<GrUniqueKeyInvalidatedMessage> Bus; + + // Invalidate two of the three, they should be purged and no longer accessible via their keys. + Bus::Post(Msg(key1)); + Bus::Post(Msg(key2)); + cache->purgeAsNeeded(); + // a should be deleted now, but we still have a ref on b. + REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key1)); + REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key2)); + REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + REPORTER_ASSERT(reporter, cache->hasUniqueKey(key3)); + + // Invalidate the third. + Bus::Post(Msg(key3)); + cache->purgeAsNeeded(); + // we still have a ref on b, c should be recycled as scratch. + REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key3)); + + // make b purgeable. It should be immediately deleted since it has no key. + b->unref(); + REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); + + // Make sure we actually get to c via it's scratch key, before we say goodbye. + GrScratchKey scratchKey; + TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey); + GrGpuResource* scratch = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0); + REPORTER_ASSERT(reporter, scratch == c); + SkSafeUnref(scratch); + + // Get rid of c. + cache->purgeAllUnlocked(); + scratch = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0); + REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, !scratch); + SkSafeUnref(scratch); +} + +static void test_cache_chained_purge(skiatest::Reporter* reporter) { + Mock mock(3, 30000); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); + + GrUniqueKey key1, key2; + make_unique_key<0>(&key1, 1); + make_unique_key<0>(&key2, 2); + + TestResource* a = new TestResource(context->getGpu()); + TestResource* b = new TestResource(context->getGpu()); + a->resourcePriv().setUniqueKey(key1); + b->resourcePriv().setUniqueKey(key2); + + // Make a cycle + a->setUnrefWhenDestroyed(b); + b->setUnrefWhenDestroyed(a); + + REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + + a->unref(); + b->unref(); + + REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + + cache->purgeAllUnlocked(); + REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + + // Break the cycle + a->setUnrefWhenDestroyed(nullptr); + REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + + cache->purgeAllUnlocked(); + REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); +} + +static void test_resource_size_changed(skiatest::Reporter* reporter) { + GrUniqueKey key1, key2; + make_unique_key<0>(&key1, 1); + make_unique_key<0>(&key2, 2); // Test changing resources sizes (both increase & decrease). { - GrResourceCache cache(2, 300); + Mock mock(3, 30000); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); TestResource* a = new TestResource(context->getGpu()); - a->setSize(100); // Test didChangeGpuMemorySize() when not in the cache. - cache.addResource(key1, a); + a->resourcePriv().setUniqueKey(key1); a->unref(); TestResource* b = new TestResource(context->getGpu()); - b->setSize(100); - cache.addResource(key2, b); + b->resourcePriv().setUniqueKey(key2); b->unref(); - REPORTER_ASSERT(reporter, 200 == cache.getCachedResourceBytes()); - REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount()); + REPORTER_ASSERT(reporter, 200 == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); + { + sk_sp<TestResource> find2( + static_cast<TestResource*>(cache->findAndRefUniqueResource(key2))); + find2->setSize(200); + sk_sp<TestResource> find1( + static_cast<TestResource*>(cache->findAndRefUniqueResource(key1))); + find1->setSize(50); + } - static_cast<TestResource*>(cache.find(key2))->setSize(200); - static_cast<TestResource*>(cache.find(key1))->setSize(50); - - REPORTER_ASSERT(reporter, 250 == cache.getCachedResourceBytes()); - REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount()); + REPORTER_ASSERT(reporter, 250 == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); } // Test increasing a resources size beyond the cache budget. { - GrResourceCache cache(2, 300); + Mock mock(2, 300); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); - TestResource* a = new TestResource(context->getGpu(), 100); - cache.addResource(key1, a); + TestResource* a = new TestResource(context->getGpu()); + a->setSize(100); + a->resourcePriv().setUniqueKey(key1); a->unref(); - TestResource* b = new TestResource(context->getGpu(), 100); - cache.addResource(key2, b); + TestResource* b = new TestResource(context->getGpu()); + b->setSize(100); + b->resourcePriv().setUniqueKey(key2); b->unref(); - REPORTER_ASSERT(reporter, 200 == cache.getCachedResourceBytes()); - REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount()); + REPORTER_ASSERT(reporter, 200 == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); - static_cast<TestResource*>(cache.find(key2))->setSize(201); - REPORTER_ASSERT(reporter, NULL == cache.find(key1)); + { + sk_sp<TestResource> find2(static_cast<TestResource*>( + cache->findAndRefUniqueResource(key2))); + find2->setSize(201); + } + REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key1)); - REPORTER_ASSERT(reporter, 201 == cache.getCachedResourceBytes()); - REPORTER_ASSERT(reporter, 1 == cache.getCachedResourceCount()); - } - - // Test changing the size of an exclusively-held resource. - { - GrResourceCache cache(2, 300); - - TestResource* a = new TestResource(context->getGpu(), 100); - cache.addResource(key1, a); - cache.makeExclusive(a->getCacheEntry()); - - TestResource* b = new TestResource(context->getGpu(), 100); - cache.addResource(key2, b); - b->unref(); - - REPORTER_ASSERT(reporter, 200 == cache.getCachedResourceBytes()); - REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount()); - REPORTER_ASSERT(reporter, NULL == cache.find(key1)); - - a->setSize(200); - - REPORTER_ASSERT(reporter, 300 == cache.getCachedResourceBytes()); - REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount()); - // Internal resource cache validation will test the detached size (debug mode only). - - cache.makeNonExclusive(a->getCacheEntry()); - a->unref(); - - REPORTER_ASSERT(reporter, 300 == cache.getCachedResourceBytes()); - REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount()); - REPORTER_ASSERT(reporter, cache.find(key1)); - // Internal resource cache validation will test the detached size (debug mode only). + REPORTER_ASSERT(reporter, 201 == cache->getResourceBytes()); + REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); } } +static void test_timestamp_wrap(skiatest::Reporter* reporter) { + static const int kCount = 50; + static const int kBudgetCnt = kCount / 2; + static const int kLockedFreq = 8; + static const int kBudgetSize = 0x80000000; + + SkRandom random; + + // Run the test 2*kCount times; + for (int i = 0; i < 2 * kCount; ++i ) { + Mock mock(kBudgetCnt, kBudgetSize); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); + + // Pick a random number of resources to add before the timestamp will wrap. + cache->changeTimestamp(SK_MaxU32 - random.nextULessThan(kCount + 1)); + + static const int kNumToPurge = kCount - kBudgetCnt; + + SkTDArray<int> shouldPurgeIdxs; + int purgeableCnt = 0; + SkTDArray<GrGpuResource*> resourcesToUnref; + + // Add kCount resources, holding onto resources at random so we have a mix of purgeable and + // unpurgeable resources. + for (int j = 0; j < kCount; ++j) { + GrUniqueKey key; + make_unique_key<0>(&key, j); + + TestResource* r = new TestResource(context->getGpu()); + r->resourcePriv().setUniqueKey(key); + if (random.nextU() % kLockedFreq) { + // Make this is purgeable. + r->unref(); + ++purgeableCnt; + if (purgeableCnt <= kNumToPurge) { + *shouldPurgeIdxs.append() = j; + } + } else { + *resourcesToUnref.append() = r; + } + } + + // Verify that the correct resources were purged. + int currShouldPurgeIdx = 0; + for (int j = 0; j < kCount; ++j) { + GrUniqueKey key; + make_unique_key<0>(&key, j); + GrGpuResource* res = cache->findAndRefUniqueResource(key); + if (currShouldPurgeIdx < shouldPurgeIdxs.count() && + shouldPurgeIdxs[currShouldPurgeIdx] == j) { + ++currShouldPurgeIdx; + REPORTER_ASSERT(reporter, nullptr == res); + } else { + REPORTER_ASSERT(reporter, nullptr != res); + } + SkSafeUnref(res); + } + + for (int j = 0; j < resourcesToUnref.count(); ++j) { + resourcesToUnref[j]->unref(); + } + } +} + +static void test_flush(skiatest::Reporter* reporter) { + Mock mock(1000000, 1000000); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); + + // The current cache impl will round the max flush count to the next power of 2. So we choose a + // power of two here to keep things simpler. + static const int kFlushCount = 16; + cache->setLimits(1000000, 1000000, kFlushCount); + + { + // Insert a resource and send a flush notification kFlushCount times. + for (int i = 0; i < kFlushCount; ++i) { + TestResource* r = new TestResource(context->getGpu()); + GrUniqueKey k; + make_unique_key<1>(&k, i); + r->resourcePriv().setUniqueKey(k); + r->unref(); + cache->notifyFlushOccurred(GrResourceCache::kExternal); + } + + // Send flush notifications to the cache. Each flush should purge the oldest resource. + for (int i = 0; i < kFlushCount; ++i) { + cache->notifyFlushOccurred(GrResourceCache::kExternal); + REPORTER_ASSERT(reporter, kFlushCount - i - 1 == cache->getResourceCount()); + for (int j = 0; j < i; ++j) { + GrUniqueKey k; + make_unique_key<1>(&k, j); + GrGpuResource* r = cache->findAndRefUniqueResource(k); + REPORTER_ASSERT(reporter, !SkToBool(r)); + SkSafeUnref(r); + } + } + + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + cache->purgeAllUnlocked(); + } + + // Do a similar test but where we leave refs on some resources to prevent them from being + // purged. + { + GrGpuResource* refedResources[kFlushCount >> 1]; + for (int i = 0; i < kFlushCount; ++i) { + TestResource* r = new TestResource(context->getGpu()); + GrUniqueKey k; + make_unique_key<1>(&k, i); + r->resourcePriv().setUniqueKey(k); + // Leave a ref on every other resource, beginning with the first. + if (SkToBool(i & 0x1)) { + refedResources[i/2] = r; + } else { + r->unref(); + } + cache->notifyFlushOccurred(GrResourceCache::kExternal); + } + + for (int i = 0; i < kFlushCount; ++i) { + // Should get a resource purged every other flush. + cache->notifyFlushOccurred(GrResourceCache::kExternal); + REPORTER_ASSERT(reporter, kFlushCount - i/2 - 1 == cache->getResourceCount()); + } + + // Unref all the resources that we kept refs on in the first loop. + for (int i = 0; i < kFlushCount >> 1; ++i) { + refedResources[i]->unref(); + } + + // After kFlushCount + 1 flushes they all will have sat in the purgeable queue for + // kFlushCount full flushes. + for (int i = 0; i < kFlushCount + 1; ++i) { + REPORTER_ASSERT(reporter, kFlushCount >> 1 == cache->getResourceCount()); + cache->notifyFlushOccurred(GrResourceCache::kExternal); + } + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + + cache->purgeAllUnlocked(); + } + + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + + // Verify that calling flush() on a GrContext with nothing to do will not trigger resource + // eviction. + context->flush(); + for (int i = 0; i < 10; ++i) { + TestResource* r = new TestResource(context->getGpu()); + GrUniqueKey k; + make_unique_key<1>(&k, i); + r->resourcePriv().setUniqueKey(k); + r->unref(); + } + REPORTER_ASSERT(reporter, 10 == cache->getResourceCount()); + for (int i = 0; i < 10 * kFlushCount; ++i) { + context->flush(); + } + REPORTER_ASSERT(reporter, 10 == cache->getResourceCount()); +} + +static void test_time_purge(skiatest::Reporter* reporter) { + Mock mock(1000000, 1000000); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); + + static constexpr int kCnts[] = {1, 10, 1024}; + auto nowish = []() { + // We sleep so that we ensure we get a value that is greater than the last call to + // GrStdSteadyClock::now(). + std::this_thread::sleep_for(GrStdSteadyClock::duration(5)); + auto result = GrStdSteadyClock::now(); + // Also sleep afterwards so we don't get this value again. + std::this_thread::sleep_for(GrStdSteadyClock::duration(5)); + return result; + }; + + for (int cnt : kCnts) { + std::unique_ptr<GrStdSteadyClock::time_point[]> timeStamps( + new GrStdSteadyClock::time_point[cnt]); + { + // Insert resources and get time points between each addition. + for (int i = 0; i < cnt; ++i) { + TestResource* r = new TestResource(context->getGpu()); + GrUniqueKey k; + make_unique_key<1>(&k, i); + r->resourcePriv().setUniqueKey(k); + r->unref(); + timeStamps.get()[i] = nowish(); + } + + // Purge based on the time points between resource additions. Each purge should remove + // the oldest resource. + for (int i = 0; i < cnt; ++i) { + cache->purgeResourcesNotUsedSince(timeStamps[i]); + REPORTER_ASSERT(reporter, cnt - i - 1 == cache->getResourceCount()); + for (int j = 0; j < i; ++j) { + GrUniqueKey k; + make_unique_key<1>(&k, j); + GrGpuResource* r = cache->findAndRefUniqueResource(k); + REPORTER_ASSERT(reporter, !SkToBool(r)); + SkSafeUnref(r); + } + } + + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + cache->purgeAllUnlocked(); + } + + // Do a similar test but where we leave refs on some resources to prevent them from being + // purged. + { + std::unique_ptr<GrGpuResource* []> refedResources(new GrGpuResource*[cnt / 2]); + for (int i = 0; i < cnt; ++i) { + TestResource* r = new TestResource(context->getGpu()); + GrUniqueKey k; + make_unique_key<1>(&k, i); + r->resourcePriv().setUniqueKey(k); + // Leave a ref on every other resource, beginning with the first. + if (SkToBool(i & 0x1)) { + refedResources.get()[i / 2] = r; + } else { + r->unref(); + } + timeStamps.get()[i] = nowish(); + } + + for (int i = 0; i < cnt; ++i) { + // Should get a resource purged every other frame. + cache->purgeResourcesNotUsedSince(timeStamps[i]); + REPORTER_ASSERT(reporter, cnt - i / 2 - 1 == cache->getResourceCount()); + } + + // Unref all the resources that we kept refs on in the first loop. + for (int i = 0; i < (cnt / 2); ++i) { + refedResources.get()[i]->unref(); + cache->purgeResourcesNotUsedSince(nowish()); + REPORTER_ASSERT(reporter, cnt / 2 - i - 1 == cache->getResourceCount()); + } + + cache->purgeAllUnlocked(); + } + + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + + // Verify that calling flush() on a GrContext with nothing to do will not trigger resource + // eviction + context->flush(); + for (int i = 0; i < 10; ++i) { + TestResource* r = new TestResource(context->getGpu()); + GrUniqueKey k; + make_unique_key<1>(&k, i); + r->resourcePriv().setUniqueKey(k); + r->unref(); + } + REPORTER_ASSERT(reporter, 10 == cache->getResourceCount()); + context->flush(); + REPORTER_ASSERT(reporter, 10 == cache->getResourceCount()); + cache->purgeResourcesNotUsedSince(nowish()); + REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); + } +} + +static void test_partial_purge(skiatest::Reporter* reporter) { + Mock mock(6, 100); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); + + enum TestsCase { + kOnlyScratch_TestCase = 0, + kPartialScratch_TestCase = 1, + kAllScratch_TestCase = 2, + kPartial_TestCase = 3, + kAll_TestCase = 4, + kNone_TestCase = 5, + kEndTests_TestCase = kNone_TestCase + 1 + }; + + for (int testCase = 0; testCase < kEndTests_TestCase; testCase++) { + + GrUniqueKey key1, key2, key3; + make_unique_key<0>(&key1, 1); + make_unique_key<0>(&key2, 2); + make_unique_key<0>(&key3, 3); + + // Add three unique resources to the cache. + TestResource *unique1 = new TestResource(context->getGpu()); + TestResource *unique2 = new TestResource(context->getGpu()); + TestResource *unique3 = new TestResource(context->getGpu()); + + unique1->resourcePriv().setUniqueKey(key1); + unique2->resourcePriv().setUniqueKey(key2); + unique3->resourcePriv().setUniqueKey(key3); + + unique1->setSize(10); + unique2->setSize(11); + unique3->setSize(12); + + // Add two scratch resources to the cache. + TestResource *scratch1 = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, + TestResource::kA_SimulatedProperty); + TestResource *scratch2 = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, + TestResource::kB_SimulatedProperty); + scratch1->setSize(13); + scratch2->setSize(14); + + + REPORTER_ASSERT(reporter, 5 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 60 == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); + + // Add resources to the purgeable queue + unique1->unref(); + scratch1->unref(); + unique2->unref(); + scratch2->unref(); + unique3->unref(); + + REPORTER_ASSERT(reporter, 5 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 60 == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 60 == cache->getPurgeableBytes()); + + switch(testCase) { + case kOnlyScratch_TestCase: { + context->purgeUnlockedResources(14, true); + REPORTER_ASSERT(reporter, 3 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 33 == cache->getBudgetedResourceBytes()); + break; + } + case kPartialScratch_TestCase: { + context->purgeUnlockedResources(3, true); + REPORTER_ASSERT(reporter, 4 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 47 == cache->getBudgetedResourceBytes()); + break; + } + case kAllScratch_TestCase: { + context->purgeUnlockedResources(50, true); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); + break; + } + case kPartial_TestCase: { + context->purgeUnlockedResources(13, false); + REPORTER_ASSERT(reporter, 3 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 37 == cache->getBudgetedResourceBytes()); + break; + } + case kAll_TestCase: { + context->purgeUnlockedResources(50, false); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); + break; + } + case kNone_TestCase: { + context->purgeUnlockedResources(0, true); + context->purgeUnlockedResources(0, false); + REPORTER_ASSERT(reporter, 5 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 60 == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 60 == cache->getPurgeableBytes()); + break; + } + }; + + // ensure all are purged before the next + context->purgeAllUnlockedResources(); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); + + } +} + +static void test_large_resource_count(skiatest::Reporter* reporter) { + // Set the cache size to double the resource count because we're going to create 2x that number + // resources, using two different key domains. Add a little slop to the bytes because we resize + // down to 1 byte after creating the resource. + static const int kResourceCnt = 2000; + + Mock mock(2 * kResourceCnt, 2 * kResourceCnt + 1000); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); + + for (int i = 0; i < kResourceCnt; ++i) { + GrUniqueKey key1, key2; + make_unique_key<1>(&key1, i); + make_unique_key<2>(&key2, i); + + TestResource* resource; + + resource = new TestResource(context->getGpu()); + resource->resourcePriv().setUniqueKey(key1); + resource->setSize(1); + resource->unref(); + + resource = new TestResource(context->getGpu()); + resource->resourcePriv().setUniqueKey(key2); + resource->setSize(1); + resource->unref(); + } + + REPORTER_ASSERT(reporter, TestResource::NumAlive() == 2 * kResourceCnt); + REPORTER_ASSERT(reporter, cache->getPurgeableBytes() == 2 * kResourceCnt); + REPORTER_ASSERT(reporter, cache->getBudgetedResourceBytes() == 2 * kResourceCnt); + REPORTER_ASSERT(reporter, cache->getBudgetedResourceCount() == 2 * kResourceCnt); + REPORTER_ASSERT(reporter, cache->getResourceBytes() == 2 * kResourceCnt); + REPORTER_ASSERT(reporter, cache->getResourceCount() == 2 * kResourceCnt); + for (int i = 0; i < kResourceCnt; ++i) { + GrUniqueKey key1, key2; + make_unique_key<1>(&key1, i); + make_unique_key<2>(&key2, i); + + REPORTER_ASSERT(reporter, cache->hasUniqueKey(key1)); + REPORTER_ASSERT(reporter, cache->hasUniqueKey(key2)); + } + + cache->purgeAllUnlocked(); + REPORTER_ASSERT(reporter, TestResource::NumAlive() == 0); + REPORTER_ASSERT(reporter, cache->getPurgeableBytes() == 0); + REPORTER_ASSERT(reporter, cache->getBudgetedResourceBytes() == 0); + REPORTER_ASSERT(reporter, cache->getBudgetedResourceCount() == 0); + REPORTER_ASSERT(reporter, cache->getResourceBytes() == 0); + REPORTER_ASSERT(reporter, cache->getResourceCount() == 0); + + for (int i = 0; i < kResourceCnt; ++i) { + GrUniqueKey key1, key2; + make_unique_key<1>(&key1, i); + make_unique_key<2>(&key2, i); + + REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key1)); + REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key2)); + } +} + +static void test_custom_data(skiatest::Reporter* reporter) { + GrUniqueKey key1, key2; + make_unique_key<0>(&key1, 1); + make_unique_key<0>(&key2, 2); + int foo = 4132; + key1.setCustomData(SkData::MakeWithCopy(&foo, sizeof(foo))); + REPORTER_ASSERT(reporter, *(int*) key1.getCustomData()->data() == 4132); + REPORTER_ASSERT(reporter, key2.getCustomData() == nullptr); + + // Test that copying a key also takes a ref on its custom data. + GrUniqueKey key3 = key1; + REPORTER_ASSERT(reporter, *(int*) key3.getCustomData()->data() == 4132); +} + +static void test_abandoned(skiatest::Reporter* reporter) { + Mock mock(10, 300); + GrContext* context = mock.context(); + sk_sp<GrGpuResource> resource(new TestResource(context->getGpu())); + context->abandonContext(); + + REPORTER_ASSERT(reporter, resource->wasDestroyed()); + + // Call all the public methods on resource in the abandoned state. They shouldn't crash. + + resource->uniqueID(); + resource->getUniqueKey(); + resource->wasDestroyed(); + resource->gpuMemorySize(); + resource->getContext(); + + resource->abandon(); + resource->resourcePriv().getScratchKey(); + resource->resourcePriv().isBudgeted(); + resource->resourcePriv().makeBudgeted(); + resource->resourcePriv().makeUnbudgeted(); + resource->resourcePriv().removeScratchKey(); + GrUniqueKey key; + make_unique_key<0>(&key, 1); + resource->resourcePriv().setUniqueKey(key); + resource->resourcePriv().removeUniqueKey(); +} + +static void test_tags(skiatest::Reporter* reporter) { +#ifdef SK_DEBUG + // We will insert 1 resource with tag "tag1", 2 with "tag2", and so on, up through kLastTagIdx. + static constexpr int kLastTagIdx = 10; + static constexpr int kNumResources = kLastTagIdx * (kLastTagIdx + 1) / 2; + + Mock mock(kNumResources, kNumResources * TestResource::kDefaultSize); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); + + SkString tagStr; + int tagIdx = 0; + int currTagCnt = 0; + + for (int i = 0; i < kNumResources; ++i, ++currTagCnt) { + sk_sp<GrGpuResource> resource(new TestResource(context->getGpu())); + GrUniqueKey key; + if (currTagCnt == tagIdx) { + tagIdx += 1; + currTagCnt = 0; + tagStr.printf("tag%d", tagIdx); + } + make_unique_key<1>(&key, i, tagStr.c_str()); + resource->resourcePriv().setUniqueKey(key); + } + SkASSERT(kLastTagIdx == tagIdx); + SkASSERT(currTagCnt == kLastTagIdx); + + // Test i = 0 to exercise unused tag string. + for (int i = 0; i <= kLastTagIdx; ++i) { + tagStr.printf("tag%d", i); + REPORTER_ASSERT(reporter, cache->countUniqueKeysWithTag(tagStr.c_str()) == i); + } +#endif +} + +DEF_GPUTEST(ResourceCacheMisc, reporter, factory) { + // The below tests create their own mock contexts. + test_no_key(reporter); + test_budgeting(reporter); + test_unbudgeted(reporter); + test_unbudgeted_to_scratch(reporter); + test_duplicate_unique_key(reporter); + test_duplicate_scratch_key(reporter); + test_remove_scratch_key(reporter); + test_scratch_key_consistency(reporter); + test_purge_invalidated(reporter); + test_cache_chained_purge(reporter); + test_resource_size_changed(reporter); + test_timestamp_wrap(reporter); + test_flush(reporter); + test_time_purge(reporter); + test_partial_purge(reporter); + test_large_resource_count(reporter); + test_custom_data(reporter); + test_abandoned(reporter); + test_tags(reporter); +} + //////////////////////////////////////////////////////////////////////////////// -DEF_GPUTEST(ResourceCache, reporter, factory) { - for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) { - GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type); - if (!GrContextFactory::IsRenderingGLContext(glType)) { - continue; - } - GrContext* context = factory->get(glType); - if (NULL == context) { - continue; +static sk_sp<GrTexture> make_normal_texture(GrResourceProvider* provider, + GrSurfaceFlags flags, + int width, int height, + int sampleCnt) { + GrSurfaceDesc desc; + desc.fFlags = flags; + desc.fWidth = width; + desc.fHeight = height; + desc.fConfig = kRGBA_8888_GrPixelConfig; + desc.fSampleCnt = sampleCnt; + + return provider->createTexture(desc, SkBudgeted::kYes); +} + +static sk_sp<GrTextureProxy> make_mipmap_proxy(GrResourceProvider* provider, + GrSurfaceFlags flags, + int width, int height, + int sampleCnt) { + SkBitmap bm; + + bm.allocN32Pixels(width, height, true); + bm.eraseColor(SK_ColorBLUE); + + sk_sp<SkMipMap> mipmaps(SkMipMap::Build(bm, SkDestinationSurfaceColorMode::kLegacy, nullptr)); + SkASSERT(mipmaps); + SkASSERT(mipmaps->countLevels() > 1); + + int mipLevelCount = mipmaps->countLevels() + 1; + + std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipLevelCount]); + + texels[0].fPixels = bm.getPixels(); + texels[0].fRowBytes = bm.rowBytes(); + + for (int i = 1; i < mipLevelCount; ++i) { + SkMipMap::Level generatedMipLevel; + mipmaps->getLevel(i - 1, &generatedMipLevel); + texels[i].fPixels = generatedMipLevel.fPixmap.addr(); + texels[i].fRowBytes = generatedMipLevel.fPixmap.rowBytes(); + } + + GrSurfaceDesc desc; + desc.fFlags = flags; + desc.fWidth = width; + desc.fHeight = height; + desc.fConfig = kRGBA_8888_GrPixelConfig; + desc.fSampleCnt = sampleCnt; + desc.fIsMipMapped = true; + + return GrSurfaceProxy::MakeDeferredMipMap(provider, desc, SkBudgeted::kYes, + texels.get(), mipLevelCount); +} + +// Exercise GrSurface::gpuMemorySize for different combos of MSAA, RT-only, +// Texture-only, both-RT-and-Texture and MIPmapped +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GPUMemorySize, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + GrResourceProvider* provider = context->resourceProvider(); + + static const int kSize = 64; + + // Normal versions + { + sk_sp<GrTexture> tex; + + tex = make_normal_texture(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 0); + size_t size = tex->gpuMemorySize(); + REPORTER_ASSERT(reporter, kSize*kSize*4 == size); + + size_t sampleCount = (size_t)context->caps()->getSampleCount(4, kRGBA_8888_GrPixelConfig); + if (sampleCount >= 4) { + tex = make_normal_texture(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize, + sampleCount); + size = tex->gpuMemorySize(); + REPORTER_ASSERT(reporter, + kSize*kSize*4 == size || // msaa4 failed + kSize*kSize*4*sampleCount == size || // auto-resolving + kSize*kSize*4*(sampleCount+1) == size); // explicit resolve buffer } - GrTextureDesc desc; - desc.fConfig = kSkia8888_GrPixelConfig; - desc.fFlags = kRenderTarget_GrTextureFlagBit; - desc.fWidth = gWidth; - desc.fHeight = gHeight; - SkImageInfo info = SkImageInfo::MakeN32Premul(gWidth, gHeight); - SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(context, info)); + tex = make_normal_texture(provider, kNone_GrSurfaceFlags, kSize, kSize, 0); + size = tex->gpuMemorySize(); + REPORTER_ASSERT(reporter, kSize*kSize*4 == size); + } - test_cache(reporter, context, surface->getCanvas()); - test_purge_invalidated(reporter, context); - test_cache_delete_on_destruction(reporter, context); - test_resource_size_changed(reporter, context); + + // Mipmapped versions + if (context->caps()->mipMapSupport()) { + sk_sp<GrTextureProxy> proxy; + + proxy = make_mipmap_proxy(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 0); + size_t size = proxy->gpuMemorySize(); + REPORTER_ASSERT(reporter, kSize*kSize*4+(kSize*kSize*4)/3 == size); + + size_t sampleCount = (size_t)context->caps()->getSampleCount(4, kRGBA_8888_GrPixelConfig); + if (sampleCount >= 4) { + proxy = make_mipmap_proxy(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize, + sampleCount); + size = proxy->gpuMemorySize(); + REPORTER_ASSERT(reporter, + kSize*kSize*4+(kSize*kSize*4)/3 == size || // msaa4 failed + kSize*kSize*4*sampleCount+(kSize*kSize*4)/3 == size || // auto-resolving + kSize*kSize*4*(sampleCount+1)+(kSize*kSize*4)/3 == size); // explicit resolve buffer + } + + proxy = make_mipmap_proxy(provider, kNone_GrSurfaceFlags, kSize, kSize, 0); + size = proxy->gpuMemorySize(); + REPORTER_ASSERT(reporter, kSize*kSize*4+(kSize*kSize*4)/3 == size); } }
diff --git a/src/third_party/skia/tests/RoundRectTest.cpp b/src/third_party/skia/tests/RoundRectTest.cpp index 261ec67..ce97445 100644 --- a/src/third_party/skia/tests/RoundRectTest.cpp +++ b/src/third_party/skia/tests/RoundRectTest.cpp
@@ -9,6 +9,99 @@ #include "SkRRect.h" #include "Test.h" +static void test_tricky_radii(skiatest::Reporter* reporter) { + { + // crbug.com/458522 + SkRRect rr; + const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 }; + const SkScalar rad = 12814; + const SkVector vec[] = { { rad, rad }, { 0, rad }, { rad, rad }, { 0, rad } }; + rr.setRectRadii(bounds, vec); + } + + { + // crbug.com//463920 + SkRect r = SkRect::MakeLTRB(0, 0, 1009, 33554432.0); + SkVector radii[4] = { + { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554432.0 }, { 110.0f, 5.0f } + }; + SkRRect rr; + rr.setRectRadii(r, radii); + + REPORTER_ASSERT(reporter, (double) rr.radii(SkRRect::kUpperRight_Corner).fY + + (double) rr.radii(SkRRect::kLowerRight_Corner).fY <= + rr.height()); + } +} + +static void test_empty_crbug_458524(skiatest::Reporter* reporter) { + SkRRect rr; + const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 }; + const SkScalar rad = 40; + rr.setRectXY(bounds, rad, rad); + + SkRRect other; + SkMatrix matrix; + matrix.setScale(0, 1); + rr.transform(matrix, &other); + REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == other.getType()); +} + +// Test that all the SkRRect entry points correctly handle un-sorted and +// zero-sized input rects +static void test_empty(skiatest::Reporter* reporter) { + static const SkRect oooRects[] = { // out of order + { 100, 0, 0, 100 }, // ooo horizontal + { 0, 100, 100, 0 }, // ooo vertical + { 100, 100, 0, 0 }, // ooo both + }; + + static const SkRect emptyRects[] = { + { 100, 100, 100, 200 }, // empty horizontal + { 100, 100, 200, 100 }, // empty vertical + { 100, 100, 100, 100 }, // empty both + { 0, 0, 0, 0 } // setEmpty-empty + }; + + static const SkVector radii[4] = { { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 } }; + + SkRRect r; + + for (size_t i = 0; i < SK_ARRAY_COUNT(oooRects); ++i) { + r.setRect(oooRects[i]); + REPORTER_ASSERT(reporter, !r.isEmpty()); + + r.setOval(oooRects[i]); + REPORTER_ASSERT(reporter, !r.isEmpty()); + + r.setRectXY(oooRects[i], 1, 2); + REPORTER_ASSERT(reporter, !r.isEmpty()); + + r.setNinePatch(oooRects[i], 0, 1, 2, 3); + REPORTER_ASSERT(reporter, !r.isEmpty()); + + r.setRectRadii(oooRects[i], radii); + REPORTER_ASSERT(reporter, !r.isEmpty()); + } + + for (size_t i = 0; i < SK_ARRAY_COUNT(emptyRects); ++i) { + r.setRect(emptyRects[i]); + REPORTER_ASSERT(reporter, r.isEmpty()); + + r.setOval(emptyRects[i]); + REPORTER_ASSERT(reporter, r.isEmpty()); + + r.setRectXY(emptyRects[i], 1, 2); + REPORTER_ASSERT(reporter, r.isEmpty()); + + r.setNinePatch(emptyRects[i], 0, 1, 2, 3); + REPORTER_ASSERT(reporter, r.isEmpty()); + + r.setRectRadii(emptyRects[i], radii); + REPORTER_ASSERT(reporter, r.isEmpty()); + } +} + static const SkScalar kWidth = 100.0f; static const SkScalar kHeight = 100.0f; @@ -33,6 +126,32 @@ REPORTER_ASSERT(reporter, rr2.isRect()); } + +static void test_9patch_rrect(skiatest::Reporter* reporter, + const SkRect& rect, + SkScalar l, SkScalar t, SkScalar r, SkScalar b, + bool checkRadii) { + SkRRect rr; + rr.setNinePatch(rect, l, t, r, b); + + REPORTER_ASSERT(reporter, SkRRect::kNinePatch_Type == rr.type()); + REPORTER_ASSERT(reporter, rr.rect() == rect); + + if (checkRadii) { + // This test doesn't hold if the radii will be rescaled by SkRRect + SkRect ninePatchRadii = { l, t, r, b }; + SkPoint rquad[4]; + ninePatchRadii.toQuad(rquad); + for (int i = 0; i < 4; ++i) { + REPORTER_ASSERT(reporter, rquad[i] == rr.radii((SkRRect::Corner) i)); + } + } + SkRRect rr2; // construct the same RR using the most general set function + SkVector radii[4] = { { l, t }, { r, t }, { r, b }, { l, b } }; + rr2.setRectRadii(rect, radii); + REPORTER_ASSERT(reporter, rr2 == rr && rr2.getType() == rr.getType()); +} + // Test out the basic API entry points static void test_round_rect_basic(skiatest::Reporter* reporter) { // Test out initialization methods @@ -109,24 +228,17 @@ REPORTER_ASSERT(reporter, rr3_3 == rr3 && rr3_3.getType() == rr3.getType()); //---- - SkRect ninePatchRadii = { 10, 9, 8, 7 }; + test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true); - SkRRect rr4; - rr4.setNinePatch(rect, ninePatchRadii.fLeft, ninePatchRadii.fTop, ninePatchRadii.fRight, - ninePatchRadii.fBottom); + { + // Test out the rrect from skia:3466 + SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f, 0.806214333f); - REPORTER_ASSERT(reporter, SkRRect::kNinePatch_Type == rr4.type()); - REPORTER_ASSERT(reporter, rr4.rect() == rect); - - SkPoint rquad[4]; - ninePatchRadii.toQuad(rquad); - for (int i = 0; i < 4; ++i) { - REPORTER_ASSERT(reporter, rquad[i] == rr4.radii((SkRRect::Corner) i)); + test_9patch_rrect(reporter, + rect2, + 0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f, + false); } - SkRRect rr4_2; // construct the same RR using the most general set function - SkVector rr4_2_radii[4] = { { 10, 9 }, { 8, 9 }, {8, 7 }, { 10, 7 } }; - rr4_2.setRectRadii(rect, rr4_2_radii); - REPORTER_ASSERT(reporter, rr4_2 == rr4 && rr4_2.getType() == rr4.getType()); //---- SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } }; @@ -143,8 +255,7 @@ // Test out == & != REPORTER_ASSERT(reporter, empty != rr3); - REPORTER_ASSERT(reporter, rr3 != rr4); - REPORTER_ASSERT(reporter, rr4 != rr5); + REPORTER_ASSERT(reporter, rr3 != rr5); } // Test out the cases when the RR degenerates to a rect @@ -433,11 +544,11 @@ assert_transform_failure(reporter, orig, matrix); matrix.reset(); - matrix.setPerspX(SkScalarToPersp(SkIntToScalar(4))); + matrix.setPerspX(4); assert_transform_failure(reporter, orig, matrix); matrix.reset(); - matrix.setPerspY(SkScalarToPersp(SkIntToScalar(5))); + matrix.setPerspY(5); assert_transform_failure(reporter, orig, matrix); // Rotation fails. @@ -492,7 +603,7 @@ REPORTER_ASSERT(reporter, orig.rect().top() == dst.rect().top()); // Keeping the scale, but adding a persp will make transform fail. - matrix.setPerspX(SkScalarToPersp(SkIntToScalar(7))); + matrix.setPerspX(7); assert_transform_failure(reporter, orig, matrix); // Scaling in -y will flip the round rect vertically. @@ -549,18 +660,18 @@ // Radii are scaled. for (int i = 0; i < 4; ++i) { REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fX, - SkScalarMul(orig.radii((SkRRect::Corner) i).fX, xScale))); + orig.radii((SkRRect::Corner) i).fX * xScale)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fY, - SkScalarMul(orig.radii((SkRRect::Corner) i).fY, yScale))); + orig.radii((SkRRect::Corner) i).fY * yScale)); } REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().width(), - SkScalarMul(orig.rect().width(), xScale))); + orig.rect().width() * xScale)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().height(), - SkScalarMul(orig.rect().height(), yScale))); + orig.rect().height() * yScale)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().left(), - SkScalarMul(orig.rect().left(), xScale))); + orig.rect().left() * xScale)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().top(), - SkScalarMul(orig.rect().top(), yScale))); + orig.rect().top() * yScale)); } static void test_round_rect_transform(skiatest::Reporter* reporter) { @@ -582,7 +693,7 @@ } } -// Test out the case where an oval already off in space is translated/scaled +// Test out the case where an oval already off in space is translated/scaled // further off into space - yielding numerical issues when the rect & radii // are transformed separatly // BUG=skia:2696 @@ -604,9 +715,9 @@ SkScalar halfHeight = SkScalarHalf(dst.height()); for (int i = 0; i < 4; ++i) { - REPORTER_ASSERT(reporter, + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fX, halfWidth)); - REPORTER_ASSERT(reporter, + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fY, halfHeight)); } } @@ -621,4 +732,7 @@ test_round_rect_contains_rect(reporter); test_round_rect_transform(reporter); test_issue_2696(reporter); + test_tricky_radii(reporter); + test_empty_crbug_458524(reporter); + test_empty(reporter); }
diff --git a/src/third_party/skia/tests/RuntimeConfigTest.cpp b/src/third_party/skia/tests/RuntimeConfigTest.cpp deleted file mode 100644 index d40a4f0..0000000 --- a/src/third_party/skia/tests/RuntimeConfigTest.cpp +++ /dev/null
@@ -1,28 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#include "SkRTConf.h" -#include "Test.h" - -SK_CONF_DECLARE(int, c_RTConfTestVariable, - "test.utils.rtconf.testVariable", 1, - "This is only a test. Do not be alarmed."); -// TODO(skia-team): more comprehensive unit tests of the SkRTConf -// system. -DEF_TEST(RuntimeConfig, reporter) { - REPORTER_ASSERT(reporter, 1 == c_RTConfTestVariable); - - SK_CONF_SET("test.utils.rtconf.testVariable", 2); -#ifdef SK_DEVELOPER - REPORTER_ASSERT(reporter, 2 == c_RTConfTestVariable); -#else // not SK_DEVELOPER - // Can not change RTConf variables in Release. - REPORTER_ASSERT(reporter, 1 == c_RTConfTestVariable); -#endif // SK_DEVELOPER - - // This should not give a warning. - SK_CONF_TRY_SET("test.utils.rtconf.nonexistentVariable", 7); -}
diff --git a/src/third_party/skia/tests/SHA1Test.cpp b/src/third_party/skia/tests/SHA1Test.cpp deleted file mode 100644 index fd5fcb7..0000000 --- a/src/third_party/skia/tests/SHA1Test.cpp +++ /dev/null
@@ -1,53 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkSHA1.h" -#include "Test.h" - -static bool digests_equal(const SkSHA1::Digest& expectedDigest, const SkSHA1::Digest& computedDigest) { - for (size_t i = 0; i < SK_ARRAY_COUNT(expectedDigest.data); ++i) { - if (expectedDigest.data[i] != computedDigest.data[i]) { - return false; - } - } - return true; -} - -static struct SHA1Test { - const char* message; - const unsigned long int repeatCount; - const SkSHA1::Digest digest; -} sha1_tests[] = { - // Reference tests from RFC3174 Section 7.3 ( http://www.ietf.org/rfc/rfc3174.txt ) - { "abc", 1, {{ 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E, 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D }} }, - { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 1, {{ 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1 }} }, - { "a", 1000000, {{ 0x34, 0xAA, 0x97, 0x3C, 0xD4, 0xC4, 0xDA, 0xA4, 0xF6, 0x1E, 0xEB, 0x2B, 0xDB, 0xAD, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6F }} }, - { "0123456701234567012345670123456701234567012345670123456701234567", 10, {{ 0xDE, 0xA3, 0x56, 0xA2, 0xCD, 0xDD, 0x90, 0xC7, 0xA7, 0xEC, 0xED, 0xC5, 0xEB, 0xB5, 0x63, 0x93, 0x4F, 0x46, 0x04, 0x52 }} }, - - // Reference tests from running GNU sha1sum on test input - { "The quick brown fox jumps over the lazy dog", 1, {{ 0x2f, 0xd4, 0xe1, 0xc6, 0x7a, 0x2d, 0x28, 0xfc, 0xed, 0x84, 0x9e, 0xe1, 0xbb, 0x76, 0xe7, 0x39, 0x1b, 0x93, 0xeb, 0x12 }} }, - { "", 1, {{ 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 }} }, -}; - -static void sha1_test(const SHA1Test& test, skiatest::Reporter* reporter) { - size_t len = strlen(test.message); - - SkSHA1 context; - for (unsigned long int i = 0; i < test.repeatCount; ++i) { - context.update(reinterpret_cast<const uint8_t*>(test.message), len); - } - SkSHA1::Digest digest; - context.finish(digest); - - REPORTER_ASSERT(reporter, digests_equal(test.digest, digest)); -} - -DEF_TEST(SHA1, reporter) { - for (size_t i = 0; i < SK_ARRAY_COUNT(sha1_tests); ++i) { - sha1_test(sha1_tests[i], reporter); - } -}
diff --git a/src/third_party/skia/tests/SListTest.cpp b/src/third_party/skia/tests/SListTest.cpp deleted file mode 100644 index 78fcc65..0000000 --- a/src/third_party/skia/tests/SListTest.cpp +++ /dev/null
@@ -1,111 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkTInternalSList.h" -#include "Test.h" - -class SListEntry { -public: - SListEntry* next() { return getSListNext(); } -private: - SK_DECLARE_INTERNAL_SLIST_INTERFACE(SListEntry); -}; - -static bool verifyEmptyList(skiatest::Reporter* reporter, - const SkTInternalSList<SListEntry>& list, - const char* stage) { - - if (!list.isEmpty()) { - ERRORF(reporter, "%s - List not empty", stage); - return false; - } - if (0 != list.getCount()) { - ERRORF(reporter, "%s - List count is not zero, %d instead", stage, list.getCount()); - return false; - } - if (list.head()) { - ERRORF(reporter, "%s - List has elements when empty", stage); - return false; - } - return true; -} - -static bool verifyList(skiatest::Reporter* reporter, - const SkTInternalSList<SListEntry>& list, - const char* stage, - SListEntry* start, int count, int step = 1) { - SListEntry* next = list.head(); - if (list.getCount() != count) { - ERRORF(reporter, "%s - List was too short, %d instead of %d", stage, list.getCount(), count); - return false; - } - int index = 0; - for(SListEntry* value = start; index < count; value += step, ++index) { - if (NULL == next) { - ERRORF(reporter, "%s - List too short, should be %d", stage, count); - return false; - } - if (next!= value) { - ERRORF(reporter, "%s - List entries at index %d of %d don't match", stage, index, count); - return false; - } - next = next->next(); - } - if (next) { - ERRORF(reporter, "%s - List too long, should be %d", stage, count); - return false; - } - return true; -} - -static void testTInternalSList(skiatest::Reporter* reporter) { - // Build a test array of data - static const int testArraySize = 10; - SListEntry testArray[testArraySize]; - // Basic add remove tests - SkTInternalSList<SListEntry> list; - verifyEmptyList(reporter, list, "start"); - // Push values in, testing on the way - for (int index = 0; index < testArraySize; ++index) { - list.push(&testArray[index]); - if (!verifyList(reporter, list, "push", &testArray[index], index+1, -1)) { - return; - } - } - // Now remove them again - for (int index = testArraySize - 1; index >= 0; --index) { - REPORTER_ASSERT(reporter, &testArray[index] == list.pop()); - if ((index != 0) && - !verifyList(reporter, list, "pop", &testArray[index-1], index, -1)) { - return; - } - } - verifyEmptyList(reporter, list, "end"); - // Move between list tests - for (int index = 0; index < testArraySize; ++index) { - list.push(&testArray[index]); - } - verifyList(reporter, list, "swap", &testArray[testArraySize-1], testArraySize, -1); - SkTInternalSList<SListEntry> other; - // Check swap moves the list over unchanged - other.swap(&list); - verifyEmptyList(reporter, list, "swap"); - verifyList(reporter, other, "swap", &testArray[testArraySize-1], testArraySize, -1); - // Check pushAll optimizes to a swap when one of the is empty - list.pushAll(&other); - verifyList(reporter, list, "pushAll-empty", &testArray[testArraySize-1], testArraySize, -1); - verifyEmptyList(reporter, other, "pushAll-empty"); - // Check pushAll when non empty works - other.push(list.pop()); - other.pushAll(&list); - verifyEmptyList(reporter, list, "pushAll"); - verifyList(reporter, other, "pushAll", &testArray[0], testArraySize, 1); -} - -DEF_TEST(SList, reporter) { - testTInternalSList(reporter); -}
diff --git a/src/third_party/skia/tests/SRGBMipMapTest.cpp b/src/third_party/skia/tests/SRGBMipMapTest.cpp new file mode 100644 index 0000000..8d035e1 --- /dev/null +++ b/src/third_party/skia/tests/SRGBMipMapTest.cpp
@@ -0,0 +1,183 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" +#if SK_SUPPORT_GPU +#include "GrCaps.h" +#include "GrClip.h" +#include "GrContext.h" +#include "GrRenderTargetContext.h" +#include "SkCanvas.h" +#include "SkGr.h" +#include "SkSurface.h" +#include "gl/GrGLGpu.h" + +// using anonymous namespace because these functions are used as template params. +namespace { +/** convert 0..1 srgb value to 0..1 linear */ +float srgb_to_linear(float srgb) { + if (srgb <= 0.04045f) { + return srgb / 12.92f; + } else { + return powf((srgb + 0.055f) / 1.055f, 2.4f); + } +} + +/** convert 0..1 linear value to 0..1 srgb */ +float linear_to_srgb(float linear) { + if (linear <= 0.0031308) { + return linear * 12.92f; + } else { + return 1.055f * powf(linear, 1.f / 2.4f) - 0.055f; + } +} +} + +static bool check_value(U8CPU value, U8CPU expected, U8CPU error) { + if (value >= expected) { + return (value - expected) <= error; + } else { + return (expected - value) <= error; + } +} + +void read_and_check_pixels(skiatest::Reporter* reporter, GrSurfaceContext* context, + U8CPU expected, const SkImageInfo& dstInfo, + U8CPU error, const char* subtestName) { + int w = dstInfo.width(); + int h = dstInfo.height(); + SkAutoTMalloc<uint32_t> readData(w * h); + memset(readData.get(), 0, sizeof(uint32_t) * w * h); + + if (!context->readPixels(dstInfo, readData.get(), 0, 0, 0)) { + ERRORF(reporter, "Could not read pixels for %s.", subtestName); + return; + } + + for (int j = 0; j < h; ++j) { + for (int i = 0; i < w; ++i) { + uint32_t read = readData[j * w + i]; + + bool success = + check_value(read & 0xff, expected, error) && + check_value((read >> 8) & 0xff, expected, error) && + check_value((read >> 16) & 0xff, expected, error); + + if (!success) { + ERRORF(reporter, "Expected 0xff%02x%02x%02x, read back as 0x%08x in %s at %d, %d.", + expected, expected, expected, read, subtestName, i, j); + return; + } + } + } +} + +DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SRGBMipMaps, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + if (!context->caps()->srgbSupport()) { + return; + } + + const int rtS = 16; + const int texS = rtS * 2; + + // Fill texture with a dither of black and 60% sRGB (~ 32.5% linear) gray. Although there is + // only one likely failure mode (doing a direct downsample of the sRGB values), this pattern + // maximizes the minimum error across all three conceivable failure modes: + // 1) Likely incorrect: + // (A + B) / 2 + // 2) No input decode, decode output: + // linear_to_srgb((A + B) / 2) + // 3) Decode input, no output encode: + // (srgb_to_linear(A) + srgb_to_linear(B)) / 2 + + const U8CPU srgb60 = sk_float_round2int(0.6f * 255.0f); + static const SkPMColor colors[2] = { + SkPackARGB32(0xFF, srgb60, srgb60, srgb60), + SkPackARGB32(0xFF, 0x00, 0x00, 0x00) + }; + uint32_t texData[texS * texS]; + for (int y = 0; y < texS; ++y) { + for (int x = 0; x < texS; ++x) { + texData[y * texS + x] = colors[(x + y) % 2]; + } + } + + // We can be pretty generous with the error detection, thanks to the choice of input. + // The closest likely failure mode is off by > 0.1, so anything that encodes within + // 10/255 of optimal is more than good enough for this test. + const U8CPU expectedSRGB = sk_float_round2int( + linear_to_srgb(srgb_to_linear(srgb60 / 255.0f) / 2.0f) * 255.0f); + const U8CPU expectedLinear = srgb60 / 2; + const U8CPU error = 10; + + const SkImageInfo iiSRGBA = SkImageInfo::Make(rtS, rtS, kRGBA_8888_SkColorType, + kPremul_SkAlphaType, + SkColorSpace::MakeSRGB()); + const SkImageInfo iiRGBA = SkImageInfo::Make(rtS, rtS, kRGBA_8888_SkColorType, + kPremul_SkAlphaType); + + // Create our test texture + GrSurfaceDesc desc; + desc.fFlags = kNone_GrSurfaceFlags; + desc.fConfig = kSRGBA_8888_GrPixelConfig; + desc.fWidth = texS; + desc.fHeight = texS; + + GrResourceProvider* resourceProvider = context->resourceProvider(); + sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(resourceProvider, + desc, SkBudgeted::kNo, + texData, 0); + + // Create two render target contexts (L32 and S32) + sk_sp<SkColorSpace> srgbColorSpace = SkColorSpace::MakeSRGB(); + sk_sp<GrRenderTargetContext> l32RenderTargetContext = context->makeDeferredRenderTargetContext( + SkBackingFit::kExact, rtS, rtS, kRGBA_8888_GrPixelConfig, nullptr); + sk_sp<GrRenderTargetContext> s32RenderTargetContext = context->makeDeferredRenderTargetContext( + SkBackingFit::kExact, rtS, rtS, kSRGBA_8888_GrPixelConfig, std::move(srgbColorSpace)); + + SkRect rect = SkRect::MakeWH(SkIntToScalar(rtS), SkIntToScalar(rtS)); + GrNoClip noClip; + GrPaint paint; + paint.setPorterDuffXPFactory(SkBlendMode::kSrc); + GrSamplerParams mipMapParams(SkShader::kRepeat_TileMode, GrSamplerParams::kMipMap_FilterMode); + paint.addColorTextureProcessor(std::move(proxy), + nullptr, SkMatrix::MakeScale(rtS), mipMapParams); + + // 1) Draw texture to S32 surface (should generate/use sRGB mips) + paint.setGammaCorrect(true); + s32RenderTargetContext->drawRect(noClip, GrPaint(paint), GrAA::kNo, SkMatrix::I(), rect); + read_and_check_pixels(reporter, s32RenderTargetContext.get(), expectedSRGB, iiSRGBA, error, + "first render of sRGB"); + + // 2) Draw texture to L32 surface (should generate/use linear mips) + paint.setGammaCorrect(false); + l32RenderTargetContext->drawRect(noClip, GrPaint(paint), GrAA::kNo, SkMatrix::I(), rect); + + // Right now, this test only runs on GL (because Vulkan doesn't support legacy mip-mapping + // skbug.com/5048). On GL, we may not have sRGB decode support. In that case, rendering sRGB + // textures to a legacy surface produces nonsense, so this part of the test is meaningless. + // + // We also skip this part of the test on command buffer (via srgbDecodeDisableAffectsMipmaps), + // because that implementation of the extension doesn't ensure that mips respect the setting. + // + // TODO: Once Vulkan supports legacy mip-mapping, we can promote this to GrCaps. Right now, + // Vulkan has most of the functionality, but not the mip-mapping part that's being tested here. + GrGLGpu* glGpu = static_cast<GrGLGpu*>(context->getGpu()); + if (glGpu->glCaps().srgbDecodeDisableSupport() && + glGpu->glCaps().srgbDecodeDisableAffectsMipmaps()) { + read_and_check_pixels(reporter, l32RenderTargetContext.get(), expectedLinear, iiRGBA, + error, "re-render as linear"); + } + + // 3) Go back to sRGB + paint.setGammaCorrect(true); + s32RenderTargetContext->drawRect(noClip, std::move(paint), GrAA::kNo, SkMatrix::I(), rect); + read_and_check_pixels(reporter, s32RenderTargetContext.get(), expectedSRGB, iiSRGBA, error, + "re-render as sRGB"); +} +#endif
diff --git a/src/third_party/skia/tests/SRGBReadWritePixelsTest.cpp b/src/third_party/skia/tests/SRGBReadWritePixelsTest.cpp new file mode 100644 index 0000000..a52e961 --- /dev/null +++ b/src/third_party/skia/tests/SRGBReadWritePixelsTest.cpp
@@ -0,0 +1,256 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" +#if SK_SUPPORT_GPU +#include "GrCaps.h" +#include "GrContext.h" +#include "GrContextPriv.h" +#include "GrSurfaceContext.h" +#include "SkCanvas.h" +#include "SkGr.h" +#include "SkSurface.h" + +// using anonymous namespace because these functions are used as template params. +namespace { +/** convert 0..1 srgb value to 0..1 linear */ +float srgb_to_linear(float srgb) { + if (srgb <= 0.04045f) { + return srgb / 12.92f; + } else { + return powf((srgb + 0.055f) / 1.055f, 2.4f); + } +} + +/** convert 0..1 linear value to 0..1 srgb */ +float linear_to_srgb(float linear) { + if (linear <= 0.0031308) { + return linear * 12.92f; + } else { + return 1.055f * powf(linear, 1.f / 2.4f) - 0.055f; + } +} +} + +/** tests a conversion with an error tolerance */ +template <float (*CONVERT)(float)> static bool check_conversion(uint32_t input, uint32_t output, + float error) { + // alpha should always be exactly preserved. + if ((input & 0xff000000) != (output & 0xff000000)) { + return false; + } + + for (int c = 0; c < 3; ++c) { + uint8_t inputComponent = (uint8_t) ((input & (0xff << (c*8))) >> (c*8)); + float lower = SkTMax(0.f, (float) inputComponent - error); + float upper = SkTMin(255.f, (float) inputComponent + error); + lower = CONVERT(lower / 255.f); + upper = CONVERT(upper / 255.f); + SkASSERT(lower >= 0.f && lower <= 255.f); + SkASSERT(upper >= 0.f && upper <= 255.f); + uint8_t outputComponent = (output & (0xff << (c*8))) >> (c*8); + if (outputComponent < SkScalarFloorToInt(lower * 255.f) || + outputComponent > SkScalarCeilToInt(upper * 255.f)) { + return false; + } + } + return true; +} + +/** tests a forward and backward conversion with an error tolerance */ +template <float (*FORWARD)(float), float (*BACKWARD)(float)> +static bool check_double_conversion(uint32_t input, uint32_t output, float error) { + // alpha should always be exactly preserved. + if ((input & 0xff000000) != (output & 0xff000000)) { + return false; + } + + for (int c = 0; c < 3; ++c) { + uint8_t inputComponent = (uint8_t) ((input & (0xff << (c*8))) >> (c*8)); + float lower = SkTMax(0.f, (float) inputComponent - error); + float upper = SkTMin(255.f, (float) inputComponent + error); + lower = FORWARD(lower / 255.f); + upper = FORWARD(upper / 255.f); + SkASSERT(lower >= 0.f && lower <= 255.f); + SkASSERT(upper >= 0.f && upper <= 255.f); + uint8_t upperComponent = SkScalarCeilToInt(upper * 255.f); + uint8_t lowerComponent = SkScalarFloorToInt(lower * 255.f); + lower = SkTMax(0.f, (float) lowerComponent - error); + upper = SkTMin(255.f, (float) upperComponent + error); + lower = BACKWARD(lowerComponent / 255.f); + upper = BACKWARD(upperComponent / 255.f); + SkASSERT(lower >= 0.f && lower <= 255.f); + SkASSERT(upper >= 0.f && upper <= 255.f); + upperComponent = SkScalarCeilToInt(upper * 255.f); + lowerComponent = SkScalarFloorToInt(lower * 255.f); + + uint8_t outputComponent = (output & (0xff << (c*8))) >> (c*8); + if (outputComponent < lowerComponent || outputComponent > upperComponent) { + return false; + } + } + return true; +} + +static bool check_srgb_to_linear_conversion(uint32_t srgb, uint32_t linear, float error) { + return check_conversion<srgb_to_linear>(srgb, linear, error); +} + +static bool check_linear_to_srgb_conversion(uint32_t linear, uint32_t srgb, float error) { + return check_conversion<linear_to_srgb>(linear, srgb, error); +} + +static bool check_linear_to_srgb_to_linear_conversion(uint32_t input, uint32_t output, float error) { + return check_double_conversion<linear_to_srgb, srgb_to_linear>(input, output, error); +} + +static bool check_srgb_to_linear_to_srgb_conversion(uint32_t input, uint32_t output, float error) { + return check_double_conversion<srgb_to_linear, linear_to_srgb>(input, output, error); +} + +typedef bool (*CheckFn) (uint32_t orig, uint32_t actual, float error); + +void read_and_check_pixels(skiatest::Reporter* reporter, GrSurfaceContext* context, + uint32_t* origData, + const SkImageInfo& dstInfo, CheckFn checker, float error, + const char* subtestName) { + int w = dstInfo.width(); + int h = dstInfo.height(); + SkAutoTMalloc<uint32_t> readData(w * h); + memset(readData.get(), 0, sizeof(uint32_t) * w * h); + + if (!context->readPixels(dstInfo, readData.get(), 0, 0, 0)) { + ERRORF(reporter, "Could not read pixels for %s.", subtestName); + return; + } + + for (int j = 0; j < h; ++j) { + for (int i = 0; i < w; ++i) { + uint32_t orig = origData[j * w + i]; + uint32_t read = readData[j * w + i]; + + if (!checker(orig, read, error)) { + ERRORF(reporter, "Expected 0x%08x, read back as 0x%08x in %s at %d, %d).", + orig, read, subtestName, i, j); + return; + } + } + } +} + +// TODO: Add tests for copySurface between srgb/linear textures. Add tests for unpremul/premul +// conversion during read/write along with srgb/linear conversions. +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SRGBReadWritePixels, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); +#if defined(GOOGLE3) + // Stack frame size is limited in GOOGLE3. + static const int kW = 63; + static const int kH = 63; +#else + static const int kW = 255; + static const int kH = 255; +#endif + uint32_t origData[kW * kH]; + for (int j = 0; j < kH; ++j) { + for (int i = 0; i < kW; ++i) { + origData[j * kW + i] = (j << 24) | (i << 16) | (i << 8) | i; + } + } + + const SkImageInfo iiSRGBA = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, + kPremul_SkAlphaType, + SkColorSpace::MakeSRGB()); + const SkImageInfo iiRGBA = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, + kPremul_SkAlphaType); + GrSurfaceDesc desc; + desc.fFlags = kRenderTarget_GrSurfaceFlag; + desc.fOrigin = kBottomLeft_GrSurfaceOrigin; + desc.fWidth = kW; + desc.fHeight = kH; + desc.fConfig = kSRGBA_8888_GrPixelConfig; + if (context->caps()->isConfigRenderable(desc.fConfig, false) && + context->caps()->isConfigTexturable(desc.fConfig)) { + + sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeDeferredSurfaceContext( + desc, SkBackingFit::kExact, + SkBudgeted::kNo); + if (!sContext) { + ERRORF(reporter, "Could not create SRGBA surface context."); + return; + } + + float error = context->caps()->shaderCaps()->floatPrecisionVaries() ? 1.2f : 0.5f; + + // Write srgba data and read as srgba and then as rgba + if (sContext->writePixels(iiSRGBA, origData, 0, 0, 0)) { + // For the all-srgba case, we allow a small error only for devices that have + // precision variation because the srgba data gets converted to linear and back in + // the shader. + float smallError = context->caps()->shaderCaps()->floatPrecisionVaries() ? 1.f : 0.0f; + read_and_check_pixels(reporter, sContext.get(), origData, iiSRGBA, + check_srgb_to_linear_to_srgb_conversion, smallError, + "write/read srgba to srgba texture"); + read_and_check_pixels(reporter, sContext.get(), origData, iiRGBA, + check_srgb_to_linear_conversion, error, + "write srgba/read rgba with srgba texture"); + } else { + ERRORF(reporter, "Could not write srgba data to srgba texture."); + } + + // Now verify that we can write linear data + if (sContext->writePixels(iiRGBA, origData, 0, 0, 0)) { + // We allow more error on GPUs with lower precision shader variables. + read_and_check_pixels(reporter, sContext.get(), origData, iiSRGBA, + check_linear_to_srgb_conversion, error, + "write rgba/read srgba with srgba texture"); + read_and_check_pixels(reporter, sContext.get(), origData, iiRGBA, + check_linear_to_srgb_to_linear_conversion, error, + "write/read rgba with srgba texture"); + } else { + ERRORF(reporter, "Could not write rgba data to srgba texture."); + } + + desc.fConfig = kRGBA_8888_GrPixelConfig; + sContext = context->contextPriv().makeDeferredSurfaceContext(desc, SkBackingFit::kExact, + SkBudgeted::kNo); + if (!sContext) { + ERRORF(reporter, "Could not create RGBA surface context."); + return; + } + + // Write srgba data to a rgba texture and read back as srgba and rgba + if (sContext->writePixels(iiSRGBA, origData, 0, 0, 0)) { +#if 0 + // We don't support this conversion (read from untagged source into tagged destination. + // If we decide there is a meaningful way to implement this, restore this test. + read_and_check_pixels(reporter, sContext.get(), origData, iiSRGBA, + check_srgb_to_linear_to_srgb_conversion, error, + "write/read srgba to rgba texture"); +#endif + // We expect the sRGB -> linear write to do no sRGB conversion (to match the behavior of + // drawing tagged sources). skbug.com/6547. So the data we read should still contain + // sRGB encoded values. + // + // srgb_to_linear_to_srgb is a proxy for the expected identity transform. + read_and_check_pixels(reporter, sContext.get(), origData, iiRGBA, + check_srgb_to_linear_to_srgb_conversion, error, + "write srgba/read rgba to rgba texture"); + } else { + ERRORF(reporter, "Could not write srgba data to rgba texture."); + } + + // Write rgba data to a rgba texture and read back as srgba + if (sContext->writePixels(iiRGBA, origData, 0, 0, 0)) { + read_and_check_pixels(reporter, sContext.get(), origData, iiSRGBA, + check_linear_to_srgb_conversion, 1.2f, + "write rgba/read srgba to rgba texture"); + } else { + ERRORF(reporter, "Could not write rgba data to rgba texture."); + } + } +} +#endif
diff --git a/src/third_party/skia/tests/SRGBTest.cpp b/src/third_party/skia/tests/SRGBTest.cpp new file mode 100644 index 0000000..fc02f7d --- /dev/null +++ b/src/third_party/skia/tests/SRGBTest.cpp
@@ -0,0 +1,87 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkPM4f.h" +#include "SkRasterPipeline.h" +#include "SkSRGB.h" +#include "SkTypes.h" +#include "Test.h" +#include <math.h> + +static uint8_t linear_to_srgb(float l) { + return (uint8_t)sk_linear_to_srgb(Sk4f{l})[0]; +} + +DEF_TEST(sk_linear_to_srgb, r) { + // All bytes should round trip. + for (int i = 0; i < 256; i++) { + int actual = linear_to_srgb(sk_linear_from_srgb[i]); + if (i != actual) { + ERRORF(r, "%d -> %d\n", i, actual); + } + } + + // Should be monotonic between 0 and 1. + uint8_t prev = 0; + for (float f = FLT_MIN; f <= 1.0f; ) { // We don't bother checking denorm values. + uint8_t srgb = linear_to_srgb(f); + + REPORTER_ASSERT(r, srgb >= prev); + prev = srgb; + + union { float flt; uint32_t bits; } pun = { f }; + pun.bits++; + SkDEBUGCODE(pun.bits += 127); + f = pun.flt; + } +} + +DEF_TEST(sk_pipeline_srgb_roundtrip, r) { + uint32_t reds[256]; + for (int i = 0; i < 256; i++) { + reds[i] = i; + } + + auto ptr = (void*)reds; + + SkRasterPipeline_<256> p; + p.append(SkRasterPipeline::load_8888, &ptr); + p.append_from_srgb(kUnpremul_SkAlphaType); + p.append(SkRasterPipeline::to_srgb); + p.append(SkRasterPipeline::store_8888, &ptr); + + p.run(0,0,256); + + for (int i = 0; i < 256; i++) { + if (reds[i] != (uint32_t)i) { + ERRORF(r, "%d doesn't round trip, %d", i, reds[i]); + } + } +} + +DEF_TEST(sk_pipeline_srgb_edge_cases, r) { + // We need to run at least 4 pixels to make sure we hit all specializations. + SkPM4f colors[4] = { {{0,1,1,1}}, {{0,0,0,0}}, {{0,0,0,0}}, {{0,0,0,0}} }; + auto& color = colors[0]; + void* dst = &color; + + SkRasterPipeline_<256> p; + p.append(SkRasterPipeline::uniform_color, &color); + p.append(SkRasterPipeline::to_srgb); + p.append(SkRasterPipeline::store_f32, &dst); + p.run(0,0,4); + + if (color.r() != 0.0f) { + ERRORF(r, "expected to_srgb() to map 0.0f to 0.0f, got %f", color.r()); + } + if (color.g() != 1.0f) { + float f = color.g(); + uint32_t x; + memcpy(&x, &f, 4); + ERRORF(r, "expected to_srgb() to map 1.0f to 1.0f, got %f (%08x)", color.g(), x); + } +}
diff --git a/src/third_party/skia/tests/SVGDeviceTest.cpp b/src/third_party/skia/tests/SVGDeviceTest.cpp new file mode 100644 index 0000000..715dbc7 --- /dev/null +++ b/src/third_party/skia/tests/SVGDeviceTest.cpp
@@ -0,0 +1,146 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCanvas.h" +#include "SkData.h" +#include "SkDOM.h" +#include "SkParse.h" +#include "SkStream.h" +#include "SkSVGCanvas.h" +#include "SkXMLWriter.h" +#include "Test.h" + +#include <string.h> + +namespace { + +void check_text_node(skiatest::Reporter* reporter, + const SkDOM& dom, + const SkDOM::Node* root, + const SkPoint& offset, + unsigned scalarsPerPos, + const char* expected) { + if (root == nullptr) { + ERRORF(reporter, "root element not found."); + return; + } + + const SkDOM::Node* textElem = dom.getFirstChild(root, "text"); + if (textElem == nullptr) { + ERRORF(reporter, "<text> element not found."); + return; + } + REPORTER_ASSERT(reporter, dom.getType(textElem) == SkDOM::kElement_Type); + + const SkDOM::Node* textNode= dom.getFirstChild(textElem); + REPORTER_ASSERT(reporter, textNode != nullptr); + if (textNode != nullptr) { + REPORTER_ASSERT(reporter, dom.getType(textNode) == SkDOM::kText_Type); + REPORTER_ASSERT(reporter, strcmp(expected, dom.getName(textNode)) == 0); + } + + int textLen = SkToInt(strlen(expected)); + + const char* x = dom.findAttr(textElem, "x"); + REPORTER_ASSERT(reporter, x != nullptr); + if (x != nullptr) { + int xposCount = (scalarsPerPos < 1) ? 1 : textLen; + REPORTER_ASSERT(reporter, SkParse::Count(x) == xposCount); + + SkAutoTMalloc<SkScalar> xpos(xposCount); + SkParse::FindScalars(x, xpos.get(), xposCount); + if (scalarsPerPos < 1) { + REPORTER_ASSERT(reporter, xpos[0] == offset.x()); + } else { + for (int i = 0; i < xposCount; ++i) { + REPORTER_ASSERT(reporter, xpos[i] == SkIntToScalar(expected[i])); + } + } + } + + const char* y = dom.findAttr(textElem, "y"); + REPORTER_ASSERT(reporter, y != nullptr); + if (y != nullptr) { + int yposCount = (scalarsPerPos < 2) ? 1 : textLen; + REPORTER_ASSERT(reporter, SkParse::Count(y) == yposCount); + + SkAutoTMalloc<SkScalar> ypos(yposCount); + SkParse::FindScalars(y, ypos.get(), yposCount); + if (scalarsPerPos < 2) { + REPORTER_ASSERT(reporter, ypos[0] == offset.y()); + } else { + for (int i = 0; i < yposCount; ++i) { + REPORTER_ASSERT(reporter, ypos[i] == -SkIntToScalar(expected[i])); + } + } + } +} + +void test_whitespace_pos(skiatest::Reporter* reporter, + const char* txt, + const char* expected) { + size_t len = strlen(txt); + + SkDOM dom; + SkPaint paint; + SkPoint offset = SkPoint::Make(10, 20); + + { + SkXMLParserWriter writer(dom.beginParsing()); + std::unique_ptr<SkCanvas> svgCanvas = SkSVGCanvas::Make(SkRect::MakeWH(100, 100), &writer); + svgCanvas->drawText(txt, len, offset.x(), offset.y(), paint); + } + check_text_node(reporter, dom, dom.finishParsing(), offset, 0, expected); + + { + SkAutoTMalloc<SkScalar> xpos(len); + for (int i = 0; i < SkToInt(len); ++i) { + xpos[i] = SkIntToScalar(txt[i]); + } + + SkXMLParserWriter writer(dom.beginParsing()); + std::unique_ptr<SkCanvas> svgCanvas = SkSVGCanvas::Make(SkRect::MakeWH(100, 100), &writer); + svgCanvas->drawPosTextH(txt, len, xpos, offset.y(), paint); + } + check_text_node(reporter, dom, dom.finishParsing(), offset, 1, expected); + + { + SkAutoTMalloc<SkPoint> pos(len); + for (int i = 0; i < SkToInt(len); ++i) { + pos[i] = SkPoint::Make(SkIntToScalar(txt[i]), -SkIntToScalar(txt[i])); + } + + SkXMLParserWriter writer(dom.beginParsing()); + std::unique_ptr<SkCanvas> svgCanvas = SkSVGCanvas::Make(SkRect::MakeWH(100, 100), &writer); + svgCanvas->drawPosText(txt, len, pos, paint); + } + check_text_node(reporter, dom, dom.finishParsing(), offset, 2, expected); +} + +} + +DEF_TEST(SVGDevice_whitespace_pos, reporter) { + static const struct { + const char* tst_in; + const char* tst_out; + } tests[] = { + { "abcd" , "abcd" }, + { "ab cd" , "ab cd" }, + { "ab \t\t cd", "ab cd" }, + { " abcd" , "abcd" }, + { " abcd" , "abcd" }, + { " \t\t abcd", "abcd" }, + { "abcd " , "abcd " }, // we allow one trailing whitespace char + { "abcd " , "abcd " }, // because it makes no difference and + { "abcd\t " , "abcd\t" }, // simplifies the implementation + { "\t\t \t ab \t\t \t cd \t\t \t ", "ab cd " }, + }; + + for (unsigned i = 0; i < SK_ARRAY_COUNT(tests); ++i) { + test_whitespace_pos(reporter, tests[i].tst_in, tests[i].tst_out); + } +}
diff --git a/src/third_party/skia/tests/ScalarTest.cpp b/src/third_party/skia/tests/ScalarTest.cpp index b6f71d1..f2f924e 100644 --- a/src/third_party/skia/tests/ScalarTest.cpp +++ b/src/third_party/skia/tests/ScalarTest.cpp
@@ -54,7 +54,7 @@ static const struct { const SkPoint* fPts; - size_t fCount; + int fCount; bool fIsFinite; } gSets[] = { { gF0, SK_ARRAY_COUNT(gF0), true },
diff --git a/src/third_party/skia/tests/ScaleToSidesTest.cpp b/src/third_party/skia/tests/ScaleToSidesTest.cpp new file mode 100644 index 0000000..430bb8c --- /dev/null +++ b/src/third_party/skia/tests/ScaleToSidesTest.cpp
@@ -0,0 +1,65 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkScaleToSides.h" + +#include <algorithm> +#include "Test.h" + +DEF_TEST(ScaleToSides, reporter) { + double interestingValues[] = { + // From sample app - PathFuzzer + 260.01662826538085938, + 63.61007690429687500, + 795.98901367187500000, + 217.71697616577148438, + 686.15960693359375000, + 556.57641601562500000, + // From skp bitbucket + 111.60000228881836, + 55.800003051757813, + 0.99999996581812677920, + 0.0, + 0.5, + 1.0, + 2.0, + 3.0, + 33.0, + 33554430.0, + 33554431.0, + 33554464.0, + 333333332.0, + 333333333.0, + 333333334.0, + FLT_MAX, + FLT_EPSILON, + FLT_MIN + }; + + int numInterestingValues = (int)SK_ARRAY_COUNT(interestingValues); + + for (int s = 0; s <= numInterestingValues; s++) { + for (int i = 0; i < numInterestingValues; i++) { + for (int j = 0; j < numInterestingValues; j++) { + for (int k = 0; k < numInterestingValues; k++) { + float radius1 = (float)interestingValues[i]; + float radius2 = (float)interestingValues[j]; + double width = interestingValues[k]; + double scale = width / ((double)radius1 + (double)radius2); + if (width > 0.0) { + if (s != 0) { + scale = std::min(scale, interestingValues[s-1]); + } + if (scale < 1.0 && scale > 0.0) { + SkScaleToSides::AdjustRadii(width, scale, &radius1, &radius2); + } + } + } + } + } + } +}
diff --git a/src/third_party/skia/tests/SerializationTest.cpp b/src/third_party/skia/tests/SerializationTest.cpp index dd9b938..3c24c55 100644 --- a/src/third_party/skia/tests/SerializationTest.cpp +++ b/src/third_party/skia/tests/SerializationTest.cpp
@@ -6,17 +6,26 @@ */ #include "Resources.h" -#include "SkBitmapSource.h" +#include "SkAnnotationKeys.h" #include "SkCanvas.h" +#include "SkFixed.h" +#include "SkFontDescriptor.h" +#include "SkImage.h" +#include "SkImageSource.h" +#include "SkLightingShader.h" +#include "SkMakeUnique.h" #include "SkMallocPixelRef.h" +#include "SkNormalSource.h" #include "SkOSFile.h" #include "SkPictureRecorder.h" +#include "SkShaderBase.h" #include "SkTableColorFilter.h" #include "SkTemplates.h" #include "SkTypeface.h" #include "SkWriteBuffer.h" #include "SkValidatingReadBuffer.h" #include "SkXfermodeImageFilter.h" +#include "sk_tool_utils.h" #include "Test.h" static const uint32_t kArraySize = 64; @@ -96,6 +105,15 @@ } }; +template<> struct SerializationUtils<SkColor4f> { + static void Write(SkWriteBuffer& writer, SkColor4f* data, uint32_t arraySize) { + writer.writeColor4fArray(data, arraySize); + } + static bool Read(SkValidatingReadBuffer& reader, SkColor4f* data, uint32_t arraySize) { + return reader.readColor4fArray(data, arraySize); + } +}; + template<> struct SerializationUtils<int32_t> { static void Write(SkWriteBuffer& writer, int32_t* data, uint32_t arraySize) { writer.writeIntArray(data, arraySize); @@ -135,7 +153,7 @@ template<typename T, bool testInvalid> static void TestObjectSerializationNoAlign(T* testObj, skiatest::Reporter* reporter) { - SkWriteBuffer writer(SkWriteBuffer::kValidation_Flag); + SkBinaryWriteBuffer writer; SerializationUtils<T>::Write(writer, testObj); size_t bytesWritten = writer.bytesWritten(); REPORTER_ASSERT(reporter, SkAlign4(bytesWritten) == bytesWritten); @@ -153,15 +171,15 @@ // Make sure this succeeds when it should SkValidatingReadBuffer buffer2(dataWritten, bytesWritten); - const unsigned char* peekBefore = static_cast<const unsigned char*>(buffer2.skip(0)); + size_t offsetBefore = buffer2.offset(); T obj2; SerializationUtils<T>::Read(buffer2, &obj2); - const unsigned char* peekAfter = static_cast<const unsigned char*>(buffer2.skip(0)); + size_t offsetAfter = buffer2.offset(); // This should have succeeded, since there are enough bytes to read this REPORTER_ASSERT(reporter, buffer2.isValid() == !testInvalid); // Note: This following test should always succeed, regardless of whether the buffer is valid, // since if it is invalid, it will simply skip to the end, as if it had read the whole buffer. - REPORTER_ASSERT(reporter, static_cast<size_t>(peekAfter - peekBefore) == bytesWritten); + REPORTER_ASSERT(reporter, offsetAfter - offsetBefore == bytesWritten); } template<typename T> @@ -173,26 +191,26 @@ template<typename T> static T* TestFlattenableSerialization(T* testObj, bool shouldSucceed, skiatest::Reporter* reporter) { - SkWriteBuffer writer(SkWriteBuffer::kValidation_Flag); + SkBinaryWriteBuffer writer; SerializationUtils<T>::Write(writer, testObj); size_t bytesWritten = writer.bytesWritten(); REPORTER_ASSERT(reporter, SkAlign4(bytesWritten) == bytesWritten); + SkASSERT(bytesWritten <= 4096); unsigned char dataWritten[4096]; - SkASSERT(bytesWritten <= sizeof(dataWritten)); writer.writeToMemory(dataWritten); // Make sure this fails when it should (test with smaller size, but still multiple of 4) SkValidatingReadBuffer buffer(dataWritten, bytesWritten - 4); - T* obj = NULL; + T* obj = nullptr; SerializationUtils<T>::Read(buffer, &obj); REPORTER_ASSERT(reporter, !buffer.isValid()); - REPORTER_ASSERT(reporter, NULL == obj); + REPORTER_ASSERT(reporter, nullptr == obj); // Make sure this succeeds when it should SkValidatingReadBuffer buffer2(dataWritten, bytesWritten); const unsigned char* peekBefore = static_cast<const unsigned char*>(buffer2.skip(0)); - T* obj2 = NULL; + T* obj2 = nullptr; SerializationUtils<T>::Read(buffer2, &obj2); const unsigned char* peekAfter = static_cast<const unsigned char*>(buffer2.skip(0)); if (shouldSucceed) { @@ -203,7 +221,7 @@ } else { // If the deserialization was supposed to fail, make sure it did REPORTER_ASSERT(reporter, !buffer.isValid()); - REPORTER_ASSERT(reporter, NULL == obj2); + REPORTER_ASSERT(reporter, nullptr == obj2); } return obj2; // Return object to perform further validity tests on it @@ -211,13 +229,13 @@ template<typename T> static void TestArraySerialization(T* data, skiatest::Reporter* reporter) { - SkWriteBuffer writer(SkWriteBuffer::kValidation_Flag); + SkBinaryWriteBuffer writer; SerializationUtils<T>::Write(writer, data, kArraySize); size_t bytesWritten = writer.bytesWritten(); // This should write the length (in 4 bytes) and the array REPORTER_ASSERT(reporter, (4 + kArraySize * sizeof(T)) == bytesWritten); - unsigned char dataWritten[1024]; + unsigned char dataWritten[2048]; writer.writeToMemory(dataWritten); // Make sure this fails when it should @@ -238,15 +256,18 @@ const SkBitmap& invalidBitmap, bool shouldSucceed, skiatest::Reporter* reporter) { - SkAutoTUnref<SkBitmapSource> validBitmapSource(SkBitmapSource::Create(validBitmap)); - SkAutoTUnref<SkBitmapSource> invalidBitmapSource(SkBitmapSource::Create(invalidBitmap)); - SkAutoTUnref<SkXfermode> mode(SkXfermode::Create(SkXfermode::kSrcOver_Mode)); - SkAutoTUnref<SkXfermodeImageFilter> xfermodeImageFilter( - SkXfermodeImageFilter::Create(mode, invalidBitmapSource, validBitmapSource)); + sk_sp<SkImage> validImage(SkImage::MakeFromBitmap(validBitmap)); + sk_sp<SkImageFilter> validBitmapSource(SkImageSource::Make(std::move(validImage))); + sk_sp<SkImage> invalidImage(SkImage::MakeFromBitmap(invalidBitmap)); + sk_sp<SkImageFilter> invalidBitmapSource(SkImageSource::Make(std::move(invalidImage))); + sk_sp<SkImageFilter> xfermodeImageFilter( + SkXfermodeImageFilter::Make(SkBlendMode::kSrcOver, + std::move(invalidBitmapSource), + std::move(validBitmapSource), nullptr)); - SkAutoTUnref<SkImageFilter> deserializedFilter( + sk_sp<SkImageFilter> deserializedFilter( TestFlattenableSerialization<SkImageFilter>( - xfermodeImageFilter, shouldSucceed, reporter)); + xfermodeImageFilter.get(), shouldSucceed, reporter)); // Try to render a small bitmap using the invalid deserialized filter // to make sure we don't crash while trying to render it @@ -262,32 +283,19 @@ } } -static void TestXfermodeSerialization(skiatest::Reporter* reporter) { - for (size_t i = 0; i <= SkXfermode::kLastMode; ++i) { - if (i == SkXfermode::kSrcOver_Mode) { - // skip SrcOver, as it is allowed to return NULL from Create() - continue; - } - SkAutoTUnref<SkXfermode> mode(SkXfermode::Create(static_cast<SkXfermode::Mode>(i))); - REPORTER_ASSERT(reporter, mode.get()); - SkAutoTUnref<SkXfermode> copy( - TestFlattenableSerialization<SkXfermode>(mode.get(), true, reporter)); - } -} - static void TestColorFilterSerialization(skiatest::Reporter* reporter) { uint8_t table[256]; for (int i = 0; i < 256; ++i) { table[i] = (i * 41) % 256; } - SkAutoTUnref<SkColorFilter> colorFilter(SkTableColorFilter::Create(table)); - SkAutoTUnref<SkColorFilter> copy( + auto colorFilter(SkTableColorFilter::Make(table)); + sk_sp<SkColorFilter> copy( TestFlattenableSerialization<SkColorFilter>(colorFilter.get(), true, reporter)); } static SkBitmap draw_picture(SkPicture& picture) { SkBitmap bitmap; - bitmap.allocN32Pixels(SkScalarCeilToInt(picture.cullRect().width()), + bitmap.allocN32Pixels(SkScalarCeilToInt(picture.cullRect().width()), SkScalarCeilToInt(picture.cullRect().height())); SkCanvas canvas(bitmap); picture.playback(&canvas); @@ -298,8 +306,6 @@ const SkBitmap& b1, const SkBitmap& b2) { REPORTER_ASSERT(reporter, b1.width() == b2.width()); REPORTER_ASSERT(reporter, b1.height() == b2.height()); - SkAutoLockPixels autoLockPixels1(b1); - SkAutoLockPixels autoLockPixels2(b2); if ((b1.width() != b2.width()) || (b1.height() != b2.height())) { @@ -315,43 +321,30 @@ } REPORTER_ASSERT(reporter, 0 == pixelErrors); } - -static void TestPictureTypefaceSerialization(skiatest::Reporter* reporter) { - // Load typeface form file. - // This test cannot run if there is no resource path. - SkString resourcePath = GetResourcePath(); - if (resourcePath.isEmpty()) { - SkDebugf("Could not run fontstream test because resourcePath not specified."); - return; - } - SkString filename = SkOSPath::Join(resourcePath.c_str(), "test.ttc"); - SkTypeface* typeface = SkTypeface::CreateFromFile(filename.c_str(), 1); - if (!typeface) { - SkDebugf("Could not run fontstream test because test.ttc not found."); - return; - } - - // Create a paint with the typeface we loaded. +static void serialize_and_compare_typeface(sk_sp<SkTypeface> typeface, const char* text, + skiatest::Reporter* reporter) +{ + // Create a paint with the typeface. SkPaint paint; paint.setColor(SK_ColorGRAY); paint.setTextSize(SkIntToScalar(30)); - SkSafeUnref(paint.setTypeface(typeface)); + paint.setTypeface(std::move(typeface)); // Paint some text. SkPictureRecorder recorder; SkIRect canvasRect = SkIRect::MakeWH(kBitmapSize, kBitmapSize); - SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(canvasRect.width()), - SkIntToScalar(canvasRect.height()), - NULL, 0); + SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(canvasRect.width()), + SkIntToScalar(canvasRect.height()), + nullptr, 0); canvas->drawColor(SK_ColorWHITE); - canvas->drawText("A!", 2, 24, 32, paint); - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); + canvas->drawText(text, 2, 24, 32, paint); + sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); // Serlialize picture and create its clone from stream. SkDynamicMemoryWStream stream; picture->serialize(&stream); - SkAutoTUnref<SkStream> inputStream(stream.detachAsStream()); - SkAutoTUnref<SkPicture> loadedPicture(SkPicture::CreateFromStream(inputStream.get())); + std::unique_ptr<SkStream> inputStream(stream.detachAsStream()); + sk_sp<SkPicture> loadedPicture(SkPicture::MakeFromStream(inputStream.get())); // Draw both original and clone picture and compare bitmaps -- they should be identical. SkBitmap origBitmap = draw_picture(*picture); @@ -359,6 +352,36 @@ compare_bitmaps(reporter, origBitmap, destBitmap); } +static void TestPictureTypefaceSerialization(skiatest::Reporter* reporter) { + { + // Load typeface from file to test CreateFromFile with index. + SkString filename = GetResourcePath("/fonts/test.ttc"); + sk_sp<SkTypeface> typeface(SkTypeface::MakeFromFile(filename.c_str(), 1)); + if (!typeface) { + INFOF(reporter, "Could not run fontstream test because test.ttc not found."); + } else { + serialize_and_compare_typeface(std::move(typeface), "A!", reporter); + } + } + + { + // Load typeface as stream to create with axis settings. + std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("/fonts/Distortable.ttf")); + if (!distortable) { + INFOF(reporter, "Could not run fontstream test because Distortable.ttf not found."); + } else { + SkFixed axis = SK_FixedSqrt2; + sk_sp<SkTypeface> typeface(SkTypeface::MakeFromFontData( + skstd::make_unique<SkFontData>(std::move(distortable), 0, &axis, 1))); + if (!typeface) { + INFOF(reporter, "Could not run fontstream test because Distortable.ttf not created."); + } else { + serialize_and_compare_typeface(std::move(typeface), "abc", reporter); + } + } + } +} + static void setup_bitmap_for_canvas(SkBitmap* bitmap) { bitmap->allocN32Pixels(kBitmapSize, kBitmapSize); } @@ -394,21 +417,16 @@ canvas->save(); canvas->scale(0.5f, 0.5f); - canvas->drawBitmap(bitmap, 0, 0, NULL); + canvas->drawBitmap(bitmap, 0, 0, nullptr); canvas->restore(); - const char beforeStr[] = "before circle"; - const char afterStr[] = "after circle"; - paint.setAntiAlias(true); paint.setColor(SK_ColorRED); - canvas->drawData(beforeStr, sizeof(beforeStr)); canvas->drawCircle(SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/3), paint); - canvas->drawData(afterStr, sizeof(afterStr)); paint.setColor(SK_ColorBLACK); paint.setTextSize(SkIntToScalar(kBitmapSize/3)); - canvas->drawText("Picture", 7, SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/4), paint); + canvas->drawString("Picture", SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/4), paint); } DEF_TEST(Serialization, reporter) { @@ -430,11 +448,6 @@ TestObjectSerialization(®ion, reporter); } - // Test xfermode serialization - { - TestXfermodeSerialization(reporter); - } - // Test color filter serialization { TestColorFilterSerialization(reporter); @@ -471,6 +484,17 @@ TestArraySerialization(data, reporter); } + // Test readColor4fArray + { + SkColor4f data[kArraySize] = { + SkColor4f::FromColor(SK_ColorBLACK), + SkColor4f::FromColor(SK_ColorWHITE), + SkColor4f::FromColor(SK_ColorRED), + { 1.f, 2.f, 4.f, 8.f } + }; + TestArraySerialization(data, reporter); + } + // Test readIntArray { int32_t data[kArraySize] = { 1, 2, 4, 8 }; @@ -510,11 +534,11 @@ SkPictureRecorder recorder; draw_something(recorder.beginRecording(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize), - NULL, 0)); - SkAutoTUnref<SkPicture> pict(recorder.endRecording()); + nullptr, 0)); + sk_sp<SkPicture> pict(recorder.finishRecordingAsPicture()); // Serialize picture - SkWriteBuffer writer(SkWriteBuffer::kValidation_Flag); + SkBinaryWriteBuffer writer; pict->flatten(writer); size_t size = writer.bytesWritten(); SkAutoTMalloc<unsigned char> data(size); @@ -522,10 +546,149 @@ // Deserialize picture SkValidatingReadBuffer reader(static_cast<void*>(data.get()), size); - SkAutoTUnref<SkPicture> readPict( - SkPicture::CreateFromBuffer(reader)); + sk_sp<SkPicture> readPict(SkPicture::MakeFromBuffer(reader)); + REPORTER_ASSERT(reporter, reader.isValid()); REPORTER_ASSERT(reporter, readPict.get()); } TestPictureTypefaceSerialization(reporter); + + // Test SkLightingShader/NormalMapSource serialization + { + const int kTexSize = 2; + + SkLights::Builder builder; + + builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f), + SkVector3::Make(1.0f, 0.0f, 0.0f))); + builder.setAmbientLightColor(SkColor3f::Make(0.2f, 0.2f, 0.2f)); + + sk_sp<SkLights> fLights = builder.finish(); + + SkBitmap diffuse = sk_tool_utils::create_checkerboard_bitmap( + kTexSize, kTexSize, + sk_tool_utils::color_to_565(0x0), + sk_tool_utils::color_to_565(0xFF804020), + 8); + + SkRect bitmapBounds = SkRect::MakeIWH(diffuse.width(), diffuse.height()); + + SkMatrix matrix; + SkRect r = SkRect::MakeWH(SkIntToScalar(kTexSize), SkIntToScalar(kTexSize)); + matrix.setRectToRect(bitmapBounds, r, SkMatrix::kFill_ScaleToFit); + + SkMatrix ctm; + ctm.setRotate(45); + SkBitmap normals; + normals.allocN32Pixels(kTexSize, kTexSize); + + sk_tool_utils::create_frustum_normal_map(&normals, SkIRect::MakeWH(kTexSize, kTexSize)); + sk_sp<SkShader> normalMap = SkShader::MakeBitmapShader(normals, SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, &matrix); + sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeFromNormalMap(std::move(normalMap), + ctm); + sk_sp<SkShader> diffuseShader = SkShader::MakeBitmapShader(diffuse, + SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix); + + sk_sp<SkShader> lightingShader = SkLightingShader::Make(diffuseShader, + normalSource, + fLights); + sk_sp<SkShader>(TestFlattenableSerialization(as_SB(lightingShader.get()), true, reporter)); + + lightingShader = SkLightingShader::Make(std::move(diffuseShader), + nullptr, + fLights); + sk_sp<SkShader>(TestFlattenableSerialization(as_SB(lightingShader.get()), true, reporter)); + + lightingShader = SkLightingShader::Make(nullptr, + std::move(normalSource), + fLights); + sk_sp<SkShader>(TestFlattenableSerialization(as_SB(lightingShader.get()), true, reporter)); + + lightingShader = SkLightingShader::Make(nullptr, + nullptr, + fLights); + sk_sp<SkShader>(TestFlattenableSerialization(as_SB(lightingShader.get()), true, reporter)); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#include "SkAnnotation.h" + +static sk_sp<SkPicture> copy_picture_via_serialization(SkPicture* src) { + SkDynamicMemoryWStream wstream; + src->serialize(&wstream); + std::unique_ptr<SkStreamAsset> rstream(wstream.detachAsStream()); + return SkPicture::MakeFromStream(rstream.get()); +} + +struct AnnotationRec { + const SkRect fRect; + const char* fKey; + sk_sp<SkData> fValue; +}; + +class TestAnnotationCanvas : public SkCanvas { + skiatest::Reporter* fReporter; + const AnnotationRec* fRec; + int fCount; + int fCurrIndex; + +public: + TestAnnotationCanvas(skiatest::Reporter* reporter, const AnnotationRec rec[], int count) + : SkCanvas(100, 100) + , fReporter(reporter) + , fRec(rec) + , fCount(count) + , fCurrIndex(0) + {} + + ~TestAnnotationCanvas() { + REPORTER_ASSERT(fReporter, fCount == fCurrIndex); + } + +protected: + void onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) { + REPORTER_ASSERT(fReporter, fCurrIndex < fCount); + REPORTER_ASSERT(fReporter, rect == fRec[fCurrIndex].fRect); + REPORTER_ASSERT(fReporter, !strcmp(key, fRec[fCurrIndex].fKey)); + REPORTER_ASSERT(fReporter, value->equals(fRec[fCurrIndex].fValue.get())); + fCurrIndex += 1; + } +}; + +/* + * Test the 3 annotation types by recording them into a picture, serializing, and then playing + * them back into another canvas. + */ +DEF_TEST(Annotations, reporter) { + SkPictureRecorder recorder; + SkCanvas* recordingCanvas = recorder.beginRecording(SkRect::MakeWH(100, 100)); + + const char* str0 = "rect-with-url"; + const SkRect r0 = SkRect::MakeWH(10, 10); + sk_sp<SkData> d0(SkData::MakeWithCString(str0)); + SkAnnotateRectWithURL(recordingCanvas, r0, d0.get()); + + const char* str1 = "named-destination"; + const SkRect r1 = SkRect::MakeXYWH(5, 5, 0, 0); // collapsed to a point + sk_sp<SkData> d1(SkData::MakeWithCString(str1)); + SkAnnotateNamedDestination(recordingCanvas, {r1.x(), r1.y()}, d1.get()); + + const char* str2 = "link-to-destination"; + const SkRect r2 = SkRect::MakeXYWH(20, 20, 5, 6); + sk_sp<SkData> d2(SkData::MakeWithCString(str2)); + SkAnnotateLinkToDestination(recordingCanvas, r2, d2.get()); + + const AnnotationRec recs[] = { + { r0, SkAnnotationKeys::URL_Key(), std::move(d0) }, + { r1, SkAnnotationKeys::Define_Named_Dest_Key(), std::move(d1) }, + { r2, SkAnnotationKeys::Link_Named_Dest_Key(), std::move(d2) }, + }; + + sk_sp<SkPicture> pict0(recorder.finishRecordingAsPicture()); + sk_sp<SkPicture> pict1(copy_picture_via_serialization(pict0.get())); + + TestAnnotationCanvas canvas(reporter, recs, SK_ARRAY_COUNT(recs)); + canvas.drawPicture(pict1); }
diff --git a/src/third_party/skia/tests/ShaderImageFilterTest.cpp b/src/third_party/skia/tests/ShaderImageFilterTest.cpp deleted file mode 100644 index 45fb5ca..0000000 --- a/src/third_party/skia/tests/ShaderImageFilterTest.cpp +++ /dev/null
@@ -1,61 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkCanvas.h" -#include "SkGradientShader.h" -#include "SkRectShaderImageFilter.h" -#include "SkShader.h" -#include "Test.h" - -DEF_TEST(ShaderImageFilter, reporter) { - int w = 10, h = 10; - SkRect r = SkRect::MakeWH(SkIntToScalar(w), SkIntToScalar(h)); // Make small 10x10 gradient - - SkBitmap filterResult, shaderResult; - - filterResult.allocN32Pixels(w, h); - SkCanvas canvasFilter(filterResult); - canvasFilter.clear(0x00000000); - - shaderResult.allocN32Pixels(w, h); - SkCanvas canvasShader(shaderResult); - canvasShader.clear(0x00000000); - - SkPoint center = SkPoint::Make(SkIntToScalar(5), SkIntToScalar(5)); - SkColor colors[] = {SK_ColorBLUE, SK_ColorRED, SK_ColorGREEN}; - SkScalar pos[] = {0, SK_ScalarHalf, SK_Scalar1}; - SkScalar radius = SkIntToScalar(5); - - // Test using the image filter - { - SkShader* s = SkGradientShader::CreateRadial( - center, radius, colors, pos, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode); - SkPaint paint; - SkImageFilter::CropRect cr(r); - paint.setImageFilter(SkRectShaderImageFilter::Create(s, &cr))->unref(); - canvasFilter.drawRect(r, paint); - s->unref(); - } - - // Test using the shader directly - { - SkShader* s = SkGradientShader::CreateRadial( - center, radius, colors, pos, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode); - SkPaint paint; - paint.setShader(s)->unref(); - canvasShader.drawRect(r, paint); - } - - // Assert that both paths yielded the same result - for (int y = 0; y < r.height(); ++y) { - const SkPMColor* filterPtr = filterResult.getAddr32(0, y); - const SkPMColor* shaderPtr = shaderResult.getAddr32(0, y); - for (int x = 0; x < r.width(); ++x, ++filterPtr, ++shaderPtr) { - REPORTER_ASSERT(reporter, *filterPtr == *shaderPtr); - } - } -}
diff --git a/src/third_party/skia/tests/ShaderOpacityTest.cpp b/src/third_party/skia/tests/ShaderOpacityTest.cpp index 90d25d7..698d544 100644 --- a/src/third_party/skia/tests/ShaderOpacityTest.cpp +++ b/src/third_party/skia/tests/ShaderOpacityTest.cpp
@@ -17,42 +17,36 @@ bmp.setInfo(info); // test 1: bitmap without pixel data - SkShader* shader = SkShader::CreateBitmapShader(bmp, + auto shader = SkShader::MakeBitmapShader(bmp, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); REPORTER_ASSERT(reporter, shader); REPORTER_ASSERT(reporter, !shader->isOpaque()); - shader->unref(); // From this point on, we have pixels bmp.allocPixels(info); // test 2: not opaque by default - shader = SkShader::CreateBitmapShader(bmp, + shader = SkShader::MakeBitmapShader(bmp, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); REPORTER_ASSERT(reporter, shader); REPORTER_ASSERT(reporter, !shader->isOpaque()); - shader->unref(); // test 3: explicitly opaque bmp.setAlphaType(kOpaque_SkAlphaType); - shader = SkShader::CreateBitmapShader(bmp, + shader = SkShader::MakeBitmapShader(bmp, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); REPORTER_ASSERT(reporter, shader); REPORTER_ASSERT(reporter, shader->isOpaque()); - shader->unref(); // test 4: explicitly not opaque bmp.setAlphaType(kPremul_SkAlphaType); - shader = SkShader::CreateBitmapShader(bmp, + shader = SkShader::MakeBitmapShader(bmp, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); REPORTER_ASSERT(reporter, shader); REPORTER_ASSERT(reporter, !shader->isOpaque()); - shader->unref(); - } -static void test_gradient(skiatest::Reporter* reporter) -{ +static void test_gradient(skiatest::Reporter* reporter) { SkPoint pts[2]; pts[0].iset(0, 0); pts[1].iset(1, 0); @@ -64,39 +58,33 @@ // test 1: all opaque colors[0] = SkColorSetARGB(0xFF, 0, 0, 0); colors[1] = SkColorSetARGB(0xFF, 0, 0, 0); - SkShader* grad = SkGradientShader::CreateLinear(pts, colors, pos, count, - mode); + auto grad = SkGradientShader::MakeLinear(pts, colors, pos, count, mode); REPORTER_ASSERT(reporter, grad); REPORTER_ASSERT(reporter, grad->isOpaque()); - grad->unref(); // test 2: all 0 alpha colors[0] = SkColorSetARGB(0, 0, 0, 0); colors[1] = SkColorSetARGB(0, 0, 0, 0); - grad = SkGradientShader::CreateLinear(pts, colors, pos, count, mode); + grad = SkGradientShader::MakeLinear(pts, colors, pos, count, mode); REPORTER_ASSERT(reporter, grad); REPORTER_ASSERT(reporter, !grad->isOpaque()); - grad->unref(); // test 3: one opaque, one transparent colors[0] = SkColorSetARGB(0xFF, 0, 0, 0); colors[1] = SkColorSetARGB(0x40, 0, 0, 0); - grad = SkGradientShader::CreateLinear(pts, colors, pos, count, mode); + grad = SkGradientShader::MakeLinear(pts, colors, pos, count, mode); REPORTER_ASSERT(reporter, grad); REPORTER_ASSERT(reporter, !grad->isOpaque()); - grad->unref(); // test 4: test 3, swapped colors[0] = SkColorSetARGB(0x40, 0, 0, 0); colors[1] = SkColorSetARGB(0xFF, 0, 0, 0); - grad = SkGradientShader::CreateLinear(pts, colors, pos, count, mode); + grad = SkGradientShader::MakeLinear(pts, colors, pos, count, mode); REPORTER_ASSERT(reporter, grad); REPORTER_ASSERT(reporter, !grad->isOpaque()); - grad->unref(); } -static void test_color(skiatest::Reporter* reporter) -{ +static void test_color(skiatest::Reporter* reporter) { SkColorShader colorShader1(SkColorSetARGB(0,0,0,0)); REPORTER_ASSERT(reporter, !colorShader1.isOpaque()); SkColorShader colorShader2(SkColorSetARGB(0xFF,0,0,0));
diff --git a/src/third_party/skia/tests/ShaderTest.cpp b/src/third_party/skia/tests/ShaderTest.cpp new file mode 100644 index 0000000..06aca1c --- /dev/null +++ b/src/third_party/skia/tests/ShaderTest.cpp
@@ -0,0 +1,81 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkImage.h" +#include "SkPerlinNoiseShader.h" +#include "SkRRect.h" +#include "SkShader.h" +#include "SkSurface.h" +#include "SkData.h" + +static void check_isabitmap(skiatest::Reporter* reporter, SkShader* shader, + int expectedW, int expectedH, + SkShader::TileMode expectedX, SkShader::TileMode expectedY, + const SkMatrix& expectedM) { + SkShader::TileMode tileModes[2]; + SkMatrix localM; + +#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP + SkBitmap bm; + REPORTER_ASSERT(reporter, shader->isABitmap(&bm, &localM, tileModes)); + REPORTER_ASSERT(reporter, bm.width() == expectedW); + REPORTER_ASSERT(reporter, bm.height() == expectedH); + REPORTER_ASSERT(reporter, localM == expectedM); + REPORTER_ASSERT(reporter, tileModes[0] == expectedX); + REPORTER_ASSERT(reporter, tileModes[1] == expectedY); +#endif + + // wack these so we don't get a false positive + localM.setScale(9999, -9999); + tileModes[0] = tileModes[1] = (SkShader::TileMode)99; + + SkImage* image = shader->isAImage(&localM, tileModes); + REPORTER_ASSERT(reporter, image); + REPORTER_ASSERT(reporter, image->width() == expectedW); + REPORTER_ASSERT(reporter, image->height() == expectedH); + REPORTER_ASSERT(reporter, localM == expectedM); + REPORTER_ASSERT(reporter, tileModes[0] == expectedX); + REPORTER_ASSERT(reporter, tileModes[1] == expectedY); +} + +DEF_TEST(Shader_isABitmap, reporter) { + const int W = 100; + const int H = 100; + SkBitmap bm; + bm.allocN32Pixels(W, H); + auto img = SkImage::MakeFromBitmap(bm); + const SkMatrix localM = SkMatrix::MakeScale(2, 3); + const SkShader::TileMode tmx = SkShader::kRepeat_TileMode; + const SkShader::TileMode tmy = SkShader::kMirror_TileMode; + + auto shader0 = SkShader::MakeBitmapShader(bm, tmx, tmy, &localM); + auto shader1 = SkImage::MakeFromBitmap(bm)->makeShader(tmx, tmy, &localM); + + check_isabitmap(reporter, shader0.get(), W, H, tmx, tmy, localM); + check_isabitmap(reporter, shader1.get(), W, H, tmx, tmy, localM); +} + +// Make sure things are ok with just a single leg. +DEF_TEST(ComposeShaderSingle, reporter) { + SkBitmap srcBitmap; + srcBitmap.allocN32Pixels(10, 10); + srcBitmap.eraseColor(SK_ColorRED); + SkCanvas canvas(srcBitmap); + SkPaint p; + p.setShader( + SkShader::MakeComposeShader( + SkShader::MakeEmptyShader(), + SkPerlinNoiseShader::MakeFractalNoise(1.0f, 1.0f, 2, 0.0f), + SkBlendMode::kClear)); + SkRRect rr; + SkVector rd[] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; + rr.setRectRadii({0, 0, 0, 0}, rd); + canvas.drawRRect(rr, p); +}
diff --git a/src/third_party/skia/tests/ShadowUtilsTest.cpp b/src/third_party/skia/tests/ShadowUtilsTest.cpp new file mode 100644 index 0000000..c0e20d5 --- /dev/null +++ b/src/third_party/skia/tests/ShadowUtilsTest.cpp
@@ -0,0 +1,60 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCanvas.h" +#include "SkPath.h" +#include "SkShadowTessellator.h" +#include "SkShadowUtils.h" +#include "SkVertices.h" +#include "Test.h" + +void tessellate_shadow(skiatest::Reporter* reporter, const SkPath& path, const SkMatrix& ctm, + bool expectSuccess) { + + auto heightParams = SkPoint3::Make(0, 0, 4); + + auto verts = SkShadowTessellator::MakeAmbient(path, ctm, heightParams, true); + if (expectSuccess != SkToBool(verts)) { + ERRORF(reporter, "Expected shadow tessellation to %s but it did not.", + expectSuccess ? "succeed" : "fail"); + } + verts = SkShadowTessellator::MakeAmbient(path, ctm, heightParams, false); + if (expectSuccess != SkToBool(verts)) { + ERRORF(reporter, "Expected shadow tessellation to %s but it did not.", + expectSuccess ? "succeed" : "fail"); + } + verts = SkShadowTessellator::MakeSpot(path, ctm, heightParams, {0, 0, 128}, 128.f, false); + if (expectSuccess != SkToBool(verts)) { + ERRORF(reporter, "Expected shadow tessellation to %s but it did not.", + expectSuccess ? "succeed" : "fail"); + } + verts = SkShadowTessellator::MakeSpot(path, ctm, heightParams, {0, 0, 128}, 128.f, false); + if (expectSuccess != SkToBool(verts)) { + ERRORF(reporter, "Expected shadow tessellation to %s but it did not.", + expectSuccess ? "succeed" : "fail"); + } +} + +DEF_TEST(ShadowUtils, reporter) { + SkCanvas canvas(100, 100); + + SkPath path; + path.cubicTo(100, 50, 20, 100, 0, 0); + tessellate_shadow(reporter, path, canvas.getTotalMatrix(), true); + + // This line segment has no area and no shadow. + path.reset(); + path.lineTo(10.f, 10.f); + tessellate_shadow(reporter, path, canvas.getTotalMatrix(), false); + + // A series of colinear line segments + path.reset(); + for (int i = 0; i < 10; ++i) { + path.lineTo((SkScalar)i, (SkScalar)i); + } + tessellate_shadow(reporter, path, canvas.getTotalMatrix(), false); +}
diff --git a/src/third_party/skia/tests/SizeTest.cpp b/src/third_party/skia/tests/SizeTest.cpp index 9800aa2..9dccdeb 100644 --- a/src/third_party/skia/tests/SizeTest.cpp +++ b/src/third_party/skia/tests/SizeTest.cpp
@@ -16,7 +16,7 @@ REPORTER_ASSERT(reporter, a.isEmpty()); a.set(5, -5); REPORTER_ASSERT(reporter, a.isEmpty()); - a.clampNegToZero(); + a = SkISize{5, 0}; REPORTER_ASSERT(reporter, a.isEmpty()); b.set(5, 0); REPORTER_ASSERT(reporter, a == b); @@ -42,7 +42,7 @@ REPORTER_ASSERT(reporter, a.isEmpty()); a.set(x, -x); REPORTER_ASSERT(reporter, a.isEmpty()); - a.clampNegToZero(); + a = SkSize{x, 0}; REPORTER_ASSERT(reporter, a.isEmpty()); b.set(x, 0); REPORTER_ASSERT(reporter, a == b);
diff --git a/src/third_party/skia/tests/Sk4x4fTest.cpp b/src/third_party/skia/tests/Sk4x4fTest.cpp new file mode 100644 index 0000000..100e139 --- /dev/null +++ b/src/third_party/skia/tests/Sk4x4fTest.cpp
@@ -0,0 +1,54 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Sk4x4f.h" +#include "Test.h" + +DEF_TEST(Sk4x4f, r) { + Sk4x4f f; + + Sk4f x{ 0, 1, 2, 3}, + y{ 4, 5, 6, 7}, + z{ 8, 9,10,11}, + w{12,13,14,15}; + f = Sk4x4f::Transpose(x,y,z,w); + REPORTER_ASSERT(r, f.r[0] == 0 && f.r[1] == 4 && f.r[2] == 8 && f.r[3] == 12); + REPORTER_ASSERT(r, f.g[0] == 1 && f.g[1] == 5 && f.g[2] == 9 && f.g[3] == 13); + REPORTER_ASSERT(r, f.b[0] == 2 && f.b[1] == 6 && f.b[2] == 10 && f.b[3] == 14); + REPORTER_ASSERT(r, f.a[0] == 3 && f.a[1] == 7 && f.a[2] == 11 && f.a[3] == 15); + + Sk4f s,t,u,v; + f.transpose(&s,&t,&u,&v); + REPORTER_ASSERT(r, (x == s).allTrue() + && (y == t).allTrue() + && (z == u).allTrue() + && (w == v).allTrue()); + + + float fs[16] = {0,1,2,3, 4,5,6,7, 8,9,10,11, 12,13,14,15}; + f = Sk4x4f::Transpose(fs); + REPORTER_ASSERT(r, f.r[0] == 0 && f.r[1] == 4 && f.r[2] == 8 && f.r[3] == 12); + REPORTER_ASSERT(r, f.g[0] == 1 && f.g[1] == 5 && f.g[2] == 9 && f.g[3] == 13); + REPORTER_ASSERT(r, f.b[0] == 2 && f.b[1] == 6 && f.b[2] == 10 && f.b[3] == 14); + REPORTER_ASSERT(r, f.a[0] == 3 && f.a[1] == 7 && f.a[2] == 11 && f.a[3] == 15); + + float fs_back[16]; + f.transpose(fs_back); + REPORTER_ASSERT(r, 0 == memcmp(fs, fs_back, sizeof(fs))); + + + uint8_t bs[16] = {0,1,2,3, 4,5,6,7, 8,9,10,11, 12,13,14,15}; + f = Sk4x4f::Transpose(bs); + REPORTER_ASSERT(r, f.r[0] == 0 && f.r[1] == 4 && f.r[2] == 8 && f.r[3] == 12); + REPORTER_ASSERT(r, f.g[0] == 1 && f.g[1] == 5 && f.g[2] == 9 && f.g[3] == 13); + REPORTER_ASSERT(r, f.b[0] == 2 && f.b[1] == 6 && f.b[2] == 10 && f.b[3] == 14); + REPORTER_ASSERT(r, f.a[0] == 3 && f.a[1] == 7 && f.a[2] == 11 && f.a[3] == 15); + + uint8_t bs_back[16]; + f.transpose(bs_back); + REPORTER_ASSERT(r, 0 == memcmp(bs, bs_back, sizeof(bs))); +}
diff --git a/src/third_party/skia/tests/SkBase64Test.cpp b/src/third_party/skia/tests/SkBase64Test.cpp index 14ff1f7..d855ef5 100644 --- a/src/third_party/skia/tests/SkBase64Test.cpp +++ b/src/third_party/skia/tests/SkBase64Test.cpp
@@ -17,10 +17,10 @@ for (int offset = 0; offset < 6; ++offset) { size_t length = 256 - offset; - size_t encodeLength = SkBase64::Encode(all + offset, length, NULL); + size_t encodeLength = SkBase64::Encode(all + offset, length, nullptr); SkAutoTMalloc<char> src(encodeLength + 1); SkBase64::Encode(all + offset, length, src.get()); - src[encodeLength] = '\0'; + src[SkToInt(encodeLength)] = '\0'; SkBase64 tryMe; tryMe.decode(src.get(), encodeLength); REPORTER_ASSERT(reporter, (strcmp((const char*) (all + offset), tryMe.getData()) == 0));
diff --git a/src/third_party/skia/tests/SkBlend_optsTest.cpp b/src/third_party/skia/tests/SkBlend_optsTest.cpp new file mode 100644 index 0000000..f8cf2d8 --- /dev/null +++ b/src/third_party/skia/tests/SkBlend_optsTest.cpp
@@ -0,0 +1,107 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <string> +#include <tuple> +#include <vector> +#include "Resources.h" +#include "SkCpu.h" +#include "SkImage.h" +#include "SkImage_Base.h" +#include "SkOpts.h" +#include "SkPM4fPriv.h" +#include "SkNx.h" +#include "Test.h" + +typedef void (*Blender)(uint32_t* dst, const uint32_t* const srcStart, int ndst, const int nsrc); + +static inline void srcover_srgb_srgb_1(uint32_t* dst, uint32_t src) { + auto d = Sk4f_fromS32(*dst), + s = Sk4f_fromS32( src); + *dst = Sk4f_toS32(s + d * (1.0f - s[3])); +} + +static void brute_force_srcover_srgb_srgb( + uint32_t* dst, const uint32_t* const src, int ndst, const int nsrc) { + while (ndst > 0) { + int n = SkTMin(ndst, nsrc); + + for (int i = 0; i < n; i++) { + srcover_srgb_srgb_1(dst++, src[i]); + } + ndst -= n; + } +} + +static SkString mismatch_message(std::string resourceName, int x, int y, + uint32_t src, uint32_t good, uint32_t bad) { + return SkStringPrintf( + "%s - missmatch at %d, %d src: %08x good: %08x bad: %08x", + resourceName.c_str(), x, y, src, good, bad); +} + +static void test_blender(std::string resourceName, skiatest::Reporter* reporter) { + std::string fileName = resourceName + ".png"; + sk_sp<SkImage> image = GetResourceAsImage(fileName.c_str()); + if (image == nullptr) { + ERRORF(reporter, "image is NULL"); + return; + } + SkBitmap bm; + sk_sp<SkColorSpace> srgbColorSpace = SkColorSpace::MakeSRGB(); + if (!as_IB(image)->getROPixels(&bm, srgbColorSpace.get())) { + ERRORF(reporter, "Could not read resource"); + return; + } + + SkPixmap pixmap; + bm.peekPixels(&pixmap); + SkASSERTF(pixmap.colorType() == kN32_SkColorType, "colorType: %d", pixmap.colorType()); + SkASSERT(pixmap.alphaType() != kUnpremul_SkAlphaType); + const uint32_t* src = pixmap.addr32(); + const int width = pixmap.rowBytesAsPixels(); + SkASSERT(width > 0); + SkASSERT(width < 4000); + SkAutoTArray<uint32_t> correctDst(width); + SkAutoTArray<uint32_t> testDst(width); + + for (int y = 0; y < pixmap.height(); y++) { + // TODO: zero is not the most interesting dst to test srcover... + sk_bzero(correctDst.get(), width * sizeof(uint32_t)); + sk_bzero(testDst.get(), width * sizeof(uint32_t)); + brute_force_srcover_srgb_srgb(correctDst.get(), src, width, width); + SkOpts:: srcover_srgb_srgb( testDst.get(), src, width, width); + for (int x = 0; x < width; x++) { + REPORTER_ASSERT_MESSAGE( + reporter, correctDst[x] == testDst[x], + mismatch_message(resourceName, x, y, src[x], correctDst[x], testDst[x])); + if (correctDst[x] != testDst[x]) break; + } + src += width; + } +} + +DEF_TEST(SkBlend_optsCheck, reporter) { + std::vector<std::string> testResources = { + "yellow_rose", "baby_tux", "plane", "mandrill_512", "iconstrip" + }; + + for (auto& resourceName : testResources) { + test_blender(resourceName, reporter); + } +} + +DEF_TEST(SkBlend_optsSqrtCheck, reporter) { + for (int c = 0; c < 256; c++) { + Sk4f i{(float)c}; + Sk4f ii = i * i; + Sk4f s = ii.sqrt() + 0.5f; + Sk4f sf = s.floor(); + REPORTER_ASSERT_MESSAGE( + reporter, i[0] == sf[0], SkStringPrintf("i: %f, s: %f", i[0], sf[0])); + } +}
diff --git a/src/third_party/skia/tests/SkColor4fTest.cpp b/src/third_party/skia/tests/SkColor4fTest.cpp new file mode 100644 index 0000000..f268e5f --- /dev/null +++ b/src/third_party/skia/tests/SkColor4fTest.cpp
@@ -0,0 +1,68 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkBitmapProcShader.h" +#include "SkColor.h" +#include "SkColorMatrixFilter.h" +#include "SkGradientShader.h" +#include "SkImage.h" +#include "SkPM4f.h" +#include "SkShader.h" + +#include "Test.h" +#include "SkRandom.h" + +const float kTolerance = 1.0f / (1 << 20); + +static bool nearly_equal(float a, float b, float tol = kTolerance) { + SkASSERT(tol >= 0); + return fabsf(a - b) <= tol; +} + +DEF_TEST(SkColor4f_FromColor, reporter) { + const struct { + SkColor fC; + SkColor4f fC4; + } recs[] = { + { SK_ColorBLACK, { 0, 0, 0, 1 } }, + { SK_ColorWHITE, { 1, 1, 1, 1 } }, + { SK_ColorRED, { 1, 0, 0, 1 } }, + { SK_ColorGREEN, { 0, 1, 0, 1 } }, + { SK_ColorBLUE, { 0, 0, 1, 1 } }, + { 0, { 0, 0, 0, 0 } }, + }; + + for (const auto& r : recs) { + SkColor4f c4 = SkColor4f::FromColor(r.fC); + REPORTER_ASSERT(reporter, c4 == r.fC4); + } +} + +DEF_TEST(Color4f_premul, reporter) { + SkRandom rand; + + for (int i = 0; i < 1000000; ++i) { + // First just test opaque colors, so that the premul should be exact + SkColor4f c4 { + rand.nextUScalar1(), rand.nextUScalar1(), rand.nextUScalar1(), 1 + }; + SkPM4f pm4 = c4.premul(); + REPORTER_ASSERT(reporter, pm4.a() == c4.fA); + REPORTER_ASSERT(reporter, pm4.r() == c4.fA * c4.fR); + REPORTER_ASSERT(reporter, pm4.g() == c4.fA * c4.fG); + REPORTER_ASSERT(reporter, pm4.b() == c4.fA * c4.fB); + + // We compare with a tolerance, in case our premul multiply is implemented at slightly + // different precision than the test code. + c4.fA = rand.nextUScalar1(); + pm4 = c4.premul(); + REPORTER_ASSERT(reporter, pm4.fVec[SK_A_INDEX] == c4.fA); + REPORTER_ASSERT(reporter, nearly_equal(pm4.r(), c4.fA * c4.fR)); + REPORTER_ASSERT(reporter, nearly_equal(pm4.g(), c4.fA * c4.fG)); + REPORTER_ASSERT(reporter, nearly_equal(pm4.b(), c4.fA * c4.fB)); + } +}
diff --git a/src/third_party/skia/tests/SkDOMTest.cpp b/src/third_party/skia/tests/SkDOMTest.cpp new file mode 100644 index 0000000..6ea4e78 --- /dev/null +++ b/src/third_party/skia/tests/SkDOMTest.cpp
@@ -0,0 +1,86 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkStream.h" +#include "Test.h" + +#if defined(SK_XML) + +#include "SkDOM.h" + +static const SkDOM::Node* check_node(skiatest::Reporter* r, const SkDOM& dom, + const SkDOM::Node* node, const char* expectedName, + SkDOM::Type expectedType) { + REPORTER_ASSERT(r, node); + if (node) { + REPORTER_ASSERT(r, !strcmp(dom.getName(node), expectedName)); + REPORTER_ASSERT(r, dom.getType(node) == expectedType); + } + return node; +} + +DEF_TEST(SkDOM_test, r) { + static const char gDoc[] = + "<root a='1' b='2'>" + "<elem1 c='3' />" + "<elem2 d='4' />" + "<elem3 e='5'>" + "<subelem1>Some text.</subelem1>" + "<subelem2 f='6' g='7'/>" + "<subelem3>Some more text.</subelem3>" + "</elem3>" + "<elem4 h='8'/>" + "</root>" + ; + + SkMemoryStream docStream(gDoc, sizeof(gDoc) - 1); + + SkDOM dom; + REPORTER_ASSERT(r, !dom.getRootNode()); + + const SkDOM::Node* root = dom.build(docStream); + REPORTER_ASSERT(r, root && dom.getRootNode() == root); + + const char* v = dom.findAttr(root, "a"); + REPORTER_ASSERT(r, v && !strcmp(v, "1")); + v = dom.findAttr(root, "b"); + REPORTER_ASSERT(r, v && !strcmp(v, "2")); + v = dom.findAttr(root, "c"); + REPORTER_ASSERT(r, v == nullptr); + + REPORTER_ASSERT(r, dom.getFirstChild(root, "elem1")); + REPORTER_ASSERT(r, !dom.getFirstChild(root, "subelem1")); + + { + const auto* elem1 = check_node(r, dom, dom.getFirstChild(root), + "elem1", SkDOM::kElement_Type); + const auto* elem2 = check_node(r, dom, dom.getNextSibling(elem1), + "elem2", SkDOM::kElement_Type); + const auto* elem3 = check_node(r, dom, dom.getNextSibling(elem2), + "elem3", SkDOM::kElement_Type); + { + const auto* subelem1 = check_node(r, dom, dom.getFirstChild(elem3), + "subelem1", SkDOM::kElement_Type); + { + check_node(r, dom, dom.getFirstChild(subelem1), + "Some text.", SkDOM::kText_Type); + } + const auto* subelem2 = check_node(r, dom, dom.getNextSibling(subelem1), + "subelem2", SkDOM::kElement_Type); + const auto* subelem3 = check_node(r, dom, dom.getNextSibling(subelem2), + "subelem3", SkDOM::kElement_Type); + { + check_node(r, dom, dom.getFirstChild(subelem3), + "Some more text.", SkDOM::kText_Type); + } + } + check_node(r, dom, dom.getNextSibling(elem3), + "elem4", SkDOM::kElement_Type); + } +} + +#endif // SK_XML
diff --git a/src/third_party/skia/tests/SkFixed15Test.cpp b/src/third_party/skia/tests/SkFixed15Test.cpp new file mode 100644 index 0000000..df957b1 --- /dev/null +++ b/src/third_party/skia/tests/SkFixed15Test.cpp
@@ -0,0 +1,34 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" +#include "SkFixed15.h" + +DEF_TEST(SkFixed15, r) { + // For all v, v*0 == 0, v*1 == v. + for (uint16_t bits = 0; bits <= 32768; bits++) { + auto v = SkFixed15::Load(bits); + REPORTER_ASSERT(r, v * 0.0f == 0.0f); + REPORTER_ASSERT(r, v * 1.0f == v); + } + + // Division and multiplication by powers of 2 is exact. + SkFixed15 v = 1.0f; + REPORTER_ASSERT(r, (v>>1) == 0.50f); + REPORTER_ASSERT(r, (v>>2) == 0.25f); + REPORTER_ASSERT(r, (v>>2<<1) == 0.50f); + + // FromU8() should be just as good as going through float. + for (int x = 0; x < 256; x++) { + REPORTER_ASSERT(r, SkFixed15::FromU8(x) == SkFixed15(x * (1/255.0f))); + } + + // to_u8() and FromU8() should roundtrip all bytes. + for (int x = 0; x < 256; x++) { + REPORTER_ASSERT(r, x == SkFixed15::FromU8(x).to_u8()); + } +}
diff --git a/src/third_party/skia/tests/SkImageTest.cpp b/src/third_party/skia/tests/SkImageTest.cpp new file mode 100644 index 0000000..4a05b44 --- /dev/null +++ b/src/third_party/skia/tests/SkImageTest.cpp
@@ -0,0 +1,47 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCanvas.h" +#include "SkImagePriv.h" +#include "Test.h" + +static const int gWidth = 20; +static const int gHeight = 20; + +// Tests that SkNewImageFromBitmap obeys pixelref origin. +DEF_TEST(SkImageFromBitmap_extractSubset, reporter) { + sk_sp<SkImage> image; + { + SkBitmap srcBitmap; + srcBitmap.allocN32Pixels(gWidth, gHeight); + srcBitmap.eraseColor(SK_ColorRED); + SkCanvas canvas(srcBitmap); + SkIRect r = SkIRect::MakeXYWH(5, 5, gWidth - 5, gWidth - 5); + SkPaint p; + p.setColor(SK_ColorGREEN); + canvas.drawIRect(r, p); + SkBitmap dstBitmap; + srcBitmap.extractSubset(&dstBitmap, r); + image = SkImage::MakeFromBitmap(dstBitmap); + } + + SkBitmap tgt; + tgt.allocN32Pixels(gWidth, gHeight); + SkCanvas canvas(tgt); + canvas.clear(SK_ColorTRANSPARENT); + canvas.drawImage(image, 0, 0, nullptr); + + uint32_t pixel = 0; + SkImageInfo info = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType); + canvas.readPixels(info, &pixel, 4, 0, 0); + REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN); + canvas.readPixels(info, &pixel, 4, gWidth - 6, gWidth - 6); + REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN); + + canvas.readPixels(info, &pixel, 4, gWidth - 5, gWidth - 5); + REPORTER_ASSERT(reporter, pixel == SK_ColorTRANSPARENT); +}
diff --git a/src/third_party/skia/tests/SkLinearBitmapPipelineTest.cpp b/src/third_party/skia/tests/SkLinearBitmapPipelineTest.cpp new file mode 100644 index 0000000..4b54641 --- /dev/null +++ b/src/third_party/skia/tests/SkLinearBitmapPipelineTest.cpp
@@ -0,0 +1,257 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <algorithm> +#include <array> +#include <tuple> +#include <vector> +#include "SkLinearBitmapPipeline.h" +#include "SkColor.h" +#include "SkNx.h" +#include "SkPoint.h" +#include "SkPM4f.h" +#include "Test.h" +#include "SkLinearBitmapPipeline_tile.h" + + +DEF_TEST(LBPBilerpEdge, reporter) { + +} + +static SkString dump(SkScalar cut, Span prefix, Span remainder) { + SkPoint prefixStart; SkScalar prefixLen; int prefixCount; + std::tie(prefixStart, prefixLen, prefixCount) = prefix; + SkPoint remainderStart; SkScalar remainderLen; int remainderCount; + std::tie(remainderStart, remainderLen, remainderCount) = remainder; + return SkStringPrintf("cut: %f prefix: (%f, %f), %f, %d - remainder: (%f, %f), %f, %d", + cut, + prefixStart.fX, prefixStart.fY, prefixLen, prefixCount, + remainderStart.fX, remainderStart.fY, remainderLen, remainderCount); +} + +static void check_span_result( + skiatest::Reporter* reporter, + Span span, SkScalar dx, SkScalar cut, SkPoint start, SkScalar len, int count) { + SkPoint originalStart; SkScalar originalLen; int originalCount; + std::tie(originalStart, originalLen, originalCount) = span; + + Span prefix = span.breakAt(cut, dx); + + SkPoint prefixStart; SkScalar prefixLen; int prefixCount; + std::tie(prefixStart, prefixLen, prefixCount) = prefix; + + REPORTER_ASSERT_MESSAGE(reporter, prefixStart == start, dump(cut, prefix, span)); + REPORTER_ASSERT_MESSAGE(reporter, prefixLen == len, dump(cut, prefix, span)); + REPORTER_ASSERT_MESSAGE(reporter, prefixCount == count, dump(cut, prefix, span)); + SkPoint expectedRemainderStart; + SkScalar expectedRemainderLen; + int expectedRemainderCount; + if (prefix.isEmpty()) { + expectedRemainderStart = originalStart; + expectedRemainderLen = originalLen; + expectedRemainderCount = originalCount; + } else { + expectedRemainderStart = SkPoint::Make(originalStart.fX + prefixLen + dx, originalStart.fY); + expectedRemainderLen = originalLen - prefixLen - dx; + expectedRemainderCount = originalCount - prefixCount; + } + + if (!span.isEmpty()) { + SkPoint remainderStart; + SkScalar remainderLen; + int remainderCount; + std::tie(remainderStart, remainderLen, remainderCount) = span; + // Remainder span + REPORTER_ASSERT_MESSAGE(reporter, expectedRemainderStart == remainderStart, + dump(cut, prefix, span)); + REPORTER_ASSERT_MESSAGE(reporter, + expectedRemainderLen == remainderLen, + dump(cut, prefix, span)); + REPORTER_ASSERT_MESSAGE(reporter, + expectedRemainderCount == remainderCount, + dump(cut, prefix, span)); + } +} + +DEF_TEST(LBPSpanOps, reporter) { + { + SkScalar dx = 1.0f; + SkPoint start = SkPoint::Make(-5, -5); + Span span{start, 9.0f, 10}; + check_span_result(reporter, span, dx, 0.0f, start, 4.0f, 5); + check_span_result(reporter, span, dx, -6.0f, SkPoint::Make(0, 0), 0.0f, 0); + check_span_result(reporter, span, dx, -5.0f, SkPoint::Make(0, 0), 0.0f, 0); + check_span_result(reporter, span, dx, -4.0f, SkPoint::Make(-5, -5), 0.0f, 1); + check_span_result(reporter, span, dx, 4.0f, SkPoint::Make(-5, -5), 8.0f, 9); + check_span_result(reporter, span, dx, 5.0f, SkPoint::Make(-5, -5), 9.0f, 10); + check_span_result(reporter, span, dx, 6.0f, SkPoint::Make(-5, -5), 9.0f, 10); + } + { + SkScalar dx = -1.0f; + SkPoint start = SkPoint::Make(5, 5); + Span span{start, -9.0f, 10}; + check_span_result(reporter, span, dx, 0.0f, start, -5.0f, 6); + check_span_result(reporter, span, dx, -6.0f, SkPoint::Make(5, 5), -9.0f, 10); + check_span_result(reporter, span, dx, -5.0f, SkPoint::Make(5, 5), -9.0f, 10); + check_span_result(reporter, span, dx, -4.0f, SkPoint::Make(5, 5), -9.0f, 10); + check_span_result(reporter, span, dx, 4.0f, SkPoint::Make(5, 5), -1.0f, 2); + check_span_result(reporter, span, dx, 5.0f, SkPoint::Make(5, 5), 0.0f, 1); + check_span_result(reporter, span, dx, 6.0f, SkPoint::Make(0, 0), 0.0f, 0); + } +} + +DEF_TEST(LBPBilerpSpanOps, reporter) { + +} + +template <typename XTiler, typename YTiler> +static bool compare_tiler_case( + XTiler& xTiler, YTiler& yTiler, Span span, skiatest::Reporter* reporter) { + Span originalSpan = span; + std::vector<SkPoint> listPoints; + std::vector<SkPoint> spanPoints; + struct Sink { + void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) { + SkASSERT(0 < n && n < 4); + if (n >= 1) storePoint({xs[0], ys[0]}); + if (n >= 2) storePoint({xs[1], ys[1]}); + if (n >= 3) storePoint({xs[2], ys[2]}); + } + + void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) { + storePoint({xs[0], ys[0]}); + storePoint({xs[1], ys[1]}); + storePoint({xs[2], ys[2]}); + storePoint({xs[3], ys[3]}); + } + + void pointSpan(Span span) { + span_fallback(span, this); + } + + void storePoint(SkPoint pt) { + fPoints->push_back({SkScalarFloorToScalar(X(pt)), SkScalarFloorToScalar(Y(pt))}); + } + + std::vector<SkPoint>* fPoints; + }; + + Sink listSink = {&listPoints}; + Sink spanSink = {&spanPoints}; + + SkPoint start; SkScalar length; int count; + std::tie(start, length, count) = span; + + SkScalar dx = length / (count - 1); + Sk4f xs = Sk4f{X(start)} + Sk4f{0.0f, dx, 2 * dx, 3 * dx}; + Sk4f ys = Sk4f{Y(start)}; + while (count >= 4) { + Sk4f txs = xs; + Sk4f tys = ys; + xTiler.tileXPoints(&txs); + yTiler.tileYPoints(&tys); + listSink.pointList4(txs, tys); + xs = xs + 4.0f * dx; + count -= 4; + } + if (count > 0) { + xTiler.tileXPoints(&xs); + yTiler.tileYPoints(&ys); + listSink.pointListFew(count, xs, ys); + } + + std::tie(start, length, count) = originalSpan; + SkScalar x = X(start); + SkScalar y = yTiler.tileY(Y(start)); + Span yAdjustedSpan{{x, y}, length, count}; + + bool handledSpan = xTiler.maybeProcessSpan(yAdjustedSpan, &spanSink); + if (handledSpan) { + auto firstNotTheSame = std::mismatch( + listPoints.begin(), listPoints.end(), spanPoints.begin()); + if (firstNotTheSame.first != listSink.fPoints->end()) { + auto element = std::distance(listPoints.begin(), firstNotTheSame.first); + SkASSERT(element >= 0); + std::tie(start, length, count) = originalSpan; + ERRORF(reporter, "Span: {%f, %f}, %f, %d", start.fX, start.fY, length, count); + ERRORF(reporter, "Size points: %d, size span: %d", + listPoints.size(), spanPoints.size()); + if ((unsigned)element >= spanPoints.size()) { + ERRORF(reporter, "Size points: %d, size span: %d", + listPoints.size(), spanPoints.size()); + // Mismatch off the end + ERRORF(reporter, + "The mismatch is at position %d and has value %f, %f - it is off the end " + "of the other.", + element, X(*firstNotTheSame.first), Y(*firstNotTheSame.first)); + } else { + ERRORF(reporter, + "Mismatch at %d - points: %f, %f - span: %f, %f", + element, listPoints[element].fX, listPoints[element].fY, + spanPoints[element].fX, spanPoints[element].fY); + } + SkFAIL("aha"); + } + } + return true; +} + +template <typename XTiler, typename YTiler> +static bool compare_tiler_spans(int width, int height, skiatest::Reporter* reporter) { + XTiler xTiler{width}; + YTiler yTiler{height}; + INFOF(reporter, "w: %d, h: %d \n", width, height); + std::array<int, 8> interestingX {{-5, -1, 0, 1, width - 1, width, width + 1, width + 5}}; + std::array<int, 8> interestingY {{-5, -1, 0, 1, height - 1, height, height + 1, height + 5}}; + std::array<int, 6> interestingCount {{1, 2, 3, 4, 5, 10}}; + std::array<SkScalar, 7> interestingScale {{0.0f, 1.0f, 0.5f, 2.1f, -2.1f, -1.0f, -0.5f}}; + for (auto scale : interestingScale) { + for (auto startX : interestingX) { + for (auto count : interestingCount) { + for (auto y : interestingY) { + Span span{ + SkPoint::Make((SkScalar)startX, (SkScalar)y), (count-1.0f) * scale, count}; + if (!compare_tiler_case(xTiler, yTiler, span, reporter)) { + return false; + } + } + } + } + } + return true; +} + +template <typename XTiler, typename YTiler> +static void test_tiler(skiatest::Reporter* reporter) { + std::array<int, 6> interestingSize {{1, 2, 3, 4, 5, 10}}; + for (auto width : interestingSize) { + for (auto height : interestingSize) { + if (!compare_tiler_spans<XTiler, YTiler>(width, height, reporter)) { return; } + } + } +} +/* +DEF_TEST(LBPStrategyClampTile, reporter) { +#if 0 + ClampStrategy tiler{SkSize::Make(1, 1)}; + Span span{SkPoint::Make(0, -5), 1.0f, 2}; + compare_tiler_case<ClampStrategy>(tiler, span, reporter); +#else + test_tiler<XClampStrategy, YClampStrategy>(reporter); +#endif +} + +DEF_TEST(LBPStrategyRepeatTile, reporter) { +#if 0 + RepeatStrategy tiler{SkSize::Make(3, 1)}; + Span span{SkPoint::Make(-5, -5), 20 * 2.1f, 100}; + compare_tiler_case<RepeatStrategy>(tiler, span, reporter); +#else + test_tiler<XRepeatStrategy, YRepeatStrategy>(reporter); +#endif +} +*/
diff --git a/src/third_party/skia/tests/SkLiteDLTest.cpp b/src/third_party/skia/tests/SkLiteDLTest.cpp new file mode 100644 index 0000000..514464a --- /dev/null +++ b/src/third_party/skia/tests/SkLiteDLTest.cpp
@@ -0,0 +1,53 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" +#include "SkLiteDL.h" +#include "SkLiteRecorder.h" + +DEF_TEST(SkLiteDL_basics, r) { + SkLiteDL p; + p.save(); + p.clipRect(SkRect{2,3,4,5}, kIntersect_SkClipOp, true); + p.drawRect(SkRect{0,0,9,9}, SkPaint{}); + p.restore(); +} + +DEF_TEST(SkLiteDL_unbalanced, r) { + SkLiteRecorder rec; + SkCanvas* c = &rec; + + SkLiteDL p; + rec.reset(&p, {2,2,3,3}); + c->save(); + c->scale(2,2); + c->save(); + c->translate(1,1); + // missing restore() but SkLiteDL::draw should balance it for us + c->restore(); + + // reinit the recorder so we can playback the original SkLiteDL + SkLiteDL p2; + rec.reset(&p2, {2,2,3,3}); + + REPORTER_ASSERT(r, 1 == rec.getSaveCount()); + p.draw(c); + REPORTER_ASSERT(r, 1 == rec.getSaveCount()); +} + +DEF_TEST(SkLiteRecorder, r) { + SkLiteDL p; + SkLiteRecorder rec; + SkCanvas* c = &rec; + + rec.reset(&p, {2,2,3,3}); + + c->save(); + c->clipRect(SkRect{2,3,4,5}, kIntersect_SkClipOp, true); + c->drawRect(SkRect{0,0,9,9}, SkPaint{}); + c->restore(); +}
diff --git a/src/third_party/skia/tests/SkNxTest.cpp b/src/third_party/skia/tests/SkNxTest.cpp new file mode 100644 index 0000000..e3f3cd6 --- /dev/null +++ b/src/third_party/skia/tests/SkNxTest.cpp
@@ -0,0 +1,360 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Sk4px.h" +#include "SkNx.h" +#include "SkRandom.h" +#include "Test.h" + +template <int N> +static void test_Nf(skiatest::Reporter* r) { + + auto assert_nearly_eq = [&](float eps, const SkNx<N, float>& v, + float a, float b, float c, float d) { + auto close = [=](float a, float b) { return fabsf(a-b) <= eps; }; + float vals[4]; + v.store(vals); + bool ok = close(vals[0], a) && close(vals[1], b) + && close( v[0], a) && close( v[1], b); + REPORTER_ASSERT(r, ok); + if (N == 4) { + ok = close(vals[2], c) && close(vals[3], d) + && close( v[2], c) && close( v[3], d); + REPORTER_ASSERT(r, ok); + } + }; + auto assert_eq = [&](const SkNx<N, float>& v, float a, float b, float c, float d) { + return assert_nearly_eq(0, v, a,b,c,d); + }; + + float vals[] = {3, 4, 5, 6}; + SkNx<N,float> a = SkNx<N,float>::Load(vals), + b(a), + c = a; + SkNx<N,float> d; + d = a; + + assert_eq(a, 3, 4, 5, 6); + assert_eq(b, 3, 4, 5, 6); + assert_eq(c, 3, 4, 5, 6); + assert_eq(d, 3, 4, 5, 6); + + assert_eq(a+b, 6, 8, 10, 12); + assert_eq(a*b, 9, 16, 25, 36); + assert_eq(a*b-b, 6, 12, 20, 30); + assert_eq((a*b).sqrt(), 3, 4, 5, 6); + assert_eq(a/b, 1, 1, 1, 1); + assert_eq(SkNx<N,float>(0)-a, -3, -4, -5, -6); + + SkNx<N,float> fours(4); + + assert_eq(fours.sqrt(), 2,2,2,2); + assert_nearly_eq(0.001f, fours.rsqrt(), 0.5, 0.5, 0.5, 0.5); + + assert_nearly_eq(0.001f, fours.invert(), 0.25, 0.25, 0.25, 0.25); + + assert_eq(SkNx<N,float>::Min(a, fours), 3, 4, 4, 4); + assert_eq(SkNx<N,float>::Max(a, fours), 4, 4, 5, 6); + + // Test some comparisons. This is not exhaustive. + REPORTER_ASSERT(r, (a == b).allTrue()); + REPORTER_ASSERT(r, (a+b == a*b-b).anyTrue()); + REPORTER_ASSERT(r, !(a+b == a*b-b).allTrue()); + REPORTER_ASSERT(r, !(a+b == a*b).anyTrue()); + REPORTER_ASSERT(r, !(a != b).anyTrue()); + REPORTER_ASSERT(r, (a < fours).anyTrue()); + REPORTER_ASSERT(r, (a <= fours).anyTrue()); + REPORTER_ASSERT(r, !(a > fours).allTrue()); + REPORTER_ASSERT(r, !(a >= fours).allTrue()); +} + +DEF_TEST(SkNf, r) { + test_Nf<2>(r); + test_Nf<4>(r); +} + +template <int N, typename T> +void test_Ni(skiatest::Reporter* r) { + auto assert_eq = [&](const SkNx<N,T>& v, T a, T b, T c, T d, T e, T f, T g, T h) { + T vals[8]; + v.store(vals); + + switch (N) { + case 8: REPORTER_ASSERT(r, vals[4] == e && vals[5] == f && vals[6] == g && vals[7] == h); + case 4: REPORTER_ASSERT(r, vals[2] == c && vals[3] == d); + case 2: REPORTER_ASSERT(r, vals[0] == a && vals[1] == b); + } + switch (N) { + case 8: REPORTER_ASSERT(r, v[4] == e && v[5] == f && + v[6] == g && v[7] == h); + case 4: REPORTER_ASSERT(r, v[2] == c && v[3] == d); + case 2: REPORTER_ASSERT(r, v[0] == a && v[1] == b); + } + }; + + T vals[] = { 1,2,3,4,5,6,7,8 }; + SkNx<N,T> a = SkNx<N,T>::Load(vals), + b(a), + c = a; + SkNx<N,T> d; + d = a; + + assert_eq(a, 1,2,3,4,5,6,7,8); + assert_eq(b, 1,2,3,4,5,6,7,8); + assert_eq(c, 1,2,3,4,5,6,7,8); + assert_eq(d, 1,2,3,4,5,6,7,8); + + assert_eq(a+a, 2,4,6,8,10,12,14,16); + assert_eq(a*a, 1,4,9,16,25,36,49,64); + assert_eq(a*a-a, 0,2,6,12,20,30,42,56); + + assert_eq(a >> 2, 0,0,0,1,1,1,1,2); + assert_eq(a << 1, 2,4,6,8,10,12,14,16); + + REPORTER_ASSERT(r, a[1] == 2); +} + +DEF_TEST(SkNx, r) { + test_Ni<2, uint16_t>(r); + test_Ni<4, uint16_t>(r); + test_Ni<8, uint16_t>(r); + + test_Ni<2, int>(r); + test_Ni<4, int>(r); + test_Ni<8, int>(r); +} + +DEF_TEST(SkNi_min_lt, r) { + // Exhaustively check the 8x8 bit space. + for (int a = 0; a < (1<<8); a++) { + for (int b = 0; b < (1<<8); b++) { + Sk16b aw(a), bw(b); + REPORTER_ASSERT(r, Sk16b::Min(aw, bw)[0] == SkTMin(a, b)); + REPORTER_ASSERT(r, !(aw < bw)[0] == !(a < b)); + }} + + // Exhausting the 16x16 bit space is kind of slow, so only do that in release builds. +#ifdef SK_DEBUG + SkRandom rand; + for (int i = 0; i < (1<<16); i++) { + uint16_t a = rand.nextU() >> 16, + b = rand.nextU() >> 16; + REPORTER_ASSERT(r, Sk16h::Min(Sk16h(a), Sk16h(b))[0] == SkTMin(a, b)); + } +#else + for (int a = 0; a < (1<<16); a++) { + for (int b = 0; b < (1<<16); b++) { + REPORTER_ASSERT(r, Sk16h::Min(Sk16h(a), Sk16h(b))[0] == SkTMin(a, b)); + }} +#endif +} + +DEF_TEST(SkNi_saturatedAdd, r) { + for (int a = 0; a < (1<<8); a++) { + for (int b = 0; b < (1<<8); b++) { + int exact = a+b; + if (exact > 255) { exact = 255; } + if (exact < 0) { exact = 0; } + + REPORTER_ASSERT(r, Sk16b(a).saturatedAdd(Sk16b(b))[0] == exact); + } + } +} + +DEF_TEST(Sk4px_muldiv255round, r) { + for (int a = 0; a < (1<<8); a++) { + for (int b = 0; b < (1<<8); b++) { + int exact = (a*b+127)/255; + + // Duplicate a and b 16x each. + auto av = Sk4px::DupAlpha(a), + bv = Sk4px::DupAlpha(b); + + // This way should always be exactly correct. + int correct = (av * bv).div255()[0]; + REPORTER_ASSERT(r, correct == exact); + + // We're a bit more flexible on this method: correct for 0 or 255, otherwise off by <=1. + int fast = av.approxMulDiv255(bv)[0]; + REPORTER_ASSERT(r, fast-exact >= -1 && fast-exact <= 1); + if (a == 0 || a == 255 || b == 0 || b == 255) { + REPORTER_ASSERT(r, fast == exact); + } + } + } +} + +DEF_TEST(Sk4px_widening, r) { + SkPMColor colors[] = { + SkPreMultiplyColor(0xff00ff00), + SkPreMultiplyColor(0x40008000), + SkPreMultiplyColor(0x7f020406), + SkPreMultiplyColor(0x00000000), + }; + auto packed = Sk4px::Load4(colors); + + auto wideLo = packed.widenLo(), + wideHi = packed.widenHi(), + wideLoHi = packed.widenLoHi(), + wideLoHiAlt = wideLo + wideHi; + REPORTER_ASSERT(r, 0 == memcmp(&wideLoHi, &wideLoHiAlt, sizeof(wideLoHi))); +} + +DEF_TEST(SkNx_abs, r) { + auto fs = Sk4f(0.0f, -0.0f, 2.0f, -4.0f).abs(); + REPORTER_ASSERT(r, fs[0] == 0.0f); + REPORTER_ASSERT(r, fs[1] == 0.0f); + REPORTER_ASSERT(r, fs[2] == 2.0f); + REPORTER_ASSERT(r, fs[3] == 4.0f); +} + +DEF_TEST(Sk4i_abs, r) { + auto is = Sk4i(0, -1, 2, -2147483647).abs(); + REPORTER_ASSERT(r, is[0] == 0); + REPORTER_ASSERT(r, is[1] == 1); + REPORTER_ASSERT(r, is[2] == 2); + REPORTER_ASSERT(r, is[3] == 2147483647); +} + +DEF_TEST(Sk4i_minmax, r) { + auto a = Sk4i(0, 2, 4, 6); + auto b = Sk4i(1, 1, 3, 7); + auto min = Sk4i::Min(a, b); + auto max = Sk4i::Max(a, b); + for(int i = 0; i < 4; ++i) { + REPORTER_ASSERT(r, min[i] == SkTMin(a[i], b[i])); + REPORTER_ASSERT(r, max[i] == SkTMax(a[i], b[i])); + } +} + +DEF_TEST(SkNx_floor, r) { + auto fs = Sk4f(0.4f, -0.4f, 0.6f, -0.6f).floor(); + REPORTER_ASSERT(r, fs[0] == 0.0f); + REPORTER_ASSERT(r, fs[1] == -1.0f); + REPORTER_ASSERT(r, fs[2] == 0.0f); + REPORTER_ASSERT(r, fs[3] == -1.0f); +} + +DEF_TEST(SkNx_shuffle, r) { + Sk4f f4(0,10,20,30); + + Sk2f f2 = SkNx_shuffle<2,1>(f4); + REPORTER_ASSERT(r, f2[0] == 20); + REPORTER_ASSERT(r, f2[1] == 10); + + f4 = SkNx_shuffle<0,1,1,0>(f2); + REPORTER_ASSERT(r, f4[0] == 20); + REPORTER_ASSERT(r, f4[1] == 10); + REPORTER_ASSERT(r, f4[2] == 10); + REPORTER_ASSERT(r, f4[3] == 20); +} + +DEF_TEST(SkNx_int_float, r) { + Sk4f f(-2.3f, 1.0f, 0.45f, 0.6f); + + Sk4i i = SkNx_cast<int>(f); + REPORTER_ASSERT(r, i[0] == -2); + REPORTER_ASSERT(r, i[1] == 1); + REPORTER_ASSERT(r, i[2] == 0); + REPORTER_ASSERT(r, i[3] == 0); + + f = SkNx_cast<float>(i); + REPORTER_ASSERT(r, f[0] == -2.0f); + REPORTER_ASSERT(r, f[1] == 1.0f); + REPORTER_ASSERT(r, f[2] == 0.0f); + REPORTER_ASSERT(r, f[3] == 0.0f); +} + +#include "SkRandom.h" + +DEF_TEST(SkNx_u16_float, r) { + { + // u16 --> float + auto h4 = Sk4h(15, 17, 257, 65535); + auto f4 = SkNx_cast<float>(h4); + REPORTER_ASSERT(r, f4[0] == 15.0f); + REPORTER_ASSERT(r, f4[1] == 17.0f); + REPORTER_ASSERT(r, f4[2] == 257.0f); + REPORTER_ASSERT(r, f4[3] == 65535.0f); + } + { + // float -> u16 + auto f4 = Sk4f(15, 17, 257, 65535); + auto h4 = SkNx_cast<uint16_t>(f4); + REPORTER_ASSERT(r, h4[0] == 15); + REPORTER_ASSERT(r, h4[1] == 17); + REPORTER_ASSERT(r, h4[2] == 257); + REPORTER_ASSERT(r, h4[3] == 65535); + } + + // starting with any u16 value, we should be able to have a perfect round-trip in/out of floats + // + SkRandom rand; + for (int i = 0; i < 10000; ++i) { + const uint16_t s16[4] { + (uint16_t)rand.nextU16(), (uint16_t)rand.nextU16(), + (uint16_t)rand.nextU16(), (uint16_t)rand.nextU16(), + }; + auto u4_0 = Sk4h::Load(s16); + auto f4 = SkNx_cast<float>(u4_0); + auto u4_1 = SkNx_cast<uint16_t>(f4); + uint16_t d16[4]; + u4_1.store(d16); + REPORTER_ASSERT(r, !memcmp(s16, d16, sizeof(s16))); + } +} + +// The SSE2 implementation of SkNx_cast<uint16_t>(Sk4i) is non-trivial, so worth a test. +DEF_TEST(SkNx_int_u16, r) { + // These are pretty hard to get wrong. + for (int i = 0; i <= 0x7fff; i++) { + uint16_t expected = (uint16_t)i; + uint16_t actual = SkNx_cast<uint16_t>(Sk4i(i))[0]; + + REPORTER_ASSERT(r, expected == actual); + } + + // A naive implementation with _mm_packs_epi32 would succeed up to 0x7fff but fail here: + for (int i = 0x8000; (1) && i <= 0xffff; i++) { + uint16_t expected = (uint16_t)i; + uint16_t actual = SkNx_cast<uint16_t>(Sk4i(i))[0]; + + REPORTER_ASSERT(r, expected == actual); + } +} + +DEF_TEST(SkNx_4fLoad4Store4, r) { + float src[] = { + 0.0f, 1.0f, 2.0f, 3.0f, + 4.0f, 5.0f, 6.0f, 7.0f, + 8.0f, 9.0f, 10.0f, 11.0f, + 12.0f, 13.0f, 14.0f, 15.0f + }; + + Sk4f a, b, c, d; + Sk4f::Load4(src, &a, &b, &c, &d); + REPORTER_ASSERT(r, 0.0f == a[0]); + REPORTER_ASSERT(r, 4.0f == a[1]); + REPORTER_ASSERT(r, 8.0f == a[2]); + REPORTER_ASSERT(r, 12.0f == a[3]); + REPORTER_ASSERT(r, 1.0f == b[0]); + REPORTER_ASSERT(r, 5.0f == b[1]); + REPORTER_ASSERT(r, 9.0f == b[2]); + REPORTER_ASSERT(r, 13.0f == b[3]); + REPORTER_ASSERT(r, 2.0f == c[0]); + REPORTER_ASSERT(r, 6.0f == c[1]); + REPORTER_ASSERT(r, 10.0f == c[2]); + REPORTER_ASSERT(r, 14.0f == c[3]); + REPORTER_ASSERT(r, 3.0f == d[0]); + REPORTER_ASSERT(r, 7.0f == d[1]); + REPORTER_ASSERT(r, 11.0f == d[2]); + REPORTER_ASSERT(r, 15.0f == d[3]); + + float dst[16]; + Sk4f::Store4(dst, a, b, c, d); + REPORTER_ASSERT(r, 0 == memcmp(dst, src, 16 * sizeof(float))); +}
diff --git a/src/third_party/skia/tests/SkPEGTest.cpp b/src/third_party/skia/tests/SkPEGTest.cpp new file mode 100644 index 0000000..6773c1b --- /dev/null +++ b/src/third_party/skia/tests/SkPEGTest.cpp
@@ -0,0 +1,304 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#if defined(SK_XML) +#include "SkPEG.h" + +using namespace skpeg; + +namespace { + +struct Alpha { + using V = char; + using MatchT = MatchResult<V>; + + static MatchT Match(const char* in) { + static constexpr unsigned kAlphaRange = 'z' - 'a'; + return static_cast<unsigned>(*in - 'a') <= kAlphaRange + || static_cast<unsigned>(*in - 'A') <= kAlphaRange + ? MatchT(in + 1, *in) + : nullptr; + } +}; + +struct Digit { + using V = uint8_t; + using MatchT = MatchResult<V>; + + static MatchT Match(const char* in) { + static constexpr unsigned kDigitRange = '9' - '0'; + return static_cast<unsigned>(*in - '0') <= kDigitRange + ? MatchT(in + 1, SkTo<uint8_t>(*in - '0')) + : nullptr; + } +}; + +void test_EOS(skiatest::Reporter* r) { + static const struct { + const char* fInput; + bool fMatch; + } gTests[] = { + { "" , true }, + { " " , false }, + { "\0" , true }, + { "foo", false }, + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) { + const auto match = EOS::Match(gTests[i].fInput); + REPORTER_ASSERT(r, match == gTests[i].fMatch); + REPORTER_ASSERT(r, match.fNext == (match ? gTests[i].fInput : nullptr)); + } +} + +void test_LIT(skiatest::Reporter* r) { + static const struct { + const char* fInput; + bool fMatch; + } gTests[] = { + { "" , false }, + { " " , false }, + { "x" , false }, + { "X" , true }, + { "xX", false }, + { "Xx", true }, + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) { + const auto match = LIT<'X'>::Match(gTests[i].fInput); + REPORTER_ASSERT(r, match == gTests[i].fMatch); + REPORTER_ASSERT(r, match.fNext == (match ? gTests[i].fInput + 1 : nullptr)); + } + + REPORTER_ASSERT(r, !(LIT<'F', 'o', 'o'>::Match(""))); + REPORTER_ASSERT(r, !(LIT<'F', 'o', 'o'>::Match("Fo"))); + REPORTER_ASSERT(r, !(LIT<'F', 'o', 'o'>::Match("FoO"))); + REPORTER_ASSERT(r, (LIT<'F', 'o', 'o'>::Match("Foo"))); + REPORTER_ASSERT(r, (LIT<'F', 'o', 'o'>::Match("Foobar"))); +} + +void test_Alpha(skiatest::Reporter* r) { + static const struct { + const char* fInput; + bool fMatch; + char fMatchValue; + } gTests[] = { + { "" , false, 0 }, + { "\r", false, 0 }, + { "\n", false, 0 }, + { "\t", false, 0 }, + { "0" , false, 0 }, + { "9" , false, 0 }, + { "a" , true , 'a' }, + { "a" , true , 'a' }, + { "z" , true , 'z' }, + { "A" , true , 'A' }, + { "Z" , true , 'Z' }, + { "az", true , 'a' }, + { "a0", true , 'a' }, + { "0a", false, 0 }, + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) { + const auto match = Alpha::Match(gTests[i].fInput); + REPORTER_ASSERT(r, match == gTests[i].fMatch); + REPORTER_ASSERT(r, match.fNext == (match ? gTests[i].fInput + 1 : nullptr)); + if (match) { + REPORTER_ASSERT(r, *match == gTests[i].fMatchValue); + } + } +} + +void test_Digit(skiatest::Reporter* r) { + static const struct { + const char* fInput; + bool fMatch; + uint8_t fMatchValue; + } gTests[] = { + { "" , false, 0 }, + { "/" , false, 0 }, + { ":" , false, 0 }, + { "x" , false, 0 }, + { "x0" , false, 0 }, + { "0" , true , 0 }, + { "1x" , true , 1 }, + { "9 a", true , 9 }, + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) { + const auto match = Digit::Match(gTests[i].fInput); + REPORTER_ASSERT(r, match == gTests[i].fMatch); + REPORTER_ASSERT(r, match.fNext == (match ? gTests[i].fInput + 1 : nullptr)); + if (match) { + REPORTER_ASSERT(r, *match == gTests[i].fMatchValue); + } + } +} + +void test_Opt(skiatest::Reporter* r) { + static const struct { + const char* fInput; + bool fMatch; + } gTests[] = { + { "" , false }, + { "fo" , false }, + { " foo" , false }, + { "foo" , true }, + { "foobar" , true }, + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) { + const auto m = Opt<LIT<'f', 'o', 'o'>>::Match(gTests[i].fInput); + REPORTER_ASSERT(r, m); + REPORTER_ASSERT(r, m->fValue.isValid() == gTests[i].fMatch); + } +} + +void test_Seq(skiatest::Reporter* r) { + REPORTER_ASSERT(r, (Seq<LIT<'X'>, EOS>::Match("X"))); + REPORTER_ASSERT(r, !(Seq<LIT<'X'>, EOS>::Match("x"))); + REPORTER_ASSERT(r, !(Seq<LIT<'X'>, EOS>::Match("xX"))); + REPORTER_ASSERT(r, !(Seq<LIT<'X'>, EOS>::Match("XX"))); + REPORTER_ASSERT(r, (Seq<LIT<'X'>, Seq<LIT<'X'>, EOS>>::Match("XX"))); + REPORTER_ASSERT(r, (Seq<LIT<'X'>, Seq<LIT<'X'>, EOS>>::Match("XX"))); + + REPORTER_ASSERT(r, !(Seq<LIT<'F', 'o', 'o'>, EOS>::Match("FooBar"))); + REPORTER_ASSERT(r, (Seq<LIT<'F', 'o', 'o'>, EOS>::Match("Foo"))); + + { + const auto m = Seq<LIT<'x'>, Digit>::Match("x5"); + REPORTER_ASSERT(r, m); + REPORTER_ASSERT(r, m->get<1>() == 5); + } + { + const auto m = Seq<Digit, Digit>::Match("42"); + REPORTER_ASSERT(r, m); + REPORTER_ASSERT(r, m->get<0>() == 4); + REPORTER_ASSERT(r, m->get<1>() == 2); + } +} + +void test_Choice(skiatest::Reporter* r) { + REPORTER_ASSERT(r, !(Choice<Digit,Alpha>::Match(""))); + REPORTER_ASSERT(r, !(Choice<Digit,Alpha>::Match("\t"))); + REPORTER_ASSERT(r, !(Choice<Digit,Alpha>::Match(" "))); + REPORTER_ASSERT(r, (Choice<Digit,Alpha>::Match("a"))); + REPORTER_ASSERT(r, (Choice<Digit,Alpha>::Match("3"))); + REPORTER_ASSERT(r, (Choice<Digit,Alpha>::Match("a "))); + REPORTER_ASSERT(r, (Choice<Digit,Alpha>::Match("3 "))); + REPORTER_ASSERT(r, !(Choice<Digit,Alpha>::Match(" a "))); + REPORTER_ASSERT(r, !(Choice<Digit,Alpha>::Match(" 3 "))); + + { + const auto m = Choice<Alpha, Digit>::Match("x"); + REPORTER_ASSERT(r, m); + REPORTER_ASSERT(r, m->v1.isValid()); + REPORTER_ASSERT(r, !m->v2.isValid()); + REPORTER_ASSERT(r, *m->v1.get() == 'x'); + } + + { + const auto m = Choice<Alpha, Digit>::Match("7"); + REPORTER_ASSERT(r, m); + REPORTER_ASSERT(r, !m->v1.isValid()); + REPORTER_ASSERT(r, m->v2.isValid()); + REPORTER_ASSERT(r, *m->v2.get() == 7); + } +} + +void test_AnySome(skiatest::Reporter* r) { + static const struct { + const char* fInput; + int fCount; + } gTests[] = { + { "" , 0 }, + { "fo" , 0 }, + { "Foo" , 0 }, + { "foo" , 1 }, + { "foofoo", 2 }, + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) { + const auto matchAny = Any<LIT<'f', 'o', 'o'>>::Match(gTests[i].fInput); + REPORTER_ASSERT(r, matchAny); + REPORTER_ASSERT(r, matchAny->fValues.count() == gTests[i].fCount); + + const auto matchSome = Some<LIT<'f', 'o', 'o'>>::Match(gTests[i].fInput); + REPORTER_ASSERT(r, matchSome == (gTests[i].fCount > 0)); + REPORTER_ASSERT(r, !matchSome || + matchSome->get<1>().fValues.count() == gTests[i].fCount - 1); + } + + { + const auto m = Any<Digit>::Match("0123456789foo"); + REPORTER_ASSERT(r, m); + REPORTER_ASSERT(r, m->fValues.count() == 10); + for (int i = 0; i < m->fValues.count(); ++i) { + REPORTER_ASSERT(r, m->fValues[i] == i); + } + } +} + +void test_Complex(skiatest::Reporter* r) { + // [0-9]+(,[0-9]+)?$ + using P0 = + Seq< + Some<Digit>, + Opt<Seq< + LIT<','>, + Some<Digit>>>, + EOS>; + + REPORTER_ASSERT(r, !P0::Match("")); + REPORTER_ASSERT(r, !P0::Match(",")); + REPORTER_ASSERT(r, !P0::Match("1,")); + REPORTER_ASSERT(r, !P0::Match(",1")); + REPORTER_ASSERT(r, P0::Match("1")); + REPORTER_ASSERT(r, P0::Match("1,2")); + REPORTER_ASSERT(r, !P0::Match("1,2 ")); + REPORTER_ASSERT(r, P0::Match("123,456")); + + // [ ]*[Ff]oo([Bb]ar)+[Bb]az[ ]*$ + using P1 = + Seq< + Any<LIT<' '>>, + Choice<LIT<'F'>, LIT<'f'>>, + LIT<'o', 'o'>, + Some<Seq< + Choice<LIT<'B'>, LIT<'b'>>, + LIT<'a', 'r'>>>, + Choice<LIT<'B'>, LIT<'b'>>, + LIT<'a', 'z'>, + Any<LIT<' '>>, + EOS>; + + REPORTER_ASSERT(r, !P1::Match("")); + REPORTER_ASSERT(r, !P1::Match("FooBar")); + REPORTER_ASSERT(r, !P1::Match("FooBaz")); + REPORTER_ASSERT(r, P1::Match("FooBarBaz")); + REPORTER_ASSERT(r, P1::Match("foobarbaz")); + REPORTER_ASSERT(r, P1::Match(" FooBarbaz ")); + REPORTER_ASSERT(r, P1::Match(" FooBarbarbarBaz ")); +} + +} // anonymous ns + +DEF_TEST(SkPEG, r) { + test_EOS(r); + test_LIT(r); + test_Alpha(r); + test_Digit(r); + test_Opt(r); + test_Seq(r); + test_Choice(r); + test_AnySome(r); + test_Complex(r); +} + +#endif // SK_XML
diff --git a/src/third_party/skia/tests/SkRasterPipelineTest.cpp b/src/third_party/skia/tests/SkRasterPipelineTest.cpp new file mode 100644 index 0000000..8edff7e --- /dev/null +++ b/src/third_party/skia/tests/SkRasterPipelineTest.cpp
@@ -0,0 +1,263 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" +#include "SkHalf.h" +#include "SkRasterPipeline.h" +#include "../src/jumper/SkJumper.h" + +DEF_TEST(SkRasterPipeline, r) { + // Build and run a simple pipeline to exercise SkRasterPipeline, + // drawing 50% transparent blue over opaque red in half-floats. + uint64_t red = 0x3c00000000003c00ull, + blue = 0x3800380000000000ull, + result; + + void* load_s_ctx = &blue; + void* load_d_ctx = &red; + void* store_ctx = &result; + + SkRasterPipeline_<256> p; + p.append(SkRasterPipeline::load_f16, &load_s_ctx); + p.append(SkRasterPipeline::load_f16_dst, &load_d_ctx); + p.append(SkRasterPipeline::srcover); + p.append(SkRasterPipeline::store_f16, &store_ctx); + p.run(0,0,1); + + // We should see half-intensity magenta. + REPORTER_ASSERT(r, ((result >> 0) & 0xffff) == 0x3800); + REPORTER_ASSERT(r, ((result >> 16) & 0xffff) == 0x0000); + REPORTER_ASSERT(r, ((result >> 32) & 0xffff) == 0x3800); + REPORTER_ASSERT(r, ((result >> 48) & 0xffff) == 0x3c00); +} + +DEF_TEST(SkRasterPipeline_empty, r) { + // No asserts... just a test that this is safe to run. + SkRasterPipeline_<256> p; + p.run(0,0,20); +} + +DEF_TEST(SkRasterPipeline_nonsense, r) { + // No asserts... just a test that this is safe to run and terminates. + // srcover() calls st->next(); this makes sure we've always got something there to call. + SkRasterPipeline_<256> p; + p.append(SkRasterPipeline::srcover); + p.run(0,0,20); +} + +DEF_TEST(SkRasterPipeline_JIT, r) { + // This tests a couple odd corners that a JIT backend can stumble over. + + uint32_t buf[72] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + + const uint32_t* src = buf + 0; + uint32_t* dst = buf + 36; + + // Copy buf[x] to buf[x+36] for x in [15,35). + SkRasterPipeline_<256> p; + p.append(SkRasterPipeline:: load_8888, &src); + p.append(SkRasterPipeline::store_8888, &dst); + p.run(15,0, 20); + + for (int i = 0; i < 36; i++) { + if (i < 15 || i == 35) { + REPORTER_ASSERT(r, dst[i] == 0); + } else { + REPORTER_ASSERT(r, dst[i] == (uint32_t)(i - 11)); + } + } +} + +static uint16_t h(float f) { + // Remember, a float is 1-8-23 (sign-exponent-mantissa) with 127 exponent bias. + uint32_t sem; + memcpy(&sem, &f, sizeof(sem)); + uint32_t s = sem & 0x80000000, + em = sem ^ s; + + // Convert to 1-5-10 half with 15 bias, flushing denorm halfs (including zero) to zero. + auto denorm = (int32_t)em < 0x38800000; // I32 comparison is often quicker, and always safe + // here. + return denorm ? SkTo<uint16_t>(0) + : SkTo<uint16_t>((s>>16) + (em>>13) - ((127-15)<<10)); +} + +static uint16_t n(uint16_t x) { + return (x<<8) | (x>>8); +} + +static float a(uint16_t x) { + return (1/65535.0f) * x; +} + +DEF_TEST(SkRasterPipeline_tail, r) { + { + float data[][4] = { + {00, 01, 02, 03}, + {10, 11, 12, 13}, + {20, 21, 22, 23}, + {30, 31, 32, 33}, + }; + + float buffer[4][4]; + float* src = &data[0][0]; + float* dst = &buffer[0][0]; + + for (unsigned i = 1; i <= 4; i++) { + memset(buffer, 0xff, sizeof(buffer)); + SkRasterPipeline_<256> p; + p.append(SkRasterPipeline::load_f32, &src); + p.append(SkRasterPipeline::store_f32, &dst); + p.run(0,0, i); + for (unsigned j = 0; j < i; j++) { + for (unsigned k = 0; k < 4; k++) { + if (buffer[j][k] != data[j][k]) { + ERRORF(r, "(%u, %u) - a: %g r: %g\n", j, k, data[j][k], buffer[j][k]); + } + } + } + for (int j = i; j < 4; j++) { + for (auto f : buffer[j]) { + REPORTER_ASSERT(r, SkScalarIsNaN(f)); + } + } + } + } + + { + uint16_t data[][4] = { + {h(00), h(01), h(02), h(03)}, + {h(10), h(11), h(12), h(13)}, + {h(20), h(21), h(22), h(23)}, + {h(30), h(31), h(32), h(33)}, + }; + uint16_t buffer[4][4]; + uint16_t* src = &data[0][0]; + uint16_t* dst = &buffer[0][0]; + + for (unsigned i = 1; i <= 4; i++) { + memset(buffer, 0xff, sizeof(buffer)); + SkRasterPipeline_<256> p; + p.append(SkRasterPipeline::load_f16, &src); + p.append(SkRasterPipeline::store_f16, &dst); + p.run(0,0, i); + for (unsigned j = 0; j < i; j++) { + REPORTER_ASSERT(r, + !memcmp(&data[j][0], &buffer[j][0], sizeof(buffer[j]))); + } + for (int j = i; j < 4; j++) { + for (auto f : buffer[j]) { + REPORTER_ASSERT(r, f == 0xffff); + } + } + } + } + + { + uint16_t data[][3] = { + {n(00), n(01), n(02)}, + {n(10), n(11), n(12)}, + {n(20), n(21), n(22)}, + {n(30), n(31), n(32)} + }; + + float answer[][4] = { + {a(00), a(01), a(02), 1.0f}, + {a(10), a(11), a(12), 1.0f}, + {a(20), a(21), a(22), 1.0f}, + {a(30), a(31), a(32), 1.0f} + }; + + float buffer[4][4]; + uint16_t* src = &data[0][0]; + float* dst = &buffer[0][0]; + + for (unsigned i = 1; i <= 4; i++) { + memset(buffer, 0xff, sizeof(buffer)); + SkRasterPipeline_<256> p; + p.append(SkRasterPipeline::load_rgb_u16_be, &src); + p.append(SkRasterPipeline::store_f32, &dst); + p.run(0,0, i); + for (unsigned j = 0; j < i; j++) { + for (unsigned k = 0; k < 4; k++) { + if (buffer[j][k] != answer[j][k]) { + ERRORF(r, "(%u, %u) - a: %g r: %g\n", j, k, answer[j][k], buffer[j][k]); + } + } + } + for (int j = i; j < 4; j++) { + for (auto f : buffer[j]) { + REPORTER_ASSERT(r, SkScalarIsNaN(f)); + } + } + } + } +} + +DEF_TEST(SkRasterPipeline_lowp, r) { + uint32_t rgba[64]; + for (int i = 0; i < 64; i++) { + rgba[i] = (4*i+0) << 0 + | (4*i+1) << 8 + | (4*i+2) << 16 + | (4*i+3) << 24; + } + + void* ptr = rgba; + + SkRasterPipeline_<256> p; + p.append(SkRasterPipeline::load_bgra, &ptr); + p.append(SkRasterPipeline::store_8888, &ptr); + p.run(0,0,64); + + for (int i = 0; i < 64; i++) { + uint32_t want = (4*i+0) << 16 + | (4*i+1) << 8 + | (4*i+2) << 0 + | (4*i+3) << 24; + if (rgba[i] != want) { + ERRORF(r, "got %08x, want %08x\n", rgba[i], want); + } + } +} + +DEF_TEST(SkRasterPipeline_2d, r) { + uint32_t rgba[2*2] = {0,0,0,0}; + + SkSTArenaAlloc<256> alloc; + SkRasterPipeline p(&alloc); + + // Splat out the (2d) dst coordinates: (0.5,0.5), (1.5,0.5), (0.5,1.5), (1.5,1.5). + p.append(SkRasterPipeline::seed_shader); + + // Scale down to [0,1] range to write out as bytes. + p.append_matrix(&alloc, SkMatrix::Concat(SkMatrix::MakeScale(0.5f), + SkMatrix::MakeTrans(-0.5f, -0.5f))); + + // Write out to rgba, with row stride = 2 pixels. + SkJumper_MemoryCtx ctx = { rgba, 2 }; + p.append(SkRasterPipeline::store_8888_2d, &ctx); + + p.run_2d(0,0, 2,2); + + REPORTER_ASSERT(r, ((rgba[0] >> 0) & 0xff) == 0); + REPORTER_ASSERT(r, ((rgba[1] >> 0) & 0xff) == 128); + REPORTER_ASSERT(r, ((rgba[2] >> 0) & 0xff) == 0); + REPORTER_ASSERT(r, ((rgba[3] >> 0) & 0xff) == 128); + + REPORTER_ASSERT(r, ((rgba[0] >> 8) & 0xff) == 0); + REPORTER_ASSERT(r, ((rgba[1] >> 8) & 0xff) == 0); + REPORTER_ASSERT(r, ((rgba[2] >> 8) & 0xff) == 128); + REPORTER_ASSERT(r, ((rgba[3] >> 8) & 0xff) == 128); +}
diff --git a/src/third_party/skia/tests/SkResourceCacheTest.cpp b/src/third_party/skia/tests/SkResourceCacheTest.cpp index f13476a..5f8770d 100644 --- a/src/third_party/skia/tests/SkResourceCacheTest.cpp +++ b/src/third_party/skia/tests/SkResourceCacheTest.cpp
@@ -10,159 +10,280 @@ #include "SkCanvas.h" #include "SkDiscardableMemoryPool.h" #include "SkGraphics.h" +#include "SkMakeUnique.h" +#include "SkMipMap.h" +#include "SkPicture.h" +#include "SkPictureRecorder.h" #include "SkResourceCache.h" - -static const int kCanvasSize = 1; -static const int kBitmapSize = 16; -static const int kScale = 8; - -static bool is_in_scaled_image_cache(const SkBitmap& orig, - SkScalar xScale, - SkScalar yScale) { - SkBitmap scaled; - float roundedImageWidth = SkScalarRoundToScalar(orig.width() * xScale); - float roundedImageHeight = SkScalarRoundToScalar(orig.height() * xScale); - return SkBitmapCache::Find(orig, roundedImageWidth, roundedImageHeight, &scaled); -} - -// Draw a scaled bitmap, then return true iff it has been cached. -static bool test_scaled_image_cache_useage() { - SkAutoTUnref<SkCanvas> canvas( - SkCanvas::NewRasterN32(kCanvasSize, kCanvasSize)); - SkBitmap bitmap; - bitmap.allocN32Pixels(kBitmapSize, kBitmapSize); - bitmap.eraseColor(0xFFFFFFFF); - SkScalar scale = SkIntToScalar(kScale); - SkScalar scaledSize = SkIntToScalar(kBitmapSize) * scale; - canvas->clipRect(SkRect::MakeLTRB(0, 0, scaledSize, scaledSize)); - SkPaint paint; - paint.setFilterLevel(SkPaint::kHigh_FilterLevel); - - canvas->drawBitmapRect(bitmap, - SkRect::MakeLTRB(0, 0, scaledSize, scaledSize), - &paint); - - return is_in_scaled_image_cache(bitmap, scale, scale); -} - -// http://crbug.com/389439 -DEF_TEST(ResourceCache_SingleAllocationByteLimit, reporter) { - size_t originalByteLimit = SkGraphics::GetResourceCacheTotalByteLimit(); - size_t originalAllocationLimit = - SkGraphics::GetResourceCacheSingleAllocationByteLimit(); - - size_t size = kBitmapSize * kScale * kBitmapSize * kScale - * SkColorTypeBytesPerPixel(kN32_SkColorType); - - SkGraphics::SetResourceCacheTotalByteLimit(0); // clear cache - SkGraphics::SetResourceCacheTotalByteLimit(2 * size); - SkGraphics::SetResourceCacheSingleAllocationByteLimit(0); // No limit - - REPORTER_ASSERT(reporter, test_scaled_image_cache_useage()); - - SkGraphics::SetResourceCacheTotalByteLimit(0); // clear cache - SkGraphics::SetResourceCacheTotalByteLimit(2 * size); - SkGraphics::SetResourceCacheSingleAllocationByteLimit(size * 2); // big enough - - REPORTER_ASSERT(reporter, test_scaled_image_cache_useage()); - - SkGraphics::SetResourceCacheTotalByteLimit(0); // clear cache - SkGraphics::SetResourceCacheTotalByteLimit(2 * size); - SkGraphics::SetResourceCacheSingleAllocationByteLimit(size / 2); // too small - - REPORTER_ASSERT(reporter, !test_scaled_image_cache_useage()); - - SkGraphics::SetResourceCacheSingleAllocationByteLimit(originalAllocationLimit); - SkGraphics::SetResourceCacheTotalByteLimit(originalByteLimit); -} +#include "SkSurface.h" //////////////////////////////////////////////////////////////////////////////////////// -static void make_bitmap(SkBitmap* bitmap, const SkImageInfo& info, SkBitmap::Allocator* allocator) { - if (allocator) { - bitmap->setInfo(info); - allocator->allocPixelRef(bitmap, 0); - } else { - bitmap->allocPixels(info); +enum LockedState { + kNotLocked, + kLocked, +}; + +enum CachedState { + kNotInCache, + kInCache, +}; + +static void check_data(skiatest::Reporter* reporter, const SkCachedData* data, + int refcnt, CachedState cacheState, LockedState lockedState) { + REPORTER_ASSERT(reporter, data->testing_only_getRefCnt() == refcnt); + REPORTER_ASSERT(reporter, data->testing_only_isInCache() == (kInCache == cacheState)); + bool isLocked = (data->data() != nullptr); + REPORTER_ASSERT(reporter, isLocked == (lockedState == kLocked)); +} + +static void test_mipmapcache(skiatest::Reporter* reporter, SkResourceCache* cache) { + cache->purgeAll(); + + SkBitmap src; + src.allocN32Pixels(5, 5); + src.setImmutable(); + + const SkDestinationSurfaceColorMode colorMode = SkDestinationSurfaceColorMode::kLegacy; + + const SkMipMap* mipmap = SkMipMapCache::FindAndRef(SkBitmapCacheDesc::Make(src), colorMode, + cache); + REPORTER_ASSERT(reporter, nullptr == mipmap); + + mipmap = SkMipMapCache::AddAndRef(src, colorMode, cache); + REPORTER_ASSERT(reporter, mipmap); + + { + const SkMipMap* mm = SkMipMapCache::FindAndRef(SkBitmapCacheDesc::Make(src), colorMode, + cache); + REPORTER_ASSERT(reporter, mm); + REPORTER_ASSERT(reporter, mm == mipmap); + mm->unref(); + } + + check_data(reporter, mipmap, 2, kInCache, kLocked); + + mipmap->unref(); + // tricky, since technically after this I'm no longer an owner, but since the cache is + // local, I know it won't get purged behind my back + check_data(reporter, mipmap, 1, kInCache, kNotLocked); + + // find us again + mipmap = SkMipMapCache::FindAndRef(SkBitmapCacheDesc::Make(src), colorMode, cache); + check_data(reporter, mipmap, 2, kInCache, kLocked); + + cache->purgeAll(); + check_data(reporter, mipmap, 1, kNotInCache, kLocked); + + mipmap->unref(); +} + +static void test_mipmap_notify(skiatest::Reporter* reporter, SkResourceCache* cache) { + const SkDestinationSurfaceColorMode colorMode = SkDestinationSurfaceColorMode::kLegacy; + const int N = 3; + + SkBitmap src[N]; + for (int i = 0; i < N; ++i) { + src[i].allocN32Pixels(5, 5); + src[i].setImmutable(); + SkMipMapCache::AddAndRef(src[i], colorMode, cache)->unref(); + } + + for (int i = 0; i < N; ++i) { + const auto desc = SkBitmapCacheDesc::Make(src[i]); + const SkMipMap* mipmap = SkMipMapCache::FindAndRef(desc, colorMode, cache); + if (cache) { + // if cache is null, we're working on the global cache, and other threads might purge + // it, making this check fragile. + REPORTER_ASSERT(reporter, mipmap); + } + SkSafeUnref(mipmap); + + src[i].reset(); // delete the underlying pixelref, which *should* remove us from the cache + + mipmap = SkMipMapCache::FindAndRef(desc, colorMode, cache); + REPORTER_ASSERT(reporter, !mipmap); } } -// http://skbug.com/2894 -DEF_TEST(BitmapCache_add_rect, reporter) { - SkResourceCache::DiscardableFactory factory = SkResourceCache::GetDiscardableFactory(); - SkBitmap::Allocator* allocator = SkBitmapCache::GetAllocator(); +#include "SkDiscardableMemoryPool.h" - SkAutoTDelete<SkResourceCache> cache; - if (factory) { - cache.reset(SkNEW_ARGS(SkResourceCache, (factory))); - } else { - const size_t byteLimit = 100 * 1024; - cache.reset(SkNEW_ARGS(SkResourceCache, (byteLimit))); - } - SkBitmap cachedBitmap; - make_bitmap(&cachedBitmap, SkImageInfo::MakeN32Premul(5, 5), allocator); - cachedBitmap.setImmutable(); +static SkDiscardableMemoryPool* gPool = 0; +static SkDiscardableMemory* pool_factory(size_t bytes) { + SkASSERT(gPool); + return gPool->create(bytes); +} - SkBitmap bm; - SkIRect rect = SkIRect::MakeWH(5, 5); - - // Wrong subset size - REPORTER_ASSERT(reporter, !SkBitmapCache::Add(cachedBitmap.getGenerationID(), SkIRect::MakeWH(4, 6), cachedBitmap, cache)); - REPORTER_ASSERT(reporter, !SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache)); - // Wrong offset value - REPORTER_ASSERT(reporter, !SkBitmapCache::Add(cachedBitmap.getGenerationID(), SkIRect::MakeXYWH(-1, 0, 5, 5), cachedBitmap, cache)); - REPORTER_ASSERT(reporter, !SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache)); - - // Should not be in the cache - REPORTER_ASSERT(reporter, !SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache)); - - REPORTER_ASSERT(reporter, SkBitmapCache::Add(cachedBitmap.getGenerationID(), rect, cachedBitmap, cache)); - // Should be in the cache, we just added it - REPORTER_ASSERT(reporter, SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache)); +static void testBitmapCache_discarded_bitmap(skiatest::Reporter* reporter, SkResourceCache* cache, + SkResourceCache::DiscardableFactory factory) { + test_mipmapcache(reporter, cache); + test_mipmap_notify(reporter, cache); } DEF_TEST(BitmapCache_discarded_bitmap, reporter) { - SkResourceCache::DiscardableFactory factory = SkResourceCache::GetDiscardableFactory(); - SkBitmap::Allocator* allocator = SkBitmapCache::GetAllocator(); - - SkAutoTDelete<SkResourceCache> cache; - if (factory) { - cache.reset(SkNEW_ARGS(SkResourceCache, (factory))); + const size_t byteLimit = 100 * 1024; + { + SkResourceCache cache(byteLimit); + testBitmapCache_discarded_bitmap(reporter, &cache, nullptr); + } + { + sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Make(byteLimit)); + gPool = pool.get(); + SkResourceCache::DiscardableFactory factory = pool_factory; + SkResourceCache cache(factory); + testBitmapCache_discarded_bitmap(reporter, &cache, factory); + } +} + +static void test_discarded_image(skiatest::Reporter* reporter, const SkMatrix& transform, + sk_sp<SkImage> (*buildImage)()) { + auto surface(SkSurface::MakeRasterN32Premul(10, 10)); + SkCanvas* canvas = surface->getCanvas(); + + // SkBitmapCache is global, so other threads could be evicting our bitmaps. Loop a few times + // to mitigate this risk. + const unsigned kRepeatCount = 42; + for (unsigned i = 0; i < kRepeatCount; ++i) { + SkAutoCanvasRestore acr(canvas, true); + + sk_sp<SkImage> image(buildImage()); + + // always use high quality to ensure caching when scaled + SkPaint paint; + paint.setFilterQuality(kHigh_SkFilterQuality); + + // draw the image (with a transform, to tickle different code paths) to ensure + // any associated resources get cached + canvas->concat(transform); + canvas->drawImage(image, 0, 0, &paint); + + const auto desc = SkBitmapCacheDesc::Make(image.get()); + + // delete the image + image.reset(nullptr); + + // all resources should have been purged + SkBitmap result; + REPORTER_ASSERT(reporter, !SkBitmapCache::Find(desc, &result)); + } +} + + +// Verify that associated bitmap cache entries are purged on SkImage destruction. +DEF_TEST(BitmapCache_discarded_image, reporter) { + // Cache entries associated with SkImages fall into two categories: + // + // 1) generated image bitmaps (managed by the image cacherator) + // 2) scaled/resampled bitmaps (cached when HQ filters are used) + // + // To exercise the first cache type, we use generated/picture-backed SkImages. + // To exercise the latter, we draw scaled bitmap images using HQ filters. + + const SkMatrix xforms[] = { + SkMatrix::MakeScale(1, 1), + SkMatrix::MakeScale(1.7f, 0.5f), + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(xforms); ++i) { + test_discarded_image(reporter, xforms[i], []() { + auto surface(SkSurface::MakeRasterN32Premul(10, 10)); + surface->getCanvas()->clear(SK_ColorCYAN); + return surface->makeImageSnapshot(); + }); + + test_discarded_image(reporter, xforms[i], []() { + SkPictureRecorder recorder; + SkCanvas* canvas = recorder.beginRecording(10, 10); + canvas->clear(SK_ColorCYAN); + return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(), + SkISize::Make(10, 10), nullptr, nullptr, + SkImage::BitDepth::kU8, + SkColorSpace::MakeSRGB()); + }); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static void* gTestNamespace; + +struct TestKey : SkResourceCache::Key { + int32_t fData; + + TestKey(int sharedID, int32_t data) : fData(data) { + this->init(&gTestNamespace, sharedID, sizeof(fData)); + } +}; + +struct TestRec : SkResourceCache::Rec { + enum { + kDidInstall = 1 << 0, + }; + + TestKey fKey; + int* fFlags; + bool fCanBePurged; + + TestRec(int sharedID, int32_t data, int* flagPtr) : fKey(sharedID, data), fFlags(flagPtr) { + fCanBePurged = false; + } + + const Key& getKey() const override { return fKey; } + size_t bytesUsed() const override { return 1024; /* just need a value */ } + bool canBePurged() override { return fCanBePurged; } + void postAddInstall(void*) override { + *fFlags |= kDidInstall; + } + const char* getCategory() const override { return "test-category"; } +}; + +static void test_duplicate_add(SkResourceCache* cache, skiatest::Reporter* reporter, + bool purgable) { + int sharedID = 1; + int data = 0; + + int flags0 = 0, flags1 = 0; + + auto rec0 = skstd::make_unique<TestRec>(sharedID, data, &flags0); + auto rec1 = skstd::make_unique<TestRec>(sharedID, data, &flags1); + SkASSERT(rec0->getKey() == rec1->getKey()); + + TestRec* r0 = rec0.get(); // save the bare-pointer since we will release rec0 + r0->fCanBePurged = purgable; + + REPORTER_ASSERT(reporter, !(flags0 & TestRec::kDidInstall)); + REPORTER_ASSERT(reporter, !(flags1 & TestRec::kDidInstall)); + + cache->add(rec0.release(), nullptr); + REPORTER_ASSERT(reporter, flags0 & TestRec::kDidInstall); + REPORTER_ASSERT(reporter, !(flags1 & TestRec::kDidInstall)); + flags0 = 0; // reset the flag + + cache->add(rec1.release(), nullptr); + if (purgable) { + // we purged rec0, and did install rec1 + REPORTER_ASSERT(reporter, !(flags0 & TestRec::kDidInstall)); + REPORTER_ASSERT(reporter, flags1 & TestRec::kDidInstall); } else { - const size_t byteLimit = 100 * 1024; - cache.reset(SkNEW_ARGS(SkResourceCache, (byteLimit))); + // we re-used rec0 and did not install rec1 + REPORTER_ASSERT(reporter, flags0 & TestRec::kDidInstall); + REPORTER_ASSERT(reporter, !(flags1 & TestRec::kDidInstall)); + r0->fCanBePurged = true; // so we can cleanup the cache } - SkBitmap cachedBitmap; - make_bitmap(&cachedBitmap, SkImageInfo::MakeN32Premul(5, 5), allocator); - cachedBitmap.setImmutable(); - cachedBitmap.unlockPixels(); +} - SkBitmap bm; - SkIRect rect = SkIRect::MakeWH(5, 5); - - // Add a bitmap to the cache. - REPORTER_ASSERT(reporter, SkBitmapCache::Add(cachedBitmap.getGenerationID(), rect, cachedBitmap, cache)); - REPORTER_ASSERT(reporter, SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache)); - - // Finding more than once works fine. - REPORTER_ASSERT(reporter, SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache)); - bm.unlockPixels(); - - // Drop the pixels in the bitmap. - if (factory) { - REPORTER_ASSERT(reporter, SkGetGlobalDiscardableMemoryPool()->getRAMUsed() > 0); - SkGetGlobalDiscardableMemoryPool()->dumpPool(); - REPORTER_ASSERT(reporter, SkGetGlobalDiscardableMemoryPool()->getRAMUsed() == 0); - - // The bitmap is not in the cache since it has been dropped. - REPORTER_ASSERT(reporter, !SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache)); +/* + * Test behavior when the same key is added more than once. + */ +DEF_TEST(ResourceCache_purge, reporter) { + for (bool purgable : { false, true }) { + { + SkResourceCache cache(1024 * 1024); + test_duplicate_add(&cache, reporter, purgable); + } + { + SkResourceCache cache(SkDiscardableMemory::Create); + test_duplicate_add(&cache, reporter, purgable); + } } - - make_bitmap(&cachedBitmap, SkImageInfo::MakeN32Premul(5, 5), allocator); - cachedBitmap.setImmutable(); - cachedBitmap.unlockPixels(); - - // We can add the bitmap back to the cache and find it again. - REPORTER_ASSERT(reporter, SkBitmapCache::Add(cachedBitmap.getGenerationID(), rect, cachedBitmap, cache)); - REPORTER_ASSERT(reporter, SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache)); }
diff --git a/src/third_party/skia/tests/SkSLErrorTest.cpp b/src/third_party/skia/tests/SkSLErrorTest.cpp new file mode 100644 index 0000000..c631382 --- /dev/null +++ b/src/third_party/skia/tests/SkSLErrorTest.cpp
@@ -0,0 +1,492 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkSLCompiler.h" + +#include "Test.h" + +#if SK_SUPPORT_GPU + +static void test_failure(skiatest::Reporter* r, const char* src, const char* error) { + SkSL::Compiler compiler; + SkSL::Program::Settings settings; + sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::Default(); + settings.fCaps = caps.get(); + compiler.convertProgram(SkSL::Program::kFragment_Kind, SkString(src), settings); + SkSL::String skError(error); + if (compiler.errorText() != skError) { + SkDebugf("SKSL ERROR:\n source: %s\n expected: %s received: %s", src, error, + compiler.errorText().c_str()); + } + REPORTER_ASSERT(r, compiler.errorText() == skError); +} + +static void test_success(skiatest::Reporter* r, const char* src) { + SkSL::Compiler compiler; + SkSL::Program::Settings settings; + sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::Default(); + settings.fCaps = caps.get(); + std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kFragment_Kind, + SkString(src), settings); + REPORTER_ASSERT(r, program); +} + +DEF_TEST(SkSLUndefinedSymbol, r) { + test_failure(r, + "void main() { x = vec2(1); }", + "error: 1: unknown identifier 'x'\n1 error\n"); +} + +DEF_TEST(SkSLUndefinedFunction, r) { + test_failure(r, + "void main() { int x = foo(1); }", + "error: 1: unknown identifier 'foo'\n1 error\n"); +} + +DEF_TEST(SkSLGenericArgumentMismatch, r) { + test_failure(r, + "void main() { float x = sin(1, 2); }", + "error: 1: call to 'sin' expected 1 argument, but found 2\n1 error\n"); + test_failure(r, + "void main() { float x = sin(true); }", + "error: 1: no match for sin(bool)\n1 error\n"); + test_success(r, + "void main() { float x = sin(1); }"); +} + +DEF_TEST(SkSLArgumentCountMismatch, r) { + test_failure(r, + "float foo(float x) { return x * x; }" + "void main() { float x = foo(1, 2); }", + "error: 1: call to 'foo' expected 1 argument, but found 2\n1 error\n"); +} + +DEF_TEST(SkSLArgumentMismatch, r) { + test_failure(r, + "float foo(float x) { return x * x; }" + "void main() { float x = foo(true); }", + "error: 1: expected 'float', but found 'bool'\n1 error\n"); +} + +DEF_TEST(SkSLIfTypeMismatch, r) { + test_failure(r, + "void main() { if (3) { } }", + "error: 1: expected 'bool', but found 'int'\n1 error\n"); +} + +DEF_TEST(SkSLDoTypeMismatch, r) { + test_failure(r, + "void main() { do { } while (vec2(1)); }", + "error: 1: expected 'bool', but found 'vec2'\n1 error\n"); +} + +DEF_TEST(SkSLWhileTypeMismatch, r) { + test_failure(r, + "void main() { while (vec3(1)) { } }", + "error: 1: expected 'bool', but found 'vec3'\n1 error\n"); +} + +DEF_TEST(SkSLForTypeMismatch, r) { + test_failure(r, + "void main() { for (int x = 0; x; x++) { } }", + "error: 1: expected 'bool', but found 'int'\n1 error\n"); +} + +DEF_TEST(SkSLConstructorTypeMismatch, r) { + test_failure(r, + "void main() { vec2 x = vec2(1.0, false); }", + "error: 1: expected 'float', but found 'bool'\n1 error\n"); + test_failure(r, + "void main() { vec2 x = vec2(bvec2(false)); }", + "error: 1: 'bvec2' is not a valid parameter to 'vec2' constructor\n1 error\n"); + test_failure(r, + "void main() { bvec2 x = bvec2(vec2(1)); }", + "error: 1: 'vec2' is not a valid parameter to 'bvec2' constructor\n1 error\n"); + test_failure(r, + "void main() { bool x = bool(1.0); }", + "error: 1: cannot construct 'bool'\n1 error\n"); + test_failure(r, + "struct foo { int x; }; void main() { foo x = foo(5); }", + "error: 1: cannot construct 'foo'\n1 error\n"); + test_failure(r, + "struct foo { int x; } foo; void main() { float x = float(foo); }", + "error: 1: invalid argument to 'float' constructor (expected a number or bool, but found 'foo')\n1 error\n"); + test_failure(r, + "struct foo { int x; } foo; void main() { vec2 x = vec2(foo); }", + "error: 1: 'foo' is not a valid parameter to 'vec2' constructor\n1 error\n"); + test_failure(r, + "void main() { mat2 x = mat2(true); }", + "error: 1: expected 'float', but found 'bool'\n1 error\n"); +} + +DEF_TEST(SkSLConstructorArgumentCount, r) { + test_failure(r, + "void main() { vec3 x = vec3(1.0, 2.0); }", + "error: 1: invalid arguments to 'vec3' constructor (expected 3 scalars, but " + "found 2)\n1 error\n"); + test_failure(r, + "void main() { vec3 x = vec3(1.0, 2.0, 3.0, 4.0); }", + "error: 1: invalid arguments to 'vec3' constructor (expected 3 scalars, but found " + "4)\n1 error\n"); +} + +DEF_TEST(SkSLSwizzleScalar, r) { + test_failure(r, + "void main() { float x = 1; float y = x.y; }", + "error: 1: cannot swizzle value of type 'float'\n1 error\n"); +} + +DEF_TEST(SkSLSwizzleMatrix, r) { + test_failure(r, + "void main() { mat2 x = mat2(1); float y = x.y; }", + "error: 1: cannot swizzle value of type 'mat2'\n1 error\n"); +} + +DEF_TEST(SkSLSwizzleOutOfBounds, r) { + test_failure(r, + "void main() { vec3 test = vec2(1).xyz; }", + "error: 1: invalid swizzle component 'z'\n1 error\n"); +} + +DEF_TEST(SkSLSwizzleTooManyComponents, r) { + test_failure(r, + "void main() { vec4 test = vec2(1).xxxxx; }", + "error: 1: too many components in swizzle mask 'xxxxx'\n1 error\n"); +} + +DEF_TEST(SkSLSwizzleDuplicateOutput, r) { + test_failure(r, + "void main() { vec4 test = vec4(1); test.xyyz = vec4(1); }", + "error: 1: cannot write to the same swizzle field more than once\n1 error\n"); +} + +DEF_TEST(SkSLAssignmentTypeMismatch, r) { + test_failure(r, + "void main() { int x = 1.0; }", + "error: 1: expected 'int', but found 'float'\n1 error\n"); + test_failure(r, + "void main() { int x; x = 1.0; }", + "error: 1: type mismatch: '=' cannot operate on 'int', 'float'\n1 error\n"); + test_success(r, + "void main() { vec3 x = vec3(0); x *= 1.0; }"); + test_failure(r, + "void main() { ivec3 x = ivec3(0); x *= 1.0; }", + "error: 1: type mismatch: '*=' cannot operate on 'ivec3', 'float'\n1 error\n"); +} + +DEF_TEST(SkSLReturnFromVoid, r) { + test_failure(r, + "void main() { return true; }", + "error: 1: may not return a value from a void function\n1 error\n"); +} + +DEF_TEST(SkSLReturnMissingValue, r) { + test_failure(r, + "int foo() { return; } void main() { }", + "error: 1: expected function to return 'int'\n1 error\n"); +} + +DEF_TEST(SkSLReturnTypeMismatch, r) { + test_failure(r, + "int foo() { return 1.0; } void main() { }", + "error: 1: expected 'int', but found 'float'\n1 error\n"); +} + +DEF_TEST(SkSLDuplicateFunction, r) { + test_failure(r, + "void main() { } void main() { }", + "error: 1: duplicate definition of void main()\n1 error\n"); + test_success(r, + "void main(); void main() { }"); +} + +DEF_TEST(SkSLUsingInvalidValue, r) { + test_failure(r, + "void main() { int x = int; }", + "error: 1: expected '(' to begin constructor invocation\n1 error\n"); + test_failure(r, + "int test() { return 1; } void main() { int x = test; }", + "error: 1: expected '(' to begin function call\n1 error\n"); +} +DEF_TEST(SkSLDifferentReturnType, r) { + test_failure(r, + "int main() { return 1; } void main() { }", + "error: 1: functions 'void main()' and 'int main()' differ only in return type\n1 " + "error\n"); +} + +DEF_TEST(SkSLDifferentModifiers, r) { + test_failure(r, + "void test(int x); void test(out int x) { }", + "error: 1: modifiers on parameter 1 differ between declaration and definition\n1 " + "error\n"); +} + +DEF_TEST(SkSLDuplicateSymbol, r) { + test_failure(r, + "int main; void main() { }", + "error: 1: symbol 'main' was already defined\n1 error\n"); + + test_failure(r, + "int x; int x; void main() { }", + "error: 1: symbol 'x' was already defined\n1 error\n"); + + test_success(r, "int x; void main() { int x; }"); +} + +DEF_TEST(SkSLBinaryTypeMismatch, r) { + test_failure(r, + "void main() { float x = 3 * true; }", + "error: 1: type mismatch: '*' cannot operate on 'int', 'bool'\n1 error\n"); + test_failure(r, + "void main() { bool x = 1 || 2.0; }", + "error: 1: type mismatch: '||' cannot operate on 'int', 'float'\n1 error\n"); +} + +DEF_TEST(SkSLCallNonFunction, r) { + test_failure(r, + "void main() { float x = 3; x(); }", + "error: 1: 'x' is not a function\n1 error\n"); +} + +DEF_TEST(SkSLInvalidUnary, r) { + test_failure(r, + "void main() { mat4 x = mat4(1); ++x; }", + "error: 1: '++' cannot operate on 'mat4'\n1 error\n"); + test_failure(r, + "void main() { vec3 x = vec3(1); --x; }", + "error: 1: '--' cannot operate on 'vec3'\n1 error\n"); + test_failure(r, + "void main() { mat4 x = mat4(1); x++; }", + "error: 1: '++' cannot operate on 'mat4'\n1 error\n"); + test_failure(r, + "void main() { vec3 x = vec3(1); x--; }", + "error: 1: '--' cannot operate on 'vec3'\n1 error\n"); + test_failure(r, + "void main() { int x = !12; }", + "error: 1: '!' cannot operate on 'int'\n1 error\n"); + test_failure(r, + "struct foo { } bar; void main() { foo x = +bar; }", + "error: 1: '+' cannot operate on 'foo'\n1 error\n"); + test_failure(r, + "struct foo { } bar; void main() { foo x = -bar; }", + "error: 1: '-' cannot operate on 'foo'\n1 error\n"); + test_success(r, + "void main() { vec2 x = vec2(1, 1); x = +x; x = -x; }"); +} + +DEF_TEST(SkSLInvalidAssignment, r) { + test_failure(r, + "void main() { 1 = 2; }", + "error: 1: cannot assign to '1'\n1 error\n"); + test_failure(r, + "uniform int x; void main() { x = 0; }", + "error: 1: cannot modify immutable variable 'x'\n1 error\n"); + test_failure(r, + "const int x; void main() { x = 0; }", + "error: 1: cannot modify immutable variable 'x'\n1 error\n"); +} + +DEF_TEST(SkSLBadIndex, r) { + test_failure(r, + "void main() { int x = 2[0]; }", + "error: 1: expected array, but found 'int'\n1 error\n"); + test_failure(r, + "void main() { vec2 x = vec2(0); int y = x[0][0]; }", + "error: 1: expected array, but found 'float'\n1 error\n"); +} + +DEF_TEST(SkSLTernaryMismatch, r) { + test_failure(r, + "void main() { int x = 5 > 2 ? true : 1.0; }", + "error: 1: ternary operator result mismatch: 'bool', 'float'\n1 error\n"); + test_failure(r, + "void main() { int x = 5 > 2 ? vec3(1) : 1.0; }", + "error: 1: ternary operator result mismatch: 'vec3', 'float'\n1 error\n"); +} + +DEF_TEST(SkSLInterfaceBlockStorageModifiers, r) { + test_failure(r, + "uniform foo { out int x; };", + "error: 1: interface block fields may not have storage qualifiers\n1 error\n"); +} + +DEF_TEST(SkSLUseWithoutInitialize, r) { + test_failure(r, + "void main() { int x; if (5 == 2) x = 3; x++; }", + "error: 1: 'x' has not been assigned\n1 error\n"); + test_failure(r, + "void main() { int x[2][2]; int i; x[i][1] = 4; }", + "error: 1: 'i' has not been assigned\n1 error\n"); + test_failure(r, + "int main() { int r; return r; }", + "error: 1: 'r' has not been assigned\n1 error\n"); + test_failure(r, + "void main() { int x; int y = x; }", + "error: 1: 'x' has not been assigned\n1 error\n"); + test_failure(r, + "void main() { bool x; if (true && (false || x)) return; }", + "error: 1: 'x' has not been assigned\n1 error\n"); + test_failure(r, + "void main() { int x; switch (3) { case 0: x = 0; case 1: x = 1; }" + "sk_FragColor = vec4(x); }", + "error: 1: 'x' has not been assigned\n1 error\n"); +} + +DEF_TEST(SkSLUnreachable, r) { + test_failure(r, + "void main() { return; return; }", + "error: 1: unreachable\n1 error\n"); + test_failure(r, + "void main() { for (;;) { continue; int x = 1; } }", + "error: 1: unreachable\n1 error\n"); +/* test_failure(r, + "void main() { for (;;) { } return; }", + "error: 1: unreachable\n1 error\n");*/ + test_failure(r, + "void main() { if (true) return; else discard; return; }", + "error: 1: unreachable\n1 error\n"); + test_failure(r, + "void main() { return; while (true); }", + "error: 1: unreachable\n1 error\n"); +} + +DEF_TEST(SkSLNoReturn, r) { + test_failure(r, + "int foo() { if (2 > 5) return 3; }", + "error: 1: function can exit without returning a value\n1 error\n"); +} + +DEF_TEST(SkSLBreakOutsideLoop, r) { + test_failure(r, + "void foo() { while(true) {} if (true) break; }", + "error: 1: break statement must be inside a loop or switch\n1 error\n"); +} + +DEF_TEST(SkSLContinueOutsideLoop, r) { + test_failure(r, + "void foo() { for(;;); continue; }", + "error: 1: continue statement must be inside a loop\n1 error\n"); + test_failure(r, + "void foo() { switch (1) { default: continue; } }", + "error: 1: continue statement must be inside a loop\n1 error\n"); +} + +DEF_TEST(SkSLStaticIfError, r) { + // ensure eliminated branch of static if / ternary is still checked for errors + test_failure(r, + "void foo() { if (true); else x = 5; }", + "error: 1: unknown identifier 'x'\n1 error\n"); + test_failure(r, + "void foo() { if (false) x = 5; }", + "error: 1: unknown identifier 'x'\n1 error\n"); + test_failure(r, + "void foo() { true ? 5 : x; }", + "error: 1: unknown identifier 'x'\n1 error\n"); + test_failure(r, + "void foo() { false ? x : 5; }", + "error: 1: unknown identifier 'x'\n1 error\n"); +} + +DEF_TEST(SkSLBadCap, r) { + test_failure(r, + "bool b = sk_Caps.bugFreeDriver;", + "error: 1: unknown capability flag 'bugFreeDriver'\n1 error\n"); +} + +DEF_TEST(SkSLDivByZero, r) { + test_failure(r, + "int x = 1 / 0;", + "error: 1: division by zero\n1 error\n"); + test_failure(r, + "float x = 1 / 0;", + "error: 1: division by zero\n1 error\n"); + test_failure(r, + "float x = 1.0 / 0.0;", + "error: 1: division by zero\n1 error\n"); + test_failure(r, + "float x = -67.0 / (3.0 - 3);", + "error: 1: division by zero\n1 error\n"); +} + +DEF_TEST(SkSLUnsupportedGLSLIdentifiers, r) { + test_failure(r, + "void main() { float x = gl_FragCoord.x; };", + "error: 1: unknown identifier 'gl_FragCoord'\n1 error\n"); + test_failure(r, + "void main() { float r = gl_FragColor.r; };", + "error: 1: unknown identifier 'gl_FragColor'\n1 error\n"); +} + +DEF_TEST(SkSLWrongSwitchTypes, r) { + test_failure(r, + "void main() { switch (vec2(1)) { case 1: break; } }", + "error: 1: expected 'int', but found 'vec2'\n1 error\n"); + test_failure(r, + "void main() { switch (1) { case vec2(1): break; } }", + "error: 1: expected 'int', but found 'vec2'\n1 error\n"); +} + +DEF_TEST(SkSLNonConstantCase, r) { + test_failure(r, + "void main() { int x = 1; switch (1) { case x: break; } }", + "error: 1: case value must be a constant\n1 error\n"); +} + +DEF_TEST(SkSLDuplicateCase, r) { + test_failure(r, + "void main() { switch (1) { case 0: case 1: case 0: break; } }", + "error: 1: duplicate case value\n1 error\n"); +} + +DEF_TEST(SkSLFieldAfterRuntimeArray, r) { + test_failure(r, + "buffer broken { float x[]; float y; };", + "error: 1: only the last entry in an interface block may be a runtime-sized " + "array\n1 error\n"); +} + +DEF_TEST(SkSLStaticIf, r) { + test_success(r, + "void main() { float x = 5; float y = 10;" + "@if (x < y) { sk_FragColor = vec4(1); } }"); + test_failure(r, + "void main() { float x = sqrt(25); float y = 10;" + "@if (x < y) { sk_FragColor = vec4(1); } }", + "error: 1: static if has non-static test\n1 error\n"); +} + +DEF_TEST(SkSLStaticSwitch, r) { + test_success(r, + "void main() {" + "int x = 1;" + "@switch (x) {" + "case 1: sk_FragColor = vec4(1); break;" + "default: sk_FragColor = vec4(0);" + "}" + "}"); + test_failure(r, + "void main() {" + "int x = int(sqrt(1));" + "@switch (x) {" + "case 1: sk_FragColor = vec4(1); break;" + "default: sk_FragColor = vec4(0);" + "}" + "}", + "error: 1: static switch has non-static test\n1 error\n"); + test_failure(r, + "void main() {" + "int x = 1;" + "@switch (x) {" + "case 1: sk_FragColor = vec4(1); if (sqrt(0) < sqrt(1)) break;" + "default: sk_FragColor = vec4(0);" + "}" + "}", + "error: 1: static switch contains non-static conditional break\n1 error\n"); +} + +#endif
diff --git a/src/third_party/skia/tests/SkSLFPTest.cpp b/src/third_party/skia/tests/SkSLFPTest.cpp new file mode 100644 index 0000000..bf8e835 --- /dev/null +++ b/src/third_party/skia/tests/SkSLFPTest.cpp
@@ -0,0 +1,398 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkSLCompiler.h" + +#include "Test.h" + +#if SK_SUPPORT_GPU + +static void test(skiatest::Reporter* r, const char* src, const GrShaderCaps& caps, + std::vector<const char*> expectedH, std::vector<const char*> expectedCPP) { + SkSL::Program::Settings settings; + settings.fCaps = ∩︀ + SkSL::Compiler compiler; + SkSL::StringStream output; + std::unique_ptr<SkSL::Program> program = compiler.convertProgram( + SkSL::Program::kFragmentProcessor_Kind, + SkString(src), + settings); + if (!program) { + SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str()); + return; + } + REPORTER_ASSERT(r, program); + bool success = compiler.toH(*program, "Test", output); + if (!success) { + SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str()); + } + REPORTER_ASSERT(r, success); + if (success) { + for (const char* expected : expectedH) { + bool found = strstr(output.str().c_str(), expected); + if (!found) { + SkDebugf("HEADER MISMATCH:\nsource:\n%s\n\nexpected:\n'%s'\n\nreceived:\n'%s'", src, + expected, output.str().c_str()); + } + REPORTER_ASSERT(r, found); + } + } + output.reset(); + success = compiler.toCPP(*program, "Test", output); + if (!success) { + SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str()); + } + REPORTER_ASSERT(r, success); + if (success) { + for (const char* expected : expectedCPP) { + bool found = strstr(output.str().c_str(), expected); + if (!found) { + SkDebugf("CPP MISMATCH:\nsource:\n%s\n\nexpected:\n'%s'\n\nreceived:\n'%s'", src, + expected, output.str().c_str()); + } + REPORTER_ASSERT(r, found); + } + } +} + +DEF_TEST(SkSLFPHelloWorld, r) { + test(r, + "void main() {" + "sk_OutColor = vec4(1);" + "}", + *SkSL::ShaderCapsFactory::Default(), + { + "/*\n" + " * Copyright 2017 Google Inc.\n" + " *\n" + " * Use of this source code is governed by a BSD-style license that can be\n" + " * found in the LICENSE file.\n" + " */\n" + "\n" + "/*\n" + " * This file was autogenerated from GrTest.fp; do not modify.\n" + " */\n" + "#ifndef GrTest_DEFINED\n" + "#define GrTest_DEFINED\n" + "#include \"SkTypes.h\"\n" + "#if SK_SUPPORT_GPU\n" + "#include \"GrFragmentProcessor.h\"\n" + "#include \"GrCoordTransform.h\"\n" + "#include \"GrColorSpaceXform.h\"\n" + "#include \"effects/GrProxyMove.h\"\n" + "class GrTest : public GrFragmentProcessor {\n" + "public:\n" + " static sk_sp<GrFragmentProcessor> Make() {\n" + " return sk_sp<GrFragmentProcessor>(new GrTest());\n" + " }\n" + " const char* name() const override { return \"Test\"; }\n" + "private:\n" + " GrTest()\n" + " : INHERITED(kNone_OptimizationFlags) {\n" + " this->initClassID<GrTest>();\n" + " }\n" + " GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;\n" + " void onGetGLSLProcessorKey(const GrShaderCaps&,GrProcessorKeyBuilder*) " + "const override;\n" + " bool onIsEqual(const GrFragmentProcessor&) const override;\n" + " GR_DECLARE_FRAGMENT_PROCESSOR_TEST\n" + " typedef GrFragmentProcessor INHERITED;\n" + "};\n" + "#endif\n" + "#endif\n" + }, + { + "/*\n" + " * Copyright 2017 Google Inc.\n" + " *\n" + " * Use of this source code is governed by a BSD-style license that can be\n" + " * found in the LICENSE file.\n" + " */\n" + "\n" + "/*\n" + " * This file was autogenerated from GrTest.fp; do not modify.\n" + " */\n" + "#include \"GrTest.h\"\n" + "#if SK_SUPPORT_GPU\n" + "#include \"glsl/GrGLSLColorSpaceXformHelper.h\"\n" + "#include \"glsl/GrGLSLFragmentProcessor.h\"\n" + "#include \"glsl/GrGLSLFragmentShaderBuilder.h\"\n" + "#include \"glsl/GrGLSLProgramBuilder.h\"\n" + "#include \"SkSLCPP.h\"\n" + "#include \"SkSLUtil.h\"\n" + "class GrGLSLTest : public GrGLSLFragmentProcessor {\n" + "public:\n" + " GrGLSLTest() {}\n" + " void emitCode(EmitArgs& args) override {\n" + " GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;\n" + " const GrTest& _outer = args.fFp.cast<GrTest>();\n" + " (void) _outer;\n" + " fragBuilder->codeAppendf(\"%s = vec4(1.0);\\n\", args.fOutputColor);\n" + " }\n" + "private:\n" + " void onSetData(const GrGLSLProgramDataManager& pdman, " + "const GrFragmentProcessor& _proc) override {\n" + " }\n" + "};\n" + "GrGLSLFragmentProcessor* GrTest::onCreateGLSLInstance() const {\n" + " return new GrGLSLTest();\n" + "}\n" + "void GrTest::onGetGLSLProcessorKey(const GrShaderCaps& caps, " + "GrProcessorKeyBuilder* b) const {\n" + "}\n" + "bool GrTest::onIsEqual(const GrFragmentProcessor& other) const {\n" + " const GrTest& that = other.cast<GrTest>();\n" + " (void) that;\n" + " return true;\n" + "}\n" + "#endif\n" + }); +} + +DEF_TEST(SkSLFPInput, r) { + test(r, + "in vec2 point;" + "void main() {" + "sk_OutColor = vec4(point, point);" + "}", + *SkSL::ShaderCapsFactory::Default(), + { + "SkPoint point() const { return fPoint; }", + "static sk_sp<GrFragmentProcessor> Make(SkPoint point) {", + "return sk_sp<GrFragmentProcessor>(new GrTest(point));", + "GrTest(SkPoint point)", + ", fPoint(point)" + }, + { + "fragBuilder->codeAppendf(\"%s = vec4(vec2(%f, %f), vec2(%f, %f));\\n\", " + "args.fOutputColor, _outer.point().fX, _outer.point().fY, " + "_outer.point().fX, _outer.point().fY);", + "if (fPoint != that.fPoint) return false;" + }); +} + +DEF_TEST(SkSLFPUniform, r) { + test(r, + "uniform vec4 color;" + "void main() {" + "sk_OutColor = color;" + "}", + *SkSL::ShaderCapsFactory::Default(), + { + "static sk_sp<GrFragmentProcessor> Make()" + }, + { + "fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kVec4f_GrSLType, " + "kDefault_GrSLPrecision, \"color\");", + }); +} + +DEF_TEST(SkSLFPInUniform, r) { + test(r, + "in uniform vec4 color;" + "void main() {" + "sk_OutColor = color;" + "}", + *SkSL::ShaderCapsFactory::Default(), + { + "static sk_sp<GrFragmentProcessor> Make(SkRect color) {", + }, + { + "fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kVec4f_GrSLType, " + "kDefault_GrSLPrecision, \"color\");", + "const SkRect colorValue = _outer.color();", + "pdman.set4fv(fColorVar, 1, (float*) &colorValue);" + }); +} + +DEF_TEST(SkSLFPSections, r) { + test(r, + "@header { header section }" + "void main() {" + "sk_OutColor = vec4(1);" + "}", + *SkSL::ShaderCapsFactory::Default(), + { + "#if SK_SUPPORT_GPU\n header section" + }, + {}); + test(r, + "@class { class section }" + "void main() {" + "sk_OutColor = vec4(1);" + "}", + *SkSL::ShaderCapsFactory::Default(), + { + "class GrTest : public GrFragmentProcessor {\n" + "public:\n" + " class section" + }, + {}); + test(r, + "@cpp { cpp section }" + "void main() {" + "sk_OutColor = vec4(1);" + "}", + *SkSL::ShaderCapsFactory::Default(), + {}, + {"cpp section"}); + test(r, + "@constructorParams { int x, float y, std::vector<float> z }" + "in float w;" + "void main() {" + "sk_OutColor = vec4(1);" + "}", + *SkSL::ShaderCapsFactory::Default(), + { + "Make(float w, int x, float y, std::vector<float> z )", + "return sk_sp<GrFragmentProcessor>(new GrTest(w, x, y, z));", + "GrTest(float w, int x, float y, std::vector<float> z )", + ", fW(w) {" + }, + {}); + test(r, + "@constructor { constructor section }" + "void main() {" + "sk_OutColor = vec4(1);" + "}", + *SkSL::ShaderCapsFactory::Default(), + { + "private:\n constructor section" + }, + {}); + test(r, + "@initializers { initializers section }" + "void main() {" + "sk_OutColor = vec4(1);" + "}", + *SkSL::ShaderCapsFactory::Default(), + { + ": INHERITED(kNone_OptimizationFlags)\n , initializers section" + }, + {}); + test(r, + "float x = 10;" + "@emitCode { fragBuilder->codeAppendf(\"float y = %d\\n\", x * 2); }" + "void main() {" + "sk_OutColor = vec4(1);" + "}", + *SkSL::ShaderCapsFactory::Default(), + {}, + { + "x = 10.0;\n" + " fragBuilder->codeAppendf(\"float y = %d\\n\", x * 2);" + }); + test(r, + "@fields { fields section }" + "void main() {" + "sk_OutColor = vec4(1);" + "}", + *SkSL::ShaderCapsFactory::Default(), + { + "GR_DECLARE_FRAGMENT_PROCESSOR_TEST\n" + " fields section typedef GrFragmentProcessor INHERITED;" + }, + {}); + test(r, + "@make { make section }" + "void main() {" + "sk_OutColor = vec4(1);" + "}", + *SkSL::ShaderCapsFactory::Default(), + { + "public:\n" + " make section" + }, + {}); + test(r, + "uniform float calculated;" + "in float provided;" + "@setData(varName) { varName.set1f(calculated, provided * 2); }" + "void main() {" + "sk_OutColor = vec4(1);" + "}", + *SkSL::ShaderCapsFactory::Default(), + {}, + { + "void onSetData(const GrGLSLProgramDataManager& varName, " + "const GrFragmentProcessor& _proc) override {\n", + "UniformHandle& calculated = fCalculatedVar;", + "auto provided = _outer.provided();", + "varName.set1f(calculated, provided * 2);" + }); + test(r, + "@test(testDataName) { testDataName section }" + "void main() {" + "sk_OutColor = vec4(1);" + "}", + *SkSL::ShaderCapsFactory::Default(), + {}, + { + "#if GR_TEST_UTILS\n" + "sk_sp<GrFragmentProcessor> GrTest::TestCreate(GrProcessorTestData* testDataName) {\n" + " testDataName section }\n" + "#endif" + }); +} + +DEF_TEST(SkSLFPColorSpaceXform, r) { + test(r, + "in uniform sampler2D image;" + "in uniform colorSpaceXform colorXform;" + "void main() {" + "sk_OutColor = sk_InColor * texture(image, vec2(0, 0), colorXform);" + "}", + *SkSL::ShaderCapsFactory::Default(), + { + "sk_sp<GrColorSpaceXform> colorXform() const { return fColorXform; }", + "GrTest(sk_sp<GrTextureProxy> image, sk_sp<GrColorSpaceXform> colorXform)", + "this->addTextureSampler(&fImage);", + "sk_sp<GrColorSpaceXform> fColorXform;" + }, + { + "fragBuilder->codeAppendf(\"vec4 _tmpVar1;%s = %s * %stexture(%s, " + "vec2(0.0, 0.0)).%s%s;\\n\", args.fOutputColor, args.fInputColor ? args.fInputColor : " + "\"vec4(1)\", fColorSpaceHelper.isValid() ? \"(_tmpVar1 = \" : \"\", " + "fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str(), " + "fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(), " + "fColorSpaceHelper.isValid() ? SkStringPrintf(\", vec4(clamp((%s * vec4(_tmpVar1.rgb, " + "1.0)).rgb, 0.0, _tmpVar1.a), _tmpVar1.a))\", args.fUniformHandler->getUniformCStr(" + "fColorSpaceHelper.gamutXformUniform())).c_str() : \"\");" + }); +} + +DEF_TEST(SkSLFPTransformedCoords, r) { + test(r, + "void main() {" + "sk_OutColor = vec4(sk_TransformedCoords2D[0], sk_TransformedCoords2D[0]);" + "}", + *SkSL::ShaderCapsFactory::Default(), + {}, + { + "SkSL::String sk_TransformedCoords2D_0 = " + "fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);", + "fragBuilder->codeAppendf(\"%s = vec4(%s, %s);\\n\", args.fOutputColor, " + "sk_TransformedCoords2D_0.c_str(), sk_TransformedCoords2D_0.c_str());" + }); + +} + +DEF_TEST(SkSLFPLayoutWhen, r) { + test(r, + "layout(when=someExpression(someOtherExpression())) uniform float sometimes;" + "void main() {" + "}", + *SkSL::ShaderCapsFactory::Default(), + {}, + { + "if (someExpression(someOtherExpression())) {\n" + " fSometimesVar = args.fUniformHandler->addUniform" + }); + +} + +#endif
diff --git a/src/third_party/skia/tests/SkSLGLSLTest.cpp b/src/third_party/skia/tests/SkSLGLSLTest.cpp new file mode 100644 index 0000000..8f09d6b --- /dev/null +++ b/src/third_party/skia/tests/SkSLGLSLTest.cpp
@@ -0,0 +1,1433 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkSLCompiler.h" + +#include "Test.h" + +#if SK_SUPPORT_GPU + +// Note that the optimizer will aggressively kill dead code and substitute constants in place of +// variables, so we have to jump through a few hoops to ensure that the code in these tests has the +// necessary side-effects to remain live. In some cases we rely on the optimizer not (yet) being +// smart enough to optimize around certain constructs; as the optimizer gets smarter it will +// undoubtedly end up breaking some of these tests. That is a good thing, as long as the new code is +// equivalent! + +static void test(skiatest::Reporter* r, const char* src, const SkSL::Program::Settings& settings, + const char* expected, SkSL::Program::Inputs* inputs, + SkSL::Program::Kind kind = SkSL::Program::kFragment_Kind) { + SkSL::Compiler compiler; + SkSL::String output; + std::unique_ptr<SkSL::Program> program = compiler.convertProgram(kind, SkString(src), settings); + if (!program) { + SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str()); + } + REPORTER_ASSERT(r, program); + *inputs = program->fInputs; + REPORTER_ASSERT(r, compiler.toGLSL(*program, &output)); + if (program) { + SkSL::String skExpected(expected); + if (output != skExpected) { + SkDebugf("GLSL MISMATCH:\nsource:\n%s\n\nexpected:\n'%s'\n\nreceived:\n'%s'", src, + expected, output.c_str()); + } + REPORTER_ASSERT(r, output == skExpected); + } +} + +static void test(skiatest::Reporter* r, const char* src, const GrShaderCaps& caps, + const char* expected, SkSL::Program::Kind kind = SkSL::Program::kFragment_Kind) { + SkSL::Program::Settings settings; + settings.fCaps = ∩︀ + SkSL::Program::Inputs inputs; + test(r, src, settings, expected, &inputs, kind); +} + +DEF_TEST(SkSLHelloWorld, r) { + test(r, + "void main() { sk_FragColor = vec4(0.75); }", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor = vec4(0.75);\n" + "}\n"); +} + +DEF_TEST(SkSLControl, r) { + test(r, + "void main() {" + "if (sqrt(2) > 5) { sk_FragColor = vec4(0.75); } else { discard; }" + "int i = 0;" + "while (i < 10) { sk_FragColor *= 0.5; i++; }" + "do { sk_FragColor += 0.01; } while (sk_FragColor.x < 0.75);" + "for (int i = 0; i < 10; i++) {" + "if (i % 2 == 1) break; else continue;" + "}" + "return;" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " if (sqrt(2.0) > 5.0) {\n" + " sk_FragColor = vec4(0.75);\n" + " } else {\n" + " discard;\n" + " }\n" + " int i = 0;\n" + " while (i < 10) {\n" + " sk_FragColor *= 0.5;\n" + " i++;\n" + " }\n" + " do {\n" + " sk_FragColor += 0.01;\n" + " } while (sk_FragColor.x < 0.75);\n" + " for (int i = 0;i < 10; i++) {\n" + " if (i % 2 == 1) break; else continue;\n" + " }\n" + " return;\n" + "}\n"); +} + +DEF_TEST(SkSLFunctions, r) { + test(r, + "float foo(float v[2]) { return v[0] * v[1]; }" + "void bar(inout float x) { float y[2], z; y[0] = x; y[1] = x * 2; z = foo(y); x = z; }" + "void main() { float x = 10; bar(x); sk_FragColor = vec4(x); }", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "float foo(float v[2]) {\n" + " return v[0] * v[1];\n" + "}\n" + "void bar(inout float x) {\n" + " float y[2], z;\n" + " y[0] = x;\n" + " y[1] = x * 2.0;\n" + " z = foo(y);\n" + " x = z;\n" + "}\n" + "void main() {\n" + " float x = 10.0;\n" + " bar(x);\n" + " sk_FragColor = vec4(x);\n" + "}\n"); +} + +DEF_TEST(SkSLOperators, r) { + test(r, + "void main() {" + "float x = 1, y = 2;" + "int z = 3;" + "x = x - x + y * z * x * (y - z);" + "y = x / y / z;" + "z = (z / 2 % 3 << 4) >> 2 << 1;" + "bool b = (x > 4) == x < 2 || 2 >= sqrt(2) && y <= z;" + "x += 12;" + "x -= 12;" + "x *= y /= z = 10;" + "b ||= false;" + "b &&= true;" + "b ^^= false;" + "z |= 0;" + "z &= -1;" + "z ^= 0;" + "z >>= 2;" + "z <<= 4;" + "z %= 5;" + "x = (vec2(sqrt(1)) , 6);" + "z = (vec2(sqrt(1)) , 6);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " float x = 1.0, y = 2.0;\n" + " int z = 3;\n" + " x = -6.0;\n" + " y = -1.0;\n" + " z = 8;\n" + " bool b = false == true || 2.0 >= sqrt(2.0) && true;\n" + " x += 12.0;\n" + " x -= 12.0;\n" + " x *= (y /= float(z = 10));\n" + " b ||= false;\n" + " b &&= true;\n" + " b ^^= false;\n" + " z |= 0;\n" + " z &= -1;\n" + " z ^= 0;\n" + " z >>= 2;\n" + " z <<= 4;\n" + " z %= 5;\n" + " x = float((vec2(sqrt(1.0)) , 6));\n" + " z = (vec2(sqrt(1.0)) , 6);\n" + "}\n"); +} + +DEF_TEST(SkSLMatrices, r) { + test(r, + "void main() {" + "mat2x4 x = mat2x4(1);" + "mat3x2 y = mat3x2(1, 0, 0, 1, vec2(2, 2));" + "mat3x4 z = x * y;" + "vec3 v1 = mat3(1) * vec3(2);" + "vec3 v2 = vec3(2) * mat3(1);" + "sk_FragColor = vec4(z[0].x, v1 + v2);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " mat3x4 z = mat2x4(1.0) * mat3x2(1.0, 0.0, 0.0, 1.0, vec2(2.0, 2.0));\n" + " vec3 v1 = mat3(1.0) * vec3(2.0);\n" + " vec3 v2 = vec3(2.0) * mat3(1.0);\n" + " sk_FragColor = vec4(z[0].x, v1 + v2);\n" + "}\n"); +} + +DEF_TEST(SkSLInterfaceBlock, r) { + test(r, + "uniform testBlock {" + "float x;" + "float y[2];" + "layout(binding=12) mat3x2 z;" + "bool w;" + "};" + "void main() {" + " sk_FragColor = vec4(x, y[0], y[1], 0);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "uniform testBlock {\n" + " float x;\n" + " float[2] y;\n" + " layout (binding = 12) mat3x2 z;\n" + " bool w;\n" + "};\n" + "void main() {\n" + " sk_FragColor = vec4(x, y[0], y[1], 0.0);\n" + "}\n"); + test(r, + "uniform testBlock {" + "float x;" + "} test;" + "void main() {" + " sk_FragColor = vec4(test.x);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "uniform testBlock {\n" + " float x;\n" + "} test;\n" + "void main() {\n" + " sk_FragColor = vec4(test.x);\n" + "}\n"); + test(r, + "uniform testBlock {" + "float x;" + "} test[2];" + "void main() {" + " sk_FragColor = vec4(test[1].x);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "uniform testBlock {\n" + " float x;\n" + "} test[2];\n" + "void main() {\n" + " sk_FragColor = vec4(test[1].x);\n" + "}\n"); +} + +DEF_TEST(SkSLStructs, r) { + test(r, + "struct A {" + "int x;" + "int y;" + "} a1, a2;" + "A a3;" + "struct B {" + "float x;" + "float y[2];" + "layout(binding=1) A z;" + "};" + "B b1, b2, b3;" + "void main() {" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "struct A {\n" + " int x;\n" + " int y;\n" + "} a1, a2;\n" + "A a3;\n" + "struct B {\n" + " float x;\n" + " float[2] y;\n" + " layout (binding = 1) A z;\n" + "} b1, b2, b3;\n" + "void main() {\n" + "}\n"); +} + +DEF_TEST(SkSLVersion, r) { + test(r, + "in float test; void main() { sk_FragColor = vec4(0.75); }", + *SkSL::ShaderCapsFactory::Version450Core(), + "#version 450 core\n" + "out vec4 sk_FragColor;\n" + "in float test;\n" + "void main() {\n" + " sk_FragColor = vec4(0.75);\n" + "}\n"); + test(r, + "in float test; void main() { sk_FragColor = vec4(0.75); }", + *SkSL::ShaderCapsFactory::Version110(), + "#version 110\n" + "varying float test;\n" + "void main() {\n" + " gl_FragColor = vec4(0.75);\n" + "}\n"); +} + +DEF_TEST(SkSLUsesPrecisionModifiers, r) { + test(r, + "void main() { float x = 0.75; highp float y = 1; x++; y++;" + "sk_FragColor.rg = vec2(x, y); }", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " float x = 0.75;\n" + " float y = 1.0;\n" + " x++;\n" + " y++;\n" + " sk_FragColor.xy = vec2(x, y);\n" + "}\n"); + test(r, + "void main() { float x = 0.75; highp float y = 1; x++; y++;" + "sk_FragColor.rg = vec2(x, y); }", + *SkSL::ShaderCapsFactory::UsesPrecisionModifiers(), + "#version 400\n" + "precision highp float;\n" + "out mediump vec4 sk_FragColor;\n" + "void main() {\n" + " float x = 0.75;\n" + " highp float y = 1.0;\n" + " x++;\n" + " y++;\n" + " sk_FragColor.xy = vec2(x, y);\n" + "}\n"); +} + +DEF_TEST(SkSLMinAbs, r) { + test(r, + "void main() {" + "float x = -5;" + "sk_FragColor.r = min(abs(x), 6);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor.x = min(abs(-5.0), 6.0);\n" + "}\n"); + + test(r, + "void main() {" + "float x = -5.0;" + "sk_FragColor.r = min(abs(x), 6.0);" + "}", + *SkSL::ShaderCapsFactory::CannotUseMinAndAbsTogether(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " float minAbsHackVar0;\n" + " float minAbsHackVar1;\n" + " sk_FragColor.x = ((minAbsHackVar0 = abs(-5.0)) < (minAbsHackVar1 = 6.0) ? " + "minAbsHackVar0 : minAbsHackVar1);\n" + "}\n"); +} + +DEF_TEST(SkSLNegatedAtan, r) { + test(r, + "void main() { vec2 x = vec2(sqrt(2)); sk_FragColor.r = atan(x.x, -x.y); }", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " vec2 x = vec2(sqrt(2.0));\n" + " sk_FragColor.x = atan(x.x, -x.y);\n" + "}\n"); + test(r, + "void main() { vec2 x = vec2(sqrt(2)); sk_FragColor.r = atan(x.x, -x.y); }", + *SkSL::ShaderCapsFactory::MustForceNegatedAtanParamToFloat(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " vec2 x = vec2(sqrt(2.0));\n" + " sk_FragColor.x = atan(x.x, -1.0 * x.y);\n" + "}\n"); +} + +DEF_TEST(SkSLModifiersDeclaration, r) { + test(r, + "layout(blend_support_all_equations) out;" + "void main() { }", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "layout (blend_support_all_equations) out ;\n" + "void main() {\n" + "}\n"); +} + +DEF_TEST(SkSLHex, r) { + test(r, + "void main() {" + "int i1 = 0x0;" + "i1++;" + "int i2 = 0x1234abcd;" + "i2++;" + "int i3 = 0x7fffffff;" + "i3++;" + "int i4 = 0xffffffff;" + "i4++;" + "int i5 = -0xbeef;" + "i5++;" + "uint u1 = 0x0;" + "u1++;" + "uint u2 = 0x1234abcd;" + "u2++;" + "uint u3 = 0x7fffffff;" + "u3++;" + "uint u4 = 0xffffffff;" + "u4++;" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " int i1 = 0;\n" + " i1++;\n" + " int i2 = 305441741;\n" + " i2++;\n" + " int i3 = 2147483647;\n" + " i3++;\n" + " int i4 = -1;\n" + " i4++;\n" + " int i5 = -48879;\n" + " i5++;\n" + " uint u1 = 0u;\n" + " u1++;\n" + " uint u2 = 305441741u;\n" + " u2++;\n" + " uint u3 = 2147483647u;\n" + " u3++;\n" + " uint u4 = 4294967295u;\n" + " u4++;\n" + "}\n"); +} + +DEF_TEST(SkSLVectorConstructors, r) { + test(r, + "vec2 v1 = vec2(1);" + "vec2 v2 = vec2(1, 2);" + "vec2 v3 = vec2(vec2(1));" + "vec3 v4 = vec3(vec2(1), 1.0);" + "ivec2 v5 = ivec2(1);" + "ivec2 v6 = ivec2(vec2(1, 2));" + "vec2 v7 = vec2(ivec2(1, 2));", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "vec2 v1 = vec2(1.0);\n" + "vec2 v2 = vec2(1.0, 2.0);\n" + "vec2 v3 = vec2(1.0);\n" + "vec3 v4 = vec3(vec2(1.0), 1.0);\n" + "ivec2 v5 = ivec2(1);\n" + "ivec2 v6 = ivec2(vec2(1.0, 2.0));\n" + "vec2 v7 = vec2(ivec2(1, 2));\n"); +} + +DEF_TEST(SkSLArrayConstructors, r) { + test(r, + "float test1[] = float[](1, 2, 3, 4);" + "vec2 test2[] = vec2[](vec2(1, 2), vec2(3, 4));" + "mat4 test3[] = mat4[]();", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "float test1[] = float[](1.0, 2.0, 3.0, 4.0);\n" + "vec2 test2[] = vec2[](vec2(1.0, 2.0), vec2(3.0, 4.0));\n" + "mat4 test3[] = mat4[]();\n"); +} + +DEF_TEST(SkSLDerivatives, r) { + test(r, + "void main() { sk_FragColor.r = dFdx(1); }", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor.x = dFdx(1.0);\n" + "}\n"); + test(r, + "void main() { sk_FragColor.r = 1; }", + *SkSL::ShaderCapsFactory::ShaderDerivativeExtensionString(), + "#version 400\n" + "precision highp float;\n" + "out mediump vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor.x = 1.0;\n" + "}\n"); + test(r, + "void main() { sk_FragColor.r = dFdx(1); }", + *SkSL::ShaderCapsFactory::ShaderDerivativeExtensionString(), + "#version 400\n" + "#extension GL_OES_standard_derivatives : require\n" + "precision highp float;\n" + "out mediump vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor.x = dFdx(1.0);\n" + "}\n"); +} + + +DEF_TEST(SkSLIntFolding, r) { + test(r, + "void main() {" + "sk_FragColor.r = 32 + 2;" + "sk_FragColor.r = 32 - 2;" + "sk_FragColor.r = 32 * 2;" + "sk_FragColor.r = 32 / 2;" + "sk_FragColor.r = 12 | 6;" + "sk_FragColor.r = 254 & 7;" + "sk_FragColor.r = 2 ^ 7;" + "sk_FragColor.r = 1 << 4;" + "sk_FragColor.r = 128 >> 2;" + "sk_FragColor.r = -1 == -1 ? 1 : -1;" + "sk_FragColor.r = -1 == -2 ? 2 : -2;" + "sk_FragColor.r = 0 != 1 ? 3 : -3;" + "sk_FragColor.r = 0 != 0 ? 4 : -4;" + "sk_FragColor.r = 6 > 5 ? 5 : -5;" + "sk_FragColor.r = 6 > 6 ? 6 : -6;" + "sk_FragColor.r = -1 < 0 ? 7 : -7;" + "sk_FragColor.r = 1 < 0 ? 8 : -8;" + "sk_FragColor.r = 6 >= 6 ? 9 : -9;" + "sk_FragColor.r = 6 >= 7 ? 10 : -10;" + "sk_FragColor.r = 6 <= 6 ? 11 : -11;" + "sk_FragColor.r = 6 <= 5 ? 12 : -12;" + "sk_FragColor.r = int(sqrt(1)) + 0;" + "sk_FragColor.r = 0 + int(sqrt(2));" + "sk_FragColor.r = int(sqrt(3)) - 0;" + "sk_FragColor.r = int(sqrt(4)) * 0;" + "sk_FragColor.r = int(sqrt(5)) * 1;" + "sk_FragColor.r = 1 * int(sqrt(6));" + "sk_FragColor.r = 0 * int(sqrt(7));" + "sk_FragColor.r = int(sqrt(8)) / 1;" + "sk_FragColor.r = 0 / int(sqrt(9));" + "int x = int(sqrt(2));" + "x += 1;" + "x += 0;" + "x -= 1;" + "x -= 0;" + "x *= 1;" + "x *= 2;" + "x /= 1;" + "x /= 2;" + "sk_FragColor.r = x;" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor.x = 34.0;\n" + " sk_FragColor.x = 30.0;\n" + " sk_FragColor.x = 64.0;\n" + " sk_FragColor.x = 16.0;\n" + " sk_FragColor.x = 14.0;\n" + " sk_FragColor.x = 6.0;\n" + " sk_FragColor.x = 5.0;\n" + " sk_FragColor.x = 16.0;\n" + " sk_FragColor.x = 32.0;\n" + " sk_FragColor.x = 1.0;\n" + " sk_FragColor.x = -2.0;\n" + " sk_FragColor.x = 3.0;\n" + " sk_FragColor.x = -4.0;\n" + " sk_FragColor.x = 5.0;\n" + " sk_FragColor.x = -6.0;\n" + " sk_FragColor.x = 7.0;\n" + " sk_FragColor.x = -8.0;\n" + " sk_FragColor.x = 9.0;\n" + " sk_FragColor.x = -10.0;\n" + " sk_FragColor.x = 11.0;\n" + " sk_FragColor.x = -12.0;\n" + " sk_FragColor.x = float(int(sqrt(1.0)));\n" + " sk_FragColor.x = float(int(sqrt(2.0)));\n" + " sk_FragColor.x = float(int(sqrt(3.0)));\n" + " sk_FragColor.x = 0.0;\n" + " sk_FragColor.x = float(int(sqrt(5.0)));\n" + " sk_FragColor.x = float(int(sqrt(6.0)));\n" + " sk_FragColor.x = 0.0;\n" + " sk_FragColor.x = float(int(sqrt(8.0)));\n" + " sk_FragColor.x = 0.0;\n" + " int x = int(sqrt(2.0));\n" + " x += 1;\n" + " x -= 1;\n" + " x *= 2;\n" + " x /= 2;\n" + " sk_FragColor.x = float(x);\n" + "}\n"); +} + +DEF_TEST(SkSLFloatFolding, r) { + test(r, + "void main() {" + "sk_FragColor.r = 32.0 + 2.0;" + "sk_FragColor.r = 32.0 - 2.0;" + "sk_FragColor.r = 32.0 * 2.0;" + "sk_FragColor.r = 32.0 / 2.0;" + "sk_FragColor.r = (12 > 2.0) ? (10 * 2 / 5 + 18 - 3) : 0;" + "sk_FragColor.r = 0.0 == 0.0 ? 1 : -1;" + "sk_FragColor.r = 0.0 == 1.0 ? 2 : -2;" + "sk_FragColor.r = 0.0 != 1.0 ? 3 : -3;" + "sk_FragColor.r = 0.0 != 0.0 ? 4 : -4;" + "sk_FragColor.r = 6.0 > 5.0 ? 5 : -5;" + "sk_FragColor.r = 6.0 > 6.0 ? 6 : -6;" + "sk_FragColor.r = 6.0 >= 6.0 ? 7 : -7;" + "sk_FragColor.r = 6.0 >= 7.0 ? 8 : -8;" + "sk_FragColor.r = 5.0 < 6.0 ? 9 : -9;" + "sk_FragColor.r = 6.0 < 6.0 ? 10 : -10;" + "sk_FragColor.r = 6.0 <= 6.0 ? 11 : -11;" + "sk_FragColor.r = 6.0 <= 5.0 ? 12 : -12;" + "sk_FragColor.r = sqrt(1) + 0;" + "sk_FragColor.r = 0 + sqrt(2);" + "sk_FragColor.r = sqrt(3) - 0;" + "sk_FragColor.r = sqrt(4) * 0;" + "sk_FragColor.r = sqrt(5) * 1;" + "sk_FragColor.r = 1 * sqrt(6);" + "sk_FragColor.r = 0 * sqrt(7);" + "sk_FragColor.r = sqrt(8) / 1;" + "sk_FragColor.r = 0 / sqrt(9);" + "sk_FragColor.r += 1;" + "sk_FragColor.r += 0;" + "sk_FragColor.r -= 1;" + "sk_FragColor.r -= 0;" + "sk_FragColor.r *= 1;" + "sk_FragColor.r *= 2;" + "sk_FragColor.r /= 1;" + "sk_FragColor.r /= 2;" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor.x = 34.0;\n" + " sk_FragColor.x = 30.0;\n" + " sk_FragColor.x = 64.0;\n" + " sk_FragColor.x = 16.0;\n" + " sk_FragColor.x = 19.0;\n" + " sk_FragColor.x = 1.0;\n" + " sk_FragColor.x = -2.0;\n" + " sk_FragColor.x = 3.0;\n" + " sk_FragColor.x = -4.0;\n" + " sk_FragColor.x = 5.0;\n" + " sk_FragColor.x = -6.0;\n" + " sk_FragColor.x = 7.0;\n" + " sk_FragColor.x = -8.0;\n" + " sk_FragColor.x = 9.0;\n" + " sk_FragColor.x = -10.0;\n" + " sk_FragColor.x = 11.0;\n" + " sk_FragColor.x = -12.0;\n" + " sk_FragColor.x = sqrt(1.0);\n" + " sk_FragColor.x = sqrt(2.0);\n" + " sk_FragColor.x = sqrt(3.0);\n" + " sk_FragColor.x = 0.0;\n" + " sk_FragColor.x = sqrt(5.0);\n" + " sk_FragColor.x = sqrt(6.0);\n" + " sk_FragColor.x = 0.0;\n" + " sk_FragColor.x = sqrt(8.0);\n" + " sk_FragColor.x = 0.0;\n" + " sk_FragColor.x += 1.0;\n" + " sk_FragColor.x -= 1.0;\n" + " sk_FragColor.x *= 2.0;\n" + " sk_FragColor.x /= 2.0;\n" + "}\n"); +} + +DEF_TEST(SkSLBoolFolding, r) { + test(r, + "void main() {" + "sk_FragColor.r = 1 == 1 || 2 == 8 ? 1 : -1;" + "sk_FragColor.r = 1 > 1 || 2 == 8 ? 2 : -2;" + "sk_FragColor.r = 1 == 1 && 2 <= 8 ? 3 : -3;" + "sk_FragColor.r = 1 == 2 && 2 == 8 ? 4 : -4;" + "sk_FragColor.r = 1 == 1 ^^ 1 != 1 ? 5 : -5;" + "sk_FragColor.r = 1 == 1 ^^ 1 == 1 ? 6 : -6;" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor.x = 1.0;\n" + " sk_FragColor.x = -2.0;\n" + " sk_FragColor.x = 3.0;\n" + " sk_FragColor.x = -4.0;\n" + " sk_FragColor.x = 5.0;\n" + " sk_FragColor.x = -6.0;\n" + "}\n"); +} + +DEF_TEST(SkSLVecFolding, r) { + test(r, + "void main() {" + "sk_FragColor.r = vec4(0.5, 1, 1, 1).x;" + "sk_FragColor = vec4(vec2(1), vec2(2, 3)) + vec4(5, 6, 7, 8);" + "sk_FragColor = vec4(8, vec3(10)) - vec4(1);" + "sk_FragColor = vec4(2) * vec4(1, 2, 3, 4);" + "sk_FragColor = vec4(12) / vec4(1, 2, 3, 4);" + "sk_FragColor.r = (vec4(12) / vec4(1, 2, 3, 4)).y;" + "sk_FragColor.x = vec4(1) == vec4(1) ? 1.0 : -1.0;" + "sk_FragColor.x = vec4(1) == vec4(2) ? 2.0 : -2.0;" + "sk_FragColor.x = vec2(1) == vec2(1, 1) ? 3.0 : -3.0;" + "sk_FragColor.x = vec2(1, 1) == vec2(1, 1) ? 4.0 : -4.0;" + "sk_FragColor.x = vec2(1) == vec2(1, 0) ? 5.0 : -5.0;" + "sk_FragColor.x = vec4(1) == vec4(vec2(1), vec2(1)) ? 6.0 : -6.0;" + "sk_FragColor.x = vec4(vec3(1), 1) == vec4(vec2(1), vec2(1)) ? 7.0 : -7.0;" + "sk_FragColor.x = vec4(vec3(1), 1) == vec4(vec2(1), 1, 0) ? 8.0 : -8.0;" + "sk_FragColor.x = vec2(1) != vec2(1, 0) ? 9.0 : -9.0;" + "sk_FragColor.x = vec4(1) != vec4(vec2(1), vec2(1)) ? 10.0 : -10.0;" + "sk_FragColor = vec4(sqrt(1)) * vec4(1);" + "sk_FragColor = vec4(1) * vec4(sqrt(2));" + "sk_FragColor = vec4(0) * vec4(sqrt(3));" + "sk_FragColor = vec4(sqrt(4)) * vec4(0);" + "sk_FragColor = vec4(0) / vec4(sqrt(5));" + "sk_FragColor = vec4(0) + vec4(sqrt(6));" + "sk_FragColor = vec4(sqrt(7)) + vec4(0);" + "sk_FragColor = vec4(sqrt(8)) - vec4(0);" + "sk_FragColor = vec4(0) + sqrt(9);" + "sk_FragColor = vec4(0) * sqrt(10);" + "sk_FragColor = vec4(0) / sqrt(11);" + "sk_FragColor = vec4(1) * sqrt(12);" + "sk_FragColor = 0 + vec4(sqrt(13));" + "sk_FragColor = 0 * vec4(sqrt(14));" + "sk_FragColor = 0 / vec4(sqrt(15));" + "sk_FragColor = 1 * vec4(sqrt(16));" + "sk_FragColor = vec4(sqrt(17)) + 0;" + "sk_FragColor = vec4(sqrt(18)) * 0;" + "sk_FragColor = vec4(sqrt(19)) * 1;" + "sk_FragColor = vec4(sqrt(19.5)) - 0;" + "sk_FragColor = sqrt(20) * vec4(1);" + "sk_FragColor = sqrt(21) + vec4(0);" + "sk_FragColor = sqrt(22) - vec4(0);" + "sk_FragColor = sqrt(23) / vec4(1);" + "sk_FragColor = vec4(sqrt(24)) / 1;" + "sk_FragColor += vec4(1);" + "sk_FragColor += vec4(0);" + "sk_FragColor -= vec4(1);" + "sk_FragColor -= vec4(0);" + "sk_FragColor *= vec4(1);" + "sk_FragColor *= vec4(2);" + "sk_FragColor /= vec4(1);" + "sk_FragColor /= vec4(2);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor.x = 0.5;\n" + " sk_FragColor = vec4(6.0, 7.0, 9.0, 11.0);\n" + " sk_FragColor = vec4(7.0, 9.0, 9.0, 9.0);\n" + " sk_FragColor = vec4(2.0, 4.0, 6.0, 8.0);\n" + " sk_FragColor = vec4(12.0, 6.0, 4.0, 3.0);\n" + " sk_FragColor.x = 6.0;\n" + " sk_FragColor.x = 1.0;\n" + " sk_FragColor.x = -2.0;\n" + " sk_FragColor.x = 3.0;\n" + " sk_FragColor.x = 4.0;\n" + " sk_FragColor.x = -5.0;\n" + " sk_FragColor.x = 6.0;\n" + " sk_FragColor.x = 7.0;\n" + " sk_FragColor.x = -8.0;\n" + " sk_FragColor.x = 9.0;\n" + " sk_FragColor.x = -10.0;\n" + " sk_FragColor = vec4(sqrt(1.0));\n" + " sk_FragColor = vec4(sqrt(2.0));\n" + " sk_FragColor = vec4(0.0);\n" + " sk_FragColor = vec4(0.0);\n" + " sk_FragColor = vec4(0.0);\n" + " sk_FragColor = vec4(sqrt(6.0));\n" + " sk_FragColor = vec4(sqrt(7.0));\n" + " sk_FragColor = vec4(sqrt(8.0));\n" + " sk_FragColor = vec4(sqrt(9.0));\n" + " sk_FragColor = vec4(0.0);\n" + " sk_FragColor = vec4(0.0);\n" + " sk_FragColor = vec4(sqrt(12.0));\n" + " sk_FragColor = vec4(sqrt(13.0));\n" + " sk_FragColor = vec4(0.0);\n" + " sk_FragColor = vec4(0.0);\n" + " sk_FragColor = vec4(sqrt(16.0));\n" + " sk_FragColor = vec4(sqrt(17.0));\n" + " sk_FragColor = vec4(0.0);\n" + " sk_FragColor = vec4(sqrt(19.0));\n" + " sk_FragColor = vec4(sqrt(19.5));\n" + " sk_FragColor = vec4(sqrt(20.0));\n" + " sk_FragColor = vec4(sqrt(21.0));\n" + " sk_FragColor = vec4(sqrt(22.0));\n" + " sk_FragColor = vec4(sqrt(23.0));\n" + " sk_FragColor = vec4(sqrt(24.0));\n" + " sk_FragColor += vec4(1.0);\n" + " sk_FragColor -= vec4(1.0);\n" + " sk_FragColor *= vec4(2.0);\n" + " sk_FragColor /= vec4(2.0);\n" + "}\n"); +} + +DEF_TEST(SkSLMatFolding, r) { + test(r, + "void main() {" + "sk_FragColor.x = mat2(vec2(1.0, 0.0), vec2(0.0, 1.0)) == " + "mat2(vec2(1.0, 0.0), vec2(0.0, 1.0)) ? 1 : -1;" + "sk_FragColor.x = mat2(vec2(1.0, 0.0), vec2(1.0, 1.0)) == " + "mat2(vec2(1.0, 0.0), vec2(0.0, 1.0)) ? 2 : -2;" + "sk_FragColor.x = mat2(1) == mat2(1) ? 3 : -3;" + "sk_FragColor.x = mat2(1) == mat2(0) ? 4 : -4;" + "sk_FragColor.x = mat2(1) == mat2(vec2(1.0, 0.0), vec2(0.0, 1.0)) ? 5 : -5;" + "sk_FragColor.x = mat2(2) == mat2(vec2(1.0, 0.0), vec2(0.0, 1.0)) ? 6 : -6;" + "sk_FragColor.x = mat3x2(2) == mat3x2(vec2(2.0, 0.0), vec2(0.0, 2.0), vec2(0.0)) ? 7 : -7;" + "sk_FragColor.x = mat2(1) != mat2(1) ? 8 : -8;" + "sk_FragColor.x = mat2(1) != mat2(0) ? 9 : -9;" + "sk_FragColor.x = mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 0.0)) == " + "mat3(mat2(1.0)) ? 10 : -10;" + "sk_FragColor.x = mat2(mat3(1.0)) == mat2(1.0) ? 11 : -11;" + "sk_FragColor.x = mat2(vec4(1.0, 0.0, 0.0, 1.0)) == mat2(1.0) ? 12 : -12;" + "sk_FragColor.x = mat2(1.0, 0.0, vec2(0.0, 1.0)) == mat2(1.0) ? 13 : -13;" + "sk_FragColor.x = mat2(vec2(1.0, 0.0), 0.0, 1.0) == mat2(1.0) ? 14 : -14;" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor.x = 1.0;\n" + " sk_FragColor.x = -2.0;\n" + " sk_FragColor.x = 3.0;\n" + " sk_FragColor.x = -4.0;\n" + " sk_FragColor.x = 5.0;\n" + " sk_FragColor.x = -6.0;\n" + " sk_FragColor.x = 7.0;\n" + " sk_FragColor.x = -8.0;\n" + " sk_FragColor.x = 9.0;\n" + " sk_FragColor.x = 10.0;\n" + " sk_FragColor.x = 11.0;\n" + " sk_FragColor.x = 12.0;\n" + " sk_FragColor.x = 13.0;\n" + " sk_FragColor.x = 14.0;\n" + "}\n"); +} + +DEF_TEST(SkSLConstantIf, r) { + test(r, + "void main() {" + "int x;" + "if (true) x = 1;" + "if (2 > 1) x = 2; else x = 3;" + "if (1 > 2) x = 4; else x = 5;" + "if (false) x = 6;" + "sk_FragColor.r = x;" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor.x = 5.0;\n" + "}\n"); +} + +DEF_TEST(SkSLCaps, r) { + test(r, + "void main() {" + "int x = 0;" + "int y = 0;" + "int z = 0;" + "int w = 0;" + "if (sk_Caps.externalTextureSupport) x = 1;" + "if (sk_Caps.fbFetchSupport) y = 1;" + "if (sk_Caps.dropsTileOnZeroDivide && sk_Caps.texelFetchSupport) z = 1;" + "if (sk_Caps.dropsTileOnZeroDivide && sk_Caps.canUseAnyFunctionInShader) w = 1;" + "sk_FragColor = vec4(x, y, z, w);" + "}", + *SkSL::ShaderCapsFactory::VariousCaps(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor = vec4(1.0, 0.0, 1.0, 0.0);\n" + "}\n"); +} + +DEF_TEST(SkSLTexture, r) { + test(r, + "uniform sampler1D one;" + "uniform sampler2D two;" + "void main() {" + "vec4 a = texture(one, 0);" + "vec4 b = texture(two, vec2(0));" + "vec4 c = texture(one, vec2(0));" + "vec4 d = texture(two, vec3(0));" + "sk_FragColor = vec4(a.x, b.x, c.x, d.x);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "uniform sampler1D one;\n" + "uniform sampler2D two;\n" + "void main() {\n" + " vec4 a = texture(one, 0.0);\n" + " vec4 b = texture(two, vec2(0.0));\n" + " vec4 c = textureProj(one, vec2(0.0));\n" + " vec4 d = textureProj(two, vec3(0.0));\n" + " sk_FragColor = vec4(a.x, b.x, c.x, d.x);\n" + "}\n"); + test(r, + "uniform sampler1D one;" + "uniform sampler2D two;" + "void main() {" + "vec4 a = texture(one, 0);" + "vec4 b = texture(two, vec2(0));" + "vec4 c = texture(one, vec2(0));" + "vec4 d = texture(two, vec3(0));" + "sk_FragColor = vec4(a.x, b.x, c.x, d.x);" + "}", + *SkSL::ShaderCapsFactory::Version110(), + "#version 110\n" + "uniform sampler1D one;\n" + "uniform sampler2D two;\n" + "void main() {\n" + " vec4 a = texture1D(one, 0.0);\n" + " vec4 b = texture2D(two, vec2(0.0));\n" + " vec4 c = texture1DProj(one, vec2(0.0));\n" + " vec4 d = texture2DProj(two, vec3(0.0));\n" + " gl_FragColor = vec4(a.x, b.x, c.x, d.x);\n" + "}\n"); +} + +DEF_TEST(SkSLOffset, r) { + test(r, + "struct Test {" + "layout(offset = 0) int x;" + "layout(offset = 4) int y;" + "int z;" + "} test;", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "struct Test {\n" + " layout (offset = 0) int x;\n" + " layout (offset = 4) int y;\n" + " int z;\n" + "} test;\n"); +} + +DEF_TEST(SkSLFragCoord, r) { + SkSL::Program::Settings settings; + settings.fFlipY = true; + sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::FragCoordsOld(); + settings.fCaps = caps.get(); + SkSL::Program::Inputs inputs; + test(r, + "void main() { sk_FragColor.xy = sk_FragCoord.xy; }", + settings, + "#version 110\n" + "#extension GL_ARB_fragment_coord_conventions : require\n" + "layout(origin_upper_left) in vec4 gl_FragCoord;\n" + "void main() {\n" + " gl_FragColor.xy = gl_FragCoord.xy;\n" + "}\n", + &inputs); + REPORTER_ASSERT(r, !inputs.fRTHeight); + + caps = SkSL::ShaderCapsFactory::FragCoordsNew(); + settings.fCaps = caps.get(); + test(r, + "void main() { sk_FragColor.xy = sk_FragCoord.xy; }", + settings, + "#version 400\n" + "layout(origin_upper_left) in vec4 gl_FragCoord;\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor.xy = gl_FragCoord.xy;\n" + "}\n", + &inputs); + REPORTER_ASSERT(r, !inputs.fRTHeight); + + caps = SkSL::ShaderCapsFactory::Default(); + settings.fCaps = caps.get(); + test(r, + "void main() { sk_FragColor.xy = sk_FragCoord.xy; }", + settings, + "#version 400\n" + "uniform float u_skRTHeight;\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " vec2 _sktmpCoord = gl_FragCoord.xy;\n" + " vec4 sk_FragCoord = vec4(_sktmpCoord.x, u_skRTHeight - _sktmpCoord.y, 1.0, 1.0);\n" + " sk_FragColor.xy = sk_FragCoord.xy;\n" + "}\n", + &inputs); + REPORTER_ASSERT(r, inputs.fRTHeight); + + settings.fFlipY = false; + test(r, + "void main() { sk_FragColor.xy = sk_FragCoord.xy; }", + settings, + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor.xy = gl_FragCoord.xy;\n" + "}\n", + &inputs); + REPORTER_ASSERT(r, !inputs.fRTHeight); +} + +DEF_TEST(SkSLVertexID, r) { + test(r, + "out int id; void main() { id = sk_VertexID; }", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out int id;\n" + "void main() {\n" + " id = gl_VertexID;\n" + "}\n", + SkSL::Program::kVertex_Kind); +} + +DEF_TEST(SkSLClipDistance, r) { + test(r, + "void main() { sk_ClipDistance[0] = 0; }", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "void main() {\n" + " gl_ClipDistance[0] = 0.0;\n" + "}\n", + SkSL::Program::kVertex_Kind); + test(r, + "void main() { sk_FragColor = vec4(sk_ClipDistance[0]); }", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor = vec4(gl_ClipDistance[0]);\n" + "}\n"); +} + +DEF_TEST(SkSLArrayTypes, r) { + test(r, + "void main() { vec2 x[2] = vec2[2](vec2(1), vec2(2));" + "vec2[2] y = vec2[2](vec2(3), vec2(4));" + "sk_FragColor = vec4(x[0], y[1]); }", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor = vec4(vec2[2](vec2(1.0), vec2(2.0))[0], " + "vec2[2](vec2(3.0), vec2(4.0))[1]);\n" + "}\n"); +} + +DEF_TEST(SkSLGeometry, r) { + test(r, + "layout(points) in;" + "layout(invocations = 2) in;" + "layout(line_strip, max_vertices = 2) out;" + "void main() {" + "gl_Position = sk_in[0].gl_Position + vec4(-0.5, 0, 0, sk_InvocationID);" + "EmitVertex();" + "gl_Position = sk_in[0].gl_Position + vec4(0.5, 0, 0, sk_InvocationID);" + "EmitVertex();" + "EndPrimitive();" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "layout (points) in ;\n" + "layout (invocations = 2) in ;\n" + "layout (line_strip, max_vertices = 2) out ;\n" + "void main() {\n" + " gl_Position = gl_in[0].gl_Position + vec4(-0.5, 0.0, 0.0, float(gl_InvocationID));\n" + " EmitVertex();\n" + " gl_Position = gl_in[0].gl_Position + vec4(0.5, 0.0, 0.0, float(gl_InvocationID));\n" + " EmitVertex();\n" + " EndPrimitive();\n" + "}\n", + SkSL::Program::kGeometry_Kind); +} + +DEF_TEST(SkSLSwitch, r) { + // basic "does a switch even work" test + test(r, + "void main() {" + " float x;" + " switch (int(sqrt(1))) {" + " case 0:" + " x = 0.0;" + " break;" + " case 1:" + " x = 1.0;" + " break;" + " default:" + " x = 2.0;" + " }" + " sk_FragColor = vec4(x);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " float x;\n" + " switch (int(sqrt(1.0))) {\n" + " case 0:\n" + " x = 0.0;\n" + " break;\n" + " case 1:\n" + " x = 1.0;\n" + " break;\n" + " default:\n" + " x = 2.0;\n" + " }\n" + " sk_FragColor = vec4(x);\n" + "}\n"); + // dead code inside of switch + test(r, + "void main() {" + " float x;" + " switch (int(sqrt(2))) {" + " case 0:" + " x = 0.0;" + " case 1:" + " x = 1.0;" + " default:" + " x = 2.0;" + " }" + " sk_FragColor = vec4(x);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " switch (int(sqrt(2.0))) {\n" + " case 0:\n" + " ;\n" + " case 1:\n" + " ;\n" + " default:\n" + " ;\n" + " }\n" + " sk_FragColor = vec4(2.0);\n" + "}\n"); + // non-static test w/ fallthrough + test(r, + "void main() {" + " float x = 0.0;" + " switch (int(sqrt(3))) {" + " case 0:" + " x = 0.0;" + " case 1:" + " x = 1.0;" + " }" + " sk_FragColor = vec4(x);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " float x = 0.0;\n" + " switch (int(sqrt(3.0))) {\n" + " case 0:\n" + " x = 0.0;\n" + " case 1:\n" + " x = 1.0;\n" + " }\n" + " sk_FragColor = vec4(x);\n" + "}\n"); + // static test w/ fallthrough + test(r, + "void main() {" + " float x = 0.0;" + " switch (0) {" + " case 0:" + " x = 0.0;" + " case 1:" + " x = 1.0;" + " }" + " sk_FragColor = vec4(x);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor = vec4(1.0);\n" + "}\n"); + // static test w/ fallthrough, different entry point + test(r, + "void main() {" + " float x = 0.0;" + " switch (1) {" + " case 0:" + " x = 0.0;" + " case 1:" + " x = 1.0;" + " }" + " sk_FragColor = vec4(x);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor = vec4(1.0);\n" + "}\n"); + // static test w/ break + test(r, + "void main() {" + " float x = 0.0;" + " switch (0) {" + " case 0:" + " x = 0.0;" + " break;" + " case 1:" + " x = 1.0;" + " }" + " sk_FragColor = vec4(x);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor = vec4(0.0);\n" + "}\n"); + // static test w/ static conditional break + test(r, + "void main() {" + " float x = 0.0;" + " switch (0) {" + " case 0:" + " x = 0.0;" + " if (x < 1) break;" + " case 1:" + " x = 1.0;" + " }" + " sk_FragColor = vec4(x);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor = vec4(0.0);\n" + "}\n"); + // static test w/ non-static conditional break + test(r, + "void main() {" + " float x = 0.0;" + " switch (0) {" + " case 0:" + " x = 0.0;" + " if (x < sqrt(1)) break;" + " case 1:" + " x = 1.0;" + " }" + " sk_FragColor = vec4(x);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " float x = 0.0;\n" + " switch (0) {\n" + " case 0:\n" + " x = 0.0;\n" + " if (0.0 < sqrt(1.0)) break;\n" + " case 1:\n" + " x = 1.0;\n" + " }\n" + " sk_FragColor = vec4(x);\n" + "}\n"); +} + +DEF_TEST(SkSLRectangleTexture, r) { + test(r, + "uniform sampler2D test;" + "void main() {" + " sk_FragColor = texture(test, vec2(0.5));" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "uniform sampler2D test;\n" + "void main() {\n" + " sk_FragColor = texture(test, vec2(0.5));\n" + "}\n"); + test(r, + "uniform sampler2DRect test;" + "void main() {" + " sk_FragColor = texture(test, vec2(0.5));" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "uniform sampler2DRect test;\n" + "void main() {\n" + " sk_FragColor = texture(test, textureSize(test) * vec2(0.5));\n" + "}\n"); + test(r, + "uniform sampler2DRect test;" + "void main() {" + " sk_FragColor = texture(test, vec3(0.5));" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "uniform sampler2DRect test;\n" + "void main() {\n" + " sk_FragColor = texture(test, vec3(textureSize(test), 1.0) * vec3(0.5));\n" + "}\n"); +} + +DEF_TEST(SkSLUnusedVars, r) { + test(r, + "void main() {" + "float a = 1, b = 2, c = 3;" + "float d = c;" + "float e = d;" + "b++;" + "d++;" + "sk_FragColor = vec4(b, b, d, d);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " float b = 2.0;\n" + " float d = 3.0;\n" + " b++;\n" + " d++;\n" + " sk_FragColor = vec4(b, b, d, d);\n" + "}\n"); +} + +DEF_TEST(SkSLMultipleAssignments, r) { + test(r, + "void main() {" + "float x;" + "float y;" + "int z;" + "x = y = z = 1;" + "sk_FragColor = vec4(z);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor = vec4(1.0);\n" + "}\n"); +} + +DEF_TEST(SkSLComplexDelete, r) { + test(r, + "uniform mat4 colorXform;" + "uniform sampler2D sampler;" + "void main() {" + "vec4 tmpColor;" + "sk_FragColor = vec4(1.0) * (tmpColor = texture(sampler, vec2(1)) , " + "colorXform != mat4(1.0) ? vec4(clamp((mat4(colorXform) * vec4(tmpColor.xyz, 1.0)).xyz, " + "0.0, tmpColor.w), tmpColor.w) : tmpColor);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "uniform mat4 colorXform;\n" + "uniform sampler2D sampler;\n" + "void main() {\n" + " vec4 tmpColor;\n" + " sk_FragColor = (tmpColor = texture(sampler, vec2(1.0)) , colorXform != mat4(1.0) ? " + "vec4(clamp((colorXform * vec4(tmpColor.xyz, 1.0)).xyz, 0.0, tmpColor.w), tmpColor.w) : " + "tmpColor);\n" + "}\n"); +} + +DEF_TEST(SkSLDependentInitializers, r) { + test(r, + "void main() {" + "float x = 0.5, y = x * 2;" + "sk_FragColor = vec4(y);" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " sk_FragColor = vec4(1.0);\n" + "}\n"); +} + +DEF_TEST(SkSLDeadLoopVar, r) { + test(r, + "void main() {" + "for (int x = 0; x < 4; ) {" + "break;" + "}" + "}", + *SkSL::ShaderCapsFactory::Default(), + "#version 400\n" + "out vec4 sk_FragColor;\n" + "void main() {\n" + " for (; true; ) {\n" + " break;\n" + " }\n" + "}\n" + ); +} + +DEF_TEST(SkSLInvocations, r) { + test(r, + "layout(points) in;" + "layout(invocations = 2) in;" + "layout(line_strip, max_vertices = 2) out;" + "void test() {" + "gl_Position = sk_in[0].gl_Position + vec4(0.5, 0, 0, sk_InvocationID);" + "EmitVertex();" + "}" + "void main() {" + "gl_Position = sk_in[0].gl_Position + vec4(-0.5, 0, 0, sk_InvocationID);" + "EmitVertex();" + "}", + *SkSL::ShaderCapsFactory::MustImplementGSInvocationsWithLoop(), + "#version 400\n" + "int sk_InvocationID;\n" + "layout (points) in ;\n" + "layout (line_strip, max_vertices = 4) out ;\n" + "void test() {\n" + " gl_Position = gl_in[0].gl_Position + vec4(0.5, 0.0, 0.0, float(sk_InvocationID));\n" + " EmitVertex();\n" + "}\n" + "void _invoke() {\n" + " gl_Position = gl_in[0].gl_Position + vec4(-0.5, 0.0, 0.0, float(sk_InvocationID));\n" + " EmitVertex();\n" + "}\n" + "void main() {\n" + " for (sk_InvocationID = 0;sk_InvocationID < 2; sk_InvocationID++) {\n" + " _invoke();\n" + " EndPrimitive();\n" + " }\n" + "}\n", + SkSL::Program::kGeometry_Kind); +} + +#endif
diff --git a/src/third_party/skia/tests/SkSLMemoryLayoutTest.cpp b/src/third_party/skia/tests/SkSLMemoryLayoutTest.cpp new file mode 100644 index 0000000..ff3d010 --- /dev/null +++ b/src/third_party/skia/tests/SkSLMemoryLayoutTest.cpp
@@ -0,0 +1,176 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkSLContext.h" +#include "SkSLMemoryLayout.h" + +#include "Test.h" + +#if SK_SUPPORT_GPU + +DEF_TEST(SkSLMemoryLayout140Test, r) { + SkSL::Context context; + SkSL::MemoryLayout layout(SkSL::MemoryLayout::k140_Standard); + + // basic types + REPORTER_ASSERT(r, 4 == layout.size(*context.fFloat_Type)); + REPORTER_ASSERT(r, 8 == layout.size(*context.fVec2_Type)); + REPORTER_ASSERT(r, 12 == layout.size(*context.fVec3_Type)); + REPORTER_ASSERT(r, 16 == layout.size(*context.fVec4_Type)); + REPORTER_ASSERT(r, 4 == layout.size(*context.fInt_Type)); + REPORTER_ASSERT(r, 8 == layout.size(*context.fIVec2_Type)); + REPORTER_ASSERT(r, 12 == layout.size(*context.fIVec3_Type)); + REPORTER_ASSERT(r, 16 == layout.size(*context.fIVec4_Type)); + REPORTER_ASSERT(r, 1 == layout.size(*context.fBool_Type)); + REPORTER_ASSERT(r, 2 == layout.size(*context.fBVec2_Type)); + REPORTER_ASSERT(r, 3 == layout.size(*context.fBVec3_Type)); + REPORTER_ASSERT(r, 4 == layout.size(*context.fBVec4_Type)); + REPORTER_ASSERT(r, 32 == layout.size(*context.fMat2x2_Type)); + REPORTER_ASSERT(r, 32 == layout.size(*context.fMat2x4_Type)); + REPORTER_ASSERT(r, 48 == layout.size(*context.fMat3x3_Type)); + REPORTER_ASSERT(r, 64 == layout.size(*context.fMat4x2_Type)); + REPORTER_ASSERT(r, 64 == layout.size(*context.fMat4x4_Type)); + REPORTER_ASSERT(r, 4 == layout.alignment(*context.fFloat_Type)); + REPORTER_ASSERT(r, 8 == layout.alignment(*context.fVec2_Type)); + REPORTER_ASSERT(r, 16 == layout.alignment(*context.fVec3_Type)); + REPORTER_ASSERT(r, 16 == layout.alignment(*context.fVec4_Type)); + REPORTER_ASSERT(r, 4 == layout.alignment(*context.fInt_Type)); + REPORTER_ASSERT(r, 8 == layout.alignment(*context.fIVec2_Type)); + REPORTER_ASSERT(r, 16 == layout.alignment(*context.fIVec3_Type)); + REPORTER_ASSERT(r, 16 == layout.alignment(*context.fIVec4_Type)); + REPORTER_ASSERT(r, 1 == layout.alignment(*context.fBool_Type)); + REPORTER_ASSERT(r, 2 == layout.alignment(*context.fBVec2_Type)); + REPORTER_ASSERT(r, 4 == layout.alignment(*context.fBVec3_Type)); + REPORTER_ASSERT(r, 4 == layout.alignment(*context.fBVec4_Type)); + REPORTER_ASSERT(r, 16 == layout.alignment(*context.fMat2x2_Type)); + REPORTER_ASSERT(r, 16 == layout.alignment(*context.fMat2x4_Type)); + REPORTER_ASSERT(r, 16 == layout.alignment(*context.fMat3x3_Type)); + REPORTER_ASSERT(r, 16 == layout.alignment(*context.fMat4x2_Type)); + REPORTER_ASSERT(r, 16 == layout.alignment(*context.fMat4x4_Type)); + + // struct 1 + std::vector<SkSL::Type::Field> fields1; + fields1.emplace_back(SkSL::Modifiers(), SkString("a"), context.fVec3_Type.get()); + SkSL::Type s1(SkSL::Position(), SkString("s1"), fields1); + REPORTER_ASSERT(r, 16 == layout.size(s1)); + REPORTER_ASSERT(r, 16 == layout.alignment(s1)); + + fields1.emplace_back(SkSL::Modifiers(), SkString("b"), context.fFloat_Type.get()); + SkSL::Type s2(SkSL::Position(), SkString("s2"), fields1); + REPORTER_ASSERT(r, 16 == layout.size(s2)); + REPORTER_ASSERT(r, 16 == layout.alignment(s2)); + + fields1.emplace_back(SkSL::Modifiers(), SkString("c"), context.fBool_Type.get()); + SkSL::Type s3(SkSL::Position(), SkString("s3"), fields1); + REPORTER_ASSERT(r, 32 == layout.size(s3)); + REPORTER_ASSERT(r, 16 == layout.alignment(s3)); + + // struct 2 + std::vector<SkSL::Type::Field> fields2; + fields2.emplace_back(SkSL::Modifiers(), SkString("a"), context.fInt_Type.get()); + SkSL::Type s4(SkSL::Position(), SkString("s4"), fields2); + REPORTER_ASSERT(r, 16 == layout.size(s4)); + REPORTER_ASSERT(r, 16 == layout.alignment(s4)); + + fields2.emplace_back(SkSL::Modifiers(), SkString("b"), context.fVec3_Type.get()); + SkSL::Type s5(SkSL::Position(), SkString("s5"), fields2); + REPORTER_ASSERT(r, 32 == layout.size(s5)); + REPORTER_ASSERT(r, 16 == layout.alignment(s5)); + + // arrays + SkSL::Type array1(SkString("float[4]"), SkSL::Type::kArray_Kind, *context.fFloat_Type, 4); + REPORTER_ASSERT(r, 64 == layout.size(array1)); + REPORTER_ASSERT(r, 16 == layout.alignment(array1)); + REPORTER_ASSERT(r, 16 == layout.stride(array1)); + + SkSL::Type array2(SkString("vec4[4]"), SkSL::Type::kArray_Kind, *context.fVec4_Type, 4); + REPORTER_ASSERT(r, 64 == layout.size(array2)); + REPORTER_ASSERT(r, 16 == layout.alignment(array2)); + REPORTER_ASSERT(r, 16 == layout.stride(array2)); +} + +DEF_TEST(SkSLMemoryLayout430Test, r) { + SkSL::Context context; + SkSL::MemoryLayout layout(SkSL::MemoryLayout::k430_Standard); + + // basic types + REPORTER_ASSERT(r, 4 == layout.size(*context.fFloat_Type)); + REPORTER_ASSERT(r, 8 == layout.size(*context.fVec2_Type)); + REPORTER_ASSERT(r, 12 == layout.size(*context.fVec3_Type)); + REPORTER_ASSERT(r, 16 == layout.size(*context.fVec4_Type)); + REPORTER_ASSERT(r, 4 == layout.size(*context.fInt_Type)); + REPORTER_ASSERT(r, 8 == layout.size(*context.fIVec2_Type)); + REPORTER_ASSERT(r, 12 == layout.size(*context.fIVec3_Type)); + REPORTER_ASSERT(r, 16 == layout.size(*context.fIVec4_Type)); + REPORTER_ASSERT(r, 1 == layout.size(*context.fBool_Type)); + REPORTER_ASSERT(r, 2 == layout.size(*context.fBVec2_Type)); + REPORTER_ASSERT(r, 3 == layout.size(*context.fBVec3_Type)); + REPORTER_ASSERT(r, 4 == layout.size(*context.fBVec4_Type)); + REPORTER_ASSERT(r, 16 == layout.size(*context.fMat2x2_Type)); + REPORTER_ASSERT(r, 32 == layout.size(*context.fMat2x4_Type)); + REPORTER_ASSERT(r, 48 == layout.size(*context.fMat3x3_Type)); + REPORTER_ASSERT(r, 32 == layout.size(*context.fMat4x2_Type)); + REPORTER_ASSERT(r, 64 == layout.size(*context.fMat4x4_Type)); + REPORTER_ASSERT(r, 4 == layout.alignment(*context.fFloat_Type)); + REPORTER_ASSERT(r, 8 == layout.alignment(*context.fVec2_Type)); + REPORTER_ASSERT(r, 16 == layout.alignment(*context.fVec3_Type)); + REPORTER_ASSERT(r, 16 == layout.alignment(*context.fVec4_Type)); + REPORTER_ASSERT(r, 4 == layout.alignment(*context.fInt_Type)); + REPORTER_ASSERT(r, 8 == layout.alignment(*context.fIVec2_Type)); + REPORTER_ASSERT(r, 16 == layout.alignment(*context.fIVec3_Type)); + REPORTER_ASSERT(r, 16 == layout.alignment(*context.fIVec4_Type)); + REPORTER_ASSERT(r, 1 == layout.alignment(*context.fBool_Type)); + REPORTER_ASSERT(r, 2 == layout.alignment(*context.fBVec2_Type)); + REPORTER_ASSERT(r, 4 == layout.alignment(*context.fBVec3_Type)); + REPORTER_ASSERT(r, 4 == layout.alignment(*context.fBVec4_Type)); + REPORTER_ASSERT(r, 8 == layout.alignment(*context.fMat2x2_Type)); + REPORTER_ASSERT(r, 16 == layout.alignment(*context.fMat2x4_Type)); + REPORTER_ASSERT(r, 16 == layout.alignment(*context.fMat3x3_Type)); + REPORTER_ASSERT(r, 8 == layout.alignment(*context.fMat4x2_Type)); + REPORTER_ASSERT(r, 16 == layout.alignment(*context.fMat4x4_Type)); + + // struct 1 + std::vector<SkSL::Type::Field> fields1; + fields1.emplace_back(SkSL::Modifiers(), SkString("a"), context.fVec3_Type.get()); + SkSL::Type s1(SkSL::Position(), SkString("s1"), fields1); + REPORTER_ASSERT(r, 16 == layout.size(s1)); + REPORTER_ASSERT(r, 16 == layout.alignment(s1)); + + fields1.emplace_back(SkSL::Modifiers(), SkString("b"), context.fFloat_Type.get()); + SkSL::Type s2(SkSL::Position(), SkString("s2"), fields1); + REPORTER_ASSERT(r, 16 == layout.size(s2)); + REPORTER_ASSERT(r, 16 == layout.alignment(s2)); + + fields1.emplace_back(SkSL::Modifiers(), SkString("c"), context.fBool_Type.get()); + SkSL::Type s3(SkSL::Position(), SkString("s3"), fields1); + REPORTER_ASSERT(r, 32 == layout.size(s3)); + REPORTER_ASSERT(r, 16 == layout.alignment(s3)); + + // struct 2 + std::vector<SkSL::Type::Field> fields2; + fields2.emplace_back(SkSL::Modifiers(), SkString("a"), context.fInt_Type.get()); + SkSL::Type s4(SkSL::Position(), SkString("s4"), fields2); + REPORTER_ASSERT(r, 4 == layout.size(s4)); + REPORTER_ASSERT(r, 4 == layout.alignment(s4)); + + fields2.emplace_back(SkSL::Modifiers(), SkString("b"), context.fVec3_Type.get()); + SkSL::Type s5(SkSL::Position(), SkString("s5"), fields2); + REPORTER_ASSERT(r, 32 == layout.size(s5)); + REPORTER_ASSERT(r, 16 == layout.alignment(s5)); + + // arrays + SkSL::Type array1(SkString("float[4]"), SkSL::Type::kArray_Kind, *context.fFloat_Type, 4); + REPORTER_ASSERT(r, 16 == layout.size(array1)); + REPORTER_ASSERT(r, 4 == layout.alignment(array1)); + REPORTER_ASSERT(r, 4 == layout.stride(array1)); + + SkSL::Type array2(SkString("vec4[4]"), SkSL::Type::kArray_Kind, *context.fVec4_Type, 4); + REPORTER_ASSERT(r, 64 == layout.size(array2)); + REPORTER_ASSERT(r, 16 == layout.alignment(array2)); + REPORTER_ASSERT(r, 16 == layout.stride(array2)); +} +#endif
diff --git a/src/third_party/skia/tests/SkSLSPIRVTest.cpp b/src/third_party/skia/tests/SkSLSPIRVTest.cpp new file mode 100644 index 0000000..4d5457f --- /dev/null +++ b/src/third_party/skia/tests/SkSLSPIRVTest.cpp
@@ -0,0 +1,42 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkSLCompiler.h" + +#include "Test.h" + +#if SK_SUPPORT_GPU + +static void test_failure(skiatest::Reporter* r, const char* src, const char* error) { + SkSL::Compiler compiler; + SkSL::Program::Settings settings; + sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::Default(); + settings.fCaps = caps.get(); + std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kFragment_Kind, + SkString(src), settings); + if (program) { + SkSL::String ignored; + compiler.toSPIRV(*program, &ignored); + } + SkSL::String skError(error); + if (compiler.errorText() != skError) { + SkDebugf("SKSL ERROR:\n source: %s\n expected: %s received: %s", src, error, + compiler.errorText().c_str()); + } + REPORTER_ASSERT(r, compiler.errorText() == skError); +} + +DEF_TEST(SkSLBadOffset, r) { + test_failure(r, + "struct Bad { layout (offset = 5) int x; } bad; void main() { bad.x = 5; }", + "error: 1: offset of field 'x' must be a multiple of 4\n1 error\n"); + test_failure(r, + "struct Bad { int x; layout (offset = 0) int y; } bad; void main() { bad.x = 5; }", + "error: 1: offset of field 'y' must be at least 4\n1 error\n"); +} + +#endif
diff --git a/src/third_party/skia/tests/SkSharedMutexTest.cpp b/src/third_party/skia/tests/SkSharedMutexTest.cpp new file mode 100644 index 0000000..8458891 --- /dev/null +++ b/src/third_party/skia/tests/SkSharedMutexTest.cpp
@@ -0,0 +1,54 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkSharedMutex.h" +#include "SkTaskGroup.h" + +#include "Test.h" + +DEF_TEST(SkSharedMutexBasic, r) { + SkSharedMutex sm; + sm.acquire(); + sm.assertHeld(); + sm.release(); + sm.acquireShared(); + sm.assertHeldShared(); + sm.releaseShared(); +} + +DEF_TEST(SkSharedMutexMultiThreaded, r) { + SkSharedMutex sm; + static const int kSharedSize = 10; + int shared[kSharedSize]; + int value = 0; + for (int i = 0; i < kSharedSize; ++i) { + shared[i] = 0; + } + SkTaskGroup().batch(8, [&](int threadIndex) { + if (threadIndex % 4 != 0) { + for (int c = 0; c < 100000; ++c) { + sm.acquireShared(); + sm.assertHeldShared(); + int v = shared[0]; + for (int i = 1; i < kSharedSize; ++i) { + REPORTER_ASSERT(r, v == shared[i]); + } + sm.releaseShared(); + } + } else { + for (int c = 0; c < 100000; ++c) { + sm.acquire(); + sm.assertHeld(); + value += 1; + for (int i = 0; i < kSharedSize; ++i) { + shared[i] = value; + } + sm.release(); + } + } + }); +}
diff --git a/src/third_party/skia/tests/SkpSkGrTest.cpp b/src/third_party/skia/tests/SkpSkGrTest.cpp index c882654..8280e14 100644 --- a/src/third_party/skia/tests/SkpSkGrTest.cpp +++ b/src/third_party/skia/tests/SkpSkGrTest.cpp
@@ -1,24 +1,22 @@ -#if !SK_SUPPORT_GPU -#error "GPU support required" -#endif +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ #include "GrContext.h" #include "GrContextFactory.h" -#include "GrRenderTarget.h" #include "SkGpuDevice.h" #include "gl/GrGLDefines.h" #include "SkBitmap.h" #include "SkCanvas.h" #include "SkColor.h" -#include "SkDevice.h" #include "SkGraphics.h" -#include "SkImageDecoder.h" #include "SkImageEncoder.h" #include "SkOSFile.h" #include "SkPicture.h" -#include "SkRTConf.h" -#include "SkRunnable.h" #include "SkStream.h" #include "SkString.h" #include "SkTArray.h" @@ -27,6 +25,10 @@ #include "SkTime.h" #include "Test.h" +#if !SK_SUPPORT_GPU +#error "GPU support required" +#endif + #ifdef SK_BUILD_FOR_WIN #define PATH_SLASH "\\" #define IN_DIR "D:\\9-30-13\\" @@ -101,7 +103,7 @@ TestStep fTestStep; int fDirNo; int fPixelError; - int fTime; + SkMSec fTime; bool fScaleOversized; }; @@ -135,7 +137,7 @@ skiatest::Reporter* fReporter; }; -class SkpSkGrThreadedRunnable : public SkRunnable { +class SkpSkGrThreadedRunnable { public: SkpSkGrThreadedRunnable(void (*testFun)(SkpSkGrThreadState*), int dirNo, const char* str, SkpSkGrThreadedTestRunner* runner) { @@ -146,7 +148,7 @@ fTestFun = testFun; } - virtual void run() SK_OVERRIDE { + void operator()() { SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024); (*fTestFun)(&fState); } @@ -157,15 +159,14 @@ SkpSkGrThreadedTestRunner::~SkpSkGrThreadedTestRunner() { for (int index = 0; index < fRunnables.count(); index++) { - SkDELETE(fRunnables[index]); + delete fRunnables[index]; } } void SkpSkGrThreadedTestRunner::render() { - SkTaskGroup tg; - for (int index = 0; index < fRunnables.count(); ++ index) { - tg.add(fRunnables[index]); - } + SkTaskGroup().batch(fRunnables.count(), [&](int i) { + fRunnables[i](); + }); } //////////////////////////////////////////////// @@ -249,11 +250,11 @@ return pngName; } -typedef GrContextFactory::GLContextType GLContextType; +typedef GrContextFactory::ContextType ContextType; #ifdef SK_BUILD_FOR_WIN -static const GLContextType kAngle = GrContextFactory::kANGLE_GLContextType; +static const ContextType kAngle = GrContextFactory::kANGLE_ContextType; #else -static const GLContextType kNative = GrContextFactory::kNative_GLContextType; +static const ContextType kNative = GrContextFactory::kNativeGL_ContextType; #endif static int similarBits(const SkBitmap& gr, const SkBitmap& sk) { @@ -339,7 +340,7 @@ SkRect rect = {0, 0, SkIntToScalar(SkTMin(maxDimension, pWidth)), SkIntToScalar(SkTMin(maxDimension, pHeight))}; canvas->clipRect(rect); - SkMSec start = SkTime::GetMSecs(); + skiatest::Timer timer; for (int x = 0; x < slices; ++x) { for (int y = 0; y < slices; ++y) { pic->draw(canvas); @@ -347,9 +348,9 @@ } canvas->translate(SkIntToScalar(xInterval), SkIntToScalar(-yInterval * slices)); } - SkMSec end = SkTime::GetMSecs(); + SkMSec elapsed = timer.elapsedMsInt(); canvas->restore(); - return end - start; + return elapsed; } static void drawPict(SkPicture* pic, SkCanvas* canvas, int scale) { @@ -366,15 +367,15 @@ static void writePict(const SkBitmap& bitmap, const char* outDir, const char* pngName) { SkString outFile = make_filepath(0, outDir, pngName); - if (!SkImageEncoder::EncodeFile(outFile.c_str(), bitmap, - SkImageEncoder::kPNG_Type, 100)) { + if (!sk_tool_utils::EncodeImageToFile(outFile.c_str(), bitmap, + SkEncodedImageFormat::kPNG, 100)) { SkDebugf("unable to encode gr %s (width=%d height=%d)br \n", pngName, bitmap.width(), bitmap.height()); } } void TestResult::testOne() { - SkPicture* pic = NULL; + sk_sp<SkPicture> pic; { SkString d; d.printf(" {%d, \"%s\"},", fDirNo, fFilename); @@ -395,7 +396,7 @@ wStream.write(&bytes[0], length); wStream.flush(); } - pic = SkPicture::CreateFromStream(&stream, &SkImageDecoder::DecodeMemory); + pic = SkPicture::MakeFromStream(&stream); if (!pic) { SkDebugf("unable to decode %s\n", fFilename); goto finish; @@ -409,7 +410,7 @@ #else GrContext* context = contextFactory.get(kNative); #endif - if (NULL == context) { + if (nullptr == context) { SkDebugf("unable to allocate context for %s\n", fFilename); goto finish; } @@ -432,25 +433,25 @@ if (scale >= 256) { SkDebugf("unable to allocate bitmap for %s (w=%d h=%d) (sw=%d sh=%d)\n", fFilename, pWidth, pHeight, dim.fX, dim.fY); - goto finish; + return; } SkCanvas skCanvas(bitmap); drawPict(pic, &skCanvas, fScaleOversized ? scale : 1); GrTextureDesc desc; - desc.fConfig = kSkia8888_GrPixelConfig; + desc.fConfig = kRGBA_8888_GrPixelConfig; desc.fFlags = kRenderTarget_GrTextureFlagBit; desc.fWidth = dim.fX; desc.fHeight = dim.fY; desc.fSampleCnt = 0; - SkAutoTUnref<GrTexture> texture(context->createUncachedTexture(desc, NULL, 0)); + sk_sp<GrTexture> texture(context->createUncachedTexture(desc, nullptr, 0)); if (!texture) { SkDebugf("unable to allocate texture for %s (w=%d h=%d)\n", fFilename, dim.fX, dim.fY); - goto finish; + return; } SkGpuDevice grDevice(context, texture.get()); SkCanvas grCanvas(&grDevice); - drawPict(pic, &grCanvas, fScaleOversized ? scale : 1); + drawPict(pic.get(), &grCanvas, fScaleOversized ? scale : 1); SkBitmap grBitmap; grBitmap.allocPixels(grCanvas.imageInfo()); @@ -458,8 +459,8 @@ if (fTestStep == kCompareBits) { fPixelError = similarBits(grBitmap, bitmap); - int skTime = timePict(pic, &skCanvas); - int grTime = timePict(pic, &grCanvas); + SkMSec skTime = timePict(pic, &skCanvas); + SkMSec grTime = timePict(pic, &grCanvas); fTime = skTime - grTime; } else if (fTestStep == kEncodeFiles) { SkString pngStr = make_png_name(fFilename); @@ -468,8 +469,6 @@ writePict(bitmap, outSkDir, pngName); } } -finish: - SkDELETE(pic); } static SkString makeStatusString(int dirNo) { @@ -566,10 +565,6 @@ }; static bool initTest() { -#if !defined SK_BUILD_FOR_WIN && !defined SK_BUILD_FOR_MAC - SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); - SK_CONF_SET("images.png.suppressDecoderWarnings", true); -#endif return make_out_dirs(); } @@ -697,8 +692,8 @@ goto skipOver; } } - *testRunner.fRunnables.append() = SkNEW_ARGS(SkpSkGrThreadedRunnable, - (&testSkGrMain, dirIndex, filename.c_str(), &testRunner)); + *testRunner.fRunnables.append() = new SkpSkGrThreadedRunnable( + &testSkGrMain, dirIndex, filename.c_str(), &testRunner); skipOver: ; }
diff --git a/src/third_party/skia/tests/SmallAllocatorTest.cpp b/src/third_party/skia/tests/SmallAllocatorTest.cpp deleted file mode 100644 index 30c8ffa..0000000 --- a/src/third_party/skia/tests/SmallAllocatorTest.cpp +++ /dev/null
@@ -1,83 +0,0 @@ -/* - * Copyright 2014 Google, Inc - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkSmallAllocator.h" -#include "SkTypes.h" -#include "Test.h" - -class CountingClass { -public: - CountingClass() { - kCount++; - } - - ~CountingClass() { - kCount--; - } - - static int GetCount() { return kCount; } - -private: - static int kCount; -}; - -int CountingClass::kCount; - -template<uint32_t kMaxObjects, size_t kBytes> void test_allocator(skiatest::Reporter* reporter) { - { - SkSmallAllocator<kMaxObjects, kBytes> alloc; - for (uint32_t i = 0; i < kMaxObjects; ++i) { - CountingClass* c = alloc.template createT<CountingClass>(); - REPORTER_ASSERT(reporter, c != NULL); - REPORTER_ASSERT(reporter, CountingClass::GetCount() == static_cast<int>(i+1)); - } - } - REPORTER_ASSERT(reporter, CountingClass::GetCount() == 0); -} - -// Tests that ensure that the destructor is called, whether the objects -// were created in fStorage or on the heap. -DEF_TEST(SmallAllocator_destructor, reporter) { - // Four times as many bytes as objects will never require any heap - // allocations (since SkAlign4(sizeof(CountingClass)) == 4 and the allocator - // will stop once it reaches kMaxObjects). - test_allocator<5, 20>(reporter); - test_allocator<10, 40>(reporter); - test_allocator<20, 80>(reporter); - -#ifndef SK_DEBUG - // Allowing less bytes than objects means some will be allocated on the - // heap. Don't run these in debug where we assert. - test_allocator<50, 20>(reporter); - test_allocator<100, 20>(reporter); -#endif -} - -class Dummy { -}; - -class DummyContainer { -public: - explicit DummyContainer(Dummy* d) - :fDummy(d) - {} - - Dummy* getDummy() const { return fDummy; } - -private: - Dummy* fDummy; -}; - -// Test that using a createT with a constructor taking a pointer as a -// parameter works as expected. -DEF_TEST(SmallAllocator_pointer, reporter) { - SkSmallAllocator<1, 8> alloc; - Dummy d; - DummyContainer* container = alloc.createT<DummyContainer>(&d); - REPORTER_ASSERT(reporter, container != NULL); - REPORTER_ASSERT(reporter, container->getDummy() == &d); -}
diff --git a/src/third_party/skia/tests/SortTest.cpp b/src/third_party/skia/tests/SortTest.cpp index e8713dd..d544ebd 100644 --- a/src/third_party/skia/tests/SortTest.cpp +++ b/src/third_party/skia/tests/SortTest.cpp
@@ -9,6 +9,8 @@ #include "SkTSort.h" #include "Test.h" +#include <stdlib.h> + extern "C" { static int compare_int(const void* a, const void* b) { return *(const int*)a - *(const int*)b;
diff --git a/src/third_party/skia/tests/SpecialImageTest.cpp b/src/third_party/skia/tests/SpecialImageTest.cpp new file mode 100644 index 0000000..70d6fd4 --- /dev/null +++ b/src/third_party/skia/tests/SpecialImageTest.cpp
@@ -0,0 +1,338 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file + */ + +#include "SkAutoPixmapStorage.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkImage.h" +#include "SkPixmap.h" +#include "SkSpecialImage.h" +#include "SkSpecialSurface.h" +#include "SkSurface.h" +#include "Test.h" + +#if SK_SUPPORT_GPU +#include "GrContext.h" +#include "GrSurfaceProxy.h" +#include "GrTextureProxy.h" +#include "SkGr.h" +#endif + + +// This test creates backing resources exactly sized to [kFullSize x kFullSize]. +// It then wraps them in an SkSpecialImage with only the center (red) region being active. +// It then draws the SkSpecialImage to a full sized (all blue) canvas and checks that none +// of the inactive (green) region leaked out. + +static const int kSmallerSize = 10; +static const int kPad = 3; +static const int kFullSize = kSmallerSize + 2 * kPad; + +// Create a bitmap with red in the center and green around it +static SkBitmap create_bm() { + SkBitmap bm; + bm.allocN32Pixels(kFullSize, kFullSize, true); + + SkCanvas temp(bm); + + temp.clear(SK_ColorGREEN); + SkPaint p; + p.setColor(SK_ColorRED); + p.setAntiAlias(false); + + temp.drawRect(SkRect::MakeXYWH(SkIntToScalar(kPad), SkIntToScalar(kPad), + SkIntToScalar(kSmallerSize), SkIntToScalar(kSmallerSize)), + p); + + return bm; +} + +// Basic test of the SkSpecialImage public API (e.g., peekTexture, peekPixels & draw) +static void test_image(const sk_sp<SkSpecialImage>& img, skiatest::Reporter* reporter, + GrContext* context, bool isGPUBacked, + int offset, int size) { + const SkIRect subset = img->subset(); + REPORTER_ASSERT(reporter, offset == subset.left()); + REPORTER_ASSERT(reporter, offset == subset.top()); + REPORTER_ASSERT(reporter, kSmallerSize == subset.width()); + REPORTER_ASSERT(reporter, kSmallerSize == subset.height()); + + //-------------- + // Test that isTextureBacked reports the correct backing type + REPORTER_ASSERT(reporter, isGPUBacked == img->isTextureBacked()); + +#if SK_SUPPORT_GPU + //-------------- + // Test asTextureProxyRef - as long as there is a context this should succeed + if (context) { + sk_sp<GrTextureProxy> proxy(img->asTextureProxyRef(context)); + REPORTER_ASSERT(reporter, proxy); + } +#endif + + //-------------- + // Test getROPixels - this should always succeed regardless of backing store + SkBitmap bitmap; + REPORTER_ASSERT(reporter, img->getROPixels(&bitmap)); + if (context) { + REPORTER_ASSERT(reporter, kSmallerSize == bitmap.width()); + REPORTER_ASSERT(reporter, kSmallerSize == bitmap.height()); + } else { + REPORTER_ASSERT(reporter, size == bitmap.width()); + REPORTER_ASSERT(reporter, size == bitmap.height()); + } + + //-------------- + // Test that draw restricts itself to the subset + SkImageFilter::OutputProperties outProps(img->getColorSpace()); + sk_sp<SkSpecialSurface> surf(img->makeSurface(outProps, SkISize::Make(kFullSize, kFullSize), + kPremul_SkAlphaType)); + + SkCanvas* canvas = surf->getCanvas(); + + canvas->clear(SK_ColorBLUE); + img->draw(canvas, SkIntToScalar(kPad), SkIntToScalar(kPad), nullptr); + + SkBitmap bm; + bm.allocN32Pixels(kFullSize, kFullSize, false); + + bool result = canvas->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), 0, 0); + SkASSERT_RELEASE(result); + + // Only the center (red) portion should've been drawn into the canvas + REPORTER_ASSERT(reporter, SK_ColorBLUE == bm.getColor(kPad-1, kPad-1)); + REPORTER_ASSERT(reporter, SK_ColorRED == bm.getColor(kPad, kPad)); + REPORTER_ASSERT(reporter, SK_ColorRED == bm.getColor(kSmallerSize+kPad-1, + kSmallerSize+kPad-1)); + REPORTER_ASSERT(reporter, SK_ColorBLUE == bm.getColor(kSmallerSize+kPad, + kSmallerSize+kPad)); + + //-------------- + // Test that asImage & makeTightSurface return appropriately sized objects + // of the correct backing type + SkIRect newSubset = SkIRect::MakeWH(subset.width(), subset.height()); + { + sk_sp<SkImage> tightImg(img->asImage(&newSubset)); + + REPORTER_ASSERT(reporter, tightImg->width() == subset.width()); + REPORTER_ASSERT(reporter, tightImg->height() == subset.height()); + REPORTER_ASSERT(reporter, isGPUBacked == tightImg->isTextureBacked()); + SkPixmap tmpPixmap; + REPORTER_ASSERT(reporter, isGPUBacked != !!tightImg->peekPixels(&tmpPixmap)); + } + { + SkImageFilter::OutputProperties outProps(img->getColorSpace()); + sk_sp<SkSurface> tightSurf(img->makeTightSurface(outProps, subset.size())); + + REPORTER_ASSERT(reporter, tightSurf->width() == subset.width()); + REPORTER_ASSERT(reporter, tightSurf->height() == subset.height()); + REPORTER_ASSERT(reporter, isGPUBacked == + !!tightSurf->getTextureHandle(SkSurface::kDiscardWrite_BackendHandleAccess)); + SkPixmap tmpPixmap; + REPORTER_ASSERT(reporter, isGPUBacked != !!tightSurf->peekPixels(&tmpPixmap)); + } +} + +DEF_TEST(SpecialImage_Raster, reporter) { + SkBitmap bm = create_bm(); + + sk_sp<SkSpecialImage> fullSImage(SkSpecialImage::MakeFromRaster( + SkIRect::MakeWH(kFullSize, kFullSize), + bm)); + + const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); + + { + sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeFromRaster(subset, bm)); + test_image(subSImg1, reporter, nullptr, false, kPad, kFullSize); + } + + { + sk_sp<SkSpecialImage> subSImg2(fullSImage->makeSubset(subset)); + test_image(subSImg2, reporter, nullptr, false, 0, kSmallerSize); + } +} + +static void test_specialimage_image(skiatest::Reporter* reporter, SkColorSpace* dstColorSpace) { + SkBitmap bm = create_bm(); + + sk_sp<SkImage> fullImage(SkImage::MakeFromBitmap(bm)); + + sk_sp<SkSpecialImage> fullSImage(SkSpecialImage::MakeFromImage( + SkIRect::MakeWH(kFullSize, kFullSize), + fullImage, dstColorSpace)); + + const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); + + { + sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeFromImage(subset, fullImage, + dstColorSpace)); + test_image(subSImg1, reporter, nullptr, false, kPad, kFullSize); + } + + { + sk_sp<SkSpecialImage> subSImg2(fullSImage->makeSubset(subset)); + test_image(subSImg2, reporter, nullptr, false, 0, kSmallerSize); + } +} + +DEF_TEST(SpecialImage_Image_Legacy, reporter) { + SkColorSpace* legacyColorSpace = nullptr; + test_specialimage_image(reporter, legacyColorSpace); +} + +DEF_TEST(SpecialImage_Image_ColorSpaceAware, reporter) { + sk_sp<SkColorSpace> srgbColorSpace = SkColorSpace::MakeSRGB(); + test_specialimage_image(reporter, srgbColorSpace.get()); +} + +#if SK_SUPPORT_GPU + +static void test_texture_backed(skiatest::Reporter* reporter, + const sk_sp<SkSpecialImage>& orig, + const sk_sp<SkSpecialImage>& gpuBacked) { + REPORTER_ASSERT(reporter, gpuBacked); + REPORTER_ASSERT(reporter, gpuBacked->isTextureBacked()); + REPORTER_ASSERT(reporter, gpuBacked->uniqueID() == orig->uniqueID()); + REPORTER_ASSERT(reporter, gpuBacked->subset().width() == orig->subset().width() && + gpuBacked->subset().height() == orig->subset().height()); + REPORTER_ASSERT(reporter, gpuBacked->getColorSpace() == orig->getColorSpace()); +} + +// Test out the SkSpecialImage::makeTextureImage entry point +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_MakeTexture, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + SkBitmap bm = create_bm(); + + const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); + + { + // raster + sk_sp<SkSpecialImage> rasterImage(SkSpecialImage::MakeFromRaster( + SkIRect::MakeWH(kFullSize, + kFullSize), + bm)); + + { + sk_sp<SkSpecialImage> fromRaster(rasterImage->makeTextureImage(context)); + test_texture_backed(reporter, rasterImage, fromRaster); + } + + { + sk_sp<SkSpecialImage> subRasterImage(rasterImage->makeSubset(subset)); + + sk_sp<SkSpecialImage> fromSubRaster(subRasterImage->makeTextureImage(context)); + test_texture_backed(reporter, subRasterImage, fromSubRaster); + } + } + + { + // gpu + const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bm.info(), *context->caps()); + + sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(context->resourceProvider(), + desc, SkBudgeted::kNo, + bm.getPixels(), bm.rowBytes())); + if (!proxy) { + return; + } + + sk_sp<SkSpecialImage> gpuImage(SkSpecialImage::MakeDeferredFromGpu( + context, + SkIRect::MakeWH(kFullSize, kFullSize), + kNeedNewImageUniqueID_SpecialImage, + std::move(proxy), nullptr)); + + { + sk_sp<SkSpecialImage> fromGPU(gpuImage->makeTextureImage(context)); + test_texture_backed(reporter, gpuImage, fromGPU); + } + + { + sk_sp<SkSpecialImage> subGPUImage(gpuImage->makeSubset(subset)); + + sk_sp<SkSpecialImage> fromSubGPU(subGPUImage->makeTextureImage(context)); + test_texture_backed(reporter, subGPUImage, fromSubGPU); + } + } +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_Gpu, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + SkBitmap bm = create_bm(); + + const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bm.info(), *context->caps()); + + sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(context->resourceProvider(), + desc, SkBudgeted::kNo, + bm.getPixels(), bm.rowBytes())); + if (!proxy) { + return; + } + + sk_sp<SkSpecialImage> fullSImg(SkSpecialImage::MakeDeferredFromGpu( + context, + SkIRect::MakeWH(kFullSize, kFullSize), + kNeedNewImageUniqueID_SpecialImage, + proxy, nullptr)); + + const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); + + { + sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeDeferredFromGpu( + context, subset, + kNeedNewImageUniqueID_SpecialImage, + std::move(proxy), nullptr)); + test_image(subSImg1, reporter, context, true, kPad, kFullSize); + } + + { + sk_sp<SkSpecialImage> subSImg2(fullSImg->makeSubset(subset)); + test_image(subSImg2, reporter, context, true, kPad, kFullSize); + } +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_DeferredGpu, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + SkBitmap bm = create_bm(); + + GrSurfaceDesc desc; + desc.fConfig = kSkia8888_GrPixelConfig; + desc.fFlags = kNone_GrSurfaceFlags; + desc.fWidth = kFullSize; + desc.fHeight = kFullSize; + + sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(context->resourceProvider(), + desc, SkBudgeted::kNo, + bm.getPixels(), 0)); + if (!proxy) { + return; + } + + sk_sp<SkSpecialImage> fullSImg(SkSpecialImage::MakeDeferredFromGpu( + context, + SkIRect::MakeWH(kFullSize, kFullSize), + kNeedNewImageUniqueID_SpecialImage, + proxy, nullptr)); + + const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); + + { + sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeDeferredFromGpu( + context, subset, + kNeedNewImageUniqueID_SpecialImage, + std::move(proxy), nullptr)); + test_image(subSImg1, reporter, context, true, kPad, kFullSize); + } + + { + sk_sp<SkSpecialImage> subSImg2(fullSImg->makeSubset(subset)); + test_image(subSImg2, reporter, context, true, kPad, kFullSize); + } +} + +#endif
diff --git a/src/third_party/skia/tests/SpecialSurfaceTest.cpp b/src/third_party/skia/tests/SpecialSurfaceTest.cpp new file mode 100644 index 0000000..a336cf6 --- /dev/null +++ b/src/third_party/skia/tests/SpecialSurfaceTest.cpp
@@ -0,0 +1,91 @@ +/* +* Copyright 2016 Google Inc. +* +* Use of this source code is governed by a BSD-style license that can be +* found in the LICENSE file +*/ + +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkSpecialImage.h" +#include "SkSpecialSurface.h" +#include "Test.h" + +#if SK_SUPPORT_GPU +#include "GrContext.h" +#include "SkGr.h" +#endif + +class TestingSpecialSurfaceAccess { +public: + static const SkIRect& Subset(const SkSpecialSurface* surf) { + return surf->subset(); + } +}; + +// Both 'kSmallerSize' and 'kFullSize' need to be a non-power-of-2 to exercise +// the gpu's loose fit behavior +static const int kSmallerSize = 10; +static const int kPad = 5; +static const int kFullSize = kSmallerSize + 2 * kPad; + +// Exercise the public API of SkSpecialSurface (e.g., getCanvas, newImageSnapshot) +static void test_surface(const sk_sp<SkSpecialSurface>& surf, + skiatest::Reporter* reporter, + int offset) { + + const SkIRect surfSubset = TestingSpecialSurfaceAccess::Subset(surf.get()); + REPORTER_ASSERT(reporter, offset == surfSubset.fLeft); + REPORTER_ASSERT(reporter, offset == surfSubset.fTop); + REPORTER_ASSERT(reporter, kSmallerSize == surfSubset.width()); + REPORTER_ASSERT(reporter, kSmallerSize == surfSubset.height()); + + SkCanvas* canvas = surf->getCanvas(); + SkASSERT_RELEASE(canvas); + + canvas->clear(SK_ColorRED); + + sk_sp<SkSpecialImage> img(surf->makeImageSnapshot()); + REPORTER_ASSERT(reporter, img); + + const SkIRect imgSubset = img->subset(); + REPORTER_ASSERT(reporter, surfSubset == imgSubset); + + // the canvas was invalidated by the newImageSnapshot call + REPORTER_ASSERT(reporter, !surf->getCanvas()); +} + +DEF_TEST(SpecialSurface_Raster, reporter) { + + SkImageInfo info = SkImageInfo::MakeN32(kSmallerSize, kSmallerSize, kOpaque_SkAlphaType); + sk_sp<SkSpecialSurface> surf(SkSpecialSurface::MakeRaster(info)); + + test_surface(surf, reporter, 0); +} + +DEF_TEST(SpecialSurface_Raster2, reporter) { + + SkBitmap bm; + bm.allocN32Pixels(kFullSize, kFullSize, true); + + const SkIRect subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); + + sk_sp<SkSpecialSurface> surf(SkSpecialSurface::MakeFromBitmap(subset, bm)); + + test_surface(surf, reporter, kPad); + + // TODO: check that the clear didn't escape the active region +} + +#if SK_SUPPORT_GPU + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialSurface_Gpu1, reporter, ctxInfo) { + sk_sp<SkSpecialSurface> surf(SkSpecialSurface::MakeRenderTarget(ctxInfo.grContext(), + kSmallerSize, kSmallerSize, + kRGBA_8888_GrPixelConfig, + nullptr)); + + test_surface(surf, reporter, 0); +} + +#endif
diff --git a/src/third_party/skia/tests/SrcOverTest.cpp b/src/third_party/skia/tests/SrcOverTest.cpp index be64710..df31598 100644 --- a/src/third_party/skia/tests/SrcOverTest.cpp +++ b/src/third_party/skia/tests/SrcOverTest.cpp
@@ -6,7 +6,6 @@ */ #include "SkColorPriv.h" -#include "SkXfermode.h" #include "Test.h" // our std SkAlpha255To256 @@ -42,8 +41,8 @@ opaqueCounter2 += (result2 == 0xFF); } #if 0 - SkDebugf("---- opaque test: [%d %d %d]\n", - opaqueCounter0, opaqueCounter1, opaqueCounter2); + INFOF(reporter, "---- opaque test: [%d %d %d]\n", + opaqueCounter0, opaqueCounter1, opaqueCounter2); #endif // we acknowledge that technique0 does not always return opaque REPORTER_ASSERT(reporter, opaqueCounter0 == 256); @@ -67,8 +66,8 @@ #if 0 // this shows where r1 (faster) differs from r2 (more exact) if (r1 != r2) { - SkDebugf("--- dst=%d i=%d r1=%d r2=%d exact=%g\n", - dst, i, r1, r2, i + dst - dst*i/255.0f); + INFOF(reporter, "--- dst=%d i=%d r1=%d r2=%d exact=%g\n", + dst, i, r1, r2, i + dst - dst*i/255.0f); } #endif }
diff --git a/src/third_party/skia/tests/StreamBufferTest.cpp b/src/third_party/skia/tests/StreamBufferTest.cpp new file mode 100644 index 0000000..96bca35 --- /dev/null +++ b/src/third_party/skia/tests/StreamBufferTest.cpp
@@ -0,0 +1,135 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkData.h" +#include "SkOSPath.h" +#include "SkStream.h" +#include "SkStreamBuffer.h" + +#include "FakeStreams.h" +#include "Test.h" + +static const char* gText = "Four score and seven years ago"; + +static void test_get_data_at_position(skiatest::Reporter* r, SkStreamBuffer* buffer, size_t position, + size_t length) { + sk_sp<SkData> data = buffer->getDataAtPosition(position, length); + REPORTER_ASSERT(r, data); + if (data) { + REPORTER_ASSERT(r, !memcmp(data->data(), gText + position, length)); + } +} + +// Test buffering from the beginning, by different amounts. +static void test_buffer_from_beginning(skiatest::Reporter* r, SkStream* stream, size_t length) { + SkStreamBuffer buffer(stream); + + // Buffer an arbitrary amount: + size_t buffered = length / 2; + REPORTER_ASSERT(r, buffer.buffer(buffered)); + REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, buffered)); + + // Buffering less is free: + REPORTER_ASSERT(r, buffer.buffer(buffered / 2)); + + // Buffer more should succeed: + REPORTER_ASSERT(r, buffer.buffer(length)); + REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, length)); +} + +// Test flushing the stream as we read. +static void test_flushing(skiatest::Reporter* r, SkStream* stream, size_t length, + bool getDataAtPosition) { + SkStreamBuffer buffer(stream); + const size_t step = 5; + for (size_t position = 0; position + step <= length; position += step) { + REPORTER_ASSERT(r, buffer.buffer(step)); + REPORTER_ASSERT(r, buffer.markPosition() == position); + + if (!getDataAtPosition) { + REPORTER_ASSERT(r, !memcmp(buffer.get(), gText + position, step)); + } + buffer.flush(); + } + + REPORTER_ASSERT(r, !buffer.buffer(step)); + + if (getDataAtPosition) { + for (size_t position = 0; position + step <= length; position += step) { + test_get_data_at_position(r, &buffer, position, step); + } + } +} + +DEF_TEST(StreamBuffer, r) { + const size_t size = strlen(gText); + sk_sp<SkData> data(SkData::MakeWithoutCopy(gText, size)); + + SkString tmpDir = skiatest::GetTmpDir(); + const char* subdir = "streamBuffer.txt"; + SkString path; + + if (!tmpDir.isEmpty()) { + path = SkOSPath::Join(tmpDir.c_str(), subdir); + SkFILEWStream writer(path.c_str()); + writer.write(gText, size); + } + + struct { + std::function<SkStream*()> createStream; + bool skipIfNoTmpDir; + } factories[] = { + { [&data]() { return new SkMemoryStream(data); }, false }, + { [&data]() { return new NotAssetMemStream(data); }, false }, + { [&path]() { return new SkFILEStream(path.c_str()); }, true }, + }; + + for (auto f : factories) { + if (tmpDir.isEmpty() && f.skipIfNoTmpDir) { + continue; + } + test_buffer_from_beginning(r, f.createStream(), size); + test_flushing(r, f.createStream(), size, false); + test_flushing(r, f.createStream(), size, true); + } + + // Stream that will receive more data. Will be owned by the SkStreamBuffer. + HaltingStream* stream = new HaltingStream(data, 6); + SkStreamBuffer buffer(stream); + + // Can only buffer less than what's available (6). + REPORTER_ASSERT(r, !buffer.buffer(7)); + REPORTER_ASSERT(r, buffer.buffer(5)); + REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, 5)); + + // Add some more data. We can buffer and read all of it. + stream->addNewData(8); + REPORTER_ASSERT(r, buffer.buffer(14)); + REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, 14)); + + // Flush the buffer, which moves the position. + buffer.flush(); + + // Add some data, and try to read more. Can only read what is + // available. + stream->addNewData(9); + REPORTER_ASSERT(r, !buffer.buffer(13)); + stream->addNewData(4); + REPORTER_ASSERT(r, buffer.buffer(13)); + + // Do not call get on this data. We'll come back to this data after adding + // more. + buffer.flush(); + const size_t remaining = size - 27; + REPORTER_ASSERT(r, remaining > 0); + stream->addNewData(remaining); + REPORTER_ASSERT(r, buffer.buffer(remaining)); + REPORTER_ASSERT(r, !memcmp(buffer.get(), gText + 27, remaining)); + + // Now go back to the data we skipped. + test_get_data_at_position(r, &buffer, 14, 13); +}
diff --git a/src/third_party/skia/tests/StreamTest.cpp b/src/third_party/skia/tests/StreamTest.cpp index ce391a3..8b5b2ae 100644 --- a/src/third_party/skia/tests/StreamTest.cpp +++ b/src/third_party/skia/tests/StreamTest.cpp
@@ -5,10 +5,15 @@ * found in the LICENSE file. */ +#include "Resources.h" +#include "SkAutoMalloc.h" #include "SkData.h" +#include "SkFrontBufferedStream.h" #include "SkOSFile.h" +#include "SkOSPath.h" #include "SkRandom.h" #include "SkStream.h" +#include "SkStreamPriv.h" #include "Test.h" #ifndef SK_BUILD_FOR_WIN @@ -58,17 +63,17 @@ REPORTER_ASSERT(reporter, stream.isValid()); test_loop_stream(reporter, &stream, s, 26, 100); - SkAutoTUnref<SkStreamAsset> stream2(stream.duplicate()); + std::unique_ptr<SkStreamAsset> stream2(stream.duplicate()); test_loop_stream(reporter, stream2.get(), s, 26, 100); } { FILE* file = ::fopen(path.c_str(), "rb"); - SkFILEStream stream(file, SkFILEStream::kCallerPasses_Ownership); + SkFILEStream stream(file); REPORTER_ASSERT(reporter, stream.isValid()); test_loop_stream(reporter, &stream, s, 26, 100); - SkAutoTUnref<SkStreamAsset> stream2(stream.duplicate()); + std::unique_ptr<SkStreamAsset> stream2(stream.duplicate()); test_loop_stream(reporter, stream2.get(), s, 26, 100); } } @@ -80,7 +85,7 @@ for (i = 0; i < 100; i++) { REPORTER_ASSERT(reporter, ds.write(s, 26)); } - REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26); + REPORTER_ASSERT(reporter, ds.bytesWritten() == 100 * 26); char* dst = new char[100 * 26 + 1]; dst[100*26] = '*'; @@ -91,15 +96,15 @@ } { - SkAutoTUnref<SkStreamAsset> stream(ds.detachAsStream()); + std::unique_ptr<SkStreamAsset> stream(ds.detachAsStream()); REPORTER_ASSERT(reporter, 100 * 26 == stream->getLength()); - REPORTER_ASSERT(reporter, ds.getOffset() == 0); + REPORTER_ASSERT(reporter, ds.bytesWritten() == 0); test_loop_stream(reporter, stream.get(), s, 26, 100); - SkAutoTUnref<SkStreamAsset> stream2(stream->duplicate()); + std::unique_ptr<SkStreamAsset> stream2(stream->duplicate()); test_loop_stream(reporter, stream2.get(), s, 26, 100); - SkAutoTUnref<SkStreamAsset> stream3(stream->fork()); + std::unique_ptr<SkStreamAsset> stream3(stream->fork()); REPORTER_ASSERT(reporter, stream3->isAtEnd()); char tmp; size_t bytes = stream->read(&tmp, 1); @@ -111,26 +116,20 @@ for (i = 0; i < 100; i++) { REPORTER_ASSERT(reporter, ds.write(s, 26)); } - REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26); + REPORTER_ASSERT(reporter, ds.bytesWritten() == 100 * 26); { - SkAutoTUnref<SkData> data(ds.copyToData()); - REPORTER_ASSERT(reporter, 100 * 26 == data->size()); - REPORTER_ASSERT(reporter, memcmp(dst, data->data(), data->size()) == 0); - } - - { - // Test that this works after a copyToData. - SkAutoTUnref<SkStreamAsset> stream(ds.detachAsStream()); - REPORTER_ASSERT(reporter, ds.getOffset() == 0); + // Test that this works after a snapshot. + std::unique_ptr<SkStreamAsset> stream(ds.detachAsStream()); + REPORTER_ASSERT(reporter, ds.bytesWritten() == 0); test_loop_stream(reporter, stream.get(), s, 26, 100); - SkAutoTUnref<SkStreamAsset> stream2(stream->duplicate()); + std::unique_ptr<SkStreamAsset> stream2(stream->duplicate()); test_loop_stream(reporter, stream2.get(), s, 26, 100); } delete[] dst; - SkString tmpDir = skiatest::Test::GetTmpDir(); + SkString tmpDir = skiatest::GetTmpDir(); if (!tmpDir.isEmpty()) { test_filestreams(reporter, tmpDir.c_str()); } @@ -148,39 +147,35 @@ size_t i; - char buffer[sizeof(sizes) * 4]; + SkDynamicMemoryWStream wstream; - SkMemoryWStream wstream(buffer, sizeof(buffer)); for (i = 0; i < SK_ARRAY_COUNT(sizes); ++i) { bool success = wstream.writePackedUInt(sizes[i]); REPORTER_ASSERT(reporter, success); } - wstream.flush(); - SkMemoryStream rstream(buffer, sizeof(buffer)); + std::unique_ptr<SkStreamAsset> rstream(wstream.detachAsStream()); for (i = 0; i < SK_ARRAY_COUNT(sizes); ++i) { - size_t n = rstream.readPackedUInt(); + size_t n = rstream->readPackedUInt(); if (sizes[i] != n) { - SkDebugf("-- %d: sizes:%x n:%x\n", i, sizes[i], n); + ERRORF(reporter, "sizes:%x != n:%x\n", i, sizes[i], n); } - REPORTER_ASSERT(reporter, sizes[i] == n); } } -// Test that setting an SkMemoryStream to a NULL data does not result in a crash when calling +// Test that setting an SkMemoryStream to a nullptr data does not result in a crash when calling // methods that access fData. static void TestDereferencingData(SkMemoryStream* memStream) { - memStream->read(NULL, 0); + memStream->read(nullptr, 0); memStream->getMemoryBase(); - SkAutoDataUnref data(memStream->copyToData()); + (void)memStream->asData(); } static void TestNullData() { - SkData* nullData = NULL; - SkMemoryStream memStream(nullData); + SkMemoryStream memStream(nullptr); TestDereferencingData(&memStream); - memStream.setData(nullData); + memStream.setData(nullptr); TestDereferencingData(&memStream); } @@ -190,3 +185,282 @@ TestPackedUInt(reporter); TestNullData(); } + +#ifndef SK_BUILD_FOR_IOS +/** + * Tests peeking and then reading the same amount. The two should provide the + * same results. + * Returns the amount successfully read minus the amount successfully peeked. + */ +static size_t compare_peek_to_read(skiatest::Reporter* reporter, + SkStream* stream, size_t bytesToPeek) { + // The rest of our tests won't be very interesting if bytesToPeek is zero. + REPORTER_ASSERT(reporter, bytesToPeek > 0); + SkAutoMalloc peekStorage(bytesToPeek); + SkAutoMalloc readStorage(bytesToPeek); + void* peekPtr = peekStorage.get(); + void* readPtr = peekStorage.get(); + + const size_t bytesPeeked = stream->peek(peekPtr, bytesToPeek); + const size_t bytesRead = stream->read(readPtr, bytesToPeek); + + // bytesRead should only be less than attempted if the stream is at the + // end. + REPORTER_ASSERT(reporter, bytesRead == bytesToPeek || stream->isAtEnd()); + + // peek and read should behave the same, except peek returned to the + // original position, so they read the same data. + REPORTER_ASSERT(reporter, !memcmp(peekPtr, readPtr, bytesPeeked)); + + // A stream should never be able to peek more than it can read. + REPORTER_ASSERT(reporter, bytesRead >= bytesPeeked); + + return bytesRead - bytesPeeked; +} + +static void test_fully_peekable_stream(skiatest::Reporter* r, SkStream* stream, size_t limit) { + for (size_t i = 1; !stream->isAtEnd(); i++) { + REPORTER_ASSERT(r, compare_peek_to_read(r, stream, i) == 0); + } +} + +static void test_peeking_front_buffered_stream(skiatest::Reporter* r, + const SkStream& original, + size_t bufferSize) { + SkStream* dupe = original.duplicate(); + REPORTER_ASSERT(r, dupe != nullptr); + std::unique_ptr<SkStream> bufferedStream(SkFrontBufferedStream::Create(dupe, bufferSize)); + REPORTER_ASSERT(r, bufferedStream != nullptr); + + size_t peeked = 0; + for (size_t i = 1; !bufferedStream->isAtEnd(); i++) { + const size_t unpeekableBytes = compare_peek_to_read(r, bufferedStream.get(), i); + if (unpeekableBytes > 0) { + // This could not have returned a number greater than i. + REPORTER_ASSERT(r, unpeekableBytes <= i); + + // We have reached the end of the buffer. Verify that it was at least + // bufferSize. + REPORTER_ASSERT(r, peeked + i - unpeekableBytes >= bufferSize); + // No more peeking is supported. + break; + } + peeked += i; + } + + // Test that attempting to peek beyond the length of the buffer does not prevent rewinding. + bufferedStream.reset(SkFrontBufferedStream::Create(original.duplicate(), bufferSize)); + REPORTER_ASSERT(r, bufferedStream != nullptr); + + const size_t bytesToPeek = bufferSize + 1; + SkAutoMalloc peekStorage(bytesToPeek); + SkAutoMalloc readStorage(bytesToPeek); + + for (size_t start = 0; start <= bufferSize; start++) { + // Skip to the starting point + REPORTER_ASSERT(r, bufferedStream->skip(start) == start); + + const size_t bytesPeeked = bufferedStream->peek(peekStorage.get(), bytesToPeek); + if (0 == bytesPeeked) { + // Peeking should only fail completely if we have read/skipped beyond the buffer. + REPORTER_ASSERT(r, start >= bufferSize); + break; + } + + // Only read the amount that was successfully peeked. + const size_t bytesRead = bufferedStream->read(readStorage.get(), bytesPeeked); + REPORTER_ASSERT(r, bytesRead == bytesPeeked); + REPORTER_ASSERT(r, !memcmp(peekStorage.get(), readStorage.get(), bytesPeeked)); + + // This should be safe to rewind. + REPORTER_ASSERT(r, bufferedStream->rewind()); + } +} + +// This test uses file system operations that don't work out of the +// box on iOS. It's likely that we don't need them on iOS. Ignoring for now. +// TODO(stephana): Re-evaluate if we need this in the future. +DEF_TEST(StreamPeek, reporter) { + // Test a memory stream. + const char gAbcs[] = "abcdefghijklmnopqrstuvwxyz"; + SkMemoryStream memStream(gAbcs, strlen(gAbcs), false); + test_fully_peekable_stream(reporter, &memStream, memStream.getLength()); + + // Test an arbitrary file stream. file streams do not support peeking. + SkFILEStream fileStream(GetResourcePath("baby_tux.webp").c_str()); + REPORTER_ASSERT(reporter, fileStream.isValid()); + if (!fileStream.isValid()) { + return; + } + SkAutoMalloc storage(fileStream.getLength()); + for (size_t i = 1; i < fileStream.getLength(); i++) { + REPORTER_ASSERT(reporter, fileStream.peek(storage.get(), i) == 0); + } + + // Now test some FrontBufferedStreams + for (size_t i = 1; i < memStream.getLength(); i++) { + test_peeking_front_buffered_stream(reporter, memStream, i); + } +} +#endif + +// Asserts that asset == expected and is peekable. +static void stream_peek_test(skiatest::Reporter* rep, + SkStreamAsset* asset, + const SkData* expected) { + if (asset->getLength() != expected->size()) { + ERRORF(rep, "Unexpected length."); + return; + } + SkRandom rand; + uint8_t buffer[4096]; + const uint8_t* expect = expected->bytes(); + for (size_t i = 0; i < asset->getLength(); ++i) { + uint32_t maxSize = + SkToU32(SkTMin(sizeof(buffer), asset->getLength() - i)); + size_t size = rand.nextRangeU(1, maxSize); + SkASSERT(size >= 1); + SkASSERT(size <= sizeof(buffer)); + SkASSERT(size + i <= asset->getLength()); + if (asset->peek(buffer, size) < size) { + ERRORF(rep, "Peek Failed!"); + return; + } + if (0 != memcmp(buffer, &expect[i], size)) { + ERRORF(rep, "Peek returned wrong bytes!"); + return; + } + uint8_t value; + REPORTER_ASSERT(rep, 1 == asset->read(&value, 1)); + if (value != expect[i]) { + ERRORF(rep, "Read Failed!"); + return; + } + } +} + +DEF_TEST(StreamPeek_BlockMemoryStream, rep) { + const static int kSeed = 1234; + SkRandom valueSource(kSeed); + SkRandom rand(kSeed << 1); + uint8_t buffer[4096]; + SkDynamicMemoryWStream dynamicMemoryWStream; + size_t totalWritten = 0; + for (int i = 0; i < 32; ++i) { + // Randomize the length of the blocks. + size_t size = rand.nextRangeU(1, sizeof(buffer)); + for (size_t j = 0; j < size; ++j) { + buffer[j] = valueSource.nextU() & 0xFF; + } + dynamicMemoryWStream.write(buffer, size); + totalWritten += size; + REPORTER_ASSERT(rep, totalWritten == dynamicMemoryWStream.bytesWritten()); + } + std::unique_ptr<SkStreamAsset> asset(dynamicMemoryWStream.detachAsStream()); + sk_sp<SkData> expected(SkData::MakeUninitialized(asset->getLength())); + uint8_t* expectedPtr = static_cast<uint8_t*>(expected->writable_data()); + valueSource.setSeed(kSeed); // reseed. + // We want the exact same same "random" string of numbers to put + // in expected. i.e.: don't rely on SkDynamicMemoryStream to work + // correctly while we are testing SkDynamicMemoryStream. + for (size_t i = 0; i < asset->getLength(); ++i) { + expectedPtr[i] = valueSource.nextU() & 0xFF; + } + stream_peek_test(rep, asset.get(), expected.get()); +} + +namespace { +class DumbStream : public SkStream { +public: + DumbStream(const uint8_t* data, size_t n) + : fData(data), fCount(n), fIdx(0) {} + size_t read(void* buffer, size_t size) override { + size_t copyCount = SkTMin(fCount - fIdx, size); + if (copyCount) { + memcpy(buffer, &fData[fIdx], copyCount); + fIdx += copyCount; + } + return copyCount; + } + bool isAtEnd() const override { + return fCount == fIdx; + } + private: + const uint8_t* fData; + size_t fCount, fIdx; +}; +} // namespace + +static void stream_copy_test(skiatest::Reporter* reporter, + const void* srcData, + size_t N, + SkStream* stream) { + SkDynamicMemoryWStream tgt; + if (!SkStreamCopy(&tgt, stream)) { + ERRORF(reporter, "SkStreamCopy failed"); + return; + } + sk_sp<SkData> data(tgt.detachAsData()); + if (data->size() != N) { + ERRORF(reporter, "SkStreamCopy incorrect size"); + return; + } + if (0 != memcmp(data->data(), srcData, N)) { + ERRORF(reporter, "SkStreamCopy bad copy"); + } +} + +DEF_TEST(DynamicMemoryWStream_detachAsData, r) { + const char az[] = "abcdefghijklmnopqrstuvwxyz"; + const unsigned N = 40000; + SkDynamicMemoryWStream dmws; + for (unsigned i = 0; i < N; ++i) { + dmws.writeText(az); + } + REPORTER_ASSERT(r, dmws.bytesWritten() == N * strlen(az)); + auto data = dmws.detachAsData(); + REPORTER_ASSERT(r, data->size() == N * strlen(az)); + const uint8_t* ptr = data->bytes(); + for (unsigned i = 0; i < N; ++i) { + if (0 != memcmp(ptr, az, strlen(az))) { + ERRORF(r, "detachAsData() memcmp failed"); + return; + } + ptr += strlen(az); + } +} + +DEF_TEST(StreamCopy, reporter) { + SkRandom random(123456); + static const int N = 10000; + SkAutoTMalloc<uint8_t> src((size_t)N); + for (int j = 0; j < N; ++j) { + src[j] = random.nextU() & 0xff; + } + // SkStreamCopy had two code paths; this test both. + DumbStream dumbStream(src.get(), (size_t)N); + stream_copy_test(reporter, src, N, &dumbStream); + SkMemoryStream smartStream(src.get(), (size_t)N); + stream_copy_test(reporter, src, N, &smartStream); +} + +DEF_TEST(StreamEmptyStreamMemoryBase, r) { + SkDynamicMemoryWStream tmp; + std::unique_ptr<SkStreamAsset> asset(tmp.detachAsStream()); + REPORTER_ASSERT(r, nullptr == asset->getMemoryBase()); +} + +#include "SkBuffer.h" + +DEF_TEST(RBuffer, reporter) { + int32_t value = 0; + SkRBuffer buffer(&value, 4); + REPORTER_ASSERT(reporter, buffer.isValid()); + + int32_t tmp; + REPORTER_ASSERT(reporter, buffer.read(&tmp, 4)); + REPORTER_ASSERT(reporter, buffer.isValid()); + + REPORTER_ASSERT(reporter, !buffer.read(&tmp, 4)); + REPORTER_ASSERT(reporter, !buffer.isValid()); +}
diff --git a/src/third_party/skia/tests/StringTest.cpp b/src/third_party/skia/tests/StringTest.cpp index 13ee5ec..8ae7412 100644 --- a/src/third_party/skia/tests/StringTest.cpp +++ b/src/third_party/skia/tests/StringTest.cpp
@@ -36,7 +36,7 @@ SkString a; SkString b((size_t)0); SkString c(""); - SkString d(NULL, 0); + SkString d(nullptr, 0); REPORTER_ASSERT(reporter, a.isEmpty()); REPORTER_ASSERT(reporter, a == b && a == c && a == d); @@ -148,6 +148,13 @@ a.appendU64(0x0000000001000000ULL, 15); REPORTER_ASSERT(reporter, a.equals("000000016777216")); + a.printf("%i", 0); + REPORTER_ASSERT(reporter, a.equals("0")); + a.printf("%g", 3.14); + REPORTER_ASSERT(reporter, a.equals("3.14")); + a.printf("hello %s", "skia"); + REPORTER_ASSERT(reporter, a.equals("hello skia")); + static const struct { SkScalar fValue; const char* fString; @@ -156,7 +163,7 @@ { SK_Scalar1, "1" }, { -SK_Scalar1, "-1" }, { SK_Scalar1/2, "0.5" }, - #ifdef SK_BUILD_FOR_WIN + #if defined(SK_BUILD_FOR_WIN) && (_MSC_VER < 1900) { 3.4028234e38f, "3.4028235e+038" }, { -3.4028234e38f, "-3.4028235e+038" }, #else @@ -168,8 +175,9 @@ a.reset(); a.appendScalar(gRec[i].fValue); REPORTER_ASSERT(reporter, a.size() <= SkStrAppendScalar_MaxSize); -// SkDebugf(" received <%s> expected <%s>\n", a.c_str(), gRec[i].fString); - REPORTER_ASSERT(reporter, a.equals(gRec[i].fString)); + if (!a.equals(gRec[i].fString)) { + ERRORF(reporter, "received <%s> expected <%s>\n", a.c_str(), gRec[i].fString); + } } REPORTER_ASSERT(reporter, SkStringPrintf("%i", 0).equals("0")); @@ -184,6 +192,26 @@ REPORTER_ASSERT(reporter, buffer[19] == 0); REPORTER_ASSERT(reporter, buffer[20] == 'a'); + REPORTER_ASSERT(reporter, SkStringPrintf("%i", 0).equals("0")); + + // 2000 is larger than the static buffer size inside SkString.cpp + a = SkStringPrintf("%2000s", " "); + REPORTER_ASSERT(reporter, a.size() == 2000); + for (size_t i = 0; i < a.size(); ++i) { + if (a[i] != ' ') { + ERRORF(reporter, "SkStringPrintf fail: a[%d] = '%c'", i, a[i]); + break; + } + } + a.reset(); + a.printf("%2000s", " "); + REPORTER_ASSERT(reporter, a.size() == 2000); + for (size_t i = 0; i < a.size(); ++i) { + if (a[i] != ' ') { + ERRORF(reporter, "SkStringPrintf fail: a[%d] = '%c'", i, a[i]); + break; + } + } } DEF_TEST(String_SkStrSplit, r) { @@ -197,4 +225,65 @@ REPORTER_ASSERT(r, results[3].equals("dee")); REPORTER_ASSERT(r, results[4].equals("f")); REPORTER_ASSERT(r, results[5].equals("g")); + + results.reset(); + SkStrSplit("\n", "\n", &results); + REPORTER_ASSERT(r, results.count() == 0); + + results.reset(); + SkStrSplit("", "\n", &results); + REPORTER_ASSERT(r, results.count() == 0); + + results.reset(); + SkStrSplit("a", "\n", &results); + REPORTER_ASSERT(r, results.count() == 1); + REPORTER_ASSERT(r, results[0].equals("a")); +} +DEF_TEST(String_SkStrSplit_All, r) { + SkTArray<SkString> results; + SkStrSplit("a-_b_c-dee--f-_-_-g-", "-_", kStrict_SkStrSplitMode, &results); + REPORTER_ASSERT(r, results.count() == 13); + REPORTER_ASSERT(r, results[0].equals("a")); + REPORTER_ASSERT(r, results[1].equals("")); + REPORTER_ASSERT(r, results[2].equals("b")); + REPORTER_ASSERT(r, results[3].equals("c")); + REPORTER_ASSERT(r, results[4].equals("dee")); + REPORTER_ASSERT(r, results[5].equals("")); + REPORTER_ASSERT(r, results[6].equals("f")); + REPORTER_ASSERT(r, results[7].equals("")); + REPORTER_ASSERT(r, results[8].equals("")); + REPORTER_ASSERT(r, results[9].equals("")); + REPORTER_ASSERT(r, results[10].equals("")); + REPORTER_ASSERT(r, results[11].equals("g")); + REPORTER_ASSERT(r, results[12].equals("")); + + results.reset(); + SkStrSplit("\n", "\n", kStrict_SkStrSplitMode, &results); + REPORTER_ASSERT(r, results.count() == 2); + REPORTER_ASSERT(r, results[0].equals("")); + REPORTER_ASSERT(r, results[1].equals("")); + + results.reset(); + SkStrSplit("", "\n", kStrict_SkStrSplitMode, &results); + REPORTER_ASSERT(r, results.count() == 0); + + results.reset(); + SkStrSplit("a", "\n", kStrict_SkStrSplitMode, &results); + REPORTER_ASSERT(r, results.count() == 1); + REPORTER_ASSERT(r, results[0].equals("a")); + + results.reset(); + SkStrSplit(",,", ",", kStrict_SkStrSplitMode, &results); + REPORTER_ASSERT(r, results.count() == 3); + REPORTER_ASSERT(r, results[0].equals("")); + REPORTER_ASSERT(r, results[1].equals("")); + REPORTER_ASSERT(r, results[2].equals("")); + + results.reset(); + SkStrSplit(",a,b,", ",", kStrict_SkStrSplitMode, &results); + REPORTER_ASSERT(r, results.count() == 4); + REPORTER_ASSERT(r, results[0].equals("")); + REPORTER_ASSERT(r, results[1].equals("a")); + REPORTER_ASSERT(r, results[2].equals("b")); + REPORTER_ASSERT(r, results[3].equals("")); }
diff --git a/src/third_party/skia/tests/StrokeTest.cpp b/src/third_party/skia/tests/StrokeTest.cpp index 15220c4..43b293f 100644 --- a/src/third_party/skia/tests/StrokeTest.cpp +++ b/src/third_party/skia/tests/StrokeTest.cpp
@@ -9,6 +9,7 @@ #include "SkPath.h" #include "SkRect.h" #include "SkStroke.h" +#include "SkStrokeRec.h" #include "Test.h" static bool equal(const SkRect& a, const SkRect& b) { @@ -18,6 +19,35 @@ SkScalarNearlyEqual(a.bottom(), b.bottom()); } +static void test_strokecubic(skiatest::Reporter* reporter) { + uint32_t hexCubicVals[] = { + 0x424c1086, 0x44bcf0cb, // fX=51.0161362 fY=1511.52478 + 0x424c107c, 0x44bcf0cb, // fX=51.0160980 fY=1511.52478 + 0x424c10c2, 0x44bcf0cb, // fX=51.0163651 fY=1511.52478 + 0x424c1119, 0x44bcf0ca, // fX=51.0166969 fY=1511.52466 + }; + SkPoint cubicVals[] = { + {51.0161362f, 1511.52478f }, + {51.0160980f, 1511.52478f }, + {51.0163651f, 1511.52478f }, + {51.0166969f, 1511.52466f }, + }; + SkPaint paint; + + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(0.394537568f); + SkPath path, fillPath; + path.moveTo(cubicVals[0]); + path.cubicTo(cubicVals[1], cubicVals[2], cubicVals[3]); + paint.getFillPath(path, &fillPath); + path.reset(); + path.moveTo(SkBits2Float(hexCubicVals[0]), SkBits2Float(hexCubicVals[1])); + path.cubicTo(SkBits2Float(hexCubicVals[2]), SkBits2Float(hexCubicVals[3]), + SkBits2Float(hexCubicVals[4]), SkBits2Float(hexCubicVals[5]), + SkBits2Float(hexCubicVals[6]), SkBits2Float(hexCubicVals[7])); + paint.getFillPath(path, &fillPath); +} + static void test_strokerect(skiatest::Reporter* reporter) { const SkScalar width = SkIntToScalar(10); SkPaint paint; @@ -45,7 +75,7 @@ bool isMiter = SkPaint::kMiter_Join == joins[i]; SkRect nested[2]; - REPORTER_ASSERT(reporter, fillPath.isNestedRects(nested) == isMiter); + REPORTER_ASSERT(reporter, fillPath.isNestedFillRects(nested) == isMiter); if (isMiter) { SkRect inner(r); inner.inset(width/2, width/2); @@ -55,6 +85,83 @@ } } +static void test_strokerec_equality(skiatest::Reporter* reporter) { + { + SkStrokeRec s1(SkStrokeRec::kFill_InitStyle); + SkStrokeRec s2(SkStrokeRec::kFill_InitStyle); + REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2)); + + // Test that style mismatch is detected. + s2.setHairlineStyle(); + REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2)); + + s1.setHairlineStyle(); + REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2)); + + // ResScale is not part of equality. + s1.setResScale(2.1f); + s2.setResScale(1.2f); + REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2)); + s1.setFillStyle(); + s2.setFillStyle(); + REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2)); + s1.setStrokeStyle(1.0f, false); + s2.setStrokeStyle(1.0f, false); + s1.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f); + s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f); + REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2)); + } + + // Stroke parameters on fill or hairline style are not part of equality. + { + SkStrokeRec s1(SkStrokeRec::kFill_InitStyle); + SkStrokeRec s2(SkStrokeRec::kFill_InitStyle); + for (int i = 0; i < 2; ++i) { + s1.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f); + s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.1f); + REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2)); + s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 2.9f); + REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2)); + s2.setStrokeParams(SkPaint::kRound_Cap, SkPaint::kRound_Join, 2.9f); + REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2)); + s1.setHairlineStyle(); + s2.setHairlineStyle(); + } + } + + // Stroke parameters on stroke style are part of equality. + { + SkStrokeRec s1(SkStrokeRec::kFill_InitStyle); + SkStrokeRec s2(SkStrokeRec::kFill_InitStyle); + s1.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f); + s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f); + s1.setStrokeStyle(1.0f, false); + + s2.setStrokeStyle(1.0f, true); + REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2)); + + s2.setStrokeStyle(2.1f, false); + REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2)); + + s2.setStrokeStyle(1.0f, false); + REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2)); + + s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.1f); + REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2)); + s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 2.9f); + REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2)); + s2.setStrokeParams(SkPaint::kRound_Cap, SkPaint::kRound_Join, 2.9f); + REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2)); + + // Sets fill. + s1.setStrokeStyle(0.0f, true); + s2.setStrokeStyle(0.0f, true); + REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2)); + } +} + DEF_TEST(Stroke, reporter) { + test_strokecubic(reporter); test_strokerect(reporter); + test_strokerec_equality(reporter); }
diff --git a/src/third_party/skia/tests/StrokerTest.cpp b/src/third_party/skia/tests/StrokerTest.cpp new file mode 100644 index 0000000..7b9a340 --- /dev/null +++ b/src/third_party/skia/tests/StrokerTest.cpp
@@ -0,0 +1,468 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "PathOpsCubicIntersectionTestData.h" +#include "PathOpsQuadIntersectionTestData.h" +#include "SkCommonFlags.h" +#include "SkPathOpsCubic.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkRandom.h" +#include "SkStrokerPriv.h" +#include "SkTime.h" +#include "Test.h" + +DEFINE_bool(timeout, true, "run until alloted time expires"); + +#define MS_TEST_DURATION 10 + +const SkScalar widths[] = {-FLT_MAX, -1, -0.1f, -FLT_EPSILON, 0, FLT_EPSILON, + 0.0000001f, 0.000001f, 0.00001f, 0.0001f, 0.001f, 0.01f, + 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 1, 1.1f, 2, 10, 10e2f, 10e3f, 10e4f, 10e5f, 10e6f, 10e7f, + 10e8f, 10e9f, 10e10f, 10e20f, FLT_MAX }; +size_t widths_count = SK_ARRAY_COUNT(widths); + +static void pathTest(const SkPath& path) { + SkPaint p; + SkPath fill; + p.setStyle(SkPaint::kStroke_Style); + for (size_t index = 0; index < widths_count; ++index) { + p.setStrokeWidth(widths[index]); + p.getFillPath(path, &fill); + } +} + +static void cubicTest(const SkPoint c[4]) { + SkPath path; + path.moveTo(c[0].fX, c[0].fY); + path.cubicTo(c[1].fX, c[1].fY, c[2].fX, c[2].fY, c[3].fX, c[3].fY); + pathTest(path); +} + +static void quadTest(const SkPoint c[3]) { + SkPath path; + path.moveTo(c[0].fX, c[0].fY); + path.quadTo(c[1].fX, c[1].fY, c[2].fX, c[2].fY); + pathTest(path); +} + +static void cubicSetTest(const CubicPts* dCubic, size_t count) { + skiatest::Timer timer; + for (size_t index = 0; index < count; ++index) { + const CubicPts& dPts = dCubic[index]; + SkDCubic d; + d.debugSet(dPts.fPts); + SkPoint c[4] = { {(float) d[0].fX, (float) d[0].fY}, {(float) d[1].fX, (float) d[1].fY}, + {(float) d[2].fX, (float) d[2].fY}, {(float) d[3].fX, (float) d[3].fY} }; + cubicTest(c); + if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) { + return; + } + } +} + +static void cubicPairSetTest(const CubicPts dCubic[][2], size_t count) { + skiatest::Timer timer; + for (size_t index = 0; index < count; ++index) { + for (int pair = 0; pair < 2; ++pair) { + const CubicPts& dPts = dCubic[index][pair]; + SkDCubic d; + d.debugSet(dPts.fPts); + SkPoint c[4] = { {(float) d[0].fX, (float) d[0].fY}, {(float) d[1].fX, (float) d[1].fY}, + {(float) d[2].fX, (float) d[2].fY}, {(float) d[3].fX, (float) d[3].fY} }; + cubicTest(c); + if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) { + return; + } + } + } +} + +static void quadSetTest(const QuadPts* dQuad, size_t count) { + skiatest::Timer timer; + for (size_t index = 0; index < count; ++index) { + const QuadPts& dPts = dQuad[index]; + SkDQuad d; + d.debugSet(dPts.fPts); + SkPoint c[3] = { {(float) d[0].fX, (float) d[0].fY}, {(float) d[1].fX, (float) d[1].fY}, + {(float) d[2].fX, (float) d[2].fY} }; + quadTest(c); + if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) { + return; + } + } +} + +static void quadPairSetTest(const QuadPts dQuad[][2], size_t count) { + skiatest::Timer timer; + for (size_t index = 0; index < count; ++index) { + for (int pair = 0; pair < 2; ++pair) { + const QuadPts& dPts = dQuad[index][pair]; + SkDQuad d; + d.debugSet(dPts.fPts); + SkPoint c[3] = { {(float) d[0].fX, (float) d[0].fY}, {(float) d[1].fX, (float) d[1].fY}, + {(float) d[2].fX, (float) d[2].fY} }; + quadTest(c); + if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) { + return; + } + } + } +} + +DEF_TEST(QuadStrokerSet, reporter) { + quadSetTest(quadraticLines, quadraticLines_count); + quadSetTest(quadraticPoints, quadraticPoints_count); + quadSetTest(quadraticModEpsilonLines, quadraticModEpsilonLines_count); + quadPairSetTest(quadraticTests, quadraticTests_count); +} + +DEF_TEST(CubicStrokerSet, reporter) { + cubicSetTest(pointDegenerates, pointDegenerates_count); + cubicSetTest(notPointDegenerates, notPointDegenerates_count); + cubicSetTest(lines, lines_count); + cubicSetTest(notLines, notLines_count); + cubicSetTest(modEpsilonLines, modEpsilonLines_count); + cubicSetTest(lessEpsilonLines, lessEpsilonLines_count); + cubicSetTest(negEpsilonLines, negEpsilonLines_count); + cubicPairSetTest(tests, tests_count); +} + +static SkScalar unbounded(SkRandom& r) { + uint32_t val = r.nextU(); + return SkBits2Float(val); +} + +static SkScalar unboundedPos(SkRandom& r) { + uint32_t val = r.nextU() & 0x7fffffff; + return SkBits2Float(val); +} + +DEF_TEST(QuadStrokerUnbounded, reporter) { + SkRandom r; + SkPaint p; + p.setStyle(SkPaint::kStroke_Style); +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + int best = 0; + sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3); +#endif + skiatest::Timer timer; + for (int i = 0; i < 1000000; ++i) { + SkPath path, fill; + path.moveTo(unbounded(r), unbounded(r)); + path.quadTo(unbounded(r), unbounded(r), unbounded(r), unbounded(r)); + p.setStrokeWidth(unboundedPos(r)); + p.getFillPath(path, &fill); +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + if (best < gMaxRecursion[2]) { + if (FLAGS_veryVerbose) { + SkDebugf("\n%s quad=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[2], + p.getStrokeWidth()); + path.dumpHex(); + SkDebugf("fill:\n"); + fill.dumpHex(); + } + best = gMaxRecursion[2]; + } +#endif + if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) { + return; + } + } +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + if (FLAGS_veryVerbose) { + SkDebugf("\n%s max quad=%d\n", __FUNCTION__, best); + } +#endif +} + +DEF_TEST(CubicStrokerUnbounded, reporter) { + SkRandom r; + SkPaint p; + p.setStyle(SkPaint::kStroke_Style); +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + int bestTan = 0; + int bestCubic = 0; + sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3); +#endif + skiatest::Timer timer; + for (int i = 0; i < 1000000; ++i) { + SkPath path, fill; + path.moveTo(unbounded(r), unbounded(r)); + path.cubicTo(unbounded(r), unbounded(r), unbounded(r), unbounded(r), + unbounded(r), unbounded(r)); + p.setStrokeWidth(unboundedPos(r)); + p.getFillPath(path, &fill); + #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + if (bestTan < gMaxRecursion[0] || bestCubic < gMaxRecursion[1]) { + if (FLAGS_veryVerbose) { + SkDebugf("\n%s tan=%d cubic=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[0], + gMaxRecursion[1], p.getStrokeWidth()); + path.dumpHex(); + SkDebugf("fill:\n"); + fill.dumpHex(); + } + bestTan = SkTMax(bestTan, gMaxRecursion[0]); + bestCubic = SkTMax(bestCubic, gMaxRecursion[1]); + } + #endif + if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) { + return; + } + } +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + if (FLAGS_veryVerbose) { + SkDebugf("\n%s max tan=%d cubic=%d\n", __FUNCTION__, bestTan, bestCubic); + } +#endif +} + +DEF_TEST(QuadStrokerConstrained, reporter) { + SkRandom r; + SkPaint p; + p.setStyle(SkPaint::kStroke_Style); +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + int best = 0; + sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3); +#endif + skiatest::Timer timer; + for (int i = 0; i < 1000000; ++i) { + SkPath path, fill; + SkPoint quad[3]; + quad[0].fX = r.nextRangeF(0, 500); + quad[0].fY = r.nextRangeF(0, 500); + const SkScalar halfSquared = 0.5f * 0.5f; + do { + quad[1].fX = r.nextRangeF(0, 500); + quad[1].fY = r.nextRangeF(0, 500); + } while (quad[0].distanceToSqd(quad[1]) < halfSquared); + do { + quad[2].fX = r.nextRangeF(0, 500); + quad[2].fY = r.nextRangeF(0, 500); + } while (quad[0].distanceToSqd(quad[2]) < halfSquared + || quad[1].distanceToSqd(quad[2]) < halfSquared); + path.moveTo(quad[0].fX, quad[0].fY); + path.quadTo(quad[1].fX, quad[1].fY, quad[2].fX, quad[2].fY); + p.setStrokeWidth(r.nextRangeF(0, 500)); + p.getFillPath(path, &fill); +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + if (best < gMaxRecursion[2]) { + if (FLAGS_veryVerbose) { + SkDebugf("\n%s quad=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[2], + p.getStrokeWidth()); + path.dumpHex(); + SkDebugf("fill:\n"); + fill.dumpHex(); + } + best = gMaxRecursion[2]; + } +#endif + if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) { + return; + } + } +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + if (FLAGS_veryVerbose) { + SkDebugf("\n%s max quad=%d\n", __FUNCTION__, best); + } +#endif +} + +DEF_TEST(CubicStrokerConstrained, reporter) { + SkRandom r; + SkPaint p; + p.setStyle(SkPaint::kStroke_Style); +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + int bestTan = 0; + int bestCubic = 0; + sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3); +#endif + skiatest::Timer timer; + for (int i = 0; i < 1000000; ++i) { + SkPath path, fill; + SkPoint cubic[4]; + cubic[0].fX = r.nextRangeF(0, 500); + cubic[0].fY = r.nextRangeF(0, 500); + const SkScalar halfSquared = 0.5f * 0.5f; + do { + cubic[1].fX = r.nextRangeF(0, 500); + cubic[1].fY = r.nextRangeF(0, 500); + } while (cubic[0].distanceToSqd(cubic[1]) < halfSquared); + do { + cubic[2].fX = r.nextRangeF(0, 500); + cubic[2].fY = r.nextRangeF(0, 500); + } while ( cubic[0].distanceToSqd(cubic[2]) < halfSquared + || cubic[1].distanceToSqd(cubic[2]) < halfSquared); + do { + cubic[3].fX = r.nextRangeF(0, 500); + cubic[3].fY = r.nextRangeF(0, 500); + } while ( cubic[0].distanceToSqd(cubic[3]) < halfSquared + || cubic[1].distanceToSqd(cubic[3]) < halfSquared + || cubic[2].distanceToSqd(cubic[3]) < halfSquared); + path.moveTo(cubic[0].fX, cubic[0].fY); + path.cubicTo(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY); + p.setStrokeWidth(r.nextRangeF(0, 500)); + p.getFillPath(path, &fill); +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + if (bestTan < gMaxRecursion[0] || bestCubic < gMaxRecursion[1]) { + if (FLAGS_veryVerbose) { + SkDebugf("\n%s tan=%d cubic=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[0], + gMaxRecursion[1], p.getStrokeWidth()); + path.dumpHex(); + SkDebugf("fill:\n"); + fill.dumpHex(); + } + bestTan = SkTMax(bestTan, gMaxRecursion[0]); + bestCubic = SkTMax(bestCubic, gMaxRecursion[1]); + } +#endif + if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) { + return; + } + } +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + if (FLAGS_veryVerbose) { + SkDebugf("\n%s max tan=%d cubic=%d\n", __FUNCTION__, bestTan, bestCubic); + } +#endif +} + +DEF_TEST(QuadStrokerRange, reporter) { + SkRandom r; + SkPaint p; + p.setStyle(SkPaint::kStroke_Style); +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + int best = 0; + sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3); +#endif + skiatest::Timer timer; + for (int i = 0; i < 1000000; ++i) { + SkPath path, fill; + SkPoint quad[3]; + quad[0].fX = r.nextRangeF(0, 500); + quad[0].fY = r.nextRangeF(0, 500); + quad[1].fX = r.nextRangeF(0, 500); + quad[1].fY = r.nextRangeF(0, 500); + quad[2].fX = r.nextRangeF(0, 500); + quad[2].fY = r.nextRangeF(0, 500); + path.moveTo(quad[0].fX, quad[0].fY); + path.quadTo(quad[1].fX, quad[1].fY, quad[2].fX, quad[2].fY); + p.setStrokeWidth(r.nextRangeF(0, 500)); + p.getFillPath(path, &fill); +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + if (best < gMaxRecursion[2]) { + if (FLAGS_veryVerbose) { + SkDebugf("\n%s quad=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[2], + p.getStrokeWidth()); + path.dumpHex(); + SkDebugf("fill:\n"); + fill.dumpHex(); + } + best = gMaxRecursion[2]; + } +#endif + if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) { + return; + } + } +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + if (FLAGS_verbose) { + SkDebugf("\n%s max quad=%d\n", __FUNCTION__, best); + } +#endif +} + +DEF_TEST(CubicStrokerRange, reporter) { + SkRandom r; + SkPaint p; + p.setStyle(SkPaint::kStroke_Style); +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + int best[2] = { 0 }; + sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3); +#endif + skiatest::Timer timer; + for (int i = 0; i < 1000000; ++i) { + SkPath path, fill; + path.moveTo(r.nextRangeF(0, 500), r.nextRangeF(0, 500)); + path.cubicTo(r.nextRangeF(0, 500), r.nextRangeF(0, 500), r.nextRangeF(0, 500), + r.nextRangeF(0, 500), r.nextRangeF(0, 500), r.nextRangeF(0, 500)); + p.setStrokeWidth(r.nextRangeF(0, 100)); + p.getFillPath(path, &fill); +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + if (best[0] < gMaxRecursion[0] || best[1] < gMaxRecursion[1]) { + if (FLAGS_veryVerbose) { + SkDebugf("\n%s tan=%d cubic=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[0], + gMaxRecursion[1], p.getStrokeWidth()); + path.dumpHex(); + SkDebugf("fill:\n"); + fill.dumpHex(); + } + best[0] = SkTMax(best[0], gMaxRecursion[0]); + best[1] = SkTMax(best[1], gMaxRecursion[1]); + } +#endif + if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) { + return; + } + } +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + if (FLAGS_veryVerbose) { + SkDebugf("\n%s max tan=%d cubic=%d\n", __FUNCTION__, best[0], best[1]); + } +#endif +} + + +DEF_TEST(QuadStrokerOneOff, reporter) { +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3); +#endif + SkPaint p; + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SkDoubleToScalar(164.683548)); + + SkPath path, fill; +path.moveTo(SkBits2Float(0x43c99223), SkBits2Float(0x42b7417e)); +path.quadTo(SkBits2Float(0x4285d839), SkBits2Float(0x43ed6645), SkBits2Float(0x43c941c8), SkBits2Float(0x42b3ace3)); + p.getFillPath(path, &fill); + if (FLAGS_veryVerbose) { + SkDebugf("\n%s path\n", __FUNCTION__); + path.dump(); + SkDebugf("fill:\n"); + fill.dump(); + } +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + if (FLAGS_veryVerbose) { + SkDebugf("max quad=%d\n", gMaxRecursion[2]); + } +#endif +} + +DEF_TEST(CubicStrokerOneOff, reporter) { +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3); +#endif + SkPaint p; + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SkDoubleToScalar(42.835968)); + + SkPath path, fill; +path.moveTo(SkBits2Float(0x433f5370), SkBits2Float(0x43d1f4b3)); +path.cubicTo(SkBits2Float(0x4331cb76), SkBits2Float(0x43ea3340), SkBits2Float(0x4388f498), SkBits2Float(0x42f7f08d), SkBits2Float(0x43f1cd32), SkBits2Float(0x42802ec1)); + p.getFillPath(path, &fill); + if (FLAGS_veryVerbose) { + SkDebugf("\n%s path\n", __FUNCTION__); + path.dump(); + SkDebugf("fill:\n"); + fill.dump(); + } +#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION + if (FLAGS_veryVerbose) { + SkDebugf("max tan=%d cubic=%d\n", gMaxRecursion[0], gMaxRecursion[1]); + } +#endif +}
diff --git a/src/third_party/skia/tests/SubsetPath.cpp b/src/third_party/skia/tests/SubsetPath.cpp new file mode 100644 index 0000000..6a7660e --- /dev/null +++ b/src/third_party/skia/tests/SubsetPath.cpp
@@ -0,0 +1,241 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SubsetPath.h" +#include "SkMathPriv.h" + +SubsetPath::SubsetPath(const SkPath& path) + : fPath(path) + , fSubset(1) { +} + +int SubsetPath::range(int* end) const { + int leadingZero = SkCLZ(fSubset); + int parts = 1 << (31 - leadingZero); + int partIndex = fSubset - parts; + SkASSERT(partIndex >= 0); + int count = fSelected.count(); + int start = count * partIndex / parts; + *end = count * (partIndex + 1) / parts; + return start; +} + +bool SubsetPath::subset(bool testFailed, SkPath* sub) { + int start, end; + if (!testFailed) { + start = range(&end); + for (; start < end; ++start) { + fSelected[start] = true; + } + } + do { + do { + ++fSubset; + start = range(&end); + // SkDebugf("%d s=%d e=%d t=%d\n", fSubset, start, end, fTries); + if (end - start > 1) { + fTries = fSelected.count(); + } else if (end - start == 1) { + if (--fTries <= 0) { + return false; + } + } + } while (start == end); + } while (!fSelected[start]); + for (; start < end; ++start) { + fSelected[start] = false; + } +#if 1 + SkDebugf("selected: "); + for (int index = 0; index < fSelected.count(); ++index) { + SkDebugf("%c", fSelected[index] ? 'x' : '-'); + } +#endif + *sub = getSubsetPath(); + return true; +} + +SubsetContours::SubsetContours(const SkPath& path) + : SubsetPath(path) { + SkPath::RawIter iter(fPath); + uint8_t verb; + SkPoint pts[4]; + bool foundCurve = false; + int contourCount = 0; + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { + switch (verb) { + case SkPath::kMove_Verb: + break; + case SkPath::kLine_Verb: + case SkPath::kQuad_Verb: + case SkPath::kConic_Verb: + case SkPath::kCubic_Verb: + foundCurve = true; + break; + case SkPath::kClose_Verb: + ++contourCount; + foundCurve = false; + break; + default: + SkDEBUGFAIL("bad verb"); + return; + } + } + contourCount += foundCurve; + for (int index = 0; index < contourCount; ++index) { + *fSelected.append() = true; + } + fTries = contourCount; +} + +SkPath SubsetContours::getSubsetPath() const { + SkPath result; + result.setFillType(fPath.getFillType()); + if (!fSelected.count()) { + return result; + } + SkPath::RawIter iter(fPath); + uint8_t verb; + SkPoint pts[4]; + int contourCount = 0; + bool enabled = fSelected[0]; + bool addMoveTo = true; + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { + if (enabled && addMoveTo) { + result.moveTo(pts[0]); + addMoveTo = false; + } + switch (verb) { + case SkPath::kMove_Verb: + break; + case SkPath::kLine_Verb: + if (enabled) { + result.lineTo(pts[1]); + } + break; + case SkPath::kQuad_Verb: + if (enabled) { + result.quadTo(pts[1], pts[2]); + } + break; + case SkPath::kConic_Verb: + if (enabled) { + result.conicTo(pts[1], pts[2], iter.conicWeight()); + } + break; + case SkPath::kCubic_Verb: + if (enabled) { + result.cubicTo(pts[1], pts[2], pts[3]); + } + break; + case SkPath::kClose_Verb: + if (enabled) { + result.close(); + } + if (++contourCount >= fSelected.count()) { + break; + } + enabled = fSelected[contourCount]; + addMoveTo = true; + continue; + default: + SkDEBUGFAIL("bad verb"); + return result; + } + } + return result; +} + +SubsetVerbs::SubsetVerbs(const SkPath& path) + : SubsetPath(path) { + SkPath::RawIter iter(fPath); + uint8_t verb; + SkPoint pts[4]; + int verbCount = 0; + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { + switch (verb) { + case SkPath::kMove_Verb: + break; + case SkPath::kLine_Verb: + case SkPath::kQuad_Verb: + case SkPath::kConic_Verb: + case SkPath::kCubic_Verb: + ++verbCount; + break; + case SkPath::kClose_Verb: + break; + default: + SkDEBUGFAIL("bad verb"); + return; + } + } + for (int index = 0; index < verbCount; ++index) { + *fSelected.append() = true; + } + fTries = verbCount; +} + +SkPath SubsetVerbs::getSubsetPath() const { + SkPath result; + result.setFillType(fPath.getFillType()); + if (!fSelected.count()) { + return result; + } + SkPath::RawIter iter(fPath); + uint8_t verb; + SkPoint pts[4]; + int verbIndex = 0; + bool addMoveTo = true; + bool addLineTo = false; + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { + bool enabled = SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb + ? fSelected[verbIndex++] : false; + if (enabled) { + if (addMoveTo) { + result.moveTo(pts[0]); + addMoveTo = false; + } else if (addLineTo) { + result.lineTo(pts[0]); + addLineTo = false; + } + } + switch (verb) { + case SkPath::kMove_Verb: + break; + case SkPath::kLine_Verb: + if (enabled) { + result.lineTo(pts[1]); + } + break; + case SkPath::kQuad_Verb: + if (enabled) { + result.quadTo(pts[1], pts[2]); + } + break; + case SkPath::kConic_Verb: + if (enabled) { + result.conicTo(pts[1], pts[2], iter.conicWeight()); + } + break; + case SkPath::kCubic_Verb: + if (enabled) { + result.cubicTo(pts[1], pts[2], pts[3]); + } + break; + case SkPath::kClose_Verb: + result.close(); + addMoveTo = true; + addLineTo = false; + continue; + default: + SkDEBUGFAIL("bad verb"); + return result; + } + addLineTo = !enabled; + } + return result; +}
diff --git a/src/third_party/skia/tests/SubsetPath.h b/src/third_party/skia/tests/SubsetPath.h new file mode 100644 index 0000000..9e4d231 --- /dev/null +++ b/src/third_party/skia/tests/SubsetPath.h
@@ -0,0 +1,66 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SubsetPath_DEFINED +#define SubsetPath_DEFINED + +#include "SkPath.h" +#include "SkTDArray.h" + +/* Given a path, generate a the desired minimal subset of the original. + + This does a binary divide-and-conquer on the path, first splitting by + contours, and then by verbs. The caller passes whether the previous subset + behaved the same as the original. If not, the subset() call restores the + prior state before returning a new subset. + + If a path fails a local test, this reduces the data to the + minimal set that fails using a pattern like: + + bool testFailed = true; + SkPath minimal; + SubsetContours subsetContours(testPath); + while (subsetContours.subset(testFailed, &minimal)) { + testFailed = myPathTest(minimal); + } + testFailed = true; + SubsetVerbs subsetVerbs(testPath); + while (subsetVerbs.subset(testFailed, &minimal)) { + testFailed = myPathTest(minimal); + } +*/ + +class SubsetPath { +public: + SubsetPath(const SkPath& path); + virtual ~SubsetPath() {} + bool subset(bool testFailed, SkPath* sub); +protected: + int range(int* end) const; + virtual SkPath getSubsetPath() const = 0; + + const SkPath& fPath; + SkTDArray<bool> fSelected; + int fSubset; + int fTries; + +}; + +class SubsetContours : public SubsetPath { +public: + SubsetContours(const SkPath& path); +protected: + SkPath getSubsetPath() const override; +}; + +class SubsetVerbs : public SubsetPath { +public: + SubsetVerbs(const SkPath& path); +protected: + SkPath getSubsetPath() const override; +}; + +#endif
diff --git a/src/third_party/skia/tests/SurfaceSemaphoreTest.cpp b/src/third_party/skia/tests/SurfaceSemaphoreTest.cpp new file mode 100644 index 0000000..72e8e05 --- /dev/null +++ b/src/third_party/skia/tests/SurfaceSemaphoreTest.cpp
@@ -0,0 +1,175 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTypes.h" + +#if SK_SUPPORT_GPU +#include "GrContextFactory.h" +#include "GrTest.h" +#include "Test.h" + +#include "GrBackendSemaphore.h" +#include "GrBackendSurface.h" +#include "SkCanvas.h" +#include "SkSurface.h" + +#ifdef SK_VULKAN +#include "vk/GrVkTypes.h" +#endif + +static const int MAIN_W = 8, MAIN_H = 16; +static const int CHILD_W = 16, CHILD_H = 16; + +void check_pixels(skiatest::Reporter* reporter, const SkBitmap& bitmap) { + const uint32_t* canvasPixels = static_cast<const uint32_t*>(bitmap.getPixels()); + + bool failureFound = false; + SkPMColor expectedPixel; + for (int cy = 0; cy < CHILD_H && !failureFound; ++cy) { + for (int cx = 0; cx < CHILD_W && !failureFound; ++cx) { + SkPMColor canvasPixel = canvasPixels[cy * CHILD_W + cx]; + if (cy < CHILD_H / 2) { + if (cx < CHILD_W / 2) { + expectedPixel = 0xFF0000FF; // Red + } else { + expectedPixel = 0xFFFF0000; // Blue + } + } else { + expectedPixel = 0xFF00FF00; // Green + } + if (expectedPixel != canvasPixel) { + failureFound = true; + ERRORF(reporter, "Wrong color at %d, %d. Got 0x%08x when we expected 0x%08x", + cx, cy, canvasPixel, expectedPixel); + } + } + } +} + +void draw_child(skiatest::Reporter* reporter, + const sk_gpu_test::ContextInfo& childInfo, + const GrBackendObject& backendImage, + const GrBackendSemaphore& semaphore) { + GrBackendTexture backendTexture = GrTest::CreateBackendTexture(childInfo.backend(), + MAIN_W, MAIN_H, + kRGBA_8888_GrPixelConfig, + backendImage); + + childInfo.testContext()->makeCurrent(); + + const SkImageInfo childII = SkImageInfo::Make(CHILD_W, CHILD_H, kRGBA_8888_SkColorType, + kPremul_SkAlphaType); + + GrContext* childCtx = childInfo.grContext(); + sk_sp<SkSurface> childSurface(SkSurface::MakeRenderTarget(childCtx, SkBudgeted::kNo, + childII, 0, kTopLeft_GrSurfaceOrigin, + nullptr)); + + sk_sp<SkImage> childImage = SkImage::MakeFromTexture(childCtx, + backendTexture, + kTopLeft_GrSurfaceOrigin, + kPremul_SkAlphaType, + nullptr); + + SkCanvas* childCanvas = childSurface->getCanvas(); + childCanvas->clear(SK_ColorRED); + + childSurface->wait(1, &semaphore); + + childCanvas->drawImage(childImage, CHILD_W/2, 0); + + SkPaint paint; + paint.setColor(SK_ColorGREEN); + SkIRect rect = SkIRect::MakeLTRB(0, CHILD_H/2, CHILD_W, CHILD_H); + childCanvas->drawIRect(rect, paint); + + // read pixels + SkBitmap bitmap; + bitmap.allocPixels(childII); + childCanvas->readPixels(bitmap, 0, 0); + + check_pixels(reporter, bitmap); +} + +void surface_semaphore_test(skiatest::Reporter* reporter, + const sk_gpu_test::ContextInfo& mainInfo, + const sk_gpu_test::ContextInfo& childInfo1, + const sk_gpu_test::ContextInfo& childInfo2) { + GrContext* mainCtx = mainInfo.grContext(); + if (!mainCtx->caps()->fenceSyncSupport()) { + return; + } + + const SkImageInfo ii = SkImageInfo::Make(MAIN_W, MAIN_H, kRGBA_8888_SkColorType, + kPremul_SkAlphaType); + + sk_sp<SkSurface> mainSurface(SkSurface::MakeRenderTarget(mainCtx, SkBudgeted::kNo, + ii, 0, kTopLeft_GrSurfaceOrigin, + nullptr)); + SkCanvas* mainCanvas = mainSurface->getCanvas(); + mainCanvas->clear(SK_ColorBLUE); + + SkAutoTArray<GrBackendSemaphore> semaphores(2); + + mainSurface->flushAndSignalSemaphores(2, semaphores.get()); + + sk_sp<SkImage> mainImage = mainSurface->makeImageSnapshot(); + GrBackendObject backendImage = mainImage->getTextureHandle(false); + + draw_child(reporter, childInfo1, backendImage, semaphores[0]); + +#ifdef SK_VULKAN + if (kVulkan_GrBackend == mainInfo.backend()) { + // In Vulkan we need to make sure we are sending the correct VkImageLayout in with the + // backendImage. After the first child draw the layout gets changed to SHADER_READ, so + // we just manually set that here. + GrVkImageInfo* vkInfo = (GrVkImageInfo*)backendImage; + vkInfo->updateImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + } +#endif + + draw_child(reporter, childInfo2, backendImage, semaphores[1]); +} + +DEF_GPUTEST(SurfaceSemaphores, reporter, factory) { +#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_MAC) + static constexpr auto kNativeGLType = sk_gpu_test::GrContextFactory::kGL_ContextType; +#else + static constexpr auto kNativeGLType = sk_gpu_test::GrContextFactory::kGLES_ContextType; +#endif + + for (int typeInt = 0; typeInt < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++typeInt) { + sk_gpu_test::GrContextFactory::ContextType contextType = + (sk_gpu_test::GrContextFactory::ContextType) typeInt; + // Use "native" instead of explicitly trying OpenGL and OpenGL ES. Do not use GLES on + // desktop since tests do not account for not fixing http://skbug.com/2809 + if (contextType == sk_gpu_test::GrContextFactory::kGL_ContextType || + contextType == sk_gpu_test::GrContextFactory::kGLES_ContextType) { + if (contextType != kNativeGLType) { + continue; + } + } + sk_gpu_test::ContextInfo ctxInfo = factory->getContextInfo( + contextType, sk_gpu_test::GrContextFactory::ContextOverrides::kDisableNVPR); + if (!sk_gpu_test::GrContextFactory::IsRenderingContext(contextType)) { + continue; + } + skiatest::ReporterContext ctx( + reporter, SkString(sk_gpu_test::GrContextFactory::ContextTypeName(contextType))); + if (ctxInfo.grContext()) { + sk_gpu_test::ContextInfo child1 = factory->getSharedContextInfo(ctxInfo.grContext(), 0); + sk_gpu_test::ContextInfo child2 = factory->getSharedContextInfo(ctxInfo.grContext(), 1); + if (!child1.grContext() || !child2.grContext()) { + continue; + } + + surface_semaphore_test(reporter, ctxInfo, child1, child2); + } + } +} + +#endif
diff --git a/src/third_party/skia/tests/SurfaceTest.cpp b/src/third_party/skia/tests/SurfaceTest.cpp index 69c8b84..5249b3d 100644 --- a/src/third_party/skia/tests/SurfaceTest.cpp +++ b/src/third_party/skia/tests/SurfaceTest.cpp
@@ -5,237 +5,271 @@ * found in the LICENSE file. */ +#include <functional> #include "SkCanvas.h" +#include "SkColorSpace_Base.h" #include "SkData.h" -#include "SkDecodingImageGenerator.h" -#include "SkImageEncoder.h" +#include "SkDevice.h" +#include "SkImage_Base.h" +#include "SkOverdrawCanvas.h" +#include "SkPath.h" +#include "SkRegion.h" #include "SkRRect.h" #include "SkSurface.h" #include "SkUtils.h" #include "Test.h" #if SK_SUPPORT_GPU -#include "GrContextFactory.h" -#else -class GrContextFactory; -class GrContext; +#include "GrContext.h" +#include "GrContextPriv.h" +#include "GrRenderTargetContext.h" +#include "GrGpu.h" +#include "GrResourceProvider.h" +#include "GrTest.h" +#include <vector> #endif -enum SurfaceType { - kRaster_SurfaceType, - kRasterDirect_SurfaceType, - kGpu_SurfaceType, - kGpuScratch_SurfaceType, -}; +#include <initializer_list> -static void release_storage(void* pixels, void* context) { +static void release_direct_surface_storage(void* pixels, void* context) { SkASSERT(pixels == context); sk_free(pixels); } - -static SkSurface* createSurface(SurfaceType surfaceType, GrContext* context, - SkImageInfo* requestedInfo = NULL) { - static const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10); - +static sk_sp<SkSurface> create_surface(SkAlphaType at = kPremul_SkAlphaType, + SkImageInfo* requestedInfo = nullptr) { + const SkImageInfo info = SkImageInfo::MakeN32(10, 10, at); if (requestedInfo) { *requestedInfo = info; } - - switch (surfaceType) { - case kRaster_SurfaceType: - return SkSurface::NewRaster(info); - case kRasterDirect_SurfaceType: { - const size_t rowBytes = info.minRowBytes(); - void* storage = sk_malloc_throw(info.getSafeSize(rowBytes)); - return SkSurface::NewRasterDirectReleaseProc(info, storage, rowBytes, - release_storage, storage); - } - case kGpu_SurfaceType: -#if SK_SUPPORT_GPU - return context ? SkSurface::NewRenderTarget(context, info, 0, NULL) : NULL; -#endif - break; - case kGpuScratch_SurfaceType: -#if SK_SUPPORT_GPU - return context ? SkSurface::NewScratchRenderTarget(context, info) : NULL; -#endif - break; + return SkSurface::MakeRaster(info); +} +static sk_sp<SkSurface> create_direct_surface(SkAlphaType at = kPremul_SkAlphaType, + SkImageInfo* requestedInfo = nullptr) { + const SkImageInfo info = SkImageInfo::MakeN32(10, 10, at); + if (requestedInfo) { + *requestedInfo = info; } - return NULL; -} - -enum ImageType { - kRasterCopy_ImageType, - kRasterData_ImageType, - kGpu_ImageType, - kCodec_ImageType, -}; - -static void test_image(skiatest::Reporter* reporter) { - SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1); - size_t rowBytes = info.minRowBytes(); - size_t size = info.getSafeSize(rowBytes); - SkData* data = SkData::NewUninitialized(size); - - REPORTER_ASSERT(reporter, 1 == data->getRefCnt()); - SkImage* image = SkImage::NewRasterData(info, data, rowBytes); - REPORTER_ASSERT(reporter, 2 == data->getRefCnt()); - image->unref(); - REPORTER_ASSERT(reporter, 1 == data->getRefCnt()); - data->unref(); -} - -static SkImage* createImage(ImageType imageType, GrContext* context, - SkColor color) { - const SkPMColor pmcolor = SkPreMultiplyColor(color); - const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10); const size_t rowBytes = info.minRowBytes(); - const size_t size = rowBytes * info.height(); - - SkAutoTUnref<SkData> data(SkData::NewUninitialized(size)); - void* addr = data->writable_data(); - sk_memset32((SkPMColor*)addr, pmcolor, SkToInt(size >> 2)); - - switch (imageType) { - case kRasterCopy_ImageType: - return SkImage::NewRasterCopy(info, addr, rowBytes); - case kRasterData_ImageType: - return SkImage::NewRasterData(info, data, rowBytes); - case kGpu_ImageType: - return NULL; // TODO - case kCodec_ImageType: { - SkBitmap bitmap; - bitmap.installPixels(info, addr, rowBytes); - SkAutoTUnref<SkData> src( - SkImageEncoder::EncodeData(bitmap, SkImageEncoder::kPNG_Type, - 100)); - return SkImage::NewFromGenerator( - SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options())); - } - } - SkASSERT(false); - return NULL; + void* storage = sk_malloc_throw(info.getSafeSize(rowBytes)); + return SkSurface::MakeRasterDirectReleaseProc(info, storage, rowBytes, + release_direct_surface_storage, + storage); } +#if SK_SUPPORT_GPU +static sk_sp<SkSurface> create_gpu_surface(GrContext* context, SkAlphaType at = kPremul_SkAlphaType, + SkImageInfo* requestedInfo = nullptr) { + const SkImageInfo info = SkImageInfo::MakeN32(10, 10, at); + if (requestedInfo) { + *requestedInfo = info; + } + return SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info); +} +static sk_sp<SkSurface> create_gpu_scratch_surface(GrContext* context, + SkAlphaType at = kPremul_SkAlphaType, + SkImageInfo* requestedInfo = nullptr) { + const SkImageInfo info = SkImageInfo::MakeN32(10, 10, at); + if (requestedInfo) { + *requestedInfo = info; + } + return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info); +} +#endif -static void test_imagepeek(skiatest::Reporter* reporter) { - static const struct { - ImageType fType; - bool fPeekShouldSucceed; - } gRec[] = { - { kRasterCopy_ImageType, true }, - { kRasterData_ImageType, true }, - { kGpu_ImageType, false }, - { kCodec_ImageType, false }, - }; +DEF_TEST(SurfaceEmpty, reporter) { + const SkImageInfo info = SkImageInfo::Make(0, 0, kN32_SkColorType, kPremul_SkAlphaType); + REPORTER_ASSERT(reporter, nullptr == SkSurface::MakeRaster(info)); + REPORTER_ASSERT(reporter, nullptr == SkSurface::MakeRasterDirect(info, nullptr, 0)); +} +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceEmpty_Gpu, reporter, ctxInfo) { + const SkImageInfo info = SkImageInfo::Make(0, 0, kN32_SkColorType, kPremul_SkAlphaType); + REPORTER_ASSERT(reporter, nullptr == + SkSurface::MakeRenderTarget(ctxInfo.grContext(), SkBudgeted::kNo, info)); +} +#endif + +static void test_canvas_peek(skiatest::Reporter* reporter, + sk_sp<SkSurface>& surface, + const SkImageInfo& requestInfo, + bool expectPeekSuccess) { const SkColor color = SK_ColorRED; const SkPMColor pmcolor = SkPreMultiplyColor(color); + surface->getCanvas()->clear(color); - for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { - SkImageInfo info; - size_t rowBytes; + SkPixmap pmap; + bool success = surface->getCanvas()->peekPixels(&pmap); + REPORTER_ASSERT(reporter, expectPeekSuccess == success); - SkAutoTUnref<SkImage> image(createImage(gRec[i].fType, NULL, color)); - if (!image.get()) { - continue; // gpu may not be enabled - } - const void* addr = image->peekPixels(&info, &rowBytes); - bool success = SkToBool(addr); - REPORTER_ASSERT(reporter, gRec[i].fPeekShouldSucceed == success); - if (success) { - REPORTER_ASSERT(reporter, 10 == info.width()); - REPORTER_ASSERT(reporter, 10 == info.height()); - REPORTER_ASSERT(reporter, kN32_SkColorType == info.colorType()); - REPORTER_ASSERT(reporter, kPremul_SkAlphaType == info.alphaType() || - kOpaque_SkAlphaType == info.alphaType()); - REPORTER_ASSERT(reporter, info.minRowBytes() <= rowBytes); - REPORTER_ASSERT(reporter, pmcolor == *(const SkPMColor*)addr); + SkPixmap pmap2; + const void* addr2 = surface->peekPixels(&pmap2) ? pmap2.addr() : nullptr; + + if (success) { + REPORTER_ASSERT(reporter, requestInfo == pmap.info()); + REPORTER_ASSERT(reporter, requestInfo.minRowBytes() <= pmap.rowBytes()); + REPORTER_ASSERT(reporter, pmcolor == *pmap.addr32()); + + REPORTER_ASSERT(reporter, pmap.addr() == pmap2.addr()); + REPORTER_ASSERT(reporter, pmap.info() == pmap2.info()); + REPORTER_ASSERT(reporter, pmap.rowBytes() == pmap2.rowBytes()); + } else { + REPORTER_ASSERT(reporter, nullptr == addr2); + } +} +DEF_TEST(SurfaceCanvasPeek, reporter) { + for (auto& surface_func : { &create_surface, &create_direct_surface }) { + SkImageInfo requestInfo; + auto surface(surface_func(kPremul_SkAlphaType, &requestInfo)); + test_canvas_peek(reporter, surface, requestInfo, true); + } +} +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceCanvasPeek_Gpu, reporter, ctxInfo) { + for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) { + SkImageInfo requestInfo; + auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, &requestInfo)); + test_canvas_peek(reporter, surface, requestInfo, false); + } +} +#endif + +static void test_snapshot_alphatype(skiatest::Reporter* reporter, const sk_sp<SkSurface>& surface, + SkAlphaType expectedAlphaType) { + REPORTER_ASSERT(reporter, surface); + if (surface) { + sk_sp<SkImage> image(surface->makeImageSnapshot()); + REPORTER_ASSERT(reporter, image); + if (image) { + REPORTER_ASSERT(reporter, image->alphaType() == expectedAlphaType); } } } - -static void test_canvaspeek(skiatest::Reporter* reporter, - GrContextFactory* factory) { - static const struct { - SurfaceType fType; - bool fPeekShouldSucceed; - } gRec[] = { - { kRaster_SurfaceType, true }, - { kRasterDirect_SurfaceType, true }, +DEF_TEST(SurfaceSnapshotAlphaType, reporter) { + for (auto& surface_func : { &create_surface, &create_direct_surface }) { + for (auto& at: { kOpaque_SkAlphaType, kPremul_SkAlphaType, kUnpremul_SkAlphaType }) { + auto surface(surface_func(at, nullptr)); + test_snapshot_alphatype(reporter, surface, at); + } + } +} #if SK_SUPPORT_GPU - { kGpu_SurfaceType, false }, - { kGpuScratch_SurfaceType, false }, +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceSnapshotAlphaType_Gpu, reporter, ctxInfo) { + for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) { + // GPU doesn't support creating unpremul surfaces, so only test opaque + premul + for (auto& at : { kOpaque_SkAlphaType, kPremul_SkAlphaType }) { + auto surface(surface_func(ctxInfo.grContext(), at, nullptr)); + test_snapshot_alphatype(reporter, surface, at); + } + } +} #endif + +static GrBackendObject get_surface_backend_texture_handle( + SkSurface* s, SkSurface::BackendHandleAccess a) { + return s->getTextureHandle(a); +} +static GrBackendObject get_surface_backend_render_target_handle( + SkSurface* s, SkSurface::BackendHandleAccess a) { + GrBackendObject result; + if (!s->getRenderTargetHandle(&result, a)) { + return 0; + } + return result; +} + +static void test_backend_handle_access_copy_on_write( + skiatest::Reporter* reporter, SkSurface* surface, SkSurface::BackendHandleAccess mode, + GrBackendObject (*func)(SkSurface*, SkSurface::BackendHandleAccess)) { + GrBackendObject obj1 = func(surface, mode); + sk_sp<SkImage> snap1(surface->makeImageSnapshot()); + + GrBackendObject obj2 = func(surface, mode); + sk_sp<SkImage> snap2(surface->makeImageSnapshot()); + + // If the access mode triggers CoW, then the backend objects should reflect it. + REPORTER_ASSERT(reporter, (obj1 == obj2) == (snap1 == snap2)); +} +DEF_TEST(SurfaceBackendHandleAccessCopyOnWrite, reporter) { + const SkSurface::BackendHandleAccess accessModes[] = { + SkSurface::kFlushRead_BackendHandleAccess, + SkSurface::kFlushWrite_BackendHandleAccess, + SkSurface::kDiscardWrite_BackendHandleAccess, }; - - const SkColor color = SK_ColorRED; - const SkPMColor pmcolor = SkPreMultiplyColor(color); - - int cnt; -#if SK_SUPPORT_GPU - cnt = GrContextFactory::kGLContextTypeCnt; -#else - cnt = 1; -#endif - - for (int i= 0; i < cnt; ++i) { - GrContext* context = NULL; -#if SK_SUPPORT_GPU - GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i; - if (!GrContextFactory::IsRenderingGLContext(glCtxType)) { - continue; + for (auto& handle_access_func : + { &get_surface_backend_texture_handle, &get_surface_backend_render_target_handle }) { + for (auto& accessMode : accessModes) { + auto surface(create_surface()); + test_backend_handle_access_copy_on_write(reporter, surface.get(), accessMode, + handle_access_func); } - context = factory->get(glCtxType); - - if (NULL == context) { - continue; - } -#endif - for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { - SkImageInfo info, requestInfo; - size_t rowBytes; - - SkAutoTUnref<SkSurface> surface(createSurface(gRec[i].fType, context, - &requestInfo)); - surface->getCanvas()->clear(color); - - const void* addr = surface->getCanvas()->peekPixels(&info, &rowBytes); - bool success = SkToBool(addr); - REPORTER_ASSERT(reporter, gRec[i].fPeekShouldSucceed == success); - - SkImageInfo info2; - size_t rb2; - const void* addr2 = surface->peekPixels(&info2, &rb2); - - if (success) { - REPORTER_ASSERT(reporter, requestInfo == info); - REPORTER_ASSERT(reporter, requestInfo.minRowBytes() <= rowBytes); - REPORTER_ASSERT(reporter, pmcolor == *(const SkPMColor*)addr); - - REPORTER_ASSERT(reporter, addr2 == addr); - REPORTER_ASSERT(reporter, info2 == info); - REPORTER_ASSERT(reporter, rb2 == rowBytes); - } else { - REPORTER_ASSERT(reporter, NULL == addr2); + } +} +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceBackendHandleAccessCopyOnWrite_Gpu, reporter, ctxInfo) { + const SkSurface::BackendHandleAccess accessModes[] = { + SkSurface::kFlushRead_BackendHandleAccess, + SkSurface::kFlushWrite_BackendHandleAccess, + SkSurface::kDiscardWrite_BackendHandleAccess, + }; + for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) { + for (auto& handle_access_func : + { &get_surface_backend_texture_handle, &get_surface_backend_render_target_handle }) { + for (auto& accessMode : accessModes) { + auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr)); + test_backend_handle_access_copy_on_write(reporter, surface.get(), accessMode, + handle_access_func); } } } } +#endif -static void TestSurfaceCopyOnWrite(skiatest::Reporter* reporter, SurfaceType surfaceType, - GrContext* context) { - // Verify that the right canvas commands trigger a copy on write - SkSurface* surface = createSurface(surfaceType, context); - SkAutoTUnref<SkSurface> aur_surface(surface); +#if SK_SUPPORT_GPU + +static void test_backend_handle_unique_id( + skiatest::Reporter* reporter, SkSurface* surface, + GrBackendObject (*func)(SkSurface*, SkSurface::BackendHandleAccess)) { + sk_sp<SkImage> image0(surface->makeImageSnapshot()); + GrBackendObject obj = func(surface, SkSurface::kFlushRead_BackendHandleAccess); + REPORTER_ASSERT(reporter, obj != 0); + sk_sp<SkImage> image1(surface->makeImageSnapshot()); + // just read access should not affect the snapshot + REPORTER_ASSERT(reporter, image0->uniqueID() == image1->uniqueID()); + + obj = func(surface, SkSurface::kFlushWrite_BackendHandleAccess); + REPORTER_ASSERT(reporter, obj != 0); + sk_sp<SkImage> image2(surface->makeImageSnapshot()); + // expect a new image, since we claimed we would write + REPORTER_ASSERT(reporter, image0->uniqueID() != image2->uniqueID()); + + obj = func(surface, SkSurface::kDiscardWrite_BackendHandleAccess); + REPORTER_ASSERT(reporter, obj != 0); + sk_sp<SkImage> image3(surface->makeImageSnapshot()); + // expect a new(er) image, since we claimed we would write + REPORTER_ASSERT(reporter, image0->uniqueID() != image3->uniqueID()); + REPORTER_ASSERT(reporter, image2->uniqueID() != image3->uniqueID()); +} +// No CPU test. +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceBackendHandleAccessIDs_Gpu, reporter, ctxInfo) { + for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) { + for (auto& test_func : { &test_backend_handle_unique_id }) { + for (auto& handle_access_func : + { &get_surface_backend_texture_handle, &get_surface_backend_render_target_handle}) { + auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr)); + test_func(reporter, surface.get(), handle_access_func); + } + } + } +} +#endif + +// Verify that the right canvas commands trigger a copy on write. +static void test_copy_on_write(skiatest::Reporter* reporter, SkSurface* surface) { SkCanvas* canvas = surface->getCanvas(); const SkRect testRect = SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(4), SkIntToScalar(5)); - SkMatrix testMatrix; - testMatrix.reset(); - testMatrix.setScale(SkIntToScalar(2), SkIntToScalar(3)); - SkPath testPath; testPath.addRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(2), SkIntToScalar(1))); @@ -279,11 +313,11 @@ #define EXPECT_COPY_ON_WRITE(command) \ { \ - SkImage* imageBefore = surface->newImageSnapshot(); \ - SkAutoTUnref<SkImage> aur_before(imageBefore); \ + sk_sp<SkImage> imageBefore = surface->makeImageSnapshot(); \ + sk_sp<SkImage> aur_before(imageBefore); \ canvas-> command ; \ - SkImage* imageAfter = surface->newImageSnapshot(); \ - SkAutoTUnref<SkImage> aur_after(imageAfter); \ + sk_sp<SkImage> imageAfter = surface->makeImageSnapshot(); \ + sk_sp<SkImage> aur_after(imageAfter); \ REPORTER_ASSERT(reporter, imageBefore != imageAfter); \ } @@ -296,179 +330,633 @@ EXPECT_COPY_ON_WRITE(drawRRect(testRRect, testPaint)) EXPECT_COPY_ON_WRITE(drawPath(testPath, testPaint)) EXPECT_COPY_ON_WRITE(drawBitmap(testBitmap, 0, 0)) - EXPECT_COPY_ON_WRITE(drawBitmapRect(testBitmap, NULL, testRect)) - EXPECT_COPY_ON_WRITE(drawBitmapMatrix(testBitmap, testMatrix, NULL)) - EXPECT_COPY_ON_WRITE(drawBitmapNine(testBitmap, testIRect, testRect, NULL)) - EXPECT_COPY_ON_WRITE(drawSprite(testBitmap, 0, 0, NULL)) - EXPECT_COPY_ON_WRITE(drawText(testText.c_str(), testText.size(), 0, 1, testPaint)) + EXPECT_COPY_ON_WRITE(drawBitmapRect(testBitmap, testRect, nullptr)) + EXPECT_COPY_ON_WRITE(drawBitmapNine(testBitmap, testIRect, testRect, nullptr)) + EXPECT_COPY_ON_WRITE(drawString(testText, 0, 1, testPaint)) EXPECT_COPY_ON_WRITE(drawPosText(testText.c_str(), testText.size(), testPoints2, \ testPaint)) - EXPECT_COPY_ON_WRITE(drawTextOnPath(testText.c_str(), testText.size(), testPath, NULL, \ + EXPECT_COPY_ON_WRITE(drawTextOnPath(testText.c_str(), testText.size(), testPath, nullptr, \ testPaint)) } +DEF_TEST(SurfaceCopyOnWrite, reporter) { + test_copy_on_write(reporter, create_surface().get()); +} +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceCopyOnWrite_Gpu, reporter, ctxInfo) { + for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) { + auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr)); + test_copy_on_write(reporter, surface.get()); + } +} +#endif -static void TestSurfaceWritableAfterSnapshotRelease(skiatest::Reporter* reporter, - SurfaceType surfaceType, - GrContext* context) { +static void test_writable_after_snapshot_release(skiatest::Reporter* reporter, + SkSurface* surface) { // This test succeeds by not triggering an assertion. // The test verifies that the surface remains writable (usable) after // acquiring and releasing a snapshot without triggering a copy on write. - SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context)); SkCanvas* canvas = surface->getCanvas(); canvas->clear(1); - surface->newImageSnapshot()->unref(); // Create and destroy SkImage + surface->makeImageSnapshot(); // Create and destroy SkImage canvas->clear(2); // Must not assert internally } +DEF_TEST(SurfaceWriteableAfterSnapshotRelease, reporter) { + test_writable_after_snapshot_release(reporter, create_surface().get()); +} +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceWriteableAfterSnapshotRelease_Gpu, reporter, ctxInfo) { + for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) { + auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr)); + test_writable_after_snapshot_release(reporter, surface.get()); + } +} +#endif #if SK_SUPPORT_GPU -static void TestSurfaceInCache(skiatest::Reporter* reporter, - SurfaceType surfaceType, - GrContext* context) { - context->freeGpuResources(); - int resourceCount; - - context->getResourceCacheUsage(&resourceCount, NULL); - REPORTER_ASSERT(reporter, 0 == resourceCount); - SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context)); - // Note: the stencil buffer is always cached, so kGpu_SurfaceType uses - // one cached resource, and kGpuScratch_SurfaceType uses two. - int expectedCachedResources = surfaceType == kGpuScratch_SurfaceType ? 2 : 1; - context->getResourceCacheUsage(&resourceCount, NULL); - REPORTER_ASSERT(reporter, expectedCachedResources == resourceCount); - - // Verify that all the cached resources are locked in cache. - context->freeGpuResources(); - context->getResourceCacheUsage(&resourceCount, NULL); - REPORTER_ASSERT(reporter, expectedCachedResources == resourceCount); - - // Verify that all the cached resources are unlocked upon surface release - surface.reset(0); - context->freeGpuResources(); - context->getResourceCacheUsage(&resourceCount, NULL); - REPORTER_ASSERT(reporter, 0 == resourceCount); -} - -static void Test_crbug263329(skiatest::Reporter* reporter, - SurfaceType surfaceType, - GrContext* context) { +static void test_crbug263329(skiatest::Reporter* reporter, + SkSurface* surface1, + SkSurface* surface2) { // This is a regression test for crbug.com/263329 // Bug was caused by onCopyOnWrite releasing the old surface texture // back to the scratch texture pool even though the texture is used // by and active SkImage_Gpu. - SkAutoTUnref<SkSurface> surface1(createSurface(surfaceType, context)); - SkAutoTUnref<SkSurface> surface2(createSurface(surfaceType, context)); SkCanvas* canvas1 = surface1->getCanvas(); SkCanvas* canvas2 = surface2->getCanvas(); canvas1->clear(1); - SkAutoTUnref<SkImage> image1(surface1->newImageSnapshot()); + sk_sp<SkImage> image1(surface1->makeImageSnapshot()); // Trigger copy on write, new backing is a scratch texture canvas1->clear(2); - SkAutoTUnref<SkImage> image2(surface1->newImageSnapshot()); + sk_sp<SkImage> image2(surface1->makeImageSnapshot()); // Trigger copy on write, old backing should not be returned to scratch // pool because it is held by image2 canvas1->clear(3); canvas2->clear(4); - SkAutoTUnref<SkImage> image3(surface2->newImageSnapshot()); + sk_sp<SkImage> image3(surface2->makeImageSnapshot()); // Trigger copy on write on surface2. The new backing store should not // be recycling a texture that is held by an existing image. canvas2->clear(5); - SkAutoTUnref<SkImage> image4(surface2->newImageSnapshot()); - REPORTER_ASSERT(reporter, image4->getTexture() != image3->getTexture()); + sk_sp<SkImage> image4(surface2->makeImageSnapshot()); + REPORTER_ASSERT(reporter, as_IB(image4)->getTexture() != as_IB(image3)->getTexture()); // The following assertion checks crbug.com/263329 - REPORTER_ASSERT(reporter, image4->getTexture() != image2->getTexture()); - REPORTER_ASSERT(reporter, image4->getTexture() != image1->getTexture()); - REPORTER_ASSERT(reporter, image3->getTexture() != image2->getTexture()); - REPORTER_ASSERT(reporter, image3->getTexture() != image1->getTexture()); - REPORTER_ASSERT(reporter, image2->getTexture() != image1->getTexture()); + REPORTER_ASSERT(reporter, as_IB(image4)->getTexture() != as_IB(image2)->getTexture()); + REPORTER_ASSERT(reporter, as_IB(image4)->getTexture() != as_IB(image1)->getTexture()); + REPORTER_ASSERT(reporter, as_IB(image3)->getTexture() != as_IB(image2)->getTexture()); + REPORTER_ASSERT(reporter, as_IB(image3)->getTexture() != as_IB(image1)->getTexture()); + REPORTER_ASSERT(reporter, as_IB(image2)->getTexture() != as_IB(image1)->getTexture()); } - -static void TestGetTexture(skiatest::Reporter* reporter, - SurfaceType surfaceType, - GrContext* context) { - SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context)); - SkAutoTUnref<SkImage> image(surface->newImageSnapshot()); - GrTexture* texture = image->getTexture(); - if (surfaceType == kGpu_SurfaceType || surfaceType == kGpuScratch_SurfaceType) { - REPORTER_ASSERT(reporter, texture); - REPORTER_ASSERT(reporter, 0 != texture->getTextureHandle()); - } else { - REPORTER_ASSERT(reporter, NULL == texture); +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceCRBug263329_Gpu, reporter, ctxInfo) { + for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) { + auto surface1(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr)); + auto surface2(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr)); + test_crbug263329(reporter, surface1.get(), surface2.get()); } - surface->notifyContentWillChange(SkSurface::kDiscard_ContentChangeMode); - REPORTER_ASSERT(reporter, image->getTexture() == texture); } #endif -static void TestSurfaceNoCanvas(skiatest::Reporter* reporter, - SurfaceType surfaceType, - GrContext* context, - SkSurface::ContentChangeMode mode) { - // Verifies the robustness of SkSurface for handling use cases where calls - // are made before a canvas is created. - { - // Test passes by not asserting - SkSurface* surface = createSurface(surfaceType, context); - SkAutoTUnref<SkSurface> aur_surface(surface); - surface->notifyContentWillChange(mode); - SkDEBUGCODE(surface->validate();) - } - { - SkSurface* surface = createSurface(surfaceType, context); - SkAutoTUnref<SkSurface> aur_surface(surface); - SkImage* image1 = surface->newImageSnapshot(); - SkAutoTUnref<SkImage> aur_image1(image1); - SkDEBUGCODE(image1->validate();) - SkDEBUGCODE(surface->validate();) - surface->notifyContentWillChange(mode); - SkDEBUGCODE(image1->validate();) - SkDEBUGCODE(surface->validate();) - SkImage* image2 = surface->newImageSnapshot(); - SkAutoTUnref<SkImage> aur_image2(image2); - SkDEBUGCODE(image2->validate();) - SkDEBUGCODE(surface->validate();) - REPORTER_ASSERT(reporter, image1 != image2); - } - +DEF_TEST(SurfaceGetTexture, reporter) { + auto surface(create_surface()); + sk_sp<SkImage> image(surface->makeImageSnapshot()); + REPORTER_ASSERT(reporter, !as_IB(image)->isTextureBacked()); + surface->notifyContentWillChange(SkSurface::kDiscard_ContentChangeMode); + REPORTER_ASSERT(reporter, !as_IB(image)->isTextureBacked()); } +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfacepeekTexture_Gpu, reporter, ctxInfo) { + for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) { + auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr)); + sk_sp<SkImage> image(surface->makeImageSnapshot()); -DEF_GPUTEST(Surface, reporter, factory) { - test_image(reporter); - - TestSurfaceCopyOnWrite(reporter, kRaster_SurfaceType, NULL); - TestSurfaceWritableAfterSnapshotRelease(reporter, kRaster_SurfaceType, NULL); - TestSurfaceNoCanvas(reporter, kRaster_SurfaceType, NULL, SkSurface::kDiscard_ContentChangeMode); - TestSurfaceNoCanvas(reporter, kRaster_SurfaceType, NULL, SkSurface::kRetain_ContentChangeMode); - - test_imagepeek(reporter); - test_canvaspeek(reporter, factory); + REPORTER_ASSERT(reporter, as_IB(image)->isTextureBacked()); + GrBackendObject textureHandle = image->getTextureHandle(false); + REPORTER_ASSERT(reporter, 0 != textureHandle); + surface->notifyContentWillChange(SkSurface::kDiscard_ContentChangeMode); + REPORTER_ASSERT(reporter, as_IB(image)->isTextureBacked()); + REPORTER_ASSERT(reporter, textureHandle == image->getTextureHandle(false)); + } +} +#endif #if SK_SUPPORT_GPU - TestGetTexture(reporter, kRaster_SurfaceType, NULL); - if (factory) { - for (int i= 0; i < GrContextFactory::kGLContextTypeCnt; ++i) { - GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i; - if (!GrContextFactory::IsRenderingGLContext(glCtxType)) { - continue; - } - GrContext* context = factory->get(glCtxType); - if (context) { - TestSurfaceInCache(reporter, kGpu_SurfaceType, context); - TestSurfaceInCache(reporter, kGpuScratch_SurfaceType, context); - Test_crbug263329(reporter, kGpu_SurfaceType, context); - Test_crbug263329(reporter, kGpuScratch_SurfaceType, context); - TestSurfaceCopyOnWrite(reporter, kGpu_SurfaceType, context); - TestSurfaceCopyOnWrite(reporter, kGpuScratch_SurfaceType, context); - TestSurfaceWritableAfterSnapshotRelease(reporter, kGpu_SurfaceType, context); - TestSurfaceWritableAfterSnapshotRelease(reporter, kGpuScratch_SurfaceType, context); - TestSurfaceNoCanvas(reporter, kGpu_SurfaceType, context, SkSurface::kDiscard_ContentChangeMode); - TestSurfaceNoCanvas(reporter, kGpuScratch_SurfaceType, context, SkSurface::kDiscard_ContentChangeMode); - TestSurfaceNoCanvas(reporter, kGpu_SurfaceType, context, SkSurface::kRetain_ContentChangeMode); - TestSurfaceNoCanvas(reporter, kGpuScratch_SurfaceType, context, SkSurface::kRetain_ContentChangeMode); - TestGetTexture(reporter, kGpu_SurfaceType, context); - TestGetTexture(reporter, kGpuScratch_SurfaceType, context); +#include "GrGpuResourcePriv.h" +#include "SkGpuDevice.h" +#include "SkImage_Gpu.h" +#include "SkSurface_Gpu.h" + +static SkBudgeted is_budgeted(const sk_sp<SkSurface>& surf) { + SkSurface_Gpu* gsurf = (SkSurface_Gpu*)surf.get(); + + GrRenderTargetProxy* proxy = gsurf->getDevice()->accessRenderTargetContext() + ->asRenderTargetProxy(); + return proxy->isBudgeted(); +} + +static SkBudgeted is_budgeted(SkImage* image) { + return ((SkImage_Gpu*)image)->peekProxy()->isBudgeted(); +} + +static SkBudgeted is_budgeted(const sk_sp<SkImage> image) { + return is_budgeted(image.get()); +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceBudget, reporter, ctxInfo) { + SkImageInfo info = SkImageInfo::MakeN32Premul(8,8); + for (auto budgeted : { SkBudgeted::kNo, SkBudgeted::kYes }) { + auto surface(SkSurface::MakeRenderTarget(ctxInfo.grContext(), budgeted, info)); + SkASSERT(surface); + REPORTER_ASSERT(reporter, budgeted == is_budgeted(surface)); + + sk_sp<SkImage> image(surface->makeImageSnapshot()); + + // Initially the image shares a texture with the surface, and the + // the budgets should always match. + REPORTER_ASSERT(reporter, budgeted == is_budgeted(surface)); + REPORTER_ASSERT(reporter, budgeted == is_budgeted(image)); + + // Now trigger copy-on-write + surface->getCanvas()->clear(SK_ColorBLUE); + + // They don't share a texture anymore but the budgets should still match. + REPORTER_ASSERT(reporter, budgeted == is_budgeted(surface)); + REPORTER_ASSERT(reporter, budgeted == is_budgeted(image)); + } +} +#endif + +static void test_no_canvas1(skiatest::Reporter* reporter, + SkSurface* surface, + SkSurface::ContentChangeMode mode) { + // Test passes by not asserting + surface->notifyContentWillChange(mode); + SkDEBUGCODE(surface->validate();) +} +static void test_no_canvas2(skiatest::Reporter* reporter, + SkSurface* surface, + SkSurface::ContentChangeMode mode) { + // Verifies the robustness of SkSurface for handling use cases where calls + // are made before a canvas is created. + sk_sp<SkImage> image1 = surface->makeImageSnapshot(); + sk_sp<SkImage> aur_image1(image1); + SkDEBUGCODE(image1->validate();) + SkDEBUGCODE(surface->validate();) + surface->notifyContentWillChange(mode); + SkDEBUGCODE(image1->validate();) + SkDEBUGCODE(surface->validate();) + sk_sp<SkImage> image2 = surface->makeImageSnapshot(); + sk_sp<SkImage> aur_image2(image2); + SkDEBUGCODE(image2->validate();) + SkDEBUGCODE(surface->validate();) + REPORTER_ASSERT(reporter, image1 != image2); +} +DEF_TEST(SurfaceNoCanvas, reporter) { + SkSurface::ContentChangeMode modes[] = + { SkSurface::kDiscard_ContentChangeMode, SkSurface::kRetain_ContentChangeMode}; + for (auto& test_func : { &test_no_canvas1, &test_no_canvas2 }) { + for (auto& mode : modes) { + test_func(reporter, create_surface().get(), mode); + } + } +} +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceNoCanvas_Gpu, reporter, ctxInfo) { + SkSurface::ContentChangeMode modes[] = + { SkSurface::kDiscard_ContentChangeMode, SkSurface::kRetain_ContentChangeMode}; + for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) { + for (auto& test_func : { &test_no_canvas1, &test_no_canvas2 }) { + for (auto& mode : modes) { + auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr)); + test_func(reporter, surface.get(), mode); } } } +} #endif + +static void check_rowbytes_remain_consistent(SkSurface* surface, skiatest::Reporter* reporter) { + SkPixmap surfacePM; + REPORTER_ASSERT(reporter, surface->peekPixels(&surfacePM)); + + sk_sp<SkImage> image(surface->makeImageSnapshot()); + SkPixmap pm; + REPORTER_ASSERT(reporter, image->peekPixels(&pm)); + + REPORTER_ASSERT(reporter, surfacePM.rowBytes() == pm.rowBytes()); + + // trigger a copy-on-write + surface->getCanvas()->drawPaint(SkPaint()); + sk_sp<SkImage> image2(surface->makeImageSnapshot()); + REPORTER_ASSERT(reporter, image->uniqueID() != image2->uniqueID()); + + SkPixmap pm2; + REPORTER_ASSERT(reporter, image2->peekPixels(&pm2)); + REPORTER_ASSERT(reporter, pm2.rowBytes() == pm.rowBytes()); +} + +DEF_TEST(surface_rowbytes, reporter) { + const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100); + + auto surf0(SkSurface::MakeRaster(info)); + check_rowbytes_remain_consistent(surf0.get(), reporter); + + // specify a larger rowbytes + auto surf1(SkSurface::MakeRaster(info, 500, nullptr)); + check_rowbytes_remain_consistent(surf1.get(), reporter); + + // Try some illegal rowByte values + auto s = SkSurface::MakeRaster(info, 396, nullptr); // needs to be at least 400 + REPORTER_ASSERT(reporter, nullptr == s); + s = SkSurface::MakeRaster(info, 1 << 30, nullptr); // allocation to large + REPORTER_ASSERT(reporter, nullptr == s); +} + +DEF_TEST(surface_raster_zeroinitialized, reporter) { + sk_sp<SkSurface> s(SkSurface::MakeRasterN32Premul(100, 100)); + SkPixmap pixmap; + REPORTER_ASSERT(reporter, s->peekPixels(&pixmap)); + + for (int i = 0; i < pixmap.info().width(); ++i) { + for (int j = 0; j < pixmap.info().height(); ++j) { + REPORTER_ASSERT(reporter, *pixmap.addr32(i, j) == 0); + } + } +} + +#if SK_SUPPORT_GPU +static sk_sp<SkSurface> create_gpu_surface_backend_texture( + GrContext* context, int sampleCnt, uint32_t color, GrBackendObject* outTexture) { + const int kWidth = 10; + const int kHeight = 10; + std::unique_ptr<uint32_t[]> pixels(new uint32_t[kWidth * kHeight]); + sk_memset32(pixels.get(), color, kWidth * kHeight); + + GrBackendObject backendHandle = context->getGpu()->createTestingOnlyBackendTexture( + pixels.get(), kWidth, kHeight, kRGBA_8888_GrPixelConfig, true); + + GrBackendTexture backendTex = GrTest::CreateBackendTexture(context->contextPriv().getBackend(), + kWidth, + kHeight, + kRGBA_8888_GrPixelConfig, + backendHandle); + + sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTexture(context, backendTex, + kDefault_GrSurfaceOrigin, sampleCnt, + nullptr, nullptr); + if (!surface) { + context->getGpu()->deleteTestingOnlyBackendTexture(backendHandle); + return nullptr; + } + *outTexture = backendHandle; + return surface; +} + +static sk_sp<SkSurface> create_gpu_surface_backend_texture_as_render_target( + GrContext* context, int sampleCnt, uint32_t color, GrBackendObject* outTexture) { + const int kWidth = 10; + const int kHeight = 10; + std::unique_ptr<uint32_t[]> pixels(new uint32_t[kWidth * kHeight]); + sk_memset32(pixels.get(), color, kWidth * kHeight); + + GrBackendObject backendHandle = context->getGpu()->createTestingOnlyBackendTexture( + pixels.get(), kWidth, kHeight, kRGBA_8888_GrPixelConfig, true); + + GrBackendTexture backendTex = GrTest::CreateBackendTexture(context->contextPriv().getBackend(), + kWidth, + kHeight, + kRGBA_8888_GrPixelConfig, + backendHandle); + sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTextureAsRenderTarget( + context, backendTex, kDefault_GrSurfaceOrigin, sampleCnt, nullptr, nullptr); + + if (!surface) { + context->getGpu()->deleteTestingOnlyBackendTexture(backendHandle); + return nullptr; + } + *outTexture = backendHandle; + return surface; +} + +static void test_surface_clear(skiatest::Reporter* reporter, sk_sp<SkSurface> surface, + std::function<sk_sp<GrSurfaceContext>(SkSurface*)> grSurfaceGetter, + uint32_t expectedValue) { + if (!surface) { + ERRORF(reporter, "Could not create GPU SkSurface."); + return; + } + int w = surface->width(); + int h = surface->height(); + std::unique_ptr<uint32_t[]> pixels(new uint32_t[w * h]); + sk_memset32(pixels.get(), ~expectedValue, w * h); + + sk_sp<GrSurfaceContext> grSurfaceContext(grSurfaceGetter(surface.get())); + if (!grSurfaceContext) { + ERRORF(reporter, "Could access render target of GPU SkSurface."); + return; + } + surface.reset(); + + SkImageInfo ii = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kPremul_SkAlphaType); + grSurfaceContext->readPixels(ii, pixels.get(), 0, 0, 0); + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + uint32_t pixel = pixels.get()[y * w + x]; + if (pixel != expectedValue) { + SkString msg; + if (expectedValue) { + msg = "SkSurface should have left render target unmodified"; + } else { + msg = "SkSurface should have cleared the render target"; + } + ERRORF(reporter, + "%s but read 0x%08x (instead of 0x%08x) at %x,%d", msg.c_str(), pixel, + expectedValue, x, y); + return; + } + } + } +} + +DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SurfaceClear_Gpu, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + + std::function<sk_sp<GrSurfaceContext>(SkSurface*)> grSurfaceContextGetters[] = { + [] (SkSurface* s){ + return sk_ref_sp(s->getCanvas()->internal_private_accessTopLayerRenderTargetContext()); + }, + [] (SkSurface* s){ + sk_sp<SkImage> i(s->makeImageSnapshot()); + SkImage_Gpu* gpuImage = (SkImage_Gpu *) as_IB(i); + sk_sp<GrTextureProxy> proxy = gpuImage->asTextureProxyRef(); + GrContext* context = gpuImage->context(); + return context->contextPriv().makeWrappedSurfaceContext(std::move(proxy), + gpuImage->refColorSpace()); + } + }; + + for (auto grSurfaceGetter : grSurfaceContextGetters) { + // Test that non-wrapped RTs are created clear. + for (auto& surface_func : {&create_gpu_surface, &create_gpu_scratch_surface}) { + auto surface = surface_func(context, kPremul_SkAlphaType, nullptr); + test_surface_clear(reporter, surface, grSurfaceGetter, 0x0); + } + // Wrapped RTs are *not* supposed to clear (to allow client to partially update a surface). + const uint32_t kOrigColor = 0xABABABAB; + for (auto& surfaceFunc : {&create_gpu_surface_backend_texture, + &create_gpu_surface_backend_texture_as_render_target}) { + GrBackendObject textureObject; + auto surface = surfaceFunc(context, 0, kOrigColor, &textureObject); + test_surface_clear(reporter, surface, grSurfaceGetter, kOrigColor); + surface.reset(); + context->getGpu()->deleteTestingOnlyBackendTexture(textureObject); + } + } +} + +static void test_surface_draw_partially( + skiatest::Reporter* reporter, sk_sp<SkSurface> surface, uint32_t origColor) { + const int kW = surface->width(); + const int kH = surface->height(); + SkPaint paint; + const SkColor kRectColor = ~origColor | 0xFF000000; + paint.setColor(kRectColor); + surface->getCanvas()->drawRect(SkRect::MakeWH(SkIntToScalar(kW), SkIntToScalar(kH)/2), + paint); + std::unique_ptr<uint32_t[]> pixels(new uint32_t[kW * kH]); + sk_memset32(pixels.get(), ~origColor, kW * kH); + // Read back RGBA to avoid format conversions that may not be supported on all platforms. + SkImageInfo readInfo = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType); + SkAssertResult(surface->readPixels(readInfo, pixels.get(), kW * sizeof(uint32_t), 0, 0)); + bool stop = false; + SkPMColor origColorPM = SkPackARGB_as_RGBA((origColor >> 24 & 0xFF), + (origColor >> 0 & 0xFF), + (origColor >> 8 & 0xFF), + (origColor >> 16 & 0xFF)); + SkPMColor rectColorPM = SkPackARGB_as_RGBA((kRectColor >> 24 & 0xFF), + (kRectColor >> 16 & 0xFF), + (kRectColor >> 8 & 0xFF), + (kRectColor >> 0 & 0xFF)); + for (int y = 0; y < kH/2 && !stop; ++y) { + for (int x = 0; x < kW && !stop; ++x) { + REPORTER_ASSERT(reporter, rectColorPM == pixels[x + y * kW]); + if (rectColorPM != pixels[x + y * kW]) { + stop = true; + } + } + } + stop = false; + for (int y = kH/2; y < kH && !stop; ++y) { + for (int x = 0; x < kW && !stop; ++x) { + REPORTER_ASSERT(reporter, origColorPM == pixels[x + y * kW]); + if (origColorPM != pixels[x + y * kW]) { + stop = true; + } + } + } +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfacePartialDraw_Gpu, reporter, ctxInfo) { + GrGpu* gpu = ctxInfo.grContext()->getGpu(); + if (!gpu) { + return; + } + static const uint32_t kOrigColor = 0xFFAABBCC; + + for (auto& surfaceFunc : {&create_gpu_surface_backend_texture, + &create_gpu_surface_backend_texture_as_render_target}) { + // Validate that we can draw to the canvas and that the original texture color is + // preserved in pixels that aren't rendered to via the surface. + // This works only for non-multisampled case. + GrBackendObject textureObject; + auto surface = surfaceFunc(ctxInfo.grContext(), 0, kOrigColor, &textureObject); + if (surface) { + test_surface_draw_partially(reporter, surface, kOrigColor); + surface.reset(); + gpu->deleteTestingOnlyBackendTexture(textureObject); + } + } +} + + +DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SurfaceAttachStencil_Gpu, reporter, ctxInfo) { + GrGpu* gpu = ctxInfo.grContext()->getGpu(); + if (!gpu) { + return; + } + if (gpu->caps()->avoidStencilBuffers()) { + return; + } + static const uint32_t kOrigColor = 0xFFAABBCC; + + for (auto& surfaceFunc : {&create_gpu_surface_backend_texture, + &create_gpu_surface_backend_texture_as_render_target}) { + for (int sampleCnt : {0, 4, 8}) { + GrBackendObject textureObject; + auto surface = surfaceFunc(ctxInfo.grContext(), sampleCnt, kOrigColor, &textureObject); + + if (!surface && sampleCnt > 0) { + // Certain platforms don't support MSAA, skip these. + continue; + } + + // Validate that we can attach a stencil buffer to an SkSurface created by either of + // our surface functions. + GrRenderTarget* rt = surface->getCanvas() + ->internal_private_accessTopLayerRenderTargetContext()->accessRenderTarget(); + REPORTER_ASSERT(reporter, + ctxInfo.grContext()->resourceProvider()->attachStencilAttachment(rt)); + gpu->deleteTestingOnlyBackendTexture(textureObject); + } + } +} +#endif + +static void test_surface_creation_and_snapshot_with_color_space( + skiatest::Reporter* reporter, + const char* prefix, + bool f16Support, + std::function<sk_sp<SkSurface>(const SkImageInfo&)> surfaceMaker) { + + auto srgbColorSpace = SkColorSpace::MakeSRGB(); + auto adobeColorSpace = SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named); + const SkMatrix44* srgbMatrix = as_CSB(srgbColorSpace)->toXYZD50(); + SkASSERT(srgbMatrix); + SkColorSpaceTransferFn oddGamma; + oddGamma.fA = 1.0f; + oddGamma.fB = oddGamma.fC = oddGamma.fD = oddGamma.fE = oddGamma.fF = 0.0f; + oddGamma.fG = 4.0f; + auto oddColorSpace = SkColorSpace::MakeRGB(oddGamma, *srgbMatrix); + auto linearColorSpace = SkColorSpace::MakeSRGBLinear(); + + const struct { + SkColorType fColorType; + sk_sp<SkColorSpace> fColorSpace; + bool fShouldWork; + const char* fDescription; + } testConfigs[] = { + { kN32_SkColorType, nullptr, true, "N32-nullptr" }, + { kN32_SkColorType, linearColorSpace, false, "N32-linear" }, + { kN32_SkColorType, srgbColorSpace, true, "N32-srgb" }, + { kN32_SkColorType, adobeColorSpace, true, "N32-adobe" }, + { kN32_SkColorType, oddColorSpace, false, "N32-odd" }, + { kRGBA_F16_SkColorType, nullptr, false, "F16-nullptr" }, + { kRGBA_F16_SkColorType, linearColorSpace, true, "F16-linear" }, + { kRGBA_F16_SkColorType, srgbColorSpace, false, "F16-srgb" }, + { kRGBA_F16_SkColorType, adobeColorSpace, false, "F16-adobe" }, + { kRGBA_F16_SkColorType, oddColorSpace, false, "F16-odd" }, + { kRGB_565_SkColorType, srgbColorSpace, false, "565-srgb" }, + { kAlpha_8_SkColorType, srgbColorSpace, false, "A8-srgb" }, + }; + + for (auto& testConfig : testConfigs) { + SkString fullTestName = SkStringPrintf("%s-%s", prefix, testConfig.fDescription); + SkImageInfo info = SkImageInfo::Make(10, 10, testConfig.fColorType, kPremul_SkAlphaType, + testConfig.fColorSpace); + + // For some GPU contexts (eg ANGLE), we don't have f16 support, so we should fail to create + // any surface of that type: + bool shouldWork = testConfig.fShouldWork && + (f16Support || kRGBA_F16_SkColorType != testConfig.fColorType); + + auto surface(surfaceMaker(info)); + REPORTER_ASSERT_MESSAGE(reporter, SkToBool(surface) == shouldWork, fullTestName.c_str()); + + if (shouldWork && surface) { + sk_sp<SkImage> image(surface->makeImageSnapshot()); + REPORTER_ASSERT_MESSAGE(reporter, image, testConfig.fDescription); + SkColorSpace* imageColorSpace = as_IB(image)->onImageInfo().colorSpace(); + REPORTER_ASSERT_MESSAGE(reporter, imageColorSpace == testConfig.fColorSpace.get(), + fullTestName.c_str()); + } + } +} + +DEF_TEST(SurfaceCreationWithColorSpace, reporter) { + auto surfaceMaker = [](const SkImageInfo& info) { + return SkSurface::MakeRaster(info); + }; + + test_surface_creation_and_snapshot_with_color_space(reporter, "raster", true, surfaceMaker); +} + +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceCreationWithColorSpace_Gpu, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + bool f16Support = context->caps()->isConfigRenderable(kRGBA_half_GrPixelConfig, false); + auto surfaceMaker = [context](const SkImageInfo& info) { + return SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info); + }; + + test_surface_creation_and_snapshot_with_color_space(reporter, "gpu", f16Support, surfaceMaker); + + std::vector<GrBackendObject> textureHandles; + auto wrappedSurfaceMaker = [context,&textureHandles](const SkImageInfo& info) { + static const int kSize = 10; + GrPixelConfig config = SkImageInfo2GrPixelConfig(info, *context->caps()); + + GrBackendObject backendHandle = context->getGpu()->createTestingOnlyBackendTexture( + nullptr, kSize, kSize, config, true); + + if (!backendHandle) { + return sk_sp<SkSurface>(nullptr); + } + textureHandles.push_back(backendHandle); + + GrBackendTexture backendTex = GrTest::CreateBackendTexture(context->contextPriv().getBackend(), + kSize, + kSize, + config, + backendHandle); + + return SkSurface::MakeFromBackendTexture(context, backendTex, + kDefault_GrSurfaceOrigin, 0, + sk_ref_sp(info.colorSpace()), nullptr); + }; + + test_surface_creation_and_snapshot_with_color_space(reporter, "wrapped", f16Support, + wrappedSurfaceMaker); + + context->flush(); + + for (auto textureHandle : textureHandles) { + context->getGpu()->deleteTestingOnlyBackendTexture(textureHandle); + } +} +#endif + +static void test_overdraw_surface(skiatest::Reporter* r, SkSurface* surface) { + SkOverdrawCanvas canvas(surface->getCanvas()); + canvas.drawPaint(SkPaint()); + sk_sp<SkImage> image = surface->makeImageSnapshot(); + + SkBitmap bitmap; + image->asLegacyBitmap(&bitmap, SkImage::kRO_LegacyBitmapMode); + for (int y = 0; y < 10; y++) { + for (int x = 0; x < 10; x++) { + REPORTER_ASSERT(r, 1 == SkGetPackedA32(*bitmap.getAddr32(x, y))); + } + } +} + +DEF_TEST(OverdrawSurface_Raster, r) { + sk_sp<SkSurface> surface = create_surface(); + test_overdraw_surface(r, surface.get()); +} + +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(OverdrawSurface_Gpu, r, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + sk_sp<SkSurface> surface = create_gpu_surface(context); + test_overdraw_surface(r, surface.get()); +} +#endif + +DEF_TEST(Surface_null, r) { + REPORTER_ASSERT(r, SkSurface::MakeNull(0, 0) == nullptr); + + const int w = 37; + const int h = 1000; + auto surf = SkSurface::MakeNull(w, h); + auto canvas = surf->getCanvas(); + + canvas->drawPaint(SkPaint()); // should not crash, but don't expect anything to draw + REPORTER_ASSERT(r, surf->makeImageSnapshot() == nullptr); }
diff --git a/src/third_party/skia/tests/SwizzlerTest.cpp b/src/third_party/skia/tests/SwizzlerTest.cpp new file mode 100644 index 0000000..655c97e --- /dev/null +++ b/src/third_party/skia/tests/SwizzlerTest.cpp
@@ -0,0 +1,162 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkSwizzle.h" +#include "SkSwizzler.h" +#include "Test.h" +#include "SkOpts.h" + +// These are the values that we will look for to indicate that the fill was successful +static const uint8_t kFillGray = 0x22; +static const uint16_t kFill565 = 0x3344; +static const uint32_t kFillColor = 0x55667788; + +static void check_fill(skiatest::Reporter* r, + const SkImageInfo& imageInfo, + uint32_t startRow, + uint32_t endRow, + size_t rowBytes, + uint32_t offset, + uint32_t colorOrIndex) { + + // Calculate the total size of the image in bytes. Use the smallest possible size. + // The offset value tells us to adjust the pointer from the memory we allocate in order + // to test on different memory alignments. If offset is nonzero, we need to increase the + // size of the memory we allocate in order to make sure that we have enough. We are + // still allocating the smallest possible size. + const size_t totalBytes = imageInfo.getSafeSize(rowBytes) + offset; + + // Create fake image data where every byte has a value of 0 + std::unique_ptr<uint8_t[]> storage(new uint8_t[totalBytes]); + memset(storage.get(), 0, totalBytes); + // Adjust the pointer in order to test on different memory alignments + uint8_t* imageData = storage.get() + offset; + uint8_t* imageStart = imageData + rowBytes * startRow; + const SkImageInfo fillInfo = imageInfo.makeWH(imageInfo.width(), endRow - startRow + 1); + SkSampler::Fill(fillInfo, imageStart, rowBytes, colorOrIndex, SkCodec::kNo_ZeroInitialized); + + // Ensure that the pixels are filled properly + // The bots should catch any memory corruption + uint8_t* indexPtr = imageData + startRow * rowBytes; + uint8_t* grayPtr = indexPtr; + uint32_t* colorPtr = (uint32_t*) indexPtr; + uint16_t* color565Ptr = (uint16_t*) indexPtr; + for (uint32_t y = startRow; y <= endRow; y++) { + for (int32_t x = 0; x < imageInfo.width(); x++) { + switch (imageInfo.colorType()) { + case kN32_SkColorType: + REPORTER_ASSERT(r, kFillColor == colorPtr[x]); + break; + case kGray_8_SkColorType: + REPORTER_ASSERT(r, kFillGray == grayPtr[x]); + break; + case kRGB_565_SkColorType: + REPORTER_ASSERT(r, kFill565 == color565Ptr[x]); + break; + default: + REPORTER_ASSERT(r, false); + break; + } + } + indexPtr += rowBytes; + colorPtr = (uint32_t*) indexPtr; + } +} + +// Test Fill() with different combinations of dimensions, alignment, and padding +DEF_TEST(SwizzlerFill, r) { + // Test on an invalid width and representative widths + const uint32_t widths[] = { 0, 10, 50 }; + + // In order to call Fill(), there must be at least one row to fill + // Test on the smallest possible height and representative heights + const uint32_t heights[] = { 1, 5, 10 }; + + // Test on interesting possibilities for row padding + const uint32_t paddings[] = { 0, 4 }; + + // Iterate over test dimensions + for (uint32_t width : widths) { + for (uint32_t height : heights) { + + // Create image info objects + const SkImageInfo colorInfo = SkImageInfo::MakeN32(width, height, kUnknown_SkAlphaType); + const SkImageInfo grayInfo = colorInfo.makeColorType(kGray_8_SkColorType); + const SkImageInfo color565Info = colorInfo.makeColorType(kRGB_565_SkColorType); + + for (uint32_t padding : paddings) { + + // Calculate row bytes + const size_t colorRowBytes = SkColorTypeBytesPerPixel(kN32_SkColorType) * width + + padding; + const size_t indexRowBytes = width + padding; + const size_t grayRowBytes = indexRowBytes; + const size_t color565RowBytes = + SkColorTypeBytesPerPixel(kRGB_565_SkColorType) * width + padding; + + // If there is padding, we can invent an offset to change the memory alignment + for (uint32_t offset = 0; offset <= padding; offset += 4) { + + // Test all possible start rows with all possible end rows + for (uint32_t startRow = 0; startRow < height; startRow++) { + for (uint32_t endRow = startRow; endRow < height; endRow++) { + + // Test fill with each color type + check_fill(r, colorInfo, startRow, endRow, colorRowBytes, offset, + kFillColor); + check_fill(r, grayInfo, startRow, endRow, grayRowBytes, offset, + kFillGray); + check_fill(r, color565Info, startRow, endRow, color565RowBytes, offset, + kFill565); + } + } + } + } + } + } +} + +DEF_TEST(SwizzleOpts, r) { + uint32_t dst, src; + + // forall c, c*255 == c, c*0 == 0 + for (int c = 0; c <= 255; c++) { + src = (255<<24) | c; + SkOpts::RGBA_to_rgbA(&dst, &src, 1); + REPORTER_ASSERT(r, dst == src); + SkOpts::RGBA_to_bgrA(&dst, &src, 1); + REPORTER_ASSERT(r, dst == (uint32_t)((255<<24) | (c<<16))); + + src = (0<<24) | c; + SkOpts::RGBA_to_rgbA(&dst, &src, 1); + REPORTER_ASSERT(r, dst == 0); + SkOpts::RGBA_to_bgrA(&dst, &src, 1); + REPORTER_ASSERT(r, dst == 0); + } + + // check a totally arbitrary color + src = 0xFACEB004; + SkOpts::RGBA_to_rgbA(&dst, &src, 1); + REPORTER_ASSERT(r, dst == 0xFACAAD04); + + // swap red and blue + SkOpts::RGBA_to_BGRA(&dst, &src, 1); + REPORTER_ASSERT(r, dst == 0xFA04B0CE); + + // all together now + SkOpts::RGBA_to_bgrA(&dst, &src, 1); + REPORTER_ASSERT(r, dst == 0xFA04ADCA); +} + +DEF_TEST(PublicSwizzleOpts, r) { + uint32_t dst, src; + + // check a totally arbitrary color + src = 0xFACEB004; + SkSwapRB(&dst, &src, 1); + REPORTER_ASSERT(r, dst == 0xFA04B0CE); +}
diff --git a/src/third_party/skia/tests/TArrayTest.cpp b/src/third_party/skia/tests/TArrayTest.cpp index b85efca..1b376e4 100644 --- a/src/third_party/skia/tests/TArrayTest.cpp +++ b/src/third_party/skia/tests/TArrayTest.cpp
@@ -5,14 +5,16 @@ * found in the LICENSE file. */ +#include "SkRandom.h" +#include "SkRefCnt.h" #include "SkTArray.h" #include "Test.h" // Tests the SkTArray<T> class template. -template <bool MEM_COPY> +template <bool MEM_MOVE> static void TestTSet_basic(skiatest::Reporter* reporter) { - SkTArray<int, MEM_COPY> a; + SkTArray<int, MEM_MOVE> a; // Starts empty. REPORTER_ASSERT(reporter, a.empty()); @@ -58,7 +60,307 @@ // {0, 3, 2 } } +template <typename T> static void test_swap(skiatest::Reporter* reporter, + SkTArray<T>* (&arrays)[4], + int (&sizes)[7]) +{ + for (auto a : arrays) { + for (auto b : arrays) { + if (a == b) { + continue; + } + + for (auto sizeA : sizes) { + for (auto sizeB : sizes) { + a->reset(); + b->reset(); + + int curr = 0; + for (int i = 0; i < sizeA; i++) { a->push_back(curr++); } + for (int i = 0; i < sizeB; i++) { b->push_back(curr++); } + + a->swap(b); + REPORTER_ASSERT(reporter, b->count() == sizeA); + REPORTER_ASSERT(reporter, a->count() == sizeB); + + curr = 0; + for (auto&& x : *b) { REPORTER_ASSERT(reporter, x == curr++); } + for (auto&& x : *a) { REPORTER_ASSERT(reporter, x == curr++); } + + a->swap(a); + curr = sizeA; + for (auto&& x : *a) { REPORTER_ASSERT(reporter, x == curr++); } + }} + }} +} + +static void test_swap(skiatest::Reporter* reporter) { + int sizes[] = {0, 1, 5, 10, 15, 20, 25}; + + SkTArray<int> arr; + SkSTArray< 5, int> arr5; + SkSTArray<10, int> arr10; + SkSTArray<20, int> arr20; + SkTArray<int>* arrays[] = { &arr, &arr5, &arr10, &arr20 }; + test_swap(reporter, arrays, sizes); + + struct MoveOnlyInt { + MoveOnlyInt(int i) : fInt(i) {} + MoveOnlyInt(MoveOnlyInt&& that) : fInt(that.fInt) {} + bool operator==(int i) { return fInt == i; } + int fInt; + }; + + SkTArray<MoveOnlyInt> moi; + SkSTArray< 5, MoveOnlyInt> moi5; + SkSTArray<10, MoveOnlyInt> moi10; + SkSTArray<20, MoveOnlyInt> moi20; + SkTArray<MoveOnlyInt>* arraysMoi[] = { &moi, &moi5, &moi10, &moi20 }; + test_swap(reporter, arraysMoi, sizes); +} + +template <typename T, bool MEM_MOVE> +void test_copy_ctor(skiatest::Reporter* reporter, SkTArray<T, MEM_MOVE>&& array) { + SkASSERT(array.empty()); + for (int i = 0; i < 5; ++i) { + array.emplace_back(new SkRefCnt); + REPORTER_ASSERT(reporter, array.back()->unique()); + } + + { + SkTArray<T, MEM_MOVE> copy(array); + for (const auto& ref : array) + REPORTER_ASSERT(reporter, !ref->unique()); + for (const auto& ref : copy) + REPORTER_ASSERT(reporter, !ref->unique()); + } + + for (const auto& ref : array) + REPORTER_ASSERT(reporter, ref->unique()); +} + +static void test_move(skiatest::Reporter* reporter) { +#define TEST_MOVE do { \ + SRC_T src; \ + src.emplace_back(sk_make_sp<SkRefCnt>()); \ + { \ + /* copy ctor */ \ + DST_T copy(src); \ + REPORTER_ASSERT(reporter, !copy[0]->unique()); \ + } \ + { \ + /* move ctor */ \ + DST_T move(std::move(src)); \ + REPORTER_ASSERT(reporter, move[0]->unique()); \ + } \ + REPORTER_ASSERT(reporter, src.empty()); \ + src.emplace_back(sk_make_sp<SkRefCnt>()); \ + { \ + /* copy assignment */ \ + DST_T copy; \ + copy = src; \ + REPORTER_ASSERT(reporter, !copy[0]->unique()); \ + } \ + { \ + /* move assignment */ \ + DST_T move; \ + move = std::move(src); \ + REPORTER_ASSERT(reporter, move[0]->unique()); \ + } \ + REPORTER_ASSERT(reporter, src.empty()); \ +} while (false) + + { + using SRC_T = SkTArray<sk_sp<SkRefCnt>, false>; + using DST_T = SkTArray<sk_sp<SkRefCnt>, false>; + TEST_MOVE; + } + + { + using SRC_T = SkTArray<sk_sp<SkRefCnt>, true>; + using DST_T = SkTArray<sk_sp<SkRefCnt>, true>; + TEST_MOVE; + } + + { + using SRC_T = SkSTArray<1, sk_sp<SkRefCnt>, false>; + using DST_T = SkSTArray<1, sk_sp<SkRefCnt>, false>; + TEST_MOVE; + } + + { + using SRC_T = SkSTArray<1, sk_sp<SkRefCnt>, true>; + using DST_T = SkSTArray<1, sk_sp<SkRefCnt>, true>; + TEST_MOVE; + } + + { + using SRC_T = SkTArray<sk_sp<SkRefCnt>, false>; + using DST_T = SkSTArray<1, sk_sp<SkRefCnt>, false>; + TEST_MOVE; + } + + { + using SRC_T = SkTArray<sk_sp<SkRefCnt>, true>; + using DST_T = SkSTArray<1, sk_sp<SkRefCnt>, true>; + TEST_MOVE; + } + + { + using SRC_T = SkSTArray<1, sk_sp<SkRefCnt>, false>; + using DST_T = SkTArray<sk_sp<SkRefCnt>, false>; + TEST_MOVE; + } + + { + using SRC_T = SkSTArray<1, sk_sp<SkRefCnt>, true>; + using DST_T = SkTArray<sk_sp<SkRefCnt>, true>; + TEST_MOVE; + } +#undef TEST_MOVE +} + +template <typename T, bool MEM_MOVE> int SkTArray<T, MEM_MOVE>::allocCntForTest() const { + return fAllocCount; +} + +void test_unnecessary_alloc(skiatest::Reporter* reporter) { + { + SkTArray<int> a; + REPORTER_ASSERT(reporter, a.allocCntForTest() == 0); + } + { + SkSTArray<10, int> a; + REPORTER_ASSERT(reporter, a.allocCntForTest() == 10); + } + { + SkTArray<int> a(1); + REPORTER_ASSERT(reporter, a.allocCntForTest() >= 1); + } + { + SkTArray<int> a, b; + b = a; + REPORTER_ASSERT(reporter, b.allocCntForTest() == 0); + } + { + SkSTArray<10, int> a; + SkTArray<int> b; + b = a; + REPORTER_ASSERT(reporter, b.allocCntForTest() == 0); + } + { + SkTArray<int> a; + SkTArray<int> b(a); + REPORTER_ASSERT(reporter, b.allocCntForTest() == 0); + } + { + SkSTArray<10, int> a; + SkTArray<int> b(a); + REPORTER_ASSERT(reporter, b.allocCntForTest() == 0); + } + { + SkTArray<int> a; + SkTArray<int> b(std::move(a)); + REPORTER_ASSERT(reporter, b.allocCntForTest() == 0); + } + { + SkSTArray<10, int> a; + SkTArray<int> b(std::move(a)); + REPORTER_ASSERT(reporter, b.allocCntForTest() == 0); + } + { + SkTArray<int> a; + SkTArray<int> b; + b = std::move(a); + REPORTER_ASSERT(reporter, b.allocCntForTest() == 0); + } + { + SkSTArray<10, int> a; + SkTArray<int> b; + b = std::move(a); + REPORTER_ASSERT(reporter, b.allocCntForTest() == 0); + } +} + +static void test_self_assignment(skiatest::Reporter* reporter) { + SkTArray<int> a; + a.push_back(1); + REPORTER_ASSERT(reporter, !a.empty()); + REPORTER_ASSERT(reporter, a.count() == 1); + REPORTER_ASSERT(reporter, a[0] == 1); + + a = a; + REPORTER_ASSERT(reporter, !a.empty()); + REPORTER_ASSERT(reporter, a.count() == 1); + REPORTER_ASSERT(reporter, a[0] == 1); +} + +template <typename Array> static void test_array_reserve(skiatest::Reporter* reporter, + Array* array, int reserveCount) { + SkRandom random; + REPORTER_ASSERT(reporter, array->allocCntForTest() >= reserveCount); + array->push_back(); + REPORTER_ASSERT(reporter, array->allocCntForTest() >= reserveCount); + array->pop_back(); + REPORTER_ASSERT(reporter, array->allocCntForTest() >= reserveCount); + while (array->count() < reserveCount) { + // Two steps forward, one step back + if (random.nextULessThan(3) < 2) { + array->push_back(); + } else if (array->count() > 0) { + array->pop_back(); + } + REPORTER_ASSERT(reporter, array->allocCntForTest() >= reserveCount); + } +} + +template<typename Array> static void test_reserve(skiatest::Reporter* reporter) { + // Test that our allocated space stays >= to the reserve count until the array is filled to + // the reserve count + for (int reserveCount : {1, 2, 10, 100}) { + // Test setting reserve in constructor. + Array array1(reserveCount); + test_array_reserve(reporter, &array1, reserveCount); + + // Test setting reserve after constructor. + Array array2; + array2.reserve(reserveCount); + test_array_reserve(reporter, &array2, reserveCount); + + // Test increasing reserve after constructor. + Array array3(reserveCount/2); + array3.reserve(reserveCount); + test_array_reserve(reporter, &array3, reserveCount); + + // Test setting reserve on non-empty array. + Array array4; + array4.push_back_n(reserveCount); + array4.reserve(reserveCount); + array4.pop_back_n(reserveCount); + test_array_reserve(reporter, &array4, 2 * reserveCount); + } +} + DEF_TEST(TArray, reporter) { TestTSet_basic<true>(reporter); TestTSet_basic<false>(reporter); + test_swap(reporter); + + test_copy_ctor(reporter, SkTArray<sk_sp<SkRefCnt>, false>()); + test_copy_ctor(reporter, SkTArray<sk_sp<SkRefCnt>, true>()); + test_copy_ctor(reporter, SkSTArray< 1, sk_sp<SkRefCnt>, false>()); + test_copy_ctor(reporter, SkSTArray< 1, sk_sp<SkRefCnt>, true>()); + test_copy_ctor(reporter, SkSTArray<10, sk_sp<SkRefCnt>, false>()); + test_copy_ctor(reporter, SkSTArray<10, sk_sp<SkRefCnt>, true>()); + + test_move(reporter); + + test_unnecessary_alloc(reporter); + + test_self_assignment(reporter); + + test_reserve<SkTArray<int>>(reporter); + test_reserve<SkSTArray<1, int>>(reporter); + test_reserve<SkSTArray<2, int>>(reporter); + test_reserve<SkSTArray<16, int>>(reporter); }
diff --git a/src/third_party/skia/tests/TDPQueueTest.cpp b/src/third_party/skia/tests/TDPQueueTest.cpp new file mode 100644 index 0000000..75eab25 --- /dev/null +++ b/src/third_party/skia/tests/TDPQueueTest.cpp
@@ -0,0 +1,205 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTDPQueue.h" +#include "SkRandom.h" +#include "Test.h" + +namespace { bool intless(const int& a, const int& b) { return a < b; } } + +static void simple_test(skiatest::Reporter* reporter) { + SkTDPQueue<int, intless> heap; + REPORTER_ASSERT(reporter, 0 == heap.count()); + + heap.insert(0); + REPORTER_ASSERT(reporter, 1 == heap.count()); + REPORTER_ASSERT(reporter, 0 == heap.peek()); + heap.pop(); + REPORTER_ASSERT(reporter, 0 == heap.count()); + + heap.insert(0); + heap.insert(1); + REPORTER_ASSERT(reporter, 2 == heap.count()); + REPORTER_ASSERT(reporter, 0 == heap.peek()); + heap.pop(); + REPORTER_ASSERT(reporter, 1 == heap.count()); + REPORTER_ASSERT(reporter, 1 == heap.peek()); + heap.pop(); + REPORTER_ASSERT(reporter, 0 == heap.count()); + + heap.insert(2); + heap.insert(1); + heap.insert(0); + REPORTER_ASSERT(reporter, 3 == heap.count()); + REPORTER_ASSERT(reporter, 0 == heap.peek()); + heap.pop(); + REPORTER_ASSERT(reporter, 2 == heap.count()); + REPORTER_ASSERT(reporter, 1 == heap.peek()); + heap.pop(); + REPORTER_ASSERT(reporter, 1 == heap.count()); + REPORTER_ASSERT(reporter, 2 == heap.peek()); + heap.pop(); + REPORTER_ASSERT(reporter, 0 == heap.count()); + + heap.insert(2); + heap.insert(3); + heap.insert(0); + heap.insert(1); + REPORTER_ASSERT(reporter, 4 == heap.count()); + REPORTER_ASSERT(reporter, 0 == heap.peek()); + heap.pop(); + REPORTER_ASSERT(reporter, 3 == heap.count()); + REPORTER_ASSERT(reporter, 1 == heap.peek()); + heap.pop(); + REPORTER_ASSERT(reporter, 2 == heap.count()); + REPORTER_ASSERT(reporter, 2 == heap.peek()); + heap.pop(); + REPORTER_ASSERT(reporter, 1 == heap.count()); + REPORTER_ASSERT(reporter, 3 == heap.peek()); + heap.pop(); + REPORTER_ASSERT(reporter, 0 == heap.count()); +} + +struct Dummy { + int fValue; + int fPriority; + mutable int fIndex; + + static bool LessP(Dummy* const& a, Dummy* const& b) { return a->fPriority < b->fPriority; } + static int* PQIndex(Dummy* const& dummy) { return &dummy->fIndex; } + + bool operator== (const Dummy& that) const { + return fValue == that.fValue && fPriority == that.fPriority; + } + bool operator!= (const Dummy& that) const { return !(*this == that); } +}; + +void random_test(skiatest::Reporter* reporter) { + SkRandom random; + static const Dummy kSentinel = {-1, -1, -1}; + + for (int i = 0; i < 100; ++i) { + // Create a random set of Dummy objects. + int count = random.nextULessThan(100); + SkTDArray<Dummy> array; + array.setReserve(count); + for (int j = 0; j < count; ++j) { + Dummy* dummy = array.append(); + dummy->fPriority = random.nextS(); + dummy->fValue = random.nextS(); + dummy->fIndex = -1; + if (*dummy == kSentinel) { + array.pop(); + --j; + } + } + + // Stick the dummy objects in the pqueue. + SkTDPQueue<Dummy*, Dummy::LessP, Dummy::PQIndex> pq; + for (int j = 0; j < count; ++j) { + pq.insert(&array[j]); + } + REPORTER_ASSERT(reporter, pq.count() == array.count()); + for (int j = 0; j < count; ++j) { + // every item should have an entry in the queue. + REPORTER_ASSERT(reporter, -1 != array[j].fIndex); + } + + // Begin the test. + while (pq.count()) { + // Make sure the top of the queue is really the highest priority. + Dummy* top = pq.peek(); + for (int k = 0; k < count; ++k) { + REPORTER_ASSERT(reporter, kSentinel == array[k] || + array[k].fPriority >= top->fPriority); + } + // Do one of three random actions: + unsigned action = random.nextULessThan(3); + switch (action) { + case 0: { // pop the top, + Dummy* top = pq.peek(); + REPORTER_ASSERT(reporter, array.begin() <= top && top < array.end()); + pq.pop(); + *top = kSentinel; + break; + } + case 1: { // remove a random element, + int item; + do { + item = random.nextULessThan(count); + } while (array[item] == kSentinel); + pq.remove(&array[item]); + array[item] = kSentinel; + break; + } + case 2: { // or change an element's priority. + int item; + do { + item = random.nextULessThan(count); + } while (array[item] == kSentinel); + array[item].fPriority = random.nextS(); + pq.priorityDidChange(&array[item]); + break; + } + } + } + } +} + +void sort_test(skiatest::Reporter* reporter) { + SkRandom random; + + SkTDPQueue<Dummy *, Dummy::LessP, Dummy::PQIndex> pqTest; + SkTDPQueue<Dummy *, Dummy::LessP, Dummy::PQIndex> pqControl; + + // Create a random set of Dummy objects and populate the test queue. + int count = random.nextULessThan(100); + SkTDArray<Dummy> testArray; + testArray.setReserve(count); + for (int i = 0; i < count; i++) { + Dummy *dummy = testArray.append(); + dummy->fPriority = random.nextS(); + dummy->fValue = random.nextS(); + dummy->fIndex = -1; + pqTest.insert(&testArray[i]); + } + + // Stick equivalent dummy objects into the control queue. + SkTDArray<Dummy> controlArray; + controlArray.setReserve(count); + for (int i = 0; i < count; i++) { + Dummy *dummy = controlArray.append(); + dummy->fPriority = testArray[i].fPriority; + dummy->fValue = testArray[i].fValue; + dummy->fIndex = -1; + pqControl.insert(&controlArray[i]); + } + + // Sort the queue + pqTest.sort(); + + // Compare elements in the queue to ensure they are in sorted order + int prevPriority = pqTest.peek()->fPriority; + for (int i = 0; i < count; i++) { + REPORTER_ASSERT(reporter, i <= pqTest.at(i)->fIndex); + REPORTER_ASSERT(reporter, prevPriority <= pqTest.at(i)->fPriority); + prevPriority = pqTest.at(i)->fPriority; + } + + // Verify that after sorting the queue still produces the same result as the control queue + for (int i = 0; i < count; i++) { + REPORTER_ASSERT(reporter, *pqControl.peek() == *pqTest.peek()); + pqControl.pop(); + pqTest.pop(); + } +} + +DEF_TEST(TDPQueueTest, reporter) { + simple_test(reporter); + random_test(reporter); + sort_test(reporter); +}
diff --git a/src/third_party/skia/tests/TDStackNesterTest.cpp b/src/third_party/skia/tests/TDStackNesterTest.cpp deleted file mode 100644 index 968654b..0000000 --- a/src/third_party/skia/tests/TDStackNesterTest.cpp +++ /dev/null
@@ -1,110 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkTDStackNester.h" - -#include "Test.h" - -/** - * Test SkTDStackNester<int>::push(). Pushes the current count onto the stack, - * and checks that the count has increased by one. - */ -static void test_push(skiatest::Reporter* reporter, SkTDStackNester<int>* nester) { - SkASSERT(nester); - const int count = nester->count(); - // test_pop depends on this value. - nester->push(count); - REPORTER_ASSERT(reporter, nester->count() == count + 1); -} - -/** - * Test SkTDStackNester<int>::pop(). Pops the top element off the stack, and - * checks that the new count is one smaller, and that the popped element - * matches the new count (as was pushed by test_push). - */ -static void test_pop(skiatest::Reporter* reporter, SkTDStackNester<int>* nester) { - SkASSERT(nester); - const int count = nester->count(); - // This test should not be called with a count <= 0. - SkASSERT(count > 0); - const int top = nester->top(); - int value = -1; - nester->pop(&value); - REPORTER_ASSERT(reporter, top == value); - const int newCount = nester->count(); - REPORTER_ASSERT(reporter, newCount == count - 1); - // Since test_push always pushes the count prior to the push, value should - // always be one less than count. - REPORTER_ASSERT(reporter, newCount == value); -} - -/** - * Test nest() and unnest(). nest() is called, and it is confirmed that the - * count is now zero. Then test_push() is called inc times, followed by a call to - * unnest(). After this call, check that the count has returned to the initial count, and - * that nestingLevel() has returned to its initial value. - */ -static void test_nest(skiatest::Reporter* reporter, SkTDStackNester<int>* nester, int inc) { - SkASSERT(nester); - SkASSERT(inc > 0); - const int initialCount = nester->count(); - const int initialNesting = nester->nestingLevel(); - - nester->nest(); - REPORTER_ASSERT(reporter, nester->count() == 0); - REPORTER_ASSERT(reporter, nester->nestingLevel() == initialNesting + 1); - - for (int i = 0; i < inc; ++i) { - test_push(reporter, nester); - } - - nester->unnest(); - REPORTER_ASSERT(reporter, nester->count() == initialCount); - REPORTER_ASSERT(reporter, nester->nestingLevel() == initialNesting); -} - -class SkTDStackNesterTester { -public: - static int GetSlotCount() { - return SkTDStackNester<int>::kSlotCount; - } -}; - -static void test_stack_nester(skiatest::Reporter* reporter) { - SkTDStackNester<int> nester; - int count = nester.count(); - REPORTER_ASSERT(reporter, 0 == count); - REPORTER_ASSERT(reporter, nester.nestingLevel() == 0); - REPORTER_ASSERT(reporter, nester.empty()); - - // Test nesting (with arbitrary number of pushes) from the beginning. - test_nest(reporter, &nester, 3); - - const int slotCount = SkTDStackNesterTester::GetSlotCount(); - - // Test pushing beyond the boundary of the first Rec. - for (; count < 2 * slotCount; ++count) { - if (3 == count) { - // Test nesting (an arbitrary number of pushes) early on. - test_nest(reporter, &nester, 7); - } else if (slotCount - 4 == count) { - // Test nesting across the boundary of a Rec. - test_nest(reporter, &nester, 6); - } - test_push(reporter, &nester); - } - - // Pop everything off the stack except for the last one, to confirm - // that the destructor handles a remaining object. - while (nester.count() > 1) { - test_pop(reporter, &nester); - } -} - -DEF_TEST(TDStackNester, reporter) { - test_stack_nester(reporter); -}
diff --git a/src/third_party/skia/tests/TLSTest.cpp b/src/third_party/skia/tests/TLSTest.cpp index 53bb5fe..2702d9c 100644 --- a/src/third_party/skia/tests/TLSTest.cpp +++ b/src/third_party/skia/tests/TLSTest.cpp
@@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include "SkAtomics.h" #include "SkGraphics.h" #include "SkPaint.h" #include "SkTLS.h" @@ -56,7 +57,7 @@ static void* FakeCreateTLS() { sk_atomic_inc(&gCounter); - return NULL; + return nullptr; } static void FakeDeleteTLS(void*) {
diff --git a/src/third_party/skia/tests/TSetTest.cpp b/src/third_party/skia/tests/TSetTest.cpp deleted file mode 100644 index 3f82648..0000000 --- a/src/third_party/skia/tests/TSetTest.cpp +++ /dev/null
@@ -1,135 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkTSet.h" -#include "Test.h" - -// Tests the SkTSet<T> class template. -// Functions that just call SkTDArray are not tested. - -static void TestTSet_basic(skiatest::Reporter* reporter) { - SkTSet<int> set0; - REPORTER_ASSERT(reporter, set0.isEmpty()); - REPORTER_ASSERT(reporter, !set0.contains(-1)); - REPORTER_ASSERT(reporter, !set0.contains(0)); - REPORTER_ASSERT(reporter, !set0.contains(1)); - REPORTER_ASSERT(reporter, set0.count() == 0); - - REPORTER_ASSERT(reporter, set0.add(0)); - REPORTER_ASSERT(reporter, !set0.isEmpty()); - REPORTER_ASSERT(reporter, !set0.contains(-1)); - REPORTER_ASSERT(reporter, set0.contains(0)); - REPORTER_ASSERT(reporter, !set0.contains(1)); - REPORTER_ASSERT(reporter, set0.count() == 1); - REPORTER_ASSERT(reporter, !set0.add(0)); - REPORTER_ASSERT(reporter, set0.count() == 1); - -#ifdef SK_DEBUG - set0.validate(); -#endif -} - -#define COUNT 1732 -#define PRIME1 10007 -#define PRIME2 1733 - -// Generates a series of positive unique pseudo-random numbers. -static int f(int i) { - return (long(i) * PRIME1) % PRIME2; -} - -// Will expose contains() too. -static void TestTSet_advanced(skiatest::Reporter* reporter) { - SkTSet<int> set0; - - for (int i = 0; i < COUNT; i++) { - REPORTER_ASSERT(reporter, !set0.contains(f(i))); - if (i > 0) { - REPORTER_ASSERT(reporter, set0.contains(f(0))); - REPORTER_ASSERT(reporter, set0.contains(f(i / 2))); - REPORTER_ASSERT(reporter, set0.contains(f(i - 1))); - } - REPORTER_ASSERT(reporter, !set0.contains(f(i))); - REPORTER_ASSERT(reporter, set0.count() == i); - REPORTER_ASSERT(reporter, set0.add(f(i))); - REPORTER_ASSERT(reporter, set0.contains(f(i))); - REPORTER_ASSERT(reporter, set0.count() == i + 1); - REPORTER_ASSERT(reporter, !set0.add(f(i))); - } - - // Test deterministic output - for (int i = 0; i < COUNT; i++) { - REPORTER_ASSERT(reporter, set0[i] == f(i)); - } - - // Test copy constructor too. - SkTSet<int> set1 = set0; - - REPORTER_ASSERT(reporter, set0.count() == set1.count()); - REPORTER_ASSERT(reporter, !set1.contains(-1000)); - - for (int i = 0; i < COUNT; i++) { - REPORTER_ASSERT(reporter, set1.contains(f(i))); - REPORTER_ASSERT(reporter, set1[i] == f(i)); - } - - // Test operator= too. - SkTSet<int> set2; - set2 = set0; - - REPORTER_ASSERT(reporter, set0.count() == set2.count()); - REPORTER_ASSERT(reporter, !set2.contains(-1000)); - - for (int i = 0; i < COUNT; i++) { - REPORTER_ASSERT(reporter, set2.contains(f(i))); - REPORTER_ASSERT(reporter, set2[i] == f(i)); - } - -#ifdef SK_DEBUG - set0.validate(); - set1.validate(); - set2.validate(); -#endif -} - -static void TestTSet_merge(skiatest::Reporter* reporter) { - SkTSet<int> set; - SkTSet<int> setOdd; - - for (int i = 0; i < COUNT; i++) { - REPORTER_ASSERT(reporter, set.add(2 * i)); - REPORTER_ASSERT(reporter, setOdd.add(2 * i + 1)); - } - // mergeInto returns the number of duplicates. Expected 0. - REPORTER_ASSERT(reporter, set.mergeInto(setOdd) == 0); - REPORTER_ASSERT(reporter, set.count() == 2 * COUNT); - - // mergeInto should now find all new numbers duplicate. - REPORTER_ASSERT(reporter, set.mergeInto(setOdd) == setOdd.count()); - REPORTER_ASSERT(reporter, set.count() == 2 * COUNT); - - for (int i = 0; i < 2 * COUNT; i++) { - REPORTER_ASSERT(reporter, set.contains(i)); - } - - // check deterministic output - for (int i = 0; i < COUNT; i++) { - REPORTER_ASSERT(reporter, set[i] == 2 * i); - REPORTER_ASSERT(reporter, set[COUNT + i] == 2 * i + 1); - } - -#ifdef SK_DEBUG - set.validate(); - setOdd.validate(); -#endif -} - -DEF_TEST(TSet, reporter) { - TestTSet_basic(reporter); - TestTSet_advanced(reporter); - TestTSet_merge(reporter); -}
diff --git a/src/third_party/skia/tests/TemplatesTest.cpp b/src/third_party/skia/tests/TemplatesTest.cpp new file mode 100644 index 0000000..9d5ca77 --- /dev/null +++ b/src/third_party/skia/tests/TemplatesTest.cpp
@@ -0,0 +1,145 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTemplates.h" +#include "Test.h" + +// Tests for some of the helpers in SkTemplates.h +static void test_automalloc_realloc(skiatest::Reporter* reporter) { + SkAutoSTMalloc<1, int> array; + + // test we have a valid pointer, should not crash + array[0] = 1; + REPORTER_ASSERT(reporter, array[0] == 1); + + // using realloc for init + array.realloc(1); + + array[0] = 1; + REPORTER_ASSERT(reporter, array[0] == 1); + + // verify realloc can grow + array.realloc(2); + REPORTER_ASSERT(reporter, array[0] == 1); + + // realloc can shrink + array.realloc(1); + REPORTER_ASSERT(reporter, array[0] == 1); + + // should not crash + array.realloc(0); + + // grow and shrink again + array.realloc(10); + for (int i = 0; i < 10; i++) { + array[i] = 10 - i; + } + array.realloc(20); + for (int i = 0; i < 10; i++) { + REPORTER_ASSERT(reporter, array[i] == 10 - i); + } + array.realloc(10); + for (int i = 0; i < 10; i++) { + REPORTER_ASSERT(reporter, array[i] == 10 - i); + } + + array.realloc(1); + REPORTER_ASSERT(reporter, array[0] = 10); + + // resets mixed with realloc, below stack alloc size + array.reset(0); + array.realloc(1); + array.reset(1); + + array[0] = 1; + REPORTER_ASSERT(reporter, array[0] == 1); + + // reset and realloc > stack size + array.reset(2); + array.realloc(3); + array[0] = 1; + REPORTER_ASSERT(reporter, array[0] == 1); + array.realloc(1); + REPORTER_ASSERT(reporter, array[0] == 1); +} + +DEF_TEST(Templates, reporter) { + test_automalloc_realloc(reporter); +} + +constexpr int static kStackPreallocCount = 10; + +// Ensures the containers in SkTemplates.h all have a consistent api. +template<typename TContainer, typename TCount> +static void test_container_apis(skiatest::Reporter* reporter) { + REPORTER_ASSERT(reporter, !TContainer((TCount)0).get()); + REPORTER_ASSERT(reporter, TContainer((TCount)1).get()); + REPORTER_ASSERT(reporter, TContainer((TCount)kStackPreallocCount).get()); + REPORTER_ASSERT(reporter, TContainer((TCount)kStackPreallocCount + 1).get()); + + TContainer container; + // The default constructor may or may not init to empty, depending on the type of container. + + container.reset((TCount)1); + REPORTER_ASSERT(reporter, container.get()); + + container.reset((TCount)kStackPreallocCount); + REPORTER_ASSERT(reporter, container.get()); + + container.reset((TCount)kStackPreallocCount + 1); + REPORTER_ASSERT(reporter, container.get()); + + container.reset((TCount)0); + REPORTER_ASSERT(reporter, !container.get()); +} + +DEF_TEST(TemplateContainerAPIs, reporter) { + test_container_apis<SkAutoTArray<int>, int>(reporter); + test_container_apis<SkAutoSTArray<kStackPreallocCount, int>, int>(reporter); + test_container_apis<SkAutoTMalloc<int>, size_t>(reporter); + test_container_apis<SkAutoSTMalloc<kStackPreallocCount, int>, size_t>(reporter); +} + +// Ensures that realloc(0) results in a null pointer. +template<typename TAutoMalloc> static void test_realloc_to_zero(skiatest::Reporter* reporter) { + TAutoMalloc autoMalloc(kStackPreallocCount); + REPORTER_ASSERT(reporter, autoMalloc.get()); + + autoMalloc.realloc(0); + REPORTER_ASSERT(reporter, !autoMalloc.get()); + + autoMalloc.realloc(kStackPreallocCount + 1); + REPORTER_ASSERT(reporter, autoMalloc.get()); + + autoMalloc.realloc(0); + REPORTER_ASSERT(reporter, !autoMalloc.get()); + + autoMalloc.realloc(kStackPreallocCount); + REPORTER_ASSERT(reporter, autoMalloc.get()); +} + +DEF_TEST(AutoReallocToZero, reporter) { + test_realloc_to_zero<SkAutoTMalloc<int> >(reporter); + test_realloc_to_zero<SkAutoSTMalloc<kStackPreallocCount, int> >(reporter); +} + +DEF_TEST(SkAutoTMallocSelfMove, r) { +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wself-move" +#endif + + SkAutoTMalloc<int> foo(20); + REPORTER_ASSERT(r, foo.get()); + + foo = std::move(foo); + REPORTER_ASSERT(r, foo.get()); + +#if defined(__clang__) + #pragma clang diagnostic pop +#endif +}
diff --git a/src/third_party/skia/tests/TessellatingPathRendererTests.cpp b/src/third_party/skia/tests/TessellatingPathRendererTests.cpp new file mode 100644 index 0000000..f9db895 --- /dev/null +++ b/src/third_party/skia/tests/TessellatingPathRendererTests.cpp
@@ -0,0 +1,430 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#include "SkPath.h" + +#if SK_SUPPORT_GPU +#include "GrClip.h" +#include "GrContext.h" +#include "SkGradientShader.h" +#include "SkShaderBase.h" +#include "effects/GrPorterDuffXferProcessor.h" +#include "ops/GrTessellatingPathRenderer.h" + +/* + * These tests pass by not crashing, hanging or asserting in Debug. + */ + +// Tests active edges made inactive by splitting. +// Also tests active edge list forced into an invalid ordering by +// splitting (mopped up in cleanup_active_edges()). +static SkPath create_path_0() { + SkPath path; + path.moveTo(229.127044677734375f, 67.34100341796875f); + path.lineTo(187.8097381591796875f, -6.7729740142822265625f); + path.lineTo(171.411407470703125f, 50.94266510009765625f); + path.lineTo(245.5253753662109375f, 9.6253643035888671875f); + path.moveTo(208.4683990478515625f, 30.284009933471679688f); + path.lineTo(171.411407470703125f, 50.94266510009765625f); + path.lineTo(187.8097381591796875f, -6.7729740142822265625f); + return path; +} + +// Intersections which fall exactly on the current vertex, and require +// a restart of the intersection checking. +static SkPath create_path_1() { + SkPath path; + path.moveTo(314.483551025390625f, 486.246002197265625f); + path.lineTo(385.41949462890625f, 532.8087158203125f); + path.lineTo(373.232879638671875f, 474.05938720703125f); + path.lineTo(326.670166015625f, 544.995361328125f); + path.moveTo(349.951507568359375f, 509.52734375f); + path.lineTo(373.232879638671875f, 474.05938720703125f); + path.lineTo(385.41949462890625f, 532.8087158203125f); + return path; +} + +// Tests active edges which are removed by splitting. +static SkPath create_path_2() { + SkPath path; + path.moveTo(343.107391357421875f, 613.62176513671875f); + path.lineTo(426.632415771484375f, 628.5740966796875f); + path.lineTo(392.3460693359375f, 579.33544921875f); + path.lineTo(377.39373779296875f, 662.86041259765625f); + path.moveTo(384.869873046875f, 621.097900390625f); + path.lineTo(392.3460693359375f, 579.33544921875f); + path.lineTo(426.632415771484375f, 628.5740966796875f); + return path; +} + +// Collinear edges merged in set_top(). +// Also, an intersection between left and right enclosing edges which +// falls above the current vertex. +static SkPath create_path_3() { + SkPath path; + path.moveTo(545.95751953125f, 791.69854736328125f); + path.lineTo(612.05816650390625f, 738.494140625f); + path.lineTo(552.4056396484375f, 732.0460205078125f); + path.lineTo(605.61004638671875f, 798.14666748046875f); + path.moveTo(579.00787353515625f, 765.0963134765625f); + path.lineTo(552.4056396484375f, 732.0460205078125f); + path.lineTo(612.05816650390625f, 738.494140625f); + return path; +} + +// Tests active edges which are made inactive by set_top(). +static SkPath create_path_4() { + SkPath path; + path.moveTo(819.2725830078125f, 751.77447509765625f); + path.lineTo(820.70904541015625f, 666.933837890625f); + path.lineTo(777.57049560546875f, 708.63592529296875f); + path.lineTo(862.4111328125f, 710.0723876953125f); + path.moveTo(819.99078369140625f, 709.3541259765625f); + path.lineTo(777.57049560546875f, 708.63592529296875f); + path.lineTo(820.70904541015625f, 666.933837890625f); + return path; +} + +static SkPath create_path_5() { + SkPath path; + path.moveTo(823.33209228515625f, 749.052734375f); + path.lineTo(823.494873046875f, 664.20013427734375f); + path.lineTo(780.9871826171875f, 706.5450439453125f); + path.lineTo(865.8397216796875f, 706.70782470703125f); + path.moveTo(823.4134521484375f, 706.6263427734375f); + path.lineTo(780.9871826171875f, 706.5450439453125f); + path.lineTo(823.494873046875f, 664.20013427734375f); + return path; +} + +static SkPath create_path_6() { + SkPath path; + path.moveTo(954.862548828125f, 562.8349609375f); + path.lineTo(899.32818603515625f, 498.679443359375f); + path.lineTo(895.017578125f, 558.52435302734375f); + path.lineTo(959.17315673828125f, 502.990081787109375f); + path.moveTo(927.0953369140625f, 530.7572021484375f); + path.lineTo(895.017578125f, 558.52435302734375f); + path.lineTo(899.32818603515625f, 498.679443359375f); + return path; +} + +static SkPath create_path_7() { + SkPath path; + path.moveTo(958.5330810546875f, 547.35516357421875f); + path.lineTo(899.93109130859375f, 485.989013671875f); + path.lineTo(898.54901123046875f, 545.97308349609375f); + path.lineTo(959.9151611328125f, 487.37109375f); + path.moveTo(929.2320556640625f, 516.67205810546875f); + path.lineTo(898.54901123046875f, 545.97308349609375f); + path.lineTo(899.93109130859375f, 485.989013671875f); + return path; +} + +static SkPath create_path_8() { + SkPath path; + path.moveTo(389.8609619140625f, 369.326873779296875f); + path.lineTo(470.6290283203125f, 395.33697509765625f); + path.lineTo(443.250030517578125f, 341.9478759765625f); + path.lineTo(417.239959716796875f, 422.7159423828125f); + path.moveTo(430.244964599609375f, 382.3319091796875f); + path.lineTo(443.250030517578125f, 341.9478759765625f); + path.lineTo(470.6290283203125f, 395.33697509765625f); + return path; +} + +static SkPath create_path_9() { + SkPath path; + path.moveTo(20, 20); + path.lineTo(50, 80); + path.lineTo(20, 80); + path.moveTo(80, 50); + path.lineTo(50, 50); + path.lineTo(20, 50); + return path; +} + +static SkPath create_path_10() { + SkPath path; + path.moveTo(257.19439697265625f, 320.876617431640625f); + path.lineTo(190.113037109375f, 320.58978271484375f); + path.lineTo(203.64404296875f, 293.8145751953125f); + path.moveTo(203.357177734375f, 360.896026611328125f); + path.lineTo(216.88824462890625f, 334.120819091796875f); + path.lineTo(230.41925048828125f, 307.345611572265625f); + return path; +} + +// A degenerate segments case, where both upper and lower segments of +// a split edge must remain active. +static SkPath create_path_11() { + SkPath path; + path.moveTo(231.9331207275390625f, 306.2012939453125f); + path.lineTo(191.4859161376953125f, 306.04547119140625f); + path.lineTo(231.0659332275390625f, 300.2642822265625f); + path.moveTo(189.946807861328125f, 302.072265625f); + path.lineTo(179.79705810546875f, 294.859771728515625f); + path.lineTo(191.0016021728515625f, 296.165679931640625f); + path.moveTo(150.8942108154296875f, 304.900146484375f); + path.lineTo(179.708892822265625f, 297.849029541015625f); + path.lineTo(190.4742279052734375f, 299.11895751953125f); + return path; +} + +// Handle the case where edge.dist(edge.fTop) != 0.0. +static SkPath create_path_12() { + SkPath path; + path.moveTo( 0.0f, 400.0f); + path.lineTo( 138.0f, 202.0f); + path.lineTo( 0.0f, 202.0f); + path.moveTo( 12.62693023681640625f, 250.57464599609375f); + path.lineTo( 8.13896942138671875f, 254.556884765625f); + path.lineTo(-18.15641021728515625f, 220.40203857421875f); + path.lineTo(-15.986493110656738281f, 219.6513519287109375f); + path.moveTo( 36.931194305419921875f, 282.485504150390625f); + path.lineTo( 15.617521286010742188f, 261.2901611328125f); + path.lineTo( 10.3829498291015625f, 252.565765380859375f); + path.lineTo(-16.165292739868164062f, 222.646026611328125f); + return path; +} + +// A degenerate segments case which exercises inactive edges being +// made active by splitting. +static SkPath create_path_13() { + SkPath path; + path.moveTo(690.62127685546875f, 509.25555419921875f); + path.lineTo(99.336181640625f, 511.71405029296875f); + path.lineTo(708.362548828125f, 512.4349365234375f); + path.lineTo(729.9940185546875f, 516.3114013671875f); + path.lineTo(738.708984375f, 518.76995849609375f); + path.lineTo(678.3463134765625f, 510.0819091796875f); + path.lineTo(681.21795654296875f, 504.81378173828125f); + path.moveTo(758.52764892578125f, 521.55963134765625f); + path.lineTo(719.1549072265625f, 514.50372314453125f); + path.lineTo(689.59063720703125f, 512.0628662109375f); + path.lineTo(679.78216552734375f, 507.447845458984375f); + return path; +} + +// Tests vertices which become "orphaned" (ie., no connected edges) +// after simplification. +static SkPath create_path_14() { + SkPath path; + path.moveTo(217.326019287109375f, 166.4752960205078125f); + path.lineTo(226.279266357421875f, 170.929473876953125f); + path.lineTo(234.3973388671875f, 177.0623626708984375f); + path.lineTo(262.0921630859375f, 188.746124267578125f); + path.moveTo(196.23638916015625f, 174.0722198486328125f); + path.lineTo(416.15277099609375f, 180.138214111328125f); + path.lineTo(192.651947021484375f, 304.0228271484375f); + return path; +} + +static SkPath create_path_15() { + SkPath path; + path.moveTo( 0.0f, 0.0f); + path.lineTo(10000.0f, 0.0f); + path.lineTo( 0.0f, -1.0f); + path.lineTo(10000.0f, 0.000001f); + path.lineTo( 0.0f, -30.0f); + return path; +} + +// Reduction of Nebraska-StateSeal.svg. Floating point error causes the +// same edge to be added to more than one poly on the same side. +static SkPath create_path_16() { + SkPath path; + path.moveTo(170.8199920654296875, 491.86700439453125); + path.lineTo(173.7649993896484375, 489.7340087890625); + path.lineTo(174.1450958251953125, 498.545989990234375); + path.lineTo( 171.998992919921875, 500.88201904296875); + path.moveTo(168.2922515869140625, 498.66265869140625); + path.lineTo(169.8589935302734375, 497.94500732421875); + path.lineTo( 172, 500.88299560546875); + path.moveTo( 169.555267333984375, 490.70111083984375); + path.lineTo(173.7649993896484375, 489.7340087890625); + path.lineTo( 170.82000732421875, 491.86700439453125); + return path; +} + +// A simple concave path. Test this with a non-invertible matrix. +static SkPath create_path_17() { + SkPath path; + path.moveTo(20, 20); + path.lineTo(80, 20); + path.lineTo(30, 30); + path.lineTo(20, 80); + return path; +} + +// A shape with a vertex collinear to the right hand edge. +// This messes up find_enclosing_edges. +static SkPath create_path_18() { + SkPath path; + path.moveTo(80, 20); + path.lineTo(80, 60); + path.lineTo(20, 60); + path.moveTo(80, 50); + path.lineTo(80, 80); + path.lineTo(20, 80); + return path; +} + +// Exercises the case where an edge becomes collinear with *two* of its +// adjacent neighbour edges after splitting. +// This is a reduction from +// http://mooooo.ooo/chebyshev-sine-approximation/horner_ulp.svg +static SkPath create_path_19() { + SkPath path; + path.moveTo( 351.99298095703125, 348.23046875); + path.lineTo( 351.91876220703125, 347.33984375); + path.lineTo( 351.91876220703125, 346.1953125); + path.lineTo( 351.90313720703125, 347.734375); + path.lineTo( 351.90313720703125, 346.1328125); + path.lineTo( 351.87579345703125, 347.93359375); + path.lineTo( 351.87579345703125, 345.484375); + path.lineTo( 351.86407470703125, 347.7890625); + path.lineTo( 351.86407470703125, 346.2109375); + path.lineTo( 351.84844970703125, 347.63763427734375); + path.lineTo( 351.84454345703125, 344.19232177734375); + path.lineTo( 351.78204345703125, 346.9483642578125); + path.lineTo( 351.758636474609375, 347.18310546875); + path.lineTo( 351.75469970703125, 346.75); + path.lineTo( 351.75469970703125, 345.46875); + path.lineTo( 352.5546875, 345.46875); + path.lineTo( 352.55078125, 347.01953125); + path.lineTo( 351.75079345703125, 347.02313232421875); + path.lineTo( 351.74688720703125, 346.15203857421875); + path.lineTo( 351.74688720703125, 347.646148681640625); + path.lineTo( 352.5390625, 346.94140625); + path.lineTo( 351.73907470703125, 346.94268798828125); + path.lineTo( 351.73516845703125, 344.48565673828125); + path.lineTo( 352.484375, 346.73828125); + path.lineTo( 351.68438720703125, 346.7401123046875); + path.lineTo( 352.4765625, 346.546875); + path.lineTo( 351.67657470703125, 346.54937744140625); + path.lineTo( 352.47265625, 346.75390625); + path.lineTo( 351.67266845703125, 346.756622314453125); + path.lineTo( 351.66876220703125, 345.612091064453125); + return path; +} + +// An intersection above the first vertex in the mesh. +// Reduction from http://crbug.com/730687 +static SkPath create_path_20() { + SkPath path; + path.moveTo( 2822128.5, 235.026336669921875); + path.lineTo( 2819349.25, 235.3623504638671875); + path.lineTo( -340558688, 23.83478546142578125); + path.lineTo( -340558752, 25.510419845581054688); + path.lineTo( -340558720, 27.18605804443359375); + return path; +} + +// An intersection whose result is NaN (due to rounded-to-inf endpoint). +static SkPath create_path_21() { + SkPath path; + path.moveTo(1.7889142061167663539e+38, 39338463358011572224.0); + path.lineTo( 1647.4193115234375, -522.603515625); + path.lineTo( 1677.74560546875, -529.0028076171875); + path.lineTo( 1678.29541015625, -528.7847900390625); + path.lineTo( 1637.5167236328125, -519.79266357421875); + path.lineTo( 1647.4193115234375, -522.603515625); + return path; +} + +// A quad which becomes NaN when interpolated. +static SkPath create_path_22() { + SkPath path; + path.moveTo(-5.71889e+13f, 1.36759e+09f); + path.quadTo(2.45472e+19f, -3.12406e+15f, -2.19589e+18f, 2.79462e+14f); + return path; +} + +static sk_sp<GrFragmentProcessor> create_linear_gradient_processor(GrContext* ctx) { + SkPoint pts[2] = { {0, 0}, {1, 1} }; + SkColor colors[2] = { SK_ColorGREEN, SK_ColorBLUE }; + sk_sp<SkShader> shader = SkGradientShader::MakeLinear( + pts, colors, nullptr, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode); + SkShaderBase::AsFPArgs args( + ctx, &SkMatrix::I(), &SkMatrix::I(), SkFilterQuality::kLow_SkFilterQuality, nullptr); + return as_SB(shader)->asFragmentProcessor(args); +} + +static void test_path(GrContext* ctx, + GrRenderTargetContext* renderTargetContext, + const SkPath& path, + const SkMatrix& matrix = SkMatrix::I(), + GrAAType aaType = GrAAType::kNone, + sk_sp<GrFragmentProcessor> fp = nullptr) { + GrTessellatingPathRenderer tess; + + GrPaint paint; + paint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc)); + if (fp) { + paint.addColorFragmentProcessor(fp); + } + + GrNoClip noClip; + GrStyle style(SkStrokeRec::kFill_InitStyle); + GrShape shape(path, style); + GrPathRenderer::DrawPathArgs args{ctx, + std::move(paint), + &GrUserStencilSettings::kUnused, + renderTargetContext, + &noClip, + &matrix, + &shape, + aaType, + false}; + tess.drawPath(args); +} + +DEF_GPUTEST_FOR_ALL_CONTEXTS(TessellatingPathRendererTests, reporter, ctxInfo) { + GrContext* ctx = ctxInfo.grContext(); + + sk_sp<GrRenderTargetContext> rtc(ctx->makeDeferredRenderTargetContext( + SkBackingFit::kApprox, + 800, 800, + kRGBA_8888_GrPixelConfig, + nullptr, + 0, + kTopLeft_GrSurfaceOrigin)); + if (!rtc) { + return; + } + + ctx->flush(); + test_path(ctx, rtc.get(), create_path_0()); + test_path(ctx, rtc.get(), create_path_1()); + test_path(ctx, rtc.get(), create_path_2()); + test_path(ctx, rtc.get(), create_path_3()); + test_path(ctx, rtc.get(), create_path_4()); + test_path(ctx, rtc.get(), create_path_5()); + test_path(ctx, rtc.get(), create_path_6()); + test_path(ctx, rtc.get(), create_path_7()); + test_path(ctx, rtc.get(), create_path_8()); + test_path(ctx, rtc.get(), create_path_9()); + test_path(ctx, rtc.get(), create_path_10()); + test_path(ctx, rtc.get(), create_path_11()); + test_path(ctx, rtc.get(), create_path_12()); + test_path(ctx, rtc.get(), create_path_13()); + test_path(ctx, rtc.get(), create_path_14()); + test_path(ctx, rtc.get(), create_path_15()); + test_path(ctx, rtc.get(), create_path_16()); + SkMatrix nonInvertibleMatrix = SkMatrix::MakeScale(0, 0); + sk_sp<GrFragmentProcessor> fp(create_linear_gradient_processor(ctx)); + test_path(ctx, rtc.get(), create_path_17(), nonInvertibleMatrix, GrAAType::kCoverage, fp); + test_path(ctx, rtc.get(), create_path_18()); + test_path(ctx, rtc.get(), create_path_19()); + test_path(ctx, rtc.get(), create_path_20(), SkMatrix(), GrAAType::kCoverage); + test_path(ctx, rtc.get(), create_path_21(), SkMatrix(), GrAAType::kCoverage); + test_path(ctx, rtc.get(), create_path_22()); +} +#endif
diff --git a/src/third_party/skia/tests/Test.cpp b/src/third_party/skia/tests/Test.cpp index d0147e1..37515dd 100644 --- a/src/third_party/skia/tests/Test.cpp +++ b/src/third_party/skia/tests/Test.cpp
@@ -8,111 +8,44 @@ #include "Test.h" #include "SkCommandLineFlags.h" -#include "SkError.h" #include "SkString.h" -#include "SkTArray.h" #include "SkTime.h" -#if SK_SUPPORT_GPU -#include "GrContext.h" -#include "gl/SkNativeGLContext.h" -#else -class GrContext; -#endif +DEFINE_string2(tmpDir, t, nullptr, "Temp directory to use."); -DEFINE_string2(tmpDir, t, NULL, "tmp directory for tests to use."); +void skiatest::Reporter::bumpTestCount() {} -using namespace skiatest; +bool skiatest::Reporter::allowExtendedTest() const { return false; } -Reporter::Reporter() : fTestCount(0) { -} +bool skiatest::Reporter::verbose() const { return false; } -void Reporter::startTest(Test* test) { - this->onStart(test); -} - -void Reporter::reportFailed(const SkString& desc) { - this->onReportFailed(desc); -} - -void Reporter::endTest(Test* test) { - this->onEnd(test); -} - -/////////////////////////////////////////////////////////////////////////////// - -Test::Test() : fReporter(NULL), fPassed(true) {} - -Test::~Test() { - SkSafeUnref(fReporter); -} - -void Test::setReporter(Reporter* r) { - SkRefCnt_SafeAssign(fReporter, r); -} - -const char* Test::getName() { - if (fName.size() == 0) { - this->onGetName(&fName); +SkString skiatest::Failure::toString() const { + SkString result = SkStringPrintf("%s:%d\t", this->fileName, this->lineNo); + if (!this->message.isEmpty()) { + result.append(this->message); + if (strlen(this->condition) > 0) { + result.append(": "); + } } - return fName.c_str(); + result.append(this->condition); + return result; } -class LocalReporter : public Reporter { -public: - explicit LocalReporter(Reporter* reporterToMimic) : fReporter(reporterToMimic) {} - - int numFailures() const { return fFailures.count(); } - const SkString& failure(int i) const { return fFailures[i]; } - -protected: - virtual void onReportFailed(const SkString& desc) SK_OVERRIDE { - fFailures.push_back(desc); - } - - // Proxy down to fReporter. We assume these calls are threadsafe. - virtual bool allowExtendedTest() const SK_OVERRIDE { - return fReporter->allowExtendedTest(); - } - - virtual void bumpTestCount() SK_OVERRIDE { - fReporter->bumpTestCount(); - } - - virtual bool verbose() const SK_OVERRIDE { - return fReporter->verbose(); - } - -private: - Reporter* fReporter; // Unowned. - SkTArray<SkString> fFailures; -}; - -void Test::run() { - // Clear the Skia error callback before running any test, to ensure that tests - // don't have unintended side effects when running more than one. - SkSetErrorCallback( NULL, NULL ); - - // Tell (likely shared) fReporter that this test has started. - fReporter->startTest(this); - - const SkMSec start = SkTime::GetMSecs(); - // Run the test into a LocalReporter so we know if it's passed or failed without interference - // from other tests that might share fReporter. - LocalReporter local(fReporter); - this->onRun(&local); - fPassed = local.numFailures() == 0; - fElapsed = SkTime::GetMSecs() - start; - - // Now tell fReporter about any failures and wrap up. - for (int i = 0; i < local.numFailures(); i++) { - fReporter->reportFailed(local.failure(i)); - } - fReporter->endTest(this); - -} - -SkString Test::GetTmpDir() { - const char* tmpDir = FLAGS_tmpDir.isEmpty() ? NULL : FLAGS_tmpDir[0]; +SkString skiatest::GetTmpDir() { + const char* tmpDir = FLAGS_tmpDir.isEmpty() ? nullptr : FLAGS_tmpDir[0]; return SkString(tmpDir); } + +skiatest::Timer::Timer() : fStartNanos(SkTime::GetNSecs()) {} + +double skiatest::Timer::elapsedNs() const { + return SkTime::GetNSecs() - fStartNanos; +} + +double skiatest::Timer::elapsedMs() const { return this->elapsedNs() * 1e-6; } + +SkMSec skiatest::Timer::elapsedMsInt() const { + const double elapsedMs = this->elapsedMs(); + SkASSERT(SK_MSecMax >= elapsedMs); + return static_cast<SkMSec>(elapsedMs); +}
diff --git a/src/third_party/skia/tests/Test.h b/src/third_party/skia/tests/Test.h index 6c85b32..db2552b 100644 --- a/src/third_party/skia/tests/Test.h +++ b/src/third_party/skia/tests/Test.h
@@ -1,4 +1,3 @@ - /* * Copyright 2011 Google Inc. * @@ -8,88 +7,96 @@ #ifndef skiatest_Test_DEFINED #define skiatest_Test_DEFINED -#include "SkRefCnt.h" #include "SkString.h" -#include "SkTRegistry.h" -#include "SkThread.h" +#include "../tools/Registry.h" #include "SkTypes.h" +#include "SkClipOpPriv.h" +#if SK_SUPPORT_GPU +#include "GrContextFactory.h" +#else +namespace sk_gpu_test { class GrContextFactory; +class ContextInfo; +class GLTestContext; +} // namespace sk_gpu_test +class GrContext; +#endif namespace skiatest { - class Test; +SkString GetTmpDir(); - class Reporter : public SkRefCnt { - public: - SK_DECLARE_INST_COUNT(Reporter) - Reporter(); +struct Failure { + Failure(const char* f, int l, const char* c, const SkString& m) + : fileName(f), lineNo(l), condition(c), message(m) {} + const char* fileName; + int lineNo; + const char* condition; + SkString message; + SkString toString() const; +}; - int countTests() const { return fTestCount; } +class Reporter : SkNoncopyable { +public: + virtual ~Reporter() {} + virtual void bumpTestCount(); + virtual void reportFailed(const skiatest::Failure&) = 0; + virtual bool allowExtendedTest() const; + virtual bool verbose() const; + virtual void* stats() const { return nullptr; } - void startTest(Test*); - void reportFailed(const SkString& desc); - void endTest(Test*); - - virtual bool allowExtendedTest() const { return false; } - virtual bool verbose() const { return false; } - virtual void bumpTestCount() { sk_atomic_inc(&fTestCount); } - - protected: - virtual void onStart(Test*) {} - virtual void onReportFailed(const SkString& desc) {} - virtual void onEnd(Test*) {} - - private: - int32_t fTestCount; - - typedef SkRefCnt INHERITED; - }; - - class Test { - public: - Test(); - virtual ~Test(); - - Reporter* getReporter() const { return fReporter; } - void setReporter(Reporter*); - - const char* getName(); - void run(); - bool passed() const { return fPassed; } - SkMSec elapsedMs() const { return fElapsed; } - - static SkString GetTmpDir(); - - virtual bool isGPUTest() const { return false; } - virtual void setGrContextFactory(GrContextFactory* factory) {} - - protected: - virtual void onGetName(SkString*) = 0; - virtual void onRun(Reporter*) = 0; - - private: - Reporter* fReporter; - SkString fName; - bool fPassed; - SkMSec fElapsed; - }; - - class GpuTest : public Test{ - public: - GpuTest() : Test(), fGrContextFactory(NULL) {} - - virtual bool isGPUTest() const { return true; } - virtual void setGrContextFactory(GrContextFactory* factory) { - fGrContextFactory = factory; + void reportFailedWithContext(const skiatest::Failure& f) { + SkString fullMessage = f.message; + if (!fContextStack.empty()) { + fullMessage.append(" ["); + for (int i = 0; i < fContextStack.count(); ++i) { + if (i > 0) { + fullMessage.append(", "); + } + fullMessage.append(fContextStack[i]); + } + fullMessage.append("]"); } + this->reportFailed(skiatest::Failure(f.fileName, f.lineNo, f.condition, fullMessage)); + } + void push(const SkString& message) { + fContextStack.push_back(message); + } + void pop() { + fContextStack.pop_back(); + } - protected: - GrContextFactory* fGrContextFactory; // Unowned. - }; +private: + SkTArray<SkString> fContextStack; +}; - typedef SkTRegistry<Test*(*)(void*)> TestRegistry; -} // namespace skiatest +#define REPORT_FAILURE(reporter, cond, message) \ + reporter->reportFailedWithContext(skiatest::Failure(__FILE__, __LINE__, cond, message)) + +class ReporterContext : SkNoncopyable { +public: + ReporterContext(Reporter* reporter, const SkString& message) : fReporter(reporter) { + fReporter->push(message); + } + ~ReporterContext() { + fReporter->pop(); + } + +private: + Reporter* fReporter; +}; + +typedef void (*TestProc)(skiatest::Reporter*, sk_gpu_test::GrContextFactory*); + +struct Test { + Test(const char* n, bool g, TestProc p) : name(n), needsGpu(g), proc(p) {} + const char* name; + bool needsGpu; + TestProc proc; +}; + +typedef sk_tools::Registry<Test> TestRegistry; /* Use the following macros to make use of the skiatest classes, e.g. @@ -110,65 +117,117 @@ } */ -#define REPORTER_ASSERT(r, cond) \ - do { \ - if (!(cond)) { \ - SkString desc; \ - desc.printf("%s:%d\t%s", __FILE__, __LINE__, #cond); \ - r->reportFailed(desc); \ - } \ - } while(0) +#if SK_SUPPORT_GPU +using GrContextFactoryContextType = sk_gpu_test::GrContextFactory::ContextType; +#else +using GrContextFactoryContextType = int; +#endif -#define REPORTER_ASSERT_MESSAGE(r, cond, message) \ - do { \ - if (!(cond)) { \ - SkString desc; \ - desc.printf("%s:%d\t%s: %s", __FILE__, __LINE__, \ - message, #cond); \ - r->reportFailed(desc); \ - } \ - } while(0) +typedef void GrContextTestFn(Reporter*, const sk_gpu_test::ContextInfo&); +typedef bool GrContextTypeFilterFn(GrContextFactoryContextType); -#define ERRORF(reporter, ...) \ - do { \ - SkString desc; \ - desc.printf("%s:%d\t", __FILE__, __LINE__); \ - desc.appendf(__VA_ARGS__) ; \ - (reporter)->reportFailed(desc); \ - } while(0) +extern bool IsGLContextType(GrContextFactoryContextType); +extern bool IsVulkanContextType(GrContextFactoryContextType); +extern bool IsRenderingGLContextType(GrContextFactoryContextType); +extern bool IsNullGLContextType(GrContextFactoryContextType); +void RunWithGPUTestContexts(GrContextTestFn*, GrContextTypeFilterFn*, + Reporter*, sk_gpu_test::GrContextFactory*); -#define DEF_TEST(name, reporter) \ - static void test_##name(skiatest::Reporter*); \ - namespace skiatest { \ - class name##Class : public Test { \ - public: \ - static Test* Factory(void*) { return SkNEW(name##Class); } \ - protected: \ - virtual void onGetName(SkString* name) SK_OVERRIDE { \ - name->set(#name); \ - } \ - virtual void onRun(Reporter* r) SK_OVERRIDE { test_##name(r); } \ - }; \ - static TestRegistry gReg_##name##Class(name##Class::Factory); \ - } \ - static void test_##name(skiatest::Reporter* reporter) +/** Timer provides wall-clock duration since its creation. */ +class Timer { +public: + /** Starts the timer. */ + Timer(); -#define DEF_GPUTEST(name, reporter, factory) \ - static void test_##name(skiatest::Reporter*, GrContextFactory*); \ - namespace skiatest { \ - class name##Class : public GpuTest { \ - public: \ - static Test* Factory(void*) { return SkNEW(name##Class); } \ - protected: \ - virtual void onGetName(SkString* name) SK_OVERRIDE { \ - name->set(#name); \ - } \ - virtual void onRun(Reporter* r) SK_OVERRIDE { \ - test_##name(r, fGrContextFactory); \ - } \ - }; \ - static TestRegistry gReg_##name##Class(name##Class::Factory); \ - } \ - static void test_##name(skiatest::Reporter* reporter, GrContextFactory* factory) + /** Nanoseconds since creation. */ + double elapsedNs() const; + + /** Milliseconds since creation. */ + double elapsedMs() const; + + /** Milliseconds since creation as an integer. + Behavior is undefined for durations longer than SK_MSecMax. + */ + SkMSec elapsedMsInt() const; +private: + double fStartNanos; +}; + +} // namespace skiatest + +#define REPORTER_ASSERT(r, cond) \ + do { \ + if (!(cond)) { \ + REPORT_FAILURE(r, #cond, SkString()); \ + } \ + } while (0) + +#define REPORTER_ASSERT_MESSAGE(r, cond, message) \ + do { \ + if (!(cond)) { \ + REPORT_FAILURE(r, #cond, SkString(message)); \ + } \ + } while (0) + +#define ERRORF(r, ...) \ + do { \ + REPORT_FAILURE(r, "", SkStringPrintf(__VA_ARGS__)); \ + } while (0) + +#define INFOF(REPORTER, ...) \ + do { \ + if ((REPORTER)->verbose()) { \ + SkDebugf(__VA_ARGS__); \ + } \ + } while (0) + +#define DEF_TEST(name, reporter) \ + static void test_##name(skiatest::Reporter*, sk_gpu_test::GrContextFactory*); \ + skiatest::TestRegistry name##TestRegistry( \ + skiatest::Test(#name, false, test_##name)); \ + void test_##name(skiatest::Reporter* reporter, sk_gpu_test::GrContextFactory*) + + +#define DEF_GPUTEST(name, reporter, factory) \ + static void test_##name(skiatest::Reporter*, sk_gpu_test::GrContextFactory*); \ + skiatest::TestRegistry name##TestRegistry( \ + skiatest::Test(#name, true, test_##name)); \ + void test_##name(skiatest::Reporter* reporter, sk_gpu_test::GrContextFactory* factory) + +#define DEF_GPUTEST_FOR_CONTEXTS(name, context_filter, reporter, context_info) \ + static void test_##name(skiatest::Reporter*, \ + const sk_gpu_test::ContextInfo& context_info); \ + static void test_gpu_contexts_##name(skiatest::Reporter* reporter, \ + sk_gpu_test::GrContextFactory* factory) { \ + skiatest::RunWithGPUTestContexts(test_##name, context_filter, reporter, factory); \ + } \ + skiatest::TestRegistry name##TestRegistry( \ + skiatest::Test(#name, true, test_gpu_contexts_##name)); \ + void test_##name(skiatest::Reporter* reporter, \ + const sk_gpu_test::ContextInfo& context_info) + +#define DEF_GPUTEST_FOR_ALL_CONTEXTS(name, reporter, context_info) \ + DEF_GPUTEST_FOR_CONTEXTS(name, nullptr, reporter, context_info) +#define DEF_GPUTEST_FOR_RENDERING_CONTEXTS(name, reporter, context_info) \ + DEF_GPUTEST_FOR_CONTEXTS(name, sk_gpu_test::GrContextFactory::IsRenderingContext, \ + reporter, context_info) +#define DEF_GPUTEST_FOR_ALL_GL_CONTEXTS(name, reporter, context_info) \ + DEF_GPUTEST_FOR_CONTEXTS(name, &skiatest::IsGLContextType, reporter, context_info) +#define DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(name, reporter, context_info) \ + DEF_GPUTEST_FOR_CONTEXTS(name, &skiatest::IsRenderingGLContextType, reporter, context_info) +#define DEF_GPUTEST_FOR_NULLGL_CONTEXT(name, reporter, context_info) \ + DEF_GPUTEST_FOR_CONTEXTS(name, &skiatest::IsNullGLContextType, reporter, context_info) +#define DEF_GPUTEST_FOR_VULKAN_CONTEXT(name, reporter, context_info) \ + DEF_GPUTEST_FOR_CONTEXTS(name, &skiatest::IsVulkanContextType, reporter, context_info) + +#define REQUIRE_PDF_DOCUMENT(TEST_NAME, REPORTER) \ + do { \ + SkDynamicMemoryWStream testStream; \ + sk_sp<SkDocument> testDoc(SkDocument::MakePDF(&testStream)); \ + if (!testDoc) { \ + INFOF(REPORTER, "PDF disabled; %s test skipped.", #TEST_NAME); \ + return; \ + } \ + } while (false) #endif
diff --git a/src/third_party/skia/tests/TestConfigParsing.cpp b/src/third_party/skia/tests/TestConfigParsing.cpp new file mode 100644 index 0000000..e24c019 --- /dev/null +++ b/src/third_party/skia/tests/TestConfigParsing.cpp
@@ -0,0 +1,463 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCommonFlagsConfig.h" +#include "SkColorSpace_Base.h" +#include "Test.h" +#include <initializer_list> + +using sk_gpu_test::GrContextFactory; + +namespace { +// The code +// SkCommandLineFlags::StringArray FLAGS_config1 = make_string_array({"a", "b"}) +// can be used to construct string array that one gets with command line flags. +// For example, the call above is equivalent of +// DEFINE_string(config1, "a b", ""); +// in cases where the default command line flag value ("a b") is used. +// make_string_array can be used to construct StringArray strings that have spaces in +// them. +SkCommandLineFlags::StringArray make_string_array(std::initializer_list<const char*> strings) { + SkTArray<SkString> array; + for (auto& s : strings) { + array.push_back(SkString(s)); + } + return SkCommandLineFlags::StringArray(array); +} +} +DEF_TEST(ParseConfigs_Gpu, reporter) { + // Parses a normal config and returns correct "tag". + // Simple GL config works + SkCommandLineFlags::StringArray config1 = make_string_array({"gl"}); + SkCommandLineConfigArray configs; + ParseConfigs(config1, &configs); + + REPORTER_ASSERT(reporter, configs.count() == 1); + REPORTER_ASSERT(reporter, configs[0]->getTag().equals("gl")); + REPORTER_ASSERT(reporter, configs[0]->getViaParts().count() == 0); +#if SK_SUPPORT_GPU + REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getContextType() + == GrContextFactory::kGL_ContextType); + REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getUseNVPR() == false); + REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getUseInstanced() == false); + REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getUseDIText() == false); + REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getSamples() == 0); + REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getColorType() == kRGBA_8888_SkColorType); + REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getColorSpace() == nullptr); +#endif +} + +DEF_TEST(ParseConfigs_OutParam, reporter) { + // Clears the out parameter. + SkCommandLineFlags::StringArray config1 = make_string_array({"gles"}); + SkCommandLineConfigArray configs; + ParseConfigs(config1, &configs); + REPORTER_ASSERT(reporter, configs.count() == 1); + REPORTER_ASSERT(reporter, configs[0]->getTag().equals("gles")); + + SkCommandLineFlags::StringArray config2 = make_string_array({"8888"}); + ParseConfigs(config2, &configs); + REPORTER_ASSERT(reporter, configs.count() == 1); + REPORTER_ASSERT(reporter, configs[0]->getTag().equals("8888")); + + SkCommandLineFlags::StringArray config3 = make_string_array({"gl"}); + ParseConfigs(config3, &configs); + REPORTER_ASSERT(reporter, configs.count() == 1); + REPORTER_ASSERT(reporter, configs[0]->getTag().equals("gl")); +} + +DEF_TEST(ParseConfigs_DefaultConfigs, reporter) { + // Parses all default configs and returns correct "tag". + + SkCommandLineFlags::StringArray config1 = make_string_array({ + "565", + "8888", + "debuggl", + "gl", + "gldft", + "nullgl", + "glmsaa8", + "glmsaa4", + "nonrendering", + "nullgl", + "gles", + "glnvpr8", + "glnvpr4", + "glnvprdit8", + "glesnvprdit4", + "pdf", + "skp", + "svg", + "xps", + "angle_d3d11_es2", + "angle_gl_es2", + "commandbuffer", + "mesa", + "hwui", + "glf16", + "glessrgb", + "gl", + "glnvpr4", + "glnvprdit4", + "glsrgb", + "glmsaa4", + "vk", + "glinst", + "glinst4", + "glinstdit4", + "glinst8", + "glinstdit8", + "glesinst", + "glesinst4", + "glesinstdit4", + "glwide", + "glnarrow", + "glnostencils", + "mock", + "mtl", + "gl4444", + "gl565" + }); + + SkCommandLineConfigArray configs; + ParseConfigs(config1, &configs); + + auto srgbColorSpace = SkColorSpace::MakeSRGB(); + + REPORTER_ASSERT(reporter, configs.count() == config1.count()); + for (int i = 0; i < config1.count(); ++i) { + REPORTER_ASSERT(reporter, configs[i]->getTag().equals(config1[i])); + REPORTER_ASSERT(reporter, configs[i]->getViaParts().count() == 0); + } +#if SK_SUPPORT_GPU + REPORTER_ASSERT(reporter, !configs[0]->asConfigGpu()); + REPORTER_ASSERT(reporter, !configs[1]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[2]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[3]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[4]->asConfigGpu()->getUseDIText()); + REPORTER_ASSERT(reporter, configs[5]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[6]->asConfigGpu()->getSamples() == 8); + REPORTER_ASSERT(reporter, configs[7]->asConfigGpu()->getSamples() == 4); + REPORTER_ASSERT(reporter, !configs[8]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[9]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[10]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[11]->asConfigGpu()->getSamples() == 8); + REPORTER_ASSERT(reporter, configs[11]->asConfigGpu()->getUseNVPR()); + REPORTER_ASSERT(reporter, !configs[11]->asConfigGpu()->getUseDIText()); + REPORTER_ASSERT(reporter, configs[12]->asConfigGpu()->getSamples() == 4); + REPORTER_ASSERT(reporter, configs[12]->asConfigGpu()->getUseNVPR()); + REPORTER_ASSERT(reporter, !configs[12]->asConfigGpu()->getUseDIText()); + REPORTER_ASSERT(reporter, configs[13]->asConfigGpu()->getSamples() == 8); + REPORTER_ASSERT(reporter, configs[13]->asConfigGpu()->getUseNVPR()); + REPORTER_ASSERT(reporter, configs[13]->asConfigGpu()->getUseDIText()); + REPORTER_ASSERT(reporter, configs[14]->asConfigGpu()->getSamples() == 4); + REPORTER_ASSERT(reporter, configs[14]->asConfigGpu()->getUseNVPR()); + REPORTER_ASSERT(reporter, configs[14]->asConfigGpu()->getUseDIText()); + REPORTER_ASSERT(reporter, !configs[15]->asConfigGpu()); + REPORTER_ASSERT(reporter, !configs[16]->asConfigGpu()); + REPORTER_ASSERT(reporter, !configs[17]->asConfigGpu()); + REPORTER_ASSERT(reporter, !configs[18]->asConfigGpu()); + REPORTER_ASSERT(reporter, !configs[23]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[24]->asConfigGpu()->getColorType() == kRGBA_F16_SkColorType); + REPORTER_ASSERT(reporter, configs[24]->asConfigGpu()->getColorSpace()); + REPORTER_ASSERT(reporter, configs[24]->asConfigGpu()->getColorSpace()->gammaIsLinear()); + const SkMatrix44* srgbXYZ = as_CSB(srgbColorSpace)->toXYZD50(); + SkASSERT(srgbXYZ); + const SkMatrix44* config25XYZ = + as_CSB(configs[24]->asConfigGpu()->getColorSpace())->toXYZD50(); + SkASSERT(config25XYZ); + REPORTER_ASSERT(reporter, *config25XYZ == *srgbXYZ); + REPORTER_ASSERT(reporter, configs[25]->asConfigGpu()->getColorType() == kRGBA_8888_SkColorType); + REPORTER_ASSERT(reporter, configs[25]->asConfigGpu()->getColorSpace() == srgbColorSpace.get()); + REPORTER_ASSERT(reporter, configs[40]->asConfigGpu()->getColorType() == kRGBA_F16_SkColorType); + REPORTER_ASSERT(reporter, configs[40]->asConfigGpu()->getColorSpace()); + REPORTER_ASSERT(reporter, configs[40]->asConfigGpu()->getColorSpace()->gammaIsLinear()); + const SkMatrix44* config41XYZ = + as_CSB(configs[40]->asConfigGpu()->getColorSpace())->toXYZD50(); + SkASSERT(config41XYZ); + REPORTER_ASSERT(reporter, *config41XYZ != *srgbXYZ); + REPORTER_ASSERT(reporter, configs[32]->asConfigGpu()->getContextType() == + GrContextFactory::kGL_ContextType); + REPORTER_ASSERT(reporter, configs[41]->asConfigGpu()->getColorType() == kRGBA_F16_SkColorType); + REPORTER_ASSERT(reporter, configs[41]->asConfigGpu()->getColorSpace()); + REPORTER_ASSERT(reporter, configs[41]->asConfigGpu()->getColorSpace()->gammaIsLinear()); + REPORTER_ASSERT(reporter, *as_CSB(configs[41]->asConfigGpu()->getColorSpace())->toXYZD50() != + *as_CSB(srgbColorSpace)->toXYZD50()); + REPORTER_ASSERT(reporter, configs[42]->asConfigGpu()->getContextType() == + GrContextFactory::kGL_ContextType); + REPORTER_ASSERT(reporter, SkToBool(configs[42]->asConfigGpu()->getContextOverrides() & + SkCommandLineConfigGpu::ContextOverrides::kAvoidStencilBuffers)); + REPORTER_ASSERT(reporter, configs[43]->asConfigGpu()->getContextType() == + GrContextFactory::kMock_ContextType); + REPORTER_ASSERT(reporter, configs[32]->asConfigGpu()->getUseInstanced()); + REPORTER_ASSERT(reporter, configs[33]->asConfigGpu()->getContextType() == + GrContextFactory::kGL_ContextType); + REPORTER_ASSERT(reporter, configs[33]->asConfigGpu()->getUseInstanced()); + REPORTER_ASSERT(reporter, configs[33]->asConfigGpu()->getSamples() == 4); + REPORTER_ASSERT(reporter, configs[34]->asConfigGpu()->getContextType() == + GrContextFactory::kGL_ContextType); + REPORTER_ASSERT(reporter, configs[34]->asConfigGpu()->getUseInstanced()); + REPORTER_ASSERT(reporter, configs[34]->asConfigGpu()->getUseDIText()); + REPORTER_ASSERT(reporter, configs[34]->asConfigGpu()->getSamples() == 4); + REPORTER_ASSERT(reporter, configs[35]->asConfigGpu()->getContextType() == + GrContextFactory::kGL_ContextType); + REPORTER_ASSERT(reporter, configs[35]->asConfigGpu()->getUseInstanced()); + REPORTER_ASSERT(reporter, configs[35]->asConfigGpu()->getSamples() == 8); + REPORTER_ASSERT(reporter, configs[36]->asConfigGpu()->getContextType() == + GrContextFactory::kGL_ContextType); + REPORTER_ASSERT(reporter, configs[36]->asConfigGpu()->getUseInstanced()); + REPORTER_ASSERT(reporter, configs[36]->asConfigGpu()->getUseDIText()); + REPORTER_ASSERT(reporter, configs[36]->asConfigGpu()->getSamples() == 8); + REPORTER_ASSERT(reporter, configs[37]->asConfigGpu()->getContextType() == + GrContextFactory::kGLES_ContextType); + REPORTER_ASSERT(reporter, configs[37]->asConfigGpu()->getUseInstanced()); + REPORTER_ASSERT(reporter, configs[38]->asConfigGpu()->getContextType() == + GrContextFactory::kGLES_ContextType); + REPORTER_ASSERT(reporter, configs[38]->asConfigGpu()->getUseInstanced()); + REPORTER_ASSERT(reporter, configs[38]->asConfigGpu()->getSamples() == 4); + REPORTER_ASSERT(reporter, configs[39]->asConfigGpu()->getContextType() == + GrContextFactory::kGLES_ContextType); + REPORTER_ASSERT(reporter, configs[39]->asConfigGpu()->getUseInstanced()); + REPORTER_ASSERT(reporter, configs[39]->asConfigGpu()->getUseDIText()); + REPORTER_ASSERT(reporter, configs[39]->asConfigGpu()->getSamples() == 4); + REPORTER_ASSERT(reporter, configs[19]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[20]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[21]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[45]->asConfigGpu()->getContextType() == + GrContextFactory::kGL_ContextType); + REPORTER_ASSERT(reporter, configs[45]->asConfigGpu()->getColorType() == kARGB_4444_SkColorType); + REPORTER_ASSERT(reporter, configs[45]->asConfigGpu()->getAlphaType() == kPremul_SkAlphaType); + REPORTER_ASSERT(reporter, configs[46]->asConfigGpu()->getContextType() == + GrContextFactory::kGL_ContextType); + REPORTER_ASSERT(reporter, configs[46]->asConfigGpu()->getColorType() == kRGB_565_SkColorType); + REPORTER_ASSERT(reporter, configs[46]->asConfigGpu()->getAlphaType() == kOpaque_SkAlphaType); +#if SK_MESA + REPORTER_ASSERT(reporter, configs[23]->asConfigGpu()); +#else + REPORTER_ASSERT(reporter, !configs[22]->asConfigGpu()); +#endif + REPORTER_ASSERT(reporter, configs[26]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[27]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[27]->asConfigGpu()->getSamples() == 4); + REPORTER_ASSERT(reporter, configs[27]->asConfigGpu()->getUseNVPR()); + REPORTER_ASSERT(reporter, configs[28]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[28]->asConfigGpu()->getSamples() == 4); + REPORTER_ASSERT(reporter, configs[28]->asConfigGpu()->getUseNVPR()); + REPORTER_ASSERT(reporter, configs[28]->asConfigGpu()->getUseDIText()); + REPORTER_ASSERT(reporter, configs[29]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[29]->asConfigGpu()->getColorType() == kRGBA_8888_SkColorType); + REPORTER_ASSERT(reporter, configs[29]->asConfigGpu()->getColorSpace() == srgbColorSpace.get()); + REPORTER_ASSERT(reporter, configs[30]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[30]->asConfigGpu()->getSamples() == 4); +#ifdef SK_VULKAN + REPORTER_ASSERT(reporter, configs[31]->asConfigGpu()); +#endif +#endif +} + +DEF_TEST(ParseConfigs_ExtendedGpuConfigsCorrect, reporter) { + SkCommandLineFlags::StringArray config1 = make_string_array({ + "gpu[api=gl,nvpr=true,dit=false]", + "gpu[api=angle_d3d9_es2]", + "gpu[api=angle_gl_es3]", + "gpu[api=mesa,samples=77]", + "gpu[dit=true,api=commandbuffer]", + "gpu[api=gles]", + "gpu[api=gl]", + "gpu[api=vulkan]", + "gpu[api=metal]", + "gpu[api=mock]", + }); + + SkCommandLineConfigArray configs; + ParseConfigs(config1, &configs); + REPORTER_ASSERT(reporter, configs.count() == config1.count()); + for (int i = 0; i < config1.count(); ++i) { + REPORTER_ASSERT(reporter, configs[i]->getTag().equals(config1[i])); + } +#if SK_SUPPORT_GPU + REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getContextType() == + GrContextFactory::kGL_ContextType); + REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getUseNVPR()); + REPORTER_ASSERT(reporter, !configs[0]->asConfigGpu()->getUseDIText()); + REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getSamples() == 0); + REPORTER_ASSERT(reporter, configs[1]->asConfigGpu()->getContextType() == + GrContextFactory::kANGLE_D3D9_ES2_ContextType); + REPORTER_ASSERT(reporter, configs[1]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[2]->asConfigGpu()->getContextType() == + GrContextFactory::kANGLE_GL_ES3_ContextType); + REPORTER_ASSERT(reporter, configs[2]->asConfigGpu()); +#if SK_MESA + REPORTER_ASSERT(reporter, configs[3]->asConfigGpu()->getContextType() == + GrContextFactory::kMESA_ContextType); +#else + REPORTER_ASSERT(reporter, !configs[3]->asConfigGpu()); +#endif + REPORTER_ASSERT(reporter, configs[4]->asConfigGpu()->getContextType() == + GrContextFactory::kCommandBuffer_ContextType); + REPORTER_ASSERT(reporter, configs[5]->asConfigGpu()->getContextType() == + GrContextFactory::kGLES_ContextType); + REPORTER_ASSERT(reporter, !configs[5]->asConfigGpu()->getUseNVPR()); + REPORTER_ASSERT(reporter, !configs[5]->asConfigGpu()->getUseDIText()); + REPORTER_ASSERT(reporter, configs[5]->asConfigGpu()->getSamples() == 0); + REPORTER_ASSERT(reporter, configs[6]->asConfigGpu()->getContextType() == + GrContextFactory::kGL_ContextType); + REPORTER_ASSERT(reporter, !configs[6]->asConfigGpu()->getUseNVPR()); + REPORTER_ASSERT(reporter, !configs[6]->asConfigGpu()->getUseDIText()); + REPORTER_ASSERT(reporter, configs[6]->asConfigGpu()->getSamples() == 0); +#ifdef SK_VULKAN + REPORTER_ASSERT(reporter, configs[7]->asConfigGpu()->getContextType() == + GrContextFactory::kVulkan_ContextType); + REPORTER_ASSERT(reporter, !configs[7]->asConfigGpu()->getUseNVPR()); + REPORTER_ASSERT(reporter, !configs[7]->asConfigGpu()->getUseDIText()); + REPORTER_ASSERT(reporter, configs[7]->asConfigGpu()->getSamples() == 0); +#endif +#ifdef SK_METAL + REPORTER_ASSERT(reporter, configs[8]->asConfigGpu()->getContextType() == + GrContextFactory::kMetal_ContextType); + REPORTER_ASSERT(reporter, !configs[8]->asConfigGpu()->getUseNVPR()); + REPORTER_ASSERT(reporter, !configs[8]->asConfigGpu()->getUseDIText()); + REPORTER_ASSERT(reporter, configs[8]->asConfigGpu()->getSamples() == 0); +#endif + REPORTER_ASSERT(reporter, configs[9]->asConfigGpu()->getContextType() == + GrContextFactory::kMock_ContextType); +#endif +} + +DEF_TEST(ParseConfigs_ExtendedGpuConfigsIncorrect, reporter) { + SkCommandLineFlags::StringArray config1 = make_string_array({ + "gpu[api=gl,nvpr=1]", // Number as bool. + "gpu[api=gl,]", // Trailing in comma. + "gpu[api=angle_glu]", // Unknown api. + "gpu[api=,samples=0]", // Empty api. + "gpu[api=gl,samples=true]", // Value true as a number. + "gpu[api=gl,samples=0,samples=0]", // Duplicate option key. + "gpu[,api=gl,samples=0]", // Leading comma. + "gpu[samples=54", // Missing closing parenthesis. + ",,", + "gpu[]", // Missing required api specifier + "gpu[samples=4]", // Missing required api specifier + "gpu[", // Missing bracket. + "samples=54" // No backend. + "gpu[nvpr=true ]", // Space. + }); + + SkCommandLineConfigArray configs; + ParseConfigs(config1, &configs); + REPORTER_ASSERT(reporter, configs.count() == config1.count()); + for (int i = 0; i < config1.count(); ++i) { + REPORTER_ASSERT(reporter, configs[i]->getTag().equals(config1[i])); + REPORTER_ASSERT(reporter, configs[i]->getBackend().equals(config1[i])); +#if SK_SUPPORT_GPU + REPORTER_ASSERT(reporter, !configs[i]->asConfigGpu()); +#endif + } +} + +DEF_TEST(ParseConfigs_ExtendedGpuConfigsSurprises, reporter) { + // These just list explicitly some properties of the system. + SkCommandLineFlags::StringArray config1 = make_string_array({ + // Options are not canonized -> two same configs have a different tag. + "gpu[api=gl,nvpr=true,dit=true]", "gpu[api=gl,dit=true,nvpr=true]", + "gpu[api=debuggl]", "gpu[api=gl]", "gpu[api=gles]", "" + "gpu[api=gl]", "gpu[api=gl,samples=0]", "gpu[api=gles,samples=0]" + }); + SkCommandLineConfigArray configs; + ParseConfigs(config1, &configs); + REPORTER_ASSERT(reporter, configs.count() == config1.count()); + for (int i = 0; i < config1.count(); ++i) { + REPORTER_ASSERT(reporter, configs[i]->getTag().equals(config1[i])); +#if SK_SUPPORT_GPU + REPORTER_ASSERT(reporter, configs[i]->getBackend().equals("gpu")); + REPORTER_ASSERT(reporter, configs[i]->asConfigGpu()); +#else + REPORTER_ASSERT(reporter, configs[i]->getBackend().equals(config1[i])); +#endif + } +} + +#if SK_SUPPORT_GPU +DEF_TEST(ParseConfigs_ViaParsing, reporter) { + SkCommandLineFlags::StringArray config1 = make_string_array({ + "a-b-c-8888", + "zz-qq-gpu", + "a-angle_gl_es2" + }); + + SkCommandLineConfigArray configs; + ParseConfigs(config1, &configs); + const struct { + const char* backend; + const char* vias[3]; + } expectedConfigs[] = { + {"8888", {"a", "b", "c"}}, + {"gpu", {"zz", "qq", nullptr}}, + {"gpu", { "a", nullptr, nullptr }} + }; + for (int i = 0; i < config1.count(); ++i) { + REPORTER_ASSERT(reporter, configs[i]->getTag().equals(config1[i])); + REPORTER_ASSERT(reporter, configs[i]->getBackend().equals(expectedConfigs[i].backend)); + for (int j = 0; j < static_cast<int>(SK_ARRAY_COUNT(expectedConfigs[i].vias)); ++j) { + if (!expectedConfigs[i].vias[j]) { + REPORTER_ASSERT(reporter, configs[i]->getViaParts().count() == j); + break; + } + REPORTER_ASSERT(reporter, + configs[i]->getViaParts()[j].equals(expectedConfigs[i].vias[j])); + } + } +} +#endif + +DEF_TEST(ParseConfigs_ViaParsingExtendedForm, reporter) { + SkCommandLineFlags::StringArray config1 = make_string_array({ + "zz-qq-gpu[api=gles]", + "abc-nbc-cbs-gpu[api=angle_d3d9_es2,samples=1]", + "a-gpu[api=gl", + "abc-def-angle_gl_es2[api=gles]", + }); + + SkCommandLineConfigArray configs; + ParseConfigs(config1, &configs); + const struct { + const char* backend; + const char* vias[3]; + } expectedConfigs[] = { +#if SK_SUPPORT_GPU + {"gpu", {"zz", "qq", nullptr}}, + {"gpu", {"abc", "nbc", "cbs"}}, +#else + {"gpu[api=gles]", {"zz", "qq", nullptr}}, + {"gpu[api=angle_d3d9_es2,samples=1]", {"abc", "nbc", "cbs"}}, +#endif + {"gpu[api=gl", {"a", nullptr, nullptr}}, // Missing bracket makes this is not extended + // form but via still works as expected. + {"angle_gl_es2[api=gles]", {"abc", "def", nullptr}} // This is not extended form. + // angle_gl_es2 is an api type not a + // backend. + }; + for (int i = 0; i < config1.count(); ++i) { + REPORTER_ASSERT(reporter, configs[i]->getTag().equals(config1[i])); + REPORTER_ASSERT(reporter, configs[i]->getBackend().equals(expectedConfigs[i].backend)); + for (int j = 0; j < static_cast<int>(SK_ARRAY_COUNT(expectedConfigs[i].vias)); ++j) { + if (!expectedConfigs[i].vias[j]) { + REPORTER_ASSERT(reporter, configs[i]->getViaParts().count() == + static_cast<int>(j)); + break; + } + REPORTER_ASSERT(reporter, + configs[i]->getViaParts()[j].equals(expectedConfigs[i].vias[j])); + } + } +#if SK_SUPPORT_GPU + REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()); + REPORTER_ASSERT(reporter, configs[1]->asConfigGpu()); + REPORTER_ASSERT(reporter, !configs[2]->asConfigGpu()); + REPORTER_ASSERT(reporter, !configs[3]->asConfigGpu()); +#endif +}
diff --git a/src/third_party/skia/tests/TestTest.cpp b/src/third_party/skia/tests/TestTest.cpp new file mode 100644 index 0000000..d366bb4 --- /dev/null +++ b/src/third_party/skia/tests/TestTest.cpp
@@ -0,0 +1,54 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#if SK_SUPPORT_GPU +#include "GrContext.h" +#include "gl/GLTestContext.h" +#endif + + +// This is an example of a normal test. +DEF_TEST(TestNormal, reporter) { + REPORTER_ASSERT(reporter, reporter); +} + +// This is an example of a GPU test that uses common GrContextFactory factory to do the test. +#if SK_SUPPORT_GPU +DEF_GPUTEST(TestGpuFactory, reporter, factory) { + REPORTER_ASSERT(reporter, reporter); + REPORTER_ASSERT(reporter, factory); +} +#endif + +// This is an example of a GPU test that tests a property that should work for all GPU contexts. +// Note: Some of the contexts might not produce a rendering output. +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_ALL_CONTEXTS(TestGpuAllContexts, reporter, ctxInfo) { + REPORTER_ASSERT(reporter, reporter); + REPORTER_ASSERT(reporter, ctxInfo.grContext()); +} +#endif + +// This is an example of a GPU test that tests a property that should work for all GPU contexts that +// produce a rendering output. +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TestGpuRenderingContexts, reporter, ctxInfo) { + REPORTER_ASSERT(reporter, reporter); + REPORTER_ASSERT(reporter, ctxInfo.grContext()); +} +#endif + +// This is an example of a GPU test that tests a property that uses the null GPU context. It should +// be used if the test tests some behavior that is mocked with the null context. +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_NULLGL_CONTEXT(TestGpuNullContext, reporter, ctxInfo) { + REPORTER_ASSERT(reporter, reporter); + REPORTER_ASSERT(reporter, ctxInfo.grContext()); +} +#endif
diff --git a/src/third_party/skia/tests/TestUtils.cpp b/src/third_party/skia/tests/TestUtils.cpp new file mode 100644 index 0000000..aca7509 --- /dev/null +++ b/src/third_party/skia/tests/TestUtils.cpp
@@ -0,0 +1,123 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "TestUtils.h" + +#if SK_SUPPORT_GPU + +#include "GrSurfaceContext.h" +#include "GrSurfaceProxy.h" +#include "GrTextureProxy.h" + +void test_read_pixels(skiatest::Reporter* reporter, + GrSurfaceContext* srcContext, uint32_t expectedPixelValues[], + const char* testName) { + int pixelCnt = srcContext->width() * srcContext->height(); + SkAutoTMalloc<uint32_t> pixels(pixelCnt); + memset(pixels.get(), 0, sizeof(uint32_t)*pixelCnt); + + SkImageInfo ii = SkImageInfo::Make(srcContext->width(), srcContext->height(), + kRGBA_8888_SkColorType, kPremul_SkAlphaType); + bool read = srcContext->readPixels(ii, pixels.get(), 0, 0, 0); + if (!read) { + ERRORF(reporter, "%s: Error reading from texture.", testName); + } + + for (int i = 0; i < pixelCnt; ++i) { + if (pixels.get()[i] != expectedPixelValues[i]) { + ERRORF(reporter, "%s: Error, pixel value %d should be 0x%08x, got 0x%08x.", + testName, i, expectedPixelValues[i], pixels.get()[i]); + break; + } + } +} + +void test_write_pixels(skiatest::Reporter* reporter, + GrSurfaceContext* dstContext, bool expectedToWork, + const char* testName) { + int pixelCnt = dstContext->width() * dstContext->height(); + SkAutoTMalloc<uint32_t> pixels(pixelCnt); + for (int y = 0; y < dstContext->width(); ++y) { + for (int x = 0; x < dstContext->height(); ++x) { + pixels.get()[y * dstContext->width() + x] = + GrPremulColor(GrColorPackRGBA(x, y, x + y, 2*y)); + } + } + + SkImageInfo ii = SkImageInfo::Make(dstContext->width(), dstContext->height(), + kRGBA_8888_SkColorType, kPremul_SkAlphaType); + bool write = dstContext->writePixels(ii, pixels.get(), 0, 0, 0); + if (!write) { + if (expectedToWork) { + ERRORF(reporter, "%s: Error writing to texture.", testName); + } + return; + } + + if (write && !expectedToWork) { + ERRORF(reporter, "%s: writePixels succeeded when it wasn't supposed to.", testName); + return; + } + + test_read_pixels(reporter, dstContext, pixels.get(), testName); +} + +void test_copy_from_surface(skiatest::Reporter* reporter, GrContext* context, + GrSurfaceProxy* proxy, uint32_t expectedPixelValues[], + bool onlyTestRTConfig, const char* testName) { + GrSurfaceDesc copyDstDesc; + copyDstDesc.fConfig = kRGBA_8888_GrPixelConfig; + copyDstDesc.fWidth = proxy->width(); + copyDstDesc.fHeight = proxy->height(); + + for (auto flags : { kNone_GrSurfaceFlags, kRenderTarget_GrSurfaceFlag }) { + if (kNone_GrSurfaceFlags == flags && onlyTestRTConfig) { + continue; + } + + copyDstDesc.fFlags = flags; + copyDstDesc.fOrigin = (kNone_GrSurfaceFlags == flags) ? kTopLeft_GrSurfaceOrigin + : kBottomLeft_GrSurfaceOrigin; + + sk_sp<GrSurfaceContext> dstContext(GrSurfaceProxy::TestCopy(context, copyDstDesc, proxy)); + + test_read_pixels(reporter, dstContext.get(), expectedPixelValues, testName); + } +} + +void test_copy_to_surface(skiatest::Reporter* reporter, GrResourceProvider* resourceProvider, + GrSurfaceContext* dstContext, const char* testName) { + + int pixelCnt = dstContext->width() * dstContext->height(); + SkAutoTMalloc<uint32_t> pixels(pixelCnt); + for (int y = 0; y < dstContext->width(); ++y) { + for (int x = 0; x < dstContext->height(); ++x) { + pixels.get()[y * dstContext->width() + x] = + GrPremulColor(GrColorPackRGBA(y, x, x * y, 2*y)); + } + } + + GrSurfaceDesc copySrcDesc; + copySrcDesc.fConfig = kRGBA_8888_GrPixelConfig; + copySrcDesc.fWidth = dstContext->width(); + copySrcDesc.fHeight = dstContext->height(); + + for (auto flags : { kNone_GrSurfaceFlags, kRenderTarget_GrSurfaceFlag }) { + copySrcDesc.fFlags = flags; + copySrcDesc.fOrigin = (kNone_GrSurfaceFlags == flags) ? kTopLeft_GrSurfaceOrigin + : kBottomLeft_GrSurfaceOrigin; + + sk_sp<GrTextureProxy> src(GrSurfaceProxy::MakeDeferred(resourceProvider, + copySrcDesc, + SkBudgeted::kYes, pixels.get(), 0)); + dstContext->copy(src.get()); + + test_read_pixels(reporter, dstContext, pixels.get(), testName); + } +} + +#endif
diff --git a/src/third_party/skia/tests/TestUtils.h b/src/third_party/skia/tests/TestUtils.h new file mode 100644 index 0000000..6f878bf --- /dev/null +++ b/src/third_party/skia/tests/TestUtils.h
@@ -0,0 +1,34 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#if SK_SUPPORT_GPU + +class GrSurfaceContext; +class GrSurfaceProxy; + +// Ensure that reading back from 'srcContext' as RGBA 8888 matches 'expectedPixelValues +void test_read_pixels(skiatest::Reporter*, + GrSurfaceContext* srcContext, uint32_t expectedPixelValues[], + const char* testName); + +// See if trying to write RGBA 8888 pixels to 'dstContext' matches matches the +// expectation ('expectedToWork') +void test_write_pixels(skiatest::Reporter*, + GrSurfaceContext* srcContext, bool expectedToWork, const char* testName); + +// Ensure that the pixels can be copied from 'proxy' to an RGBA 8888 destination (both +// texture-backed and rendertarget-backed). +void test_copy_from_surface(skiatest::Reporter*, GrContext*, + GrSurfaceProxy* proxy, uint32_t expectedPixelValues[], + bool onlyTestRTConfig, const char* testName); + +// Ensure that RGBA 8888 pixels can be copied into 'dstContext' +void test_copy_to_surface(skiatest::Reporter*, GrResourceProvider*, + GrSurfaceContext* dstContext, const char* testName); +#endif
diff --git a/src/third_party/skia/tests/TextBlobCacheTest.cpp b/src/third_party/skia/tests/TextBlobCacheTest.cpp new file mode 100644 index 0000000..995328d --- /dev/null +++ b/src/third_party/skia/tests/TextBlobCacheTest.cpp
@@ -0,0 +1,165 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "sk_tool_utils.h" + +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkPoint.h" +#include "SkTextBlob.h" +#include "SkFontMgr.h" +#include "SkGraphics.h" +#include "SkSurface.h" +#include "SkTypeface.h" +#include "../src/fonts/SkRandomScalerContext.h" + +#ifdef SK_BUILD_FOR_WIN + #include "SkTypeface_win.h" +#endif + +#include "Test.h" + +#if SK_SUPPORT_GPU +#include "GrContext.h" +#include "GrTest.h" + +static void draw(SkCanvas* canvas, int redraw, const SkTArray<sk_sp<SkTextBlob>>& blobs) { + int yOffset = 0; + for (int r = 0; r < redraw; r++) { + for (int i = 0; i < blobs.count(); i++) { + const auto& blob = blobs[i]; + const SkRect& bounds = blob->bounds(); + yOffset += SkScalarCeilToInt(bounds.height()); + SkPaint paint; + canvas->drawTextBlob(blob, 0, SkIntToScalar(yOffset), paint); + } + } +} + +static const int kWidth = 1024; +static const int kHeight = 768; + +// This test hammers the GPU textblobcache and font atlas +static void text_blob_cache_inner(skiatest::Reporter* reporter, GrContext* context, + int maxTotalText, int maxGlyphID, int maxFamilies, bool normal, + bool stressTest) { + // setup surface + uint32_t flags = 0; + SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); + + // configure our context for maximum stressing of cache and atlas + if (stressTest) { + GrTest::SetupAlwaysEvictAtlas(context); + context->setTextBlobCacheLimit_ForTesting(0); + } + + SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, kN32_SkColorType, kPremul_SkAlphaType); + auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 0, &props)); + REPORTER_ASSERT(reporter, surface); + if (!surface) { + return; + } + + SkCanvas* canvas = surface->getCanvas(); + + sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); + + int count = SkMin32(fm->countFamilies(), maxFamilies); + + // make a ton of text + SkAutoTArray<uint16_t> text(maxTotalText); + for (int i = 0; i < maxTotalText; i++) { + text[i] = i % maxGlyphID; + } + + // generate textblobs + SkTArray<sk_sp<SkTextBlob>> blobs; + for (int i = 0; i < count; i++) { + SkPaint paint; + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint.setTextSize(48); // draw big glyphs to really stress the atlas + + SkString familyName; + fm->getFamilyName(i, &familyName); + sk_sp<SkFontStyleSet> set(fm->createStyleSet(i)); + for (int j = 0; j < set->count(); ++j) { + SkFontStyle fs; + set->getStyle(j, &fs, nullptr); + + // We use a typeface which randomy returns unexpected mask formats to fuzz + sk_sp<SkTypeface> orig(set->createTypeface(j)); + if (normal) { + paint.setTypeface(orig); + } else { + paint.setTypeface(sk_make_sp<SkRandomTypeface>(orig, paint, true)); + } + + SkTextBlobBuilder builder; + for (int aa = 0; aa < 2; aa++) { + for (int subpixel = 0; subpixel < 2; subpixel++) { + for (int lcd = 0; lcd < 2; lcd++) { + paint.setAntiAlias(SkToBool(aa)); + paint.setSubpixelText(SkToBool(subpixel)); + paint.setLCDRenderText(SkToBool(lcd)); + if (!SkToBool(lcd)) { + paint.setTextSize(160); + } + const SkTextBlobBuilder::RunBuffer& run = builder.allocRun(paint, + maxTotalText, + 0, 0, + nullptr); + memcpy(run.glyphs, text.get(), maxTotalText * sizeof(uint16_t)); + } + } + } + blobs.emplace_back(builder.make()); + } + } + + // create surface where LCD is impossible + info = SkImageInfo::MakeN32Premul(kWidth, kHeight); + SkSurfaceProps propsNoLCD(0, kUnknown_SkPixelGeometry); + auto surfaceNoLCD(canvas->makeSurface(info, &propsNoLCD)); + REPORTER_ASSERT(reporter, surface); + if (!surface) { + return; + } + + SkCanvas* canvasNoLCD = surfaceNoLCD->getCanvas(); + + // test redraw + draw(canvas, 2, blobs); + draw(canvasNoLCD, 2, blobs); + + // test draw after free + context->freeGpuResources(); + draw(canvas, 1, blobs); + + context->freeGpuResources(); + draw(canvasNoLCD, 1, blobs); + + // test draw after abandon + context->abandonContext(); + draw(canvas, 1, blobs); +} + +DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobCache, reporter, ctxInfo) { + text_blob_cache_inner(reporter, ctxInfo.grContext(), 1024, 256, 30, true, false); +} + +DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobStressCache, reporter, ctxInfo) { + text_blob_cache_inner(reporter, ctxInfo.grContext(), 256, 256, 10, true, true); +} + +DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobAbnormal, reporter, ctxInfo) { + text_blob_cache_inner(reporter, ctxInfo.grContext(), 256, 256, 10, false, false); +} + +DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobStressAbnormal, reporter, ctxInfo) { + text_blob_cache_inner(reporter, ctxInfo.grContext(), 256, 256, 10, false, true); +} +#endif
diff --git a/src/third_party/skia/tests/TextBlobTest.cpp b/src/third_party/skia/tests/TextBlobTest.cpp index 5d08b01..c00970f 100644 --- a/src/third_party/skia/tests/TextBlobTest.cpp +++ b/src/third_party/skia/tests/TextBlobTest.cpp
@@ -7,11 +7,11 @@ #include "SkPaint.h" #include "SkPoint.h" -#include "SkTextBlob.h" +#include "SkTextBlobRunIterator.h" +#include "SkTypeface.h" #include "Test.h" - class TextBlobTester { public: // This unit test feeds an SkTextBlobBuilder various runs then checks to see if @@ -20,7 +20,7 @@ SkTextBlobBuilder builder; // empty run set - RunBuilderTest(reporter, builder, NULL, 0, NULL, 0); + RunBuilderTest(reporter, builder, nullptr, 0, nullptr, 0); RunDef set1[] = { { 128, SkTextBlob::kDefault_Positioning, 100, 100 }, @@ -104,28 +104,28 @@ // Explicit bounds. { - SkAutoTUnref<const SkTextBlob> blob(builder.build()); - REPORTER_ASSERT(reporter, blob->bounds().isEmpty()); + sk_sp<SkTextBlob> blob(builder.make()); + REPORTER_ASSERT(reporter, !blob); } { SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20); builder.allocRun(font, 16, 0, 0, &r1); - SkAutoTUnref<const SkTextBlob> blob(builder.build()); + sk_sp<SkTextBlob> blob(builder.make()); REPORTER_ASSERT(reporter, blob->bounds() == r1); } { SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20); builder.allocRunPosH(font, 16, 0, &r1); - SkAutoTUnref<const SkTextBlob> blob(builder.build()); + sk_sp<SkTextBlob> blob(builder.make()); REPORTER_ASSERT(reporter, blob->bounds() == r1); } { SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20); builder.allocRunPos(font, 16, &r1); - SkAutoTUnref<const SkTextBlob> blob(builder.build()); + sk_sp<SkTextBlob> blob(builder.make()); REPORTER_ASSERT(reporter, blob->bounds() == r1); } @@ -138,18 +138,115 @@ builder.allocRunPosH(font, 16, 0, &r2); builder.allocRunPos(font, 16, &r3); - SkAutoTUnref<const SkTextBlob> blob(builder.build()); + sk_sp<SkTextBlob> blob(builder.make()); REPORTER_ASSERT(reporter, blob->bounds() == SkRect::MakeXYWH(0, 5, 65, 65)); } { - // Verify empty blob bounds after building some non-empty blobs. - SkAutoTUnref<const SkTextBlob> blob(builder.build()); - REPORTER_ASSERT(reporter, blob->bounds().isEmpty()); + sk_sp<SkTextBlob> blob(builder.make()); + REPORTER_ASSERT(reporter, !blob); } // Implicit bounds - // FIXME: not supported yet. + + { + // Exercise the empty bounds path, and ensure that RunRecord-aligned pos buffers + // don't trigger asserts (http://crbug.com/542643). + SkPaint p; + p.setTextSize(0); + p.setTextEncoding(SkPaint::kUTF8_TextEncoding); + + const char* txt = "BOOO"; + const size_t txtLen = strlen(txt); + const int glyphCount = p.textToGlyphs(txt, txtLen, nullptr); + + p.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPos(p, glyphCount); + + p.setTextEncoding(SkPaint::kUTF8_TextEncoding); + p.textToGlyphs(txt, txtLen, buffer.glyphs); + + memset(buffer.pos, 0, sizeof(SkScalar) * glyphCount * 2); + sk_sp<SkTextBlob> blob(builder.make()); + REPORTER_ASSERT(reporter, blob->bounds().isEmpty()); + } + } + + // Verify that text-related properties are captured in run paints. + static void TestPaintProps(skiatest::Reporter* reporter) { + SkPaint font; + font.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + // Kitchen sink font. + font.setTextSize(42); + font.setTextScaleX(4.2f); + font.setTypeface(SkTypeface::MakeDefault()); + font.setTextSkewX(0.42f); + font.setTextAlign(SkPaint::kCenter_Align); + font.setHinting(SkPaint::kFull_Hinting); + font.setAntiAlias(true); + font.setFakeBoldText(true); + font.setLinearText(true); + font.setSubpixelText(true); + font.setDevKernText(true); + font.setLCDRenderText(true); + font.setEmbeddedBitmapText(true); + font.setAutohinted(true); + font.setVerticalText(true); + font.setFlags(font.getFlags() | SkPaint::kGenA8FromLCD_Flag); + + // Ensure we didn't pick default values by mistake. + SkPaint defaultPaint; + REPORTER_ASSERT(reporter, defaultPaint.getTextSize() != font.getTextSize()); + REPORTER_ASSERT(reporter, defaultPaint.getTextScaleX() != font.getTextScaleX()); + REPORTER_ASSERT(reporter, defaultPaint.getTypeface() != font.getTypeface()); + REPORTER_ASSERT(reporter, defaultPaint.getTextSkewX() != font.getTextSkewX()); + REPORTER_ASSERT(reporter, defaultPaint.getTextAlign() != font.getTextAlign()); + REPORTER_ASSERT(reporter, defaultPaint.getHinting() != font.getHinting()); + REPORTER_ASSERT(reporter, defaultPaint.isAntiAlias() != font.isAntiAlias()); + REPORTER_ASSERT(reporter, defaultPaint.isFakeBoldText() != font.isFakeBoldText()); + REPORTER_ASSERT(reporter, defaultPaint.isLinearText() != font.isLinearText()); + REPORTER_ASSERT(reporter, defaultPaint.isSubpixelText() != font.isSubpixelText()); + REPORTER_ASSERT(reporter, defaultPaint.isDevKernText() != font.isDevKernText()); + REPORTER_ASSERT(reporter, defaultPaint.isLCDRenderText() != font.isLCDRenderText()); + REPORTER_ASSERT(reporter, defaultPaint.isEmbeddedBitmapText() != font.isEmbeddedBitmapText()); + REPORTER_ASSERT(reporter, defaultPaint.isAutohinted() != font.isAutohinted()); + REPORTER_ASSERT(reporter, defaultPaint.isVerticalText() != font.isVerticalText()); + REPORTER_ASSERT(reporter, (defaultPaint.getFlags() & SkPaint::kGenA8FromLCD_Flag) != + (font.getFlags() & SkPaint::kGenA8FromLCD_Flag)); + + SkTextBlobBuilder builder; + AddRun(font, 1, SkTextBlob::kDefault_Positioning, SkPoint::Make(0, 0), builder); + AddRun(font, 1, SkTextBlob::kHorizontal_Positioning, SkPoint::Make(0, 0), builder); + AddRun(font, 1, SkTextBlob::kFull_Positioning, SkPoint::Make(0, 0), builder); + sk_sp<SkTextBlob> blob(builder.make()); + + SkTextBlobRunIterator it(blob.get()); + while (!it.done()) { + SkPaint paint; + it.applyFontToPaint(&paint); + + REPORTER_ASSERT(reporter, paint.getTextSize() == font.getTextSize()); + REPORTER_ASSERT(reporter, paint.getTextScaleX() == font.getTextScaleX()); + REPORTER_ASSERT(reporter, paint.getTypeface() == font.getTypeface()); + REPORTER_ASSERT(reporter, paint.getTextSkewX() == font.getTextSkewX()); + REPORTER_ASSERT(reporter, paint.getTextAlign() == font.getTextAlign()); + REPORTER_ASSERT(reporter, paint.getHinting() == font.getHinting()); + REPORTER_ASSERT(reporter, paint.isAntiAlias() == font.isAntiAlias()); + REPORTER_ASSERT(reporter, paint.isFakeBoldText() == font.isFakeBoldText()); + REPORTER_ASSERT(reporter, paint.isLinearText() == font.isLinearText()); + REPORTER_ASSERT(reporter, paint.isSubpixelText() == font.isSubpixelText()); + REPORTER_ASSERT(reporter, paint.isDevKernText() == font.isDevKernText()); + REPORTER_ASSERT(reporter, paint.isLCDRenderText() == font.isLCDRenderText()); + REPORTER_ASSERT(reporter, paint.isEmbeddedBitmapText() == font.isEmbeddedBitmapText()); + REPORTER_ASSERT(reporter, paint.isAutohinted() == font.isAutohinted()); + REPORTER_ASSERT(reporter, paint.isVerticalText() == font.isVerticalText()); + REPORTER_ASSERT(reporter, (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) == + (font.getFlags() & SkPaint::kGenA8FromLCD_Flag)); + + it.next(); + } + } private: @@ -174,9 +271,13 @@ posCount += in[i].count * in[i].pos; } - SkAutoTUnref<const SkTextBlob> blob(builder.build()); + sk_sp<SkTextBlob> blob(builder.make()); + REPORTER_ASSERT(reporter, (inCount > 0) == SkToBool(blob)); + if (!blob) { + return; + } - SkTextBlob::RunIterator it(blob); + SkTextBlobRunIterator it(blob.get()); for (unsigned i = 0; i < outCount; ++i) { REPORTER_ASSERT(reporter, !it.done()); REPORTER_ASSERT(reporter, out[i].pos == it.positioning()); @@ -206,7 +307,7 @@ static void AddRun(const SkPaint& font, int count, SkTextBlob::GlyphPositioning pos, const SkPoint& offset, SkTextBlobBuilder& builder, - const SkRect* bounds = NULL) { + const SkRect* bounds = nullptr) { switch (pos) { case SkTextBlob::kDefault_Positioning: { const SkTextBlobBuilder::RunBuffer& rb = builder.allocRun(font, count, offset.x(), @@ -241,3 +342,128 @@ TextBlobTester::TestBuilder(reporter); TextBlobTester::TestBounds(reporter); } + +DEF_TEST(TextBlob_paint, reporter) { + TextBlobTester::TestPaintProps(reporter); +} + +DEF_TEST(TextBlob_extended, reporter) { + SkTextBlobBuilder textBlobBuilder; + SkPaint paint; + const char text1[] = "Foo"; + const char text2[] = "Bar"; + + int glyphCount = paint.textToGlyphs(text1, strlen(text1), nullptr); + SkAutoTMalloc<uint16_t> glyphs(glyphCount); + (void)paint.textToGlyphs(text1, strlen(text1), glyphs.get()); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + auto run = textBlobBuilder.allocRunText( + paint, glyphCount, 0, 0, SkToInt(strlen(text2)), SkString(), nullptr); + memcpy(run.glyphs, glyphs.get(), sizeof(uint16_t) * glyphCount); + memcpy(run.utf8text, text2, strlen(text2)); + for (int i = 0; i < glyphCount; ++i) { + run.clusters[i] = SkTMin(SkToU32(i), SkToU32(strlen(text2))); + } + sk_sp<SkTextBlob> blob(textBlobBuilder.make()); + REPORTER_ASSERT(reporter, blob); + + for (SkTextBlobRunIterator it(blob.get()); !it.done(); it.next()) { + REPORTER_ASSERT(reporter, it.glyphCount() == (uint32_t)glyphCount); + for (uint32_t i = 0; i < it.glyphCount(); ++i) { + REPORTER_ASSERT(reporter, it.glyphs()[i] == glyphs[i]); + } + REPORTER_ASSERT(reporter, SkTextBlob::kDefault_Positioning == it.positioning()); + REPORTER_ASSERT(reporter, (SkPoint{0.0f, 0.0f}) == it.offset()); + REPORTER_ASSERT(reporter, it.textSize() > 0); + REPORTER_ASSERT(reporter, it.clusters()); + for (uint32_t i = 0; i < it.glyphCount(); ++i) { + REPORTER_ASSERT(reporter, i == it.clusters()[i]); + } + REPORTER_ASSERT(reporter, 0 == strncmp(text2, it.text(), it.textSize())); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#include "SkCanvas.h" +#include "SkSurface.h" +#include "SkTDArray.h" + +static void add_run(SkTextBlobBuilder* builder, const char text[], SkScalar x, SkScalar y, + sk_sp<SkTypeface> tf) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setSubpixelText(true); + paint.setTextSize(16); + paint.setTypeface(tf); + + int glyphCount = paint.textToGlyphs(text, strlen(text), nullptr); + + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + SkTextBlobBuilder::RunBuffer buffer = builder->allocRun(paint, glyphCount, x, y); + + paint.setTextEncoding(SkPaint::kUTF8_TextEncoding); + (void)paint.textToGlyphs(text, strlen(text), buffer.glyphs); +} + +static sk_sp<SkImage> render(const SkTextBlob* blob) { + auto surf = SkSurface::MakeRasterN32Premul(SkScalarRoundToInt(blob->bounds().width()), + SkScalarRoundToInt(blob->bounds().height())); + if (!surf) { + return nullptr; // bounds are empty? + } + surf->getCanvas()->clear(SK_ColorWHITE); + surf->getCanvas()->drawTextBlob(blob, -blob->bounds().left(), -blob->bounds().top(), SkPaint()); + return surf->makeImageSnapshot(); +} + +/* + * Build a blob with more than one typeface. + * Draw it into an offscreen, + * then serialize and deserialize, + * Then draw the new instance and assert it draws the same as the original. + */ +DEF_TEST(TextBlob_serialize, reporter) { + SkTextBlobBuilder builder; + + sk_sp<SkTypeface> tf0; + sk_sp<SkTypeface> tf1 = SkTypeface::MakeFromName("Times", SkFontStyle()); + + add_run(&builder, "Hello", 10, 20, tf0); + add_run(&builder, "World", 10, 40, tf1); + sk_sp<SkTextBlob> blob0 = builder.make(); + + SkTDArray<SkTypeface*> array; + sk_sp<SkData> data = blob0->serialize([&array](SkTypeface* tf) { + if (array.find(tf) < 0) { + *array.append() = tf; + } + }); + REPORTER_ASSERT(reporter, array.count() > 0); + + sk_sp<SkTextBlob> blob1 = SkTextBlob::Deserialize(data->data(), data->size(), + [&array, reporter](uint32_t uniqueID) { + for (int i = 0; i < array.count(); ++i) { + if (array[i]->uniqueID() == uniqueID) { + return sk_ref_sp(array[i]); + } + } + REPORTER_ASSERT(reporter, false); + return sk_sp<SkTypeface>(nullptr); + }); + + sk_sp<SkImage> img0 = render(blob0.get()); + sk_sp<SkImage> img1 = render(blob1.get()); + if (img0 && img1) { + REPORTER_ASSERT(reporter, img0->width() == img1->width()); + REPORTER_ASSERT(reporter, img0->height() == img1->height()); + + sk_sp<SkData> enc0 = img0->encodeToData(); + sk_sp<SkData> enc1 = img1->encodeToData(); + REPORTER_ASSERT(reporter, enc0->equals(enc1.get())); + if (false) { // in case you want to actually see the images... + SkFILEWStream("textblob_serialize_img0.png").write(enc0->data(), enc0->size()); + SkFILEWStream("textblob_serialize_img1.png").write(enc1->data(), enc1->size()); + } + } +}
diff --git a/src/third_party/skia/tests/TextureCompressionTest.cpp b/src/third_party/skia/tests/TextureCompressionTest.cpp deleted file mode 100644 index 7dd285d..0000000 --- a/src/third_party/skia/tests/TextureCompressionTest.cpp +++ /dev/null
@@ -1,272 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkBitmap.h" -#include "SkData.h" -#include "SkEndian.h" -#include "SkImageInfo.h" -#include "SkTextureCompressor.h" -#include "Test.h" - -// TODO: Create separate tests for RGB and RGBA data once -// ASTC and ETC1 decompression is implemented. - -static bool decompresses_a8(SkTextureCompressor::Format fmt) { - switch (fmt) { - case SkTextureCompressor::kLATC_Format: - case SkTextureCompressor::kR11_EAC_Format: - return true; - - default: - return false; - } -} - -static bool compresses_a8(SkTextureCompressor::Format fmt) { - switch (fmt) { - case SkTextureCompressor::kLATC_Format: - case SkTextureCompressor::kR11_EAC_Format: - case SkTextureCompressor::kASTC_12x12_Format: - return true; - - default: - return false; - } -} - -/** - * Make sure that we properly fail when we don't have multiple of four image dimensions. - */ -DEF_TEST(CompressAlphaFailDimensions, reporter) { - SkBitmap bitmap; - static const int kWidth = 17; - static const int kHeight = 17; - SkImageInfo info = SkImageInfo::MakeA8(kWidth, kHeight); - - // R11_EAC and LATC are both dimensions of 4, so we need to make sure that we - // are violating those assumptions. And if we are, then we're also violating the - // assumptions of ASTC, which is 12x12 since any number not divisible by 4 is - // also not divisible by 12. Our dimensions are prime, so any block dimension - // larger than 1 should fail. - REPORTER_ASSERT(reporter, kWidth % 4 != 0); - REPORTER_ASSERT(reporter, kHeight % 4 != 0); - - bool setInfoSuccess = bitmap.setInfo(info); - REPORTER_ASSERT(reporter, setInfoSuccess); - - bitmap.allocPixels(info); - bitmap.unlockPixels(); - - for (int i = 0; i < SkTextureCompressor::kFormatCnt; ++i) { - const SkTextureCompressor::Format fmt = static_cast<SkTextureCompressor::Format>(i); - if (!compresses_a8(fmt)) { - continue; - } - SkAutoDataUnref data(SkTextureCompressor::CompressBitmapToFormat(bitmap, fmt)); - REPORTER_ASSERT(reporter, NULL == data); - } -} - -/** - * Make sure that we properly fail when we don't have the correct bitmap type. - * compressed textures can (currently) only be created from A8 bitmaps. - */ -DEF_TEST(CompressAlphaFailColorType, reporter) { - SkBitmap bitmap; - static const int kWidth = 12; - static const int kHeight = 12; - SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight); - - // ASTC is at most 12x12, and any dimension divisible by 12 is also divisible - // by 4, which is the dimensions of R11_EAC and LATC. In the future, we might - // support additional variants of ASTC, such as 5x6 and 8x8, in which case this would - // need to be updated. - REPORTER_ASSERT(reporter, kWidth % 12 == 0); - REPORTER_ASSERT(reporter, kHeight % 12 == 0); - - bool setInfoSuccess = bitmap.setInfo(info); - REPORTER_ASSERT(reporter, setInfoSuccess); - - bitmap.allocPixels(info); - bitmap.unlockPixels(); - - for (int i = 0; i < SkTextureCompressor::kFormatCnt; ++i) { - const SkTextureCompressor::Format fmt = static_cast<SkTextureCompressor::Format>(i); - if (!compresses_a8(fmt)) { - continue; - } - SkAutoDataUnref data(SkTextureCompressor::CompressBitmapToFormat(bitmap, fmt)); - REPORTER_ASSERT(reporter, NULL == data); - } -} - -/** - * Make sure that if you compress a texture with alternating black/white pixels, and - * then decompress it, you get what you started with. - */ -DEF_TEST(CompressCheckerboard, reporter) { - SkBitmap bitmap; - static const int kWidth = 48; // We need the number to be divisible by both - static const int kHeight = 48; // 12 (ASTC) and 16 (ARM NEON R11 EAC). - SkImageInfo info = SkImageInfo::MakeA8(kWidth, kHeight); - - // ASTC is at most 12x12, and any dimension divisible by 12 is also divisible - // by 4, which is the dimensions of R11_EAC and LATC. In the future, we might - // support additional variants of ASTC, such as 5x6 and 8x8, in which case this would - // need to be updated. Additionally, ARM NEON and SSE code paths support up to - // four blocks of R11 EAC at once, so they operate on 16-wide blocks. Hence, the - // valid width and height is going to be the LCM of 12 and 16 which is 4*4*3 = 48 - REPORTER_ASSERT(reporter, kWidth % 48 == 0); - REPORTER_ASSERT(reporter, kHeight % 48 == 0); - - bool setInfoSuccess = bitmap.setInfo(info); - REPORTER_ASSERT(reporter, setInfoSuccess); - - bitmap.allocPixels(info); - bitmap.unlockPixels(); - - // Populate bitmap - { - SkAutoLockPixels alp(bitmap); - - uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap.getPixels()); - REPORTER_ASSERT(reporter, pixels); - if (NULL == pixels) { - return; - } - - for (int y = 0; y < kHeight; ++y) { - for (int x = 0; x < kWidth; ++x) { - if ((x ^ y) & 1) { - pixels[x] = 0xFF; - } else { - pixels[x] = 0; - } - } - pixels += bitmap.rowBytes(); - } - } - - SkAutoMalloc decompMemory(kWidth*kHeight); - uint8_t* decompBuffer = reinterpret_cast<uint8_t*>(decompMemory.get()); - REPORTER_ASSERT(reporter, decompBuffer); - if (NULL == decompBuffer) { - return; - } - - for (int i = 0; i < SkTextureCompressor::kFormatCnt; ++i) { - const SkTextureCompressor::Format fmt = static_cast<SkTextureCompressor::Format>(i); - - // Ignore formats for RGBA data, since the decompressed buffer - // won't match the size and contents of the original. - if (!decompresses_a8(fmt) || !compresses_a8(fmt)) { - continue; - } - - SkAutoDataUnref data(SkTextureCompressor::CompressBitmapToFormat(bitmap, fmt)); - REPORTER_ASSERT(reporter, data); - if (NULL == data) { - continue; - } - - bool decompResult = - SkTextureCompressor::DecompressBufferFromFormat( - decompBuffer, kWidth, - data->bytes(), - kWidth, kHeight, fmt); - REPORTER_ASSERT(reporter, decompResult); - - SkAutoLockPixels alp(bitmap); - uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap.getPixels()); - REPORTER_ASSERT(reporter, pixels); - if (NULL == pixels) { - continue; - } - - for (int y = 0; y < kHeight; ++y) { - for (int x = 0; x < kWidth; ++x) { - bool ok = pixels[y*bitmap.rowBytes() + x] == decompBuffer[y*kWidth + x]; - REPORTER_ASSERT(reporter, ok); - } - } - } -} - -/** - * Make sure that if we pass in a solid color bitmap that we get the appropriate results - */ -DEF_TEST(CompressLATC, reporter) { - - const SkTextureCompressor::Format kLATCFormat = SkTextureCompressor::kLATC_Format; - static const int kLATCEncodedBlockSize = 8; - - SkBitmap bitmap; - static const int kWidth = 8; - static const int kHeight = 8; - SkImageInfo info = SkImageInfo::MakeA8(kWidth, kHeight); - - bool setInfoSuccess = bitmap.setInfo(info); - REPORTER_ASSERT(reporter, setInfoSuccess); - - bitmap.allocPixels(info); - bitmap.unlockPixels(); - - int latcDimX, latcDimY; - SkTextureCompressor::GetBlockDimensions(kLATCFormat, &latcDimX, &latcDimY); - - REPORTER_ASSERT(reporter, kWidth % latcDimX == 0); - REPORTER_ASSERT(reporter, kHeight % latcDimY == 0); - const size_t kSizeToBe = - SkTextureCompressor::GetCompressedDataSize(kLATCFormat, kWidth, kHeight); - REPORTER_ASSERT(reporter, kSizeToBe == ((kWidth*kHeight*kLATCEncodedBlockSize)/16)); - REPORTER_ASSERT(reporter, (kSizeToBe % kLATCEncodedBlockSize) == 0); - - for (int lum = 0; lum < 256; ++lum) { - bitmap.lockPixels(); - uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap.getPixels()); - REPORTER_ASSERT(reporter, pixels); - if (NULL == pixels) { - bitmap.unlockPixels(); - continue; - } - - for (int i = 0; i < kWidth*kHeight; ++i) { - pixels[i] = lum; - } - bitmap.unlockPixels(); - - SkAutoDataUnref latcData( - SkTextureCompressor::CompressBitmapToFormat(bitmap, kLATCFormat)); - REPORTER_ASSERT(reporter, latcData); - if (NULL == latcData) { - continue; - } - - REPORTER_ASSERT(reporter, kSizeToBe == latcData->size()); - - // Make sure that it all matches a given block encoding. Since we have - // COMPRESS_LATC_FAST defined in SkTextureCompressor_LATC.cpp, we are using - // an approximation scheme that optimizes for speed against coverage maps. - // That means that each palette in the encoded block is exactly the same, - // and that the three bits saved per pixel are computed from the top three - // bits of the luminance value. - const uint64_t kIndexEncodingMap[8] = { 1, 7, 6, 5, 4, 3, 2, 0 }; - const uint64_t kIndex = kIndexEncodingMap[lum >> 5]; - const uint64_t kConstColorEncoding = - SkEndian_SwapLE64( - 255 | - (kIndex << 16) | (kIndex << 19) | (kIndex << 22) | (kIndex << 25) | - (kIndex << 28) | (kIndex << 31) | (kIndex << 34) | (kIndex << 37) | - (kIndex << 40) | (kIndex << 43) | (kIndex << 46) | (kIndex << 49) | - (kIndex << 52) | (kIndex << 55) | (kIndex << 58) | (kIndex << 61)); - - const uint64_t* blockPtr = reinterpret_cast<const uint64_t*>(latcData->data()); - for (size_t i = 0; i < (kSizeToBe/8); ++i) { - REPORTER_ASSERT(reporter, blockPtr[i] == kConstColorEncoding); - } - } -}
diff --git a/src/third_party/skia/tests/TileGridTest.cpp b/src/third_party/skia/tests/TileGridTest.cpp deleted file mode 100644 index 16434ab..0000000 --- a/src/third_party/skia/tests/TileGridTest.cpp +++ /dev/null
@@ -1,251 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkCanvas.h" -#include "SkPictureRecorder.h" -#include "SkTileGrid.h" -#include "Test.h" - -enum Tile { - kTopLeft_Tile = 0x1, - kTopRight_Tile = 0x2, - kBottomLeft_Tile = 0x4, - kBottomRight_Tile = 0x8, - - kAll_Tile = kTopLeft_Tile | kTopRight_Tile | kBottomLeft_Tile | kBottomRight_Tile, -}; - -class MockCanvas : public SkCanvas { -public: - MockCanvas(const SkBitmap& bm) : SkCanvas(bm) {} - - virtual void drawRect(const SkRect& rect, const SkPaint&) { - // This capture occurs before quick reject. - fRects.push(rect); - } - - SkTDArray<SkRect> fRects; -}; - -static void verifyTileHits(skiatest::Reporter* reporter, SkRect rect, - uint32_t tileMask, int borderPixels = 0) { - SkTileGridFactory::TileGridInfo info; - info.fMargin.set(borderPixels, borderPixels); - info.fOffset.setZero(); - info.fTileInterval.set(10 - 2 * borderPixels, 10 - 2 * borderPixels); - SkTileGrid grid(2, 2, info); - grid.insert(NULL, rect, false); - REPORTER_ASSERT(reporter, grid.tileCount(0, 0) == - ((tileMask & kTopLeft_Tile)? 1 : 0)); - REPORTER_ASSERT(reporter, grid.tileCount(1, 0) == - ((tileMask & kTopRight_Tile)? 1 : 0)); - REPORTER_ASSERT(reporter, grid.tileCount(0, 1) == - ((tileMask & kBottomLeft_Tile)? 1 : 0)); - REPORTER_ASSERT(reporter, grid.tileCount(1, 1) == - ((tileMask & kBottomRight_Tile)? 1 : 0)); -} - -DEF_TEST(TileGrid_UnalignedQuery, reporter) { - // Use SkTileGridPicture to generate a SkTileGrid with a helper - SkTileGridFactory::TileGridInfo info; - info.fMargin.setEmpty(); - info.fOffset.setZero(); - info.fTileInterval.set(10, 10); - SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), - SkIntToScalar(8), SkIntToScalar(8)); - SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(11), SkIntToScalar(11), - SkIntToScalar(1), SkIntToScalar(1)); - SkTileGridFactory factory(info); - SkPictureRecorder recorder; - SkCanvas* canvas = recorder.beginRecording(20, 20, &factory, 0); - SkPaint paint; - canvas->drawRect(rect1, paint); - canvas->drawRect(rect2, paint); - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); - - SkBitmap store; - store.allocN32Pixels(1, 1); - - // Test parts of top-left tile - { - MockCanvas mockCanvas(store); - picture->playback(&mockCanvas); - REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count()); - REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]); - } - { - MockCanvas mockCanvas(store); - mockCanvas.translate(-7.99f, -7.99f); - picture->playback(&mockCanvas); - REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count()); - REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]); - } - // Corner overlap - { - MockCanvas mockCanvas(store); - mockCanvas.translate(-9.5f, -9.5f); - picture->playback(&mockCanvas); - REPORTER_ASSERT(reporter, 2 == mockCanvas.fRects.count()); - REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]); - REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[1]); - } - // Intersect bottom right tile, but does not overlap rect 2 - { - MockCanvas mockCanvas(store); - mockCanvas.translate(-16.0f, -16.0f); - picture->playback(&mockCanvas); - REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count()); - REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]); - } - // Out of bounds queries, snap to border tiles - { - MockCanvas mockCanvas(store); - mockCanvas.translate(2.0f, 0.0f); - picture->playback(&mockCanvas); - REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count()); - REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]); - } - { - MockCanvas mockCanvas(store); - mockCanvas.translate(0.0f, 2.0f); - picture->playback(&mockCanvas); - REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count()); - REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]); - } - { - MockCanvas mockCanvas(store); - mockCanvas.translate(-22.0f, -16.0f); - picture->playback(&mockCanvas); - REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count()); - REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]); - } - { - MockCanvas mockCanvas(store); - mockCanvas.translate(-16.0f, -22.0f); - picture->playback(&mockCanvas); - REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count()); - REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]); - } -} - -DEF_TEST(TileGrid_OverlapOffsetQueryAlignment, reporter) { - // Use SkTileGridPicture to generate a SkTileGrid with a helper - SkTileGridFactory::TileGridInfo info; - info.fMargin.set(1, 1); - info.fOffset.set(-1, -1); - info.fTileInterval.set(8, 8); - - // rect landing entirely in top left tile - SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), - SkIntToScalar(1), SkIntToScalar(1)); - // rect landing entirely in center tile - SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(12), SkIntToScalar(12), - SkIntToScalar(1), SkIntToScalar(1)); - // rect landing entirely in bottomright tile - SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(19), SkIntToScalar(19), - SkIntToScalar(1), SkIntToScalar(1)); - SkTileGridFactory factory(info); - SkPictureRecorder recorder; - SkCanvas* canvas = recorder.beginRecording(20, 20, &factory, 0); - SkPaint paint; - canvas->drawRect(rect1, paint); - canvas->drawRect(rect2, paint); - canvas->drawRect(rect3, paint); - SkAutoTUnref<SkPicture> picture(recorder.endRecording()); - - SkBitmap tileBitmap; - tileBitmap.allocN32Pixels(10, 10); - SkBitmap moreThanATileBitmap; - moreThanATileBitmap.allocN32Pixels(11, 11); - SkBitmap tinyBitmap; - tinyBitmap.allocN32Pixels(2, 2); - // Test parts of top-left tile - { - // The offset should cancel the top and left borders of the top left tile - // So a look-up at interval 0-10 should be grid aligned, - MockCanvas mockCanvas(tileBitmap); - picture->playback(&mockCanvas); - REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count()); - REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]); - } - { - // Encroaching border by one pixel - MockCanvas mockCanvas(moreThanATileBitmap); - picture->playback(&mockCanvas); - REPORTER_ASSERT(reporter, 2 == mockCanvas.fRects.count()); - REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]); - REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[1]); - } - { - // Tile stride is 8 (tileWidth - 2 * border pixels - // so translating by 8, should make query grid-aligned - // with middle tile. - MockCanvas mockCanvas(tileBitmap); - mockCanvas.translate(SkIntToScalar(-8), SkIntToScalar(-8)); - picture->playback(&mockCanvas); - REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count()); - REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]); - } - { - MockCanvas mockCanvas(tileBitmap); - mockCanvas.translate(-7.9f, -7.9f); - picture->playback(&mockCanvas); - REPORTER_ASSERT(reporter, 2 == mockCanvas.fRects.count()); - REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]); - REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[1]); - } - { - MockCanvas mockCanvas(tileBitmap); - mockCanvas.translate(-8.1f, -8.1f); - picture->playback(&mockCanvas); - REPORTER_ASSERT(reporter, 2 == mockCanvas.fRects.count()); - REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]); - REPORTER_ASSERT(reporter, rect3 == mockCanvas.fRects[1]); - } - { - // Regression test for crbug.com/234688 - // Once the 2x2 device region is inset by margin, it yields an empty - // adjusted region, sitting right on top of the tile boundary. - MockCanvas mockCanvas(tinyBitmap); - mockCanvas.translate(-8.0f, -8.0f); - picture->playback(&mockCanvas); - // This test passes by not asserting. We do not validate the rects recorded - // because the result is numerically unstable (floating point equality). - // The content of any one of the four tiles of the tilegrid would be a valid - // result since any bbox that covers the center point of the canvas will be - // recorded in all four tiles. - } -} - -DEF_TEST(TileGrid, reporter) { - // Out of bounds - verifyTileHits(reporter, SkRect::MakeXYWH(30, 0, 1, 1), 0); - verifyTileHits(reporter, SkRect::MakeXYWH(0, 30, 1, 1), 0); - verifyTileHits(reporter, SkRect::MakeXYWH(-10, 0, 1, 1), 0); - verifyTileHits(reporter, SkRect::MakeXYWH(0, -10, 1, 1), 0); - - // Dilation for AA consideration - verifyTileHits(reporter, SkRect::MakeXYWH(0, 0, 9, 9), kTopLeft_Tile); - verifyTileHits(reporter, SkRect::MakeXYWH(0, 0, 10, 10), kAll_Tile); - verifyTileHits(reporter, SkRect::MakeXYWH(9, 9, 1, 1), kAll_Tile); - verifyTileHits(reporter, SkRect::MakeXYWH(10, 10, 1, 1), kAll_Tile); - verifyTileHits(reporter, SkRect::MakeXYWH(11, 11, 1, 1), kBottomRight_Tile); - - // BorderPixels - verifyTileHits(reporter, SkRect::MakeXYWH(0, 0, 6, 6), kTopLeft_Tile, 1); - verifyTileHits(reporter, SkRect::MakeXYWH(0, 0, 7, 7), kAll_Tile, 1); - verifyTileHits(reporter, SkRect::MakeXYWH(9, 9, 1, 1), kAll_Tile, 1); - verifyTileHits(reporter, SkRect::MakeXYWH(10, 10, 1, 1), kBottomRight_Tile, 1); - verifyTileHits(reporter, SkRect::MakeXYWH(17, 17, 1, 1), kBottomRight_Tile, 1); - - // BBoxes that overlap tiles - verifyTileHits(reporter, SkRect::MakeXYWH(5, 5, 10, 1), kTopLeft_Tile | kTopRight_Tile); - verifyTileHits(reporter, SkRect::MakeXYWH(5, 5, 1, 10), kTopLeft_Tile | - kBottomLeft_Tile); - verifyTileHits(reporter, SkRect::MakeXYWH(5, 5, 10, 10), kAll_Tile); - verifyTileHits(reporter, SkRect::MakeXYWH(-10, -10, 40, 40), kAll_Tile); -}
diff --git a/src/third_party/skia/tests/Time.cpp b/src/third_party/skia/tests/Time.cpp new file mode 100644 index 0000000..f6bf9ff --- /dev/null +++ b/src/third_party/skia/tests/Time.cpp
@@ -0,0 +1,54 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTime.h" +#include "Test.h" + +// Sanity checks for the GetDateTime function. +DEF_TEST(Time_GetDateTime, r) { + SkTime::DateTime dateTime; + SkTime::GetDateTime(&dateTime); + + // TODO(future generation): update these values. + const uint16_t kMinimumSaneYear = 1964; + const uint16_t kMaximumSaneYear = 2064; + + if (dateTime.fYear < kMinimumSaneYear) { + ERRORF(r, + "SkTime::GetDateTime: %u (CurrentYear) < %u (MinimumSaneYear)", + static_cast<unsigned>(dateTime.fYear), + static_cast<unsigned>(kMinimumSaneYear)); + } + if (dateTime.fYear > kMaximumSaneYear) { + ERRORF(r, + "SkTime::GetDateTime: %u (CurrentYear) > %u (MaximumSaneYear)", + static_cast<unsigned>(dateTime.fYear), + static_cast<unsigned>(kMaximumSaneYear)); + } + + REPORTER_ASSERT(r, dateTime.fMonth >= 1); + REPORTER_ASSERT(r, dateTime.fMonth <= 12); + + REPORTER_ASSERT(r, dateTime.fDay >= 1); + REPORTER_ASSERT(r, dateTime.fDay <= 31); + + REPORTER_ASSERT(r, dateTime.fHour <= 23); + + REPORTER_ASSERT(r, dateTime.fMinute <= 59); + + REPORTER_ASSERT(r, dateTime.fSecond <= 60); // leap seconds are 23:59:60 + + // The westernmost timezone is -12:00. + // The easternmost timezone is +14:00. + REPORTER_ASSERT(r, SkTAbs(SkToInt(dateTime.fTimeZoneMinutes)) <= 14 * 60); + + SkString timeStamp; + dateTime.toISO8601(&timeStamp); + REPORTER_ASSERT(r, timeStamp.size() > 0); + INFOF(r, "\nCurrent Time (ISO-8601 format): \"%s\"\n", + timeStamp.c_str()); +}
diff --git a/src/third_party/skia/tests/TopoSortTest.cpp b/src/third_party/skia/tests/TopoSortTest.cpp new file mode 100644 index 0000000..9cee485 --- /dev/null +++ b/src/third_party/skia/tests/TopoSortTest.cpp
@@ -0,0 +1,141 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkRandom.h" +#include "SkTTopoSort.h" +#include "Test.h" + +#include "sk_tool_utils.h" + +typedef void (*CreateGraphPF)(SkTDArray<sk_tool_utils::TopoTestNode*>* graph); + +/* Simple diamond + * 3 + * / \ + * 1 2 + * \ / + * 0 + */ +static void create_graph0(SkTDArray<sk_tool_utils::TopoTestNode*>* graph) { + sk_tool_utils::TopoTestNode::AllocNodes(graph, 4); + + (*graph)[0]->dependsOn((*graph)[1]); + (*graph)[0]->dependsOn((*graph)[2]); + (*graph)[1]->dependsOn((*graph)[3]); + (*graph)[2]->dependsOn((*graph)[3]); +} + +/* Simple chain + * 3 + * | + * 2 + * | + * 1 + * | + * 0 + */ +static void create_graph1(SkTDArray<sk_tool_utils::TopoTestNode*>* graph) { + sk_tool_utils::TopoTestNode::AllocNodes(graph, 4); + + (*graph)[0]->dependsOn((*graph)[1]); + (*graph)[1]->dependsOn((*graph)[2]); + (*graph)[2]->dependsOn((*graph)[3]); +} + +/* Loop + * 2 + * / \ + * 0 --- 1 + */ +static void create_graph2(SkTDArray<sk_tool_utils::TopoTestNode*>* graph) { + sk_tool_utils::TopoTestNode::AllocNodes(graph, 3); + + (*graph)[0]->dependsOn((*graph)[1]); + (*graph)[1]->dependsOn((*graph)[2]); + (*graph)[2]->dependsOn((*graph)[0]); +} + +/* Double diamond + * 6 + * / \ + * 4 5 + * \ / + * 3 + * / \ + * 1 2 + * \ / + * 0 + */ +static void create_graph3(SkTDArray<sk_tool_utils::TopoTestNode*>* graph) { + sk_tool_utils::TopoTestNode::AllocNodes(graph, 7); + + (*graph)[0]->dependsOn((*graph)[1]); + (*graph)[0]->dependsOn((*graph)[2]); + (*graph)[1]->dependsOn((*graph)[3]); + (*graph)[2]->dependsOn((*graph)[3]); + + (*graph)[3]->dependsOn((*graph)[4]); + (*graph)[3]->dependsOn((*graph)[5]); + (*graph)[4]->dependsOn((*graph)[6]); + (*graph)[5]->dependsOn((*graph)[6]); +} + +/* Two independent diamonds + * 3 7 + * / \ / \ + * 1 2 5 6 + * \ / \ / + * 0 4 + */ +static void create_graph4(SkTDArray<sk_tool_utils::TopoTestNode*>* graph) { + sk_tool_utils::TopoTestNode::AllocNodes(graph, 8); + + (*graph)[0]->dependsOn((*graph)[1]); + (*graph)[0]->dependsOn((*graph)[2]); + (*graph)[1]->dependsOn((*graph)[3]); + (*graph)[2]->dependsOn((*graph)[3]); + + (*graph)[4]->dependsOn((*graph)[5]); + (*graph)[4]->dependsOn((*graph)[6]); + (*graph)[5]->dependsOn((*graph)[7]); + (*graph)[6]->dependsOn((*graph)[7]); +} + +DEF_TEST(TopoSort, reporter) { + SkRandom rand; + + struct { + CreateGraphPF fCreate; + bool fExpectedResult; + } tests[] = { + { create_graph0, true }, + { create_graph1, true }, + { create_graph2, false }, + { create_graph3, true }, + { create_graph4, true }, + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(tests); ++i) { + SkTDArray<sk_tool_utils::TopoTestNode*> graph; + + (tests[i].fCreate)(&graph); + + sk_tool_utils::TopoTestNode::Shuffle(&graph, &rand); + + bool actualResult = SkTTopoSort<sk_tool_utils::TopoTestNode>(&graph); + REPORTER_ASSERT(reporter, actualResult == tests[i].fExpectedResult); + + if (tests[i].fExpectedResult) { + for (int j = 0; j < graph.count(); ++j) { + REPORTER_ASSERT(reporter, graph[j]->check()); + } + } + + //SkDEBUGCODE(print(graph);) + sk_tool_utils::TopoTestNode::DeallocNodes(&graph); + } +}
diff --git a/src/third_party/skia/tests/TraceMemoryDumpTest.cpp b/src/third_party/skia/tests/TraceMemoryDumpTest.cpp new file mode 100644 index 0000000..a8cdbfe --- /dev/null +++ b/src/third_party/skia/tests/TraceMemoryDumpTest.cpp
@@ -0,0 +1,38 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTraceMemoryDump.h" + +#include "Test.h" + +/* + * Build test for SkTraceMemoryDump. + */ +class TestSkTraceMemoryDump : public SkTraceMemoryDump { +public: + TestSkTraceMemoryDump() { } + ~TestSkTraceMemoryDump() override { } + + void dumpNumericValue(const char* dumpName, const char* valueName, const char* units, + uint64_t value) override { } + void setMemoryBacking(const char* dumpName, const char* backingType, + const char* backingObjectId) override { } + void setDiscardableMemoryBacking( + const char* dumpName, + const SkDiscardableMemory& discardableMemoryObject) override { } + LevelOfDetail getRequestedDetails() const override { + return SkTraceMemoryDump::kObjectsBreakdowns_LevelOfDetail; + } +}; + +DEF_TEST(SkTraceMemoryDump, reporter) { + TestSkTraceMemoryDump x; + x.dumpNumericValue("foobar", "size", "bytes", 42); + if (x.getRequestedDetails() == SkTraceMemoryDump::kObjectsBreakdowns_LevelOfDetail) { + x.dumpNumericValue("foobar/object1", "size", "bytes", 23); + } +}
diff --git a/src/third_party/skia/tests/TransferPixelsTest.cpp b/src/third_party/skia/tests/TransferPixelsTest.cpp new file mode 100755 index 0000000..5da292f --- /dev/null +++ b/src/third_party/skia/tests/TransferPixelsTest.cpp
@@ -0,0 +1,179 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// This is a GPU-backend specific test. It relies on static intializers to work + +#include "SkTypes.h" + +#if SK_SUPPORT_GPU + +#include "GrContextFactory.h" +#include "GrContextPriv.h" +#include "GrGpu.h" +#include "GrResourceProvider.h" +#include "GrSurfaceProxy.h" +#include "GrTexture.h" +#include "GrTest.h" +#include "SkGr.h" +#include "SkSurface.h" +#include "Test.h" + +using sk_gpu_test::GrContextFactory; + +void fill_transfer_data(int left, int top, int width, int height, int bufferWidth, + GrColor* data) { + + // build red-green gradient + for (int j = top; j < top + height; ++j) { + for (int i = left; i < left + width; ++i) { + unsigned int red = (unsigned int)(256.f*((i - left) / (float)width)); + unsigned int green = (unsigned int)(256.f*((j - top) / (float)height)); + data[i + j*bufferWidth] = GrColorPackRGBA(red - (red>>8), + green - (green>>8), 0xff, 0xff); + } + } +} + +bool does_full_buffer_contain_correct_values(GrColor* srcBuffer, + GrColor* dstBuffer, + int width, + int height, + int bufferWidth, + int bufferHeight, + GrSurfaceOrigin origin) { + GrColor* srcPtr = srcBuffer; + bool bottomUp = SkToBool(kBottomLeft_GrSurfaceOrigin == origin); + GrColor* dstPtr = bottomUp ? dstBuffer + bufferWidth*(bufferHeight-1) : dstBuffer; + int dstIncrement = bottomUp ? -bufferWidth : +bufferWidth; + + for (int j = 0; j < height; ++j) { + for (int i = 0; i < width; ++i) { + if (srcPtr[i] != dstPtr[i]) { + return false; + } + } + srcPtr += bufferWidth; + dstPtr += dstIncrement; + } + return true; +} + +void basic_transfer_test(skiatest::Reporter* reporter, GrContext* context, GrPixelConfig config, + GrSurfaceOrigin origin, bool renderTarget) { + // set up the data + const int kTextureWidth = 16; + const int kTextureHeight = 16; + const int kBufferWidth = 20; + const int kBufferHeight = 16; + size_t rowBytes = kBufferWidth * sizeof(GrColor); + SkAutoTMalloc<GrColor> srcBuffer(kBufferWidth*kBufferHeight); + SkAutoTMalloc<GrColor> dstBuffer(kBufferWidth*kBufferHeight); + + fill_transfer_data(0, 0, kTextureWidth, kTextureHeight, kBufferWidth, srcBuffer.get()); + + // create and fill transfer buffer + size_t size = rowBytes*kBufferHeight; + uint32_t bufferFlags = GrResourceProvider::kNoPendingIO_Flag; + sk_sp<GrBuffer> buffer(context->resourceProvider()->createBuffer(size, + kXferCpuToGpu_GrBufferType, + kDynamic_GrAccessPattern, + bufferFlags)); + if (!buffer) { + return; + } + + void* data = buffer->map(); + memcpy(data, srcBuffer.get(), size); + buffer->unmap(); + + // create texture + GrSurfaceDesc desc; + desc.fConfig = config; + desc.fFlags = renderTarget ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags; + desc.fOrigin = origin; + desc.fWidth = kTextureWidth; + desc.fHeight = kTextureHeight; + desc.fSampleCnt = 0; + sk_sp<GrTexture> tex = context->resourceProvider()->createTexture(desc, SkBudgeted::kNo); + + ////////////////////////// + // transfer full data + + bool result; + result = context->getGpu()->transferPixels(tex.get(), 0, 0, kTextureWidth, kTextureHeight, + config, buffer.get(), 0, rowBytes); + REPORTER_ASSERT(reporter, result); + + memset(dstBuffer.get(), 0xCDCD, size); + result = context->getGpu()->readPixels(tex.get(), 0, 0, kTextureWidth, kTextureHeight, config, + dstBuffer.get(), rowBytes); + if (result) { + REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_values(srcBuffer, + dstBuffer, + kTextureWidth, + kTextureHeight, + kBufferWidth, + kBufferHeight, + origin)); + } + + ////////////////////////// + // transfer partial data + + const int kLeft = 2; + const int kTop = 10; + const int kWidth = 10; + const int kHeight = 2; + + // change color of subrectangle + fill_transfer_data(kLeft, kTop, kWidth, kHeight, kBufferWidth, srcBuffer.get()); + data = buffer->map(); + memcpy(data, srcBuffer.get(), size); + buffer->unmap(); + + size_t offset = sizeof(GrColor)*(kTop*kBufferWidth + kLeft); + result = context->getGpu()->transferPixels(tex.get(), kLeft, kTop, kWidth, kHeight, config, + buffer.get(), offset, rowBytes); + REPORTER_ASSERT(reporter, result); + + memset(dstBuffer.get(), 0xCDCD, size); + result = context->getGpu()->readPixels(tex.get(), 0, 0, kTextureWidth, kTextureHeight, config, + dstBuffer.get(), rowBytes); + if (result) { + REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_values(srcBuffer, + dstBuffer, + kTextureWidth, + kTextureHeight, + kBufferWidth, + kBufferHeight, + origin)); + } +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TransferPixelsTest, reporter, ctxInfo) { + // RGBA + basic_transfer_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig, + kTopLeft_GrSurfaceOrigin, false); + basic_transfer_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig, + kTopLeft_GrSurfaceOrigin, true); + basic_transfer_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig, + kBottomLeft_GrSurfaceOrigin, false); + basic_transfer_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig, + kBottomLeft_GrSurfaceOrigin, true); + + // BGRA + basic_transfer_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig, + kTopLeft_GrSurfaceOrigin, false); + basic_transfer_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig, + kTopLeft_GrSurfaceOrigin, true); + basic_transfer_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig, + kBottomLeft_GrSurfaceOrigin, false); + basic_transfer_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig, + kBottomLeft_GrSurfaceOrigin, true); +} + +#endif
diff --git a/src/third_party/skia/tests/TypefaceTest.cpp b/src/third_party/skia/tests/TypefaceTest.cpp index bb5d361..1da94bb 100644 --- a/src/third_party/skia/tests/TypefaceTest.cpp +++ b/src/third_party/skia/tests/TypefaceTest.cpp
@@ -5,13 +5,171 @@ * found in the LICENSE file. */ +#include "SkAdvancedTypefaceMetrics.h" +#include "SkData.h" +#include "SkFixed.h" +#include "SkFontMgr.h" +#include "SkMakeUnique.h" +#include "SkOTTable_OS_2.h" +#include "SkSFNTHeader.h" +#include "SkStream.h" +#include "SkRefCnt.h" #include "SkTypeface.h" +#include "SkTypefaceCache.h" +#include "Resources.h" #include "Test.h" +#include <memory> + +static void TypefaceStyle_test(skiatest::Reporter* reporter, + uint16_t weight, uint16_t width, SkData* data) +{ + sk_sp<SkData> dataCopy; + if (!data->unique()) { + dataCopy = SkData::MakeWithCopy(data->data(), data->size()); + data = dataCopy.get(); + } + SkSFNTHeader* sfntHeader = static_cast<SkSFNTHeader*>(data->writable_data()); + + SkSFNTHeader::TableDirectoryEntry* tableEntry = + SkTAfter<SkSFNTHeader::TableDirectoryEntry>(sfntHeader); + SkSFNTHeader::TableDirectoryEntry* os2TableEntry = nullptr; + int numTables = SkEndian_SwapBE16(sfntHeader->numTables); + for (int tableEntryIndex = 0; tableEntryIndex < numTables; ++tableEntryIndex) { + if (SkOTTableOS2::TAG == tableEntry[tableEntryIndex].tag) { + os2TableEntry = tableEntry + tableEntryIndex; + break; + } + } + SkASSERT_RELEASE(os2TableEntry); + + size_t os2TableOffset = SkEndian_SwapBE32(os2TableEntry->offset); + SkOTTableOS2_V0* os2Table = SkTAddOffset<SkOTTableOS2_V0>(sfntHeader, os2TableOffset); + os2Table->usWeightClass.value = SkEndian_SwapBE16(weight); + using WidthType = SkOTTableOS2_V0::WidthClass::Value; + os2Table->usWidthClass.value = static_cast<WidthType>(SkEndian_SwapBE16(width)); + + sk_sp<SkTypeface> newTypeface(SkTypeface::MakeFromStream(new SkMemoryStream(sk_ref_sp(data)))); + SkASSERT_RELEASE(newTypeface); + + SkFontStyle newStyle = newTypeface->fontStyle(); + + //printf("%d, %f\n", weight, (newStyle.weight() - (float)0x7FFF) / (float)0x7FFF); + //printf("%d, %f\n", width , (newStyle.width() - (float)0x7F) / (float)0x7F); + //printf("%d, %d\n", weight, newStyle.weight()); + //printf("%d, %d\n", width , newStyle.width()); + + // Some back-ends (CG, GDI, DW) support OS/2 version A which uses 0 - 10 (but all differently). + REPORTER_ASSERT(reporter, + newStyle.weight() == weight || + (weight <= 10 && newStyle.weight() == 100 * weight) || + (weight == 4 && newStyle.weight() == 350) || // GDI weirdness + (weight == 5 && newStyle.weight() == 400) || // GDI weirdness + (weight == 0 && newStyle.weight() == 1) || // DW weirdness + (weight == 1000 && newStyle.weight() == 999) // DW weirdness + ); + + // Some back-ends (GDI) don't support width, ensure these always report 'medium'. + REPORTER_ASSERT(reporter, + newStyle.width() == width || + newStyle.width() == 5); +} +DEF_TEST(TypefaceStyle, reporter) { + std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("/fonts/Em.ttf")); + if (!stream) { + REPORT_FAILURE(reporter, "/fonts/Em.ttf", SkString("Cannot load resource")); + return; + } + sk_sp<SkData> data(SkData::MakeFromStream(stream.get(), stream->getLength())); + + using SkFS = SkFontStyle; + for (int weight = SkFS::kInvisible_Weight; weight <= SkFS::kExtraBlack_Weight; ++weight) { + TypefaceStyle_test(reporter, weight, 5, data.get()); + } + for (int width = SkFS::kUltraCondensed_Width; width <= SkFS::kUltraExpanded_Width; ++width) { + TypefaceStyle_test(reporter, 400, width, data.get()); + } +} + +DEF_TEST(TypefaceAxes, reporter) { + std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("/fonts/Distortable.ttf")); + if (!distortable) { + REPORT_FAILURE(reporter, "distortable", SkString()); + return; + } + constexpr int numberOfAxesInDistortable = 1; + + sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault(); + // The position may be over specified. If there are multiple values for a given axis, + // ensure the last one since that's what css-fonts-4 requires. + const SkFontArguments::VariationPosition::Coordinate position[] = { + { SkSetFourByteTag('w','g','h','t'), 1.618033988749895f }, + { SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 }, + }; + SkFontArguments params; + params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)}); + // TODO: if axes are set and the back-end doesn't support them, should we create the typeface? + sk_sp<SkTypeface> typeface(fm->createFromStream(distortable.release(), params)); + + int count = typeface->getVariationDesignPosition(nullptr, 0); + if (count == -1) { + return; + } + REPORTER_ASSERT(reporter, count == numberOfAxesInDistortable); + + SkFontArguments::VariationPosition::Coordinate positionRead[numberOfAxesInDistortable]; + count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead)); + REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(positionRead)); + + REPORTER_ASSERT(reporter, positionRead[0].axis == position[1].axis); + + // Convert to fixed for "almost equal". + SkFixed fixedRead = SkScalarToFixed(positionRead[0].value); + SkFixed fixedOriginal = SkScalarToFixed(position[1].value); + REPORTER_ASSERT(reporter, fixedRead == fixedOriginal); +} + +DEF_TEST(TypefaceVariationIndex, reporter) { + std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("/fonts/Distortable.ttf")); + if (!distortable) { + REPORT_FAILURE(reporter, "distortable", SkString()); + return; + } + + sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault(); + SkFontArguments params; + // The first named variation position in Distortable is 'Thin'. + params.setCollectionIndex(0x00010000); + sk_sp<SkTypeface> typeface(fm->createFromStream(distortable.release(), params)); + if (!typeface) { + // FreeType is the only weird thing that supports this, Skia just needs to make sure if it + // gets one of these things make sense. + return; + } + + int count = typeface->getVariationDesignPosition(nullptr, 0); + if (!(count == 1)) { + REPORT_FAILURE(reporter, "count == 1", SkString()); + return; + } + + SkFontArguments::VariationPosition::Coordinate positionRead[1]; + count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead)); + if (count == -1) { + return; + } + if (!(count == 1)) { + REPORT_FAILURE(reporter, "count == 1", SkString()); + return; + } + REPORTER_ASSERT(reporter, positionRead[0].axis == SkSetFourByteTag('w','g','h','t')); + REPORTER_ASSERT(reporter, positionRead[0].value == 0.5); +} + DEF_TEST(Typeface, reporter) { - SkAutoTUnref<SkTypeface> t1(SkTypeface::CreateFromName(NULL, SkTypeface::kNormal)); - SkAutoTUnref<SkTypeface> t2(SkTypeface::RefDefault(SkTypeface::kNormal)); + sk_sp<SkTypeface> t1(SkTypeface::MakeFromName(nullptr, SkFontStyle())); + sk_sp<SkTypeface> t2(SkTypeface::MakeDefault(SkTypeface::kNormal)); REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), t2.get())); REPORTER_ASSERT(reporter, SkTypeface::Equal(0, t1.get())); @@ -20,7 +178,81 @@ REPORTER_ASSERT(reporter, SkTypeface::Equal(t2.get(), 0)); #ifdef SK_BUILD_FOR_ANDROID - SkAutoTUnref<SkTypeface> t3(SkTypeface::CreateFromName("non-existent-font", SkTypeface::kNormal)); - REPORTER_ASSERT(reporter, NULL == t3.get()); + sk_sp<SkTypeface> t3(SkTypeface::MakeFromName("non-existent-font", SkFontStyle())); + REPORTER_ASSERT(reporter, nullptr == t3); #endif } + +namespace { + +class SkEmptyTypeface : public SkTypeface { +public: + static sk_sp<SkTypeface> Create() { return sk_sp<SkTypeface>(new SkEmptyTypeface()); } +protected: + SkEmptyTypeface() : SkTypeface(SkFontStyle(), true) { } + + SkStreamAsset* onOpenStream(int* ttcIndex) const override { return nullptr; } + SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&, + const SkDescriptor*) const override { + return nullptr; + } + void onFilterRec(SkScalerContextRec*) const override { } + std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override { + return nullptr; + } + void onGetFontDescriptor(SkFontDescriptor*, bool*) const override { } + virtual int onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const override { + SK_ABORT("unimplemented"); + return 0; + } + int onCountGlyphs() const override { return 0; } + int onGetUPEM() const override { return 0; } + void onGetFamilyName(SkString* familyName) const override { familyName->reset(); } + SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override { + SK_ABORT("unimplemented"); + return nullptr; + } + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override + { + return 0; + } + int onGetTableTags(SkFontTableTag tags[]) const override { return 0; } + size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override { return 0; } +}; + +} + +static bool count_proc(SkTypeface* face, void* ctx) { + int* count = static_cast<int*>(ctx); + *count = *count + 1; + return false; +} +static int count(skiatest::Reporter* reporter, const SkTypefaceCache& cache) { + int count = 0; + SkTypeface* none = cache.findByProcAndRef(count_proc, &count); + REPORTER_ASSERT(reporter, none == nullptr); + return count; +} + +DEF_TEST(TypefaceCache, reporter) { + sk_sp<SkTypeface> t1(SkEmptyTypeface::Create()); + { + SkTypefaceCache cache; + REPORTER_ASSERT(reporter, count(reporter, cache) == 0); + { + sk_sp<SkTypeface> t0(SkEmptyTypeface::Create()); + cache.add(t0.get()); + REPORTER_ASSERT(reporter, count(reporter, cache) == 1); + cache.add(t1.get()); + REPORTER_ASSERT(reporter, count(reporter, cache) == 2); + cache.purgeAll(); + REPORTER_ASSERT(reporter, count(reporter, cache) == 2); + } + REPORTER_ASSERT(reporter, count(reporter, cache) == 2); + cache.purgeAll(); + REPORTER_ASSERT(reporter, count(reporter, cache) == 1); + } + REPORTER_ASSERT(reporter, t1->unique()); +}
diff --git a/src/third_party/skia/tests/UtilsTest.cpp b/src/third_party/skia/tests/UtilsTest.cpp index 2c84c95..ed861c6 100644 --- a/src/third_party/skia/tests/UtilsTest.cpp +++ b/src/third_party/skia/tests/UtilsTest.cpp
@@ -14,7 +14,7 @@ class RefClass : public SkRefCnt { public: - SK_DECLARE_INST_COUNT(RefClass) + RefClass(int n) : fN(n) {} int get() const { return fN; } @@ -27,33 +27,33 @@ static void test_autounref(skiatest::Reporter* reporter) { RefClass obj(0); - REPORTER_ASSERT(reporter, 1 == obj.getRefCnt()); + REPORTER_ASSERT(reporter, obj.unique()); - SkAutoTUnref<RefClass> tmp(&obj); + sk_sp<RefClass> tmp(&obj); REPORTER_ASSERT(reporter, &obj == tmp.get()); - REPORTER_ASSERT(reporter, 1 == obj.getRefCnt()); + REPORTER_ASSERT(reporter, obj.unique()); - REPORTER_ASSERT(reporter, &obj == tmp.detach()); - REPORTER_ASSERT(reporter, 1 == obj.getRefCnt()); - REPORTER_ASSERT(reporter, NULL == tmp.detach()); - REPORTER_ASSERT(reporter, NULL == tmp.get()); + REPORTER_ASSERT(reporter, &obj == tmp.release()); + REPORTER_ASSERT(reporter, obj.unique()); + REPORTER_ASSERT(reporter, nullptr == tmp.release()); + REPORTER_ASSERT(reporter, nullptr == tmp.get()); obj.ref(); - REPORTER_ASSERT(reporter, 2 == obj.getRefCnt()); + REPORTER_ASSERT(reporter, !obj.unique()); { - SkAutoTUnref<RefClass> tmp2(&obj); + sk_sp<RefClass> tmp2(&obj); } - REPORTER_ASSERT(reporter, 1 == obj.getRefCnt()); + REPORTER_ASSERT(reporter, obj.unique()); } static void test_autostarray(skiatest::Reporter* reporter) { RefClass obj0(0); RefClass obj1(1); - REPORTER_ASSERT(reporter, 1 == obj0.getRefCnt()); - REPORTER_ASSERT(reporter, 1 == obj1.getRefCnt()); + REPORTER_ASSERT(reporter, obj0.unique()); + REPORTER_ASSERT(reporter, obj1.unique()); { - SkAutoSTArray<2, SkAutoTUnref<RefClass> > tmp; + SkAutoSTArray<2, sk_sp<RefClass> > tmp; REPORTER_ASSERT(reporter, 0 == tmp.count()); tmp.reset(0); // test out reset(0) when already at 0 @@ -61,14 +61,14 @@ REPORTER_ASSERT(reporter, 4 == tmp.count()); tmp[0].reset(SkRef(&obj0)); tmp[1].reset(SkRef(&obj1)); - REPORTER_ASSERT(reporter, 2 == obj0.getRefCnt()); - REPORTER_ASSERT(reporter, 2 == obj1.getRefCnt()); + REPORTER_ASSERT(reporter, !obj0.unique()); + REPORTER_ASSERT(reporter, !obj1.unique()); // test out reset with data in the array (and a new allocation) tmp.reset(0); REPORTER_ASSERT(reporter, 0 == tmp.count()); - REPORTER_ASSERT(reporter, 1 == obj0.getRefCnt()); - REPORTER_ASSERT(reporter, 1 == obj1.getRefCnt()); + REPORTER_ASSERT(reporter, obj0.unique()); + REPORTER_ASSERT(reporter, obj1.unique()); tmp.reset(2); // this should use the preexisting allocation REPORTER_ASSERT(reporter, 2 == tmp.count()); @@ -77,42 +77,42 @@ } // test out destructor with data in the array (and using existing allocation) - REPORTER_ASSERT(reporter, 1 == obj0.getRefCnt()); - REPORTER_ASSERT(reporter, 1 == obj1.getRefCnt()); + REPORTER_ASSERT(reporter, obj0.unique()); + REPORTER_ASSERT(reporter, obj1.unique()); { // test out allocating ctor (this should allocate new memory) - SkAutoSTArray<2, SkAutoTUnref<RefClass> > tmp(4); + SkAutoSTArray<2, sk_sp<RefClass> > tmp(4); REPORTER_ASSERT(reporter, 4 == tmp.count()); tmp[0].reset(SkRef(&obj0)); tmp[1].reset(SkRef(&obj1)); - REPORTER_ASSERT(reporter, 2 == obj0.getRefCnt()); - REPORTER_ASSERT(reporter, 2 == obj1.getRefCnt()); + REPORTER_ASSERT(reporter, !obj0.unique()); + REPORTER_ASSERT(reporter, !obj1.unique()); // Test out resut with data in the array and malloced storage tmp.reset(0); - REPORTER_ASSERT(reporter, 1 == obj0.getRefCnt()); - REPORTER_ASSERT(reporter, 1 == obj1.getRefCnt()); + REPORTER_ASSERT(reporter, obj0.unique()); + REPORTER_ASSERT(reporter, obj1.unique()); tmp.reset(2); // this should use the preexisting storage tmp[0].reset(SkRef(&obj0)); tmp[1].reset(SkRef(&obj1)); - REPORTER_ASSERT(reporter, 2 == obj0.getRefCnt()); - REPORTER_ASSERT(reporter, 2 == obj1.getRefCnt()); + REPORTER_ASSERT(reporter, !obj0.unique()); + REPORTER_ASSERT(reporter, !obj1.unique()); tmp.reset(4); // this should force a new malloc - REPORTER_ASSERT(reporter, 1 == obj0.getRefCnt()); - REPORTER_ASSERT(reporter, 1 == obj1.getRefCnt()); + REPORTER_ASSERT(reporter, obj0.unique()); + REPORTER_ASSERT(reporter, obj1.unique()); tmp[0].reset(SkRef(&obj0)); tmp[1].reset(SkRef(&obj1)); - REPORTER_ASSERT(reporter, 2 == obj0.getRefCnt()); - REPORTER_ASSERT(reporter, 2 == obj1.getRefCnt()); + REPORTER_ASSERT(reporter, !obj0.unique()); + REPORTER_ASSERT(reporter, !obj1.unique()); } - REPORTER_ASSERT(reporter, 1 == obj0.getRefCnt()); - REPORTER_ASSERT(reporter, 1 == obj1.getRefCnt()); + REPORTER_ASSERT(reporter, obj0.unique()); + REPORTER_ASSERT(reporter, obj1.unique()); } ///////////////////////////////////////////////////////////////////////////// @@ -219,3 +219,64 @@ test_autounref(reporter); test_autostarray(reporter); } + +#define ASCII_BYTE "X" +#define CONTINUATION_BYTE "\x80" +#define LEADING_TWO_BYTE "\xC4" +#define LEADING_THREE_BYTE "\xE0" +#define LEADING_FOUR_BYTE "\xF0" +#define INVALID_BYTE "\xFC" +static bool valid_utf8(const char* p, size_t l) { + return SkUTF8_CountUnicharsWithError(p, l) >= 0; +} +DEF_TEST(Utils_UTF8_ValidLength, r) { + const char* goodTestcases[] = { + "", + ASCII_BYTE, + ASCII_BYTE ASCII_BYTE, + LEADING_TWO_BYTE CONTINUATION_BYTE, + ASCII_BYTE LEADING_TWO_BYTE CONTINUATION_BYTE, + ASCII_BYTE ASCII_BYTE LEADING_TWO_BYTE CONTINUATION_BYTE, + LEADING_THREE_BYTE CONTINUATION_BYTE CONTINUATION_BYTE, + ASCII_BYTE LEADING_THREE_BYTE CONTINUATION_BYTE CONTINUATION_BYTE, + ASCII_BYTE ASCII_BYTE LEADING_THREE_BYTE CONTINUATION_BYTE CONTINUATION_BYTE, + LEADING_FOUR_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE, + ASCII_BYTE LEADING_FOUR_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE, + ASCII_BYTE ASCII_BYTE LEADING_FOUR_BYTE CONTINUATION_BYTE CONTINUATION_BYTE + CONTINUATION_BYTE, + }; + for (const char* testcase : goodTestcases) { + REPORTER_ASSERT(r, valid_utf8(testcase, strlen(testcase))); + } + const char* badTestcases[] = { + INVALID_BYTE, + INVALID_BYTE CONTINUATION_BYTE, + INVALID_BYTE CONTINUATION_BYTE CONTINUATION_BYTE, + INVALID_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE, + LEADING_TWO_BYTE, + CONTINUATION_BYTE, + CONTINUATION_BYTE CONTINUATION_BYTE, + LEADING_THREE_BYTE CONTINUATION_BYTE, + CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE, + LEADING_FOUR_BYTE CONTINUATION_BYTE, + CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE, + + ASCII_BYTE INVALID_BYTE, + ASCII_BYTE INVALID_BYTE CONTINUATION_BYTE, + ASCII_BYTE INVALID_BYTE CONTINUATION_BYTE CONTINUATION_BYTE, + ASCII_BYTE INVALID_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE, + ASCII_BYTE LEADING_TWO_BYTE, + ASCII_BYTE CONTINUATION_BYTE, + ASCII_BYTE CONTINUATION_BYTE CONTINUATION_BYTE, + ASCII_BYTE LEADING_THREE_BYTE CONTINUATION_BYTE, + ASCII_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE, + ASCII_BYTE LEADING_FOUR_BYTE CONTINUATION_BYTE, + ASCII_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE, + + // LEADING_FOUR_BYTE LEADING_TWO_BYTE CONTINUATION_BYTE, + }; + for (const char* testcase : badTestcases) { + REPORTER_ASSERT(r, !valid_utf8(testcase, strlen(testcase))); + } + +}
diff --git a/src/third_party/skia/tests/VerticesTest.cpp b/src/third_party/skia/tests/VerticesTest.cpp new file mode 100644 index 0000000..399aba2 --- /dev/null +++ b/src/third_party/skia/tests/VerticesTest.cpp
@@ -0,0 +1,88 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkVertices.h" +#include "Test.h" + +static bool equal(const SkVertices* v0, const SkVertices* v1) { + if (v0->mode() != v1->mode()) { + return false; + } + if (v0->vertexCount() != v1->vertexCount()) { + return false; + } + if (v0->indexCount() != v1->indexCount()) { + return false; + } + + if (!!v0->texCoords() != !!v1->texCoords()) { + return false; + } + if (!!v0->colors() != !!v1->colors()) { + return false; + } + + for (int i = 0; i < v0->vertexCount(); ++i) { + if (v0->positions()[i] != v1->positions()[i]) { + return false; + } + if (v0->texCoords()) { + if (v0->texCoords()[i] != v1->texCoords()[i]) { + return false; + } + } + if (v0->colors()) { + if (v0->colors()[i] != v1->colors()[i]) { + return false; + } + } + } + for (int i = 0; i < v0->indexCount(); ++i) { + if (v0->indices()[i] != v1->indices()[i]) { + return false; + } + } + return true; +} + +DEF_TEST(Vertices, reporter) { + int vCount = 5; + int iCount = 9; // odd value exercises padding logic in encode() + + const uint32_t texFlags[] = { 0, SkVertices::kHasTexCoords_BuilderFlag }; + const uint32_t colFlags[] = { 0, SkVertices::kHasColors_BuilderFlag }; + for (auto texF : texFlags) { + for (auto colF : colFlags) { + uint32_t flags = texF | colF; + + SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, vCount, iCount, flags); + + for (int i = 0; i < vCount; ++i) { + float x = (float)i; + builder.positions()[i].set(x, 1); + if (builder.texCoords()) { + builder.texCoords()[i].set(x, 2); + } + if (builder.colors()) { + builder.colors()[i] = SkColorSetARGB(0xFF, i, 0x80, 0); + } + } + for (int i = 0; i < builder.indexCount(); ++i) { + builder.indices()[i] = i % vCount; + } + + sk_sp<SkVertices> v0 = builder.detach(); + sk_sp<SkData> data = v0->encode(); + sk_sp<SkVertices> v1 = SkVertices::Decode(data->data(), data->size()); + + REPORTER_ASSERT(reporter, v0->uniqueID() != 0); + REPORTER_ASSERT(reporter, v1->uniqueID() != 0); + REPORTER_ASSERT(reporter, v0->uniqueID() != v1->uniqueID()); + REPORTER_ASSERT(reporter, equal(v0.get(), v1.get())); + } + } +}
diff --git a/src/third_party/skia/tests/VkClearTests.cpp b/src/third_party/skia/tests/VkClearTests.cpp new file mode 100644 index 0000000..c797ef2 --- /dev/null +++ b/src/third_party/skia/tests/VkClearTests.cpp
@@ -0,0 +1,208 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// This is a GPU-backend specific test. It relies on static intializers to work + +#include "SkTypes.h" + +#if SK_SUPPORT_GPU && defined(SK_VULKAN) + +#include "GrContextFactory.h" +#include "GrTest.h" +#include "Test.h" +#include "vk/GrVkGpu.h" + +using sk_gpu_test::GrContextFactory; + +bool does_full_buffer_contain_correct_color(GrColor* buffer, + GrColor clearColor, + GrPixelConfig config, + int width, + int height) { + GrColor matchColor; + if (kRGBA_8888_GrPixelConfig == config) { + matchColor = clearColor; + } else if (kBGRA_8888_GrPixelConfig) { + // Hack to flip the R, B componets in the GrColor so that the comparrison will work below + matchColor = GrColorPackRGBA(GrColorUnpackB(clearColor), + GrColorUnpackG(clearColor), + GrColorUnpackR(clearColor), + GrColorUnpackA(clearColor)); + } else { + // currently only supporting rgba_8888 and bgra_8888 + return false; + } + + for (int j = 0; j < height; ++j) { + for (int i = 0; i < width; ++i) { + if (buffer[j * width + i] != matchColor) { + return false; + } + } + } + return true; +} + +void basic_clear_test(skiatest::Reporter* reporter, GrContext* context, GrPixelConfig config) { +#if 0 + GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu()); + SkAutoTMalloc<GrColor> buffer(25); + + GrSurfaceDesc surfDesc; + surfDesc.fFlags = kRenderTarget_GrSurfaceFlag; + surfDesc.fOrigin = kTopLeft_GrSurfaceOrigin; + surfDesc.fWidth = 5; + surfDesc.fHeight = 5; + surfDesc.fConfig = config; + surfDesc.fSampleCnt = 0; + GrTexture* tex = gpu->createTexture(surfDesc, SkBudgeted::kNo); + SkASSERT(tex); + SkASSERT(tex->asRenderTarget()); + SkIRect rect = SkIRect::MakeWH(5, 5); + + gpu->clear(rect, GrColor_TRANSPARENT_BLACK, tex->asRenderTarget()); + + gpu->readPixels(tex, 0, 0, 5, 5, config, (void*)buffer.get(), 0); + + REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(), + GrColor_TRANSPARENT_BLACK, + config, + 5, + 5)); + + gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget()); + + gpu->readPixels(tex, 0, 0, 5, 5, config, (void*)buffer.get(), 0); + + REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(), + GrColor_WHITE, + config, + 5, + 5)); + + GrColor myColor = GrColorPackRGBA(0xFF, 0x7F, 0x40, 0x20); + + gpu->clear(rect, myColor, tex->asRenderTarget()); + + gpu->readPixels(tex, 0, 0, 5, 5, config, (void*)buffer.get(), 0); + + REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(), + myColor, + config, + 5, + 5)); +#endif +} + +void sub_clear_test(skiatest::Reporter* reporter, GrContext* context, GrPixelConfig config) { +#if 0 + const int width = 10; + const int height = 10; + const int subWidth = width/2; + const int subHeight = height/2; + GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu()); + SkAutoTMalloc<GrColor> buffer(width * height); + SkAutoTMalloc<GrColor> subBuffer(subWidth * subHeight); + + GrSurfaceDesc surfDesc; + surfDesc.fFlags = kRenderTarget_GrSurfaceFlag; + surfDesc.fOrigin = kTopLeft_GrSurfaceOrigin; + surfDesc.fWidth = width; + surfDesc.fHeight = height; + surfDesc.fConfig = config; + surfDesc.fSampleCnt = 0; + GrTexture* tex = gpu->createTexture(surfDesc, SkBudgeted::kNo); + SkASSERT(tex); + SkASSERT(tex->asRenderTarget()); + + SkIRect fullRect = SkIRect::MakeWH(10, 10); + gpu->clear(fullRect, GrColor_TRANSPARENT_BLACK, tex->asRenderTarget()); + + gpu->readPixels(tex, 0, 0, width, height, config, (void*)buffer.get(), 0); + + REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(), + GrColor_TRANSPARENT_BLACK, + config, + width, + height)); + SkIRect rect; + rect = SkIRect::MakeXYWH(0, 0, subWidth, subHeight); + gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget()); + rect = SkIRect::MakeXYWH(subWidth, 0, subWidth, subHeight); + gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget()); + rect = SkIRect::MakeXYWH(0, subHeight, subWidth, subHeight); + gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget()); + + // Should fail since bottom right sub area has not been cleared to white + gpu->readPixels(tex, 0, 0, width, height, config, (void*)buffer.get(), 0); + REPORTER_ASSERT(reporter, !does_full_buffer_contain_correct_color(buffer.get(), + GrColor_WHITE, + config, + width, + height)); + + rect = SkIRect::MakeXYWH(subWidth, subHeight, subWidth, subHeight); + gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget()); + + gpu->readPixels(tex, 0, 0, width, height, config, (void*)buffer.get(), 0); + REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(), + GrColor_WHITE, + config, + width, + height)); + + // Try different colors and that each sub area has correct color + GrColor subColor1 = GrColorPackRGBA(0xFF, 0x00, 0x00, 0xFF); + GrColor subColor2 = GrColorPackRGBA(0x00, 0xFF, 0x00, 0xFF); + GrColor subColor3 = GrColorPackRGBA(0x00, 0x00, 0xFF, 0xFF); + GrColor subColor4 = GrColorPackRGBA(0xFF, 0xFF, 0x00, 0xFF); + + rect = SkIRect::MakeXYWH(0, 0, subWidth, subHeight); + gpu->clear(rect, subColor1, tex->asRenderTarget()); + rect = SkIRect::MakeXYWH(subWidth, 0, subWidth, subHeight); + gpu->clear(rect, subColor2, tex->asRenderTarget()); + rect = SkIRect::MakeXYWH(0, subHeight, subWidth, subHeight); + gpu->clear(rect, subColor3, tex->asRenderTarget()); + rect = SkIRect::MakeXYWH(subWidth, subHeight, subWidth, subHeight); + gpu->clear(rect, subColor4, tex->asRenderTarget()); + + gpu->readPixels(tex, 0, 0, subWidth, subHeight, config, (void*)subBuffer.get(), 0); + REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(subBuffer.get(), + subColor1, + config, + subWidth, + subHeight)); + gpu->readPixels(tex, subWidth, 0, subWidth, subHeight, config, (void*)subBuffer.get(), 0); + REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(subBuffer.get(), + subColor2, + config, + subWidth, + subHeight)); + gpu->readPixels(tex, 0, subHeight, subWidth, subHeight, config, (void*)subBuffer.get(), 0); + REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(subBuffer.get(), + subColor3, + config, + subWidth, + subHeight)); + gpu->readPixels(tex, subWidth, subHeight, subWidth, subHeight, + config, (void*)subBuffer.get(), 0); + REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(subBuffer.get(), + subColor4, + config, + subWidth, + subHeight)); +#endif +} + +DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkClearTests, reporter, ctxInfo) { + basic_clear_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig); + basic_clear_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig); + sub_clear_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig); + sub_clear_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig); +} + +#endif
diff --git a/src/third_party/skia/tests/VkHeapTests.cpp b/src/third_party/skia/tests/VkHeapTests.cpp new file mode 100644 index 0000000..fd5f88a --- /dev/null +++ b/src/third_party/skia/tests/VkHeapTests.cpp
@@ -0,0 +1,239 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// This is a GPU-backend specific test. It relies on static intializers to work + +#include "SkTypes.h" + +#if SK_SUPPORT_GPU && defined(SK_VULKAN) + +#include "GrContextFactory.h" +#include "GrTest.h" +#include "Test.h" +#include "vk/GrVkGpu.h" + +using sk_gpu_test::GrContextFactory; + +void subheap_test(skiatest::Reporter* reporter, GrContext* context) { + GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu()); + + // memtype doesn't matter, we're just testing the suballocation algorithm so we'll use 0 + GrVkSubHeap heap(gpu, 0, 0, 64 * 1024, 32); + GrVkAlloc alloc0, alloc1, alloc2, alloc3; + // test full allocation and free + REPORTER_ASSERT(reporter, heap.alloc(64 * 1024, &alloc0)); + REPORTER_ASSERT(reporter, alloc0.fOffset == 0); + REPORTER_ASSERT(reporter, alloc0.fSize == 64 * 1024); + REPORTER_ASSERT(reporter, heap.freeSize() == 0 && heap.largestBlockSize() == 0); + heap.free(alloc0); + REPORTER_ASSERT(reporter, heap.freeSize() == 64*1024 && heap.largestBlockSize() == 64 * 1024); + + // now let's suballoc some memory + REPORTER_ASSERT(reporter, heap.alloc(16 * 1024, &alloc0)); + REPORTER_ASSERT(reporter, heap.alloc(23 * 1024, &alloc1)); + REPORTER_ASSERT(reporter, heap.alloc(18 * 1024, &alloc2)); + REPORTER_ASSERT(reporter, heap.freeSize() == 7 * 1024 && heap.largestBlockSize() == 7 * 1024); + // free lone block + heap.free(alloc1); + REPORTER_ASSERT(reporter, heap.freeSize() == 30 * 1024 && heap.largestBlockSize() == 23 * 1024); + // allocate into smallest free block + REPORTER_ASSERT(reporter, heap.alloc(6 * 1024, &alloc3)); + REPORTER_ASSERT(reporter, heap.freeSize() == 24 * 1024 && heap.largestBlockSize() == 23 * 1024); + // allocate into exact size free block + REPORTER_ASSERT(reporter, heap.alloc(23 * 1024, &alloc1)); + REPORTER_ASSERT(reporter, heap.freeSize() == 1 * 1024 && heap.largestBlockSize() == 1 * 1024); + // free lone block + heap.free(alloc2); + REPORTER_ASSERT(reporter, heap.freeSize() == 19 * 1024 && heap.largestBlockSize() == 18 * 1024); + // free and merge with preceding block and following + heap.free(alloc3); + REPORTER_ASSERT(reporter, heap.freeSize() == 25 * 1024 && heap.largestBlockSize() == 25 * 1024); + // free and merge with following block + heap.free(alloc1); + REPORTER_ASSERT(reporter, heap.freeSize() == 48 * 1024 && heap.largestBlockSize() == 48 * 1024); + // free starting block and merge with following + heap.free(alloc0); + REPORTER_ASSERT(reporter, heap.freeSize() == 64 * 1024 && heap.largestBlockSize() == 64 * 1024); + + // realloc + REPORTER_ASSERT(reporter, heap.alloc(4 * 1024, &alloc0)); + REPORTER_ASSERT(reporter, heap.alloc(35 * 1024, &alloc1)); + REPORTER_ASSERT(reporter, heap.alloc(10 * 1024, &alloc2)); + REPORTER_ASSERT(reporter, heap.freeSize() == 15 * 1024 && heap.largestBlockSize() == 15 * 1024); + // free starting block and merge with following + heap.free(alloc0); + REPORTER_ASSERT(reporter, heap.freeSize() == 19 * 1024 && heap.largestBlockSize() == 15 * 1024); + // free block and merge with preceding + heap.free(alloc1); + REPORTER_ASSERT(reporter, heap.freeSize() == 54 * 1024 && heap.largestBlockSize() == 39 * 1024); + // free block and merge with preceding and following + heap.free(alloc2); + REPORTER_ASSERT(reporter, heap.freeSize() == 64 * 1024 && heap.largestBlockSize() == 64 * 1024); + + // fragment + REPORTER_ASSERT(reporter, heap.alloc(19 * 1024, &alloc0)); + REPORTER_ASSERT(reporter, heap.alloc(5 * 1024, &alloc1)); + REPORTER_ASSERT(reporter, heap.alloc(15 * 1024, &alloc2)); + REPORTER_ASSERT(reporter, heap.alloc(3 * 1024, &alloc3)); + REPORTER_ASSERT(reporter, heap.freeSize() == 22 * 1024 && heap.largestBlockSize() == 22 * 1024); + heap.free(alloc0); + REPORTER_ASSERT(reporter, heap.freeSize() == 41 * 1024 && heap.largestBlockSize() == 22 * 1024); + heap.free(alloc2); + REPORTER_ASSERT(reporter, heap.freeSize() == 56 * 1024 && heap.largestBlockSize() == 22 * 1024); + REPORTER_ASSERT(reporter, !heap.alloc(40 * 1024, &alloc0)); + heap.free(alloc3); + REPORTER_ASSERT(reporter, heap.freeSize() == 59 * 1024 && heap.largestBlockSize() == 40 * 1024); + REPORTER_ASSERT(reporter, heap.alloc(40 * 1024, &alloc0)); + REPORTER_ASSERT(reporter, heap.freeSize() == 19 * 1024 && heap.largestBlockSize() == 19 * 1024); + heap.free(alloc1); + REPORTER_ASSERT(reporter, heap.freeSize() == 24 * 1024 && heap.largestBlockSize() == 24 * 1024); + heap.free(alloc0); + REPORTER_ASSERT(reporter, heap.freeSize() == 64 * 1024 && heap.largestBlockSize() == 64 * 1024); + + // unaligned sizes + REPORTER_ASSERT(reporter, heap.alloc(19 * 1024 - 31, &alloc0)); + REPORTER_ASSERT(reporter, heap.alloc(5 * 1024 - 5, &alloc1)); + REPORTER_ASSERT(reporter, heap.alloc(15 * 1024 - 19, &alloc2)); + REPORTER_ASSERT(reporter, heap.alloc(3 * 1024 - 3, &alloc3)); + REPORTER_ASSERT(reporter, heap.freeSize() == 22 * 1024 && heap.largestBlockSize() == 22 * 1024); + heap.free(alloc0); + REPORTER_ASSERT(reporter, heap.freeSize() == 41 * 1024 && heap.largestBlockSize() == 22 * 1024); + heap.free(alloc2); + REPORTER_ASSERT(reporter, heap.freeSize() == 56 * 1024 && heap.largestBlockSize() == 22 * 1024); + REPORTER_ASSERT(reporter, !heap.alloc(40 * 1024, &alloc0)); + heap.free(alloc3); + REPORTER_ASSERT(reporter, heap.freeSize() == 59 * 1024 && heap.largestBlockSize() == 40 * 1024); + REPORTER_ASSERT(reporter, heap.alloc(40 * 1024, &alloc0)); + REPORTER_ASSERT(reporter, heap.freeSize() == 19 * 1024 && heap.largestBlockSize() == 19 * 1024); + heap.free(alloc1); + REPORTER_ASSERT(reporter, heap.freeSize() == 24 * 1024 && heap.largestBlockSize() == 24 * 1024); + heap.free(alloc0); + REPORTER_ASSERT(reporter, heap.freeSize() == 64 * 1024 && heap.largestBlockSize() == 64 * 1024); +} + +void suballoc_test(skiatest::Reporter* reporter, GrContext* context) { + GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu()); + + // memtype/heap index don't matter, we're just testing the allocation algorithm so we'll use 0 + GrVkHeap heap(gpu, GrVkHeap::kSubAlloc_Strategy, 64 * 1024); + GrVkAlloc alloc0, alloc1, alloc2, alloc3; + const VkDeviceSize kAlignment = 16; + const uint32_t kMemType = 0; + const uint32_t kHeapIndex = 0; + + REPORTER_ASSERT(reporter, heap.allocSize() == 0 && heap.usedSize() == 0); + + // fragment allocations so we need to grow heap + REPORTER_ASSERT(reporter, heap.alloc(19 * 1024 - 3, kAlignment, kMemType, kHeapIndex, &alloc0)); + REPORTER_ASSERT(reporter, heap.alloc(5 * 1024 - 9, kAlignment, kMemType, kHeapIndex, &alloc1)); + REPORTER_ASSERT(reporter, heap.alloc(15 * 1024 - 15, kAlignment, kMemType, kHeapIndex, &alloc2)); + REPORTER_ASSERT(reporter, heap.alloc(3 * 1024 - 6, kAlignment, kMemType, kHeapIndex, &alloc3)); + REPORTER_ASSERT(reporter, heap.allocSize() == 64 * 1024 && heap.usedSize() == 42 * 1024); + heap.free(alloc0); + REPORTER_ASSERT(reporter, heap.allocSize() == 64 * 1024 && heap.usedSize() == 23 * 1024); + heap.free(alloc2); + REPORTER_ASSERT(reporter, heap.allocSize() == 64 * 1024 && heap.usedSize() == 8 * 1024); + // we expect the heap to grow here + REPORTER_ASSERT(reporter, heap.alloc(40 * 1024, kAlignment, kMemType, kHeapIndex, &alloc0)); + REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 48 * 1024); + heap.free(alloc3); + REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 45 * 1024); + // heap should not grow here (first subheap has exactly enough room) + REPORTER_ASSERT(reporter, heap.alloc(40 * 1024, kAlignment, kMemType, kHeapIndex, &alloc3)); + REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 85 * 1024); + // heap should not grow here (second subheap has room) + REPORTER_ASSERT(reporter, heap.alloc(22 * 1024, kAlignment, kMemType, kHeapIndex, &alloc2)); + REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 107 * 1024); + heap.free(alloc1); + REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 102 * 1024); + heap.free(alloc0); + REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 62 * 1024); + heap.free(alloc2); + REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 40 * 1024); + heap.free(alloc3); + REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 0 * 1024); + // heap should not grow here (allocating more than subheap size) + REPORTER_ASSERT(reporter, heap.alloc(128 * 1024, kAlignment, kMemType, kHeapIndex, &alloc0)); + REPORTER_ASSERT(reporter, 0 == alloc0.fSize); + REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 0 * 1024); + heap.free(alloc0); + REPORTER_ASSERT(reporter, heap.alloc(24 * 1024, kAlignment, kMemType, kHeapIndex, &alloc0)); + REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 24 * 1024); + // heap should alloc a new subheap because the memory type is different + REPORTER_ASSERT(reporter, heap.alloc(24 * 1024, kAlignment, kMemType+1, kHeapIndex, &alloc1)); + REPORTER_ASSERT(reporter, heap.allocSize() == 192 * 1024 && heap.usedSize() == 48 * 1024); + // heap should alloc a new subheap because the alignment is different + REPORTER_ASSERT(reporter, heap.alloc(24 * 1024, 128, kMemType, kHeapIndex, &alloc2)); + REPORTER_ASSERT(reporter, heap.allocSize() == 256 * 1024 && heap.usedSize() == 72 * 1024); + heap.free(alloc2); + heap.free(alloc0); + heap.free(alloc1); + REPORTER_ASSERT(reporter, heap.allocSize() == 256 * 1024 && heap.usedSize() == 0 * 1024); +} + +void singlealloc_test(skiatest::Reporter* reporter, GrContext* context) { + GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu()); + + // memtype/heap index don't matter, we're just testing the allocation algorithm so we'll use 0 + GrVkHeap heap(gpu, GrVkHeap::kSingleAlloc_Strategy, 64 * 1024); + GrVkAlloc alloc0, alloc1, alloc2, alloc3; + const VkDeviceSize kAlignment = 64; + const uint32_t kMemType = 0; + const uint32_t kHeapIndex = 0; + + REPORTER_ASSERT(reporter, heap.allocSize() == 0 && heap.usedSize() == 0); + + // make a few allocations + REPORTER_ASSERT(reporter, heap.alloc(49 * 1024 - 3, kAlignment, kMemType, kHeapIndex, &alloc0)); + REPORTER_ASSERT(reporter, heap.alloc(5 * 1024 - 37, kAlignment, kMemType, kHeapIndex, &alloc1)); + REPORTER_ASSERT(reporter, heap.alloc(15 * 1024 - 11, kAlignment, kMemType, kHeapIndex, &alloc2)); + REPORTER_ASSERT(reporter, heap.alloc(3 * 1024 - 29, kAlignment, kMemType, kHeapIndex, &alloc3)); + REPORTER_ASSERT(reporter, heap.allocSize() == 72 * 1024 && heap.usedSize() == 72 * 1024); + heap.free(alloc0); + REPORTER_ASSERT(reporter, heap.allocSize() == 72 * 1024 && heap.usedSize() == 23 * 1024); + heap.free(alloc2); + REPORTER_ASSERT(reporter, heap.allocSize() == 72 * 1024 && heap.usedSize() == 8 * 1024); + // heap should not grow here (first subheap has room) + REPORTER_ASSERT(reporter, heap.alloc(40 * 1024, kAlignment, kMemType, kHeapIndex, &alloc0)); + REPORTER_ASSERT(reporter, heap.allocSize() == 72 * 1024 && heap.usedSize() == 48 * 1024); + heap.free(alloc3); + REPORTER_ASSERT(reporter, heap.allocSize() == 72 * 1024 && heap.usedSize() == 45 * 1024); + // check for exact fit -- heap should not grow here (third subheap has room) + REPORTER_ASSERT(reporter, heap.alloc(15 * 1024 - 63, kAlignment, kMemType, kHeapIndex, &alloc2)); + REPORTER_ASSERT(reporter, heap.allocSize() == 72 * 1024 && heap.usedSize() == 60 * 1024); + heap.free(alloc2); + REPORTER_ASSERT(reporter, heap.allocSize() == 72 * 1024 && heap.usedSize() == 45 * 1024); + // heap should grow here (no subheap has room) + REPORTER_ASSERT(reporter, heap.alloc(40 * 1024, kAlignment, kMemType, kHeapIndex, &alloc3)); + REPORTER_ASSERT(reporter, heap.allocSize() == 112 * 1024 && heap.usedSize() == 85 * 1024); + heap.free(alloc1); + REPORTER_ASSERT(reporter, heap.allocSize() == 112 * 1024 && heap.usedSize() == 80 * 1024); + heap.free(alloc0); + REPORTER_ASSERT(reporter, heap.allocSize() == 112 * 1024 && heap.usedSize() == 40 * 1024); + heap.free(alloc3); + REPORTER_ASSERT(reporter, heap.allocSize() == 112 * 1024 && heap.usedSize() == 0 * 1024); + REPORTER_ASSERT(reporter, heap.alloc(24 * 1024, kAlignment, kMemType, kHeapIndex, &alloc0)); + REPORTER_ASSERT(reporter, heap.allocSize() == 112 * 1024 && heap.usedSize() == 24 * 1024); + // heap should alloc a new subheap because the memory type is different + REPORTER_ASSERT(reporter, heap.alloc(24 * 1024, kAlignment, kMemType + 1, kHeapIndex, &alloc1)); + REPORTER_ASSERT(reporter, heap.allocSize() == 136 * 1024 && heap.usedSize() == 48 * 1024); + // heap should alloc a new subheap because the alignment is different + REPORTER_ASSERT(reporter, heap.alloc(24 * 1024, 128, kMemType, kHeapIndex, &alloc2)); + REPORTER_ASSERT(reporter, heap.allocSize() == 160 * 1024 && heap.usedSize() == 72 * 1024); + heap.free(alloc1); + heap.free(alloc2); + heap.free(alloc0); + REPORTER_ASSERT(reporter, heap.allocSize() == 160 * 1024 && heap.usedSize() == 0 * 1024); +} + +DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkHeapTests, reporter, ctxInfo) { + subheap_test(reporter, ctxInfo.grContext()); + suballoc_test(reporter, ctxInfo.grContext()); + singlealloc_test(reporter, ctxInfo.grContext()); +} + +#endif
diff --git a/src/third_party/skia/tests/VkUploadPixelsTests.cpp b/src/third_party/skia/tests/VkUploadPixelsTests.cpp new file mode 100644 index 0000000..3fa9419 --- /dev/null +++ b/src/third_party/skia/tests/VkUploadPixelsTests.cpp
@@ -0,0 +1,152 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// This is a GPU-backend specific test. It relies on static intializers to work + +#include "SkTypes.h" + +#if SK_SUPPORT_GPU && defined(SK_VULKAN) + +#include "GrContextFactory.h" +#include "GrContextPriv.h" +#include "GrSurfaceProxy.h" +#include "GrTest.h" +#include "SkGr.h" +#include "Test.h" +#include "vk/GrVkGpu.h" + +using sk_gpu_test::GrContextFactory; + +void fill_pixel_data(int width, int height, GrColor* data) { + + // build red-green gradient + for (int j = 0; j < height; ++j) { + for (int i = 0; i < width; ++i) { + unsigned int red = (unsigned int)(256.f*(i / (float)width)); + unsigned int green = (unsigned int)(256.f*(j / (float)height)); + data[i + j*width] = GrColorPackRGBA(red - (red>>8), green - (green>>8), 0xff, 0xff); + } + } +} + +bool does_full_buffer_contain_correct_color(GrColor* srcBuffer, + GrColor* dstBuffer, + int width, + int height) { + GrColor* srcPtr = srcBuffer; + GrColor* dstPtr = dstBuffer; + for (int j = 0; j < height; ++j) { + for (int i = 0; i < width; ++i) { + if (srcPtr[i] != dstPtr[i]) { + return false; + } + } + srcPtr += width; + dstPtr += width; + } + return true; +} + +void basic_texture_test(skiatest::Reporter* reporter, GrContext* context, GrPixelConfig config, + bool renderTarget) { + const int kWidth = 16; + const int kHeight = 16; + SkAutoTMalloc<GrColor> srcBuffer(kWidth*kHeight); + SkAutoTMalloc<GrColor> dstBuffer(kWidth*kHeight); + + fill_pixel_data(kWidth, kHeight, srcBuffer.get()); + + GrSurfaceDesc surfDesc; + surfDesc.fFlags = renderTarget ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags; + surfDesc.fOrigin = kTopLeft_GrSurfaceOrigin; + surfDesc.fWidth = kWidth; + surfDesc.fHeight = kHeight; + surfDesc.fConfig = config; + surfDesc.fSampleCnt = 0; + + SkColorType ct; + SkAssertResult(GrPixelConfigToColorType(config, &ct)); + + sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), + surfDesc, SkBudgeted::kNo, + srcBuffer, 0); + REPORTER_ASSERT(reporter, proxy); + if (proxy) { + sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext( + proxy, nullptr); + + SkImageInfo dstInfo = SkImageInfo::Make(kWidth, kHeight, ct, kOpaque_SkAlphaType); + + bool result = sContext->readPixels(dstInfo, dstBuffer, 0, 0, 0); + REPORTER_ASSERT(reporter, result); + REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(srcBuffer, + dstBuffer, + kWidth, + kHeight)); + + dstInfo = SkImageInfo::Make(10, 2, ct, kOpaque_SkAlphaType); + result = sContext->writePixels(dstInfo, srcBuffer, 0, 2, 10); + REPORTER_ASSERT(reporter, result); + + memset(dstBuffer, 0, kWidth*kHeight*sizeof(GrColor)); + + result = sContext->readPixels(dstInfo, dstBuffer, 0, 2, 10); + REPORTER_ASSERT(reporter, result); + + REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(srcBuffer, + dstBuffer, + 10, + 2)); + } + + surfDesc.fOrigin = kBottomLeft_GrSurfaceOrigin; + + proxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), + surfDesc, SkBudgeted::kNo, + srcBuffer, 0); + REPORTER_ASSERT(reporter, proxy); + if (proxy) { + sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext( + proxy, nullptr); + + SkImageInfo dstInfo = SkImageInfo::Make(kWidth, kHeight, ct, kOpaque_SkAlphaType); + + bool result = sContext->readPixels(dstInfo, dstBuffer, 0, 0, 0); + REPORTER_ASSERT(reporter, result); + REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(srcBuffer, + dstBuffer, + kWidth, + kHeight)); + + dstInfo = SkImageInfo::Make(4, 5, ct, kOpaque_SkAlphaType); + result = sContext->writePixels(dstInfo, srcBuffer, 0, 5, 4); + REPORTER_ASSERT(reporter, result); + + memset(dstBuffer, 0, kWidth*kHeight*sizeof(GrColor)); + + result = sContext->readPixels(dstInfo, dstBuffer, 0, 5, 4); + REPORTER_ASSERT(reporter, result); + + REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(srcBuffer, + dstBuffer, + 4, + 5)); + + } +} + +DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkUploadPixelsTests, reporter, ctxInfo) { + // RGBA + basic_texture_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig, false); + basic_texture_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig, true); + + // BGRA + basic_texture_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig, false); + basic_texture_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig, true); +} + +#endif
diff --git a/src/third_party/skia/tests/VkWrapTests.cpp b/src/third_party/skia/tests/VkWrapTests.cpp new file mode 100644 index 0000000..4e36315 --- /dev/null +++ b/src/third_party/skia/tests/VkWrapTests.cpp
@@ -0,0 +1,175 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// This is a GPU-backend specific test. It relies on static intializers to work + +#include "SkTypes.h" + +#if SK_SUPPORT_GPU && defined(SK_VULKAN) + +#include "GrContextFactory.h" +#include "GrTest.h" +#include "GrTexture.h" + +#include "Test.h" +#include "vk/GrVkCaps.h" +#include "vk/GrVkGpu.h" +#include "vk/GrVkMemory.h" +#include "vk/GrVkTypes.h" + +using sk_gpu_test::GrContextFactory; + +const int kW = 1024; +const int kH = 1024; +const GrPixelConfig kPixelConfig = kRGBA_8888_GrPixelConfig; + +void wrap_tex_test(skiatest::Reporter* reporter, GrContext* context) { + + GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu()); + + GrBackendObject backendObj = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kPixelConfig, + false); + const GrVkImageInfo* imageInfo = reinterpret_cast<const GrVkImageInfo*>(backendObj); + + GrBackendTexture backendTex = GrBackendTexture(kW, kH, *imageInfo); + sk_sp<GrTexture> tex = gpu->wrapBackendTexture(backendTex, + kTopLeft_GrSurfaceOrigin, + kBorrow_GrWrapOwnership); + REPORTER_ASSERT(reporter, tex); + + // image is null + GrVkImageInfo backendCopy = *imageInfo; + backendCopy.fImage = VK_NULL_HANDLE; + backendTex = GrBackendTexture(kW, kH, backendCopy); + tex = gpu->wrapBackendTexture(backendTex, + kTopLeft_GrSurfaceOrigin, + kBorrow_GrWrapOwnership); + REPORTER_ASSERT(reporter, !tex); + tex = gpu->wrapBackendTexture(backendTex, + kTopLeft_GrSurfaceOrigin, + kAdopt_GrWrapOwnership); + REPORTER_ASSERT(reporter, !tex); + + // alloc is null + backendCopy.fImage = imageInfo->fImage; + backendCopy.fAlloc = { VK_NULL_HANDLE, 0, 0, 0 }; + backendTex = GrBackendTexture(kW, kH, backendCopy); + tex = gpu->wrapBackendTexture(backendTex, + kTopLeft_GrSurfaceOrigin, + kBorrow_GrWrapOwnership); + REPORTER_ASSERT(reporter, !tex); + tex = gpu->wrapBackendTexture(backendTex, + kTopLeft_GrSurfaceOrigin, + kAdopt_GrWrapOwnership); + REPORTER_ASSERT(reporter, !tex); + // check adopt creation + backendCopy.fAlloc = imageInfo->fAlloc; + backendTex = GrBackendTexture(kW, kH, backendCopy); + tex = gpu->wrapBackendTexture(backendTex, + kTopLeft_GrSurfaceOrigin, + kAdopt_GrWrapOwnership); + + REPORTER_ASSERT(reporter, tex); + + gpu->deleteTestingOnlyBackendTexture(backendObj, true); +} + +void wrap_rt_test(skiatest::Reporter* reporter, GrContext* context) { + GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu()); + + GrBackendObject backendObj = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kPixelConfig, + true); + const GrVkImageInfo* backendTex = reinterpret_cast<const GrVkImageInfo*>(backendObj); + + GrBackendRenderTarget backendRT(kW, kH, 0, 0, *backendTex); + + sk_sp<GrRenderTarget> rt = gpu->wrapBackendRenderTarget(backendRT, kTopLeft_GrSurfaceOrigin); + REPORTER_ASSERT(reporter, rt); + + // image is null + GrVkImageInfo backendCopy = *backendTex; + backendCopy.fImage = VK_NULL_HANDLE; + GrBackendRenderTarget backendRT2(kW, kH, 0, 0, backendCopy); + rt = gpu->wrapBackendRenderTarget(backendRT2, kTopLeft_GrSurfaceOrigin); + REPORTER_ASSERT(reporter, !rt); + + // alloc is null + backendCopy.fImage = backendTex->fImage; + backendCopy.fAlloc = { VK_NULL_HANDLE, 0, 0, 0 }; + // can wrap null alloc + GrBackendRenderTarget backendRT3(kW, kH, 0, 0, backendCopy); + rt = gpu->wrapBackendRenderTarget(backendRT3, kTopLeft_GrSurfaceOrigin); + REPORTER_ASSERT(reporter, rt); + + // When we wrapBackendRenderTarget it is always borrowed, so we must make sure to free the + // resource when we're done. + gpu->deleteTestingOnlyBackendTexture(backendObj, false); +} + +void wrap_trt_test(skiatest::Reporter* reporter, GrContext* context) { + GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu()); + + GrBackendObject backendObj = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kPixelConfig, + true); + const GrVkImageInfo* imageInfo = reinterpret_cast<const GrVkImageInfo*>(backendObj); + + GrBackendTexture backendTex = GrBackendTexture(kW, kH, *imageInfo); + sk_sp<GrTexture> tex = gpu->wrapRenderableBackendTexture(backendTex, + kTopLeft_GrSurfaceOrigin, + 0, + kBorrow_GrWrapOwnership); + REPORTER_ASSERT(reporter, tex); + + // image is null + GrVkImageInfo backendCopy = *imageInfo; + backendCopy.fImage = VK_NULL_HANDLE; + backendTex = GrBackendTexture(kW, kH, backendCopy); + tex = gpu->wrapRenderableBackendTexture(backendTex, + kTopLeft_GrSurfaceOrigin, + 0, + kBorrow_GrWrapOwnership); + REPORTER_ASSERT(reporter, !tex); + tex = gpu->wrapRenderableBackendTexture(backendTex, + kTopLeft_GrSurfaceOrigin, + 0, + kAdopt_GrWrapOwnership); + REPORTER_ASSERT(reporter, !tex); + + // alloc is null + backendCopy.fImage = imageInfo->fImage; + backendCopy.fAlloc = { VK_NULL_HANDLE, 0, 0, 0 }; + backendTex = GrBackendTexture(kW, kH, backendCopy); + tex = gpu->wrapRenderableBackendTexture(backendTex, + kTopLeft_GrSurfaceOrigin, + 0, + kBorrow_GrWrapOwnership); + REPORTER_ASSERT(reporter, !tex); + tex = gpu->wrapRenderableBackendTexture(backendTex, + kTopLeft_GrSurfaceOrigin, + 0, + kAdopt_GrWrapOwnership); + REPORTER_ASSERT(reporter, !tex); + + // check adopt creation + backendCopy.fAlloc = imageInfo->fAlloc; + backendTex = GrBackendTexture(kW, kH, backendCopy); + tex = gpu->wrapRenderableBackendTexture(backendTex, + kTopLeft_GrSurfaceOrigin, + 0, + kAdopt_GrWrapOwnership); + REPORTER_ASSERT(reporter, tex); + + gpu->deleteTestingOnlyBackendTexture(backendObj, true); +} + +DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkWrapTests, reporter, ctxInfo) { + wrap_tex_test(reporter, ctxInfo.grContext()); + wrap_rt_test(reporter, ctxInfo.grContext()); + wrap_trt_test(reporter, ctxInfo.grContext()); +} + +#endif
diff --git a/src/third_party/skia/tests/WArrayTest.cpp b/src/third_party/skia/tests/WArrayTest.cpp deleted file mode 100644 index 7f03e5b..0000000 --- a/src/third_party/skia/tests/WArrayTest.cpp +++ /dev/null
@@ -1,207 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Test.h" - -// Include the implementation so we can make an appropriate template instance. -#include "SkAdvancedTypefaceMetrics.h" - -using namespace skia_advanced_typeface_metrics_utils; - -// Negative values and zeros in a range plus trailing zeros. -// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -static const int16_t data1[] = {-1, 0, -3, 4, 5, 6, 7, 0, 0, 0, 8, 0, 0, 0, 0}; -static const char* expected1 = "0[-1 0 -3 4 5 6 7 0 0 0 8]"; - -// Run with leading and trailing zeros. -// Test rules: d 0 1 2 3 4 5 6 7 8 9 10 11 -static const int16_t data2[] = {0, 0, 0, 100, 100, 100, 100, 100, 100, 100, 0, 0}; -static const char* expected2 = "3 9 100"; - -// Removing 0's from a range. -// Test rules: a 0 1 2 3 4 5 6 7 8 9 10 11 -static const int16_t data3[] = {1, 2, 0, 0, 0, 3, 4, 0, 0, 0, 0, 5}; -static const char* expected3 = "0[1 2 0 0 0 3 4] 11[5]"; - -// Removing 0's from a run/range and between runs. -// Test rules: a, b 0 1 2 3 4 5 6 7 8 9 10 11 12 14 15 -static const int16_t data4[] = {1, 0, 0, 0, 1, 2, 2, 2, 3, 0, 0, 0, 0, 3, 4}; -static const char* expected4 = "0[1 0 0 0 1] 5 7 2 8[3] 13[3 4]"; - -// Runs that starts outside a range. -// Test rules: a, e 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 -static const int16_t data5[] = {1, 1, 2, 3, 0, 0, 0, 0, 5, 5, 6, 7, 0, 0, 0, 0, 8, 0}; -static const char* expected5 = "0 1 1 2[2 3] 8 9 5 10[6 7] 16[8]"; - -// Zeros and runs that should be broken out. -// Test rules: a, b, e 0 1 2 3 4 5 6 7 8 9 10 11 12 13 -static const int16_t data6[] = {1, 0, 0, 0, 0, 1, 2, 3, 3, 4, 5, 5, 5, 6}; -static const char* expected6 = "0[1] 5[1 2 3 3 4] 10 12 5 13[6]"; - -// Don't cares that aren't enough to break out a run. -// Test rules: c 0 1 2 3 4 5 -static const int16_t data7[] = {1, 2, 10, 11, 2, 3}; -static const char* expected7 = "0[1 2 10 11 2 3]"; -static const uint32_t subset7[] = {0, 1, 4, 5}; -static const char* expectedSubset7 = "0[1 2 0 0 2 3]"; - -// Don't cares that are enough to break out a run. -// Test rules: c 0 1 2 3 4 5 6 -static const int16_t data8[] = {1, 2, 10, 11, 12, 2, 3}; -static const char* expected8 = "0[1 2 10 11 12 2 3]"; -static const uint32_t subset8[] = {0, 1, 5, 6}; -static const char* expectedSubset8 = "0[1] 1 5 2 6[3]"; - -// Leading don't cares. -// Test rules: d 0 1 2 3 4 -static const int16_t data9[] = {1, 1, 10, 2, 3}; -static const char* expected9 = "0 1 1 2[10 2 3]"; -static const uint32_t subset9[] = {0, 1, 3, 4}; -static const char* expectedSubset9 = "0 1 1 3[2 3]"; - -// Almost run of don't cares inside a range. -// Test rules: c 0 1 2 3 4 5 -static const int16_t data10[] = {1, 2, 10, 11, 12, 3}; -static const char* expected10 = "0[1 2 10 11 12 3]"; -static const uint32_t subset10[] = {0, 1, 5}; -static const char* expectedSubset10 = "0[1 2 0 0 0 3]"; - -// Run of don't cares inside a range. -// Test rules: c 0 1 2 3 4 5 6 -static const int16_t data11[] = {1, 2, 10, 11, 12, 13, 3}; -static const char* expected11 = "0[1 2 10 11 12 13 3]"; -static const uint32_t subset11[] = {0, 1, 6}; -static const char* expectedSubset11 = "0[1 2] 6[3]"; - -// Almost run within a range with leading don't cares. -// Test rules: c 0 1 2 3 4 5 6 -static const int16_t data12[] = {1, 10, 11, 2, 12, 13, 3}; -static const char* expected12 = "0[1 10 11 2 12 13 3]"; -static const uint32_t subset12[] = {0, 3, 6}; -static const char* expectedSubset12 = "0[1 0 0 2 0 0 3]"; - -// Run within a range with leading don't cares. -// Test rules: c 0 1 2 3 4 5 6 7 -static const int16_t data13[] = {1, 10, 11, 2, 2, 12, 13, 3}; -static const char* expected13 = "0[1 10 11 2 2 12 13 3]"; -static const uint32_t subset13[] = {0, 3, 4, 7}; -static const char* expectedSubset13 = "0[1] 1 6 2 7[3]"; - -// Enough don't cares to breakup something. -// Test rules: a 0 1 2 3 4 5 -static const int16_t data14[] = {1, 0, 0, 0, 0, 2}; -static const char* expected14 = "0[1] 5[2]"; -static const uint32_t subset14[] = {0, 5}; -static const char* expectedSubset14 = "0[1] 5[2]"; - -static SkString stringify_advance_data(SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>* data) { - SkString result; - bool leadingSpace = false; - while (data != NULL) { - if (leadingSpace) { - result.append(" "); - } else { - leadingSpace = true; - } - switch(data->fType) { - case SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>::kRun: - result.appendf("%d %d %d", data->fStartId, data->fEndId, data->fAdvance[0]); - break; - case SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>::kRange: - result.appendf("%d[", data->fStartId); - for (int i = 0; i < data->fAdvance.count(); ++i) { - if (i > 0) { - result.append(" "); - } - result.appendf("%d", data->fAdvance[i]); - } - result.append("]"); - break; - case SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>::kDefault: - result.appendf("<Default=%d>", data->fAdvance[0]); - break; - } - data = data->fNext.get(); - } - return result; -} - -class TestWData { - public: - TestWData(skiatest::Reporter* reporter, - const int16_t advances[], int advanceLen, - const uint32_t subset[], int subsetLen, - const char* expected) - : fAdvances(advances) - , fAdvancesLen(advanceLen) - , fSubset(subset) - , fSubsetLen(subsetLen) - , fExpected(expected) { - REPORTER_ASSERT(reporter, RunTest()); - } - - private: - const int16_t* fAdvances; - const int fAdvancesLen; - const uint32_t* fSubset; - const int fSubsetLen; - const char* fExpected; - - static bool getAdvance(void* tc, int gId, int16_t* advance) { - TestWData* testCase = (TestWData*)tc; - if (gId >= 0 && gId < testCase->fAdvancesLen) { - *advance = testCase->fAdvances[gId]; - return true; - } - return false; - } - - bool RunTest() { - SkAutoTDelete<SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t> > result; - result.reset(getAdvanceData((void*)this, fAdvancesLen, fSubset, fSubsetLen, getAdvance)); - - SkString stringResult = stringify_advance_data(result); - if (!stringResult.equals(fExpected)) { - SkDebugf("Expected: %s\n Result: %s\n", fExpected, stringResult.c_str()); - return false; - } - return true; - } -}; - -DEF_TEST(WArray, reporter) { - TestWData(reporter, data1, SK_ARRAY_COUNT(data1), NULL, 0, expected1); - TestWData(reporter, data2, SK_ARRAY_COUNT(data2), NULL, 0, expected2); - TestWData(reporter, data3, SK_ARRAY_COUNT(data3), NULL, 0, expected3); - TestWData(reporter, data4, SK_ARRAY_COUNT(data4), NULL, 0, expected4); - TestWData(reporter, data5, SK_ARRAY_COUNT(data5), NULL, 0, expected5); - TestWData(reporter, data6, SK_ARRAY_COUNT(data6), NULL, 0, expected6); - TestWData(reporter, data7, SK_ARRAY_COUNT(data7), NULL, 0, expected7); - TestWData(reporter, data7, SK_ARRAY_COUNT(data7), subset7, - SK_ARRAY_COUNT(subset7), expectedSubset7); - TestWData(reporter, data8, SK_ARRAY_COUNT(data8), NULL, 0, expected8); - TestWData(reporter, data8, SK_ARRAY_COUNT(data8), subset8, - SK_ARRAY_COUNT(subset8), expectedSubset8); - TestWData(reporter, data9, SK_ARRAY_COUNT(data9), NULL, 0, expected9); - TestWData(reporter, data9, SK_ARRAY_COUNT(data9), subset9, - SK_ARRAY_COUNT(subset9), expectedSubset9); - TestWData(reporter, data10, SK_ARRAY_COUNT(data10), NULL, 0, expected10); - TestWData(reporter, data10, SK_ARRAY_COUNT(data10), subset10, - SK_ARRAY_COUNT(subset10), expectedSubset10); - TestWData(reporter, data11, SK_ARRAY_COUNT(data11), NULL, 0, expected11); - TestWData(reporter, data11, SK_ARRAY_COUNT(data11), subset11, - SK_ARRAY_COUNT(subset11), expectedSubset11); - TestWData(reporter, data12, SK_ARRAY_COUNT(data12), NULL, 0, expected12); - TestWData(reporter, data12, SK_ARRAY_COUNT(data12), subset12, - SK_ARRAY_COUNT(subset12), expectedSubset12); - TestWData(reporter, data13, SK_ARRAY_COUNT(data13), NULL, 0, expected13); - TestWData(reporter, data13, SK_ARRAY_COUNT(data13), subset13, - SK_ARRAY_COUNT(subset13), expectedSubset13); - TestWData(reporter, data14, SK_ARRAY_COUNT(data14), NULL, 0, expected14); - TestWData(reporter, data14, SK_ARRAY_COUNT(data14), subset14, - SK_ARRAY_COUNT(subset14), expectedSubset14); -}
diff --git a/src/third_party/skia/tests/WindowRectanglesTest.cpp b/src/third_party/skia/tests/WindowRectanglesTest.cpp new file mode 100644 index 0000000..e954565 --- /dev/null +++ b/src/third_party/skia/tests/WindowRectanglesTest.cpp
@@ -0,0 +1,76 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTypes.h" +#include "Test.h" + +#if SK_SUPPORT_GPU + +#include "GrWindowRectangles.h" + +static SkIRect next_irect(SkRandom& r) { + return {r.nextS(), r.nextS(), r.nextS(), r.nextS()}; +} + +DEF_TEST(WindowRectangles, reporter) { + SkRandom r; + + SkIRect windowData[GrWindowRectangles::kMaxWindows]; + for (int i = 0; i < GrWindowRectangles::kMaxWindows; ++i) { + windowData[i] = next_irect(r); + } + + GrWindowRectangles wr; + for (int i = 0; i < GrWindowRectangles::kMaxWindows - 1; ++i) { + REPORTER_ASSERT(reporter, wr.count() == i); + REPORTER_ASSERT(reporter, !memcmp(wr.data(), windowData, i * sizeof(SkIRect))); + + GrWindowRectangles wr2(wr); + REPORTER_ASSERT(reporter, wr2 == wr); + REPORTER_ASSERT(reporter, wr2.count() == wr.count()); + REPORTER_ASSERT(reporter, !memcmp(wr2.data(), wr.data(), i * sizeof(SkIRect))); + + wr.addWindow(windowData[i]); + } + + SkASSERT(wr.count() == GrWindowRectangles::kMaxWindows - 1); + { + GrWindowRectangles A(wr), B(wr); + REPORTER_ASSERT(reporter, B == A); + REPORTER_ASSERT(reporter, B.data() == A.data()); // Should use copy-on-write. + + A.addWindow(windowData[GrWindowRectangles::kMaxWindows - 1]); + REPORTER_ASSERT(reporter, B.data() != A.data()); + REPORTER_ASSERT(reporter, B != A); + + B.addWindow(SkIRect::MakeLargest()); + REPORTER_ASSERT(reporter, B != A); + + REPORTER_ASSERT(reporter, !memcmp(A.data(), windowData, + GrWindowRectangles::kMaxWindows * sizeof(SkIRect))); + REPORTER_ASSERT(reporter, !memcmp(B.data(), windowData, + (GrWindowRectangles::kMaxWindows - 1) * sizeof(SkIRect))); + REPORTER_ASSERT(reporter, + B.data()[GrWindowRectangles::kMaxWindows - 1] == SkIRect::MakeLargest()); + } + { + GrWindowRectangles A(wr), B(wr); + REPORTER_ASSERT(reporter, B == A); + REPORTER_ASSERT(reporter, B.data() == A.data()); // Should use copy-on-write. + + A.addWindow(windowData[GrWindowRectangles::kMaxWindows - 1]); + B.addWindow(windowData[GrWindowRectangles::kMaxWindows - 1]); + REPORTER_ASSERT(reporter, B == A); + REPORTER_ASSERT(reporter, B.data() != A.data()); + REPORTER_ASSERT(reporter, !memcmp(B.data(), A.data(), + GrWindowRectangles::kMaxWindows * sizeof(SkIRect))); + REPORTER_ASSERT(reporter, !memcmp(A.data(), windowData, + GrWindowRectangles::kMaxWindows * sizeof(SkIRect))); + } +} + +#endif
diff --git a/src/third_party/skia/tests/WritePixelsTest.cpp b/src/third_party/skia/tests/WritePixelsTest.cpp index f47c67b..e876c8f 100644 --- a/src/third_party/skia/tests/WritePixelsTest.cpp +++ b/src/third_party/skia/tests/WritePixelsTest.cpp
@@ -5,30 +5,29 @@ * found in the LICENSE file. */ -#include "SkBitmapDevice.h" #include "SkCanvas.h" #include "SkColorPriv.h" #include "SkMathPriv.h" -#include "SkRegion.h" #include "SkSurface.h" #include "Test.h" #include "sk_tool_utils.h" #if SK_SUPPORT_GPU -#include "GrContextFactory.h" -#include "SkGpuDevice.h" -#else -class GrContext; -class GrContextFactory; +#include "GrBackendSurface.h" +#include "GrContext.h" +#include "GrGpu.h" +#include "GrTest.h" #endif +#include <initializer_list> + static const int DEV_W = 100, DEV_H = 100; static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H); static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1, DEV_H * SK_Scalar1); static const U8CPU DEV_PAD = 0xee; -static SkPMColor getCanvasColor(int x, int y) { +static SkPMColor get_canvas_color(int x, int y) { SkASSERT(x >= 0 && x < DEV_W); SkASSERT(y >= 0 && y < DEV_H); @@ -58,7 +57,7 @@ } // assumes any premu/.unpremul has been applied -static uint32_t packColorType(SkColorType ct, U8CPU a, U8CPU r, U8CPU g, U8CPU b) { +static uint32_t pack_color_type(SkColorType ct, U8CPU a, U8CPU r, U8CPU g, U8CPU b) { uint32_t r32; uint8_t* result = reinterpret_cast<uint8_t*>(&r32); switch (ct) { @@ -81,7 +80,7 @@ return r32; } -static uint32_t getBitmapColor(int x, int y, int w, SkColorType ct, SkAlphaType at) { +static uint32_t get_bitmap_color(int x, int y, int w, SkColorType ct, SkAlphaType at) { int n = y * w + x; U8CPU b = n & 0xff; U8CPU g = (n >> 8) & 0xff; @@ -109,24 +108,24 @@ g = SkMulDiv255Ceiling(g, a); b = SkMulDiv255Ceiling(b, a); } - return packColorType(ct, a, r, g , b); + return pack_color_type(ct, a, r, g , b); } -static void fillCanvas(SkCanvas* canvas) { +static void fill_canvas(SkCanvas* canvas) { SkBitmap bmp; if (bmp.isNull()) { bmp.allocN32Pixels(DEV_W, DEV_H); for (int y = 0; y < DEV_H; ++y) { for (int x = 0; x < DEV_W; ++x) { - *bmp.getAddr32(x, y) = getCanvasColor(x, y); + *bmp.getAddr32(x, y) = get_canvas_color(x, y); } } } canvas->save(); canvas->setMatrix(SkMatrix::I()); - canvas->clipRect(DEV_RECT_S, SkRegion::kReplace_Op); + canvas->clipRect(DEV_RECT_S, kReplace_SkClipOp); SkPaint paint; - paint.setXfermodeMode(SkXfermode::kSrc_Mode); + paint.setBlendMode(SkBlendMode::kSrc); canvas->drawBitmap(bmp, 0, 0, &paint); canvas->restore(); } @@ -165,7 +164,7 @@ return color; } -static bool checkPixel(SkPMColor a, SkPMColor b, bool didPremulConversion) { +static bool check_pixel(SkPMColor a, SkPMColor b, bool didPremulConversion) { if (!didPremulConversion) { return a == b; } @@ -195,13 +194,15 @@ // At some point this will be unsupported, as we won't allow accessBitmap() to magically call // readPixels for the client. SkBitmap secretDevBitmap; - canvas->readPixels(SkIRect::MakeWH(canvasInfo.width(), canvasInfo.height()), &secretDevBitmap); + secretDevBitmap.allocN32Pixels(canvasInfo.width(), canvasInfo.height()); + if (!canvas->readPixels(secretDevBitmap, 0, 0)) { + return false; + } - SkAutoLockPixels alp(secretDevBitmap); canvasRowBytes = secretDevBitmap.rowBytes(); canvasPixels = static_cast<const uint32_t*>(secretDevBitmap.getPixels()); - if (NULL == canvasPixels) { + if (nullptr == canvasPixels) { return false; } @@ -220,21 +221,21 @@ if (writeRect.contains(cx, cy)) { int bx = cx - writeX; int by = cy - writeY; - uint32_t bmpColor8888 = getBitmapColor(bx, by, bitmap.width(), + uint32_t bmpColor8888 = get_bitmap_color(bx, by, bitmap.width(), bmInfo.colorType(), bmInfo.alphaType()); bool mul = (kUnpremul_SkAlphaType == bmInfo.alphaType()); SkPMColor bmpPMColor = convert_to_PMColor(bmInfo.colorType(), bmInfo.alphaType(), bmpColor8888); - bool check = checkPixel(bmpPMColor, canvasPixel, mul); - REPORTER_ASSERT(reporter, check); - if (!check) { + if (!check_pixel(bmpPMColor, canvasPixel, mul)) { + ERRORF(reporter, "Expected canvas pixel at %d, %d to be 0x%08x, got 0x%08x. " + "Write performed premul: %d", cx, cy, bmpPMColor, canvasPixel, mul); return false; } } else { - bool check; - SkPMColor testColor = getCanvasColor(cx, cy); - REPORTER_ASSERT(reporter, check = (canvasPixel == testColor)); - if (!check) { + SkPMColor testColor = get_canvas_color(cx, cy); + if (canvasPixel != testColor) { + ERRORF(reporter, "Canvas pixel outside write rect at %d, %d changed." + " Should be 0x%08x, got 0x%08x. ", cx, cy, testColor, canvasPixel); return false; } } @@ -255,40 +256,18 @@ return true; } -enum DevType { - kRaster_DevType, -#if SK_SUPPORT_GPU - kGpu_BottomLeft_DevType, - kGpu_TopLeft_DevType, -#endif -}; - -struct CanvasConfig { - DevType fDevType; - bool fTightRowBytes; -}; - -static const CanvasConfig gCanvasConfigs[] = { - {kRaster_DevType, true}, - {kRaster_DevType, false}, -#if SK_SUPPORT_GPU - {kGpu_BottomLeft_DevType, true}, // row bytes has no meaning on gpu devices - {kGpu_TopLeft_DevType, true}, // row bytes has no meaning on gpu devices -#endif -}; - #include "SkMallocPixelRef.h" // This is a tricky pattern, because we have to setConfig+rowBytes AND specify // a custom pixelRef (which also has to specify its rowBytes), so we have to be // sure that the two rowBytes match (and the infos match). // -static bool allocRowBytes(SkBitmap* bm, const SkImageInfo& info, size_t rowBytes) { +static bool alloc_row_bytes(SkBitmap* bm, const SkImageInfo& info, size_t rowBytes) { if (!bm->setInfo(info, rowBytes)) { return false; } - SkPixelRef* pr = SkMallocPixelRef::NewAllocate(info, rowBytes, NULL); - bm->setPixelRef(pr)->unref(); + sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(info, rowBytes); + bm->setPixelRef(std::move(pr), 0, 0); return true; } @@ -296,47 +275,15 @@ sk_free(pixels); } -static SkSurface* create_surface(const CanvasConfig& c, GrContext* grCtx) { - SkImageInfo info = SkImageInfo::MakeN32Premul(DEV_W, DEV_H); - switch (c.fDevType) { - case kRaster_DevType: { - const size_t rowBytes = c.fTightRowBytes ? info.minRowBytes() : 4 * DEV_W + 100; - const size_t size = info.getSafeSize(rowBytes); - void* pixels = sk_malloc_throw(size); - // if rowBytes isn't tight then set the padding to a known value - if (!c.fTightRowBytes) { - memset(pixels, DEV_PAD, size); - } - return SkSurface::NewRasterDirectReleaseProc(info, pixels, rowBytes, free_pixels, NULL); - } -#if SK_SUPPORT_GPU - case kGpu_BottomLeft_DevType: - case kGpu_TopLeft_DevType: - GrTextureDesc desc; - desc.fFlags = kRenderTarget_GrTextureFlagBit; - desc.fWidth = DEV_W; - desc.fHeight = DEV_H; - desc.fConfig = kSkia8888_GrPixelConfig; - desc.fOrigin = kGpu_TopLeft_DevType == c.fDevType ? - kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin; - GrAutoScratchTexture ast(grCtx, desc, GrContext::kExact_ScratchTexMatch); - SkAutoTUnref<GrTexture> tex(ast.detach()); - return SkSurface::NewRenderTargetDirect(tex->asRenderTarget()); -#endif - } - return NULL; -} - static bool setup_bitmap(SkBitmap* bm, SkColorType ct, SkAlphaType at, int w, int h, int tightRB) { size_t rowBytes = tightRB ? 0 : 4 * w + 60; SkImageInfo info = SkImageInfo::Make(w, h, ct, at); - if (!allocRowBytes(bm, info, rowBytes)) { + if (!alloc_row_bytes(bm, info, rowBytes)) { return false; } - SkAutoLockPixels alp(*bm); for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { - *bm->getAddr32(x, y) = getBitmapColor(x, y, w, ct, at); + *bm->getAddr32(x, y) = get_bitmap_color(x, y, w, ct, at); } } return true; @@ -348,20 +295,16 @@ canvas->writePixels(info, &pixel, sizeof(SkPMColor), 0, 0); } -static void test_surface_genid(skiatest::Reporter* reporter) { +DEF_TEST(WritePixelsSurfaceGenID, reporter) { const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100); - SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster(info)); + auto surface(SkSurface::MakeRaster(info)); uint32_t genID1 = surface->generationID(); call_writepixels(surface->getCanvas()); uint32_t genID2 = surface->generationID(); REPORTER_ASSERT(reporter, genID1 != genID2); } -DEF_GPUTEST(WritePixels, reporter, factory) { - test_surface_genid(reporter); - - SkCanvas canvas; - +static void test_write_pixels(skiatest::Reporter* reporter, SkSurface* surface) { const SkIRect testRects[] = { // entire thing DEV_RECT, @@ -409,72 +352,101 @@ SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10), }; - for (size_t i = 0; i < SK_ARRAY_COUNT(gCanvasConfigs); ++i) { - int glCtxTypeCnt = 1; -#if SK_SUPPORT_GPU - bool isGPUDevice = kGpu_TopLeft_DevType == gCanvasConfigs[i].fDevType || - kGpu_BottomLeft_DevType == gCanvasConfigs[i].fDevType; - if (isGPUDevice) { - glCtxTypeCnt = GrContextFactory::kGLContextTypeCnt; - } -#endif - for (int glCtxType = 0; glCtxType < glCtxTypeCnt; ++glCtxType) { - GrContext* context = NULL; -#if SK_SUPPORT_GPU - if (isGPUDevice) { - GrContextFactory::GLContextType type = - static_cast<GrContextFactory::GLContextType>(glCtxType); - if (!GrContextFactory::IsRenderingGLContext(type)) { - continue; - } - context = factory->get(type); - if (NULL == context) { - continue; - } - } -#endif + SkCanvas& canvas = *surface->getCanvas(); - SkAutoTUnref<SkSurface> surface(create_surface(gCanvasConfigs[i], context)); - SkCanvas& canvas = *surface->getCanvas(); + static const struct { + SkColorType fColorType; + SkAlphaType fAlphaType; + } gSrcConfigs[] = { + { kRGBA_8888_SkColorType, kPremul_SkAlphaType }, + { kRGBA_8888_SkColorType, kUnpremul_SkAlphaType }, + { kBGRA_8888_SkColorType, kPremul_SkAlphaType }, + { kBGRA_8888_SkColorType, kUnpremul_SkAlphaType }, + }; + for (size_t r = 0; r < SK_ARRAY_COUNT(testRects); ++r) { + const SkIRect& rect = testRects[r]; + for (int tightBmp = 0; tightBmp < 2; ++tightBmp) { + for (size_t c = 0; c < SK_ARRAY_COUNT(gSrcConfigs); ++c) { + const SkColorType ct = gSrcConfigs[c].fColorType; + const SkAlphaType at = gSrcConfigs[c].fAlphaType; - static const struct { - SkColorType fColorType; - SkAlphaType fAlphaType; - } gSrcConfigs[] = { - { kRGBA_8888_SkColorType, kPremul_SkAlphaType }, - { kRGBA_8888_SkColorType, kUnpremul_SkAlphaType }, - { kBGRA_8888_SkColorType, kPremul_SkAlphaType }, - { kBGRA_8888_SkColorType, kUnpremul_SkAlphaType }, - }; - for (size_t r = 0; r < SK_ARRAY_COUNT(testRects); ++r) { - const SkIRect& rect = testRects[r]; - for (int tightBmp = 0; tightBmp < 2; ++tightBmp) { - for (size_t c = 0; c < SK_ARRAY_COUNT(gSrcConfigs); ++c) { - const SkColorType ct = gSrcConfigs[c].fColorType; - const SkAlphaType at = gSrcConfigs[c].fAlphaType; + fill_canvas(&canvas); + SkBitmap bmp; + REPORTER_ASSERT(reporter, setup_bitmap(&bmp, ct, at, rect.width(), + rect.height(), SkToBool(tightBmp))); + uint32_t idBefore = surface->generationID(); - fillCanvas(&canvas); - SkBitmap bmp; - REPORTER_ASSERT(reporter, setup_bitmap(&bmp, ct, at, rect.width(), - rect.height(), SkToBool(tightBmp))); - uint32_t idBefore = surface->generationID(); + // sk_tool_utils::write_pixels(&canvas, bmp, rect.fLeft, rect.fTop, ct, at); + canvas.writePixels(bmp, rect.fLeft, rect.fTop); - // sk_tool_utils::write_pixels(&canvas, bmp, rect.fLeft, rect.fTop, ct, at); - canvas.writePixels(bmp, rect.fLeft, rect.fTop); + uint32_t idAfter = surface->generationID(); + REPORTER_ASSERT(reporter, check_write(reporter, &canvas, bmp, + rect.fLeft, rect.fTop)); - uint32_t idAfter = surface->generationID(); - REPORTER_ASSERT(reporter, check_write(reporter, &canvas, bmp, - rect.fLeft, rect.fTop)); - - // we should change the genID iff pixels were actually written. - SkIRect canvasRect = SkIRect::MakeSize(canvas.getDeviceSize()); - SkIRect writeRect = SkIRect::MakeXYWH(rect.fLeft, rect.fTop, - bmp.width(), bmp.height()); - bool intersects = SkIRect::Intersects(canvasRect, writeRect) ; - REPORTER_ASSERT(reporter, intersects == (idBefore != idAfter)); - } - } + // we should change the genID iff pixels were actually written. + SkIRect canvasRect = SkIRect::MakeSize(canvas.getBaseLayerSize()); + SkIRect writeRect = SkIRect::MakeXYWH(rect.fLeft, rect.fTop, + bmp.width(), bmp.height()); + bool intersects = SkIRect::Intersects(canvasRect, writeRect) ; + REPORTER_ASSERT(reporter, intersects == (idBefore != idAfter)); } } } } +DEF_TEST(WritePixels, reporter) { + const SkImageInfo info = SkImageInfo::MakeN32Premul(DEV_W, DEV_H); + for (auto& tightRowBytes : { true, false }) { + const size_t rowBytes = tightRowBytes ? info.minRowBytes() : 4 * DEV_W + 100; + const size_t size = info.getSafeSize(rowBytes); + void* pixels = sk_malloc_throw(size); + // if rowBytes isn't tight then set the padding to a known value + if (!tightRowBytes) { + memset(pixels, DEV_PAD, size); + } + auto surface(SkSurface::MakeRasterDirectReleaseProc(info, pixels, rowBytes, + free_pixels, nullptr)); + test_write_pixels(reporter, surface.get()); + } +} +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixels_Gpu, reporter, ctxInfo) { + const SkImageInfo ii = SkImageInfo::MakeN32Premul(DEV_W, DEV_H); + + for (auto& origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) { + for (int sampleCnt : {0, 4}) { + sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctxInfo.grContext(), + SkBudgeted::kNo, ii, sampleCnt, + origin, nullptr)); + if (!surface && sampleCnt > 0) { + // Some platforms don't support MSAA + continue; + } + test_write_pixels(reporter, surface.get()); + } + } +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixelsNonTexture_Gpu, reporter, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + + for (auto& origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) { + for (int sampleCnt : {0, 4}) { + auto handle = context->getGpu()->createTestingOnlyBackendTexture( + nullptr, DEV_W, DEV_H, kSkia8888_GrPixelConfig, true); + GrBackendTexture backendTexture = GrTest::CreateBackendTexture( + ctxInfo.backend(), DEV_W, DEV_H, kSkia8888_GrPixelConfig, handle); + sk_sp<SkSurface> surface(SkSurface::MakeFromBackendTextureAsRenderTarget( + context, backendTexture, origin, sampleCnt, nullptr, nullptr)); + if (!surface) { + context->getGpu()->deleteTestingOnlyBackendTexture(handle); + continue; + } + + test_write_pixels(reporter, surface.get()); + + surface.reset(); + context->getGpu()->deleteTestingOnlyBackendTexture(handle); + } + } +} +#endif
diff --git a/src/third_party/skia/tests/Writer32Test.cpp b/src/third_party/skia/tests/Writer32Test.cpp index 56cea2a..6cb79f8 100644 --- a/src/third_party/skia/tests/Writer32Test.cpp +++ b/src/third_party/skia/tests/Writer32Test.cpp
@@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include "SkAutoMalloc.h" #include "SkRandom.h" #include "SkReader32.h" #include "SkWriter32.h" @@ -31,8 +32,8 @@ uint8_t storage[8]; SkWriter32 writer(storage, sizeof(storage)); - // Can we write NULL? - writer.writeString(NULL); + // Can we write nullptr? + writer.writeString(nullptr); const int32_t expected[] = { 0x0, 0x0 }; check_contents(reporter, writer, expected, sizeof(expected)); } @@ -144,16 +145,13 @@ static void testWritePad(skiatest::Reporter* reporter, SkWriter32* writer) { // Create some random data to write. - const size_t dataSize = 10<<2; - SkASSERT(SkIsAlign4(dataSize)); + const size_t dataSize = 10; - SkAutoMalloc originalData(dataSize); + SkAutoTMalloc<uint32_t> originalData(dataSize); { SkRandom rand(0); - uint32_t* ptr = static_cast<uint32_t*>(originalData.get()); - uint32_t* stop = ptr + (dataSize>>2); - while (ptr < stop) { - *ptr++ = rand.nextU(); + for (size_t i = 0; i < dataSize; i++) { + originalData[(int) i] = rand.nextU(); } // Write the random data to the writer at different lengths for @@ -163,7 +161,7 @@ } } - uint32_t totalBytes = writer->bytesWritten(); + size_t totalBytes = writer->bytesWritten(); SkAutoMalloc readStorage(totalBytes); writer->flatten(readStorage.get()); @@ -235,19 +233,6 @@ testOverwriteT(reporter, &writer); } -DEF_TEST(Writer32_contiguous, reporter) { - uint32_t storage[256]; - SkWriter32 writer; - writer.reset(storage, sizeof(storage)); - // This write is small enough to fit in storage, so it's contiguous. - test1(reporter, &writer); - REPORTER_ASSERT(reporter, writer.contiguousArray() != NULL); - - // Everything other aspect of contiguous/non-contiguous is an - // implementation detail, not part of the public contract for - // SkWriter32, and so not tested here. -} - DEF_TEST(Writer32_small, reporter) { SkSWriter32<8 * sizeof(intptr_t)> writer; test1(reporter, &writer); @@ -281,34 +266,44 @@ test_rewind(reporter); } -DEF_TEST(Writer32_snapshot, reporter) { - int32_t array[] = { 1, 2, 4, 11 }; - SkSWriter32<sizeof(array) + 4> writer; - writer.write(array, sizeof(array)); - check_contents(reporter, writer, array, sizeof(array)); - const void* beforeData = writer.contiguousArray(); - SkAutoDataUnref snapshot(writer.snapshotAsData()); - // check the snapshot forced a copy of the static data - REPORTER_ASSERT(reporter, snapshot->data() != beforeData); - REPORTER_ASSERT(reporter, snapshot->size() == writer.bytesWritten()); -} +DEF_TEST(Writer32_data, reporter) { + const char* str = "0123456789"; + sk_sp<SkData> data0(SkData::MakeWithCString(str)); + sk_sp<SkData> data1(SkData::MakeEmpty()); -DEF_TEST(Writer32_snapshot_dynamic, reporter) { - int32_t array[] = { 1, 2, 4, 11 }; - SkWriter32 writer; - writer.write(array, sizeof(array)); - check_contents(reporter, writer, array, sizeof(array)); - // force a capacity increase so we can test COW behaviour - writer.write(array, sizeof(array)); - writer.rewindToOffset(sizeof(array)); - const void* beforeData = writer.contiguousArray(); - SkAutoDataUnref snapshot(writer.snapshotAsData()); - // check the snapshot still points to the same data as the writer - REPORTER_ASSERT(reporter, writer.contiguousArray() == beforeData); - REPORTER_ASSERT(reporter, snapshot->data() == beforeData); - REPORTER_ASSERT(reporter, snapshot->size() == writer.bytesWritten()); - // write more data that would fit in the buffer - writer.write(array, sizeof(array)); - // test it triggered COW anyway - REPORTER_ASSERT(reporter, writer.contiguousArray() != beforeData); + const size_t sizes[] = { + SkWriter32::WriteDataSize(nullptr), + SkWriter32::WriteDataSize(data0.get()), + SkWriter32::WriteDataSize(data1.get()), + }; + + SkSWriter32<1000> writer; + size_t sizeWritten = 0; + + writer.writeData(nullptr); + sizeWritten += sizes[0]; + REPORTER_ASSERT(reporter, sizeWritten == writer.bytesWritten()); + + writer.writeData(data0.get()); + sizeWritten += sizes[1]; + REPORTER_ASSERT(reporter, sizeWritten == writer.bytesWritten()); + + writer.writeData(data1.get()); + sizeWritten += sizes[2]; + REPORTER_ASSERT(reporter, sizeWritten == writer.bytesWritten()); + + auto result(writer.snapshotAsData()); + + SkReader32 reader(result->data(), result->size()); + auto d0(reader.readData()), + d1(reader.readData()), + d2(reader.readData()); + + REPORTER_ASSERT(reporter, 0 == d0->size()); + REPORTER_ASSERT(reporter, strlen(str)+1 == d1->size()); + REPORTER_ASSERT(reporter, !memcmp(str, d1->data(), strlen(str)+1)); + REPORTER_ASSERT(reporter, 0 == d2->size()); + + REPORTER_ASSERT(reporter, reader.offset() == sizeWritten); + REPORTER_ASSERT(reporter, reader.eof()); }
diff --git a/src/third_party/skia/tests/XfermodeTest.cpp b/src/third_party/skia/tests/XfermodeTest.cpp deleted file mode 100644 index 34f5233..0000000 --- a/src/third_party/skia/tests/XfermodeTest.cpp +++ /dev/null
@@ -1,57 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkColor.h" -#include "SkXfermode.h" -#include "Test.h" - -#define ILLEGAL_MODE ((SkXfermode::Mode)-1) - -static void test_asMode(skiatest::Reporter* reporter) { - for (int mode = 0; mode <= SkXfermode::kLastMode; mode++) { - SkXfermode* xfer = SkXfermode::Create((SkXfermode::Mode) mode); - - SkXfermode::Mode reportedMode = ILLEGAL_MODE; - REPORTER_ASSERT(reporter, reportedMode != mode); - - // test IsMode - REPORTER_ASSERT(reporter, SkXfermode::AsMode(xfer, &reportedMode)); - REPORTER_ASSERT(reporter, reportedMode == mode); - - // repeat that test, but with asMode instead - if (xfer) { - reportedMode = (SkXfermode::Mode) -1; - REPORTER_ASSERT(reporter, xfer->asMode(&reportedMode)); - REPORTER_ASSERT(reporter, reportedMode == mode); - xfer->unref(); - } else { - REPORTER_ASSERT(reporter, SkXfermode::kSrcOver_Mode == mode); - } - } -} - -static void test_IsMode(skiatest::Reporter* reporter) { - REPORTER_ASSERT(reporter, SkXfermode::IsMode(NULL, - SkXfermode::kSrcOver_Mode)); - - for (int i = 0; i <= SkXfermode::kLastMode; ++i) { - SkXfermode::Mode mode = (SkXfermode::Mode)i; - - SkXfermode* xfer = SkXfermode::Create(mode); - REPORTER_ASSERT(reporter, SkXfermode::IsMode(xfer, mode)); - SkSafeUnref(xfer); - - if (SkXfermode::kSrcOver_Mode != mode) { - REPORTER_ASSERT(reporter, !SkXfermode::IsMode(NULL, mode)); - } - } -} - -DEF_TEST(Xfermode, reporter) { - test_asMode(reporter); - test_IsMode(reporter); -}
diff --git a/src/third_party/skia/tests/YUVCacheTest.cpp b/src/third_party/skia/tests/YUVCacheTest.cpp new file mode 100644 index 0000000..b34cf06 --- /dev/null +++ b/src/third_party/skia/tests/YUVCacheTest.cpp
@@ -0,0 +1,77 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCachedData.h" +#include "SkYUVPlanesCache.h" +#include "SkResourceCache.h" +#include "Test.h" + +enum LockedState { + kUnlocked, + kLocked, +}; + +enum CachedState { + kNotInCache, + kInCache, +}; + +static void check_data(skiatest::Reporter* reporter, SkCachedData* data, + int refcnt, CachedState cacheState, LockedState lockedState) { + REPORTER_ASSERT(reporter, data->testing_only_getRefCnt() == refcnt); + REPORTER_ASSERT(reporter, data->testing_only_isInCache() == (kInCache == cacheState)); + bool isLocked = (data->data() != nullptr); + REPORTER_ASSERT(reporter, isLocked == (lockedState == kLocked)); +} + +DEF_TEST(YUVPlanesCache, reporter) { + SkResourceCache cache(1024); + + SkYUVPlanesCache::Info yuvInfo; + for (int i = 0; i < 3; i++) { + yuvInfo.fSizeInfo.fSizes[i].fWidth = 20 * i; + yuvInfo.fSizeInfo.fSizes[i].fHeight = 10 * i; + yuvInfo.fSizeInfo.fWidthBytes[i] = 80 * i; + } + yuvInfo.fColorSpace = kRec601_SkYUVColorSpace; + + const uint32_t genID = 12345678; + + SkCachedData* data = SkYUVPlanesCache::FindAndRef(genID, &yuvInfo, &cache); + REPORTER_ASSERT(reporter, nullptr == data); + + size_t size = 256; + data = cache.newCachedData(size); + memset(data->writable_data(), 0xff, size); + + SkYUVPlanesCache::Add(genID, data, &yuvInfo, &cache); + check_data(reporter, data, 2, kInCache, kLocked); + + data->unref(); + check_data(reporter, data, 1, kInCache, kUnlocked); + + SkYUVPlanesCache::Info yuvInfoRead; + data = SkYUVPlanesCache::FindAndRef(genID, &yuvInfoRead, &cache); + + REPORTER_ASSERT(reporter, data); + REPORTER_ASSERT(reporter, data->size() == size); + for (int i = 0; i < 3; ++i) { + REPORTER_ASSERT(reporter, yuvInfo.fSizeInfo.fSizes[i].fWidth == + yuvInfoRead.fSizeInfo.fSizes[i].fWidth); + REPORTER_ASSERT(reporter, yuvInfo.fSizeInfo.fSizes[i].fHeight == + yuvInfoRead.fSizeInfo.fSizes[i].fHeight); + REPORTER_ASSERT(reporter, yuvInfo.fSizeInfo.fWidthBytes[i] == + yuvInfoRead.fSizeInfo.fWidthBytes[i]); + } + REPORTER_ASSERT(reporter, yuvInfo.fColorSpace == yuvInfoRead.fColorSpace); + + check_data(reporter, data, 2, kInCache, kLocked); + + cache.purgeAll(); + check_data(reporter, data, 1, kNotInCache, kLocked); + data->unref(); +}
diff --git a/src/third_party/skia/tests/YUVTest.cpp b/src/third_party/skia/tests/YUVTest.cpp new file mode 100644 index 0000000..0c31c09 --- /dev/null +++ b/src/third_party/skia/tests/YUVTest.cpp
@@ -0,0 +1,126 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Resources.h" +#include "SkAutoMalloc.h" +#include "SkCodec.h" +#include "SkStream.h" +#include "SkTemplates.h" +#include "SkYUVSizeInfo.h" +#include "Test.h" + +static void codec_yuv(skiatest::Reporter* reporter, + const char path[], + SkISize expectedSizes[3]) { + std::unique_ptr<SkStream> stream(GetResourceAsStream(path)); + if (!stream) { + return; + } + std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release())); + REPORTER_ASSERT(reporter, codec); + if (!codec) { + return; + } + + // Test queryYUV8() + SkYUVSizeInfo info; + bool success = codec->queryYUV8(nullptr, nullptr); + REPORTER_ASSERT(reporter, !success); + success = codec->queryYUV8(&info, nullptr); + REPORTER_ASSERT(reporter, (expectedSizes == nullptr) == !success); + if (!success) { + return; + } + REPORTER_ASSERT(reporter, + 0 == memcmp((const void*) &info, (const void*) expectedSizes, 3 * sizeof(SkISize))); + REPORTER_ASSERT(reporter, info.fWidthBytes[SkYUVSizeInfo::kY] == + (uint32_t) SkAlign8(info.fSizes[SkYUVSizeInfo::kY].width())); + REPORTER_ASSERT(reporter, info.fWidthBytes[SkYUVSizeInfo::kU] == + (uint32_t) SkAlign8(info.fSizes[SkYUVSizeInfo::kU].width())); + REPORTER_ASSERT(reporter, info.fWidthBytes[SkYUVSizeInfo::kV] == + (uint32_t) SkAlign8(info.fSizes[SkYUVSizeInfo::kV].width())); + SkYUVColorSpace colorSpace; + success = codec->queryYUV8(&info, &colorSpace); + REPORTER_ASSERT(reporter, + 0 == memcmp((const void*) &info, (const void*) expectedSizes, 3 * sizeof(SkISize))); + REPORTER_ASSERT(reporter, info.fWidthBytes[SkYUVSizeInfo::kY] == + (uint32_t) SkAlign8(info.fSizes[SkYUVSizeInfo::kY].width())); + REPORTER_ASSERT(reporter, info.fWidthBytes[SkYUVSizeInfo::kU] == + (uint32_t) SkAlign8(info.fSizes[SkYUVSizeInfo::kU].width())); + REPORTER_ASSERT(reporter, info.fWidthBytes[SkYUVSizeInfo::kV] == + (uint32_t) SkAlign8(info.fSizes[SkYUVSizeInfo::kV].width())); + REPORTER_ASSERT(reporter, kJPEG_SkYUVColorSpace == colorSpace); + + // Allocate the memory for the YUV decode + size_t totalBytes = + info.fWidthBytes[SkYUVSizeInfo::kY] * info.fSizes[SkYUVSizeInfo::kY].height() + + info.fWidthBytes[SkYUVSizeInfo::kU] * info.fSizes[SkYUVSizeInfo::kU].height() + + info.fWidthBytes[SkYUVSizeInfo::kV] * info.fSizes[SkYUVSizeInfo::kV].height(); + SkAutoMalloc storage(totalBytes); + void* planes[3]; + planes[0] = storage.get(); + planes[1] = SkTAddOffset<void>(planes[0], + info.fWidthBytes[SkYUVSizeInfo::kY] * info.fSizes[SkYUVSizeInfo::kY].height()); + planes[2] = SkTAddOffset<void>(planes[1], + info.fWidthBytes[SkYUVSizeInfo::kU] * info.fSizes[SkYUVSizeInfo::kU].height()); + + // Test getYUV8Planes() + REPORTER_ASSERT(reporter, SkCodec::kInvalidInput == + codec->getYUV8Planes(info, nullptr)); + REPORTER_ASSERT(reporter, SkCodec::kSuccess == + codec->getYUV8Planes(info, planes)); +} + +DEF_TEST(Jpeg_YUV_Codec, r) { + SkISize sizes[3]; + + sizes[0].set(128, 128); + sizes[1].set(64, 64); + sizes[2].set(64, 64); + codec_yuv(r, "color_wheel.jpg", sizes); + + // H2V2 + sizes[0].set(512, 512); + sizes[1].set(256, 256); + sizes[2].set(256, 256); + codec_yuv(r, "mandrill_512_q075.jpg", sizes); + + // H1V1 + sizes[1].set(512, 512); + sizes[2].set(512, 512); + codec_yuv(r, "mandrill_h1v1.jpg", sizes); + + // H2V1 + sizes[1].set(256, 512); + sizes[2].set(256, 512); + codec_yuv(r, "mandrill_h2v1.jpg", sizes); + + // Non-power of two dimensions + sizes[0].set(439, 154); + sizes[1].set(220, 77); + sizes[2].set(220, 77); + codec_yuv(r, "cropped_mandrill.jpg", sizes); + + sizes[0].set(8, 8); + sizes[1].set(4, 4); + sizes[2].set(4, 4); + codec_yuv(r, "randPixels.jpg", sizes); + + // Progressive images + sizes[0].set(512, 512); + sizes[1].set(512, 512); + sizes[2].set(512, 512); + codec_yuv(r, "brickwork-texture.jpg", sizes); + codec_yuv(r, "brickwork_normal-map.jpg", sizes); + + // A CMYK encoded image should fail. + codec_yuv(r, "CMYK.jpg", nullptr); + // A grayscale encoded image should fail. + codec_yuv(r, "grayscale.jpg", nullptr); + // A PNG should fail. + codec_yuv(r, "arrow.png", nullptr); +}
diff --git a/src/third_party/skia/tests/image-bitmap.cpp b/src/third_party/skia/tests/image-bitmap.cpp new file mode 100644 index 0000000..59ab3f7 --- /dev/null +++ b/src/third_party/skia/tests/image-bitmap.cpp
@@ -0,0 +1,34 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +#include "SkBitmap.h" +#include "SkImage.h" + +// https://bug.skia.org/5096 +// Test that when we make an image from a subset of a bitmap, that it +// has a diff (ID, dimensions) from an image made from the entire +// bitmap or a different subset of the image. +DEF_TEST(ImageBitmapIdentity, r) { + SkBitmap bm, a, b; + bm.allocN32Pixels(32, 64); + bm.eraseColor(SK_ColorBLACK); + bm.setImmutable(); + (void)bm.extractSubset(&a, SkIRect::MakeXYWH(0, 0, 32, 32)); + (void)bm.extractSubset(&b, SkIRect::MakeXYWH(0, 32, 32, 32)); + REPORTER_ASSERT(r, a.getGenerationID() == b.getGenerationID()); + auto img = SkImage::MakeFromBitmap(bm); + auto imgA = SkImage::MakeFromBitmap(a); + auto imgB = SkImage::MakeFromBitmap(b); + REPORTER_ASSERT(r, img->uniqueID() == bm.getGenerationID()); + REPORTER_ASSERT(r, img->uniqueID() != imgA->uniqueID()); + REPORTER_ASSERT(r, img->uniqueID() != imgB->uniqueID()); + REPORTER_ASSERT(r, imgA->uniqueID() != imgB->uniqueID()); + REPORTER_ASSERT(r, imgA->uniqueID() != a.getGenerationID()); + REPORTER_ASSERT(r, imgB->uniqueID() != b.getGenerationID()); +}
diff --git a/src/third_party/skia/tests/skbug5221.cpp b/src/third_party/skia/tests/skbug5221.cpp new file mode 100644 index 0000000..356a620 --- /dev/null +++ b/src/third_party/skia/tests/skbug5221.cpp
@@ -0,0 +1,33 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" +#include "SkCanvas.h" +#include "SkSurface.h" + +// This passes by not crashing. +static void test(SkCanvas* canvas) { + SkPaint paint; + paint.setAntiAlias(true); + canvas->scale(63, 0); + static const char kTxt[] = "A"; + canvas->drawText(kTxt, SK_ARRAY_COUNT(kTxt), 50, 50, paint); +} + +DEF_TEST(skbug5221, r) { + sk_sp<SkSurface> surface(SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(256, 256))); + test(surface->getCanvas()); +} + +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_ALL_CONTEXTS(skbug5221_GPU, r, contextInfo) { + sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget( + contextInfo.grContext(), SkBudgeted::kYes, + SkImageInfo::Make(256, 256, kRGBA_8888_SkColorType, kPremul_SkAlphaType))); + test(surface->getCanvas()); +} +#endif
diff --git a/src/third_party/skia/tests/skbug6389.cpp b/src/third_party/skia/tests/skbug6389.cpp new file mode 100644 index 0000000..f525158 --- /dev/null +++ b/src/third_party/skia/tests/skbug6389.cpp
@@ -0,0 +1,23 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Resources.h" +#include "SkBlurMaskFilter.h" +#include "SkCanvas.h" +#include "SkImageSource.h" +#include "SkSurface.h" +#include "Test.h" + +DEF_TEST(skbug_6389, r) { + auto s = SkSurface::MakeRasterN32Premul(100, 100); + SkPaint p; + p.setMaskFilter(SkBlurMaskFilter::Make(SkBlurStyle::kNormal_SkBlurStyle, 5, + SkBlurMaskFilter::kHighQuality_BlurFlag)); + p.setImageFilter(SkImageSource::Make(GetResourceAsImage("mandrill_512.png"), {0, 0, 0, 0}, + {0, 0, 0, 0}, (SkFilterQuality)0)); + s->getCanvas()->drawPaint(p); +}
diff --git a/src/third_party/skia/tests/skbug6653.cpp b/src/third_party/skia/tests/skbug6653.cpp new file mode 100644 index 0000000..d200e14 --- /dev/null +++ b/src/third_party/skia/tests/skbug6653.cpp
@@ -0,0 +1,100 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTypes.h" + +#include "SkCanvas.h" +#include "SkSurface.h" + +#if SK_SUPPORT_GPU + +#include "GrContext.h" +#include "GrTest.h" +#include "Test.h" + +static SkBitmap read_pixels(sk_sp<SkSurface> surface) { + SkBitmap bmp; + bmp.allocN32Pixels(surface->width(), surface->height()); + if (!surface->getCanvas()->readPixels(bmp, 0, 0)) { + SkDebugf("readPixels failed\n"); + } + return bmp; +} + +static sk_sp<SkSurface> make_surface(GrContext* context) { + SkImageInfo info = SkImageInfo::Make(50, 50, kRGBA_8888_SkColorType, kPremul_SkAlphaType); + return SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 4, + kBottomLeft_GrSurfaceOrigin, nullptr); +} + +// Tests that readPixels returns up-to-date results. Demonstrates a bug on Galaxy S6 +// (Mali T760), in MSAA mode. +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(skbug6653, reporter, ctxInfo) { + GrContext* ctx = ctxInfo.grContext(); + SkRect rect = SkRect::MakeWH(50, 50); + + SkPaint paint; + paint.setColor(SK_ColorWHITE); + paint.setStrokeWidth(5); + paint.setStyle(SkPaint::kStroke_Style); + + // The one device that fails this test (Galaxy S6) does so in a flaky fashion. Trying many + // times makes it more likely to fail. Also, interacting with the phone (eg swiping between + // different home screens) while the test is running makes it fail close to 100%. + static const int kNumIterations = 50; + + for (int i = 0; i < kNumIterations; ++i) { + auto s0 = make_surface(ctx); + if (!s0) { + // MSAA may not be supported + return; + } + + auto s1 = make_surface(ctx); + s1->getCanvas()->clear(SK_ColorBLACK); + s1->getCanvas()->drawOval(rect, paint); + SkBitmap b1 = read_pixels(s1); + s1 = nullptr; + + // The bug requires that all three of the following surfaces are cleared to the same color + auto s2 = make_surface(ctx); + s2->getCanvas()->clear(SK_ColorBLUE); + SkBitmap b2 = read_pixels(s2); + s2 = nullptr; + + auto s3 = make_surface(ctx); + s3->getCanvas()->clear(SK_ColorBLUE); + SkBitmap b3 = read_pixels(s3); + s0->getCanvas()->drawBitmap(b3, 0, 0); + s3 = nullptr; + + auto s4 = make_surface(ctx); + s4->getCanvas()->clear(SK_ColorBLUE); + s4->getCanvas()->drawOval(rect, paint); + + // When this fails, b4 will "succeed", but return an empty bitmap (containing just the + // clear color). Regardless, b5 will contain the oval that was just drawn, so diffing the + // two bitmaps tests for the failure case. + SkBitmap b4 = read_pixels(s4); + SkBitmap b5 = read_pixels(s4); + + bool match = true; + for (int y = 0; y < b4.height() && match; ++y) { + for (int x = 0; x < b4.width() && match; ++x) { + uint32_t pixelA = *b4.getAddr32(x, y); + uint32_t pixelB = *b5.getAddr32(x, y); + if (pixelA != pixelB) { + match = false; + } + } + } + + REPORTER_ASSERT(reporter, match); + } +} + +#endif
diff --git a/src/third_party/skia/tests/skia_test.cpp b/src/third_party/skia/tests/skia_test.cpp index 0058215..6b7df71 100644 --- a/src/third_party/skia/tests/skia_test.cpp +++ b/src/third_party/skia/tests/skia_test.cpp
@@ -8,9 +8,11 @@ #include "CrashHandler.h" #include "OverwriteLine.h" #include "Resources.h" +#include "SkAtomics.h" #include "SkCommonFlags.h" #include "SkGraphics.h" #include "SkOSFile.h" +#include "SkPathOpsDebug.h" #include "SkTArray.h" #include "SkTaskGroup.h" #include "SkTemplates.h" @@ -23,82 +25,101 @@ #endif using namespace skiatest; +using namespace sk_gpu_test; +DEFINE_bool2(dumpOp, d, false, "dump the pathOps to a file to recover mid-crash."); DEFINE_bool2(extendedTest, x, false, "run extended tests for pathOps."); +DEFINE_bool2(runFail, f, false, "check for success on tests known to fail."); +DEFINE_bool2(verifyOp, y, false, "compare the pathOps result against a region."); + +#if DEBUG_COIN +DEFINE_bool2(coinTest, c, false, "detect unused coincidence algorithms."); +#endif // need to explicitly declare this, or we get some weird infinite loop llist template TestRegistry* TestRegistry::gHead; +void (*gVerboseFinalize)() = nullptr; -class Iter { +// The threads report back to this object when they are done. +class Status { public: - Iter() { this->reset(); } - void reset() { fReg = TestRegistry::Head(); } - - Test* next(Reporter* r) { - if (fReg) { - TestRegistry::Factory fact = fReg->factory(); - fReg = fReg->next(); - Test* test = fact(NULL); - test->setReporter(r); - return test; - } - return NULL; - } - -private: - const TestRegistry* fReg; -}; - -class DebugfReporter : public Reporter { -public: - explicit DebugfReporter(int total) : fDone(0), fTotal(total) {} - - virtual bool allowExtendedTest() const SK_OVERRIDE { return FLAGS_extendedTest; } - virtual bool verbose() const SK_OVERRIDE { return FLAGS_veryVerbose; } - -protected: - virtual void onReportFailed(const SkString& desc) SK_OVERRIDE { - SkDebugf("\nFAILED: %s", desc.c_str()); - } - - virtual void onEnd(Test* test) SK_OVERRIDE { + explicit Status(int total) + : fDone(0), fTestCount(0), fFailCount(0), fTotal(total) {} + // Threadsafe. + void endTest(const char* testName, + bool success, + SkMSec elapsed, + int testCount) { const int done = 1 + sk_atomic_inc(&fDone); - - if (!test->passed()) { - SkDebugf("\n---- %s FAILED", test->getName()); + for (int i = 0; i < testCount; ++i) { + sk_atomic_inc(&fTestCount); + } + if (!success) { + SkDebugf("\n---- %s FAILED", testName); } SkString prefix(kSkOverwriteLine); SkString time; if (FLAGS_verbose) { prefix.printf("\n"); - time.printf("%5dms ", test->elapsedMs()); + time.printf("%5dms ", elapsed); } - SkDebugf("%s[%3d/%3d] %s%s", prefix.c_str(), done, fTotal, time.c_str(), test->getName()); + SkDebugf("%s[%3d/%3d] %s%s", prefix.c_str(), done, fTotal, time.c_str(), + testName); } + void reportFailure() { sk_atomic_inc(&fFailCount); } + + int32_t testCount() { return fTestCount; } + int32_t failCount() { return fFailCount; } + private: int32_t fDone; // atomic + int32_t fTestCount; // atomic + int32_t fFailCount; // atomic const int fTotal; }; -// Deletes self when run. -class SkTestRunnable : public SkRunnable { +class SkTestRunnable { public: - // Takes ownership of test. - SkTestRunnable(Test* test, int32_t* failCount) : fTest(test), fFailCount(failCount) {} + SkTestRunnable(const Test& test, + Status* status, + GrContextFactory* grContextFactory = nullptr) + : fTest(test), fStatus(status), fGrContextFactory(grContextFactory) {} - virtual void run() { - fTest->run(); - if(!fTest->passed()) { - sk_atomic_inc(fFailCount); + void operator()() { + struct TestReporter : public skiatest::Reporter { + public: + TestReporter() : fStats(nullptr), fError(false), fTestCount(0) {} + void bumpTestCount() override { ++fTestCount; } + bool allowExtendedTest() const override { + return FLAGS_extendedTest; + } + bool verbose() const override { return FLAGS_veryVerbose; } + void reportFailed(const skiatest::Failure& failure) override { + SkDebugf("\nFAILED: %s", failure.toString().c_str()); + fError = true; + } + void* stats() const override { return fStats; } + void* fStats; + bool fError; + int fTestCount; + } reporter; + + const Timer timer; + fTest.proc(&reporter, fGrContextFactory); + SkMSec elapsed = timer.elapsedMsInt(); + if (reporter.fError) { + fStatus->reportFailure(); } - SkDELETE(this); + fStatus->endTest(fTest.name, !reporter.fError, elapsed, + reporter.fTestCount); } private: - SkAutoTDelete<Test> fTest; - int32_t* fFailCount; + Test fTest; + Status* fStatus; + GrContextFactory* fGrContextFactory; }; static bool should_run(const char* testName, bool isGPUTest) { @@ -114,17 +135,17 @@ return true; } -int test_main(); -int test_main() { +int main(int argc, char** argv) { + SkCommandLineFlags::Parse(argc, argv); +#if DEBUG_DUMP_VERIFY + SkPathOpsDebug::gDumpOp = FLAGS_dumpOp; + SkPathOpsDebug::gVerifyOp = FLAGS_verifyOp; +#endif + SkPathOpsDebug::gRunFail = FLAGS_runFail; + SkPathOpsDebug::gVeryVerbose = FLAGS_veryVerbose; SetupCrashHandler(); -#if SK_ENABLE_INST_COUNT - if (FLAGS_leaks) { - gPrintInstCount = true; - } -#endif - - SkGraphics::Init(); + SkAutoGraphics ag; { SkString header("Skia UnitTests:"); @@ -134,7 +155,7 @@ header.appendf(" %s", FLAGS_match[index]); } } - SkString tmpDir = Test::GetTmpDir(); + SkString tmpDir = skiatest::GetTmpDir(); if (!tmpDir.isEmpty()) { header.appendf(" --tmpDir %s", tmpDir.c_str()); } @@ -142,83 +163,108 @@ if (!resourcePath.isEmpty()) { header.appendf(" --resourcePath %s", resourcePath.c_str()); } +#if DEBUG_COIN + if (FLAGS_coinTest) { + header.appendf(" -c"); + } +#endif + if (FLAGS_dumpOp) { + header.appendf(" -d"); + } +#ifdef SK_DEBUG + if (FLAGS_runFail) { + header.appendf(" -f"); + } +#endif + if (FLAGS_verbose) { + header.appendf(" -v"); + } + if (FLAGS_veryVerbose) { + header.appendf(" -V"); + } + if (FLAGS_extendedTest) { + header.appendf(" -x"); + } + if (FLAGS_verifyOp) { + header.appendf(" -y"); + } #ifdef SK_DEBUG header.append(" SK_DEBUG"); #else header.append(" SK_RELEASE"); #endif - header.appendf(" skia_arch_width=%d", (int)sizeof(void*) * 8); if (FLAGS_veryVerbose) { header.appendf("\n"); } - SkDebugf(header.c_str()); + SkDebugf("%s", header.c_str()); } // Count tests first. int total = 0; int toRun = 0; - Test* test; - Iter iter; - while ((test = iter.next(NULL/*reporter not needed*/)) != NULL) { - SkAutoTDelete<Test> owned(test); - if (should_run(test->getName(), test->isGPUTest())) { + for (const TestRegistry* iter = TestRegistry::Head(); iter; + iter = iter->next()) { + const Test& test = iter->factory(); + if (should_run(test.name, test.needsGpu)) { toRun++; } total++; } // Now run them. - iter.reset(); - int32_t failCount = 0; int skipCount = 0; SkTaskGroup::Enabler enabled(FLAGS_threads); SkTaskGroup cpuTests; - SkTArray<Test*> gpuTests; // Always passes ownership to an SkTestRunnable + SkTArray<const Test*> gpuTests; - DebugfReporter reporter(toRun); - for (int i = 0; i < total; i++) { - SkAutoTDelete<Test> test(iter.next(&reporter)); - if (!should_run(test->getName(), test->isGPUTest())) { + Status status(toRun); + for (const TestRegistry* iter = TestRegistry::Head(); iter; + iter = iter->next()) { + const Test& test = iter->factory(); + if (!should_run(test.name, test.needsGpu)) { ++skipCount; - } else if (test->isGPUTest()) { - gpuTests.push_back() = test.detach(); + } else if (test.needsGpu) { + gpuTests.push_back(&test); } else { - cpuTests.add(SkNEW_ARGS(SkTestRunnable, (test.detach(), &failCount))); + cpuTests.add(SkTestRunnable(test, &status)); } } + GrContextFactory* grContextFactoryPtr = nullptr; #if SK_SUPPORT_GPU // Give GPU tests a context factory if that makes sense on this machine. GrContextFactory grContextFactory; - for (int i = 0; i < gpuTests.count(); i++) { - gpuTests[i]->setGrContextFactory(&grContextFactory); - } + grContextFactoryPtr = &grContextFactory; + #endif // Run GPU tests on this thread. for (int i = 0; i < gpuTests.count(); i++) { - SkNEW_ARGS(SkTestRunnable, (gpuTests[i], &failCount))->run(); + SkTestRunnable(*gpuTests[i], &status, grContextFactoryPtr)(); } // Block until threaded tests finish. cpuTests.wait(); if (FLAGS_verbose) { - SkDebugf("\nFinished %d tests, %d failures, %d skipped. (%d internal tests)", - toRun, failCount, skipCount, reporter.countTests()); + SkDebugf( + "\nFinished %d tests, %d failures, %d skipped. " + "(%d internal tests)", + toRun, status.failCount(), skipCount, status.testCount()); + if (gVerboseFinalize) { + (*gVerboseFinalize)(); + } } - SkGraphics::Term(); SkDebugf("\n"); - return (failCount == 0) ? 0 : 1; -} - -#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) -int main(int argc, char** argv) { - SkCommandLineFlags::Parse(argc, argv); - return test_main(); -} +#if DEBUG_COIN + if (FLAGS_coinTest) { + SkPathOpsDebug::DumpCoinDict(); + } #endif + + return (status.failCount() == 0) ? 0 : 1; +}