static double quadAngle(skiatest::Reporter* reporter, const SkDQuad& quad, double t) { const SkDVector& pt = quad.ptAtT(t) - quad[0]; double angle = (atan2(pt.fY, pt.fX) + SK_ScalarPI) * 8 / (SK_ScalarPI * 2); REPORTER_ASSERT(reporter, angle >= 0 && angle <= 8); return angle; }
static bool bruteMinT(skiatest::Reporter* reporter, const SkDQuad& quad1, const SkDQuad& quad2, TRange* lowerRange, TRange* upperRange) { double maxRadius = SkTMin(maxDist(quad1), maxDist(quad2)); double maxQuads = SkTMax(maxQuad(quad1), maxQuad(quad2)); double r = maxRadius / 2; double rStep = r / 2; SkDPoint best1 = {SK_ScalarInfinity, SK_ScalarInfinity}; SkDPoint best2 = {SK_ScalarInfinity, SK_ScalarInfinity}; int bestCCW = -1; double bestR = maxRadius; upperRange->tMin = 0; lowerRange->tMin = 1; do { do { // find upper bounds of single result TRange tRange; bool stepUp = orderTRange(reporter, quad1, quad2, r, &tRange); if (stepUp) { SkDPoint pt1 = quad1.ptAtT(tRange.t1); if (equalPoints(pt1, best1, maxQuads)) { break; } best1 = pt1; SkDPoint pt2 = quad2.ptAtT(tRange.t2); if (equalPoints(pt2, best2, maxQuads)) { break; } best2 = pt2; if (gPathOpsAngleIdeasVerbose) { SkDebugf("u bestCCW=%d ccw=%d bestMin=%1.9g:%1.9g r=%1.9g tMin=%1.9g\n", bestCCW, tRange.ccw, lowerRange->tMin, upperRange->tMin, r, tRange.tMin); } if (bestCCW >= 0 && bestCCW != (int) tRange.ccw) { if (tRange.tMin < upperRange->tMin) { upperRange->tMin = 0; } else { stepUp = false; } } if (upperRange->tMin < tRange.tMin) { bestCCW = tRange.ccw; bestR = r; *upperRange = tRange; } if (lowerRange->tMin > tRange.tMin) { *lowerRange = tRange; } } r += stepUp ? rStep : -rStep; rStep /= 2; } while (rStep > FLT_EPSILON); if (bestCCW < 0) { REPORTER_ASSERT(reporter, bestR < maxRadius); return false; } double lastHighR = bestR; r = bestR / 2; rStep = r / 2; do { // find lower bounds of single result TRange tRange; bool success = orderTRange(reporter, quad1, quad2, r, &tRange); if (success) { if (gPathOpsAngleIdeasVerbose) { SkDebugf("l bestCCW=%d ccw=%d bestMin=%1.9g:%1.9g r=%1.9g tMin=%1.9g\n", bestCCW, tRange.ccw, lowerRange->tMin, upperRange->tMin, r, tRange.tMin); } if (bestCCW != (int) tRange.ccw || upperRange->tMin < tRange.tMin) { bestCCW = tRange.ccw; *upperRange = tRange; bestR = lastHighR; break; // need to establish a new upper bounds } SkDPoint pt1 = quad1.ptAtT(tRange.t1); SkDPoint pt2 = quad2.ptAtT(tRange.t2); if (equalPoints(pt1, best1, maxQuads)) { goto breakOut; } best1 = pt1; if (equalPoints(pt2, best2, maxQuads)) { goto breakOut; } best2 = pt2; if (equalPoints(pt1, pt2, maxQuads)) { success = false; } else { if (upperRange->tMin < tRange.tMin) { *upperRange = tRange; } if (lowerRange->tMin > tRange.tMin) { *lowerRange = tRange; } } lastHighR = SkTMin(r, lastHighR); } r += success ? -rStep : rStep; rStep /= 2; } while (rStep > FLT_EPSILON); } while (rStep > FLT_EPSILON); breakOut: if (gPathOpsAngleIdeasVerbose) { SkDebugf("l a2-a1==%1.9g\n", lowerRange->a2 - lowerRange->a1); } return true; }
static void TestDeferredCanvasBitmapCaching(skiatest::Reporter* reporter) { SkBitmap store; store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100); store.allocPixels(); SkDevice device(store); NotificationCounter notificationCounter; SkDeferredCanvas canvas(&device); canvas.setNotificationClient(¬ificationCounter); const int imageCount = 2; SkBitmap sourceImages[imageCount]; for (int i = 0; i < imageCount; i++) { sourceImages[i].setConfig(SkBitmap::kARGB_8888_Config, 100, 100); sourceImages[i].allocPixels(); } 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 TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) { SkBitmap store; SkRect fullRect; fullRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(gWidth), SkIntToScalar(gHeight)); SkRect partialRect; partialRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(1), SkIntToScalar(1)); create(&store, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF); SkDevice device(store); SkDeferredCanvas canvas(&device); // 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(SkCanvas::kMatrixClip_SaveFlag); 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, SkCanvas::kARGB_ClipLayer_SaveFlag); 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(SkCanvas::kMatrixClip_SaveFlag); 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, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF); bmp.setIsOpaque(true); 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 ); SkBitmap bmp; create(&bmp, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF); bmp.setIsOpaque(false); 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(SkCanvas::kMatrixClip_SaveFlag); 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(SkCanvas::kMatrixClip_SaveFlag); 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()); } }
~TestAnnotationCanvas() { REPORTER_ASSERT(fReporter, fCount == fCurrIndex); }
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 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)); } } // assert: rotate/perspect is never treated as sprite for (int i = 0; i < 1000; ++i) { 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)); } } size.set(500, 600); const SkScalar tooMuchSubpixel = 100.1f; mat.setTranslate(tooMuchSubpixel, 0); REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits)); mat.setTranslate(0, tooMuchSubpixel); REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits)); const SkScalar tinySubPixel = 100.02f; mat.setTranslate(tinySubPixel, 0); REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, bilerBits)); mat.setTranslate(0, tinySubPixel); REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, bilerBits)); const SkScalar twoThirds = SK_Scalar1 * 2 / 3; 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)); const SkScalar oneThird = SK_Scalar1 / 3; 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)); const SkScalar oneFortyth = SK_Scalar1 / 40; 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)); }
static void TestWStream(skiatest::Reporter* reporter) { SkDynamicMemoryWStream ds; const char s[] = "abcdefghijklmnopqrstuvwxyz"; int i; for (i = 0; i < 100; i++) { REPORTER_ASSERT(reporter, ds.write(s, 26)); } REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26); char* dst = new char[100 * 26 + 1]; dst[100*26] = '*'; ds.copyTo(dst); REPORTER_ASSERT(reporter, dst[100*26] == '*'); for (i = 0; i < 100; i++) { REPORTER_ASSERT(reporter, memcmp(&dst[i * 26], s, 26) == 0); } { SkAutoTDelete<SkStreamAsset> stream(ds.detachAsStream()); REPORTER_ASSERT(reporter, 100 * 26 == stream->getLength()); REPORTER_ASSERT(reporter, ds.getOffset() == 0); test_loop_stream(reporter, stream.get(), s, 26, 100); SkAutoTDelete<SkStreamAsset> stream2(stream->duplicate()); test_loop_stream(reporter, stream2.get(), s, 26, 100); SkAutoTDelete<SkStreamAsset> stream3(stream->fork()); REPORTER_ASSERT(reporter, stream3->isAtEnd()); char tmp; size_t bytes = stream->read(&tmp, 1); REPORTER_ASSERT(reporter, 0 == bytes); stream3->rewind(); test_loop_stream(reporter, stream3.get(), s, 26, 100); } for (i = 0; i < 100; i++) { REPORTER_ASSERT(reporter, ds.write(s, 26)); } REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26); { sk_sp<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. SkAutoTDelete<SkStreamAsset> stream(ds.detachAsStream()); REPORTER_ASSERT(reporter, ds.getOffset() == 0); test_loop_stream(reporter, stream.get(), s, 26, 100); SkAutoTDelete<SkStreamAsset> stream2(stream->duplicate()); test_loop_stream(reporter, stream2.get(), s, 26, 100); } delete[] dst; SkString tmpDir = skiatest::GetTmpDir(); if (!tmpDir.isEmpty()) { test_filestreams(reporter, tmpDir.c_str()); } }
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)); }
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); } }
DEF_TEST(StreamEmptyStreamMemoryBase, r) { SkDynamicMemoryWStream tmp; SkAutoTDelete<SkStreamAsset> asset(tmp.detachAsStream()); REPORTER_ASSERT(r, nullptr == asset->getMemoryBase()); }
static void testTightBoundsQuads(PathOpsThreadState* data) { SkRandom ran; const int bitWidth = 32; const int bitHeight = 32; const float pathMin = 1; const float pathMax = (float) (bitHeight - 2); SkBitmap& bits = *data->fBitmap; if (bits.width() == 0) { bits.allocN32Pixels(bitWidth, bitHeight); } SkCanvas canvas(bits); SkPaint paint; for (int index = 0; index < 100; ++index) { SkPath path; int contourCount = ran.nextRangeU(1, 10); for (int cIndex = 0; cIndex < contourCount; ++cIndex) { int lineCount = ran.nextRangeU(1, 10); path.moveTo(ran.nextRangeF(1, pathMax), ran.nextRangeF(pathMin, pathMax)); for (int lIndex = 0; lIndex < lineCount; ++lIndex) { if (ran.nextBool()) { path.lineTo(ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax)); } else { path.quadTo(ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax)); } } if (ran.nextBool()) { path.close(); } } SkRect classicBounds = path.getBounds(); SkRect tightBounds; REPORTER_ASSERT(data->fReporter, TightBounds(path, &tightBounds)); REPORTER_ASSERT(data->fReporter, classicBounds.contains(tightBounds)); canvas.drawColor(SK_ColorWHITE); canvas.drawPath(path, paint); SkIRect bitsWritten = {31, 31, 0, 0}; for (int y = 0; y < bitHeight; ++y) { uint32_t* addr1 = data->fBitmap->getAddr32(0, y); bool lineWritten = false; for (int x = 0; x < bitWidth; ++x) { if (addr1[x] == (uint32_t) -1) { continue; } lineWritten = true; bitsWritten.fLeft = SkTMin(bitsWritten.fLeft, x); bitsWritten.fRight = SkTMax(bitsWritten.fRight, x); } if (!lineWritten) { continue; } bitsWritten.fTop = SkTMin(bitsWritten.fTop, y); bitsWritten.fBottom = SkTMax(bitsWritten.fBottom, y); } if (!bitsWritten.isEmpty()) { SkIRect tightOut; tightBounds.roundOut(&tightOut); REPORTER_ASSERT(data->fReporter, tightOut.contains(bitsWritten)); } } }
static void assert_data(skiatest::Reporter* reporter, SkData* ref, const void* data, size_t len) { REPORTER_ASSERT(reporter, ref->size() == len); REPORTER_ASSERT(reporter, !memcmp(ref->data(), data, len)); }
static void assert_len(skiatest::Reporter* reporter, SkData* ref, size_t len) { REPORTER_ASSERT(reporter, ref->size() == len); }
static void testQuadAngles(skiatest::Reporter* reporter, const SkDQuad& quad1, const SkDQuad& quad2, int testNo) { 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].angle(0), seg[1].angle(0)); const SkDPoint& origin = quad1[0]; REPORTER_ASSERT(reporter, origin == quad2[0]); double a1s = atan2(origin.fY - quad1[1].fY, quad1[1].fX - origin.fX); double a1e = atan2(origin.fY - quad1[2].fY, quad1[2].fX - origin.fX); double a2s = atan2(origin.fY - quad2[1].fY, quad2[1].fX - origin.fX); double a2e = atan2(origin.fY - quad2[2].fY, quad2[2].fX - origin.fX); bool oldSchoolOverlap = radianBetween(a1s, a2s, a1e) || radianBetween(a1s, a2e, a1e) || radianBetween(a2s, a1s, a2e) || radianBetween(a2s, a1e, a2e); int overlap = quadHullsOverlap(reporter, quad1, quad2); bool realMatchesOverlap = realOverlap == overlap || SK_ScalarPI - fabs(a2s - a1s) < 0.002; if (realOverlap != overlap) { SkDebugf("\nSK_ScalarPI - fabs(a2s - a1s) = %1.9g\n", SK_ScalarPI - fabs(a2s - a1s)); } if (!realMatchesOverlap) { DumpQ(quad1, quad2, testNo); } REPORTER_ASSERT(reporter, realMatchesOverlap); if (oldSchoolOverlap != (overlap < 0)) { overlap = quadHullsOverlap(reporter, quad1, quad2); // set a breakpoint and debug if assert fires REPORTER_ASSERT(reporter, oldSchoolOverlap == (overlap < 0)); } SkDVector v1s = quad1[1] - quad1[0]; SkDVector v1e = quad1[2] - quad1[0]; SkDVector v2s = quad2[1] - quad2[0]; SkDVector v2e = quad2[2] - quad2[0]; double vDir[2] = { v1s.cross(v1e), v2s.cross(v2e) }; bool ray1In2 = v1s.cross(v2s) * vDir[1] <= 0 && v1s.cross(v2e) * vDir[1] >= 0; bool ray2In1 = v2s.cross(v1s) * vDir[0] <= 0 && v2s.cross(v1e) * vDir[0] >= 0; if (overlap >= 0) { // verify that hulls really don't overlap REPORTER_ASSERT(reporter, !ray1In2); REPORTER_ASSERT(reporter, !ray2In1); bool ctrl1In2 = v1e.cross(v2s) * vDir[1] <= 0 && v1e.cross(v2e) * vDir[1] >= 0; REPORTER_ASSERT(reporter, !ctrl1In2); bool ctrl2In1 = v2e.cross(v1s) * vDir[0] <= 0 && v2e.cross(v1e) * vDir[0] >= 0; REPORTER_ASSERT(reporter, !ctrl2In1); // check answer against reference bruteForce(reporter, quad1, quad2, overlap > 0); } // continue end point rays and see if they intersect the opposite curve SkDLine rays[] = {{{origin, quad2[2]}}, {{origin, quad1[2]}}}; const SkDQuad* quads[] = {&quad1, &quad2}; SkDVector midSpokes[2]; SkIntersections intersect[2]; double minX, minY, maxX, maxY; minX = minY = SK_ScalarInfinity; maxX = maxY = -SK_ScalarInfinity; double maxWidth = 0; bool useIntersect = false; double smallestTs[] = {1, 1}; for (unsigned index = 0; index < SK_ARRAY_COUNT(quads); ++index) { const SkDQuad& q = *quads[index]; midSpokes[index] = q.ptAtT(0.5) - origin; minX = SkTMin(SkTMin(SkTMin(minX, origin.fX), q[1].fX), q[2].fX); minY = SkTMin(SkTMin(SkTMin(minY, origin.fY), q[1].fY), q[2].fY); maxX = SkTMax(SkTMax(SkTMax(maxX, origin.fX), q[1].fX), q[2].fX); maxY = SkTMax(SkTMax(SkTMax(maxY, origin.fY), q[1].fY), q[2].fY); maxWidth = SkTMax(maxWidth, SkTMax(maxX - minX, maxY - minY)); intersect[index].intersectRay(q, rays[index]); const SkIntersections& i = intersect[index]; REPORTER_ASSERT(reporter, i.used() >= 1); bool foundZero = false; double smallT = 1; for (int idx2 = 0; idx2 < i.used(); ++idx2) { double t = i[0][idx2]; if (t == 0) { foundZero = true; continue; } if (smallT > t) { smallT = t; } } REPORTER_ASSERT(reporter, foundZero == true); if (smallT == 1) { continue; } SkDVector ray = q.ptAtT(smallT) - origin; SkDVector end = rays[index][1] - origin; if (ray.fX * end.fX < 0 || ray.fY * end.fY < 0) { continue; } double rayDist = ray.length(); double endDist = end.length(); double delta = fabs(rayDist - endDist) / maxWidth; if (delta > 1e-4) { useIntersect ^= true; } smallestTs[index] = smallT; } bool firstInside; if (useIntersect) { int sIndex = (int) (smallestTs[1] < 1); REPORTER_ASSERT(reporter, smallestTs[sIndex ^ 1] == 1); double t = smallestTs[sIndex]; const SkDQuad& q = *quads[sIndex]; SkDVector ray = q.ptAtT(t) - origin; SkDVector end = rays[sIndex][1] - origin; double rayDist = ray.length(); double endDist = end.length(); SkDVector mid = q.ptAtT(t / 2) - origin; double midXray = mid.crossCheck(ray); if (gPathOpsAngleIdeasVerbose) { SkDebugf("rayDist>endDist:%d sIndex==0:%d vDir[sIndex]<0:%d midXray<0:%d\n", rayDist > endDist, sIndex == 0, vDir[sIndex] < 0, midXray < 0); } SkASSERT(SkScalarSignAsInt(SkDoubleToScalar(midXray)) == SkScalarSignAsInt(SkDoubleToScalar(vDir[sIndex]))); firstInside = (rayDist > endDist) ^ (sIndex == 0) ^ (vDir[sIndex] < 0); } else if (overlap >= 0) { return; // answer has already been determined } else { firstInside = checkParallel(reporter, quad1, quad2); } if (overlap < 0) { SkDEBUGCODE(int realEnds =) PathOpsAngleTester::EndsIntersect(seg[0].angle(0), seg[1].angle(0)); SkASSERT(realEnds == (firstInside ? 1 : 0)); }
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 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(Serialization, reporter) { // Test matrix serialization { SkMatrix matrix = SkMatrix::I(); TestObjectSerialization(&matrix, reporter); } // Test path serialization { SkPath path; TestObjectSerialization(&path, reporter); } // Test region serialization { SkRegion region; TestObjectSerialization(®ion, reporter); } // Test xfermode serialization { TestXfermodeSerialization(reporter); } // Test color filter serialization { TestColorFilterSerialization(reporter); } // Test string serialization { SkString string("string"); TestObjectSerializationNoAlign<SkString, false>(&string, reporter); TestObjectSerializationNoAlign<SkString, true>(&string, reporter); } // Test rrect serialization { // SkRRect does not initialize anything. // An uninitialized SkRRect can be serialized, // but will branch on uninitialized data when deserialized. SkRRect rrect; SkRect rect = SkRect::MakeXYWH(1, 2, 20, 30); SkVector corners[4] = { {1, 2}, {2, 3}, {3,4}, {4,5} }; rrect.setRectRadii(rect, corners); TestAlignment(&rrect, reporter); } // Test readByteArray { unsigned char data[kArraySize] = { 1, 2, 3 }; TestArraySerialization(data, reporter); } // Test readColorArray { SkColor data[kArraySize] = { SK_ColorBLACK, SK_ColorWHITE, SK_ColorRED }; TestArraySerialization(data, reporter); } // Test readIntArray { int32_t data[kArraySize] = { 1, 2, 4, 8 }; TestArraySerialization(data, reporter); } // Test readPointArray { SkPoint data[kArraySize] = { {6, 7}, {42, 128} }; TestArraySerialization(data, reporter); } // Test readScalarArray { SkScalar data[kArraySize] = { SK_Scalar1, SK_ScalarHalf, SK_ScalarMax }; TestArraySerialization(data, reporter); } // Test invalid deserializations { SkImageInfo info = SkImageInfo::MakeN32Premul(kBitmapSize, kBitmapSize); SkBitmap validBitmap; validBitmap.setInfo(info); // Create a bitmap with a really large height SkBitmap invalidBitmap; invalidBitmap.setInfo(info.makeWH(info.width(), 1000000000)); // The deserialization should succeed, and the rendering shouldn't crash, // even when the device fails to initialize, due to its size TestBitmapSerialization(validBitmap, invalidBitmap, true, reporter); } // Test simple SkPicture serialization { SkPictureRecorder recorder; draw_something(recorder.beginRecording(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize), nullptr, 0)); SkAutoTUnref<SkPicture> pict(recorder.endRecording()); // Serialize picture SkWriteBuffer writer(SkWriteBuffer::kValidation_Flag); pict->flatten(writer); size_t size = writer.bytesWritten(); SkAutoTMalloc<unsigned char> data(size); writer.writeToMemory(static_cast<void*>(data.get())); // Deserialize picture SkValidatingReadBuffer reader(static_cast<void*>(data.get()), size); SkAutoTUnref<SkPicture> readPict( SkPicture::CreateFromBuffer(reader)); REPORTER_ASSERT(reporter, readPict.get()); } TestPictureTypefaceSerialization(reporter); }
static void test_ptrs(skiatest::Reporter* reporter) { SkRefCnt ref; REPORTER_ASSERT(reporter, ref.unique()); { SkMetaData md0, md1; const char name[] = "refcnt"; md0.setRefCnt(name, &ref); REPORTER_ASSERT(reporter, md0.findRefCnt(name)); REPORTER_ASSERT(reporter, md0.hasRefCnt(name, &ref)); REPORTER_ASSERT(reporter, !ref.unique()); md1 = md0; REPORTER_ASSERT(reporter, md1.findRefCnt(name)); REPORTER_ASSERT(reporter, md1.hasRefCnt(name, &ref)); 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, !ref.unique()); } REPORTER_ASSERT(reporter, ref.unique()); }
DEF_TEST(Serialization, reporter) { // Test matrix serialization { SkMatrix matrix = SkMatrix::I(); TestObjectSerialization(&matrix, reporter); } // Test path serialization { SkPath path; TestObjectSerialization(&path, reporter); } // Test region serialization { SkRegion region; TestObjectSerialization(®ion, reporter); } // Test xfermode serialization { TestXfermodeSerialization(reporter); } // Test color filter serialization { TestColorFilterSerialization(reporter); } // Test string serialization { SkString string("string"); TestObjectSerializationNoAlign<SkString, false>(&string, reporter); TestObjectSerializationNoAlign<SkString, true>(&string, reporter); } // Test rrect serialization { // SkRRect does not initialize anything. // An uninitialized SkRRect can be serialized, // but will branch on uninitialized data when deserialized. SkRRect rrect; SkRect rect = SkRect::MakeXYWH(1, 2, 20, 30); SkVector corners[4] = { {1, 2}, {2, 3}, {3,4}, {4,5} }; rrect.setRectRadii(rect, corners); TestAlignment(&rrect, reporter); } // Test readByteArray { unsigned char data[kArraySize] = { 1, 2, 3 }; TestArraySerialization(data, reporter); } // Test readColorArray { SkColor data[kArraySize] = { SK_ColorBLACK, SK_ColorWHITE, SK_ColorRED }; TestArraySerialization(data, reporter); } // Test readIntArray { int32_t data[kArraySize] = { 1, 2, 4, 8 }; TestArraySerialization(data, reporter); } // Test readPointArray { SkPoint data[kArraySize] = { {6, 7}, {42, 128} }; TestArraySerialization(data, reporter); } // Test readScalarArray { SkScalar data[kArraySize] = { SK_Scalar1, SK_ScalarHalf, SK_ScalarMax }; TestArraySerialization(data, reporter); } // Test invalid deserializations { SkImageInfo info = SkImageInfo::MakeN32Premul(kBitmapSize, kBitmapSize); SkBitmap validBitmap; validBitmap.setInfo(info); // Create a bitmap with a really large height SkBitmap invalidBitmap; invalidBitmap.setInfo(info.makeWH(info.width(), 1000000000)); // The deserialization should succeed, and the rendering shouldn't crash, // even when the device fails to initialize, due to its size TestBitmapSerialization(validBitmap, invalidBitmap, true, reporter); } // Test simple SkPicture serialization { SkPictureRecorder recorder; draw_something(recorder.beginRecording(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize), nullptr, 0)); sk_sp<SkPicture> pict(recorder.finishRecordingAsPicture()); // Serialize picture SkBinaryWriteBuffer writer; pict->flatten(writer); size_t size = writer.bytesWritten(); SkAutoTMalloc<unsigned char> data(size); writer.writeToMemory(static_cast<void*>(data.get())); // Deserialize picture SkValidatingReadBuffer reader(static_cast<void*>(data.get()), size); sk_sp<SkPicture> readPict(SkPicture::MakeFromBuffer(reader)); REPORTER_ASSERT(reporter, readPict.get()); } TestPictureTypefaceSerialization(reporter); // Test SkLightingShader/NormalMapSource serialization { const int kTexSize = 2; SkLights::Builder builder; builder.add(SkLights::Light(SkColor3f::Make(1.0f, 1.0f, 1.0f), SkVector3::Make(1.0f, 0.0f, 0.0f))); builder.add(SkLights::Light(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 = SkMakeBitmapShader(normals, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix, nullptr); sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeFromNormalMap(std::move(normalMap), ctm); sk_sp<SkShader> lightingShader = SkLightingShader::Make(diffuse, fLights, &matrix, std::move(normalSource)); SkAutoTUnref<SkShader>(TestFlattenableSerialization(lightingShader.get(), true, reporter)); // TODO test equality? } }