static SkPDFDict* get_gradient_resource_dict( SkPDFObject* functionShader, SkPDFObject* gState) { SkTDArray<SkPDFObject*> patterns; if (functionShader) { patterns.push(functionShader); } SkTDArray<SkPDFObject*> graphicStates; if (gState) { graphicStates.push(gState); } return SkPDFResourceDict::Create(&graphicStates, &patterns, NULL, NULL); }
// https://www.w3.org/TR/SVG/painting.html#StrokeDasharrayProperty bool SkSVGAttributeParser::parseDashArray(SkSVGDashArray* dashArray) { bool parsedValue = false; if (this->parseExpectedStringToken("none")) { *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kNone); parsedValue = true; } else if (this->parseExpectedStringToken("inherit")) { *dashArray = SkSVGDashArray(SkSVGDashArray::Type::kInherit); parsedValue = true; } else { SkTDArray<SkSVGLength> dashes; for (;;) { SkSVGLength dash; // parseLength() also consumes trailing separators. if (!this->parseLength(&dash)) { break; } dashes.push(dash); parsedValue = true; } if (parsedValue) { *dashArray = SkSVGDashArray(std::move(dashes)); } } return parsedValue && this->parseEOSToken(); }
static bool verify_query(SkIRect query, DataRect rects[], SkTDArray<void*>& found) { SkTDArray<void*> expected; // manually intersect with every rectangle for (int i = 0; i < NUM_RECTS; ++i) { if (SkIRect::IntersectsNoEmptyCheck(query, rects[i].rect)) { expected.push(rects[i].data); } } if (expected.count() != found.count()) { return false; } if (0 == expected.count()) { return true; } // Just cast to long since sorting by the value of the void*'s was being problematic... SkTQSort(reinterpret_cast<long*>(expected.begin()), reinterpret_cast<long*>(expected.end() - 1)); SkTQSort(reinterpret_cast<long*>(found.begin()), reinterpret_cast<long*>(found.end() - 1)); return found == expected; }
void SkRTree::insert(const SkRect boundsArray[], int N) { SkASSERT(0 == fCount); SkTDArray<Branch> branches; branches.setReserve(N); for (int i = 0; i < N; i++) { const SkRect& bounds = boundsArray[i]; if (bounds.isEmpty()) { continue; } Branch* b = branches.push(); b->fBounds = bounds; b->fOpIndex = i; } fCount = branches.count(); if (fCount) { if (1 == fCount) { fNodes.setReserve(1); Node* n = this->allocateNodeAtLevel(0); n->fNumChildren = 1; n->fChildren[0] = branches[0]; fRoot.fSubtree = n; fRoot.fBounds = branches[0].fBounds; } else { fNodes.setReserve(CountNodes(fCount, fAspectRatio)); fRoot = this->bulkLoad(&branches); } } }
static sk_sp<SkPDFStream> get_subset_font_stream( std::unique_ptr<SkStreamAsset> fontAsset, const SkBitSet& glyphUsage, const char* fontName, int ttcIndex) { // Generate glyph id array in format needed by sfntly. // TODO(halcanary): sfntly should take a more compact format. SkTDArray<unsigned> subset; if (!glyphUsage.has(0)) { subset.push(0); // Always include glyph 0. } glyphUsage.exportTo(&subset); unsigned char* subsetFont{nullptr}; sk_sp<SkData> fontData(stream_to_data(std::move(fontAsset))); #if defined(SK_BUILD_FOR_GOOGLE3) // TODO(halcanary): update SK_BUILD_FOR_GOOGLE3 to newest version of Sfntly. (void)ttcIndex; int subsetFontSize = SfntlyWrapper::SubsetFont(fontName, fontData->bytes(), fontData->size(), subset.begin(), subset.count(), &subsetFont); #else (void)fontName; int subsetFontSize = SfntlyWrapper::SubsetFont(ttcIndex, fontData->bytes(), fontData->size(), subset.begin(), subset.count(), &subsetFont); #endif fontData.reset(); subset.reset(); SkASSERT(subsetFontSize > 0 || subsetFont == nullptr); if (subsetFontSize < 1) { return nullptr; } SkASSERT(subsetFont != nullptr); auto subsetStream = sk_make_sp<SkPDFStream>( SkData::MakeWithProc( subsetFont, subsetFontSize, [](const void* p, void*) { delete[] (unsigned char*)p; }, nullptr)); subsetStream->dict()->insertInt("Length1", subsetFontSize); return subsetStream; }
GrTextureStripAtlas* GrTextureStripAtlas::GetAtlas(const GrTextureStripAtlas::Desc& desc) { static SkTDArray<AtlasEntry> gAtlasEntries; static GrTHashTable<AtlasEntry, AtlasHashKey, 8> gAtlasCache; AtlasHashKey key; key.setKeyData(desc.asKey()); AtlasEntry* entry = gAtlasCache.find(key); if (NULL != entry) { return entry->fAtlas; } else { entry = gAtlasEntries.push(); entry->fAtlas = SkNEW_ARGS(GrTextureStripAtlas, (desc)); entry->fKey = key; gAtlasCache.insert(key, entry); return entry->fAtlas; } }
static bool verify_query(SkRect query, SkRect rects[], SkTDArray<unsigned>& found) { SkTDArray<unsigned> expected; // manually intersect with every rectangle for (int i = 0; i < NUM_RECTS; ++i) { if (SkRect::Intersects(query, rects[i])) { expected.push(i); } } if (expected.count() != found.count()) { return false; } if (0 == expected.count()) { return true; } return found == expected; }
void SkPDFDocument::getCountOfFontTypes( int counts[SkAdvancedTypefaceMetrics::kNotEmbeddable_Font + 1]) const { sk_bzero(counts, sizeof(int) * (SkAdvancedTypefaceMetrics::kNotEmbeddable_Font + 1)); SkTDArray<SkFontID> seenFonts; for (int pageNumber = 0; pageNumber < fPages.count(); pageNumber++) { const SkTDArray<SkPDFFont*>& fontResources = fPages[pageNumber]->getFontResources(); for (int font = 0; font < fontResources.count(); font++) { SkFontID fontID = fontResources[font]->typeface()->uniqueID(); if (seenFonts.find(fontID) == -1) { counts[fontResources[font]->getType()]++; seenFonts.push(fontID); } } } }
static void gather_tests() { if (!FLAGS_src.contains("tests")) { return; } for (const skiatest::TestRegistry* r = skiatest::TestRegistry::Head(); r; r = r->next()) { if (!in_shard()) { continue; } // Despite its name, factory() is returning a reference to // link-time static const POD data. const skiatest::Test& test = r->factory(); if (SkCommandLineFlags::ShouldSkip(FLAGS_match, test.name)) { continue; } if (test.needsGpu && gpu_supported()) { (FLAGS_gpu_threading ? gThreadedTests : gGPUTests).push(test); } else if (!test.needsGpu && FLAGS_cpu) { gThreadedTests.push(test); } } }
// https://www.w3.org/TR/SVG/shapes.html#PolygonElementPointsAttribute bool SkSVGAttributeParser::parsePoints(SkSVGPointsType* points) { SkTDArray<SkPoint> pts; bool parsedValue = false; for (;;) { this->parseWSToken(); SkScalar x, y; if (!this->parseScalarToken(&x)) { break; } // comma-wsp: // (wsp+ comma? wsp*) | (comma wsp*) bool wsp = this->parseWSToken(); bool comma = this->parseExpectedStringToken(","); if (!(wsp || comma)) { break; } this->parseWSToken(); if (!this->parseScalarToken(&y)) { break; } pts.push(SkPoint::Make(x, y)); parsedValue = true; } if (parsedValue && this->parseEOSToken()) { *points = pts; return true; } return false; }
// TODO(halcanary): expose notEmbeddableCount in SkDocument void GetCountOfFontTypes( const SkTDArray<SkPDFDevice*>& pageDevices, int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1], int* notSubsettableCount, int* notEmbeddableCount) { sk_bzero(counts, sizeof(int) * (SkAdvancedTypefaceMetrics::kOther_Font + 1)); SkTDArray<SkFontID> seenFonts; int notSubsettable = 0; int notEmbeddable = 0; for (int pageNumber = 0; pageNumber < pageDevices.count(); pageNumber++) { const SkTDArray<SkPDFFont*>& fontResources = pageDevices[pageNumber]->getFontResources(); for (int font = 0; font < fontResources.count(); font++) { SkFontID fontID = fontResources[font]->typeface()->uniqueID(); if (seenFonts.find(fontID) == -1) { counts[fontResources[font]->getType()]++; seenFonts.push(fontID); if (!fontResources[font]->canSubset()) { notSubsettable++; } if (!fontResources[font]->canEmbed()) { notEmbeddable++; } } } } if (notSubsettableCount) { *notSubsettableCount = notSubsettable; } if (notEmbeddableCount) { *notEmbeddableCount = notEmbeddable; } }
// static void SkPDFPage::GeneratePageTree(const SkTDArray<SkPDFPage*>& pages, SkPDFCatalog* catalog, SkTDArray<SkPDFDict*>* pageTree, SkPDFDict** rootNode) { // PDF wants a tree describing all the pages in the document. We arbitrary // choose 8 (kNodeSize) as the number of allowed children. The internal // nodes have type "Pages" with an array of children, a parent pointer, and // the number of leaves below the node as "Count." The leaves are passed // into the method, have type "Page" and need a parent pointer. This method // builds the tree bottom up, skipping internal nodes that would have only // one child. static const int kNodeSize = 8; SkAutoTUnref<SkPDFName> kidsName(new SkPDFName("Kids")); SkAutoTUnref<SkPDFName> countName(new SkPDFName("Count")); SkAutoTUnref<SkPDFName> parentName(new SkPDFName("Parent")); // curNodes takes a reference to its items, which it passes to pageTree. SkTDArray<SkPDFDict*> curNodes; curNodes.setReserve(pages.count()); for (int i = 0; i < pages.count(); i++) { SkSafeRef(pages[i]); curNodes.push(pages[i]); } // nextRoundNodes passes its references to nodes on to curNodes. SkTDArray<SkPDFDict*> nextRoundNodes; nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize); int treeCapacity = kNodeSize; do { for (int i = 0; i < curNodes.count(); ) { if (i > 0 && i + 1 == curNodes.count()) { nextRoundNodes.push(curNodes[i]); break; } SkPDFDict* newNode = new SkPDFDict("Pages"); SkAutoTUnref<SkPDFObjRef> newNodeRef(new SkPDFObjRef(newNode)); SkAutoTUnref<SkPDFArray> kids(new SkPDFArray); kids->reserve(kNodeSize); int count = 0; for (; i < curNodes.count() && count < kNodeSize; i++, count++) { curNodes[i]->insert(parentName.get(), newNodeRef.get()); kids->append(new SkPDFObjRef(curNodes[i]))->unref(); // TODO(vandebo): put the objects in strict access order. // Probably doesn't matter because they are so small. if (curNodes[i] != pages[0]) { pageTree->push(curNodes[i]); // Transfer reference. catalog->addObject(curNodes[i], false); } else { SkSafeUnref(curNodes[i]); catalog->addObject(curNodes[i], true); } } // treeCapacity is the number of leaf nodes possible for the // current set of subtrees being generated. (i.e. 8, 64, 512, ...). // It is hard to count the number of leaf nodes in the current // subtree. However, by construction, we know that unless it's the // last subtree for the current depth, the leaf count will be // treeCapacity, otherwise it's what ever is left over after // consuming treeCapacity chunks. int pageCount = treeCapacity; if (i == curNodes.count()) { pageCount = ((pages.count() - 1) % treeCapacity) + 1; } newNode->insert(countName.get(), new SkPDFInt(pageCount))->unref(); newNode->insert(kidsName.get(), kids.get()); nextRoundNodes.push(newNode); // Transfer reference. } curNodes = nextRoundNodes; nextRoundNodes.rewind(); treeCapacity *= kNodeSize; } while (curNodes.count() > 1); pageTree->push(curNodes[0]); // Transfer reference. catalog->addObject(curNodes[0], false); if (rootNode) { *rootNode = curNodes[0]; } }
// static void SkPDFPage::GeneratePageTree(const SkTDArray<SkPDFPage*>& pages, SkPDFCatalog* catalog, SkTDArray<SkPDFDict*>* pageTree, SkPDFDict** rootNode) { // PDF wants a tree describing all the pages in the document. We arbitrary // choose 8 (kNodeSize) as the number of allowed children. The internal // nodes have type "Pages" with an array of children, a parent pointer, and // the number of leaves below the node as "Count." The leaves are passed // into the method, have type "Page" and need a parent pointer. This method // builds the tree bottom up, skipping internal nodes that would have only // one child. static const int kNodeSize = 8; SkRefPtr<SkPDFName> kidsName = new SkPDFName("Kids"); kidsName->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFName> countName = new SkPDFName("Count"); countName->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFName> parentName = new SkPDFName("Parent"); parentName->unref(); // SkRefPtr and new both took a reference. // curNodes takes a reference to its items, which it passes to pageTree. SkTDArray<SkPDFDict*> curNodes; curNodes.setReserve(pages.count()); for (int i = 0; i < pages.count(); i++) { SkSafeRef(pages[i]); curNodes.push(pages[i]); } // nextRoundNodes passes its references to nodes on to curNodes. SkTDArray<SkPDFDict*> nextRoundNodes; nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize); int treeCapacity = kNodeSize; do { for (int i = 0; i < curNodes.count(); ) { if (i > 0 && i + 1 == curNodes.count()) { nextRoundNodes.push(curNodes[i]); break; } SkPDFDict* newNode = new SkPDFDict("Pages"); SkRefPtr<SkPDFObjRef> newNodeRef = new SkPDFObjRef(newNode); newNodeRef->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFArray> kids = new SkPDFArray; kids->unref(); // SkRefPtr and new both took a reference. kids->reserve(kNodeSize); int count = 0; for (; i < curNodes.count() && count < kNodeSize; i++, count++) { curNodes[i]->insert(parentName.get(), newNodeRef.get()); kids->append(new SkPDFObjRef(curNodes[i]))->unref(); // TODO(vandebo): put the objects in strict access order. // Probably doesn't matter because they are so small. if (curNodes[i] != pages[0]) { pageTree->push(curNodes[i]); // Transfer reference. catalog->addObject(curNodes[i], false); } else { SkSafeUnref(curNodes[i]); catalog->addObject(curNodes[i], true); } } newNode->insert(kidsName.get(), kids.get()); int pageCount = treeCapacity; if (count < kNodeSize) { pageCount = pages.count() % treeCapacity; } newNode->insert(countName.get(), new SkPDFInt(pageCount))->unref(); nextRoundNodes.push(newNode); // Transfer reference. } curNodes = nextRoundNodes; nextRoundNodes.rewind(); treeCapacity *= kNodeSize; } while (curNodes.count() > 1); pageTree->push(curNodes[0]); // Transfer reference. catalog->addObject(curNodes[0], false); if (rootNode) { *rootNode = curNodes[0]; } }
static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices, SkWStream* stream) { if (pageDevices.isEmpty()) { return false; } SkTDArray<SkPDFDict*> pages; SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict)); for (int i = 0; i < pageDevices.count(); i++) { SkASSERT(pageDevices[i]); SkASSERT(i == 0 || pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon()); SkAutoTUnref<SkPDFDict> page(create_pdf_page(pageDevices[i])); pageDevices[i]->appendDestinations(dests, page.get()); pages.push(page.detach()); } SkTDArray<SkPDFDict*> pageTree; SkAutoTUnref<SkPDFDict> docCatalog(SkNEW_ARGS(SkPDFDict, ("Catalog"))); SkPDFDict* pageTreeRoot; generate_page_tree(pages, &pageTree, &pageTreeRoot); docCatalog->insertObjRef("Pages", SkRef(pageTreeRoot)); if (dests->size() > 0) { docCatalog->insertObjRef("Dests", dests.detach()); } /* TODO(vandebo): output intent SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent"); outputIntent->insertName("S", "GTS_PDFA1"); outputIntent->insertString("OutputConditionIdentifier", "sRGB"); SkAutoTUnref<SkPDFArray> intentArray(new SkPDFArray); intentArray->appendObject(SkRef(outputIntent.get())); docCatalog->insertObject("OutputIntent", intentArray.detach()); */ // Build font subsetting info before proceeding. SkPDFSubstituteMap substitutes; perform_font_subsetting(pageDevices, &substitutes); SkPDFObjNumMap objNumMap; if (objNumMap.addObject(docCatalog.get())) { docCatalog->addResources(&objNumMap, substitutes); } size_t baseOffset = stream->bytesWritten(); emit_pdf_header(stream); SkTDArray<int32_t> offsets; for (int i = 0; i < objNumMap.objects().count(); ++i) { SkPDFObject* object = objNumMap.objects()[i]; size_t offset = stream->bytesWritten(); // This assert checks that size(pdf_header) > 0 and that // the output stream correctly reports bytesWritten(). SkASSERT(offset > baseOffset); offsets.push(SkToS32(offset - baseOffset)); SkASSERT(object == substitutes.getSubstitute(object)); SkASSERT(objNumMap.getObjectNumber(object) == i + 1); stream->writeDecAsText(i + 1); stream->writeText(" 0 obj\n"); // Generation number is always 0. object->emitObject(stream, objNumMap, substitutes); stream->writeText("\nendobj\n"); } int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset); // Include the zeroth object in the count. int32_t objCount = SkToS32(offsets.count() + 1); stream->writeText("xref\n0 "); stream->writeDecAsText(objCount); stream->writeText("\n0000000000 65535 f \n"); for (int i = 0; i < offsets.count(); i++) { SkASSERT(offsets[i] > 0); stream->writeBigDecAsText(offsets[i], 10); stream->writeText(" 00000 n \n"); } emit_pdf_footer(stream, objNumMap, substitutes, docCatalog.get(), objCount, xRefFileOffset); // The page tree has both child and parent pointers, so it creates a // reference cycle. We must clear that cycle to properly reclaim memory. for (int i = 0; i < pageTree.count(); i++) { pageTree[i]->clear(); } pageTree.safeUnrefAll(); pages.unrefAll(); return true; }
void draw_paths(SkCanvas* canvas, ShadowMode mode) { SkTArray<SkPath> paths; paths.push_back().addRoundRect(SkRect::MakeWH(50, 50), 10, 10); SkRRect oddRRect; oddRRect.setNinePatch(SkRect::MakeWH(50, 50), 9, 13, 6, 16); paths.push_back().addRRect(oddRRect); paths.push_back().addRect(SkRect::MakeWH(50, 50)); paths.push_back().addCircle(25, 25, 25); paths.push_back().cubicTo(100, 50, 20, 100, 0, 0); paths.push_back().addOval(SkRect::MakeWH(20, 60)); // star SkTArray<SkPath> concavePaths; concavePaths.push_back().moveTo(0.0f, -33.3333f); concavePaths.back().lineTo(9.62f, -16.6667f); concavePaths.back().lineTo(28.867f, -16.6667f); concavePaths.back().lineTo(19.24f, 0.0f); concavePaths.back().lineTo(28.867f, 16.6667f); concavePaths.back().lineTo(9.62f, 16.6667f); concavePaths.back().lineTo(0.0f, 33.3333f); concavePaths.back().lineTo(-9.62f, 16.6667f); concavePaths.back().lineTo(-28.867f, 16.6667f); concavePaths.back().lineTo(-19.24f, 0.0f); concavePaths.back().lineTo(-28.867f, -16.6667f); concavePaths.back().lineTo(-9.62f, -16.6667f); concavePaths.back().close(); // dumbbell concavePaths.push_back().moveTo(50, 0); concavePaths.back().cubicTo(100, 25, 60, 50, 50, 0); concavePaths.back().cubicTo(0, -25, 40, -50, 50, 0); static constexpr SkScalar kPad = 15.f; static constexpr SkScalar kLightR = 100.f; static constexpr SkScalar kHeight = 50.f; // transform light position relative to canvas to handle tiling SkPoint lightXY = canvas->getTotalMatrix().mapXY(250, 400); SkPoint3 lightPos = { lightXY.fX, lightXY.fY, 500 }; canvas->translate(3 * kPad, 3 * kPad); canvas->save(); SkScalar x = 0; SkScalar dy = 0; SkTDArray<SkMatrix> matrices; matrices.push()->reset(); SkMatrix* m = matrices.push(); m->setRotate(33.f, 25.f, 25.f); m->postScale(1.2f, 0.8f, 25.f, 25.f); for (auto& m : matrices) { for (int flags : { kNone_ShadowFlag, kTransparentOccluder_ShadowFlag }) { for (const auto& path : paths) { SkRect postMBounds = path.getBounds(); m.mapRect(&postMBounds); SkScalar w = postMBounds.width() + kHeight; SkScalar dx = w + kPad; if (x + dx > kW - 3 * kPad) { canvas->restore(); canvas->translate(0, dy); canvas->save(); x = 0; dy = 0; } canvas->save(); canvas->concat(m); if (kDebugColorNoOccluders == mode || kDebugColorOccluders == mode) { draw_shadow(canvas, path, kHeight, SK_ColorRED, lightPos, kLightR, true, flags); draw_shadow(canvas, path, kHeight, SK_ColorBLUE, lightPos, kLightR, false, flags); } else if (kGrayscale == mode) { SkColor ambientColor = SkColorSetARGB(0.1f * 255, 0, 0, 0); SkColor spotColor = SkColorSetARGB(0.25f * 255, 0, 0, 0); SkShadowUtils::DrawShadow(canvas, path, SkPoint3{0, 0, kHeight}, lightPos, kLightR, ambientColor, spotColor, flags); } SkPaint paint; paint.setAntiAlias(true); if (kDebugColorNoOccluders == mode) { // Draw the path outline in green on top of the ambient and spot shadows. if (SkToBool(flags & kTransparentOccluder_ShadowFlag)) { paint.setColor(SK_ColorCYAN); } else { paint.setColor(SK_ColorGREEN); } paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(0); } else { paint.setColor(kDebugColorOccluders == mode ? SK_ColorLTGRAY : SK_ColorWHITE); if (SkToBool(flags & kTransparentOccluder_ShadowFlag)) { paint.setAlpha(128); } paint.setStyle(SkPaint::kFill_Style); } canvas->drawPath(path, paint); canvas->restore(); canvas->translate(dx, 0); x += dx; dy = SkTMax(dy, postMBounds.height() + kPad + kHeight); } } } // concave paths canvas->restore(); canvas->translate(kPad, dy); canvas->save(); x = kPad; dy = 0; for (auto& m : matrices) { // for the concave paths we are not clipping, so transparent and opaque are the same for (const auto& path : concavePaths) { SkRect postMBounds = path.getBounds(); m.mapRect(&postMBounds); SkScalar w = postMBounds.width() + kHeight; SkScalar dx = w + kPad; canvas->save(); canvas->concat(m); if (kDebugColorNoOccluders == mode || kDebugColorOccluders == mode) { draw_shadow(canvas, path, kHeight, SK_ColorRED, lightPos, kLightR, true, kNone_ShadowFlag); draw_shadow(canvas, path, kHeight, SK_ColorBLUE, lightPos, kLightR, false, kNone_ShadowFlag); } else if (kGrayscale == mode) { SkColor ambientColor = SkColorSetARGB(0.1f * 255, 0, 0, 0); SkColor spotColor = SkColorSetARGB(0.25f * 255, 0, 0, 0); SkShadowUtils::DrawShadow(canvas, path, SkPoint3{ 0, 0, kHeight }, lightPos, kLightR, ambientColor, spotColor, kNone_ShadowFlag); } SkPaint paint; paint.setAntiAlias(true); if (kDebugColorNoOccluders == mode) { // Draw the path outline in green on top of the ambient and spot shadows. paint.setColor(SK_ColorGREEN); paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(0); } else { paint.setColor(kDebugColorOccluders == mode ? SK_ColorLTGRAY : SK_ColorWHITE); paint.setStyle(SkPaint::kFill_Style); } canvas->drawPath(path, paint); canvas->restore(); canvas->translate(dx, 0); x += dx; dy = SkTMax(dy, postMBounds.height() + kPad + kHeight); } } // Show where the light is in x,y as a circle (specified in device space). SkMatrix invCanvasM = canvas->getTotalMatrix(); if (invCanvasM.invert(&invCanvasM)) { canvas->save(); canvas->concat(invCanvasM); SkPaint paint; paint.setColor(SK_ColorBLACK); paint.setAntiAlias(true); canvas->drawCircle(lightPos.fX, lightPos.fY, kLightR / 10.f, paint); canvas->restore(); } }
void SkCommandLineFlags::Parse(int argc, char** argv) { // Only allow calling this function once. static bool gOnce; if (gOnce) { SkDebugf("Parse should only be called once at the beginning of main!\n"); SkASSERT(false); return; } gOnce = true; bool helpPrinted = false; // Loop over argv, starting with 1, since the first is just the name of the program. for (int i = 1; i < argc; i++) { if (0 == strcmp("-h", argv[i]) || 0 == strcmp("--help", argv[i])) { // Print help message. SkTDArray<const char*> helpFlags; for (int j = i + 1; j < argc; j++) { if (SkStrStartsWith(argv[j], '-')) { break; } helpFlags.append(1, &argv[j]); } if (0 == helpFlags.count()) { // Only print general help message if help for specific flags is not requested. SkDebugf("%s\n%s\n", argv[0], gUsage.c_str()); } SkDebugf("Flags:\n"); if (0 == helpFlags.count()) { // If no flags followed --help, print them all SkTDArray<SkFlagInfo*> allFlags; for (SkFlagInfo* flag = SkCommandLineFlags::gHead; flag; flag = flag->next()) { allFlags.push(flag); } SkTQSort(&allFlags[0], &allFlags[allFlags.count() - 1], CompareFlagsByName()); for (int i = 0; i < allFlags.count(); ++i) { print_help_for_flag(allFlags[i]); if (allFlags[i]->extendedHelp().size() > 0) { SkDebugf(" Use '--help %s' for more information.\n", allFlags[i]->name().c_str()); } } } else { for (SkFlagInfo* flag = SkCommandLineFlags::gHead; flag; flag = flag->next()) { for (int k = 0; k < helpFlags.count(); k++) { if (flag->name().equals(helpFlags[k]) || flag->shortName().equals(helpFlags[k])) { print_extended_help_for_flag(flag); helpFlags.remove(k); break; } } } } if (helpFlags.count() > 0) { SkDebugf("Requested help for unrecognized flags:\n"); for (int k = 0; k < helpFlags.count(); k++) { SkDebugf(" --%s\n", helpFlags[k]); } } helpPrinted = true; } if (!helpPrinted) { bool flagMatched = false; SkFlagInfo* flag = gHead; while (flag != nullptr) { if (flag->match(argv[i])) { flagMatched = true; switch (flag->getFlagType()) { case SkFlagInfo::kBool_FlagType: // Can be handled by match, above, but can also be set by the next // string. if (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) { i++; bool value; if (parse_bool_arg(argv[i], &value)) { flag->setBool(value); } } break; case SkFlagInfo::kString_FlagType: flag->resetStrings(); // Add all arguments until another flag is reached. while (i+1 < argc) { char* end = nullptr; // Negative numbers aren't flags. ignore_result(strtod(argv[i+1], &end)); if (end == argv[i+1] && SkStrStartsWith(argv[i+1], '-')) { break; } i++; flag->append(argv[i]); } break; case SkFlagInfo::kInt_FlagType: i++; flag->setInt(atoi(argv[i])); break; case SkFlagInfo::kDouble_FlagType: i++; flag->setDouble(atof(argv[i])); break; default: SkDEBUGFAIL("Invalid flag type"); } break; } flag = flag->next(); } if (!flagMatched) { #if SK_BUILD_FOR_MAC if (SkStrStartsWith(argv[i], "NSDocumentRevisions") || SkStrStartsWith(argv[i], "-NSDocumentRevisions")) { i++; // skip YES } else #endif if (FLAGS_undefok) { SkDebugf("FYI: ignoring unknown flag '%s'.\n", argv[i]); } else { SkDebugf("Got unknown flag '%s'. Exiting.\n", argv[i]); exit(-1); } } } } // Since all of the flags have been set, release the memory used by each // flag. FLAGS_x can still be used after this. SkFlagInfo* flag = gHead; gHead = nullptr; while (flag != nullptr) { SkFlagInfo* next = flag->next(); delete flag; flag = next; } if (helpPrinted) { exit(0); } }