void buildNameToFamilyMap(SkTDArray<FontFamily*> families, const bool isolated) { for (int i = 0; i < families.count(); i++) { FontFamily& family = *families[i]; SkTArray<NameToFamily, true>* nameToFamily = &fNameToFamilyMap; if (family.fIsFallbackFont) { nameToFamily = &fFallbackNameToFamilyMap; if (0 == family.fNames.count()) { SkString& fallbackName = family.fNames.push_back(); fallbackName.printf("%.2x##fallback", i); } } sk_sp<SkFontStyleSet_Android> newSet = sk_make_sp<SkFontStyleSet_Android>(family, fScanner, isolated); if (0 == newSet->count()) { continue; } for (const SkString& name : family.fNames) { nameToFamily->emplace_back(NameToFamily{name, newSet.get()}); } fStyleSets.emplace_back(std::move(newSet)); } }
// return root node. static sk_sp<SkPDFDict> generate_page_tree(SkTArray<sk_sp<SkPDFDict>>* pages) { // 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; // curNodes takes a reference to its items, which it passes to pageTree. int totalPageCount = pages->count(); SkTArray<sk_sp<SkPDFDict>> curNodes; curNodes.swap(pages); // nextRoundNodes passes its references to nodes on to curNodes. int treeCapacity = kNodeSize; do { SkTArray<sk_sp<SkPDFDict>> nextRoundNodes; for (int i = 0; i < curNodes.count(); ) { if (i > 0 && i + 1 == curNodes.count()) { SkASSERT(curNodes[i]); nextRoundNodes.emplace_back(std::move(curNodes[i])); break; } auto newNode = sk_make_sp<SkPDFDict>("Pages"); auto kids = sk_make_sp<SkPDFArray>(); kids->reserve(kNodeSize); int count = 0; for (; i < curNodes.count() && count < kNodeSize; i++, count++) { SkASSERT(curNodes[i]); curNodes[i]->insertObjRef("Parent", newNode); kids->appendObjRef(std::move(curNodes[i])); } // 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 = ((totalPageCount - 1) % treeCapacity) + 1; } newNode->insertInt("Count", pageCount); newNode->insertObject("Kids", std::move(kids)); nextRoundNodes.emplace_back(std::move(newNode)); } SkDEBUGCODE( for (const auto& n : curNodes) { SkASSERT(!n); } ); curNodes.swap(&nextRoundNodes); nextRoundNodes.reset(); treeCapacity *= kNodeSize; } while (curNodes.count() > 1);
void SkInternalAtlasTextTarget::addDrawOp(const GrClip& clip, std::unique_ptr<GrAtlasTextOp> op) { SkASSERT(clip.quickContains(SkRect::MakeIWH(fWidth, fHeight))); // The SkAtlasTextRenderer currently only handles grayscale SDF glyphs. if (op->maskType() != GrAtlasTextOp::kGrayscaleDistanceField_MaskType) { return; } const GrCaps& caps = *this->context()->internal().grContext()->contextPriv().caps(); op->finalizeForTextTarget(fColor, caps); int n = SkTMin(kMaxBatchLookBack, fOps.count()); for (int i = 0; i < n; ++i) { GrAtlasTextOp* other = fOps.fromBack(i).get(); if (other->combineIfPossible(op.get(), caps) == GrOp::CombineResult::kMerged) { fOpMemoryPool->release(std::move(op)); return; } if (GrRectsOverlap(op->bounds(), other->bounds())) { break; } } op->visitProxies([](GrSurfaceProxy*) {}); fOps.emplace_back(std::move(op)); }
// This test hammers the GPU textblobcache and font atlas static void text_blob_cache_inner(skiatest::Reporter* reporter, GrContext* context, int maxTotalText, int maxGlyphID, int maxFamilies, bool normal, bool stressTest) { // setup surface uint32_t flags = 0; SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); // configure our context for maximum stressing of cache and atlas if (stressTest) { GrTest::SetupAlwaysEvictAtlas(context); context->setTextBlobCacheLimit_ForTesting(0); } SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, kN32_SkColorType, kPremul_SkAlphaType); auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 0, &props)); REPORTER_ASSERT(reporter, surface); if (!surface) { return; } SkCanvas* canvas = surface->getCanvas(); sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); int count = SkMin32(fm->countFamilies(), maxFamilies); // make a ton of text SkAutoTArray<uint16_t> text(maxTotalText); for (int i = 0; i < maxTotalText; i++) { text[i] = i % maxGlyphID; } // generate textblobs SkTArray<sk_sp<SkTextBlob>> blobs; for (int i = 0; i < count; i++) { SkPaint paint; paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setTextSize(48); // draw big glyphs to really stress the atlas SkString familyName; fm->getFamilyName(i, &familyName); sk_sp<SkFontStyleSet> set(fm->createStyleSet(i)); for (int j = 0; j < set->count(); ++j) { SkFontStyle fs; set->getStyle(j, &fs, nullptr); // We use a typeface which randomy returns unexpected mask formats to fuzz sk_sp<SkTypeface> orig(set->createTypeface(j)); if (normal) { paint.setTypeface(orig); } else { paint.setTypeface(sk_make_sp<SkRandomTypeface>(orig, paint, true)); } SkTextBlobBuilder builder; for (int aa = 0; aa < 2; aa++) { for (int subpixel = 0; subpixel < 2; subpixel++) { for (int lcd = 0; lcd < 2; lcd++) { paint.setAntiAlias(SkToBool(aa)); paint.setSubpixelText(SkToBool(subpixel)); paint.setLCDRenderText(SkToBool(lcd)); if (!SkToBool(lcd)) { paint.setTextSize(160); } const SkTextBlobBuilder::RunBuffer& run = builder.allocRun(paint, maxTotalText, 0, 0, nullptr); memcpy(run.glyphs, text.get(), maxTotalText * sizeof(uint16_t)); } } } blobs.emplace_back(builder.make()); } } // create surface where LCD is impossible info = SkImageInfo::MakeN32Premul(kWidth, kHeight); SkSurfaceProps propsNoLCD(0, kUnknown_SkPixelGeometry); auto surfaceNoLCD(canvas->makeSurface(info, &propsNoLCD)); REPORTER_ASSERT(reporter, surface); if (!surface) { return; } SkCanvas* canvasNoLCD = surfaceNoLCD->getCanvas(); // test redraw draw(canvas, 2, blobs); draw(canvasNoLCD, 2, blobs); // test draw after free context->freeGpuResources(); draw(canvas, 1, blobs); context->freeGpuResources(); draw(canvasNoLCD, 1, blobs); // test draw after abandon context->abandonContext(); draw(canvas, 1, blobs); }
std::unique_ptr<GrFragmentProcessor> GrFragmentProcessor::RunInSeries( std::unique_ptr<GrFragmentProcessor>* series, int cnt) { class SeriesFragmentProcessor : public GrFragmentProcessor { public: static std::unique_ptr<GrFragmentProcessor> Make( std::unique_ptr<GrFragmentProcessor>* children, int cnt) { return std::unique_ptr<GrFragmentProcessor>(new SeriesFragmentProcessor(children, cnt)); } const char* name() const override { return "Series"; } std::unique_ptr<GrFragmentProcessor> clone() const override { SkSTArray<4, std::unique_ptr<GrFragmentProcessor>> children(this->numChildProcessors()); for (int i = 0; i < this->numChildProcessors(); ++i) { if (!children.push_back(this->childProcessor(i).clone())) { return nullptr; } } return Make(children.begin(), this->numChildProcessors()); } private: GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { class GLFP : public GrGLSLFragmentProcessor { public: void emitCode(EmitArgs& args) override { // First guy's input might be nil. SkString temp("out0"); this->emitChild(0, args.fInputColor, &temp, args); SkString input = temp; for (int i = 1; i < this->numChildProcessors() - 1; ++i) { temp.printf("out%d", i); this->emitChild(i, input.c_str(), &temp, args); input = temp; } // Last guy writes to our output variable. this->emitChild(this->numChildProcessors() - 1, input.c_str(), args); } }; return new GLFP; } SeriesFragmentProcessor(std::unique_ptr<GrFragmentProcessor>* children, int cnt) : INHERITED(kSeriesFragmentProcessor_ClassID, OptFlags(children, cnt)) { SkASSERT(cnt > 1); for (int i = 0; i < cnt; ++i) { this->registerChildProcessor(std::move(children[i])); } } static OptimizationFlags OptFlags(std::unique_ptr<GrFragmentProcessor>* children, int cnt) { OptimizationFlags flags = kAll_OptimizationFlags; for (int i = 0; i < cnt && flags != kNone_OptimizationFlags; ++i) { flags &= children[i]->optimizationFlags(); } return flags; } void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {} bool onIsEqual(const GrFragmentProcessor&) const override { return true; } GrColor4f constantOutputForConstantInput(GrColor4f color) const override { int childCnt = this->numChildProcessors(); for (int i = 0; i < childCnt; ++i) { color = ConstantOutputForConstantInput(this->childProcessor(i), color); } return color; } typedef GrFragmentProcessor INHERITED; }; if (!cnt) { return nullptr; } if (1 == cnt) { return std::move(series[0]); } // Run the through the series, do the invariant output processing, and look for eliminations. GrProcessorAnalysisColor inputColor; inputColor.setToUnknown(); GrColorFragmentProcessorAnalysis info(inputColor, unique_ptr_address_as_pointer_address(series), cnt); SkTArray<std::unique_ptr<GrFragmentProcessor>> replacementSeries; GrColor4f knownColor; int leadingFPsToEliminate = info.initialProcessorsToEliminate(&knownColor); if (leadingFPsToEliminate) { std::unique_ptr<GrFragmentProcessor> colorFP( GrConstColorProcessor::Make(knownColor, GrConstColorProcessor::InputMode::kIgnore)); if (leadingFPsToEliminate == cnt) { return colorFP; } cnt = cnt - leadingFPsToEliminate + 1; replacementSeries.reserve(cnt); replacementSeries.emplace_back(std::move(colorFP)); for (int i = 0; i < cnt - 1; ++i) { replacementSeries.emplace_back(std::move(series[leadingFPsToEliminate + i])); } series = replacementSeries.begin(); } return SeriesFragmentProcessor::Make(series, cnt); }