void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coincidences, bool partial) { int count = coincidences.count(); #if DEBUG_CONCIDENT if (count > 0) { SkDebugf("%s count=%d\n", __FUNCTION__, count); } #endif // look for a lineup where the partial implies another adjoining coincidence for (int index = 0; index < count; ++index) { const SkCoincidence& coincidence = coincidences[index]; int thisIndex = coincidence.fSegments[0]; SkOpSegment& thisOne = fSegments[thisIndex]; if (thisOne.done()) { continue; } SkOpContour* otherContour = coincidence.fOther; int otherIndex = coincidence.fSegments[1]; SkOpSegment& other = otherContour->fSegments[otherIndex]; if (other.done()) { continue; } double startT = coincidence.fTs[0][0]; double endT = coincidence.fTs[0][1]; if (startT == endT) { // this can happen in very large compares continue; } double oStartT = coincidence.fTs[1][0]; double oEndT = coincidence.fTs[1][1]; if (oStartT == oEndT) { continue; } bool swapStart = startT > endT; bool swapOther = oStartT > oEndT; const SkPoint* startPt = &coincidence.fPts[0][0]; const SkPoint* endPt = &coincidence.fPts[0][1]; if (swapStart) { SkTSwap(startT, endT); SkTSwap(oStartT, oEndT); SkTSwap(startPt, endPt); } bool cancel = swapOther != swapStart; int step = swapStart ? -1 : 1; int oStep = swapOther ? -1 : 1; double oMatchStart = cancel ? oEndT : oStartT; if (partial ? startT != 0 || oMatchStart != 0 : (startT == 0) != (oMatchStart == 0)) { bool added = false; if (oMatchStart != 0) { const SkPoint& oMatchStartPt = cancel ? *endPt : *startPt; added = thisOne.joinCoincidence(&other, oMatchStart, oMatchStartPt, oStep, cancel); } if (!cancel && startT != 0 && !added) { (void) other.joinCoincidence(&thisOne, startT, *startPt, step, cancel); } } double oMatchEnd = cancel ? oStartT : oEndT; if (partial ? endT != 1 || oMatchEnd != 1 : (endT == 1) != (oMatchEnd == 1)) { bool added = false; if (cancel && endT != 1 && !added) { (void) other.joinCoincidence(&thisOne, endT, *endPt, -step, cancel); } } } }
void GrDrawTarget::drawBatch(const GrPipelineBuilder& pipelineBuilder, GrDrawContext* drawContext, const GrClip& clip, GrDrawBatch* batch) { // Setup clip GrAppliedClip appliedClip; if (!clip.apply(fContext, pipelineBuilder, drawContext, &batch->bounds(), &appliedClip)) { return; } GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps; if (appliedClip.clipCoverageFragmentProcessor()) { arfps.set(&pipelineBuilder); arfps.addCoverageFragmentProcessor(appliedClip.clipCoverageFragmentProcessor()); } GrPipeline::CreateArgs args; args.fPipelineBuilder = &pipelineBuilder; args.fCaps = this->caps(); args.fScissor = &appliedClip.scissorState(); args.fHasStencilClip = appliedClip.hasStencilClip(); if (pipelineBuilder.hasUserStencilSettings() || appliedClip.hasStencilClip()) { if (!fResourceProvider->attachStencilAttachment(pipelineBuilder.getRenderTarget())) { SkDebugf("ERROR creating stencil attachment. Draw skipped.\n"); return; } } batch->getPipelineOptimizations(&args.fOpts); GrScissorState finalScissor; if (args.fOpts.fOverrides.fUsePLSDstRead || fClipBatchToBounds) { GrRenderTarget* rt = pipelineBuilder.getRenderTarget(); GrGLIRect viewport; viewport.fLeft = 0; viewport.fBottom = 0; viewport.fWidth = rt->width(); viewport.fHeight = rt->height(); SkIRect ibounds; ibounds.fLeft = SkTPin(SkScalarFloorToInt(batch->bounds().fLeft), viewport.fLeft, viewport.fWidth); ibounds.fTop = SkTPin(SkScalarFloorToInt(batch->bounds().fTop), viewport.fBottom, viewport.fHeight); ibounds.fRight = SkTPin(SkScalarCeilToInt(batch->bounds().fRight), viewport.fLeft, viewport.fWidth); ibounds.fBottom = SkTPin(SkScalarCeilToInt(batch->bounds().fBottom), viewport.fBottom, viewport.fHeight); if (appliedClip.scissorState().enabled()) { const SkIRect& scissorRect = appliedClip.scissorState().rect(); if (!ibounds.intersect(scissorRect)) { return; } } finalScissor.set(ibounds); args.fScissor = &finalScissor; } args.fOpts.fColorPOI.completeCalculations(pipelineBuilder.fColorFragmentProcessors.begin(), pipelineBuilder.numColorFragmentProcessors()); args.fOpts.fCoveragePOI.completeCalculations( pipelineBuilder.fCoverageFragmentProcessors.begin(), pipelineBuilder.numCoverageFragmentProcessors()); if (!this->setupDstReadIfNecessary(pipelineBuilder, clip, args.fOpts, &args.fDstTexture, batch->bounds())) { return; } if (!batch->installPipeline(args)) { return; } #ifdef ENABLE_MDB SkASSERT(fRenderTarget); batch->pipeline()->addDependenciesTo(fRenderTarget); #endif this->recordBatch(batch); }
void ShowOp(SkPathOp op, const char* pathOne, const char* pathTwo) { SkDebugf(" testPathOp(reporter, %s, %s, %s);\n", pathOne, pathTwo, gOpStrs[op]); SkDebugf("}\n"); }
void TestResult::testOne() { SkPicture* pic = nullptr; { SkString d; d.printf(" {%d, \"%s\"},", fDirNo, fFilename); SkString path = make_filepath(fDirNo, IN_DIR, fFilename); SkFILEStream stream(path.c_str()); if (!stream.isValid()) { SkDebugf("invalid stream %s\n", path.c_str()); goto finish; } if (fTestStep == kEncodeFiles) { size_t length = stream.getLength(); SkTArray<char, true> bytes; bytes.push_back_n(length); stream.read(&bytes[0], length); stream.rewind(); SkString wPath = make_filepath(0, outSkpDir, fFilename); SkFILEWStream wStream(wPath.c_str()); wStream.write(&bytes[0], length); wStream.flush(); } pic = SkPicture::CreateFromStream(&stream, &SkImageDecoder::DecodeMemory); if (!pic) { SkDebugf("unable to decode %s\n", fFilename); goto finish; } int pWidth = pic->width(); int pHeight = pic->height(); int pLargerWH = SkTMax(pWidth, pHeight); GrContextFactory contextFactory; #ifdef SK_BUILD_FOR_WIN GrContext* context = contextFactory.get(kAngle); #else GrContext* context = contextFactory.get(kNative); #endif if (nullptr == context) { SkDebugf("unable to allocate context for %s\n", fFilename); goto finish; } int maxWH = context->getMaxRenderTargetSize(); int scale = 1; while (pLargerWH / scale > maxWH) { scale *= 2; } SkBitmap bitmap; SkIPoint dim; do { dim.fX = (pWidth + scale - 1) / scale; dim.fY = (pHeight + scale - 1) / scale; bool success = bitmap.allocN32Pixels(dim.fX, dim.fY); if (success) { break; } SkDebugf("-%d-", scale); } while ((scale *= 2) < 256); 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; } SkCanvas skCanvas(bitmap); drawPict(pic, &skCanvas, fScaleOversized ? scale : 1); GrTextureDesc desc; desc.fConfig = kSkia8888_GrPixelConfig; desc.fFlags = kRenderTarget_GrTextureFlagBit; desc.fWidth = dim.fX; desc.fHeight = dim.fY; desc.fSampleCnt = 0; SkAutoTUnref<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; } SkGpuDevice grDevice(context, texture.get()); SkCanvas grCanvas(&grDevice); drawPict(pic, &grCanvas, fScaleOversized ? scale : 1); SkBitmap grBitmap; grBitmap.allocPixels(grCanvas.imageInfo()); grCanvas.readPixels(&grBitmap, 0, 0); if (fTestStep == kCompareBits) { fPixelError = similarBits(grBitmap, bitmap); int skTime = timePict(pic, &skCanvas); int grTime = timePict(pic, &grCanvas); fTime = skTime - grTime; } else if (fTestStep == kEncodeFiles) { SkString pngStr = make_png_name(fFilename); const char* pngName = pngStr.c_str(); writePict(grBitmap, outGrDir, pngName); writePict(bitmap, outSkDir, pngName); } } finish: delete pic; }
DEF_TEST(SkpSkGrThreaded, reporter) { if (!initTest()) { return; } SkpSkGrThreadedTestRunner testRunner(reporter); for (int dirIndex = 1; dirIndex <= 100; ++dirIndex) { SkString pictDir = make_in_dir_name(dirIndex); if (pictDir.size() == 0) { continue; } SkOSFile::Iter iter(pictDir.c_str(), "skp"); SkString filename; while (iter.next(&filename)) { SkString pngName = make_png_name(filename.c_str()); SkString oldPng = make_filepath(dirIndex, outSkDir, pngName.c_str()); SkString newPng = make_filepath(dirIndex, outGrDir, pngName.c_str()); if (sk_exists(oldPng.c_str()) && sk_exists(newPng.c_str())) { bumpCount(reporter, true); continue; } for (size_t index = 0; index < skipOverSkGrCount; ++index) { if (skipOverSkGr[index].directory == dirIndex && strcmp(filename.c_str(), skipOverSkGr[index].filename) == 0) { bumpCount(reporter, true); goto skipOver; } } *testRunner.fRunnables.append() = new SkpSkGrThreadedRunnable( &testSkGrMain, dirIndex, filename.c_str(), &testRunner); skipOver: ; } } testRunner.render(); SkpSkGrThreadState& max = testRunner.fRunnables[0]->fState; for (int dirIndex = 2; dirIndex <= 100; ++dirIndex) { SkpSkGrThreadState& state = testRunner.fRunnables[dirIndex - 1]->fState; for (int index = 0; index < state.fFoundCount; ++index) { int maxIdx = max.fFoundCount; if (maxIdx < kMaxFiles) { max.fError[maxIdx] = state.fError[index]; strcpy(max.fFilesFound[maxIdx], state.fFilesFound[index]); max.fDirsFound[maxIdx] = state.fDirsFound[index]; ++max.fFoundCount; continue; } for (maxIdx = 0; maxIdx < max.fFoundCount; ++maxIdx) { if (max.fError[maxIdx] < state.fError[index]) { max.fError[maxIdx] = state.fError[index]; strcpy(max.fFilesFound[maxIdx], state.fFilesFound[index]); max.fDirsFound[maxIdx] = state.fDirsFound[index]; break; } } } } TestResult encoder; encoder.fTestStep = kEncodeFiles; for (int index = 0; index < max.fFoundCount; ++index) { encoder.fDirNo = max.fDirsFound[index]; strcpy(encoder.fFilename, max.fFilesFound[index]); encoder.testOne(); SkDebugf("+"); } }
SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex) { while (chase->count()) { SkOpSpan* span; chase->pop(&span); const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex); SkOpSegment* segment = backPtr.fOther; *tIndex = backPtr.fOtherIndex; bool sortable = true; bool done = true; *endIndex = -1; if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done, &sortable)) { *tIndex = last->start(); *endIndex = last->end(); #if TRY_ROTATE *chase->insert(0) = span; #else *chase->append() = span; #endif return last->segment(); } if (done) { continue; } if (!sortable) { continue; } // find first angle, initialize winding to computed wind sum const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex); const SkOpAngle* firstAngle; SkDEBUGCODE(firstAngle = angle); SkDEBUGCODE(bool loop = false); int winding; do { angle = angle->next(); SkASSERT(angle != firstAngle || !loop); SkDEBUGCODE(loop |= angle == firstAngle); segment = angle->segment(); winding = segment->windSum(angle); } while (winding == SK_MinS32); int spanWinding = segment->spanSign(angle->start(), angle->end()); #if DEBUG_WINDING SkDebugf("%s winding=%d spanWinding=%d\n", __FUNCTION__, winding, spanWinding); #endif // turn span winding into contour winding if (spanWinding * winding < 0) { winding += spanWinding; } // we care about first sign and whether wind sum indicates this // edge is inside or outside. Maybe need to pass span winding // or first winding or something into this function? // advance to first undone angle, then return it and winding // (to set whether edges are active or not) firstAngle = angle; winding -= firstAngle->segment()->spanSign(firstAngle); while ((angle = angle->next()) != firstAngle) { segment = angle->segment(); int maxWinding = winding; winding -= segment->spanSign(angle); #if DEBUG_SORT SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__, segment->debugID(), maxWinding, winding, angle->sign()); #endif *tIndex = angle->start(); *endIndex = angle->end(); int lesser = SkMin32(*tIndex, *endIndex); const SkOpSpan& nextSpan = segment->span(lesser); if (!nextSpan.fDone) { // FIXME: this be wrong? assign startWinding if edge is in // same direction. If the direction is opposite, winding to // assign is flipped sign or +/- 1? if (SkOpSegment::UseInnerWinding(maxWinding, winding)) { maxWinding = winding; } // allowed to do nothing (void) segment->markAndChaseWinding(angle, maxWinding, 0, NULL); break; } } *chase->insert(0) = span; return segment; } return NULL; }
static int contourRangeCheckY(const SkTArray<SkOpContour*, true>& contourList, SkOpSegment** currentPtr, int* indexPtr, int* endIndexPtr, double* bestHit, SkScalar* bestDx, bool* tryAgain, double* midPtr, bool opp) { const int index = *indexPtr; const int endIndex = *endIndexPtr; const double mid = *midPtr; const SkOpSegment* current = *currentPtr; double tAtMid = current->tAtMid(index, endIndex, mid); SkPoint basePt = current->ptAtT(tAtMid); int contourCount = contourList.count(); SkScalar bestY = SK_ScalarMin; SkOpSegment* bestSeg = NULL; int bestTIndex = 0; bool bestOpp; bool hitSomething = false; for (int cTest = 0; cTest < contourCount; ++cTest) { SkOpContour* contour = contourList[cTest]; bool testOpp = contour->operand() ^ current->operand() ^ opp; if (basePt.fY < contour->bounds().fTop) { continue; } if (bestY > contour->bounds().fBottom) { continue; } int segmentCount = contour->segments().count(); for (int test = 0; test < segmentCount; ++test) { SkOpSegment* testSeg = &contour->segments()[test]; SkScalar testY = bestY; double testHit; int testTIndex = testSeg->crossedSpanY(basePt, &testY, &testHit, &hitSomething, tAtMid, testOpp, testSeg == current); if (testTIndex < 0) { if (testTIndex == SK_MinS32) { hitSomething = true; bestSeg = NULL; goto abortContours; // vertical encountered, return and try different point } continue; } if (testSeg == current && current->betweenTs(index, testHit, endIndex)) { double baseT = current->t(index); double endT = current->t(endIndex); double newMid = (testHit - baseT) / (endT - baseT); #if DEBUG_WINDING double midT = current->tAtMid(index, endIndex, mid); SkPoint midXY = current->xyAtT(midT); double newMidT = current->tAtMid(index, endIndex, newMid); SkPoint newXY = current->xyAtT(newMidT); SkDebugf("%s [%d] mid=%1.9g->%1.9g s=%1.9g (%1.9g,%1.9g) m=%1.9g (%1.9g,%1.9g)" " n=%1.9g (%1.9g,%1.9g) e=%1.9g (%1.9g,%1.9g)\n", __FUNCTION__, current->debugID(), mid, newMid, baseT, current->xAtT(index), current->yAtT(index), baseT + mid * (endT - baseT), midXY.fX, midXY.fY, baseT + newMid * (endT - baseT), newXY.fX, newXY.fY, endT, current->xAtT(endIndex), current->yAtT(endIndex)); #endif *midPtr = newMid * 2; // calling loop with divide by 2 before continuing return SK_MinS32; } bestSeg = testSeg; *bestHit = testHit; bestOpp = testOpp; bestTIndex = testTIndex; bestY = testY; } } abortContours: int result; if (!bestSeg) { result = hitSomething ? SK_MinS32 : 0; } else { if (bestSeg->windSum(bestTIndex) == SK_MinS32) { *currentPtr = bestSeg; *indexPtr = bestTIndex; *endIndexPtr = bestSeg->nextSpan(bestTIndex, 1); SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0); *tryAgain = true; return 0; } result = bestSeg->windingAtT(*bestHit, bestTIndex, bestOpp, bestDx); SkASSERT(result == SK_MinS32 || *bestDx); } double baseT = current->t(index); double endT = current->t(endIndex); *bestHit = baseT + mid * (endT - baseT); return result; }
/** * 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) != SkImageDecoder::kFailure; 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) != SkImageDecoder::kFailure; 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); } } }
~MyObserver() { if (fCount != 0) { SkDebugf("--- pvjpeg left %d allocations\n", fCount); } }
void draw(SkCanvas* canvas) { SkIPoint pt1 = {45, 66}; SkIPoint pt2 = SkIPoint::Make(45, 66); SkDebugf("pt1 %c= pt2\n", pt1 == pt2 ? '=' : '!'); }
// 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); } } }
static void TestPathMeasure(skiatest::Reporter* reporter) { SkPath path; path.moveTo(0, 0); path.lineTo(SK_Scalar1, 0); path.lineTo(SK_Scalar1, SK_Scalar1); path.lineTo(0, SK_Scalar1); SkPathMeasure meas(path, true); SkScalar length = meas.getLength(); SkASSERT(length == SK_Scalar1*4); path.reset(); path.moveTo(0, 0); path.lineTo(SK_Scalar1*3, SK_Scalar1*4); meas.setPath(&path, false); length = meas.getLength(); REPORTER_ASSERT(reporter, length == SK_Scalar1*5); path.reset(); path.addCircle(0, 0, SK_Scalar1); meas.setPath(&path, true); length = meas.getLength(); // SkDebugf("circle arc-length = %g\n", length); for (int i = 0; i < 8; i++) { SkScalar d = length * i / 8; SkPoint p; SkVector v; meas.getPosTan(d, &p, &v); #if 0 SkDebugf("circle arc-length=%g, pos[%g %g] tan[%g %g]\n", d, p.fX, p.fY, v.fX, v.fY); #endif } // Test the behavior following a close not followed by a move. path.reset(); path.lineTo(SK_Scalar1, 0); path.lineTo(SK_Scalar1, SK_Scalar1); path.lineTo(0, SK_Scalar1); path.close(); path.lineTo(-SK_Scalar1, 0); meas.setPath(&path, false); length = meas.getLength(); REPORTER_ASSERT(reporter, length == SK_Scalar1 * 4); meas.nextContour(); length = meas.getLength(); REPORTER_ASSERT(reporter, length == SK_Scalar1); SkPoint position; SkVector tangent; REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(position.fX, -SK_ScalarHalf, SK_Scalar1 * 0.0001)); REPORTER_ASSERT(reporter, position.fY == 0); REPORTER_ASSERT(reporter, tangent.fX == -SK_Scalar1); REPORTER_ASSERT(reporter, tangent.fY == 0); // Test degenerate paths path.reset(); path.moveTo(0, 0); path.lineTo(0, 0); path.lineTo(SK_Scalar1, 0); path.quadTo(SK_Scalar1, 0, SK_Scalar1, 0); path.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1 * 2); path.cubicTo(SK_Scalar1, SK_Scalar1 * 2, SK_Scalar1, SK_Scalar1 * 2, SK_Scalar1, SK_Scalar1 * 2); path.cubicTo(SK_Scalar1*2, SK_Scalar1 * 2, SK_Scalar1*3, SK_Scalar1 * 2, SK_Scalar1*4, SK_Scalar1 * 2); meas.setPath(&path, false); length = meas.getLength(); REPORTER_ASSERT(reporter, length == SK_Scalar1 * 6); REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(position.fX, SK_ScalarHalf, SK_Scalar1 * 0.0001)); REPORTER_ASSERT(reporter, position.fY == 0); REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1); REPORTER_ASSERT(reporter, tangent.fY == 0); REPORTER_ASSERT(reporter, meas.getPosTan(SK_Scalar1 * 2.5f, &position, &tangent)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(position.fX, SK_Scalar1, SK_Scalar1 * 0.0001)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(position.fY, SK_Scalar1 * 1.5f)); REPORTER_ASSERT(reporter, tangent.fX == 0); REPORTER_ASSERT(reporter, tangent.fY == SK_Scalar1); REPORTER_ASSERT(reporter, meas.getPosTan(SK_Scalar1 * 4.5f, &position, &tangent)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(position.fX, SK_Scalar1 * 2.5f, SK_Scalar1 * 0.0001)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(position.fY, SK_Scalar1 * 2.0f, SK_Scalar1 * 0.0001)); REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1); REPORTER_ASSERT(reporter, tangent.fY == 0); path.reset(); path.moveTo(0, 0); path.lineTo(SK_Scalar1, 0); path.moveTo(SK_Scalar1, SK_Scalar1); path.moveTo(SK_Scalar1 * 2, SK_Scalar1 * 2); path.lineTo(SK_Scalar1, SK_Scalar1 * 2); meas.setPath(&path, false); length = meas.getLength(); REPORTER_ASSERT(reporter, length == SK_Scalar1); REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(position.fX, SK_ScalarHalf, SK_Scalar1 * 0.0001)); REPORTER_ASSERT(reporter, position.fY == 0); REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1); REPORTER_ASSERT(reporter, tangent.fY == 0); meas.nextContour(); length = meas.getLength(); REPORTER_ASSERT(reporter, length == SK_Scalar1); REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(position.fX, SK_Scalar1 * 1.5f, SK_Scalar1 * 0.0001)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(position.fY, SK_Scalar1 * 2.0f, SK_Scalar1 * 0.0001)); REPORTER_ASSERT(reporter, tangent.fX == -SK_Scalar1); REPORTER_ASSERT(reporter, tangent.fY == 0); }
SkPixelRef* SkCreateRLEPixelRef(const SkBitmap& src) { if (SkBitmap::kIndex8_Config != src.config() && SkBitmap::kA8_Config != src.config()) { return NULL; } size_t maxPacked = SkPackBits::ComputeMaxSize8(src.width()); // estimate the rle size based on the original size size_t size = src.getSize() >> 3; if (size < maxPacked) { size = maxPacked; } ChunkRLEPixels* rlePixels = SkNEW_ARGS(ChunkRLEPixels, (src.width(), src.height(), size)); uint8_t* dstRow = NULL; size_t free = 0; size_t totalPacked = 0; for (int y = 0; y < src.height(); y++) { const uint8_t* srcRow = src.getAddr8(0, y); if (free < maxPacked) { dstRow = (uint8_t*)rlePixels->fStorage.allocThrow(size); free = size; } size_t packedSize = SkPackBits::Pack8(srcRow, src.width(), dstRow); SkASSERT(packedSize <= free); rlePixels->setPackedAtY(y, dstRow); dstRow += packedSize; free -= packedSize; totalPacked += packedSize; } //#ifdef SK_DEBUG #if 0 // test uint8_t* buffer = new uint8_t[src.width()]; for (int y = 0; y < src.height(); y++) { const uint8_t* srcRow = src.getAddr8(0, y); SkPackBits::Unpack8(buffer, 0, src.width(), rlePixels->packedAtY(y)); int n = memcmp(buffer, srcRow, src.width()); if (n) { SkDebugf("----- memcmp returned %d on line %d\n", n, y); } SkASSERT(n == 0); } delete[] buffer; size_t totalAlloc = src.height() * sizeof(uint8_t*) + totalPacked; SkDebugf("--- RLE: orig [%d %d] %d, rle %d %d savings %g\n", src.width(), src.height(), src.getSize(), src.height() * sizeof(uint8_t*), totalPacked, (float)totalAlloc / src.getSize()); #endif // transfer ownership of rlePixels to our pixelref return SkNEW_ARGS(RLEPixelRef, (rlePixels, src.getColorTable())); }
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]; SkDPoint t1[3], t2[3]; bool toggle = true; 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); double dist[3][3]; dist[1][1] = t1[1].distance(t2[1]); int best_i = 1, best_j = 1; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { if (i == 1 && j == 1) { continue; } dist[i][j] = t1[i].distance(t2[j]); if (dist[best_i][best_j] > dist[i][j]) { best_i = i; best_j = j; } } } if (best_i == 0) { t1Seed -= t1Step; } else if (best_i == 2) { t1Seed += t1Step; } if (best_j == 0) { t2Seed -= t2Step; } else if (best_j == 2) { t2Seed += t2Step; } if (best_i == 1 && best_j == 1) { if ((toggle ^= true)) { t1Step /= 2; } else { t2Step /= 2; } } } while (!t1[1].approximatelyEqual(t2[1])); t1Step = t2Step = 0.1; double t10 = t1Seed - t1Step * 2; double t12 = t1Seed + t1Step * 2; double t20 = t2Seed - t2Step * 2; double t22 = t2Seed + t2Step * 2; SkDPoint test; while (!approximately_zero(t1Step)) { test = cubic1.ptAtT(t10); t10 += t1[1].approximatelyEqual(test) ? -t1Step : t1Step; t1Step /= 2; } t1Step = 0.1; while (!approximately_zero(t1Step)) { test = cubic1.ptAtT(t12); t12 -= t1[1].approximatelyEqual(test) ? -t1Step : t1Step; t1Step /= 2; } while (!approximately_zero(t2Step)) { test = cubic2.ptAtT(t20); t20 += t2[1].approximatelyEqual(test) ? -t2Step : t2Step; t2Step /= 2; } t2Step = 0.1; while (!approximately_zero(t2Step)) { test = cubic2.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); 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); 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 }
static int filter_picture(const SkString& inFile, const SkString& outFile) { SkAutoTDelete<SkPicture> inPicture; SkFILEStream inStream(inFile.c_str()); if (inStream.isValid()) { inPicture.reset(SkPicture::CreateFromStream(&inStream)); } if (NULL == inPicture.get()) { SkDebugf("Could not read file %s\n", inFile.c_str()); return -1; } int localCount[SK_ARRAY_COUNT(gOptTable)]; memset(localCount, 0, sizeof(localCount)); SkDebugCanvas debugCanvas(inPicture->width(), inPicture->height()); debugCanvas.setBounds(inPicture->width(), inPicture->height()); inPicture->draw(&debugCanvas); // delete the initial save and restore since replaying the commands will // re-add them if (debugCanvas.getSize() > 1) { debugCanvas.deleteDrawCommandAt(0); debugCanvas.deleteDrawCommandAt(debugCanvas.getSize()-1); } bool changed = true; int numBefore = debugCanvas.getSize(); while (changed) { changed = false; for (int i = 0; i < debugCanvas.getSize(); ++i) { for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) { if ((*gOptTable[opt].fCheck)(&debugCanvas, i)) { (*gOptTable[opt].fApply)(&debugCanvas, i); ++gOptTable[opt].fNumTimesApplied; ++localCount[opt]; if (debugCanvas.getSize() == i) { // the optimization removed all the remaining operations break; } opt = 0; // try all the opts all over again changed = true; } } } } int numAfter = debugCanvas.getSize(); if (!outFile.isEmpty()) { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(inPicture->width(), inPicture->height(), NULL, 0); debugCanvas.draw(canvas); SkAutoTUnref<SkPicture> outPicture(recorder.endRecording()); SkFILEWStream outStream(outFile.c_str()); outPicture->serialize(&outStream); } bool someOptFired = false; for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) { if (0 != localCount[opt]) { SkDebugf("%d: %d ", opt, localCount[opt]); someOptFired = true; } } if (!someOptFired) { SkDebugf("No opts fired\n"); } else { SkDebugf("\t before: %d after: %d delta: %d\n", numBefore, numAfter, numBefore-numAfter); } return 0; }
virtual void allocateBuffer(uint8* &buffer, int32 buffersize) { ++fCount; // we double the allocation to work around bug when height is odd buffer = (uint8*)sk_malloc_throw(buffersize << 1); SkDebugf("--- pvjpeg alloc [%d] %d addr=%p\n", fCount, buffersize, buffer); }
int tool_main(int argc, char** argv) { #if SK_ENABLE_INST_COUNT gPrintInstCount = true; #endif SkGraphics::Init(); if (argc < 3) { usage(); return -1; } SkString inFile, outFile, inDir, outDir; char* const* stop = argv + argc; for (++argv; argv < stop; ++argv) { if (strcmp(*argv, "-i") == 0) { argv++; if (argv < stop && **argv) { inFile.set(*argv); } else { SkDebugf("missing arg for -i\n"); usage(); return -1; } } else if (strcmp(*argv, "--input-dir") == 0) { argv++; if (argv < stop && **argv) { inDir.set(*argv); } else { SkDebugf("missing arg for --input-dir\n"); usage(); return -1; } } else if (strcmp(*argv, "--output-dir") == 0) { argv++; if (argv < stop && **argv) { outDir.set(*argv); } else { SkDebugf("missing arg for --output-dir\n"); usage(); return -1; } } else if (strcmp(*argv, "-o") == 0) { argv++; if (argv < stop && **argv) { outFile.set(*argv); } else { SkDebugf("missing arg for -o\n"); usage(); return -1; } } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) { usage(); return 0; } else { SkDebugf("unknown arg %s\n", *argv); usage(); return -1; } } SkOSFile::Iter iter(inDir.c_str(), "skp"); SkString inputFilename, outputFilename; if (iter.next(&inputFilename)) { do { inFile = SkOSPath::SkPathJoin(inDir.c_str(), inputFilename.c_str()); if (!outDir.isEmpty()) { outFile = SkOSPath::SkPathJoin(outDir.c_str(), inputFilename.c_str()); } SkDebugf("Executing %s\n", inputFilename.c_str()); filter_picture(inFile, outFile); } while(iter.next(&inputFilename)); } else if (!inFile.isEmpty()) { filter_picture(inFile, outFile); } else { usage(); return -1; } for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) { SkDebugf("opt %d: %d\n", opt, gOptTable[opt].fNumTimesApplied); } SkGraphics::Term(); return 0; }
virtual void deallocateBuffer(uint8 *buffer) { SkDebugf("--- pvjpeg free [%d] addr=%p\n", fCount, buffer); --fCount; sk_free(buffer); }
SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList, SkOpAngle::IncludeType angleIncludeType, bool* firstContour, int* indexPtr, int* endIndexPtr, SkPoint* topLeft, bool* unsortable, bool* done, bool* onlyVertical, bool firstPass) { SkOpSegment* current = findTopSegment(contourList, indexPtr, endIndexPtr, topLeft, unsortable, done, firstPass); if (!current) { return NULL; } const int startIndex = *indexPtr; const int endIndex = *endIndexPtr; if (*firstContour) { current->initWinding(startIndex, endIndex, angleIncludeType); *firstContour = false; return current; } int minIndex = SkMin32(startIndex, endIndex); int sumWinding = current->windSum(minIndex); if (sumWinding == SK_MinS32) { int index = endIndex; int oIndex = startIndex; do { const SkOpSpan& span = current->span(index); if ((oIndex < index ? span.fFromAngle : span.fToAngle) == NULL) { current->addSimpleAngle(index); } sumWinding = current->computeSum(oIndex, index, angleIncludeType); SkTSwap(index, oIndex); } while (sumWinding == SK_MinS32 && index == startIndex); } if (sumWinding != SK_MinS32 && sumWinding != SK_NaN32) { return current; } int contourWinding; int oppContourWinding = 0; // the simple upward projection of the unresolved points hit unsortable angles // shoot rays at right angles to the segment to find its winding, ignoring angle cases bool tryAgain; double tHit; SkScalar hitDx = 0; SkScalar hitOppDx = 0; // keep track of subsequent returns to detect infinite loops SkTDArray<SortableTop> sortableTops; do { // if current is vertical, find another candidate which is not // if only remaining candidates are vertical, then they can be marked done SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0); skipVertical(contourList, ¤t, indexPtr, endIndexPtr); SkASSERT(current); // FIXME: if null, all remaining are vertical SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0); tryAgain = false; contourWinding = rightAngleWinding(contourList, ¤t, indexPtr, endIndexPtr, &tHit, &hitDx, &tryAgain, onlyVertical, false); if (tryAgain) { bool giveUp = false; int count = sortableTops.count(); for (int index = 0; index < count; ++index) { const SortableTop& prev = sortableTops[index]; if (giveUp) { prev.fSegment->markDoneFinal(prev.fIndex); } else if (prev.fSegment == current && (prev.fIndex == *indexPtr || prev.fEndIndex == *endIndexPtr)) { // remaining edges are non-vertical and cannot have their winding computed // mark them as done and return, and hope that assembly can fill the holes giveUp = true; index = -1; } } if (giveUp) { *done = true; return NULL; } } SortableTop* sortableTop = sortableTops.append(); sortableTop->fSegment = current; sortableTop->fIndex = *indexPtr; sortableTop->fEndIndex = *endIndexPtr; #if DEBUG_SORT SkDebugf("%s current=%d index=%d endIndex=%d tHit=%1.9g hitDx=%1.9g try=%d vert=%d\n", __FUNCTION__, current->debugID(), *indexPtr, *endIndexPtr, tHit, hitDx, tryAgain, *onlyVertical); #endif if (*onlyVertical) { return current; } if (tryAgain) { continue; } if (angleIncludeType < SkOpAngle::kBinarySingle) { break; } oppContourWinding = rightAngleWinding(contourList, ¤t, indexPtr, endIndexPtr, &tHit, &hitOppDx, &tryAgain, NULL, true); } while (tryAgain); bool success = current->initWinding(*indexPtr, *endIndexPtr, tHit, contourWinding, hitDx, oppContourWinding, hitOppDx); if (current->done()) { return NULL; } else if (!success) { // check if the span has a valid winding int min = SkTMin(*indexPtr, *endIndexPtr); const SkOpSpan& span = current->span(min); if (span.fWindSum == SK_MinS32) { return NULL; } } return current; }
static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[]) { const char* value = dom.findAttr(node, attr); if (value) SkDebugf("unknown attribute %s=\"%s\"\n", attr, value); }
/* check start and end of each contour if not the same, record them match them up connect closest reassemble contour pieces into new path */ void Assemble(const SkPathWriter& path, SkPathWriter* simple) { #if DEBUG_PATH_CONSTRUCTION SkDebugf("%s\n", __FUNCTION__); #endif SkTArray<SkOpContour> contours; SkOpEdgeBuilder builder(path, contours); builder.finish(); int count = contours.count(); int outer; SkTArray<int, true> runs(count); // indices of partial contours for (outer = 0; outer < count; ++outer) { const SkOpContour& eContour = contours[outer]; const SkPoint& eStart = eContour.start(); const SkPoint& eEnd = eContour.end(); #if DEBUG_ASSEMBLE SkDebugf("%s contour", __FUNCTION__); if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) { SkDebugf("[%d]", runs.count()); } else { SkDebugf(" "); } SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n", eStart.fX, eStart.fY, eEnd.fX, eEnd.fY); #endif if (SkDPoint::ApproximatelyEqual(eStart, eEnd)) { eContour.toPath(simple); continue; } runs.push_back(outer); } count = runs.count(); if (count == 0) { return; } SkTArray<int, true> sLink, eLink; sLink.push_back_n(count); eLink.push_back_n(count); int rIndex, iIndex; for (rIndex = 0; rIndex < count; ++rIndex) { sLink[rIndex] = eLink[rIndex] = SK_MaxS32; } const int ends = count * 2; // all starts and ends const int entries = (ends - 1) * count; // folded triangle : n * (n - 1) / 2 SkTArray<double, true> distances; distances.push_back_n(entries); for (rIndex = 0; rIndex < ends - 1; ++rIndex) { outer = runs[rIndex >> 1]; const SkOpContour& oContour = contours[outer]; const SkPoint& oPt = rIndex & 1 ? oContour.end() : oContour.start(); const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2) * ends - rIndex - 1; for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) { int inner = runs[iIndex >> 1]; const SkOpContour& iContour = contours[inner]; const SkPoint& iPt = iIndex & 1 ? iContour.end() : iContour.start(); double dx = iPt.fX - oPt.fX; double dy = iPt.fY - oPt.fY; double dist = dx * dx + dy * dy; distances[row + iIndex] = dist; // oStart distance from iStart } } SkTArray<int, true> sortedDist; sortedDist.push_back_n(entries); for (rIndex = 0; rIndex < entries; ++rIndex) { sortedDist[rIndex] = rIndex; } SkTQSort<int>(sortedDist.begin(), sortedDist.end() - 1, DistanceLessThan(distances.begin())); int remaining = count; // number of start/end pairs for (rIndex = 0; rIndex < entries; ++rIndex) { int pair = sortedDist[rIndex]; int row = pair / ends; int col = pair - row * ends; int thingOne = row < col ? row : ends - row - 2; int ndxOne = thingOne >> 1; bool endOne = thingOne & 1; int* linkOne = endOne ? eLink.begin() : sLink.begin(); if (linkOne[ndxOne] != SK_MaxS32) { continue; } int thingTwo = row < col ? col : ends - row + col - 1; int ndxTwo = thingTwo >> 1; bool endTwo = thingTwo & 1; int* linkTwo = endTwo ? eLink.begin() : sLink.begin(); if (linkTwo[ndxTwo] != SK_MaxS32) { continue; } SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]); bool flip = endOne == endTwo; linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo; linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne; if (!--remaining) { break; } } SkASSERT(!remaining); #if DEBUG_ASSEMBLE for (rIndex = 0; rIndex < count; ++rIndex) { int s = sLink[rIndex]; int e = eLink[rIndex]; SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e', s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e); } #endif rIndex = 0; do { bool forward = true; bool first = true; int sIndex = sLink[rIndex]; SkASSERT(sIndex != SK_MaxS32); sLink[rIndex] = SK_MaxS32; int eIndex; if (sIndex < 0) { eIndex = sLink[~sIndex]; sLink[~sIndex] = SK_MaxS32; } else { eIndex = eLink[sIndex]; eLink[sIndex] = SK_MaxS32; } SkASSERT(eIndex != SK_MaxS32); #if DEBUG_ASSEMBLE SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e', sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e', eIndex < 0 ? ~eIndex : eIndex); #endif do { outer = runs[rIndex]; const SkOpContour& contour = contours[outer]; if (first) { first = false; const SkPoint* startPtr = &contour.start(); simple->deferredMove(startPtr[0]); } if (forward) { contour.toPartialForward(simple); } else { contour.toPartialBackward(simple); } #if DEBUG_ASSEMBLE SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex, eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex, sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)); #endif if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) { simple->close(); break; } if (forward) { eIndex = eLink[rIndex]; SkASSERT(eIndex != SK_MaxS32); eLink[rIndex] = SK_MaxS32; if (eIndex >= 0) { SkASSERT(sLink[eIndex] == rIndex); sLink[eIndex] = SK_MaxS32; } else { SkASSERT(eLink[~eIndex] == ~rIndex); eLink[~eIndex] = SK_MaxS32; } } else { eIndex = sLink[rIndex]; SkASSERT(eIndex != SK_MaxS32); sLink[rIndex] = SK_MaxS32; if (eIndex >= 0) { SkASSERT(eLink[eIndex] == rIndex); eLink[eIndex] = SK_MaxS32; } else { SkASSERT(sLink[~eIndex] == ~rIndex); sLink[~eIndex] = SK_MaxS32; } } rIndex = eIndex; if (rIndex < 0) { forward ^= 1; rIndex = ~rIndex; } } while (true); for (rIndex = 0; rIndex < count; ++rIndex) { if (sLink[rIndex] != SK_MaxS32) { break; } } } while (rIndex < count); #if DEBUG_ASSEMBLE for (rIndex = 0; rIndex < count; ++rIndex) { SkASSERT(sLink[rIndex] == SK_MaxS32); SkASSERT(eLink[rIndex] == SK_MaxS32); } #endif }
void SkScriptEngine2::decompile(const unsigned char* start, size_t length) { SkASSERT(length > 0); const unsigned char* opCode = start; do { SkASSERT((size_t)(opCode - start) < length); SkScriptEngine2::TypeOp op = (SkScriptEngine2::TypeOp) *opCode++; SkASSERT((size_t)op < gOpNamesSize); SkDebugf("%d: %s", opCode - start - 1, gOpNames[op].fName); switch (op) { case SkScriptEngine2::kCallback: { int index; memcpy(&index, opCode, sizeof(index)); opCode += sizeof(index); SkDebugf(" index: %d", index); } break; case SkScriptEngine2::kFunctionCall: case SkScriptEngine2::kMemberOp: case SkScriptEngine2::kPropertyOp: { size_t ref; memcpy(&ref, opCode, sizeof(ref)); opCode += sizeof(ref); SkDebugf(" ref: %d", ref); } break; case SkScriptEngine2::kIntegerAccumulator: case SkScriptEngine2::kIntegerOperand: { int32_t integer; memcpy(&integer, opCode, sizeof(integer)); opCode += sizeof(int32_t); SkDebugf(" integer: %d", integer); } break; case SkScriptEngine2::kScalarAccumulator: case SkScriptEngine2::kScalarOperand: { SkScalar scalar; memcpy(&scalar, opCode, sizeof(scalar)); opCode += sizeof(SkScalar); #ifdef SK_CAN_USE_FLOAT SkDebugf(" scalar: %g", SkScalarToFloat(scalar)); #else SkDebugf(" scalar: %x", scalar); #endif } break; case SkScriptEngine2::kStringAccumulator: case SkScriptEngine2::kStringOperand: { int size; SkString* strPtr = new SkString(); memcpy(&size, opCode, sizeof(size)); opCode += sizeof(size); strPtr->set((char*) opCode, size); opCode += size; SkDebugf(" string: %s", strPtr->c_str()); delete strPtr; } break; case SkScriptEngine2::kBoxToken: { SkOperand2::OpType type; memcpy(&type, opCode, sizeof(type)); opCode += sizeof(type); size_t index = 0; if (type == 0) SkDebugf(" type: %s", gOperandNames[index].fName); else { while (type != 0) { SkASSERT(index + 1 < gOperandNamesSize); if (type & (1 << index)) { type = (SkOperand2::OpType) (type & ~(1 << index)); SkDebugf(" type: %s", gOperandNames[index + 1].fName); } index++; } } } break; case SkScriptEngine2::kIfOp: case SkScriptEngine2::kLogicalAndInt: case SkScriptEngine2::kElseOp: case SkScriptEngine2::kLogicalOrInt: { int size; memcpy(&size, opCode, sizeof(size)); opCode += sizeof(size); SkDebugf(" offset (address): %d (%d)", size, opCode - start + size); } break; case SkScriptEngine2::kEnd: goto done; case SkScriptEngine2::kNop: SkASSERT(0); default: break; } SkDebugf("\n"); } while (true); done: SkDebugf("\n"); }
DEF_TEST(SkpSkGr, reporter) { SkTArray<TestResult, true> errors; if (!initTest()) { return; } SkpSkGrThreadState state; state.init(0); int smallCount = 0; for (int dirNo = 1; dirNo <= 100; ++dirNo) { SkString pictDir = make_in_dir_name(dirNo); SkASSERT(pictDir.size()); if (reporter->verbose()) { SkDebugf("dirNo=%d\n", dirNo); } SkOSFile::Iter iter(pictDir.c_str(), "skp"); SkString filename; int testCount = 0; PreParser preParser(dirNo); SkFILEWStream statusStream(makeStatusString(dirNo).c_str()); while (iter.next(&filename)) { for (size_t index = 0; index < skipOverSkGrCount; ++index) { if (skipOverSkGr[index].directory == dirNo && strcmp(filename.c_str(), skipOverSkGr[index].filename) == 0) { goto skipOver; } } if (preParser.match(filename, &statusStream, &state.fResult)) { addError(&state); ++testCount; goto checkEarlyExit; } if (state.fSmallestError > 5000000) { goto breakOut; } { TestResult& result = state.fResult; result.test(dirNo, filename); SkString outStr(result.status()); statusStream.write(outStr.c_str(), outStr.size()); statusStream.flush(); if (1) { SkDebugf("%s", outStr.c_str()); } bool noMatch = addError(&state); if (noMatch) { smallCount = 0; } else if (++smallCount > 10000) { goto breakOut; } } ++testCount; if (reporter->verbose()) { if (testCount % 100 == 0) { SkDebugf("#%d\n", testCount); } } skipOver: reporter->bumpTestCount(); checkEarlyExit: if (1 && testCount == 20) { break; } } } breakOut: if (reporter->verbose()) { for (int index = 0; index < state.fFoundCount; ++index) { SkDebugf("%d %s %d\n", state.fDirsFound[index], state.fFilesFound[index], state.fError[index]); } } for (int index = 0; index < state.fFoundCount; ++index) { TestResult::Test(state.fDirsFound[index], state.fFilesFound[index], kEncodeFiles, reporter->verbose()); if (reporter->verbose()) SkDebugf("+"); } }
// A function used to determine at runtime if the target CPU supports // the ARM NEON instruction set. This implementation is Linux-specific. static bool sk_cpu_arm_check_neon(void) { bool result = false; #if NEON_DEBUG // Allow forcing the mode through the environment during debugging. # ifdef SK_BUILD_FOR_ANDROID // On Android, we use a system property # define PROP_NAME "debug.skia.arm_neon_mode" char prop[PROP_VALUE_MAX]; if (__system_property_get(PROP_NAME, prop) > 0) { # else # define PROP_NAME "SKIA_ARM_NEON_MODE" // On ARM Linux, we use an environment variable const char* prop = getenv(PROP_NAME); if (prop != NULL) { # endif SkDebugf("%s: %s", PROP_NAME, prop); if (!strcmp(prop, "1")) { SkDebugf("Forcing ARM Neon mode to full!\n"); return true; } if (!strcmp(prop, "0")) { SkDebugf("Disabling ARM NEON mode\n"); return false; } } SkDebugf("Running dynamic CPU feature detection\n"); #endif // There is no user-accessible CPUID instruction on ARM that we can use. // Instead, we must parse /proc/cpuinfo and look for the 'neon' feature. // For example, here's a typical output (Nexus S running ICS 4.0.3): /* Processor : ARMv7 Processor rev 2 (v7l) BogoMIPS : 994.65 Features : swp half thumb fastmult vfp edsp thumbee neon vfpv3 CPU implementer : 0x41 CPU architecture: 7 CPU variant : 0x2 CPU part : 0xc08 CPU revision : 2 Hardware : herring Revision : 000b Serial : 3833c77d6dc000ec */ char buffer[4096]; // If we fail any of the following, assume we don't have NEON instructions // This allows us to return immediately in case of error. result = false; do { // open /proc/cpuinfo int fd = TEMP_FAILURE_RETRY(open("/proc/cpuinfo", O_RDONLY)); if (fd < 0) { SkDebugf("Could not open /proc/cpuinfo: %s\n", strerror(errno)); break; } // Read the file. To simplify our search, we're going to place two // sentinel '\n' characters: one at the start of the buffer, and one at // the end. This means we reserve the first and last buffer bytes. buffer[0] = '\n'; int size = TEMP_FAILURE_RETRY(read(fd, buffer+1, sizeof(buffer)-2)); close(fd); if (size < 0) { // should not happen SkDebugf("Could not read /proc/cpuinfo: %s\n", strerror(errno)); break; } SkDebugf("START /proc/cpuinfo:\n%.*s\nEND /proc/cpuinfo\n", size, buffer+1); // Compute buffer limit, and place final sentinel char* buffer_end = buffer + 1 + size; buffer_end[0] = '\n'; // Now, find a line that starts with "Features", i.e. look for // '\nFeatures ' in our buffer. const char features[] = "\nFeatures\t"; const size_t features_len = sizeof(features)-1; char* line = (char*) memmem(buffer, buffer_end - buffer, features, features_len); if (line == NULL) { // Weird, no Features line, bad kernel? SkDebugf("Could not find a line starting with 'Features'" "in /proc/cpuinfo ?\n"); break; } line += features_len; // Skip the "\nFeatures\t" prefix // Find the end of the current line char* line_end = (char*) memchr(line, '\n', buffer_end - line); if (line_end == NULL) line_end = buffer_end; // Now find an instance of 'neon' in the flags list. We want to // ensure it's only 'neon' and not something fancy like 'noneon' // so check that it follows a space. const char neon[] = " neon"; const size_t neon_len = sizeof(neon)-1; const char* flag = (const char*) memmem(line, line_end - line, neon, neon_len); if (flag == NULL) break; // Ensure it is followed by a space or a newline. if (flag[neon_len] != ' ' && flag[neon_len] != '\n') break; // Fine, we support Arm NEON ! result = true; } while (0); if (result) { SkDebugf("Device supports ARM NEON instructions!\n"); } else { SkDebugf("Device does NOT support ARM NEON instructions!\n"); } return result; } static pthread_once_t sOnce; static bool sHasArmNeon; // called through pthread_once() void sk_cpu_arm_probe_features(void) { sHasArmNeon = sk_cpu_arm_check_neon(); } bool sk_cpu_arm_has_neon(void) { pthread_once(&sOnce, sk_cpu_arm_probe_features); return sHasArmNeon; }
bool GrDrawTarget::setupDstReadIfNecessary(const GrPipelineBuilder& pipelineBuilder, const GrClip& clip, const GrPipelineOptimizations& optimizations, GrXferProcessor::DstTexture* dstTexture, const SkRect& batchBounds) { SkRect bounds = batchBounds; bounds.outset(0.5f, 0.5f); if (!pipelineBuilder.willXPNeedDstTexture(*this->caps(), optimizations)) { return true; } GrRenderTarget* rt = pipelineBuilder.getRenderTarget(); if (this->caps()->textureBarrierSupport()) { if (GrTexture* rtTex = rt->asTexture()) { // The render target is a texture, so we can read from it directly in the shader. The XP // will be responsible to detect this situation and request a texture barrier. dstTexture->setTexture(rtTex); dstTexture->setOffset(0, 0); return true; } } SkIRect copyRect; clip.getConservativeBounds(rt->width(), rt->height(), ©Rect); SkIRect drawIBounds; bounds.roundOut(&drawIBounds); if (!copyRect.intersect(drawIBounds)) { #ifdef SK_DEBUG GrCapsDebugf(this->caps(), "Missed an early reject. " "Bailing on draw from setupDstReadIfNecessary.\n"); #endif return false; } // MSAA consideration: When there is support for reading MSAA samples in the shader we could // have per-sample dst values by making the copy multisampled. GrSurfaceDesc desc; if (!fGpu->initCopySurfaceDstDesc(rt, &desc)) { desc.fOrigin = kDefault_GrSurfaceOrigin; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fConfig = rt->config(); } desc.fWidth = copyRect.width(); desc.fHeight = copyRect.height(); static const uint32_t kFlags = 0; SkAutoTUnref<GrTexture> copy(fResourceProvider->createApproxTexture(desc, kFlags)); if (!copy) { SkDebugf("Failed to create temporary copy of destination texture.\n"); return false; } SkIPoint dstPoint = {0, 0}; this->copySurface(copy, rt, copyRect, dstPoint); dstTexture->setTexture(copy); dstTexture->setOffset(copyRect.fLeft, copyRect.fTop); return true; }
static void setup_benchmark(sk_tools::PictureBenchmark* benchmark) { sk_tools::PictureRenderer::DrawFilterFlags drawFilters[SkDrawFilter::kTypeCount]; sk_bzero(drawFilters, sizeof(drawFilters)); if (FLAGS_filter.count() > 0) { const char* filters = FLAGS_filter[0]; const char* colon = strchr(filters, ':'); if (colon) { int32_t type = -1; size_t typeLen = colon - filters; for (size_t tIndex = 0; tIndex < kFilterTypesCount; ++tIndex) { if (typeLen == strlen(gFilterTypes[tIndex]) && !strncmp(filters, gFilterTypes[tIndex], typeLen)) { type = SkToS32(tIndex); break; } } if (type < 0) { SkString err; err.printf("Unknown type for --filter %s\n", filters); gLogger.logError(err); exit(-1); } int flag = -1; size_t flagLen = strlen(filters) - typeLen - 1; for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) { if (flagLen == strlen(gFilterFlags[fIndex]) && !strncmp(colon + 1, gFilterFlags[fIndex], flagLen)) { flag = 1 << fIndex; break; } } if (flag < 0) { SkString err; err.printf("Unknown flag for --filter %s\n", filters); gLogger.logError(err); exit(-1); } for (int index = 0; index < SkDrawFilter::kTypeCount; ++index) { if (type != SkDrawFilter::kTypeCount && index != type) { continue; } drawFilters[index] = (sk_tools::PictureRenderer::DrawFilterFlags) (drawFilters[index] | flag); } } else { SkString err; err.printf("Unknown arg for --filter %s : missing colon\n", filters); gLogger.logError(err); exit(-1); } } if (FLAGS_timers.count() > 0) { size_t index = 0; bool timerWall = false; bool truncatedTimerWall = false; bool timerCpu = false; bool truncatedTimerCpu = false; bool timerGpu = false; while (index < strlen(FLAGS_timers[0])) { switch (FLAGS_timers[0][index]) { case 'w': timerWall = true; break; case 'c': timerCpu = true; break; case 'W': truncatedTimerWall = true; break; case 'C': truncatedTimerCpu = true; break; case 'g': timerGpu = true; break; default: SkDebugf("mystery character\n"); break; } index++; } benchmark->setTimersToShow(timerWall, truncatedTimerWall, timerCpu, truncatedTimerCpu, timerGpu); } SkString errorString; SkAutoTUnref<sk_tools::PictureRenderer> renderer(parseRenderer(errorString, kBench_PictureTool)); if (errorString.size() > 0) { gLogger.logError(errorString); } if (NULL == renderer.get()) { exit(-1); } if (FLAGS_timeIndividualTiles) { if (FLAGS_multi > 1) { gLogger.logError("Cannot time individual tiles with more than one thread.\n"); exit(-1); } sk_tools::TiledPictureRenderer* tiledRenderer = renderer->getTiledRenderer(); if (NULL == tiledRenderer) { gLogger.logError("--timeIndividualTiles requires tiled rendering.\n"); exit(-1); } if (!tiledRenderer->supportsTimingIndividualTiles()) { gLogger.logError("This renderer does not support --timeIndividualTiles.\n"); exit(-1); } benchmark->setTimeIndividualTiles(true); } if (FLAGS_readPath.count() < 1) { gLogger.logError(".skp files or directories are required.\n"); exit(-1); } renderer->setDrawFilters(drawFilters, filtersName(drawFilters)); benchmark->setPrintMin(FLAGS_min); benchmark->setLogPerIter(FLAGS_logPerIter); benchmark->setRenderer(renderer); benchmark->setRepeats(FLAGS_repeat); benchmark->setLogger(&gLogger); }
void ShowFunctionHeader() { SkDebugf("\nstatic void test#(skiatest::Reporter* reporter) {\n"); }
bool GrVkCopyManager::copySurfaceAsDraw(GrVkGpu* gpu, GrSurface* dst, GrSurfaceOrigin dstOrigin, GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect, const SkIPoint& dstPoint, bool canDiscardOutsideDstRect) { // None of our copy methods can handle a swizzle. TODO: Make copySurfaceAsDraw handle the // swizzle. if (gpu->caps()->shaderCaps()->configOutputSwizzle(src->config()) != gpu->caps()->shaderCaps()->configOutputSwizzle(dst->config())) { return false; } GrVkRenderTarget* rt = static_cast<GrVkRenderTarget*>(dst->asRenderTarget()); if (!rt) { return false; } GrVkTexture* srcTex = static_cast<GrVkTexture*>(src->asTexture()); if (!srcTex) { return false; } if (VK_NULL_HANDLE == fVertShaderModule) { SkASSERT(VK_NULL_HANDLE == fFragShaderModule && nullptr == fPipelineLayout && nullptr == fVertexBuffer.get() && nullptr == fUniformBuffer.get()); if (!this->createCopyProgram(gpu)) { SkDebugf("Failed to create copy program.\n"); return false; } } SkASSERT(fPipelineLayout); GrVkResourceProvider& resourceProv = gpu->resourceProvider(); GrVkCopyPipeline* pipeline = resourceProv.findOrCreateCopyPipeline(rt, fShaderStageInfo, fPipelineLayout->layout()); if (!pipeline) { return false; } // UPDATE UNIFORM DESCRIPTOR SET int w = srcRect.width(); int h = srcRect.height(); // dst rect edges in NDC (-1 to 1) int dw = dst->width(); int dh = dst->height(); float dx0 = 2.f * dstPoint.fX / dw - 1.f; float dx1 = 2.f * (dstPoint.fX + w) / dw - 1.f; float dy0 = 2.f * dstPoint.fY / dh - 1.f; float dy1 = 2.f * (dstPoint.fY + h) / dh - 1.f; if (kBottomLeft_GrSurfaceOrigin == dstOrigin) { dy0 = -dy0; dy1 = -dy1; } float sx0 = (float)srcRect.fLeft; float sx1 = (float)(srcRect.fLeft + w); float sy0 = (float)srcRect.fTop; float sy1 = (float)(srcRect.fTop + h); int sh = src->height(); if (kBottomLeft_GrSurfaceOrigin == srcOrigin) { sy0 = sh - sy0; sy1 = sh - sy1; } // src rect edges in normalized texture space (0 to 1). int sw = src->width(); sx0 /= sw; sx1 /= sw; sy0 /= sh; sy1 /= sh; float uniData[] = { dx1 - dx0, dy1 - dy0, dx0, dy0, // posXform sx1 - sx0, sy1 - sy0, sx0, sy0 }; // texCoordXform fUniformBuffer->updateData(gpu, uniData, sizeof(uniData), nullptr); const GrVkDescriptorSet* uniformDS = resourceProv.getUniformDescriptorSet(); SkASSERT(uniformDS); VkDescriptorBufferInfo uniBufferInfo; uniBufferInfo.buffer = fUniformBuffer->buffer(); uniBufferInfo.offset = fUniformBuffer->offset(); uniBufferInfo.range = fUniformBuffer->size(); VkWriteDescriptorSet descriptorWrites; descriptorWrites.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptorWrites.pNext = nullptr; descriptorWrites.dstSet = uniformDS->descriptorSet(); descriptorWrites.dstBinding = GrVkUniformHandler::kGeometryBinding; descriptorWrites.dstArrayElement = 0; descriptorWrites.descriptorCount = 1; descriptorWrites.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; descriptorWrites.pImageInfo = nullptr; descriptorWrites.pBufferInfo = &uniBufferInfo; descriptorWrites.pTexelBufferView = nullptr; GR_VK_CALL(gpu->vkInterface(), UpdateDescriptorSets(gpu->device(), 1, &descriptorWrites, 0, nullptr)); // UPDATE SAMPLER DESCRIPTOR SET const GrVkDescriptorSet* samplerDS = gpu->resourceProvider().getSamplerDescriptorSet(fSamplerDSHandle); GrSamplerState samplerState = GrSamplerState::ClampNearest(); GrVkSampler* sampler = resourceProv.findOrCreateCompatibleSampler( samplerState, GrVkYcbcrConversionInfo()); VkDescriptorImageInfo imageInfo; memset(&imageInfo, 0, sizeof(VkDescriptorImageInfo)); imageInfo.sampler = sampler->sampler(); imageInfo.imageView = srcTex->textureView()->imageView(); imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkWriteDescriptorSet writeInfo; memset(&writeInfo, 0, sizeof(VkWriteDescriptorSet)); writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfo.pNext = nullptr; writeInfo.dstSet = samplerDS->descriptorSet(); writeInfo.dstBinding = 0; writeInfo.dstArrayElement = 0; writeInfo.descriptorCount = 1; writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeInfo.pImageInfo = &imageInfo; writeInfo.pBufferInfo = nullptr; writeInfo.pTexelBufferView = nullptr; GR_VK_CALL(gpu->vkInterface(), UpdateDescriptorSets(gpu->device(), 1, &writeInfo, 0, nullptr)); VkDescriptorSet vkDescSets[] = { uniformDS->descriptorSet(), samplerDS->descriptorSet() }; GrVkRenderTarget* texRT = static_cast<GrVkRenderTarget*>(srcTex->asRenderTarget()); if (texRT) { gpu->resolveRenderTargetNoFlush(texRT); } // TODO: Make tighter bounds and then adjust bounds for origin and granularity if we see // any perf issues with using the whole bounds SkIRect bounds = SkIRect::MakeWH(rt->width(), rt->height()); // Change layouts of rt and texture. We aren't blending so we don't need color attachment read // access for blending. GrVkImage* targetImage = rt->msaaImage() ? rt->msaaImage() : rt; VkAccessFlags dstAccessFlags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; if (!canDiscardOutsideDstRect) { // We need to load the color attachment so need to be able to read it. dstAccessFlags |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; } targetImage->setImageLayout(gpu, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, dstAccessFlags, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, false); srcTex->setImageLayout(gpu, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, false); GrStencilAttachment* stencil = rt->renderTargetPriv().getStencilAttachment(); if (stencil) { GrVkStencilAttachment* vkStencil = (GrVkStencilAttachment*)stencil; // We aren't actually using the stencil but we still load and store it so we need // appropriate barriers. // TODO: Once we refactor surface and how we conntect stencil to RTs, we should not even // have the stencil on this render pass if possible. vkStencil->setImageLayout(gpu, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, false); } VkAttachmentLoadOp loadOp = canDiscardOutsideDstRect ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : VK_ATTACHMENT_LOAD_OP_LOAD; GrVkRenderPass::LoadStoreOps vkColorOps(loadOp, VK_ATTACHMENT_STORE_OP_STORE); GrVkRenderPass::LoadStoreOps vkStencilOps(VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE); const GrVkRenderPass* renderPass; const GrVkResourceProvider::CompatibleRPHandle& rpHandle = rt->compatibleRenderPassHandle(); if (rpHandle.isValid()) { renderPass = gpu->resourceProvider().findRenderPass(rpHandle, vkColorOps, vkStencilOps); } else { renderPass = gpu->resourceProvider().findRenderPass(*rt, vkColorOps, vkStencilOps); } SkASSERT(renderPass->isCompatible(*rt->simpleRenderPass())); GrVkPrimaryCommandBuffer* cmdBuffer = gpu->currentCommandBuffer(); cmdBuffer->beginRenderPass(gpu, renderPass, nullptr, *rt, bounds, true); GrVkSecondaryCommandBuffer* secondary = gpu->cmdPool()->findOrCreateSecondaryCommandBuffer(gpu); if (!secondary) { return false; } secondary->begin(gpu, rt->framebuffer(), renderPass); secondary->bindPipeline(gpu, pipeline); // Uniform DescriptorSet, Sampler DescriptorSet, and vertex shader uniformBuffer SkSTArray<3, const GrVkRecycledResource*> descriptorRecycledResources; descriptorRecycledResources.push_back(uniformDS); descriptorRecycledResources.push_back(samplerDS); descriptorRecycledResources.push_back(fUniformBuffer->resource()); // One sampler, texture view, and texture SkSTArray<3, const GrVkResource*> descriptorResources; descriptorResources.push_back(sampler); descriptorResources.push_back(srcTex->textureView()); descriptorResources.push_back(srcTex->resource()); secondary->bindDescriptorSets(gpu, descriptorRecycledResources, descriptorResources, fPipelineLayout, 0, 2, vkDescSets, 0, nullptr); // Set Dynamic viewport and stencil // We always use one viewport the size of the RT VkViewport viewport; viewport.x = 0.0f; viewport.y = 0.0f; viewport.width = SkIntToScalar(rt->width()); viewport.height = SkIntToScalar(rt->height()); viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; secondary->setViewport(gpu, 0, 1, &viewport); // We assume the scissor is not enabled so just set it to the whole RT VkRect2D scissor; scissor.extent.width = rt->width(); scissor.extent.height = rt->height(); scissor.offset.x = 0; scissor.offset.y = 0; secondary->setScissor(gpu, 0, 1, &scissor); secondary->bindInputBuffer(gpu, 0, fVertexBuffer.get()); secondary->draw(gpu, 4, 1, 0, 0); secondary->end(gpu); cmdBuffer->executeCommands(gpu, secondary); cmdBuffer->endRenderPass(gpu); secondary->unref(gpu); // Release all temp resources which should now be reffed by the cmd buffer pipeline->unref(gpu); uniformDS->unref(gpu); samplerDS->unref(gpu); sampler->unref(gpu); renderPass->unref(gpu); return true; }
void SkApply::dump(SkAnimateMaker* maker) { dumpBase(maker); if (dynamicScope.isEmpty() == false) SkDebugf("dynamicScope=\"%s\" ", dynamicScope.c_str()); if (dontDraw) SkDebugf("dontDraw=\"true\" "); if (begin != 0) //perhaps we want this no matter what? SkDebugf("begin=\"%g\" ", (float) begin/1000.0f); //is this correct? if (interval != (SkMSec) -1) SkDebugf("interval=\"%g\" ", (float) interval/1000.0f); if (steps != -1) SkDebugf("steps=\"%d\" ", steps); if (restore) SkDebugf("restore=\"true\" "); if (transition == kTransition_reverse) SkDebugf("transition=\"reverse\" "); if (mode == kMode_immediate) { SkDebugf("mode=\"immediate\" "); } else if (mode == kMode_create) { SkDebugf("mode=\"create\" "); } bool closedYet = false; SkDisplayList::fIndent += 4; int save = SkDisplayList::fDumpIndex; if (scope) { if (closedYet == false) { SkDebugf(">\n"); closedYet = true; } scope->dump(maker); } int index; // if (fActive) { for (index = 0; index < fAnimators.count(); index++) { if (closedYet == false) { SkDebugf(">\n"); closedYet = true; } SkAnimateBase* animator = fAnimators[index]; animator->dump(maker); // } } SkDisplayList::fIndent -= 4; SkDisplayList::fDumpIndex = save; if (closedYet) dumpEnd(maker); else SkDebugf("/>\n"); }
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; 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; proxies[1] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), dummyDesc, SkBudgeted::kNo, nullptr, 0); if (!proxies[0] || !proxies[1]) { SkDebugf("Could not allocate dummy textures"); return false; } // dummy scissor state GrScissorState scissor; SkRandom random; 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; } 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; }