diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-03 13:42:47 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:27:51 +0000 |
commit | 8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (patch) | |
tree | d29d987c4d7b173cf853279b79a51598f104b403 /chromium/third_party/skia/tools | |
parent | 830c9e163d31a9180fadca926b3e1d7dfffb5021 (diff) | |
download | qtwebengine-chromium-8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec.tar.gz |
BASELINE: Update Chromium to 66.0.3359.156
Change-Id: I0c9831ad39911a086b6377b16f995ad75a51e441
Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/third_party/skia/tools')
120 files changed, 5569 insertions, 3085 deletions
diff --git a/chromium/third_party/skia/tools/BinaryAsset.h b/chromium/third_party/skia/tools/BinaryAsset.h deleted file mode 100644 index 6fb7157596e..00000000000 --- a/chromium/third_party/skia/tools/BinaryAsset.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef BinaryAsset_DEFINED -#define BinaryAsset_DEFINED - -#include <cstddef> - -struct BinaryAsset { - const char* name; - const void* data; - size_t len; -}; - -#endif // BinaryAsset_DEFINED diff --git a/chromium/third_party/skia/tools/OverwriteLine.h b/chromium/third_party/skia/tools/OverwriteLine.h index e8f0504b1af..8985a6a01c6 100644 --- a/chromium/third_party/skia/tools/OverwriteLine.h +++ b/chromium/third_party/skia/tools/OverwriteLine.h @@ -3,7 +3,7 @@ // Print this string to reset and clear your current terminal line. static const char* kSkOverwriteLine = -#ifdef SK_BUILD_FOR_WIN32 +#ifdef SK_BUILD_FOR_WIN "\r \r" #elif defined(SK_BUILD_FOR_IOS) "\r" diff --git a/chromium/third_party/skia/tools/ProcStats.cpp b/chromium/third_party/skia/tools/ProcStats.cpp index 51ddf55aa50..9515368f9e7 100644 --- a/chromium/third_party/skia/tools/ProcStats.cpp +++ b/chromium/third_party/skia/tools/ProcStats.cpp @@ -19,7 +19,7 @@ return static_cast<int>(ru.ru_maxrss / 1024); // Linux reports kilobytes. #endif } -#elif defined(SK_BUILD_FOR_WIN32) +#elif defined(SK_BUILD_FOR_WIN) #include <windows.h> #include <psapi.h> int sk_tools::getMaxResidentSetSizeMB() { @@ -59,7 +59,7 @@ return rssPages * pageSize / 1024 / 1024; } -#elif defined(SK_BUILD_FOR_WIN32) +#elif defined(SK_BUILD_FOR_WIN) int sk_tools::getCurrResidentSetSizeMB() { PROCESS_MEMORY_COUNTERS info; GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); diff --git a/chromium/third_party/skia/tools/ResourceFactory.h b/chromium/third_party/skia/tools/ResourceFactory.h new file mode 100644 index 00000000000..7e890173128 --- /dev/null +++ b/chromium/third_party/skia/tools/ResourceFactory.h @@ -0,0 +1,14 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef ResourceFactory_DEFINED +#define ResourceFactory_DEFINED + +#include <SkData.h> + +extern sk_sp<SkData> (*gResourceFactory)(const char*); + +#endif // ResourceFactory_DEFINED diff --git a/chromium/third_party/skia/tools/Resources.cpp b/chromium/third_party/skia/tools/Resources.cpp index 9d540269869..0e343c4883c 100644 --- a/chromium/third_party/skia/tools/Resources.cpp +++ b/chromium/third_party/skia/tools/Resources.cpp @@ -5,7 +5,7 @@ * found in the LICENSE file. */ -#include "BinaryAsset.h" +#include "ResourceFactory.h" #include "Resources.h" #include "SkBitmap.h" #include "SkCommandLineFlags.h" @@ -19,6 +19,8 @@ DEFINE_string2(resourcePath, i, "resources", "Directory with test resources: images, fonts, etc."); +sk_sp<SkData> (*gResourceFactory)(const char*) = nullptr; + SkString GetResourcePath(const char* resource) { return SkOSPath::Join(FLAGS_resourcePath[0], resource); } @@ -27,7 +29,6 @@ void SetResourcePath(const char* resource) { FLAGS_resourcePath.set(0, resource); } - bool DecodeDataToBitmap(sk_sp<SkData> data, SkBitmap* dst) { std::unique_ptr<SkImageGenerator> gen(SkImageGenerator::MakeFromEncoded(std::move(data))); return gen && dst->tryAllocPixels(gen->getInfo()) && @@ -41,27 +42,20 @@ std::unique_ptr<SkStreamAsset> GetResourceAsStream(const char* resource) { : nullptr; } -#ifdef SK_EMBED_RESOURCES -extern BinaryAsset gResources[]; sk_sp<SkData> GetResourceAsData(const char* resource) { - for (const BinaryAsset* ptr = gResources; ptr->name; ++ptr) { - if (0 == strcmp(resource, ptr->name)) { - return SkData::MakeWithoutCopy(ptr->data, ptr->len); + if (gResourceFactory) { + if (auto data = gResourceFactory(resource)) { + return data; } + SkDebugf("Resource \"%s\" not found.\n", resource); + SK_ABORT("missing resource"); + } + if (auto data = SkData::MakeFromFileName(GetResourcePath(resource).c_str())) { + return data; } SkDebugf("Resource \"%s\" not found.\n", resource); - SK_ABORT("missing resource"); return nullptr; } -#else -sk_sp<SkData> GetResourceAsData(const char* resource) { - auto data = SkData::MakeFromFileName(GetResourcePath(resource).c_str()); - if (!data) { - SkDebugf("Resource \"%s\" not found.\n", resource); - } - return data; -} -#endif sk_sp<SkTypeface> MakeResourceAsTypeface(const char* resource) { std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream(resource)); diff --git a/chromium/third_party/skia/tools/bookmaker/bookmaker.cpp b/chromium/third_party/skia/tools/bookmaker/bookmaker.cpp index b63813d6675..02dbbc1961f 100644 --- a/chromium/third_party/skia/tools/bookmaker/bookmaker.cpp +++ b/chromium/third_party/skia/tools/bookmaker/bookmaker.cpp @@ -7,26 +7,36 @@ #include "bookmaker.h" +#ifdef SK_BUILD_FOR_WIN +#include <Windows.h> +#endif + DEFINE_string2(status, a, "", "File containing status of documentation. (Use in place of -b -i)"); DEFINE_string2(bmh, b, "", "Path to a *.bmh file or a directory."); DEFINE_bool2(catalog, c, false, "Write example catalog.htm. (Requires -b -f -r)"); DEFINE_string2(examples, e, "", "File of fiddlecli input, usually fiddle.json (For now, disables -r -f -s)"); DEFINE_string2(fiddle, f, "", "File of fiddlecli output, usually fiddleout.json."); -DEFINE_bool2(hack, h, false, "Do a find/replace hack to update all *.bmh files. (Requires -b)"); +DEFINE_bool2(hack, H, false, "Do a find/replace hack to update all *.bmh files. (Requires -b)"); +// h is reserved for help DEFINE_string2(include, i, "", "Path to a *.h file or a directory."); DEFINE_bool2(selfcheck, k, false, "Check bmh against itself. (Requires -b)"); DEFINE_bool2(stdout, o, false, "Write file out to standard out."); DEFINE_bool2(populate, p, false, "Populate include from bmh. (Requires -b -i)"); +// q is reserved for quiet DEFINE_string2(ref, r, "", "Resolve refs and write *.md files to path. (Requires -b -f)"); DEFINE_string2(spellcheck, s, "", "Spell-check [once, all, mispelling]. (Requires -b)"); DEFINE_bool2(tokens, t, false, "Write bmh from include. (Requires -b -i)"); DEFINE_bool2(crosscheck, x, false, "Check bmh against includes. (Requires -b -i)"); +// v is reserved for verbose DEFINE_bool2(skip, z, false, "Skip degenerate missed in legacy preprocessor."); /* recipe for generating timestamps for existing doxygen comments find include/core -type f -name '*.h' -print -exec git blame {} \; > ~/all.blame.txt todos: +add new markup to associate typedef SaveLayerFlags with Enum so that, for + documentation purposes, this enum is named rather than anonymous +check column 1 of subtopic tables to see that they start lowercase and don't have a trailing period space table better for Constants should Return be on same line as 'Return Value'? remove anonymous header, e.g. Enum SkPaint::::anonymous_2 @@ -38,6 +48,7 @@ enum comments should be disallowed unless after #Enum and before first #Const trouble with aliases, plurals need to keep first letter of includeWriter @param / @return lowercase Quad -> quad, Quads -> quads +deprecated methods should be sorted down in md out, and show include "Deprecated." text body. see head of selfCheck.cpp for additional todos */ @@ -50,7 +61,7 @@ see head of selfCheck.cpp for additional todos */ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markType, - const vector<string>& typeNameBuilder) { + const vector<string>& typeNameBuilder, HasTag hasTag) { Definition* definition = nullptr; switch (markType) { case MarkType::kComment: @@ -134,6 +145,11 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy return false; } } + if (HasTag::kYes == hasTag) { + if (!this->checkEndMarker(markType, definition->fName)) { + return false; + } + } if (!this->popParentStack(definition)) { return false; } @@ -178,20 +194,42 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy definition = rootDefinition; definition->fFileName = fFileName; definition->fContentStart = fChar; - definition->fName = typeNameBuilder[0]; - Definition* parent = fParent; - while (parent && MarkType::kTopic != parent->fMarkType - && MarkType::kSubtopic != parent->fMarkType) { - parent = parent->fParent; + if (MarkType::kTopic == markType) { + if (fParent) { + return this->reportError<bool>("#Topic must be root"); + } + // topic name is unappended + definition->fName = typeNameBuilder[0]; + } else { + if (!fParent) { + return this->reportError<bool>("#Subtopic may not be root"); + } + Definition* parent = fParent; + while (MarkType::kTopic != parent->fMarkType && MarkType::kSubtopic != parent->fMarkType) { + parent = parent->fParent; + if (!parent) { + // subtopic must have subtopic or topic in parent chain + return this->reportError<bool>("#Subtopic missing parent"); + } + } + if (MarkType::kSubtopic == parent->fMarkType) { + // subtopic prepends parent subtopic name, but not parent topic name + definition->fName = parent->fName + '_'; + } + definition->fName += typeNameBuilder[0]; + definition->fFiddle = parent->fFiddle + '_'; } - definition->fFiddle = parent ? parent->fFiddle + '_' : ""; definition->fFiddle += Definition::NormalizedName(typeNameBuilder[0]); this->setAsParent(definition); } { - const string& fullTopic = hasEnd ? fParent->fFiddle : definition->fFiddle; + SkASSERT(hasEnd ? fParent : definition); + string fullTopic = hasEnd ? fParent->fFiddle : definition->fFiddle; Definition* defPtr = fTopicMap[fullTopic]; if (hasEnd) { + if (HasTag::kYes == hasTag && !this->checkEndMarker(markType, fullTopic)) { + return false; + } if (!definition) { definition = defPtr; } else if (definition != defPtr) { @@ -237,7 +275,6 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy case MarkType::kDescription: case MarkType::kStdOut: // may be one-liner - case MarkType::kBug: case MarkType::kNoExample: case MarkType::kParam: case MarkType::kReturn: @@ -274,7 +311,6 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy } // not one-liners case MarkType::kCode: - case MarkType::kDeprecated: case MarkType::kExample: case MarkType::kExperimental: case MarkType::kFormula: @@ -333,22 +369,28 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy // always treated as one-liners (can't detect misuse easily) case MarkType::kAlias: case MarkType::kAnchor: + case MarkType::kBug: case MarkType::kDefine: + case MarkType::kDeprecated: case MarkType::kDuration: - case MarkType::kError: case MarkType::kFile: case MarkType::kHeight: + case MarkType::kIllustration: case MarkType::kImage: - case MarkType::kLiteral: + case MarkType::kIn: + case MarkType::kLine: + case MarkType::kLiteral: case MarkType::kOutdent: case MarkType::kPlatform: + case MarkType::kPopulate: case MarkType::kSeeAlso: case MarkType::kSet: case MarkType::kSubstitute: case MarkType::kTime: case MarkType::kVolatile: case MarkType::kWidth: - if (hasEnd && MarkType::kAnchor != markType) { + // todo : add check disallowing children? + if (hasEnd && MarkType::kAnchor != markType && MarkType::kLine != markType) { return this->reportError<bool>("one liners omit end element"); } else if (!hasEnd && MarkType::kAnchor == markType) { return this->reportError<bool>("anchor line must have end element last"); @@ -385,6 +427,32 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy return this->reportError<bool>("duplicate alias"); } fAliasMap[alias] = definition; + definition->fFiddle = definition->fParent->fFiddle; + } + else if (MarkType::kLine == markType) { + const char* nextLF = this->strnchr('\n', this->fEnd); + const char* start = fChar; + const char* end = this->trimmedBracketEnd(fMC); + this->skipToEndBracket(fMC, nextLF); + if (fMC != this->next() || fMC != this->next()) { + return this->reportError<bool>("expected ## to delineate line"); + } + fMarkup.emplace_front(MarkType::kText, start, fLineCount, definition); + Definition* text = &fMarkup.front(); + text->fContentStart = start; + text->fContentEnd = end; + text->fTerminator = fChar; + definition->fContentEnd = text->fContentEnd; + definition->fTerminator = fChar; + definition->fChildren.emplace_back(text); + } else if (MarkType::kDeprecated == markType) { + this->skipSpace(); + fParent->fDeprecated = true; + fParent->fToBeDeprecated = this->skipExact("soon"); + this->skipSpace(); + if ('\n' != this->peek()) { + return this->reportError<bool>("unexpected text after #Deprecated"); + } } break; case MarkType::kExternal: @@ -411,6 +479,33 @@ void BmhParser::reportDuplicates(const Definition& def, const string& dup) const } } + +static Definition* find_fiddle(Definition* def, string name) { + if (MarkType::kExample == def->fMarkType && name == def->fFiddle) { + return def; + } + for (auto& child : def->fChildren) { + Definition* result = find_fiddle(child, name); + if (result) { + return result; + } + } + return nullptr; +} + +Definition* BmhParser::findExample(string name) const { + for (const auto& topic : fTopicMap) { + if (topic.second->fParent) { + continue; + } + Definition* def = find_fiddle(topic.second, name); + if (def) { + return def; + } + } + return nullptr; +} + static void find_examples(const Definition& def, vector<string>* exampleNames) { if (MarkType::kExample == def.fMarkType) { exampleNames->push_back(def.fFiddle); @@ -420,6 +515,39 @@ static void find_examples(const Definition& def, vector<string>* exampleNames) { } } +bool BmhParser::checkEndMarker(MarkType markType, string match) const { + TextParser tp(fFileName, fLine, fChar, fLineCount); + tp.skipSpace(); + if (fMC != tp.next()) { + return this->reportError<bool>("mismatched end marker expect #"); + } + const char* nameStart = tp.fChar; + tp.skipToNonAlphaNum(); + string markName(nameStart, tp.fChar - nameStart); + if (fMaps[(int) markType].fName != markName) { + return this->reportError<bool>("expected #XXX ## to match"); + } + tp.skipSpace(); + nameStart = tp.fChar; + tp.skipToNonAlphaNum(); + markName = string(nameStart, tp.fChar - nameStart); + if ("" == markName) { + if (fMC != tp.next() || fMC != tp.next()) { + return this->reportError<bool>("expected ##"); + } + return true; + } + std::replace(markName.begin(), markName.end(), '-', '_'); + auto defPos = match.rfind(markName); + if (string::npos == defPos) { + return this->reportError<bool>("mismatched end marker v1"); + } + if (markName.size() != match.size() - defPos) { + return this->reportError<bool>("mismatched end marker v2"); + } + return true; +} + bool BmhParser::checkExamples() const { vector<string> exampleNames; for (const auto& topic : fTopicMap) { @@ -673,7 +801,8 @@ bool BmhParser::findDefinitions() { } else { vector<string> parentName; parentName.push_back(fParent->fName); - if (!this->addDefinition(fChar - 1, true, fParent->fMarkType, parentName)) { + if (!this->addDefinition(fChar - 1, true, fParent->fMarkType, parentName, + HasTag::kNo)) { return false; } } @@ -717,14 +846,16 @@ bool BmhParser::findDefinitions() { if (hasEnd && expectEnd) { SkASSERT(fMC != this->peek()); } - if (!this->addDefinition(defStart, hasEnd, markType, typeNameBuilder)) { + if (!this->addDefinition(defStart, hasEnd, markType, typeNameBuilder, + HasTag::kYes)) { return false; } continue; } else if (this->peek() == ' ') { if (!fParent || (MarkType::kTable != fParent->fMarkType && MarkType::kLegend != fParent->fMarkType - && MarkType::kList != fParent->fMarkType)) { + && MarkType::kList != fParent->fMarkType + && MarkType::kLine != fParent->fMarkType)) { int endHashes = this->endHashCount(); if (endHashes <= 1) { if (fParent) { @@ -814,6 +945,9 @@ fail: return MarkType::kNone; } + // write #In to show containing #Topic + // write #Line with one liner from Member_Functions, Constructors, Operators if method, + // from Constants if enum, otherwise from #Subtopic containing match bool HackParser::hackFiles() { string filename(fFileName); size_t len = filename.length() - 1; @@ -821,42 +955,219 @@ bool HackParser::hackFiles() { --len; } filename = filename.substr(len + 1); - // remove trailing period from #Param and #Return - FILE* out = fopen(filename.c_str(), "wb"); - if (!out) { + if (filename.substr(0, 2) != "Sk") { + return true; + } + size_t under = filename.find('_'); + SkASSERT(under); + string className = filename.substr(0, under); + fOut = fopen(filename.c_str(), "wb"); + if (!fOut) { SkDebugf("could not open output file %s\n", filename.c_str()); return false; } - const char* start = fStart; - do { - const char* match = this->strnchr('#', fEnd); - if (!match) { - break; - } - this->skipTo(match); - this->next(); - if (!this->startsWith("Param") && !this->startsWith("Return")) { - continue; - } - const char* end = this->strnstr("##", fEnd); - while (true) { - TextParser::Save lastPeriod(this); - this->next(); - if (!this->skipToEndBracket('.', end)) { - lastPeriod.restore(); - break; + auto mapEntry = fBmhParser.fClassMap.find(className); + SkASSERT(fBmhParser.fClassMap.end() != mapEntry); + const Definition* classMarkup = &mapEntry->second; + const Definition* root = classMarkup->fParent; + SkASSERT(root); + SkASSERT(root->fTerminator); + SkASSERT('\n' == root->fTerminator[0]); + SkASSERT(!root->fParent); + fStart = root->fStart; + fChar = fStart; + fClassesAndStructs = nullptr; + fConstants = nullptr; + fConstructors = nullptr; + fMemberFunctions = nullptr; + fMembers = nullptr; + fOperators = nullptr; + fRelatedFunctions = nullptr; + this->topicIter(root); + fprintf(fOut, "%.*s", (int) (fEnd - fChar), fChar); + fclose(fOut); + if (this->writtenFileDiffers(filename, root->fFileName)) { + SkDebugf("wrote %s\n", filename.c_str()); + } else { + remove(filename.c_str()); + } + return true; +} + +string HackParser::searchTable(const Definition* tableHolder, const Definition* match) { + if (!tableHolder) { + return ""; + } + string bestMatch; + string result; + for (auto table : tableHolder->fChildren) { + if (MarkType::kTable == table->fMarkType) { + for (auto row : table->fChildren) { + if (MarkType::kRow == row->fMarkType) { + const Definition* col0 = row->fChildren[0]; + size_t len = col0->fContentEnd - col0->fContentStart; + string method = string(col0->fContentStart, len); + if (len - 2 == method.find("()") && islower(method[0]) + && Definition::MethodType::kOperator != match->fMethodType) { + method = method.substr(0, len - 2); + } + if (string::npos == match->fName.find(method)) { + continue; + } + if (bestMatch.length() < method.length()) { + bestMatch = method; + const Definition * col1 = row->fChildren[1]; + if (col1->fContentEnd <= col1->fContentStart) { + SkASSERT(string::npos != col1->fFileName.find("SkImageInfo")); + result = "incomplete"; + } else { + result = string(col1->fContentStart, col1->fContentEnd - + col1->fContentStart); + } + } + } } } - if ('.' == this->peek()) { - fprintf(out, "%.*s", (int) (fChar - start), start); - this->next(); - start = fChar; + } + return result; +} + +// returns true if topic has method +void HackParser::topicIter(const Definition* topic) { + if (string::npos != topic->fName.find(MdOut::kClassesAndStructs)) { + SkASSERT(!fClassesAndStructs); + fClassesAndStructs = topic; + } + if (string::npos != topic->fName.find(MdOut::kConstants)) { + SkASSERT(!fConstants); + fConstants = topic; + } + if (string::npos != topic->fName.find(MdOut::kConstructors)) { + SkASSERT(!fConstructors); + fConstructors = topic; + } + if (string::npos != topic->fName.find(MdOut::kMemberFunctions)) { + SkASSERT(!fMemberFunctions); + fMemberFunctions = topic; + } + if (string::npos != topic->fName.find(MdOut::kMembers)) { + SkASSERT(!fMembers); + fMembers = topic; + } + if (string::npos != topic->fName.find(MdOut::kOperators)) { + SkASSERT(!fOperators); + fOperators = topic; + } + if (string::npos != topic->fName.find(MdOut::kRelatedFunctions)) { + SkASSERT(!fRelatedFunctions); + fRelatedFunctions = topic; + } + for (auto child : topic->fChildren) { + string oneLiner; + bool hasIn = false; + bool hasLine = false; + for (auto part : child->fChildren) { + hasIn |= MarkType::kIn == part->fMarkType; + hasLine |= MarkType::kLine == part->fMarkType; + } + switch (child->fMarkType) { + case MarkType::kMethod: { + hasIn |= MarkType::kTopic != topic->fMarkType && + MarkType::kSubtopic != topic->fMarkType; // don't write #In if parent is class + hasLine |= child->fClone; + if (!hasLine) { + // find member_functions, add entry 2nd column text to #Line + for (auto tableHolder : { fMemberFunctions, fConstructors, fOperators }) { + if (!tableHolder) { + continue; + } + if (Definition::MethodType::kConstructor == child->fMethodType + && fConstructors != tableHolder) { + continue; + } + if (Definition::MethodType::kOperator == child->fMethodType + && fOperators != tableHolder) { + continue; + } + string temp = this->searchTable(tableHolder, child); + if ("" != temp) { + SkASSERT("" == oneLiner || temp == oneLiner); + oneLiner = temp; + } + } + if ("" == oneLiner) { + #ifdef SK_DEBUG + const Definition* rootParent = topic; + while (rootParent->fParent && MarkType::kClass != rootParent->fMarkType + && MarkType::kStruct != rootParent->fMarkType) { + rootParent = rootParent->fParent; + } + #endif + SkASSERT(rootParent); + SkASSERT(MarkType::kClass == rootParent->fMarkType + || MarkType::kStruct == rootParent->fMarkType); + hasLine = true; + } + } + + if (hasIn && hasLine) { + continue; + } + const char* start = fChar; + const char* end = child->fContentStart; + fprintf(fOut, "%.*s", (int) (end - start), start); + fChar = end; + // write to method markup header end + if (!hasIn) { + fprintf(fOut, "\n#In %s", topic->fName.c_str()); + } + if (!hasLine) { + fprintf(fOut, "\n#Line # %s ##", oneLiner.c_str()); + } + } break; + case MarkType::kTopic: + case MarkType::kSubtopic: + this->addOneLiner(fRelatedFunctions, child, hasLine, true); + this->topicIter(child); + break; + case MarkType::kStruct: + case MarkType::kClass: + this->addOneLiner(fClassesAndStructs, child, hasLine, false); + this->topicIter(child); + break; + case MarkType::kEnum: + case MarkType::kEnumClass: + this->addOneLiner(fConstants, child, hasLine, true); + break; + case MarkType::kMember: + this->addOneLiner(fMembers, child, hasLine, false); + break; + default: + ; } - } while (!this->eof()); - fprintf(out, "%.*s", (int) (fEnd - start), start); - fclose(out); - SkDebugf("wrote %s\n", filename.c_str()); - return true; + } +} + +void HackParser::addOneLiner(const Definition* defTable, const Definition* child, bool hasLine, + bool lfAfter) { + if (hasLine) { + return; + } + string oneLiner = this->searchTable(defTable, child); + if ("" == oneLiner) { + return; + } + const char* start = fChar; + const char* end = child->fContentStart; + fprintf(fOut, "%.*s", (int) (end - start), start); + fChar = end; + if (!lfAfter) { + fprintf(fOut, "\n"); + } + fprintf(fOut, "#Line # %s ##", oneLiner.c_str()); + if (lfAfter) { + fprintf(fOut, "\n"); + } } bool BmhParser::hasEndToken() const { @@ -1032,6 +1343,20 @@ TextParser::TextParser(const Definition* definition) : definition->fLineCount) { } +string TextParser::ReportFilename(string file) { + string fullName; +#ifdef SK_BUILD_FOR_WIN + TCHAR pathChars[MAX_PATH]; + DWORD pathLen = GetCurrentDirectory(MAX_PATH, pathChars); + for (DWORD index = 0; index < pathLen; ++index) { + fullName += pathChars[index] == (char)pathChars[index] ? (char)pathChars[index] : '?'; + } + fullName += '\\'; +#endif + fullName += file; + return fullName; +} + void TextParser::reportError(const char* errorStr) const { this->reportWarning(errorStr); SkDebugf(""); // convenient place to set a breakpoint @@ -1047,7 +1372,8 @@ void TextParser::reportWarning(const char* errorStr) const { spaces -= lineLen; lineLen = err.lineLength(); } - SkDebugf("\n%s(%zd): error: %s\n", fFileName.c_str(), err.fLineCount, errorStr); + string fullName = this->ReportFilename(fFileName); + SkDebugf("\n%s(%zd): error: %s\n", fullName.c_str(), err.fLineCount, errorStr); if (0 == lineLen) { SkDebugf("[blank line]\n"); } else { @@ -1185,6 +1511,18 @@ bool BmhParser::skipToDefinitionEnd(MarkType markType) { return this->reportError<bool>("unbalanced stack"); } +bool BmhParser::skipToString() { + this->skipSpace(); + if (fMC != this->peek()) { + return this->reportError<bool>("expected end mark"); + } + this->next(); + this->skipSpace(); + // body is text from here to double fMC + // no single fMC allowed, no linefeed allowed + return true; +} + vector<string> BmhParser::topicName() { vector<string> result; this->skipWhiteSpace(); @@ -1236,7 +1574,6 @@ vector<string> BmhParser::typeName(MarkType markType, bool* checkEnd) { this->skipNoName(); break; case MarkType::kCode: - case MarkType::kDeprecated: case MarkType::kDescription: case MarkType::kDoxygen: case MarkType::kExperimental: @@ -1250,19 +1587,25 @@ vector<string> BmhParser::typeName(MarkType markType, bool* checkEnd) { case MarkType::kTrack: this->skipNoName(); break; + case MarkType::kLine: + this->skipToString(); + break; case MarkType::kAlias: case MarkType::kAnchor: case MarkType::kBug: // fixme: expect number case MarkType::kDefine: case MarkType::kDefinedBy: + case MarkType::kDeprecated: case MarkType::kDuration: - case MarkType::kError: case MarkType::kFile: case MarkType::kHeight: + case MarkType::kIllustration: case MarkType::kImage: + case MarkType::kIn: case MarkType::kLiteral: case MarkType::kOutdent: case MarkType::kPlatform: + case MarkType::kPopulate: case MarkType::kReturn: case MarkType::kSeeAlso: case MarkType::kSet: @@ -1343,10 +1686,14 @@ string BmhParser::uniqueName(const string& base, MarkType markType) { int number = 2; string numBuilder(builder); do { - for (const auto& iter : fParent->fChildren) { + for (auto& iter : fParent->fChildren) { if (markType == iter->fMarkType) { if (iter->fName == numBuilder) { - fCloned = true; + if (iter->fDeprecated) { + iter->fClone = true; + } else { + fCloned = true; + } numBuilder = builder + '_' + to_string(number); goto tryNext; } @@ -1399,8 +1746,14 @@ tryNext: ; } if (MarkType::kMethod == markType) { cloned->fCloned = true; + if (cloned->fDeprecated) { + cloned->fClone = true; + } else { + fCloned = true; + } + } else { + fCloned = true; } - fCloned = true; numBuilder = builder + '_' + to_string(number); } while (++number); return numBuilder; @@ -1549,19 +1902,6 @@ int main(int argc, char** const argv) { SkCommandLineFlags::PrintUsage(); return 1; } - if (FLAGS_hack) { - if (FLAGS_bmh.isEmpty()) { - SkDebugf("-k or --hack requires -b\n"); - SkCommandLineFlags::PrintUsage(); - return 1; - } - HackParser hacker; - if (!hacker.parseFile(FLAGS_bmh[0], ".bmh")) { - SkDebugf("hack failed\n"); - return -1; - } - return 0; - } if ((FLAGS_include.isEmpty() || FLAGS_bmh.isEmpty()) && FLAGS_status.isEmpty() && FLAGS_populate) { SkDebugf("-p requires -b -i or -a\n"); @@ -1591,6 +1931,9 @@ int main(int argc, char** const argv) { } bmhParser.reset(); if (!FLAGS_bmh.isEmpty()) { + if (FLAGS_tokens) { + IncludeParser::RemoveFile(FLAGS_bmh[0], FLAGS_include[0]); + } if (!bmhParser.parseFile(FLAGS_bmh[0], ".bmh")) { return -1; } @@ -1599,6 +1942,19 @@ int main(int argc, char** const argv) { return -1; } } + if (FLAGS_hack) { + if (FLAGS_bmh.isEmpty()) { + SkDebugf("-k or --hack requires -b\n"); + SkCommandLineFlags::PrintUsage(); + return 1; + } + HackParser hacker(bmhParser); + if (!hacker.parseFile(FLAGS_bmh[0], ".bmh")) { + SkDebugf("hack failed\n"); + return -1; + } + return 0; + } if (FLAGS_selfcheck && !SelfCheck(bmhParser)) { return -1; } diff --git a/chromium/third_party/skia/tools/bookmaker/bookmaker.h b/chromium/third_party/skia/tools/bookmaker/bookmaker.h index 6752225d527..7bb743c9cb3 100644 --- a/chromium/third_party/skia/tools/bookmaker/bookmaker.h +++ b/chromium/third_party/skia/tools/bookmaker/bookmaker.h @@ -53,6 +53,7 @@ enum class KeyWord { kElse, kEndif, kEnum, + kError, kFloat, kFriend, kIf, @@ -98,7 +99,6 @@ enum class MarkType { kDuration, kEnum, kEnumClass, - kError, kExample, kExperimental, kExternal, @@ -106,8 +106,11 @@ enum class MarkType { kFormula, kFunction, kHeight, + kIllustration, kImage, + kIn, kLegend, + kLine, kLink, kList, kLiteral, // don't lookup hyperlinks, do substitution, etc @@ -118,6 +121,7 @@ enum class MarkType { kOutdent, kParam, kPlatform, + kPopulate, kPrivate, kReturn, kRoot, @@ -387,6 +391,7 @@ public: } void reportError(const char* errorStr) const; + static string ReportFilename(string file); void reportWarning(const char* errorStr) const; template <typename T> T reportError(const char* errorStr) const { @@ -763,6 +768,11 @@ public: kSubtractFrom, }; + enum class Format { + kIncludeReturn, + kOmitReturn, + }; + Definition() {} Definition(const char* start, const char* end, int line, Definition* parent) @@ -823,10 +833,23 @@ public: bool crossCheck2(const Definition& includeToken) const; bool crossCheck(const Definition& includeToken) const; bool crossCheckInside(const char* start, const char* end, const Definition& includeToken) const; + + const Definition* csParent() const { + Definition* test = fParent; + while (test) { + if (MarkType::kStruct == test->fMarkType || MarkType::kClass == test->fMarkType) { + return test; + } + test = test->fParent; + } + return nullptr; + } + bool exampleToScript(string* result, ExampleOptions ) const; string extractText(TrimExtract trimExtract) const; string fiddleName() const; - string formatFunction() const; + const Definition* findClone(string match) const; + string formatFunction(Format format) const; const Definition* hasChild(MarkType markType) const; bool hasMatch(const string& name) const; const Definition* hasParam(const string& ref) const; @@ -844,6 +867,7 @@ public: } virtual bool isRoot() const { return false; } + bool isStructOrClass() const; int length() const { return (int) (fContentEnd - fContentStart); @@ -870,6 +894,7 @@ public: } virtual RootDefinition* rootParent() { SkASSERT(0); return nullptr; } + virtual const RootDefinition* rootParent() const { SkASSERT(0); return nullptr; } void setCanonicalFiddle(); void setParentIndex() { @@ -878,6 +903,17 @@ public: void setWrapper(); + const Definition* topicParent() const { + Definition* test = fParent; + while (test) { + if (MarkType::kTopic == test->fMarkType) { + return test; + } + test = test->fParent; + } + return nullptr; + } + string fText; // if text is constructed instead of in a file, it's put here const char* fStart = nullptr; // .. in original text file, or the start of fText const char* fContentStart; // start past optional markup name @@ -902,9 +938,11 @@ public: Type fType = Type::kNone; bool fClone = false; bool fCloned = false; + bool fDeprecated = false; bool fOperatorConst = false; bool fPrivate = false; bool fShort = false; + bool fToBeDeprecated = false; bool fMemberStart = false; bool fAnonymous = false; mutable bool fVisited = false; @@ -941,6 +979,7 @@ public: const Definition* find(const string& ref, AllowParens ) const; bool isRoot() const override { return true; } RootDefinition* rootParent() override { return fRootParent; } + const RootDefinition* rootParent() const override { return fRootParent; } void setRootParent(RootDefinition* rootParent) { fRootParent = rootParent; } unordered_map<string, RootDefinition*> fBranches; @@ -1159,10 +1198,11 @@ public: }; enum class Resolvable { - kNo, // neither resolved nor output - kYes, // resolved, output - kOut, // not resolved, but output + kNo, // neither resolved nor output + kYes, // resolved, output + kOut, // not resolved, but output kLiteral, // output untouched (FIXME: is this really different from kOut?) + kClone, // resolved, output, with references to clones as well }; enum class Exemplary { @@ -1177,6 +1217,11 @@ public: kColumnEnd, }; + enum class HasTag { + kNo, + kYes, + }; + #define M(mt) (1LL << (int) MarkType::k##mt) #define M_D M(Description) #define M_CS M(Class) | M(Struct) @@ -1190,6 +1235,7 @@ public: #define R_Y Resolvable::kYes #define R_N Resolvable::kNo #define R_O Resolvable::kOut +#define R_C Resolvable::kClone #define E_Y Exemplary::kYes #define E_N Exemplary::kNo @@ -1207,7 +1253,7 @@ public: , { "Code", nullptr, MarkType::kCode, R_O, E_N, M_CSST | M_E | M(Method) } , { "", nullptr, MarkType::kColumn, R_Y, E_N, M(Row) } , { "", nullptr, MarkType::kComment, R_N, E_N, 0 } -, { "Const", &fConstMap, MarkType::kConst, R_Y, E_N, M_E | M_ST } +, { "Const", &fConstMap, MarkType::kConst, R_Y, E_O, M_E | M_ST } , { "Define", nullptr, MarkType::kDefine, R_O, E_N, M_ST } , { "DefinedBy", nullptr, MarkType::kDefinedBy, R_N, E_N, M(Method) } , { "Deprecated", nullptr, MarkType::kDeprecated, R_Y, E_N, 0 } @@ -1216,32 +1262,38 @@ public: , { "Duration", nullptr, MarkType::kDuration, R_N, E_N, M(Example) | M(NoExample) } , { "Enum", &fEnumMap, MarkType::kEnum, R_Y, E_O, M_CSST | M(Root) } , { "EnumClass", &fClassMap, MarkType::kEnumClass, R_Y, E_O, M_CSST | M(Root) } -, { "Error", nullptr, MarkType::kError, R_N, E_N, M(Example) | M(NoExample) } -, { "Example", nullptr, MarkType::kExample, R_O, E_N, M_CSST | M_E | M(Method) } +, { "Example", nullptr, MarkType::kExample, + R_O, E_N, M_CSST | M_E | M(Method) | M(Const) } , { "Experimental", nullptr, MarkType::kExperimental, R_Y, E_N, 0 } , { "External", nullptr, MarkType::kExternal, R_Y, E_N, M(Root) } , { "File", nullptr, MarkType::kFile, R_N, E_N, M(Track) } , { "Formula", nullptr, MarkType::kFormula, R_O, E_N, - M(Column) | M_ST | M(Member) | M(Method) | M_D } + M(Column) | M_E | M_ST | M(Member) | M(Method) | M_D } , { "Function", nullptr, MarkType::kFunction, R_O, E_N, M(Example) | M(NoExample) } , { "Height", nullptr, MarkType::kHeight, R_N, E_N, M(Example) | M(NoExample) } +, { "Illustration", nullptr, MarkType::kIllustration, R_N, E_N, M(Subtopic) } , { "Image", nullptr, MarkType::kImage, R_N, E_N, M(Example) | M(NoExample) } +, { "In", nullptr, MarkType::kIn, R_N, E_N, + M_CSST | M_E | M(Method) | M(Typedef) } , { "Legend", nullptr, MarkType::kLegend, R_Y, E_N, M(Table) } +, { "Line", nullptr, MarkType::kLine, R_N, E_N, + M_CSST | M_E | M(Method) | M(Typedef) } , { "", nullptr, MarkType::kLink, R_N, E_N, M(Anchor) } , { "List", nullptr, MarkType::kList, R_Y, E_N, M(Method) | M_CSST | M_E | M_D } , { "Literal", nullptr, MarkType::kLiteral, R_N, E_N, M(Code) } , { "", nullptr, MarkType::kMarkChar, R_N, E_N, 0 } -, { "Member", nullptr, MarkType::kMember, R_Y, E_N, M(Class) | M(Struct) } +, { "Member", nullptr, MarkType::kMember, R_Y, E_N, M_CSST } , { "Method", &fMethodMap, MarkType::kMethod, R_Y, E_Y, M_CSST } , { "NoExample", nullptr, MarkType::kNoExample, R_O, E_N, M_CSST | M_E | M(Method) } , { "Outdent", nullptr, MarkType::kOutdent, R_N, E_N, M(Code) } , { "Param", nullptr, MarkType::kParam, R_Y, E_N, M(Method) } , { "Platform", nullptr, MarkType::kPlatform, R_N, E_N, M(Example) | M(NoExample) } +, { "Populate", nullptr, MarkType::kPopulate, R_N, E_N, M(Subtopic) } , { "Private", nullptr, MarkType::kPrivate, R_N, E_N, 0 } , { "Return", nullptr, MarkType::kReturn, R_Y, E_N, M(Method) } , { "", nullptr, MarkType::kRoot, R_Y, E_N, 0 } , { "", nullptr, MarkType::kRow, R_Y, E_N, M(Table) | M(List) } -, { "SeeAlso", nullptr, MarkType::kSeeAlso, R_Y, E_N, +, { "SeeAlso", nullptr, MarkType::kSeeAlso, R_C, E_N, M_CSST | M_E | M(Method) | M(Typedef) } , { "Set", nullptr, MarkType::kSet, R_N, E_N, M(Example) | M(NoExample) } , { "StdOut", nullptr, MarkType::kStdOut, R_N, E_N, M(Example) | M(NoExample) } @@ -1250,7 +1302,7 @@ public: , { "Subtopic", nullptr, MarkType::kSubtopic, R_Y, E_Y, M_CSST } , { "Table", nullptr, MarkType::kTable, R_Y, E_N, M(Method) | M_CSST | M_E } , { "Template", nullptr, MarkType::kTemplate, R_Y, E_N, 0 } -, { "", nullptr, MarkType::kText, R_Y, E_N, 0 } +, { "", nullptr, MarkType::kText, R_N, E_N, 0 } , { "Time", nullptr, MarkType::kTime, R_Y, E_N, M(Track) } , { "ToDo", nullptr, MarkType::kToDo, R_N, E_N, 0 } , { "Topic", nullptr, MarkType::kTopic, R_Y, E_Y, M_CS | M(Root) | M(Topic) } @@ -1278,7 +1330,8 @@ public: ~BmhParser() override {} bool addDefinition(const char* defStart, bool hasEnd, MarkType markType, - const vector<string>& typeNameBuilder); + const vector<string>& typeNameBuilder, HasTag hasTag); + bool checkEndMarker(MarkType markType, string name) const; bool checkExamples() const; bool checkParamReturn(const Definition* definition) const; bool dumpExamples(const char* fiddleJsonFileName) const; @@ -1288,7 +1341,7 @@ public: int endHashCount() const; bool endTableColumn(const char* end, const char* terminator); - RootDefinition* findBmhObject(MarkType markType, const string& typeName) { + RootDefinition* findBmhObject(MarkType markType, const string& typeName) const { auto map = fMaps[(int) markType].fBmh; if (!map) { return nullptr; @@ -1297,6 +1350,7 @@ public: } bool findDefinitions(); + Definition* findExample(string name) const; MarkType getMarkType(MarkLookup lookup) const; bool hasEndToken() const; string memberName(); @@ -1331,6 +1385,7 @@ public: bool skipNoName(); bool skipToDefinitionEnd(MarkType markType); + bool skipToString(); void spellCheck(const char* match, SkCommandLineFlags::StringArray report) const; void spellStatus(const char* match, SkCommandLineFlags::StringArray report) const; vector<string> topicName(); @@ -1409,7 +1464,6 @@ public: , { nullptr, MarkType::kDuration } , { &fIEnumMap, MarkType::kEnum } , { &fIEnumMap, MarkType::kEnumClass } - , { nullptr, MarkType::kError } , { nullptr, MarkType::kExample } , { nullptr, MarkType::kExperimental } , { nullptr, MarkType::kExternal } @@ -1417,9 +1471,12 @@ public: , { nullptr, MarkType::kFormula } , { nullptr, MarkType::kFunction } , { nullptr, MarkType::kHeight } - , { nullptr, MarkType::kImage } - , { nullptr, MarkType::kLegend } - , { nullptr, MarkType::kLink } + , { nullptr, MarkType::kIllustration } + , { nullptr, MarkType::kImage } + , { nullptr, MarkType::kIn } + , { nullptr, MarkType::kLegend } + , { nullptr, MarkType::kLine } + , { nullptr, MarkType::kLink } , { nullptr, MarkType::kList } , { nullptr, MarkType::kLiteral } , { nullptr, MarkType::kMarkChar } @@ -1429,6 +1486,7 @@ public: , { nullptr, MarkType::kOutdent } , { nullptr, MarkType::kParam } , { nullptr, MarkType::kPlatform } + , { nullptr, MarkType::kPopulate } , { nullptr, MarkType::kPrivate } , { nullptr, MarkType::kReturn } , { nullptr, MarkType::kRoot } @@ -1475,7 +1533,7 @@ public: IClassDefinition* defineClass(const Definition& includeDef, const string& className); void dumpClassTokens(IClassDefinition& classDef); void dumpComment(const Definition& ); - void dumpEnum(const Definition& ); + void dumpEnum(const Definition& , const string& name); void dumpMethod(const Definition& ); void dumpMember(const Definition& ); bool dumpTokens(const string& directory); @@ -1523,7 +1581,7 @@ public: return false; } string name(path); - return parseInclude(name); + return this->parseInclude(name); } bool parseInclude(const string& name); @@ -1549,6 +1607,9 @@ public: this->addDefinition(container); } + static void RemoveFile(const char* docs, const char* includes); + static void RemoveOneFile(const char* docs, const char* includesFileOrPath); + void reset() override { INHERITED::resetCommon(); fRootTopic = nullptr; @@ -1679,6 +1740,14 @@ public: this->lf(1); } + void writeTableRow(size_t pad1, const string& col1, size_t pad2, const string& col2) { + this->lf(1); + string row = "# " + col1 + string(pad1 - col1.length(), ' ') + " # " + + col2 + string(pad2 - col2.length(), ' ') + " ##"; + this->writeString(row); + this->lf(1); + } + void writeTableTrailer() { this->lf(1); this->writeString("#Table ##"); @@ -1765,6 +1834,7 @@ public: enum class PunctuationState { kStart, kDelimiter, + kParen, // treated as a delimiter unless following a space, and followed by word kPeriod, kSpace, }; @@ -1775,6 +1845,11 @@ public: kExternal, }; + enum class SkipFirstLine { + kNo, + kYes, + }; + enum class Wrote { kNone, kLF, @@ -1812,10 +1887,13 @@ public: return 0 == size; } - void descriptionOut(const Definition* def); + void constOut(const Definition* memberStart, const Definition& child, + const Definition* bmhConst); + void descriptionOut(const Definition* def, SkipFirstLine ); void enumHeaderOut(const RootDefinition* root, const Definition& child); void enumMembersOut(const RootDefinition* root, Definition& child); void enumSizeItems(const Definition& child); + Definition* findMemberCommentBlock(const vector<Definition*>& bmhChildren, const string& name) const; int lookupMethod(const PunctuationState punctuation, const Word word, const int start, const int run, int lastWrite, const char* data, bool hasIndirection); @@ -1846,6 +1924,7 @@ public: Definition* structMemberOut(const Definition* memberStart, const Definition& child); void structOut(const Definition* root, const Definition& child, const char* commentStart, const char* commentEnd); + void structSetMembersShort(const vector<Definition*>& bmhChildren); void structSizeMembers(const Definition& child); private: BmhParser* fBmhParser; @@ -1886,7 +1965,7 @@ protected: INHERITED::resetCommon(); } - Definition* findExample(const string& name) const; + Definition* findExample(string name) const { return fBmhParser->findExample(name); } bool parseFiddles(); virtual bool pngOut(Definition* example) = 0; virtual bool textOut(Definition* example, const char* stdOutStart, @@ -1947,10 +2026,15 @@ private: class HackParser : public ParserCommon { public: - HackParser() : ParserCommon() { + HackParser(const BmhParser& bmhParser) + : ParserCommon() + , fBmhParser(bmhParser) { this->reset(); } + void addOneLiner(const Definition* defTable, const Definition* child, bool hasLine, + bool lfAfter); + bool parseFromFile(const char* path) override { if (!INHERITED::parseSetup(path)) { return false; @@ -1962,7 +2046,19 @@ public: INHERITED::resetCommon(); } + string searchTable(const Definition* tableHolder, const Definition* match); + + void topicIter(const Definition* ); + private: + const BmhParser& fBmhParser; + const Definition* fClassesAndStructs; + const Definition* fConstants; + const Definition* fConstructors; + const Definition* fMemberFunctions; + const Definition* fMembers; + const Definition* fOperators; + const Definition* fRelatedFunctions; bool hackFiles(); typedef ParserCommon INHERITED; @@ -1977,6 +2073,17 @@ public: bool buildReferences(const char* docDir, const char* mdOutDirOrFile); bool buildStatus(const char* docDir, const char* mdOutDir); + + static constexpr const char* kClassesAndStructs = "Class_or_Struct"; + static constexpr const char* kConstants = "Constant"; + static constexpr const char* kConstructors = "Constructor"; + static constexpr const char* kMemberFunctions = "Member_Function"; + static constexpr const char* kMembers = "Member"; + static constexpr const char* kOperators = "Operator"; + static constexpr const char* kOverview = "Overview"; + static constexpr const char* kRelatedFunctions = "Related_Function"; + static constexpr const char* kSubtopics = "Overview_Subtopic"; + private: enum class TableState { kNone, @@ -1984,19 +2091,38 @@ private: kColumn, }; + struct TableContents { + TableContents() + : fShowClones(false) { + } + + string fDescription; + vector<const Definition*> fMembers; + bool fShowClones; + }; + string addReferences(const char* start, const char* end, BmhParser::Resolvable ); bool buildRefFromFile(const char* fileName, const char* outDir); bool checkParamReturnBody(const Definition* def) const; void childrenOut(const Definition* def, const char* contentStart); + const Definition* csParent() const; const Definition* findParamType(); const Definition* isDefined(const TextParser& parser, const string& ref, bool report) const; string linkName(const Definition* ) const; - string linkRef(const string& leadingSpaces, const Definition*, const string& ref) const; + string linkRef(const string& leadingSpaces, const Definition*, const string& ref, + BmhParser::Resolvable ) const; void markTypeOut(Definition* ); void mdHeaderOut(int depth) { mdHeaderOutLF(depth, 2); } void mdHeaderOutLF(int depth, int lf); - bool parseFromFile(const char* path) override { - return true; + void overviewOut(); + bool parseFromFile(const char* path) override { return true; } + void populateTables(const Definition* def); + + TableContents& populator(const char* key) { + auto entry = fPopulators.find(key); + // FIXME: this should have been detected earlier + SkASSERT(fPopulators.end() != entry); + return entry->second; } void reset() override { @@ -2029,11 +2155,17 @@ private: } void resolveOut(const char* start, const char* end, BmhParser::Resolvable ); + void rowOut(const char * name, const string& description); + void subtopicOut(const TableContents& tableContents); + void subtopicsOut(); + + unordered_map<string, TableContents> fPopulators; + vector<const Definition*> fClassStack; const BmhParser& fBmhParser; const Definition* fEnumClass; Definition* fMethod; - RootDefinition* fRoot; + const RootDefinition* fRoot; const Definition* fLastParam; TableState fTableState; bool fHasFiddle; diff --git a/chromium/third_party/skia/tools/bookmaker/definition.cpp b/chromium/third_party/skia/tools/bookmaker/definition.cpp index 74f4d5a7fb0..68e001aebb6 100644 --- a/chromium/third_party/skia/tools/bookmaker/definition.cpp +++ b/chromium/third_party/skia/tools/bookmaker/definition.cpp @@ -533,9 +533,6 @@ bool Definition::exampleToScript(string* result, ExampleOptions exampleOptions) case MarkType::kDuration: durationStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart); break; - case MarkType::kError: - result->clear(); - return true; case MarkType::kHeight: heightStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart); break; @@ -563,6 +560,7 @@ bool Definition::exampleToScript(string* result, ExampleOptions exampleOptions) break; case MarkType::kToDo: break; + case MarkType::kBug: case MarkType::kMarkChar: case MarkType::kPlatform: // ignore for now @@ -899,7 +897,7 @@ bool Definition::crossCheckInside(const char* start, const char* end, return false; } -string Definition::formatFunction() const { +string Definition::formatFunction(Format format) const { const char* end = fContentStart; while (end > fStart && ' ' >= end[-1]) { --end; @@ -915,6 +913,9 @@ string Definition::formatFunction() const { const char* nameInParser = methodParser.strnstr(name.c_str(), methodParser.fEnd); methodParser.skipTo(nameInParser); const char* lastEnd = methodParser.fChar; + if (Format::kOmitReturn == format) { + lastStart = lastEnd; + } const char* paren = methodParser.strnchr('(', methodParser.fEnd); size_t indent; if (paren) { @@ -985,8 +986,10 @@ string Definition::formatFunction() const { if (delimiter) { if (nextEnd - nextStart >= (ptrdiff_t) (limit - written)) { written = indent; - methodStr += '\n'; - methodStr += string(indent, ' '); + if (Format::kIncludeReturn == format) { + methodStr += '\n'; + methodStr += string(indent, ' '); + } } methodParser.skipTo(delimiter); } @@ -1017,6 +1020,22 @@ string Definition::fiddleName() const { return fFiddle.substr(start, end - start); } +const Definition* Definition::findClone(string match) const { + for (auto child : fChildren) { + if (!child->fClone) { + continue; + } + if (match == child->fName) { + return child; + } + auto inner = child->findClone(match); + if (inner) { + return inner; + } + } + return nullptr; +} + const Definition* Definition::hasChild(MarkType markType) const { for (auto iter : fChildren) { if (markType == iter->fMarkType) { @@ -1052,6 +1071,16 @@ bool Definition::hasMatch(const string& name) const { return false; } +bool Definition::isStructOrClass() const { + if (MarkType::kStruct != fMarkType && MarkType::kClass != fMarkType) { + return false; + } + if (string::npos != fFileName.find("undocumented.bmh")) { + return false; + } + return true; +} + bool Definition::methodHasReturn(const string& name, TextParser* methodParser) const { if (methodParser->skipExact("static")) { methodParser->skipWhiteSpace(); @@ -1190,7 +1219,73 @@ string Definition::NormalizedName(string name) { return normalizedName; } -bool Definition::paramsMatch(const string& match, const string& name) const { +static string unpreformat(const string& orig) { + string result; + int amp = 0; + for (auto c : orig) { + switch (amp) { + case 0: + if ('&' == c) { + amp = 1; + } else { + amp = 0; + result += c; + } + break; + case 1: + if ('l' == c) { + amp = 2; + } else if ('g' == c) { + amp = 3; + } else { + amp = 0; + result += "&"; + result += c; + } + break; + case 2: + if ('t' == c) { + amp = 4; + } else { + amp = 0; + result += "&l"; + result += c; + } + break; + case 3: + if ('t' == c) { + amp = 5; + } else { + amp = 0; + result += "&g"; + result += c; + } + break; + case 4: + if (';' == c) { + result += '<'; + } else { + result += "<"; + result += c; + } + amp = 0; + break; + case 5: + if (';' == c) { + result += '>'; + } else { + result += ">"; + result += c; + } + amp = 0; + break; + } + } + return result; +} + +bool Definition::paramsMatch(const string& matchFormatted, const string& name) const { + string match = unpreformat(matchFormatted); TextParser def(fFileName, fStart, fContentStart, fLineCount); const char* dName = def.strnstr(name.c_str(), fContentStart); if (!dName) { diff --git a/chromium/third_party/skia/tools/bookmaker/fiddleParser.cpp b/chromium/third_party/skia/tools/bookmaker/fiddleParser.cpp index faf551006f7..682c87c845b 100644 --- a/chromium/third_party/skia/tools/bookmaker/fiddleParser.cpp +++ b/chromium/third_party/skia/tools/bookmaker/fiddleParser.cpp @@ -7,32 +7,6 @@ #include "bookmaker.h" -static Definition* find_fiddle(Definition* def, const string& name) { - if (MarkType::kExample == def->fMarkType && name == def->fFiddle) { - return def; - } - for (auto& child : def->fChildren) { - Definition* result = find_fiddle(child, name); - if (result) { - return result; - } - } - return nullptr; -} - -Definition* FiddleBase::findExample(const string& name) const { - for (const auto& topic : fBmhParser->fTopicMap) { - if (topic.second->fParent) { - continue; - } - Definition* def = find_fiddle(topic.second, name); - if (def) { - return def; - } - } - return nullptr; -} - bool FiddleBase::parseFiddles() { if (!this->skipExact("{\n")) { return false; diff --git a/chromium/third_party/skia/tools/bookmaker/includeParser.cpp b/chromium/third_party/skia/tools/bookmaker/includeParser.cpp index 26281b61845..385b9c92da7 100644 --- a/chromium/third_party/skia/tools/bookmaker/includeParser.cpp +++ b/chromium/third_party/skia/tools/bookmaker/includeParser.cpp @@ -6,6 +6,8 @@ */ #include "bookmaker.h" +#include "SkOSFile.h" +#include "SkOSPath.h" const IncludeKey kKeyWords[] = { { "", KeyWord::kNone, KeyProperty::kNone }, @@ -22,6 +24,7 @@ const IncludeKey kKeyWords[] = { { "else", KeyWord::kElse, KeyProperty::kPreprocessor }, { "endif", KeyWord::kEndif, KeyProperty::kPreprocessor }, { "enum", KeyWord::kEnum, KeyProperty::kObject }, + { "error", KeyWord::kError, KeyProperty::kPreprocessor }, { "float", KeyWord::kFloat, KeyProperty::kNumber }, { "friend", KeyWord::kFriend, KeyProperty::kModifier }, { "if", KeyWord::kIf, KeyProperty::kPreprocessor }, @@ -156,6 +159,7 @@ bool IncludeParser::checkForWord() { // these do not link to other # directives case KeyWord::kDefine: case KeyWord::kInclude: + case KeyWord::kError: break; // these start a # directive link case KeyWord::kIf: @@ -338,8 +342,10 @@ bool IncludeParser::crossCheck(BmhParser& bmhParser) { def = root->find(withParens, RootDefinition::AllowParens::kNo); } if (!def) { - SkDebugf("method missing from bmh: %s\n", fullName.c_str()); - fFailed = true; + if (!root->fDeprecated) { + SkDebugf("method missing from bmh: %s\n", fullName.c_str()); + fFailed = true; + } break; } if (def->crossCheck2(token)) { @@ -385,8 +391,10 @@ bool IncludeParser::crossCheck(BmhParser& bmhParser) { def = root->find(anonName, RootDefinition::AllowParens::kYes); } if (!def) { - SkDebugf("enum missing from bmh: %s\n", fullName.c_str()); - fFailed = true; + if (!root->fDeprecated) { + SkDebugf("enum missing from bmh: %s\n", fullName.c_str()); + fFailed = true; + } break; } } @@ -398,8 +406,10 @@ bool IncludeParser::crossCheck(BmhParser& bmhParser) { } } if (MarkType::kCode != def->fMarkType) { - SkDebugf("enum code missing from bmh: %s\n", fullName.c_str()); - fFailed = true; + if (!root->fDeprecated) { + SkDebugf("enum code missing from bmh: %s\n", fullName.c_str()); + fFailed = true; + } break; } if (def->crossCheck(token)) { @@ -419,8 +429,10 @@ bool IncludeParser::crossCheck(BmhParser& bmhParser) { } if (!def) { if (string::npos == child->fName.find("Legacy_")) { - SkDebugf("const missing from bmh: %s\n", constName.c_str()); - fFailed = true; + if (!root->fDeprecated) { + SkDebugf("const missing from bmh: %s\n", constName.c_str()); + fFailed = true; + } } } else { def->fVisited = true; @@ -430,7 +442,7 @@ bool IncludeParser::crossCheck(BmhParser& bmhParser) { case MarkType::kMember: if (def) { def->fVisited = true; - } else { + } else if (!root->fDeprecated) { SkDebugf("member missing from bmh: %s\n", fullName.c_str()); fFailed = true; } @@ -438,7 +450,7 @@ bool IncludeParser::crossCheck(BmhParser& bmhParser) { case MarkType::kTypedef: if (def) { def->fVisited = true; - } else { + } else if (!root->fDeprecated) { SkDebugf("typedef missing from bmh: %s\n", fullName.c_str()); fFailed = true; } @@ -471,7 +483,7 @@ bool IncludeParser::crossCheck(BmhParser& bmhParser) { } if (crossChecks) { if (1 == crossChecks) { - SkDebugf("%s", firstCheck.c_str()); + SkDebugf(" %s", firstCheck.c_str()); } SkDebugf("\n"); } @@ -527,7 +539,7 @@ void IncludeParser::dumpClassTokens(IClassDefinition& classDef) { switch (token.fMarkType) { case MarkType::kEnum: case MarkType::kEnumClass: - this->dumpEnum(token); + this->dumpEnum(token, token.fName); break; case MarkType::kMethod: this->dumpMethod(token); @@ -582,8 +594,9 @@ void IncludeParser::dumpComment(const Definition& token) { Definition methodName; TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd, token.fLineCount); + bool debugCode = methodParser.skipExact("SkDEBUGCODE("); if (MarkType::kMethod == token.fMarkType) { - methodName.fName = string(token.fContentStart, + methodName.fName = debugCode ? token.fName : string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart)); methodHasReturn = !methodParser.startsWith("void ") && !methodParser.startsWith("static void ") @@ -731,8 +744,8 @@ void IncludeParser::dumpComment(const Definition& token) { } } -void IncludeParser::dumpEnum(const Definition& token) { - this->writeTag("Enum", token.fName); +void IncludeParser::dumpEnum(const Definition& token, const string& name) { + this->writeTag("Enum", name); this->lf(2); this->writeString("#Code"); this->lfAlways(1); @@ -896,40 +909,29 @@ bool IncludeParser::dumpTokens(const string& dir, const string& skClassName) { } this->lf(2); string className(skClassName.substr(2)); - vector<string> sortedClasses; - size_t maxLen = 0; + vector<string> classNames; + vector<string> constNames; + vector<string> constructorNames; + vector<string> memberNames; + vector<string> operatorNames; + size_t classMaxLen = 0; + size_t constMaxLen = 0; + size_t constructorMaxLen = 0; + size_t memberMaxLen = 0; + size_t operatorMaxLen = 0; for (const auto& oneClass : fIClassMap) { if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) { continue; } string structName = oneClass.first.substr(skClassName.length() + 2); - maxLen = SkTMax(maxLen, structName.length()); - sortedClasses.emplace_back(structName); + classMaxLen = SkTMax(classMaxLen, structName.length()); + classNames.emplace_back(structName); } - this->writeTag("Topic", "Overview"); - this->lf(2); - this->writeTag("Subtopic", "Subtopics"); - this->writeEndTag("ToDo", "manually add subtopics"); - this->writeTableHeader("topics", 0, "description"); - this->writeTableTrailer(); - this->writeEndTag(); - this->lf(2); - if (maxLen) { - this->writeTag("Subtopic", "Structs"); - this->writeTableHeader("description", maxLen, "struct"); - for (auto& name : sortedClasses) { - this->writeTableRow(maxLen, name); - } - this->writeTableTrailer(); - this->writeEndTag("Subtopic"); - this->lf(2); + for (const auto& oneEnum : fIEnumMap) { + string enumName = oneEnum.first; + constMaxLen = SkTMax(constMaxLen, enumName.length()); + constNames.emplace_back(enumName); } - maxLen = 0; - size_t constructorMax = 0; - size_t operatorMax = 0; - vector<string> sortedNames; - vector<string> constructorNames; - vector<string> operatorNames; for (const auto& token : classMap.fTokens) { if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) { continue; @@ -940,13 +942,13 @@ bool IncludeParser::dumpTokens(const string& dir, const string& skClassName) { } if ((name.substr(0, 2) == "Sk" && 2 == name.find(className)) || '~' == name[0]) { name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart)); - constructorMax = SkTMax(constructorMax, name.length()); + constructorMaxLen = SkTMax(constructorMaxLen, name.length()); constructorNames.emplace_back(name); continue; } if (name.substr(0, 8) == "operator") { name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart)); - operatorMax = SkTMax(operatorMax, name.length()); + operatorMaxLen = SkTMax(operatorMaxLen, name.length()); operatorNames.emplace_back(name); continue; } @@ -958,45 +960,119 @@ bool IncludeParser::dumpTokens(const string& dir, const string& skClassName) { } size_t paren = name.find('('); size_t funcLen = string::npos == paren ? name.length() : paren; - maxLen = SkTMax(maxLen, funcLen); - sortedNames.emplace_back(name); + memberMaxLen = SkTMax(memberMaxLen, funcLen); + memberNames.emplace_back(name); + } + this->writeTag("Topic", "Overview"); + this->lf(2); + this->writeTag("Subtopic", "Subtopics"); + string classesName = classMaxLen ? "Classes_and_Structs" : ""; + string constsName = constructorMaxLen ? "Constants" : ""; + string constructorsName = constructorMaxLen ? "Constructors" : ""; + string membersName = memberMaxLen ? "Member_Functions" : ""; + string operatorsName = operatorMaxLen ? "Operators" : ""; + size_t nameLen = SkTMax(classesName.size(), SkTMax(constsName.size(), + SkTMax(constructorsName.size(), SkTMax(membersName.size(), operatorsName.size())))); + this->writeTableHeader("name", nameLen, "description"); + string classDesc = classMaxLen ? "embedded struct and class members" : ""; + string constDesc = constMaxLen ? "enum and enum class, const values" : ""; + string constructorDesc = constructorMaxLen ? "functions that construct " + className : ""; + string memberDesc = memberMaxLen ? "static functions and member methods" : ""; + string operatorDesc = operatorMaxLen ? "operator overloading methods" : ""; + size_t descLen = SkTMax(classDesc.size(), SkTMax(constDesc.size(), SkTMax(constructorDesc.size(), + SkTMax(memberDesc.size(), operatorDesc.size())))); + if (classMaxLen) { + this->writeTableRow(nameLen, classesName, descLen, classDesc); + } + if (constMaxLen) { + this->writeTableRow(nameLen, constsName, descLen, constDesc); + } + if (constructorMaxLen) { + this->writeTableRow(nameLen, constructorsName, descLen, constructorDesc); + } + if (memberMaxLen) { + this->writeTableRow(nameLen, membersName, descLen, memberDesc); + } + if (operatorMaxLen) { + this->writeTableRow(nameLen, operatorsName, descLen, operatorDesc); + } + this->writeTableTrailer(); + this->writeEndTag(); + this->lf(2); + if (classMaxLen) { + std::sort(classNames.begin(), classNames.end()); + this->writeTag("Subtopic", "Classes_and_Structs"); + this->writeTableHeader("name", classMaxLen, "description"); + for (auto& name : classNames) { + this->writeTableRow(classMaxLen, name); + } + this->writeTableTrailer(); + this->writeEndTag("Subtopic"); + this->lf(2); + } + if (constMaxLen) { + std::sort(constNames.begin(), constNames.end()); + this->writeTag("Subtopic", "Constants"); + this->writeTableHeader("name", constMaxLen, "description"); + for (auto& name : constNames) { + this->writeTableRow(constMaxLen, name); + } + this->writeTableTrailer(); + this->writeEndTag("Subtopic"); + this->lf(2); } - if (constructorMax) { + if (constructorMaxLen) { std::sort(constructorNames.begin(), constructorNames.end()); this->writeTag("Subtopic", "Constructors"); - this->writeTableHeader("description", constructorMax, "function"); + this->writeTableHeader("name", constructorMaxLen, "description"); for (auto& name : constructorNames) { - this->writeTableRow(constructorMax, name); + this->writeTableRow(constructorMaxLen, name); } this->writeTableTrailer(); this->writeEndTag("Subtopic"); this->lf(2); } - if (operatorMax) { + if (operatorMaxLen) { std::sort(operatorNames.begin(), operatorNames.end()); this->writeTag("Subtopic", "Operators"); - this->writeTableHeader("description", operatorMax, "function"); + this->writeTableHeader("name", operatorMaxLen, "description"); for (auto& name : operatorNames) { - this->writeTableRow(operatorMax, name); + this->writeTableRow(operatorMaxLen, name); } this->writeTableTrailer(); this->writeEndTag("Subtopic"); this->lf(2); } - std::sort(sortedNames.begin(), sortedNames.end()); - this->writeTag("Subtopic", "Member_Functions"); - this->writeTableHeader("description", maxLen, "function"); - for (auto& name : sortedNames) { - size_t paren = name.find('('); - size_t funcLen = string::npos == paren ? name.length() : paren; - this->writeTableRow(maxLen, name.substr(0, funcLen)); + if (memberMaxLen) { + std::sort(memberNames.begin(), memberNames.end()); + this->writeTag("Subtopic", "Member_Functions"); + this->writeTableHeader("name", memberMaxLen, "description"); + for (auto& name : memberNames) { + size_t paren = name.find('('); + size_t funcLen = string::npos == paren ? name.length() : paren; + this->writeTableRow(memberMaxLen, name.substr(0, funcLen)); + } + this->writeTableTrailer(); + this->writeEndTag("Subtopic"); + this->lf(2); } - this->writeTableTrailer(); - this->writeEndTag("Subtopic"); - this->lf(2); this->writeEndTag("Topic"); this->lf(2); - + for (auto& oneEnum : fIEnumMap) { + this->writeString( + "# ------------------------------------------------------------------------------"); + this->dumpEnum(oneEnum.second, oneEnum.first); + this->lf(2); + this->writeTag("Example"); + this->lfcr(); + this->writeString("// incomplete"); + this->writeEndTag(); + this->lf(2); + this->writeTag("SeeAlso", "incomplete"); + this->lf(2); + this->writeEndTag("Enum", oneEnum.first); + this->lf(2); + } for (auto& oneClass : fIClassMap) { if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) { continue; @@ -1231,7 +1307,13 @@ bool IncludeParser::parseDefine() { } bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) { - string nameStr; + TextParser parser(child); + parser.skipToEndBracket('{'); + if (parser.eof()) { + return true; // if enum is a forward declaration, do nothing + } + parser.next(); + string nameStr; if (child->fTokens.size() > 0) { auto token = child->fTokens.begin(); if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) { @@ -1241,26 +1323,42 @@ bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) { nameStr += string(token->fStart, token->fContentEnd - token->fStart); } } - markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd, - child->fLineCount, markupDef); - Definition* markupChild = &markupDef->fTokens.back(); + Definition* markupChild; + if (!markupDef) { + auto finder = fIEnumMap.find(nameStr); + if (fIEnumMap.end() != finder) { + return child->reportError<bool>("duplicate global enum name"); + } + markupChild = &fIEnumMap[nameStr]; + markupChild->fContentStart = child->fContentStart; + markupChild->fName = nameStr; + markupChild->fFiddle = nameStr; + markupChild->fContentEnd = child->fContentEnd; + markupChild->fFileName = child->fFileName; + markupChild->fLineCount = child->fLineCount; + } else { + markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd, + child->fLineCount, markupDef); + markupChild = &markupDef->fTokens.back(); + } SkASSERT(KeyWord::kNone == markupChild->fKeyWord); markupChild->fKeyWord = KeyWord::kEnum; TextParser enumName(child); enumName.skipExact("enum "); + enumName.skipWhiteSpace(); if (enumName.skipExact("class ")) { + enumName.skipWhiteSpace(); markupChild->fMarkType = MarkType::kEnumClass; } const char* nameStart = enumName.fChar; enumName.skipToSpace(); - markupChild->fName = markupDef->fName + "::" + - string(nameStart, (size_t) (enumName.fChar - nameStart)); + if (markupDef) { + markupChild->fName = markupDef->fName + "::"; + } + markupChild->fName += string(nameStart, (size_t) (enumName.fChar - nameStart)); if (!this->findComments(*child, markupChild)) { return false; } - TextParser parser(child); - parser.skipToEndBracket('{'); - parser.next(); const char* dataEnd; do { parser.skipWhiteSpace(); @@ -1311,6 +1409,10 @@ bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) { if ('=' == parser.peek()) { parser.skipToEndBracket(','); } + if (!parser.eof() && '#' == parser.peek()) { + // fixme: handle preprecessor, but just skip it for now + continue; + } if (parser.eof() || ',' != parser.peek()) { return this->reportError<bool>("enum member must end with comma 2"); } @@ -1367,11 +1469,13 @@ bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) { // FIXME: ? add comment as well ? markupChild->fChildren.push_back(member); } - IClassDefinition& classDef = fIClassMap[markupDef->fName]; - SkASSERT(classDef.fStart); - string uniqueName = this->uniqueName(classDef.fEnums, nameStr); - markupChild->fName = uniqueName; - classDef.fEnums[uniqueName] = markupChild; + if (markupDef) { + IClassDefinition& classDef = fIClassMap[markupDef->fName]; + SkASSERT(classDef.fStart); + string uniqueName = this->uniqueName(classDef.fEnums, nameStr); + markupChild->fName = uniqueName; + classDef.fEnums[uniqueName] = markupChild; + } return true; } @@ -1697,6 +1801,7 @@ bool IncludeParser::parseObject(Definition* child, Definition* markupDef) { if (child->boilerplateEndIf()) { break; } + case KeyWord::kError: case KeyWord::kInclude: // ignored for now break; @@ -1809,7 +1914,7 @@ bool IncludeParser::parseChar() { if (KeyWord::kNone == keyWord) { return this->reportError<bool>("unhandled preprocessor directive"); } - if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord) { + if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) { this->popBracket(); } } else if (Bracket::kSlashSlash == this->topBracket()) { @@ -2218,3 +2323,36 @@ void IncludeParser::validate() const { } IncludeParser::ValidateKeyWords(); } + +void IncludeParser::RemoveFile(const char* docs, const char* includes) { + if (!sk_isdir(includes)) { + IncludeParser::RemoveOneFile(docs, includes); + } else { + SkOSFile::Iter it(includes, ".h"); + for (SkString file; it.next(&file); ) { + SkString p = SkOSPath::Join(includes, file.c_str()); + const char* hunk = p.c_str(); + if (!SkStrEndsWith(hunk, ".h")) { + continue; + } + IncludeParser::RemoveOneFile(docs, hunk); + } + } +} + +void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) { + const char* lastForward = strrchr(includesFile, '/'); + const char* lastBackward = strrchr(includesFile, '\\'); + const char* last = lastForward > lastBackward ? lastForward : lastBackward; + if (!last) { + last = includesFile; + } else { + last += 1; + } + SkString baseName(last); + SkASSERT(baseName.endsWith(".h")); + baseName.remove(baseName.size() - 2, 2); + baseName.append("_Reference.bmh"); + SkString fullName = SkOSPath::Join(docs, baseName.c_str()); + remove(fullName.c_str()); +} diff --git a/chromium/third_party/skia/tools/bookmaker/includeWriter.cpp b/chromium/third_party/skia/tools/bookmaker/includeWriter.cpp index 230e524703e..328e9412cbc 100644 --- a/chromium/third_party/skia/tools/bookmaker/includeWriter.cpp +++ b/chromium/third_party/skia/tools/bookmaker/includeWriter.cpp @@ -7,11 +7,34 @@ #include "bookmaker.h" -void IncludeWriter::descriptionOut(const Definition* def) { +void IncludeWriter::constOut(const Definition* memberStart, const Definition& child, + const Definition* bmhConst) { + const char* bodyEnd = fDeferComment ? fDeferComment->fContentStart - 1 : + memberStart->fContentStart; + this->writeBlockTrim((int) (bodyEnd - fStart), fStart); // may write nothing + this->lf(2); + this->writeCommentHeader(); + fIndent += 4; + this->descriptionOut(bmhConst, SkipFirstLine::kYes); + fIndent -= 4; + this->writeCommentTrailer(); + fStart = memberStart->fContentStart; +} + +void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirstLine) { const char* commentStart = def->fContentStart; + if (SkipFirstLine::kYes == skipFirstLine) { + TextParser parser(def); + SkAssertResult(parser.skipLine()); + commentStart = parser.fChar; + } int commentLen = (int) (def->fContentEnd - commentStart); bool breakOut = false; SkDEBUGCODE(bool wroteCode = false); + if (def->fDeprecated) { + this->writeString(def->fToBeDeprecated ? "To be deprecated soon." : "Deprecated."); + this->lfcr(); + } for (auto prop : def->fChildren) { switch (prop->fMarkType) { case MarkType::kCode: { @@ -52,6 +75,12 @@ void IncludeWriter::descriptionOut(const Definition* def) { case MarkType::kDefinedBy: commentStart = prop->fTerminator; break; + case MarkType::kBug: { + string bugstr("(see skbug.com/" + string(prop->fContentStart, + prop->fContentEnd - prop->fContentStart) + ')'); + this->writeString(bugstr); + this->lfcr(); + } case MarkType::kDeprecated: case MarkType::kPrivate: commentLen = (int) (prop->fStart - commentStart); @@ -62,6 +91,11 @@ void IncludeWriter::descriptionOut(const Definition* def) { } } commentStart = prop->fContentStart; + if (def->fToBeDeprecated) { + commentStart += 4; // skip over "soon" // FIXME: this is awkward + } else if (MarkType::kBug == prop->fMarkType) { + commentStart = prop->fContentEnd; + } commentLen = (int) (prop->fContentEnd - commentStart); if (commentLen > 0) { this->writeBlockIndent(commentLen, commentStart); @@ -114,6 +148,8 @@ void IncludeWriter::descriptionOut(const Definition* def) { } } } break; + case MarkType::kIn: + case MarkType::kLine: case MarkType::kToDo: commentLen = (int) (prop->fStart - commentStart); if (commentLen > 0) { @@ -139,7 +175,7 @@ void IncludeWriter::descriptionOut(const Definition* def) { SkASSERT(MarkType::kColumn == column->fMarkType); this->writeString("-"); this->writeSpace(); - this->descriptionOut(column); + this->descriptionOut(column, SkipFirstLine::kNo); this->lf(1); } } @@ -157,7 +193,7 @@ void IncludeWriter::descriptionOut(const Definition* def) { break; } } - SkASSERT(wroteCode || (commentLen > 0 && commentLen < 1500)); + SkASSERT(wroteCode || (commentLen > 0 && commentLen < 1500) || def->fDeprecated); if (commentLen > 0) { this->rewriteBlock(commentLen, commentStart, Phrase::kNo); } @@ -384,9 +420,16 @@ void IncludeWriter::enumMembersOut(const RootDefinition* root, Definition& child SkASSERT(currentEnumItem); if (currentEnumItem->fShort) { this->indentToColumn(fEnumItemCommentTab); - this->writeString("//!<"); - this->writeSpace(); - this->rewriteBlock(commentLen, commentStart, Phrase::kNo); + if (commentLen || currentEnumItem->fDeprecated) { + this->writeString("//!<"); + this->writeSpace(); + if (currentEnumItem->fDeprecated) { + this->writeString(child.fToBeDeprecated ? "to be deprecated soon" + : "deprecated"); + } else { + this->rewriteBlock(commentLen, commentStart, Phrase::kNo); + } + } } if (onePast) { fIndent -= 4; @@ -438,23 +481,35 @@ void IncludeWriter::enumMembersOut(const RootDefinition* root, Definition& child commentEnd = currentEnumItem->fContentEnd; } TextParser enumComment(fFileName, commentStart, commentEnd, currentEnumItem->fLineCount); + bool isDeprecated = false; if (enumComment.skipToLineStart()) { // skip const value commentStart = enumComment.fChar; commentLen = (int) (commentEnd - commentStart); } else { - const Definition* privateDef = currentEnumItem->fChildren[0]; - SkASSERT(MarkType::kPrivate == privateDef->fMarkType); - commentStart = privateDef->fContentStart; - commentLen = (int) (privateDef->fContentEnd - privateDef->fContentStart); + const Definition* childDef = currentEnumItem->fChildren[0]; + isDeprecated = MarkType::kDeprecated == childDef->fMarkType; + if (MarkType::kPrivate == childDef->fMarkType || isDeprecated) { + commentStart = childDef->fContentStart; + if (currentEnumItem->fToBeDeprecated) { + SkASSERT(isDeprecated); + commentStart += 4; // skip over "soon" // FIXME: this is awkward + } + commentLen = (int) (childDef->fContentEnd - commentStart); + } } // FIXME: may assert here if there's no const value // should have detected and errored on that earlier when enum fContentStart was set - SkASSERT(commentLen > 0 && commentLen < 1000); + SkASSERT((commentLen > 0 && commentLen < 1000) || isDeprecated); if (!currentEnumItem->fShort) { this->writeCommentHeader(); fIndent += 4; - bool wroteLineFeed = Wrote::kLF == - this->rewriteBlock(commentLen, commentStart, Phrase::kNo); + bool wroteLineFeed = false; + if (isDeprecated) { + this->writeString(currentEnumItem->fToBeDeprecated + ? "To be deprecated soon." : "Deprecated."); + } + wroteLineFeed = Wrote::kLF == + this->rewriteBlock(commentLen, commentStart, Phrase::kNo); fIndent -= 4; if (wroteLineFeed || fColumn > 100 - 3 /* space * / */ ) { this->lfcr(); @@ -599,7 +654,7 @@ void IncludeWriter::methodOut(const Definition* method, const Definition& child) } this->writeCommentHeader(); fIndent += 4; - this->descriptionOut(method); + this->descriptionOut(method, SkipFirstLine::kNo); // compute indention column size_t column = 0; bool hasParmReturn = false; @@ -650,6 +705,7 @@ void IncludeWriter::methodOut(const Definition* method, const Definition& child) this->writeCommentTrailer(); fBmhMethod = nullptr; fMethodDef = nullptr; + fEnumDef = nullptr; fWroteMethod = true; } @@ -663,12 +719,41 @@ void IncludeWriter::structOut(const Definition* root, const Definition& child, this->writeString(child.fName.c_str()); fIndent += 4; this->lfcr(); - this->rewriteBlock((int) (commentEnd - commentStart), commentStart, Phrase::kNo); + if (child.fDeprecated) { + this->writeString(child.fToBeDeprecated ? "to be deprecated soon" : "deprecated"); + } else { + this->rewriteBlock((int)(commentEnd - commentStart), commentStart, Phrase::kNo); + } fIndent -= 4; this->lfcr(); this->writeCommentTrailer(); } +Definition* IncludeWriter::findMemberCommentBlock(const vector<Definition*>& bmhChildren, + const string& name) const { + for (auto memberDef : bmhChildren) { + if (MarkType::kMember != memberDef->fMarkType) { + continue; + } + string match = memberDef->fName; + // if match.endsWith(name) ... + if (match.length() >= name.length() && + 0 == match.compare(match.length() - name.length(), name.length(), name)) { + return memberDef; + } + } + for (auto memberDef : bmhChildren) { + if (MarkType::kSubtopic != memberDef->fMarkType && MarkType::kTopic != memberDef->fMarkType) { + continue; + } + Definition* result = this->findMemberCommentBlock(memberDef->fChildren, name); + if (result) { + return result; + } + } + return nullptr; +} + Definition* IncludeWriter::structMemberOut(const Definition* memberStart, const Definition& child) { const char* blockStart = !fWroteMethod && fDeferComment ? fLastComment->fContentEnd : fStart; const char* blockEnd = fWroteMethod && fDeferComment ? fDeferComment->fStart - 1 : @@ -679,22 +764,14 @@ Definition* IncludeWriter::structMemberOut(const Definition* memberStart, const fIndentNext = false; } fWroteMethod = false; - const char* commentStart = nullptr; - ptrdiff_t commentLen = 0; string name(child.fContentStart, (int) (child.fContentEnd - child.fContentStart)); - bool isShort = false; - Definition* commentBlock = nullptr; - for (auto memberDef : fBmhStructDef->fChildren) { - if (memberDef->fName.length() - name.length() == memberDef->fName.find(name)) { - commentStart = memberDef->fContentStart; - commentLen = memberDef->fContentEnd - commentStart; - isShort = memberDef->fShort; - commentBlock = memberDef; - SkASSERT(!isShort || memberDef->fChildren.size() == 0); - break; - } + Definition* commentBlock = this->findMemberCommentBlock(fBmhStructDef->fChildren, name); + if (!commentBlock) { + return memberStart->reportError<Definition*>("member missing comment block"); } - if (!isShort) { + if (!commentBlock->fShort) { + const char* commentStart = commentBlock->fContentStart; + ptrdiff_t commentLen = commentBlock->fContentEnd - commentStart; this->writeCommentHeader(); bool wroteLineFeed = false; fIndent += 4; @@ -739,16 +816,40 @@ Definition* IncludeWriter::structMemberOut(const Definition* memberStart, const valueStart->fContentStart); } this->writeString(";"); - if (isShort) { + if (commentBlock->fShort) { this->indentToColumn(fStructCommentTab); this->writeString("//!<"); this->writeSpace(); - this->rewriteBlock(commentLen, commentStart, Phrase::kNo); + string extract = commentBlock->extractText(Definition::TrimExtract::kYes); + this->rewriteBlock(extract.length(), &extract.front(), Phrase::kNo); } this->lf(2); return valueEnd; } +// iterate through bmh children and see which comments fit on include lines +void IncludeWriter::structSetMembersShort(const vector<Definition*>& bmhChildren) { + for (auto memberDef : bmhChildren) { + if (MarkType::kMember != memberDef->fMarkType) { + continue; + } + string extract = memberDef->extractText(Definition::TrimExtract::kYes); + bool multiline = string::npos != extract.find('\n'); + if (multiline) { + memberDef->fShort = false; + } else { + ptrdiff_t lineLen = extract.length() + 5 /* //!< space */ ; + memberDef->fShort = fStructCommentTab + lineLen < 100; + } + } + for (auto memberDef : bmhChildren) { + if (MarkType::kSubtopic != memberDef->fMarkType && MarkType::kTopic != memberDef->fMarkType) { + continue; + } + this->structSetMembersShort(memberDef->fChildren); + } +} + void IncludeWriter::structSizeMembers(const Definition& child) { int longestType = 0; Definition* typeStart = nullptr; @@ -860,20 +961,7 @@ void IncludeWriter::structSizeMembers(const Definition& child) { fStructValueTab -= 1 /* ; */ ; } // iterate through bmh children and see which comments fit on include lines - for (auto& member : fBmhStructDef->fChildren) { - if (MarkType::kMember != member->fMarkType) { - continue; - } - TextParser memberLine(member); - memberLine.trimEnd(); - const char* commentStart = memberLine.fChar; - memberLine.skipLine(); - ptrdiff_t lineLen = memberLine.fChar - commentStart + 5 /* //!< space */ ; - if (!memberLine.eof()) { - memberLine.skipWhiteSpace(); - } - member->fShort = memberLine.eof() && fStructCommentTab + lineLen < 100; - } + this->structSetMembersShort(fBmhStructDef->fChildren); } static bool find_start(const Definition* startDef, const char* start) { @@ -892,6 +980,9 @@ static bool find_start(const Definition* startDef, const char* start) { } bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefinition* root) { + if (!def->fTokens.size()) { + return true; + } ParentPair pair = { def, prevPair }; // write bulk of original include up to class, method, enum, etc., excepting preceding comment // find associated bmh object @@ -908,6 +999,8 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti bool inConstructor = false; bool inInline = false; bool eatOperator = false; + bool sawConst = false; + bool staticOnly = false; const Definition* requireDense = nullptr; const Definition* startDef = nullptr; for (auto& child : def->fTokens) { @@ -1033,6 +1126,10 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti methodName += string(fContinuation, continueEnd - fContinuation); method = root->find(methodName, RootDefinition::AllowParens::kNo); if (!method) { + if (fBmhStructDef && fBmhStructDef->fDeprecated) { + fContinuation = nullptr; + continue; + } fLineCount = child.fLineCount; return this->reportError<bool>("method not found"); } @@ -1058,6 +1155,9 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti } this->methodOut(method, child); continue; + } else if (fBmhStructDef && fBmhStructDef->fDeprecated) { + fContinuation = nullptr; + continue; } fLineCount = child.fLineCount; return this->reportError<bool>("method not found"); @@ -1130,10 +1230,10 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti } #endif } - const Definition* cIncludeStructDef = nullptr; switch (child.fKeyWord) { case KeyWord::kStruct: case KeyWord::kClass: + fStructMemberTab = 0; // if struct contains members, compute their name and comment tabs if (child.fChildren.size() > 0) { const ParentPair* testPair = &pair; @@ -1233,14 +1333,14 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti } } // FIXME: trigger error earlier if inner #Struct or #Class is missing #Code - SkASSERT(nextBlock); // FIXME: check enum for correct order earlier - const char* commentStart = codeBlock->fTerminator; - const char* commentEnd = nextBlock->fStart; - if (fIndentNext) { -// fIndent += 4; + if (!fBmhStructDef->fDeprecated) { + SkASSERT(codeBlock); + SkASSERT(nextBlock); // FIXME: check enum for correct order earlier + const char* commentStart = codeBlock->fTerminator; + const char* commentEnd = nextBlock->fStart; + fIndentNext = true; + this->structOut(root, *fBmhStructDef, commentStart, commentEnd); } - fIndentNext = true; - this->structOut(root, *fBmhStructDef, commentStart, commentEnd); } fDeferComment = nullptr; } else { @@ -1254,7 +1354,18 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti } break; case KeyWord::kConst: case KeyWord::kConstExpr: + sawConst = !memberStart || staticOnly; + if (!memberStart) { + memberStart = &child; + staticOnly = true; + } + break; case KeyWord::kStatic: + if (!memberStart) { + memberStart = &child; + staticOnly = true; + } + break; case KeyWord::kInt: case KeyWord::kUint8_t: case KeyWord::kUint16_t: @@ -1264,7 +1375,9 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti case KeyWord::kSize_t: case KeyWord::kFloat: case KeyWord::kBool: + case KeyWord::kChar: case KeyWord::kVoid: + staticOnly = false; if (!memberStart) { memberStart = &child; } @@ -1283,28 +1396,7 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti default: SkASSERT(0); } - if (cIncludeStructDef) { - TextParser structName(&child); - SkAssertResult(structName.skipToEndBracket('{')); - startDef = &child; - fStart = structName.fChar + 1; - this->writeBlock((int) (fStart - child.fStart), child.fStart); - this->lf(2); - fIndent += 4; - if (!this->populate(&child, &pair, const_cast<Definition*>(cIncludeStructDef)->asRoot())) { - return false; - } - // output any remaining definitions at current indent level - const char* structEnd = child.fContentEnd; - SkAssertResult('}' == structEnd[-1]); - --structEnd; - this->writeBlockTrim((int) (structEnd - fStart), fStart); - this->lf(2); - fStart = structEnd; - fIndent -= 4; - fContinuation = nullptr; - fDeferComment = nullptr; - } else if (KeyWord::kUint8_t == child.fKeyWord) { + if (KeyWord::kUint8_t == child.fKeyWord) { continue; } else { if (fInEnum && KeyWord::kClass == child.fChildren[0]->fKeyWord) { @@ -1316,7 +1408,6 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti return false; } if (KeyWord::kClass == child.fKeyWord || KeyWord::kStruct == child.fKeyWord) { - fStructMemberTab = 0; if (fInStruct) { fInStruct = false; do { @@ -1375,6 +1466,7 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti auto iter = def->fTokens.begin(); std::advance(iter, child.fParentIndex - 1); memberStart = &*iter; + staticOnly = false; if (!fStructMemberTab) { SkASSERT(KeyWord::kStruct == def->fParent->fKeyWord); fIndent += 4; @@ -1384,13 +1476,36 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti fIndentNext = true; } } - memberEnd = this->structMemberOut(memberStart, child); - startDef = &child; - fStart = child.fContentEnd + 1; - fDeferComment = nullptr; + SkASSERT(fBmhStructDef); + if (!fBmhStructDef->fDeprecated) { + memberEnd = this->structMemberOut(memberStart, child); + startDef = &child; + fStart = child.fContentEnd + 1; + fDeferComment = nullptr; + } + } else if (MarkType::kNone == child.fMarkType && sawConst + && fEnumDef && !fEnumDef->fDeprecated) { + const Definition* bmhConst = nullptr; + string match; + if (root) { + match = root->fName + "::"; + } + match += string(child.fContentStart, child.fContentEnd - child.fContentStart); + for (auto enumChild : fEnumDef->fChildren) { + if (MarkType::kConst == enumChild->fMarkType && enumChild->fName == match) { + bmhConst = enumChild; + break; + } + } + if (bmhConst) { + this->constOut(memberStart, child, bmhConst); + fDeferComment = nullptr; + sawConst = false; + } } if (child.fMemberStart) { memberStart = &child; + staticOnly = false; } const char attrDeprecated[] = "SK_ATTR_DEPRECATED"; const size_t attrDeprecatedLen = sizeof(attrDeprecated) - 1; @@ -1403,6 +1518,8 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti if (Definition::Type::kPunctuation == child.fType) { if (Punctuation::kSemicolon == child.fPunctuation) { memberStart = nullptr; + sawConst = false; + staticOnly = false; if (inStruct) { fInStruct = false; } @@ -1546,6 +1663,7 @@ string IncludeWriter::resolveRef(const char* start, const char* end, bool first, *refType = RefType::kNormal; SkASSERT(string::npos == undername.find(' ')); const Definition* rootDef = nullptr; + string substitute; { auto rootDefIter = fBmhParser->fTopicMap.find(undername); if (fBmhParser->fTopicMap.end() != rootDefIter) { @@ -1561,10 +1679,18 @@ string IncludeWriter::resolveRef(const char* start, const char* end, bool first, if (fBmhParser->fTopicMap.end() != rootDefIter) { rootDef = rootDefIter->second; } - } else { + if (!rootDef) { + size_t doubleColon = fBmhStructDef->fName.rfind("::"); + if (string::npos != doubleColon && undername + == fBmhStructDef->fName.substr(doubleColon + 2)) { + substitute = fBmhStructDef->fName; + } + } + } + if (!rootDef && !substitute.length()) { auto aliasIter = fBmhParser->fAliasMap.find(undername); if (fBmhParser->fAliasMap.end() != aliasIter) { - rootDef = aliasIter->second->fParent; + rootDef = aliasIter->second; } else if (!first) { SkDebugf("unfound: %s\n", undername.c_str()); this->reportError("reference unfound"); @@ -1573,33 +1699,71 @@ string IncludeWriter::resolveRef(const char* start, const char* end, bool first, } } } - string substitute; if (rootDef) { - for (auto child : rootDef->fChildren) { - if (MarkType::kSubstitute == child->fMarkType) { - substitute = string(child->fContentStart, - (int) (child->fContentEnd - child->fContentStart)); - break; + MarkType rootType = rootDef->fMarkType; + bool isTopic = MarkType::kSubtopic == rootType || MarkType::kTopic == rootType; + auto substituteParent = MarkType::kAlias == rootType ? rootDef->fParent : + isTopic ? rootDef : nullptr; + if (substituteParent) { + for (auto child : substituteParent->fChildren) { + if (MarkType::kSubstitute == child->fMarkType) { + substitute = string(child->fContentStart, + (int) (child->fContentEnd - child->fContentStart)); + break; + } + } + } + if (!substitute.length()) { + string match = rootDef->fName; + size_t index; + while (string::npos != (index = match.find('_'))) { + match.erase(index, 1); + } + string skmatch = "Sk" + match; + auto parent = substituteParent ? substituteParent : rootDef; + for (auto child : parent->fChildren) { + // there may be more than one + // prefer the one mostly closely matching in text + if ((MarkType::kClass == child->fMarkType || + MarkType::kStruct == child->fMarkType || + (MarkType::kEnum == child->fMarkType && !child->fAnonymous) || + MarkType::kEnumClass == child->fMarkType) && (match == child->fName || + skmatch == child->fName)) { + substitute = child->fName; + break; + } } } if (!substitute.length()) { for (auto child : rootDef->fChildren) { + // there may be more than one + // if so, it's a bug since it's unknown which is the right one if (MarkType::kClass == child->fMarkType || MarkType::kStruct == child->fMarkType || (MarkType::kEnum == child->fMarkType && !child->fAnonymous) || MarkType::kEnumClass == child->fMarkType) { + SkASSERT("" == substitute); substitute = child->fName; - if (MarkType::kEnum == child->fMarkType && fInEnum) { + if (MarkType::kEnum == child->fMarkType) { size_t parentClassEnd = substitute.find("::"); SkASSERT(string::npos != parentClassEnd); - substitute = substitute.substr(parentClassEnd + 2); + string subEnd = substitute.substr(parentClassEnd + 2); + if (fInEnum) { + substitute = subEnd; + } + if (subEnd == undername) { + break; + } } - break; } } } if (!substitute.length()) { - auto parent = rootDef->fParent; + const Definition* parent = rootDef; + do { + parent = parent->fParent; + } while (parent && (MarkType::kSubtopic == parent->fMarkType + || MarkType::kTopic == parent->fMarkType)); if (parent) { if (MarkType::kClass == parent->fMarkType || MarkType::kStruct == parent->fMarkType || @@ -1607,10 +1771,8 @@ string IncludeWriter::resolveRef(const char* start, const char* end, bool first, MarkType::kEnumClass == parent->fMarkType) { if (parent->fParent != fRootTopic) { substitute = parent->fName; - size_t under = undername.find('_'); - SkASSERT(string::npos != under); - string secondHalf(&undername[under], (size_t) (undername.length() - under)); - substitute += ConvertRef(secondHalf, false); + substitute += ' '; + substitute += ConvertRef(rootDef->fName, false); } else { substitute += ConvertRef(undername, first); } @@ -1633,6 +1795,7 @@ int IncludeWriter::lookupMethod(const PunctuationState punctuation, const Word w ++wordStart; } const int wordEnd = PunctuationState::kDelimiter == punctuation || + PunctuationState::kParen == punctuation || PunctuationState::kPeriod == punctuation ? run - 1 : run; string temp; if (hasIndirection && '(' != data[wordEnd - 1] && ')' != data[wordEnd - 1]) { @@ -1668,6 +1831,7 @@ int IncludeWriter::lookupMethod(const PunctuationState punctuation, const Word w int IncludeWriter::lookupReference(const PunctuationState punctuation, const Word word, const int start, const int run, int lastWrite, const char last, const char* data) { const int end = PunctuationState::kDelimiter == punctuation || + PunctuationState::kParen == punctuation || PunctuationState::kPeriod == punctuation ? run - 1 : run; RefType refType = RefType::kUndefined; string resolved = string(&data[start], (size_t) (end - start)); @@ -1767,7 +1931,7 @@ IncludeWriter::Wrote IncludeWriter::rewriteBlock(int size, const char* data, Phr case Word::kMixed: if (hasUpper && hasLower && !hasSymbol && lastSpace > 0) { lastWrite = this->lookupMethod(punctuation, word, lastSpace, run, - lastWrite, data, hasIndirection && !hasSymbol); + lastWrite, data, hasIndirection); } break; default: @@ -1785,26 +1949,7 @@ IncludeWriter::Wrote IncludeWriter::rewriteBlock(int size, const char* data, Phr hasSymbol = false; lastSpace = run; break; - case '.': - switch (word) { - case Word::kStart: - punctuation = PunctuationState::kDelimiter; - case Word::kCap: - case Word::kFirst: - case Word::kUnderline: - case Word::kMixed: - if (PunctuationState::kDelimiter == punctuation || - PunctuationState::kPeriod == punctuation) { - word = Word::kMixed; - } - punctuation = PunctuationState::kPeriod; - break; - default: - SkASSERT(0); - } - embeddedIndirection = true; - break; - case ',': case ';': case ':': + case '.': case ',': case ';': case ':': case ')': switch (word) { case Word::kStart: punctuation = PunctuationState::kDelimiter; @@ -1816,12 +1961,13 @@ IncludeWriter::Wrote IncludeWriter::rewriteBlock(int size, const char* data, Phr PunctuationState::kPeriod == punctuation) { word = Word::kMixed; } - punctuation = PunctuationState::kDelimiter; + punctuation = '.' == c ? PunctuationState::kPeriod : + PunctuationState::kDelimiter; break; default: SkASSERT(0); } - embeddedSymbol = true; + ('.' == c ? embeddedIndirection : embeddedSymbol) = true; break; case '>': if ('-' == last) { @@ -1838,16 +1984,12 @@ IncludeWriter::Wrote IncludeWriter::rewriteBlock(int size, const char* data, Phr break; case '(': if (' ' == last) { - punctuation = PunctuationState::kDelimiter; + punctuation = PunctuationState::kParen; } else { word = Word::kMixed; } embeddedSymbol = true; break; - case ')': // assume word type has already been set - punctuation = PunctuationState::kDelimiter; - embeddedSymbol = true; - break; case '_': switch (word) { case Word::kStart: diff --git a/chromium/third_party/skia/tools/bookmaker/mdOut.cpp b/chromium/third_party/skia/tools/bookmaker/mdOut.cpp index 24009d02f8c..f74853b470a 100644 --- a/chromium/third_party/skia/tools/bookmaker/mdOut.cpp +++ b/chromium/third_party/skia/tools/bookmaker/mdOut.cpp @@ -34,6 +34,15 @@ static string preformat(const string& orig) { return result; } +static bool all_lower(const string& ref) { + for (auto ch : ref) { + if (!islower(ch)) { + return false; + } + } + return true; +} + // FIXME: preserve inter-line spaces and don't add new ones string MdOut::addReferences(const char* refStart, const char* refEnd, BmhParser::Resolvable resolvable) { @@ -128,8 +137,12 @@ string MdOut::addReferences(const char* refStart, const char* refEnd, } ref = fullRef; } - } - result += linkRef(leadingSpaces, def, ref); + } else if (BmhParser::Resolvable::kClone != resolvable && + all_lower(ref) && (t.eof() || '(' != t.peek())) { + add_ref(leadingSpaces, ref, &result); + continue; + } + result += linkRef(leadingSpaces, def, ref, resolvable); continue; } if (!t.eof() && '(' == t.peek()) { @@ -141,7 +154,7 @@ string MdOut::addReferences(const char* refStart, const char* refEnd, ref = string(start, t.fChar - start); if (const Definition* def = this->isDefined(t, ref, true)) { SkASSERT(def->fFiddle.length()); - result += linkRef(leadingSpaces, def, ref); + result += linkRef(leadingSpaces, def, ref, resolvable); continue; } } @@ -168,7 +181,7 @@ string MdOut::addReferences(const char* refStart, const char* refEnd, // will also need to see if Example Description matches var in example const Definition* def; if (fMethod && (def = fMethod->hasParam(ref))) { - result += linkRef(leadingSpaces, def, ref); + result += linkRef(leadingSpaces, def, ref, resolvable); fLastParam = def; distFromParam = 0; continue; @@ -183,7 +196,7 @@ string MdOut::addReferences(const char* refStart, const char* refEnd, if (paramType) { string fullName = paramType->fName + "::" + ref; if (paramType->hasMatch(fullName)) { - result += linkRef(leadingSpaces, paramType, ref); + result += linkRef(leadingSpaces, paramType, ref, resolvable); continue; } } @@ -199,37 +212,37 @@ string MdOut::addReferences(const char* refStart, const char* refEnd, } auto topicIter = fBmhParser.fTopicMap.find(ref); if (topicIter != fBmhParser.fTopicMap.end()) { - result += linkRef(leadingSpaces, topicIter->second, ref); + result += linkRef(leadingSpaces, topicIter->second, ref, resolvable); continue; } bool startsSentence = t.sentenceEnd(start); if (!t.eof() && ' ' != t.peek()) { - add_ref(leadingSpaces, ref, &result); + add_ref(leadingSpaces, ref, &result); continue; } if (t.fChar + 1 >= t.fEnd || (!isupper(t.fChar[1]) && startsSentence)) { - add_ref(leadingSpaces, ref, &result); + add_ref(leadingSpaces, ref, &result); continue; } if (isupper(t.fChar[1]) && startsSentence) { TextParser next(t.fFileName, &t.fChar[1], t.fEnd, t.fLineCount); string nextWord(next.fChar, next.wordEnd() - next.fChar); if (this->isDefined(t, nextWord, true)) { - add_ref(leadingSpaces, ref, &result); + add_ref(leadingSpaces, ref, &result); continue; } } - Definition* test = fRoot; + const Definition* test = fRoot; do { if (!test->isRoot()) { continue; } for (string prefix : { "_", "::" } ) { - RootDefinition* root = test->asRoot(); + const RootDefinition* root = test->asRoot(); string prefixed = root->fName + prefix + ref; if (const Definition* def = root->find(prefixed, RootDefinition::AllowParens::kYes)) { - result += linkRef(leadingSpaces, def, ref); + result += linkRef(leadingSpaces, def, ref, resolvable); goto found; } } @@ -244,6 +257,8 @@ string MdOut::addReferences(const char* refStart, const char* refEnd, return result; } + + bool MdOut::buildReferences(const char* docDir, const char* mdFileOrPath) { if (!sk_isdir(mdFileOrPath)) { SkString mdFile = SkOSPath::Basename(mdFileOrPath); @@ -259,15 +274,8 @@ bool MdOut::buildReferences(const char* docDir, const char* mdFileOrPath) { SkOSFile::Iter it(docDir, ".bmh"); for (SkString file; it.next(&file); ) { SkString p = SkOSPath::Join(docDir, file.c_str()); - const char* hunk = p.c_str(); - if (!SkStrEndsWith(hunk, ".bmh")) { - continue; - } - if (SkStrEndsWith(hunk, "markup.bmh")) { // don't look inside this for now - continue; - } - if (!this->buildRefFromFile(hunk, mdFileOrPath)) { - SkDebugf("failed to parse %s\n", hunk); + if (!this->buildRefFromFile(p.c_str(), mdFileOrPath)) { + SkDebugf("failed to parse %s\n", p.c_str()); return false; } } @@ -289,6 +297,15 @@ bool MdOut::buildStatus(const char* statusFile, const char* outDir) { } bool MdOut::buildRefFromFile(const char* name, const char* outDir) { + if (!SkStrEndsWith(name, ".bmh")) { + return true; + } + if (SkStrEndsWith(name, "markup.bmh")) { // don't look inside this for now + return true; + } + if (SkStrEndsWith(name, "illustrations.bmh")) { // don't look inside this for now + return true; + } fFileName = string(name); string filename(name); if (filename.substr(filename.length() - 4) == ".bmh") { @@ -344,6 +361,16 @@ bool MdOut::buildRefFromFile(const char* name, const char* outDir) { this->lfAlways(1); FPRINTF("==="); } + fPopulators.clear(); + fPopulators[kClassesAndStructs].fDescription = "embedded struct and class members"; + fPopulators[kConstants].fDescription = "enum and enum class, const values"; + fPopulators[kConstructors].fDescription = "functions that construct"; + fPopulators[kMemberFunctions].fDescription = "static functions and member methods"; + fPopulators[kMembers].fDescription = "member values"; + fPopulators[kOperators].fDescription = "operator overloading methods"; + fPopulators[kRelatedFunctions].fDescription = "similar methods grouped together"; + fPopulators[kSubtopics].fDescription = ""; + this->populateTables(fRoot); this->markTypeOut(topicDef); } if (fOut) { @@ -411,6 +438,24 @@ void MdOut::childrenOut(const Definition* def, const char* start) { } } +const Definition* MdOut::csParent() const { + const Definition* csParent = fRoot->csParent(); + if (!csParent) { + const Definition* topic = fRoot; + while (topic && MarkType::kTopic != topic->fMarkType) { + topic = topic->fParent; + } + for (auto child : topic->fChildren) { + if (child->isStructOrClass() || MarkType::kTypedef == child->fMarkType) { + csParent = child; + break; + } + } + SkASSERT(csParent || string::npos == fRoot->fFileName.find("Sk")); + } + return csParent; +} + const Definition* MdOut::findParamType() { SkASSERT(fMethod); TextParser parser(fMethod->fFileName, fMethod->fStart, fMethod->fContentStart, @@ -473,12 +518,12 @@ const Definition* MdOut::isDefined(const TextParser& parser, const string& ref, if (const Definition* definition = fRoot->find(ref, RootDefinition::AllowParens::kYes)) { return definition; } - Definition* test = fRoot; + const Definition* test = fRoot; do { if (!test->isRoot()) { continue; } - RootDefinition* root = test->asRoot(); + const RootDefinition* root = test->asRoot(); for (auto& leaf : root->fBranches) { if (ref == leaf.first) { return leaf.second; @@ -502,6 +547,11 @@ const Definition* MdOut::isDefined(const TextParser& parser, const string& ref, } } } + string fiddlePrefixed = root->fFiddle + "_" + ref; + auto topicIter = fBmhParser.fTopicMap.find(fiddlePrefixed); + if (topicIter != fBmhParser.fTopicMap.end()) { + return topicIter->second; + } } while ((test = test->fParent)); } size_t doubleColon = ref.find("::"); @@ -523,8 +573,9 @@ const Definition* MdOut::isDefined(const TextParser& parser, const string& ref, // try with a prefix if ('k' == ref[0]) { for (auto const& iter : fBmhParser.fEnumMap) { - if (iter.second.find(ref, RootDefinition::AllowParens::kYes)) { - return &iter.second; + auto def = iter.second.find(ref, RootDefinition::AllowParens::kYes); + if (def) { + return def; } } if (fEnumClass) { @@ -603,38 +654,42 @@ string MdOut::linkName(const Definition* ref) const { // for now, hard-code to html links // def should not include SkXXX_ string MdOut::linkRef(const string& leadingSpaces, const Definition* def, - const string& ref) const { + const string& ref, BmhParser::Resolvable resolvable) const { string buildup; + string refName; const string* str = &def->fFiddle; SkASSERT(str->length() > 0); - size_t under = str->find('_'); - Definition* curRoot = fRoot; - string classPart = string::npos != under ? str->substr(0, under) : *str; - bool classMatch = curRoot->fName == classPart; - while (curRoot->fParent) { - curRoot = curRoot->fParent; - classMatch |= curRoot->fName == classPart; + string classPart = *str; + bool globalEnumMember = false; + if (MarkType::kAlias == def->fMarkType) { + def = def->fParent; + SkASSERT(def); + SkASSERT(MarkType::kSubtopic == def->fMarkType ||MarkType::kTopic == def->fMarkType); } - const Definition* defRoot; - const Definition* temp = def; - do { - defRoot = temp; - if (!(temp = temp->fParent)) { - break; + if (MarkType::kSubtopic == def->fMarkType) { + const Definition* topic = def->topicParent(); + SkASSERT(topic); + classPart = topic->fName; + refName = def->fName; + } else if (MarkType::kTopic == def->fMarkType) { + refName = def->fName; + } else { + if ('k' == (*str)[0] && string::npos != str->find("_Sk")) { + globalEnumMember = true; + } else { + SkASSERT("Sk" == str->substr(0, 2) || "SK" == str->substr(0, 2) + // FIXME: kitchen sink catch below, need to do better + || string::npos != def->fFileName.find("undocumented")); + size_t under = str->find('_'); + classPart = string::npos != under ? str->substr(0, under) : *str; } - classMatch |= temp != defRoot && temp->fName == classPart; - } while (true); - string namePart = string::npos != under ? str->substr(under + 1, str->length()) : *str; + refName = def->fFiddle; + } + bool classMatch = fRoot->fFileName == def->fFileName; SkASSERT(fRoot); SkASSERT(fRoot->fFileName.length()); - if (classMatch) { - buildup = "#"; - if (*str != classPart && "Sk" == classPart.substr(0, 2)) { - buildup += classPart + "_"; - } - buildup += namePart; - } else { - string filename = defRoot->asRoot()->fFileName; + if (!classMatch) { + string filename = def->fFileName; if (filename.substr(filename.length() - 4) == ".bmh") { filename = filename.substr(0, filename.length() - 4); } @@ -642,19 +697,46 @@ string MdOut::linkRef(const string& leadingSpaces, const Definition* def, while (start > 0 && (isalnum(filename[start - 1]) || '_' == filename[start - 1])) { --start; } - buildup = filename.substr(start) + "#" + (classMatch ? namePart : *str); + buildup = filename.substr(start); } + buildup += "#" + refName; if (MarkType::kParam == def->fMarkType) { const Definition* parent = def->fParent; SkASSERT(MarkType::kMethod == parent->fMarkType); buildup = '#' + parent->fFiddle + '_' + ref; } string refOut(ref); - std::replace(refOut.begin(), refOut.end(), '_', ' '); + if (!globalEnumMember) { + std::replace(refOut.begin(), refOut.end(), '_', ' '); + } if (ref.length() > 2 && islower(ref[0]) && "()" == ref.substr(ref.length() - 2)) { refOut = refOut.substr(0, refOut.length() - 2); } - return leadingSpaces + "<a href=\"" + buildup + "\">" + refOut + "</a>"; + string result = leadingSpaces + "<a href=\"" + buildup + "\">" + refOut + "</a>"; + if (BmhParser::Resolvable::kClone == resolvable && MarkType::kMethod == def->fMarkType && + def->fCloned && !def->fClone) { + bool found = false; + string match = def->fName; + if ("()" == match.substr(match.length() - 2)) { + match = match.substr(0, match.length() - 2); + } + match += '_'; + auto classIter = fBmhParser.fClassMap.find(classPart); + if (fBmhParser.fClassMap.end() != classIter) { + for (char num = '2'; num <= '9'; ++num) { + string clone = match + num; + const auto& leafIter = classIter->second.fLeaves.find(clone); + if (leafIter != classIter->second.fLeaves.end()) { + result += "<sup><a href=\"" + buildup + "_" + num + "\">[" + num + "]</a></sup>"; + found = true; + } + } + } + if (!found) { + SkDebugf(""); // convenient place to set a breakpoint + } + } + return result; } void MdOut::markTypeOut(Definition* def) { @@ -755,8 +837,6 @@ void MdOut::markTypeOut(Definition* def) { FPRINTF("<a name=\"%s\"></a> Enum %s", def->fFiddle.c_str(), def->fName.c_str()); this->lf(2); break; - case MarkType::kError: - break; case MarkType::kExample: { this->mdHeaderOut(3); FPRINTF("Example\n" @@ -773,7 +853,7 @@ void MdOut::markTypeOut(Definition* def) { gpuAndCpu = platParse.strnstr("cpu", platParse.fEnd); } } - if (fHasFiddle && !def->hasChild(MarkType::kError)) { + if (fHasFiddle) { SkASSERT(def->fHash.length() > 0); FPRINTF("<div><fiddle-embed name=\"%s\"", def->fHash.c_str()); if (showGpu) { @@ -806,10 +886,31 @@ void MdOut::markTypeOut(Definition* def) { break; case MarkType::kHeight: break; + case MarkType::kIllustration: { + string illustName = "Illustrations_" + def->fParent->fFiddle; + auto illustIter = fBmhParser.fTopicMap.find(illustName); + SkASSERT(fBmhParser.fTopicMap.end() != illustIter); + Definition* illustDef = illustIter->second; + SkASSERT(MarkType::kSubtopic == illustDef->fMarkType); + SkASSERT(1 == illustDef->fChildren.size()); + Definition* illustExample = illustDef->fChildren[0]; + SkASSERT(MarkType::kExample == illustExample->fMarkType); + string hash = illustExample->fHash; + SkASSERT("" != hash); + string title; + this->writePending(); + FPRINTF("![%s](https://fiddle.skia.org/i/%s_raster.png \"%s\")", + def->fName.c_str(), hash.c_str(), title.c_str()); + this->lf(2); + } break; case MarkType::kImage: break; + case MarkType::kIn: + break; case MarkType::kLegend: break; + case MarkType::kLine: + break; case MarkType::kLink: break; case MarkType::kList: @@ -835,15 +936,15 @@ void MdOut::markTypeOut(Definition* def) { } break; case MarkType::kMethod: { string method_name = def->methodName(); - string formattedStr = def->formatFunction(); + string formattedStr = def->formatFunction(Definition::Format::kIncludeReturn); - if (!def->isClone()) { - this->lfAlways(2); - FPRINTF("<a name=\"%s\"></a>", def->fFiddle.c_str()); + this->lfAlways(2); + FPRINTF("<a name=\"%s\"></a>", def->fFiddle.c_str()); + if (!def->isClone()) { this->mdHeaderOutLF(2, 1); FPRINTF("%s", method_name.c_str()); - this->lf(2); - } + } + this->lf(2); // TODO: put in css spec that we can define somewhere else (if markup supports that) // TODO: 50em below should match limit = 80 in formatFunction() @@ -897,6 +998,15 @@ void MdOut::markTypeOut(Definition* def) { } break; case MarkType::kPlatform: break; + case MarkType::kPopulate: { + SkASSERT(MarkType::kSubtopic == def->fParent->fMarkType); + string name = def->fParent->fName; + if (kSubtopics == name) { + this->subtopicsOut(); + } else { + this->subtopicOut(this->populator(name.c_str())); + } + } break; case MarkType::kPrivate: break; case MarkType::kReturn: @@ -1100,6 +1210,73 @@ void MdOut::mdHeaderOutLF(int depth, int lf) { FPRINTF(" "); } +void MdOut::populateTables(const Definition* def) { + const Definition* csParent = this->csParent(); + if (!csParent) { + return; + } + for (auto child : def->fChildren) { + if (MarkType::kTopic == child->fMarkType || MarkType::kSubtopic == child->fMarkType) { + string name = child->fName; + bool builtInTopic = name == kClassesAndStructs || name == kConstants + || name == kConstructors || name == kMemberFunctions || name == kMembers + || name == kOperators || name == kOverview || name == kRelatedFunctions + || name == kSubtopics; + if (!builtInTopic && child->fName != kOverview) { + this->populator(kRelatedFunctions).fMembers.push_back(child); + } + this->populateTables(child); + continue; + } + if (child->isStructOrClass()) { + if (fClassStack.size() > 0) { + this->populator(kClassesAndStructs).fMembers.push_back(child); + } + fClassStack.push_back(child); + this->populateTables(child); + fClassStack.pop_back(); + continue; + } + if (MarkType::kEnum == child->fMarkType || MarkType::kEnumClass == child->fMarkType) { + this->populator(kConstants).fMembers.push_back(child); + continue; + } + if (MarkType::kMember == child->fMarkType) { + this->populator(kMembers).fMembers.push_back(child); + continue; + } + if (MarkType::kMethod != child->fMarkType) { + continue; + } + if (child->fClone) { + continue; + } + if (Definition::MethodType::kConstructor == child->fMethodType + || Definition::MethodType::kDestructor == child->fMethodType) { + this->populator(kConstructors).fMembers.push_back(child); + continue; + } + if (Definition::MethodType::kOperator == child->fMethodType) { + this->populator(kOperators).fMembers.push_back(child); + continue; + } + this->populator(kMemberFunctions).fMembers.push_back(child); + if (csParent && (0 == child->fName.find(csParent->fName + "::Make") + || 0 == child->fName.find(csParent->fName + "::make"))) { + this->populator(kConstructors).fMembers.push_back(child); + continue; + } + for (auto item : child->fChildren) { + if (MarkType::kIn == item->fMarkType) { + string name(item->fContentStart, item->fContentEnd - item->fContentStart); + fPopulators[name].fMembers.push_back(child); + fPopulators[name].fShowClones = true; + break; + } + } + } +} + void MdOut::resolveOut(const char* start, const char* end, BmhParser::Resolvable resolvable) { if ((BmhParser::Resolvable::kLiteral == resolvable || fRespectLeadingSpace) && end > start) { while ('\n' == *start) { @@ -1178,3 +1355,88 @@ void MdOut::resolveOut(const char* start, const char* end, BmhParser::Resolvable #endif } } + +void MdOut::rowOut(const char* name, const string& description) { + this->lfAlways(1); + FPRINTF("| "); + this->resolveOut(name, name + strlen(name), BmhParser::Resolvable::kYes); + FPRINTF(" | "); + this->resolveOut(&description.front(), &description.back() + 1, BmhParser::Resolvable::kYes); + FPRINTF(" |"); + this->lf(1); +} + +void MdOut::subtopicsOut() { + const Definition* csParent = this->csParent(); + SkASSERT(csParent); + this->rowOut("name", "description"); + this->rowOut("---", "---"); + for (auto item : { kClassesAndStructs, kConstants, kConstructors, kMemberFunctions, + kMembers, kOperators, kRelatedFunctions } ) { + for (auto entry : this->populator(item).fMembers) { + if (entry->csParent() == csParent) { + string description = fPopulators.find(item)->second.fDescription; + if (kConstructors == item) { + description += " " + csParent->fName; + } + this->rowOut(item, description); + break; + } + } + } +} + +void MdOut::subtopicOut(const TableContents& tableContents) { + const auto& data = tableContents.fMembers; + const Definition* csParent = this->csParent(); + SkASSERT(csParent); + fRoot = csParent->asRoot(); + this->rowOut("name", "description"); + this->rowOut("---", "---"); + std::map<string, const Definition*> items; + for (auto entry : data) { + if (entry->csParent() != csParent) { + continue; + } + size_t start = entry->fName.find_last_of("::"); + string name = entry->fName.substr(string::npos == start ? 0 : start + 1); + items[name] = entry; + } + for (auto entry : items) { + if (entry.second->fDeprecated) { + continue; + } + const Definition* oneLiner = nullptr; + for (auto child : entry.second->fChildren) { + if (MarkType::kLine == child->fMarkType) { + oneLiner = child; + break; + } + } + if (!oneLiner) { + SkDebugf(""); // convenient place to set a breakpoint + } + // TODO: detect this earlier? throw error here? + SkASSERT(oneLiner); + this->rowOut(entry.first.c_str(), string(oneLiner->fContentStart, + oneLiner->fContentEnd - oneLiner->fContentStart)); + if (tableContents.fShowClones && entry.second->fCloned) { + int cloneNo = 2; + string builder = entry.second->fName; + if ("()" == builder.substr(builder.length() - 2)) { + builder = builder.substr(0, builder.length() - 2); + } + builder += '_'; + this->rowOut("", + preformat(entry.second->formatFunction(Definition::Format::kOmitReturn))); + do { + string match = builder + to_string(cloneNo); + auto child = csParent->findClone(match); + if (!child) { + break; + } + this->rowOut("", preformat(child->formatFunction(Definition::Format::kOmitReturn))); + } while (++cloneNo); + } + } +} diff --git a/chromium/third_party/skia/tools/bookmaker/parserCommon.cpp b/chromium/third_party/skia/tools/bookmaker/parserCommon.cpp index cfd42f700fc..2e9ad81e455 100644 --- a/chromium/third_party/skia/tools/bookmaker/parserCommon.cpp +++ b/chromium/third_party/skia/tools/bookmaker/parserCommon.cpp @@ -6,7 +6,6 @@ */ #include "bookmaker.h" - #include "SkOSFile.h" #include "SkOSPath.h" diff --git a/chromium/third_party/skia/tools/bookmaker/selfCheck.cpp b/chromium/third_party/skia/tools/bookmaker/selfCheck.cpp index 0b28d16c70d..5f1eb38ece7 100644 --- a/chromium/third_party/skia/tools/bookmaker/selfCheck.cpp +++ b/chromium/third_party/skia/tools/bookmaker/selfCheck.cpp @@ -7,14 +7,32 @@ #include "bookmaker.h" -// Check that summary contains all methods +#ifdef SK_BUILD_FOR_WIN +#include <windows.h> +#endif -// Check that mutiple like-named methods are under one Subtopic -// Check that all subtopics are in table of contents + /* SkDebugf works in both visual studio and git shell, but + in git shell output is not piped to grep. + printf does not generate output in visual studio, but + does in git shell and can be piped. + */ +#ifdef SK_BUILD_FOR_WIN +#define PRINTF(...) \ +do { \ + if (IsDebuggerPresent()) { \ + SkDebugf(__VA_ARGS__); \ + } else { \ + printf(__VA_ARGS__); \ + } \ +} while (false) +#else +#define PRINTF(...) \ + printf(__VA_ARGS__) +#endif + -// Check that all constructors are in a table of contents -// should be 'creators' instead of constructors? +// Check that mutiple like-named methods are under one Subtopic // Check that SeeAlso reference each other @@ -37,142 +55,95 @@ public: return fBmhParser.reportError<bool>("expected root topic"); } fRoot = topicDef->asRoot(); - if (!this->checkMethodSummary()) { - return false; - } - if (!this->checkMethodSubtopic()) { - return false; - } - if (!this->checkSubtopicContents()) { - return false; - } - if (!this->checkConstructors()) { - return false; - } if (!this->checkSeeAlso()) { return false; } - if (!this->checkCreators()) { - return false; - } + // report functions that are not covered by related hierarchy + if (!this->checkRelatedFunctions()) { + return false; + } } return true; } protected: - bool checkConstructors() { - return true; - } - - bool checkCreators() { - return true; - } - - bool checkMethodSubtopic() { - return true; - } - bool checkMethodSummary() { - SkDebugf(""); - // look for struct or class in fChildren - for (auto& rootChild : fRoot->fChildren) { - if (MarkType::kStruct == rootChild->fMarkType || - MarkType::kClass == rootChild->fMarkType) { - auto& cs = rootChild; - // expect Overview as Topic in every main class or struct - Definition* overview = nullptr; - for (auto& csChild : cs->fChildren) { - if ("Overview" == csChild->fName) { - if (!overview) { - return cs->reportError<bool>("expected only one Overview"); - } - overview = csChild; - } - } - if (!overview) { - return cs->reportError<bool>("missing #Topic Overview"); - } - Definition* memberFunctions = nullptr; - for (auto& overChild : overview->fChildren) { - if ("Member_Functions" == overChild->fName) { - memberFunctions = overChild; - break; - } - } - if (!memberFunctions) { - return overview->reportError<bool>("missing #Subtopic Member_Functions"); - } - if (MarkType::kSubtopic != memberFunctions->fMarkType) { - return memberFunctions->reportError<bool>("expected #Subtopic Member_Functions"); - } - Definition* memberTable = nullptr; - for (auto& memberChild : memberFunctions->fChildren) { - if (MarkType::kTable == memberChild->fMarkType && - memberChild->fName == memberFunctions->fName) { - memberTable = memberChild; - break; - } - } - if (!memberTable) { - return memberFunctions->reportError<bool>("missing #Table in Member_Functions"); - } - vector<string> overviewEntries; // build map of overview entries - bool expectLegend = true; - string prior = " "; // expect entries to be alphabetical - for (auto& memberRow : memberTable->fChildren) { - if (MarkType::kLegend == memberRow->fMarkType) { - if (!expectLegend) { - return memberRow->reportError<bool>("expect #Legend only once"); - } - // todo: check if legend format matches table's rows' format - expectLegend = false; - } else if (expectLegend) { - return memberRow->reportError<bool>("expect #Legend first"); - } - if (MarkType::kRow != memberRow->fMarkType) { - continue; // let anything through for now; can tighten up in the future - } - // expect column 0 to point to function name - // todo: content end points past space; could tighten that up - Definition* column0 = memberRow->fChildren[0]; - string name = string(column0->fContentStart, - column0->fTerminator - column0->fContentStart); - if (prior > name) { - return memberRow->reportError<bool>("expect alphabetical order"); - } - if (prior == name) { - return memberRow->reportError<bool>("expect unique names"); - } - // todo: error if name is all lower case and doesn't end in () - overviewEntries.push_back(name); - prior = name; - } - // mark corresponding methods as visited (may be more than one per entry) - for (auto& csChild : cs->fChildren) { - if (MarkType::kMethod != csChild->fMarkType) { - // only check methods for now - continue; - } - auto start = csChild->fName.find_last_of(':'); - start = string::npos == start ? 0 : start + 1; - string name = csChild->fName.substr(start); - if (overviewEntries.end() == - std::find(overviewEntries.begin(), overviewEntries.end(), name)) { - return csChild->reportError<bool>("missing in Overview"); + void checkMethod(string topic, const Definition* csChild, vector<string>* reported) { + if (MarkType::kSubtopic == csChild->fMarkType) { + for (auto child : csChild->fChildren) { + checkMethod(topic, child, reported); + } + return; + } else if (MarkType::kMethod != csChild->fMarkType) { + // only check methods for now + return; + } + bool containsMarkTypeIn = csChild->fDeprecated // no markup for deprecated + || Definition::MethodType::kConstructor == csChild->fMethodType + || Definition::MethodType::kDestructor == csChild->fMethodType + || Definition::MethodType::kOperator == csChild->fMethodType + || csChild->fClone; + for (auto child : csChild->fChildren) { + if (MarkType::kIn == child->fMarkType) { + containsMarkTypeIn = true; + string subtopic(child->fContentStart, + child->fContentEnd - child->fContentStart); + string fullname = topic + '_' + subtopic; + auto topEnd = fBmhParser.fTopicMap.end(); + auto topFind = fBmhParser.fTopicMap.find(fullname); + auto reportEnd = reported->end(); + auto reportFind = std::find(reported->begin(), reported->end(), subtopic); + if (topEnd == topFind) { + if (reportEnd == reportFind) { + reported->push_back(subtopic); } } } } - return true; + if (!containsMarkTypeIn) { + PRINTF("No #In: %s\n", csChild->fName.c_str()); + } } + bool checkRelatedFunctions() { + const Definition* cs = this->classOrStruct(); + if (!cs) { + return true; + } + const Definition* topic = cs->fParent; + SkASSERT(topic); + SkASSERT(MarkType::kTopic == topic->fMarkType); + string topicName = topic->fName; + vector<string> methodNames; + vector<string> reported; + string prefix = cs->fName + "::"; + for (auto& csChild : cs->fChildren) { + checkMethod(topicName, csChild, &reported); + } + for (auto missing : reported) { + string fullname = topicName + '_' + missing; + PRINTF("No #Subtopic: %s\n", fullname.c_str()); + } + return true; + } + bool checkSeeAlso() { return true; } - bool checkSubtopicContents() { - return true; - } + const Definition* classOrStruct() { + for (auto& rootChild : fRoot->fChildren) { + if (rootChild->isStructOrClass()) { + return rootChild; + } + } + return nullptr; + } + + enum class Optional { + kNo, + kYes, + }; private: const BmhParser& fBmhParser; diff --git a/chromium/third_party/skia/tools/bookmaker/spellCheck.cpp b/chromium/third_party/skia/tools/bookmaker/spellCheck.cpp index 838e44ec6e8..e5ad7463796 100644 --- a/chromium/third_party/skia/tools/bookmaker/spellCheck.cpp +++ b/chromium/third_party/skia/tools/bookmaker/spellCheck.cpp @@ -53,7 +53,6 @@ private: INHERITED::resetCommon(); fMethod = nullptr; fRoot = nullptr; - fTableState = TableState::kNone; fInCode = false; fInConst = false; fInFormula = false; @@ -74,7 +73,7 @@ private: const BmhParser& fBmhParser; Definition* fMethod; RootDefinition* fRoot; - TableState fTableState; + int fLocalLine; bool fInCode; bool fInConst; bool fInDescription; @@ -136,10 +135,6 @@ bool SpellCheck::check(Definition* def) { fLineCount = def->fLineCount; string printable = def->printableName(); const char* textStart = def->fContentStart; - if (MarkType::kParam != def->fMarkType && MarkType::kConst != def->fMarkType && - MarkType::kPrivate != def->fMarkType && TableState::kNone != fTableState) { - fTableState = TableState::kNone; - } switch (def->fMarkType) { case MarkType::kAlias: break; @@ -159,12 +154,6 @@ bool SpellCheck::check(Definition* def) { break; case MarkType::kConst: { fInConst = true; - if (TableState::kNone == fTableState) { - fTableState = TableState::kRow; - } - if (TableState::kRow == fTableState) { - fTableState = TableState::kColumn; - } this->wordCheck(def->fName); const char* lineEnd = strchr(textStart, '\n'); this->wordCheck(lineEnd - textStart, textStart); @@ -187,8 +176,6 @@ bool SpellCheck::check(Definition* def) { case MarkType::kEnumClass: this->wordCheck(def->fName); break; - case MarkType::kError: - break; case MarkType::kExample: break; case MarkType::kExperimental: @@ -204,10 +191,16 @@ bool SpellCheck::check(Definition* def) { break; case MarkType::kHeight: break; + case MarkType::kIllustration: + break; case MarkType::kImage: break; + case MarkType::kIn: + break; case MarkType::kLegend: break; + case MarkType::kLine: + break; case MarkType::kLink: break; case MarkType::kList: @@ -223,11 +216,9 @@ bool SpellCheck::check(Definition* def) { if (all_lower(method_name)) { method_name += "()"; } - string formattedStr = def->formatFunction(); if (!def->isClone() && Definition::MethodType::kOperator != def->fMethodType) { this->wordCheck(method_name); } - fTableState = TableState::kNone; fMethod = def; } break; case MarkType::kNoExample: @@ -235,12 +226,6 @@ bool SpellCheck::check(Definition* def) { case MarkType::kOutdent: break; case MarkType::kParam: { - if (TableState::kNone == fTableState) { - fTableState = TableState::kRow; - } - if (TableState::kRow == fTableState) { - fTableState = TableState::kColumn; - } TextParser paramParser(def->fFileName, def->fStart, def->fContentStart, def->fLineCount); paramParser.skipWhiteSpace(); @@ -256,6 +241,8 @@ bool SpellCheck::check(Definition* def) { } break; case MarkType::kPlatform: break; + case MarkType::kPopulate: + break; case MarkType::kPrivate: break; case MarkType::kReturn: @@ -264,6 +251,8 @@ bool SpellCheck::check(Definition* def) { break; case MarkType::kSeeAlso: break; + case MarkType::kSet: + break; case MarkType::kStdOut: { fInStdOut = true; TextParser code(def); @@ -338,8 +327,6 @@ bool SpellCheck::check(Definition* def) { case MarkType::kConst: fInConst = false; case MarkType::kParam: - SkASSERT(TableState::kColumn == fTableState); - fTableState = TableState::kRow; break; case MarkType::kReturn: case MarkType::kSeeAlso: @@ -392,6 +379,7 @@ void SpellCheck::leafCheck(const char* start, const char* end) { const char* wordStart = nullptr; const char* wordEnd = nullptr; const char* possibleEnd = nullptr; + fLocalLine = 0; do { if (wordStart && wordEnd) { if (!allLower || (!inQuotes && '\"' != lastCh && !inParens @@ -459,6 +447,9 @@ void SpellCheck::leafCheck(const char* start, const char* end) { allLower = false; case '-': // note that dash doesn't clear allLower break; + case '\n': + ++fLocalLine; + // fall through default: wordEnd = chPtr; break; @@ -494,7 +485,8 @@ void SpellCheck::report(SkCommandLineFlags::StringArray report) { continue; } if (iter.second.fCount == 1) { - SkDebugf("%s(%d): %s\n", iter.second.fFile.c_str(), iter.second.fLine, + string fullName = this->ReportFilename(iter.second.fFile); + SkDebugf("%s(%d): %s\n", fullName.c_str(), iter.second.fLine, iter.first.c_str()); } } @@ -564,7 +556,8 @@ void SpellCheck::report(SkCommandLineFlags::StringArray report) { break; } if (check.compare(mispelled) == 0) { - SkDebugf("%s(%d): %s\n", iter.second.fFile.c_str(), iter.second.fLine, + string fullName = this->ReportFilename(iter.second.fFile); + SkDebugf("%s(%d): %s\n", fullName.c_str(), iter.second.fLine, iter.first.c_str()); if (report.count() == ++index) { break; @@ -655,7 +648,7 @@ void SpellCheck::wordCheck(const string& str) { } else { CheckEntry* entry = &mappy[str]; entry->fFile = fFileName; - entry->fLine = fLineCount; + entry->fLine = fLineCount + fLocalLine; entry->fCount = 1; } } diff --git a/chromium/third_party/skia/tools/check-headers-self-sufficient b/chromium/third_party/skia/tools/check-headers-self-sufficient index 9b676c1094c..fe58b63fb4d 100755 --- a/chromium/third_party/skia/tools/check-headers-self-sufficient +++ b/chromium/third_party/skia/tools/check-headers-self-sufficient @@ -77,7 +77,6 @@ all_header_args = [ '-Itools/flags', '-Itools/gpu', '-Itools/timer', - '-Ithird_party/etc1', '-Ithird_party/externals/jsoncpp/include', '-Ithird_party/externals/libjpeg-turbo', '-Ithird_party/externals/sfntly/cpp/src', diff --git a/chromium/third_party/skia/tools/colorspaceinfo.cpp b/chromium/third_party/skia/tools/colorspaceinfo.cpp index 390f7cc4cb1..2873e46508d 100644 --- a/chromium/third_party/skia/tools/colorspaceinfo.cpp +++ b/chromium/third_party/skia/tools/colorspaceinfo.cpp @@ -518,7 +518,7 @@ int main(int argc, char** argv) { return ss.str(); }; - if (SkColorSpace_Base::Type::kXYZ == as_CSB(colorSpace)->type()) { + if (colorSpace->toXYZD50()) { SkDebugf("XYZ/TRC color space\n"); // Load a graph of the CIE XYZ color gamut. diff --git a/chromium/third_party/skia/tools/debugger/SkDrawCommand.cpp b/chromium/third_party/skia/tools/debugger/SkDrawCommand.cpp index 34189fa6a46..57666891033 100644 --- a/chromium/third_party/skia/tools/debugger/SkDrawCommand.cpp +++ b/chromium/third_party/skia/tools/debugger/SkDrawCommand.cpp @@ -15,7 +15,7 @@ #include "SkDashPathEffect.h" #include "SkImageFilter.h" #include "SkJsonWriteBuffer.h" -#include "SkMaskFilter.h" +#include "SkMaskFilterBase.h" #include "SkObjectParser.h" #include "SkPaintDefaults.h" #include "SkPathEffect.h" @@ -922,7 +922,7 @@ static SkBitmap* load_bitmap(const Json::Value& jsonBitmap, UrlDataManager& urlD std::unique_ptr<SkBitmap> bitmap(new SkBitmap()); if (nullptr != image) { - if (!image->asLegacyBitmap(bitmap.get(), SkImage::kRW_LegacyBitmapMode)) { + if (!image->asLegacyBitmap(bitmap.get())) { SkDebugf("image decode failed\n"); return nullptr; } @@ -1071,8 +1071,8 @@ static void apply_paint_maskfilter(const SkPaint& paint, Json::Value* target, UrlDataManager& urlDataManager) { SkMaskFilter* maskFilter = paint.getMaskFilter(); if (maskFilter != nullptr) { - SkMaskFilter::BlurRec blurRec; - if (maskFilter->asABlur(&blurRec)) { + SkMaskFilterBase::BlurRec blurRec; + if (as_MFB(maskFilter)->asABlur(&blurRec)) { Json::Value blur(Json::objectValue); blur[SKDEBUGCANVAS_ATTRIBUTE_SIGMA] = Json::Value(blurRec.fSigma); switch (blurRec.fStyle) { diff --git a/chromium/third_party/skia/tools/debugger/SkObjectParser.cpp b/chromium/third_party/skia/tools/debugger/SkObjectParser.cpp index 352a01da237..dae2cde92e5 100644 --- a/chromium/third_party/skia/tools/debugger/SkObjectParser.cpp +++ b/chromium/third_party/skia/tools/debugger/SkObjectParser.cpp @@ -349,9 +349,6 @@ SkString* SkObjectParser::RegionToString(const SkRegion& region) { SkString* SkObjectParser::SaveLayerFlagsToString(SkCanvas::SaveLayerFlags saveLayerFlags) { SkString* mFlags = new SkString("SkCanvas::SaveFlags: "); - if (saveLayerFlags & SkCanvas::kIsOpaque_SaveLayerFlag) { - mFlags->append("kIsOpaque_SaveLayerFlag "); - } if (saveLayerFlags & SkCanvas::kPreserveLCDText_SaveLayerFlag) { mFlags->append("kPreserveLCDText_SaveLayerFlag "); } diff --git a/chromium/third_party/skia/tools/fiddle/fiddle_main.cpp b/chromium/third_party/skia/tools/fiddle/fiddle_main.cpp index 4ba84f38ca0..9ade250b0ae 100644 --- a/chromium/third_party/skia/tools/fiddle/fiddle_main.cpp +++ b/chromium/third_party/skia/tools/fiddle/fiddle_main.cpp @@ -133,7 +133,7 @@ static bool setup_backend_objects(GrContext* context, backingDesc.fHeight = bm.height(); // This config must match the SkColorType used in draw.cpp in the SkImage and Surface factories backingDesc.fConfig = kRGBA_8888_GrPixelConfig; - backingDesc.fSampleCnt = 0; + backingDesc.fSampleCnt = 1; if (!bm.empty()) { SkPixmap originalPixmap; @@ -265,7 +265,7 @@ int main(int argc, char** argv) { perror("Unable to decode the source image."); return 1; } - SkAssertResult(image->asLegacyBitmap(&source, SkImage::kRO_LegacyBitmapMode)); + SkAssertResult(image->asLegacyBitmap(&source)); } } sk_sp<SkData> rasterData, gpuData, pdfData, skpData; diff --git a/chromium/third_party/skia/tools/flags/SkCommonFlags.cpp b/chromium/third_party/skia/tools/flags/SkCommonFlags.cpp index dd3a855d3fc..4f895058abd 100644 --- a/chromium/third_party/skia/tools/flags/SkCommonFlags.cpp +++ b/chromium/third_party/skia/tools/flags/SkCommonFlags.cpp @@ -31,13 +31,13 @@ DEFINE_bool(simpleCodec, false, "Runs of a subset of the codec tests. " "For nanobench, this means always N32, Premul or Opaque."); DEFINE_string2(match, m, nullptr, - "[~][^]substring[$] [...] of GM name to run.\n" + "[~][^]substring[$] [...] of name to run.\n" "Multiple matches may be separated by spaces.\n" - "~ causes a matching GM to always be skipped\n" - "^ requires the start of the GM to match\n" - "$ requires the end of the GM to match\n" + "~ causes a matching name to always be skipped\n" + "^ requires the start of the name to match\n" + "$ requires the end of the name to match\n" "^ and $ requires an exact match\n" - "If a GM does not match any list entry,\n" + "If a name does not match any list entry,\n" "it is skipped unless some list entry starts with ~"); DEFINE_bool2(quiet, q, false, "if true, don't print status updates."); @@ -50,9 +50,21 @@ DEFINE_bool(releaseAndAbandonGpuContext, false, "Test releasing all gpu resources and abandoning the GrContext after running each " "test"); +DEFINE_bool(disableDriverCorrectnessWorkarounds, false, "Disables all GPU driver correctness " + "workarounds"); + +#ifdef SK_BUILD_FOR_ANDROID +DEFINE_string(skps, "/data/local/tmp/skps", "Directory to read skps from."); +DEFINE_string(jpgs, "/data/local/tmp/resources", "Directory to read jpgs from."); +DEFINE_string(jsons, "/data/local/tmp/jsons", "Directory to read (Bodymovin) jsons from."); +#else DEFINE_string(skps, "skps", "Directory to read skps from."); +DEFINE_string(jpgs, "jpgs", "Directory to read jpgs from."); +DEFINE_string(jsons, "jsons", "Directory to read (Bodymovin) jsons from."); +#endif -DEFINE_string(jsons, "", "Directory to read Bodymovin JSONs from, or a single JSON file."); +DEFINE_bool(nativeFonts, true, "If true, use native font manager and rendering. " + "If false, fonts will draw as portably as possible."); DEFINE_string(svgs, "", "Directory to read SVGs from, or a single SVG file."); @@ -87,6 +99,9 @@ DEFINE_bool(deltaAA, kDefaultDeltaAA, DEFINE_bool(forceDeltaAA, false, "Force delta anti-aliasing for all paths."); +DEFINE_int32(backendTiles, 3, "Number of tiles in the experimental threaded backend."); +DEFINE_int32(backendThreads, 2, "Number of threads in the experimental threaded backend."); + bool CollectImages(SkCommandLineFlags::StringArray images, SkTArray<SkString>* output) { SkASSERT(output); @@ -156,6 +171,7 @@ void SetCtxOptionsFromCommonFlags(GrContextOptions* ctxOptions) { ctxOptions->fAllowPathMaskCaching = FLAGS_cachePathMasks; ctxOptions->fSuppressGeometryShaders = FLAGS_noGS; ctxOptions->fGpuPathRenderers = CollectGpuPathRenderersFromFlags(); + ctxOptions->fDisableDriverCorrectnessWorkarounds = FLAGS_disableDriverCorrectnessWorkarounds; } #endif diff --git a/chromium/third_party/skia/tools/flags/SkCommonFlags.h b/chromium/third_party/skia/tools/flags/SkCommonFlags.h index a3eb0011001..1f0c8d14f81 100644 --- a/chromium/third_party/skia/tools/flags/SkCommonFlags.h +++ b/chromium/third_party/skia/tools/flags/SkCommonFlags.h @@ -25,8 +25,10 @@ DECLARE_bool(abandonGpuContext); DECLARE_bool(releaseAndAbandonGpuContext); DECLARE_string(skps); DECLARE_bool(ddl); +DECLARE_string(jpgs); DECLARE_string(jsons); DECLARE_string(svgs); +DECLARE_bool(nativeFonts); DECLARE_int32(threads); DECLARE_string(resourcePath); DECLARE_bool(verbose); @@ -39,6 +41,8 @@ DECLARE_bool(deltaAA); DECLARE_bool(forceDeltaAA); DECLARE_string(key); DECLARE_string(properties); +DECLARE_int32(backendTiles); +DECLARE_int32(backendThreads) /** * Helper to assist in collecting image paths from |dir| specified through a command line flag. diff --git a/chromium/third_party/skia/tools/flags/SkCommonFlagsConfig.cpp b/chromium/third_party/skia/tools/flags/SkCommonFlagsConfig.cpp index 00db61a2406..f9988f8769a 100644 --- a/chromium/third_party/skia/tools/flags/SkCommonFlagsConfig.cpp +++ b/chromium/third_party/skia/tools/flags/SkCommonFlagsConfig.cpp @@ -5,7 +5,6 @@ * found in the LICENSE file. */ -#include "SkColorSpace_Base.h" #include "SkCommonFlagsConfig.h" #include "SkImageInfo.h" @@ -43,18 +42,7 @@ static const struct { { "glesmsaa4", "gpu", "api=gles,samples=4" }, { "glnvpr4", "gpu", "api=gl,nvpr=true,samples=4" }, { "glnvpr8" , "gpu", "api=gl,nvpr=true,samples=8" }, - { "glnvprdit4", "gpu", "api=gl,nvpr=true,samples=4,dit=true" }, - { "glnvprdit8" , "gpu", "api=gl,nvpr=true,samples=8,dit=true" }, { "glesnvpr4", "gpu", "api=gles,nvpr=true,samples=4" }, - { "glesnvprdit4", "gpu", "api=gles,nvpr=true,samples=4,dit=true" }, - { "glinst", "gpu", "api=gl,inst=true" }, - { "glinst4", "gpu", "api=gl,inst=true,samples=4" }, - { "glinstdit4", "gpu", "api=gl,inst=true,samples=4,dit=true" }, - { "glinst8" , "gpu", "api=gl,inst=true,samples=8" }, - { "glinstdit8" , "gpu", "api=gl,inst=true,samples=8,dit=true" }, - { "glesinst", "gpu", "api=gles,inst=true" }, - { "glesinst4", "gpu", "api=gles,inst=true,samples=4" }, - { "glesinstdit4", "gpu", "api=gles,inst=true,samples=4,dit=true" }, { "gl4444", "gpu", "api=gl,color=4444" }, { "gl565", "gpu", "api=gl,color=565" }, { "glf16", "gpu", "api=gl,color=f16" }, @@ -67,6 +55,7 @@ static const struct { { "glwide", "gpu", "api=gl,color=f16_wide" }, { "glnarrow", "gpu", "api=gl,color=f16_narrow" }, { "glnostencils", "gpu", "api=gl,stencils=false" }, + { "gles4444", "gpu", "api=gles,color=4444" }, { "glessrgb", "gpu", "api=gles,color=srgb" }, { "gleswide", "gpu", "api=gles,color=f16_wide" }, { "glesnarrow", "gpu", "api=gles,color=f16_narrow" }, @@ -399,7 +388,7 @@ SkCommandLineConfigGpu* parse_command_line_config_gpu(const SkString& tag, bool seenUseDIText =false; bool useDIText = false; bool seenSamples = false; - int samples = 0; + int samples = 1; bool seenColor = false; SkColorType colorType = kRGBA_8888_SkColorType; SkAlphaType alphaType = kPremul_SkAlphaType; diff --git a/chromium/third_party/skia/tools/SkRandomScalerContext.cpp b/chromium/third_party/skia/tools/fonts/SkRandomScalerContext.cpp index 49d9ab43ffd..31a85b783ea 100644 --- a/chromium/third_party/skia/tools/SkRandomScalerContext.cpp +++ b/chromium/third_party/skia/tools/fonts/SkRandomScalerContext.cpp @@ -12,7 +12,6 @@ #include "SkMakeUnique.h" #include "SkPath.h" #include "SkRandomScalerContext.h" -#include "SkRasterizer.h" class SkDescriptor; diff --git a/chromium/third_party/skia/tools/SkRandomScalerContext.h b/chromium/third_party/skia/tools/fonts/SkRandomScalerContext.h index b71689d9e2d..b71689d9e2d 100644 --- a/chromium/third_party/skia/tools/SkRandomScalerContext.h +++ b/chromium/third_party/skia/tools/fonts/SkRandomScalerContext.h diff --git a/chromium/third_party/skia/tools/fonts/SkTestFontMgr.cpp b/chromium/third_party/skia/tools/fonts/SkTestFontMgr.cpp new file mode 100644 index 00000000000..3b5d1589f2b --- /dev/null +++ b/chromium/third_party/skia/tools/fonts/SkTestFontMgr.cpp @@ -0,0 +1,147 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkFontDescriptor.h" +#include "SkTestFontMgr.h" +#include "sk_tool_utils.h" + +namespace { + +static constexpr const char* kFamilyNames[] = { + "Toy Liberation Sans", + "Toy Liberation Serif", + "Toy Liberation Mono", +}; + +class FontStyleSet final : public SkFontStyleSet { +public: + explicit FontStyleSet(int familyIndex) { + using sk_tool_utils::create_portable_typeface; + const char* familyName = kFamilyNames[familyIndex]; + + fTypefaces[0] = create_portable_typeface(familyName, SkFontStyle::Normal()); + fTypefaces[1] = create_portable_typeface(familyName, SkFontStyle::Bold()); + fTypefaces[2] = create_portable_typeface(familyName, SkFontStyle::Italic()); + fTypefaces[3] = create_portable_typeface(familyName, SkFontStyle::BoldItalic()); + } + + int count() override { return 4; } + + void getStyle(int index, SkFontStyle* style, SkString* name) override { + switch (index) { + default: + case 0: if (style) { *style = SkFontStyle::Normal(); } + if (name) { *name = "Normal"; } + break; + case 1: if (style) { *style = SkFontStyle::Bold(); } + if (name) { *name = "Bold"; } + break; + case 2: if (style) { *style = SkFontStyle::Italic(); } + if (name) { *name = "Italic"; } + break; + case 3: if (style) { *style = SkFontStyle::BoldItalic(); } + if (name) { *name = "BoldItalic"; } + break; + } + } + + SkTypeface* createTypeface(int index) override { + return SkRef(fTypefaces[index].get()); + } + + SkTypeface* matchStyle(const SkFontStyle& pattern) override { + return this->matchStyleCSS3(pattern); + } + +private: + sk_sp<SkTypeface> fTypefaces[4]; +}; + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + +class FontMgr final : public SkFontMgr { +public: + FontMgr() { + fFamilies[0] = sk_make_sp<FontStyleSet>(0); + fFamilies[1] = sk_make_sp<FontStyleSet>(1); + fFamilies[2] = sk_make_sp<FontStyleSet>(2); + } + + int onCountFamilies() const override { return SK_ARRAY_COUNT(fFamilies); } + + void onGetFamilyName(int index, SkString* familyName) const override { + *familyName = kFamilyNames[index]; + } + + SkFontStyleSet* onCreateStyleSet(int index) const override { + return SkRef(fFamilies[index].get()); + } + + SkFontStyleSet* onMatchFamily(const char familyName[]) const override { + if (familyName) { + if (strstr(familyName, "ans")) { return this->createStyleSet(0); } + if (strstr(familyName, "erif")) { return this->createStyleSet(1); } + if (strstr(familyName, "ono")) { return this->createStyleSet(2); } + } + return this->createStyleSet(0); + } + + + SkTypeface* onMatchFamilyStyle(const char familyName[], + const SkFontStyle& style) const override { + sk_sp<SkFontStyleSet> styleSet(this->matchFamily(familyName)); + return styleSet->matchStyle(style); + } + + SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], + const SkFontStyle& style, + const char* bcp47[], int bcp47Count, + SkUnichar character) const override { + (void)bcp47; + (void)bcp47Count; + (void)character; + return this->matchFamilyStyle(familyName, style); + } + + SkTypeface* onMatchFaceStyle(const SkTypeface* tf, + const SkFontStyle& style) const override { + SkString familyName; + tf->getFamilyName(&familyName); + return this->matchFamilyStyle(familyName.c_str(), style); + } + + sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData>, int ttcIndex) const override { + return nullptr; + } + sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset>, + int ttcIndex) const override { + return nullptr; + } + sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset>, + const SkFontArguments&) const override { + return nullptr; + } + sk_sp<SkTypeface> onMakeFromFontData(std::unique_ptr<SkFontData>) const override { + return nullptr; + } + sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override { + return nullptr; + } + + sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], + SkFontStyle style) const override { + return sk_sp<SkTypeface>(this->matchFamilyStyle(familyName, style)); + } + +private: + sk_sp<FontStyleSet> fFamilies[3]; +}; +} + +namespace sk_tool_utils { +sk_sp<SkFontMgr> MakePortableFontMgr() { return sk_make_sp<FontMgr>(); } +} // namespace sk_tool_utils diff --git a/chromium/third_party/skia/tools/fonts/SkTestFontMgr.h b/chromium/third_party/skia/tools/fonts/SkTestFontMgr.h new file mode 100644 index 00000000000..6f4bb9c3c0c --- /dev/null +++ b/chromium/third_party/skia/tools/fonts/SkTestFontMgr.h @@ -0,0 +1,19 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTestFontMgr_DEFINED +#define SkTestFontMgr_DEFINED + +#include "SkFontMgr.h" + +// An SkFontMgr that always uses sk_tool_utils::create_portable_typeface(). + +namespace sk_tool_utils { + sk_sp<SkFontMgr> MakePortableFontMgr(); +} // namespace sk_tool_utils + +#endif //SkTestFontMgr_DEFINED diff --git a/chromium/third_party/skia/tools/SkTestScalerContext.cpp b/chromium/third_party/skia/tools/fonts/SkTestScalerContext.cpp index f74b1bd57ac..f74b1bd57ac 100644 --- a/chromium/third_party/skia/tools/SkTestScalerContext.cpp +++ b/chromium/third_party/skia/tools/fonts/SkTestScalerContext.cpp diff --git a/chromium/third_party/skia/tools/SkTestScalerContext.h b/chromium/third_party/skia/tools/fonts/SkTestScalerContext.h index e62210b2e30..e62210b2e30 100644 --- a/chromium/third_party/skia/tools/SkTestScalerContext.h +++ b/chromium/third_party/skia/tools/fonts/SkTestScalerContext.h diff --git a/chromium/third_party/skia/tools/create_test_font.cpp b/chromium/third_party/skia/tools/fonts/create_test_font.cpp index 4fd8dbbdbb0..51c56eaee1f 100644 --- a/chromium/third_party/skia/tools/create_test_font.cpp +++ b/chromium/third_party/skia/tools/fonts/create_test_font.cpp @@ -5,10 +5,10 @@ * found in the LICENSE file. */ -// running create_test_font generates ./tools/test_font_index.inc -// and ./tools/test_font_<generic name>.inc which are read by ./tools/sk_tool_utils_font.cpp +// Running create_test_font generates ./tools/fonts/test_font_index.inc +// and ./tools/fonts/test_font_<generic name>.inc which are read by +// ./tools/fonts/sk_tool_utils_font.cpp -#include "Resources.h" #include "SkFontStyle.h" #include "SkOSFile.h" #include "SkOSPath.h" @@ -72,6 +72,7 @@ const char gHeader[] = static FILE* font_header(const char* family) { SkString outPath(SkOSPath::Join(".", "tools")); + outPath = SkOSPath::Join(outPath.c_str(), "fonts"); outPath = SkOSPath::Join(outPath.c_str(), "test_font_"); SkString fam(family); do { @@ -430,7 +431,7 @@ static void generate_index(const char* defaultName) { } int main(int , char * const []) { - generate_fonts("/Library/Fonts/"); + generate_fonts("/Library/Fonts/"); // or /usr/share/fonts/truetype/ttf-liberation/ generate_index(DEFAULT_FONT_NAME); return 0; } diff --git a/chromium/third_party/skia/tools/generate_fir_coeff.py b/chromium/third_party/skia/tools/fonts/generate_fir_coeff.py index 70f521fdafc..f5cc5e55a07 100644 --- a/chromium/third_party/skia/tools/generate_fir_coeff.py +++ b/chromium/third_party/skia/tools/fonts/generate_fir_coeff.py @@ -28,18 +28,26 @@ def withinStdDevRange(a, b): return (withinStdDev(b) - withinStdDev(a)) / 2; -#We have a bunch of smudged samples which represent the average coverage of a range. -#We have a 'center' which may not line up with those samples. -#From the 'center' we want to make a normal approximation where '5' sample width out we're at '3' std deviations. -#The first and last samples may not be fully covered. - -#This is the sub-sample shift for each set of FIR coefficients (the centers of the lcds in the samples) -#Each subpxl takes up 1/3 of a pixel, so they are centered at x=(i/n+1/2n), or 1/6, 3/6, 5/6 of a pixel. -#Each sample takes up 1/4 of a pixel, so the results fall at (x*4)%1, or 2/3, 0, 1/3 of a sample. +# We have some smudged samples which represent the average coverage of a range. +# We have a 'center' which may not line up with those samples. +# From center make a normal where 5 sample widths out is at 3 std deviations. +# The first and last samples may not be fully covered. + +# This is the sub-sample shift for each set of FIR coefficients +# (the centers of the lcds in the samples) +# Each subpxl takes up 1/3 of a pixel, +# so they are centered at x=(i/n+1/2n), or 1/6, 3/6, 5/6 of a pixel. +# Each sample takes up 1/4 of a pixel, +# so the results fall at (x*4)%1, or 2/3, 0, 1/3 of a sample. samples_per_pixel = 4 subpxls_per_pixel = 3 #sample_offsets is (frac, int) in sample units. -sample_offsets = [math.modf((float(subpxl_index)/subpxls_per_pixel + 1.0/(2.0*subpxls_per_pixel))*samples_per_pixel) for subpxl_index in range(subpxls_per_pixel)] +sample_offsets = [ + math.modf( + (float(subpxl_index)/subpxls_per_pixel + 1.0/(2.0*subpxls_per_pixel)) + * samples_per_pixel + ) for subpxl_index in range(subpxls_per_pixel) +] #How many samples to consider to the left and right of the subpxl center. sample_units_width = 5 @@ -65,7 +73,9 @@ for sample_offset, sample_align in sample_offsets: if current_sample_right > sample_offset + sample_units_width: done = True current_sample_right = sample_offset + sample_units_width - current_std_dev_right = current_std_dev_left + ((current_sample_right - current_sample_left) / sample_units_width) * std_dev_max + current_std_dev_right = current_std_dev_left + ( + (current_sample_right - current_sample_left) / sample_units_width + ) * std_dev_max coverage = withinStdDevRange(current_std_dev_left, current_std_dev_right) coeffs.append(coverage * target_sum) @@ -74,15 +84,17 @@ for sample_offset, sample_align in sample_offsets: current_sample_left = current_sample_right current_std_dev_left = current_std_dev_right - # Now we have the numbers we want, but our rounding needs to add up to target_sum. + # Have the numbers, but rounding needs to add up to target_sum. delta = 0 coeffs_rounded_sum = sum(coeffs_rounded) if coeffs_rounded_sum > target_sum: - # The coeffs add up to too much. Subtract 1 from the ones which were rounded up the most. + # The coeffs add up to too much. + # Subtract 1 from the ones which were rounded up the most. delta = -1 if coeffs_rounded_sum < target_sum: - # The coeffs add up to too little. Add 1 to the ones which were rounded down the most. + # The coeffs add up to too little. + # Add 1 to the ones which were rounded down the most. delta = 1 if delta: @@ -102,18 +114,20 @@ for sample_offset, sample_align in sample_offsets: coeff_pkg = [IndexTracker(i, diff) for i, diff in enumerate(coeff_diff)] coeff_pkg.sort() - # num_elements_to_force_round had better be < (2 * sample_units_width + 1) or + # num_elements_to_force_round better be < (2 * sample_units_width + 1) or # * our math was wildy wrong # * an awful lot of the curve is out side our sample # either is pretty bad, and probably means the results will not be useful. num_elements_to_force_round = abs(coeffs_rounded_sum - target_sum) for i in xrange(num_elements_to_force_round): - print "Adding %d to index %d to force round %f." % (delta, coeff_pkg[i].index, coeffs[coeff_pkg[i].index]) + print "Adding %d to index %d to force round %f." % ( + delta, coeff_pkg[i].index, coeffs[coeff_pkg[i].index]) coeffs_rounded[coeff_pkg[i].index] += delta print "Prepending %d 0x00 for allignment." % (sample_align,) coeffs_rounded_aligned = ([0] * int(sample_align)) + coeffs_rounded - print ', '.join(["0x%0.2X" % coeff_rounded for coeff_rounded in coeffs_rounded_aligned]) + print ', '.join(["0x%0.2X" % coeff_rounded + for coeff_rounded in coeffs_rounded_aligned]) print sum(coeffs), hex(sum(coeffs_rounded)) print diff --git a/chromium/third_party/skia/tools/fonts/sk_tool_utils_font.cpp b/chromium/third_party/skia/tools/fonts/sk_tool_utils_font.cpp new file mode 100644 index 00000000000..d2aac851bbb --- /dev/null +++ b/chromium/third_party/skia/tools/fonts/sk_tool_utils_font.cpp @@ -0,0 +1,148 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Resources.h" +#include "SkCommonFlags.h" +#include "SkFontMgr.h" +#include "SkFontStyle.h" +#include "SkMutex.h" +#include "SkOSFile.h" +#include "SkTestScalerContext.h" +#include "SkUtils.h" +#include "sk_tool_utils.h" + +namespace sk_tool_utils { + +#include "test_font_monospace.inc" +#include "test_font_sans_serif.inc" +#include "test_font_serif.inc" +#include "test_font_index.inc" + +void release_portable_typefaces() { + for (int index = 0; index < gTestFontsCount; ++index) { + SkTestFontData& fontData = gTestFonts[index]; + fontData.fCachedFont.reset(); + } +} + +SK_DECLARE_STATIC_MUTEX(gTestFontMutex); + +sk_sp<SkTypeface> create_font(const char* name, SkFontStyle style) { + SkTestFontData* fontData = nullptr; + const SubFont* sub; + if (name) { + for (int index = 0; index < gSubFontsCount; ++index) { + sub = &gSubFonts[index]; + if (!strcmp(name, sub->fName) && sub->fStyle == style) { + fontData = &sub->fFont; + break; + } + } + if (!fontData) { + // Once all legacy callers to portable fonts are converted, replace this with + // SK_ABORT(); + SkDebugf("missing %s weight %d, width %d, slant %d\n", + name, style.weight(), style.width(), style.slant()); + // If we called SkTypeface::CreateFromName() here we'd recurse infinitely, + // so we reimplement its core logic here inline without the recursive aspect. + sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); + return fm->legacyMakeTypeface(name, style); + } + } else { + sub = &gSubFonts[gDefaultFontIndex]; + fontData = &sub->fFont; + } + sk_sp<SkTestFont> font; + { + SkAutoMutexAcquire ac(gTestFontMutex); + if (fontData->fCachedFont) { + font = fontData->fCachedFont; + } else { + font = sk_make_sp<SkTestFont>(*fontData); + fontData->fCachedFont = font; + } + } + return sk_make_sp<SkTestTypeface>(std::move(font), style); +} + +sk_sp<SkTypeface> emoji_typeface() { +#if defined(SK_BUILD_FOR_WIN) + sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); + const char *colorEmojiFontName = "Segoe UI Emoji"; + sk_sp<SkTypeface> typeface(fm->matchFamilyStyle(colorEmojiFontName, SkFontStyle())); + if (typeface) { + return typeface; + } + sk_sp<SkTypeface> fallback(fm->matchFamilyStyleCharacter( + colorEmojiFontName, SkFontStyle(), nullptr /* bcp47 */, 0 /* bcp47Count */, + 0x1f4b0 /* character: 💰 */)); + if (fallback) { + return fallback; + } + // If we don't have Segoe UI Emoji and can't find a fallback, try Segoe UI Symbol. + // Windows 7 does not have Segoe UI Emoji; Segoe UI Symbol has the (non - color) emoji. + return SkTypeface::MakeFromName("Segoe UI Symbol", SkFontStyle()); + +#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) + return SkTypeface::MakeFromName("Apple Color Emoji", SkFontStyle()); + +#else + return MakeResourceAsTypeface("fonts/Funkster.ttf"); + +#endif +} + +const char* emoji_sample_text() { +#if defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) + return "\xF0\x9F\x92\xB0" "\xF0\x9F\x8F\xA1" "\xF0\x9F\x8E\x85" // 💰🏡🎅 + "\xF0\x9F\x8D\xAA" "\xF0\x9F\x8D\x95" "\xF0\x9F\x9A\x80" // 🍪🍕🚀 + "\xF0\x9F\x9A\xBB" "\xF0\x9F\x92\xA9" "\xF0\x9F\x93\xB7" // 🚻💩📷 + "\xF0\x9F\x93\xA6" // 📦 + "\xF0\x9F\x87\xBA" "\xF0\x9F\x87\xB8" "\xF0\x9F\x87\xA6"; // 🇺🇸🇦 +#else + return "Hamburgefons"; +#endif +} + +static const char* platform_os_name() { + for (int index = 0; index < FLAGS_key.count(); index += 2) { + if (!strcmp("os", FLAGS_key[index])) { + return FLAGS_key[index + 1]; + } + } + return ""; +} + +static bool extra_config_contains(const char* substring) { + for (int index = 0; index < FLAGS_key.count(); index += 2) { + if (0 == strcmp("extra_config", FLAGS_key[index]) + && strstr(FLAGS_key[index + 1], substring)) { + return true; + } + } + return false; +} + +const char* platform_font_manager() { + if (extra_config_contains("GDI")) { + return "GDI"; + } + if (extra_config_contains("NativeFonts")){ + return platform_os_name(); + } + return ""; +} + +sk_sp<SkTypeface> create_portable_typeface(const char* name, SkFontStyle style) { + return create_font(name, style); +} + +void set_portable_typeface(SkPaint* paint, const char* name, SkFontStyle style) { + paint->setTypeface(create_font(name, style)); +} + +} diff --git a/chromium/third_party/skia/tools/test_font_index.inc b/chromium/third_party/skia/tools/fonts/test_font_index.inc index aedfcde271a..aedfcde271a 100644 --- a/chromium/third_party/skia/tools/test_font_index.inc +++ b/chromium/third_party/skia/tools/fonts/test_font_index.inc diff --git a/chromium/third_party/skia/tools/test_font_monospace.inc b/chromium/third_party/skia/tools/fonts/test_font_monospace.inc index 60bcd6978ac..a8e2c4ce5bd 100644 --- a/chromium/third_party/skia/tools/test_font_monospace.inc +++ b/chromium/third_party/skia/tools/fonts/test_font_monospace.inc @@ -1193,9 +1193,8 @@ const SkFixed LiberationMonoNormalWidths[] = { const int LiberationMonoNormalCharCodesCount = (int) SK_ARRAY_COUNT(LiberationMonoNormalCharCodes); const SkPaint::FontMetrics LiberationMonoNormalMetrics = { -0x0000000f, -0.83252f, -0.83252f, 0.300293f, 0.300293f, 0, 0.600098f, 0.00989532f, --0.0244141f, 0.608887f, 0.52832f, 0.0102921f, 0.0410156f, 0.23291f, 0.000778198f, --0.00404358f +0x0000000f, -0.83252f, -0.83252f, 0.300293f, 0.300293f, 0, 0.600098f, 0.633301f, -0.0244141f, +0.608887f, 0.52832f, 0.658691f, 0.0410156f, 0.23291f, 0.0498047f, -0.258789f }; const SkScalar LiberationMonoBoldPoints[] = { @@ -2344,9 +2343,8 @@ const SkFixed LiberationMonoBoldWidths[] = { const int LiberationMonoBoldCharCodesCount = (int) SK_ARRAY_COUNT(LiberationMonoBoldCharCodes); const SkPaint::FontMetrics LiberationMonoBoldMetrics = { -0x0000000f, -0.833496f, -0.833496f, 0.300293f, 0.300293f, 0, 0.600098f, 0.010025f, --0.0268555f, 0.614746f, 0.52832f, 0.0102921f, 0.100098f, 0.23291f, 0.000778198f, --0.00404358f +0x0000000f, -0.833496f, -0.833496f, 0.300293f, 0.300293f, 0, 0.600098f, 0.641602f, -0.0268555f, +0.614746f, 0.52832f, 0.658691f, 0.100098f, 0.23291f, 0.0498047f, -0.258789f }; const SkScalar LiberationMonoItalicPoints[] = { @@ -3611,9 +3609,8 @@ const SkFixed LiberationMonoItalicWidths[] = { const int LiberationMonoItalicCharCodesCount = (int) SK_ARRAY_COUNT(LiberationMonoItalicCharCodes); const SkPaint::FontMetrics LiberationMonoItalicMetrics = { -0x0000000f, -0.833496f, -0.833496f, 0.300293f, 0.300293f, 0, 0.600098f, 0.0124817f, --0.0942383f, 0.70459f, 0.52832f, 0.0102921f, 0.0410156f, 0.23291f, 0.000778198f, --0.00404358f +0x0000000f, -0.833496f, -0.833496f, 0.300293f, 0.300293f, 0, 0.600098f, 0.798828f, -0.0942383f, +0.70459f, 0.52832f, 0.658691f, 0.0410156f, 0.23291f, 0.0498047f, -0.258789f }; const SkScalar LiberationMonoBoldItalicPoints[] = { @@ -4845,8 +4842,7 @@ const SkFixed LiberationMonoBoldItalicWidths[] = { const int LiberationMonoBoldItalicCharCodesCount = (int) SK_ARRAY_COUNT(LiberationMonoBoldItalicCharCodes); const SkPaint::FontMetrics LiberationMonoBoldItalicMetrics = { -0x0000000f, -0.83252f, -0.83252f, 0.300293f, 0.300293f, 0, 0.600098f, 0.0123749f, --0.0942383f, 0.697754f, 0.52832f, 0.0102921f, 0.100098f, 0.23291f, 0.000778198f, --0.00404358f +0x0000000f, -0.83252f, -0.83252f, 0.300293f, 0.300293f, 0, 0.600098f, 0.791992f, -0.0942383f, +0.697754f, 0.52832f, 0.658691f, 0.100098f, 0.23291f, 0.0498047f, -0.258789f }; diff --git a/chromium/third_party/skia/tools/test_font_sans_serif.inc b/chromium/third_party/skia/tools/fonts/test_font_sans_serif.inc index 2011c5f8102..8bdfb33d2d3 100644 --- a/chromium/third_party/skia/tools/test_font_sans_serif.inc +++ b/chromium/third_party/skia/tools/fonts/test_font_sans_serif.inc @@ -1187,9 +1187,8 @@ const SkFixed LiberationSansNormalWidths[] = { const int LiberationSansNormalCharCodesCount = (int) SK_ARRAY_COUNT(LiberationSansNormalCharCodes); const SkPaint::FontMetrics LiberationSansNormalMetrics = { -0x0000000f, -0.910156f, -0.905273f, 0.211914f, 0.303223f, 0.0327148f, 0.589355f, 0.0195847f, --0.203125f, 1.05029f, 0.52832f, 0.0107498f, 0.0732422f, 0.105957f, 0.000778198f, --0.00404358f +0x0000000f, -0.910156f, -0.905273f, 0.211914f, 0.303223f, 0.0327148f, 0.589355f, 1.25342f, +-0.203125f, 1.05029f, 0.52832f, 0.687988f, 0.0732422f, 0.105957f, 0.0498047f, -0.258789f }; const SkScalar LiberationSansBoldPoints[] = { @@ -2352,8 +2351,8 @@ const SkFixed LiberationSansBoldWidths[] = { const int LiberationSansBoldCharCodesCount = (int) SK_ARRAY_COUNT(LiberationSansBoldCharCodes); const SkPaint::FontMetrics LiberationSansBoldMetrics = { -0x0000000f, -1.0332f, -0.905273f, 0.211914f, 0.303223f, 0.0327148f, 0.612305f, 0.0194702f, --0.184082f, 1.06201f, 0.52832f, 0.0107498f, 0.10498f, 0.105957f, 0.000778198f, -0.00404358f +0x0000000f, -1.0332f, -0.905273f, 0.211914f, 0.303223f, 0.0327148f, 0.612305f, 1.24609f, +-0.184082f, 1.06201f, 0.52832f, 0.687988f, 0.10498f, 0.105957f, 0.0498047f, -0.258789f }; const SkScalar LiberationSansItalicPoints[] = { @@ -3593,8 +3592,8 @@ const SkFixed LiberationSansItalicWidths[] = { const int LiberationSansItalicCharCodesCount = (int) SK_ARRAY_COUNT(LiberationSansItalicCharCodes); const SkPaint::FontMetrics LiberationSansItalicMetrics = { -0x0000000f, -1.01416f, -0.905273f, 0.211914f, 0.303223f, 0.0327148f, 0.590332f, 0.0208511f, --0.271973f, 1.0625f, 0.52832f, 0.0107498f, 0.0732422f, 0.105957f, 0.000778198f, -0.00404358f +0x0000000f, -1.01416f, -0.905273f, 0.211914f, 0.303223f, 0.0327148f, 0.590332f, 1.33447f, +-0.271973f, 1.0625f, 0.52832f, 0.687988f, 0.0732422f, 0.105957f, 0.0498047f, -0.258789f }; const SkScalar LiberationSansBoldItalicPoints[] = { @@ -4853,7 +4852,7 @@ const SkFixed LiberationSansBoldItalicWidths[] = { const int LiberationSansBoldItalicCharCodesCount = (int) SK_ARRAY_COUNT(LiberationSansBoldItalicCharCodes); const SkPaint::FontMetrics LiberationSansBoldItalicMetrics = { -0x0000000f, -1.02979f, -0.905273f, 0.211914f, 0.303223f, 0.0327148f, 0.61377f, 0.0208969f, --0.208984f, 1.12842f, 0.52832f, 0.0107498f, 0.10498f, 0.105957f, 0.000778198f, -0.00404358f +0x0000000f, -1.02979f, -0.905273f, 0.211914f, 0.303223f, 0.0327148f, 0.61377f, 1.3374f, +-0.208984f, 1.12842f, 0.52832f, 0.687988f, 0.10498f, 0.105957f, 0.0498047f, -0.258789f }; diff --git a/chromium/third_party/skia/tools/test_font_serif.inc b/chromium/third_party/skia/tools/fonts/test_font_serif.inc index 8035fb8f26b..4c7121c72de 100644 --- a/chromium/third_party/skia/tools/test_font_serif.inc +++ b/chromium/third_party/skia/tools/fonts/test_font_serif.inc @@ -1264,9 +1264,8 @@ const SkFixed LiberationSerifNormalWidths[] = { const int LiberationSerifNormalCharCodesCount = (int) SK_ARRAY_COUNT(LiberationSerifNormalCharCodes); const SkPaint::FontMetrics LiberationSerifNormalMetrics = { -0x0000000f, -0.981445f, -0.891113f, 0.216309f, 0.303223f, 0.0424805f, 0.567383f, 0.0184937f, --0.176758f, 1.00684f, 0.458984f, 0.010231f, 0.0488281f, 0.108887f, 0.000762939f, --0.00320435f +0x0000000f, -0.981445f, -0.891113f, 0.216309f, 0.303223f, 0.0424805f, 0.567383f, 1.18359f, +-0.176758f, 1.00684f, 0.458984f, 0.654785f, 0.0488281f, 0.108887f, 0.0488281f, -0.205078f }; const SkScalar LiberationSerifBoldPoints[] = { @@ -2515,9 +2514,8 @@ const SkFixed LiberationSerifBoldWidths[] = { const int LiberationSerifBoldCharCodesCount = (int) SK_ARRAY_COUNT(LiberationSerifBoldCharCodes); const SkPaint::FontMetrics LiberationSerifBoldMetrics = { -0x0000000f, -1.00781f, -0.891113f, 0.216309f, 0.303223f, 0.0424805f, 0.59375f, 0.0197983f, --0.182129f, 1.08496f, 0.458984f, 0.010231f, 0.0952148f, 0.108887f, 0.000778198f, --0.00404358f +0x0000000f, -1.00781f, -0.891113f, 0.216309f, 0.303223f, 0.0424805f, 0.59375f, 1.26709f, +-0.182129f, 1.08496f, 0.458984f, 0.654785f, 0.0952148f, 0.108887f, 0.0498047f, -0.258789f }; const SkScalar LiberationSerifItalicPoints[] = { @@ -3834,9 +3832,8 @@ const SkFixed LiberationSerifItalicWidths[] = { const int LiberationSerifItalicCharCodesCount = (int) SK_ARRAY_COUNT(LiberationSerifItalicCharCodes); const SkPaint::FontMetrics LiberationSerifItalicMetrics = { -0x0000000f, -0.980957f, -0.891113f, 0.216309f, 0.303223f, 0.0424805f, 0.559082f, 0.0197601f, --0.176758f, 1.08789f, 0.458984f, 0.010231f, 0.0488281f, 0.108887f, 0.000778198f, --0.00404358f +0x0000000f, -0.980957f, -0.891113f, 0.216309f, 0.303223f, 0.0424805f, 0.559082f, 1.26465f, +-0.176758f, 1.08789f, 0.458984f, 0.654785f, 0.0488281f, 0.108887f, 0.0498047f, -0.258789f }; const SkScalar LiberationSerifBoldItalicPoints[] = { @@ -5147,8 +5144,7 @@ const SkFixed LiberationSerifBoldItalicWidths[] = { const int LiberationSerifBoldItalicCharCodesCount = (int) SK_ARRAY_COUNT(LiberationSerifBoldItalicCharCodes); const SkPaint::FontMetrics LiberationSerifBoldItalicMetrics = { -0x0000000f, -0.980957f, -0.891113f, 0.216309f, 0.303223f, 0.0424805f, 0.578125f, 0.0207596f, --0.178223f, 1.15039f, 0.458984f, 0.010231f, 0.0952148f, 0.108887f, 0.000778198f, --0.00404358f +0x0000000f, -0.980957f, -0.891113f, 0.216309f, 0.303223f, 0.0424805f, 0.578125f, 1.32861f, +-0.178223f, 1.15039f, 0.458984f, 0.654785f, 0.0952148f, 0.108887f, 0.0498047f, -0.258789f }; diff --git a/chromium/third_party/skia/tools/gpu/GrContextFactory.cpp b/chromium/third_party/skia/tools/gpu/GrContextFactory.cpp index c6cceb48430..36926062475 100644 --- a/chromium/third_party/skia/tools/gpu/GrContextFactory.cpp +++ b/chromium/third_party/skia/tools/gpu/GrContextFactory.cpp @@ -25,7 +25,7 @@ #include "mock/MockTestContext.h" #include "GrCaps.h" -#if defined(SK_BUILD_FOR_WIN32) && defined(SK_ENABLE_DISCRETE_GPU) +#if defined(SK_BUILD_FOR_WIN) && defined(SK_ENABLE_DISCRETE_GPU) extern "C" { // NVIDIA documents that the presence and value of this symbol programmatically enable the high // performance GPU in laptops with switchable graphics. diff --git a/chromium/third_party/skia/tools/gpu/GrTest.cpp b/chromium/third_party/skia/tools/gpu/GrTest.cpp index f5115747260..c6d3bbe2ef4 100644 --- a/chromium/third_party/skia/tools/gpu/GrTest.cpp +++ b/chromium/third_party/skia/tools/gpu/GrTest.cpp @@ -147,17 +147,18 @@ void GrContext::printGpuStats() const { SkDebugf("%s", out.c_str()); } -sk_sp<SkImage> GrContext::getFontAtlasImage_ForTesting(GrMaskFormat format) { - GrAtlasGlyphCache* cache = this->getAtlasGlyphCache(); +sk_sp<SkImage> GrContext::getFontAtlasImage_ForTesting(GrMaskFormat format, uint32_t index) { + GrAtlasGlyphCache* cache = this->contextPriv().getAtlasGlyphCache(); - const sk_sp<GrTextureProxy>* proxies = cache->getProxies(format); - if (!proxies[0]) { + unsigned int numProxies; + const sk_sp<GrTextureProxy>* proxies = cache->getProxies(format, &numProxies); + if (index >= numProxies || !proxies[index]) { return nullptr; } - SkASSERT(proxies[0]->priv().isExact()); + SkASSERT(proxies[index]->priv().isExact()); sk_sp<SkImage> image(new SkImage_Gpu(this, kNeedNewImageUniqueID, kPremul_SkAlphaType, - std::move(proxies[0]), nullptr, SkBudgeted::kNo)); + proxies[index], nullptr, SkBudgeted::kNo)); return image; } @@ -182,6 +183,16 @@ void GrGpu::Stats::dumpKeyValuePairs(SkTArray<SkString>* keys, SkTArray<double>* #endif +GrBackendTexture GrGpu::createTestingOnlyBackendTexture(void* pixels, int w, int h, + SkColorType colorType, bool isRenderTarget, + GrMipMapped mipMapped) { + GrPixelConfig config = SkImageInfo2GrPixelConfig(colorType, nullptr, *this->caps()); + if (kUnknown_GrPixelConfig == config) { + return GrBackendTexture(); + } + return this->createTestingOnlyBackendTexture(pixels, w, h, config, isRenderTarget, mipMapped); +} + #if GR_CACHE_STATS void GrResourceCache::getStats(Stats* stats) const { stats->reset(); @@ -298,6 +309,16 @@ void GrDrawingManager::testingOnly_removeOnFlushCallbackObject(GrOnFlushCallback ////////////////////////////////////////////////////////////////////////////// +GrPixelConfig GrBackendTexture::testingOnly_getPixelConfig() const { + return fConfig; +} + +GrPixelConfig GrBackendRenderTarget::testingOnly_getPixelConfig() const { + return fConfig; +} + +////////////////////////////////////////////////////////////////////////////// + #define DRAW_OP_TEST_EXTERN(Op) \ extern std::unique_ptr<GrDrawOp> Op##__Test(GrPaint&&, SkRandom*, GrContext*, GrFSAAType) #define DRAW_OP_TEST_ENTRY(Op) Op##__Test diff --git a/chromium/third_party/skia/tools/gpu/gl/debug/DebugGLTestContext.cpp b/chromium/third_party/skia/tools/gpu/gl/debug/DebugGLTestContext.cpp index 4152997b4d9..2b056933661 100644 --- a/chromium/third_party/skia/tools/gpu/gl/debug/DebugGLTestContext.cpp +++ b/chromium/third_party/skia/tools/gpu/gl/debug/DebugGLTestContext.cpp @@ -102,6 +102,12 @@ public: this->setTexture(texture); } + GrGLboolean isTexture(GrGLuint textureID) override { + GrTextureObj *texture = FIND(textureID, GrTextureObj, kTexture_ObjTypes); + + return texture ? GR_GL_TRUE : GR_GL_FALSE; + } + //////////////////////////////////////////////////////////////////////////////// GrGLvoid bufferData(GrGLenum target, GrGLsizeiptr size, const GrGLvoid* data, GrGLenum usage) override { diff --git a/chromium/third_party/skia/tools/gpu/vk/GrVulkanDefines.h b/chromium/third_party/skia/tools/gpu/vk/GrVulkanDefines.h index 192f1c5abce..2eb49208fa8 100644 --- a/chromium/third_party/skia/tools/gpu/vk/GrVulkanDefines.h +++ b/chromium/third_party/skia/tools/gpu/vk/GrVulkanDefines.h @@ -8,7 +8,7 @@ #ifndef GrVulkanDefines_DEFINED #define GrVulkanDefines_DEFINED -#if defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_WIN32) +#if defined(SK_BUILD_FOR_WIN) # if !defined(VK_USE_PLATFORM_WIN32_KHR) # define VK_USE_PLATFORM_WIN32_KHR # endif diff --git a/chromium/third_party/skia/tools/install_dependencies.sh b/chromium/third_party/skia/tools/install_dependencies.sh index 9d1a45fa157..0cf879e43e4 100755 --- a/chromium/third_party/skia/tools/install_dependencies.sh +++ b/chromium/third_party/skia/tools/install_dependencies.sh @@ -23,15 +23,16 @@ dpkg_all_installed() { if command -v lsb_release > /dev/null ; then case $(lsb_release -i -s) in - Ubuntu) + Ubuntu|Debian) PACKAGES=$(cat<<-EOF build-essential freeglut3-dev libfontconfig-dev libfreetype6-dev libgif-dev + libgl1-mesa-dev libglu1-mesa-dev - libpng12-dev + libpng-dev libqt4-dev EOF ) diff --git a/chromium/third_party/skia/tools/list_gms.cpp b/chromium/third_party/skia/tools/list_gms.cpp new file mode 100644 index 00000000000..dde7f471b6f --- /dev/null +++ b/chromium/third_party/skia/tools/list_gms.cpp @@ -0,0 +1,25 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <algorithm> +#include <iostream> +#include <string> +#include <vector> + +#include "gm.h" + +int main() { + std::vector<std::string> gms; + for (const skiagm::GMRegistry* r = skiagm::GMRegistry::Head(); r; r = r->next()) { + std::unique_ptr<skiagm::GM> gm(r->factory()(nullptr)); + gms.push_back(std::string(gm->getName())); + } + std::sort(gms.begin(), gms.end()); + for (const std::string& gm : gms) { + std::cout << gm << '\n'; + } +} diff --git a/chromium/third_party/skia/tools/list_gpu_unit_tests.cpp b/chromium/third_party/skia/tools/list_gpu_unit_tests.cpp new file mode 100644 index 00000000000..18127b6950a --- /dev/null +++ b/chromium/third_party/skia/tools/list_gpu_unit_tests.cpp @@ -0,0 +1,27 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <algorithm> +#include <iostream> +#include <string> +#include <vector> + +#include "Test.h" + +int main() { + std::vector<std::string> tests; + for (const skiatest::TestRegistry* r = skiatest::TestRegistry::Head(); r; r = r->next()) { + const skiatest::Test& test = r->factory(); + if (test.needsGpu) { + tests.push_back(std::string(test.name)); + } + } + std::sort(tests.begin(), tests.end()); + for (const std::string& test : tests) { + std::cout << test << '\n'; + } +} diff --git a/chromium/third_party/skia/tools/ok.cpp b/chromium/third_party/skia/tools/ok.cpp deleted file mode 100644 index 29c2a224f1e..00000000000 --- a/chromium/third_party/skia/tools/ok.cpp +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -// ok is an experimental test harness, maybe to replace DM. Key features: -// * work is balanced across separate processes for stability and isolation; -// * ok is entirely opt-in. No more maintaining huge --blacklists. - -#include "SkGraphics.h" -#include "SkImage.h" -#include "ok.h" -#include <chrono> -#include <list> -#include <stdio.h> -#include <stdlib.h> -#include <vector> - -#if !defined(__has_include) - #define __has_include(x) 0 -#endif - -static thread_local const char* tls_currently_running = ""; - -#if __has_include(<execinfo.h>) - #include <execinfo.h> - - #define CAN_BACKTRACE - static void backtrace(int fd) { - void* stack[128]; - int frames = backtrace(stack, sizeof(stack)/sizeof(*stack)); - backtrace_symbols_fd(stack, frames, fd); - } - -#elif __has_include(<dlfcn.h>) && __has_include(<unwind.h>) - #include <cxxabi.h> - #include <dlfcn.h> - #include <unwind.h> - - #define CAN_BACKTRACE - static void backtrace(int fd) { - FILE* file = fdopen(fd, "a"); - _Unwind_Backtrace([](_Unwind_Context* ctx, void* arg) { - auto file = (FILE*)arg; - if (auto ip = (void*)_Unwind_GetIP(ctx)) { - const char* name = "[unknown]"; - void* addr = nullptr; - Dl_info info; - if (dladdr(ip, &info) && info.dli_sname && info.dli_saddr) { - name = info.dli_sname; - addr = info.dli_saddr; - } - - int ok; - char* demangled = abi::__cxa_demangle(name, nullptr,0, &ok); - if (ok == 0 && demangled) { - name = demangled; - } - - fprintf(file, "\t%p %s+%zu\n", ip, name, (size_t)ip - (size_t)addr); - free(demangled); - } - return _URC_NO_REASON; - }, file); - fflush(file); - } -#endif - -#if defined(CAN_BACKTRACE) && __has_include(<fcntl.h>) && __has_include(<signal.h>) - #include <fcntl.h> - #include <signal.h> - - // We'd ordinarily just use lockf(), but fcntl() is more portable to older Android NDK APIs. - static void lock_or_unlock_fd(int fd, short type) { - struct flock fl{}; - fl.l_type = type; - fl.l_whence = SEEK_CUR; - fl.l_start = 0; - fl.l_len = 0; // 0 == the entire file - fcntl(fd, F_SETLKW, &fl); - } - static void lock_fd(int fd) { lock_or_unlock_fd(fd, F_WRLCK); } - static void unlock_fd(int fd) { lock_or_unlock_fd(fd, F_UNLCK); } - - static int log_fd = 2/*stderr*/; - - static void log(const char* msg) { - write(log_fd, msg, strlen(msg)); - } - - static void setup_crash_handler() { - static void (*original_handlers[32])(int); - for (int sig : std::vector<int>{ SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV }) { - original_handlers[sig] = signal(sig, [](int sig) { - lock_fd(log_fd); - log("\ncaught signal "); - switch (sig) { - #define CASE(s) case s: log(#s); break - CASE(SIGABRT); - CASE(SIGBUS); - CASE(SIGFPE); - CASE(SIGILL); - CASE(SIGSEGV); - #undef CASE - } - log(" while running '"); - log(tls_currently_running); - log("'\n"); - backtrace(log_fd); - unlock_fd(log_fd); - - signal(sig, original_handlers[sig]); - raise(sig); - }); - } - } - - static void defer_logging() { - log_fd = fileno(tmpfile()); - atexit([] { - lseek(log_fd, 0, SEEK_SET); - char buf[1024]; - while (size_t bytes = read(log_fd, buf, sizeof(buf))) { - write(2, buf, bytes); - } - }); - } - - void ok_log(const char* msg) { - lock_fd(log_fd); - log("["); - log(tls_currently_running); - log("]\t"); - log(msg); - log("\n"); - unlock_fd(log_fd); - } - -#else - static void setup_crash_handler() {} - static void defer_logging() {} - - void ok_log(const char* msg) { - fprintf(stderr, "[%s]\t%s\n", tls_currently_running, msg); - } -#endif - -struct EngineType { - const char *name, *help; - std::unique_ptr<Engine> (*factory)(Options); -}; -static std::vector<EngineType> engine_types; - -struct StreamType { - const char *name, *help; - std::unique_ptr<Stream> (*factory)(Options); -}; -static std::vector<StreamType> stream_types; - -struct DstType { - const char *name, *help; - std::unique_ptr<Dst> (*factory)(Options); -}; -static std::vector<DstType> dst_types; - -struct ViaType { - const char *name, *help; - std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>); -}; -static std::vector<ViaType> via_types; - -template <typename T> -static std::string help_for(std::vector<T> registered) { - std::string help; - for (auto r : registered) { - help += "\n "; - help += r.name; - help += ": "; - help += r.help; - } - return help; -} - -int main(int argc, char** argv) { - SkGraphics::Init(); - setup_crash_handler(); - - std::unique_ptr<Engine> engine; - std::unique_ptr<Stream> stream; - std::function<std::unique_ptr<Dst>(void)> dst_factory = []{ - // A default Dst that's enough for unit tests and not much else. - struct : Dst { - Status draw(Src* src) override { return src->draw(nullptr); } - sk_sp<SkImage> image() override { return nullptr; } - } dst; - return move_unique(dst); - }; - - auto help = [&] { - std::string engine_help = help_for(engine_types), - stream_help = help_for(stream_types), - dst_help = help_for( dst_types), - via_help = help_for( via_types); - - printf("%s [engine] src[:k=v,...] dst[:k=v,...] [via[:k=v,...] ...] \n" - " engine: how to execute tasks%s \n" - " src: content to draw%s \n" - " dst: how to draw that content%s \n" - " via: wrappers around dst%s \n" - " Most srcs, dsts and vias have options, e.g. skp:dir=skps sw:ct=565 \n", - argv[0], - engine_help.c_str(), stream_help.c_str(), dst_help.c_str(), via_help.c_str()); - return 1; - }; - - for (int i = 1; i < argc; i++) { - if (0 == strcmp("-h", argv[i])) { return help(); } - if (0 == strcmp("--help", argv[i])) { return help(); } - - for (auto e : engine_types) { - size_t len = strlen(e.name); - if (0 == strncmp(e.name, argv[i], len)) { - switch (argv[i][len]) { - case ':': len++; - case '\0': engine = e.factory(Options{argv[i]+len}); - } - } - } - - for (auto s : stream_types) { - size_t len = strlen(s.name); - if (0 == strncmp(s.name, argv[i], len)) { - switch (argv[i][len]) { - case ':': len++; - case '\0': stream = s.factory(Options{argv[i]+len}); - } - } - } - for (auto d : dst_types) { - size_t len = strlen(d.name); - if (0 == strncmp(d.name, argv[i], len)) { - switch (argv[i][len]) { - case ':': len++; - case '\0': dst_factory = [=]{ - return d.factory(Options{argv[i]+len}); - }; - } - } - } - for (auto v : via_types) { - size_t len = strlen(v.name); - if (0 == strncmp(v.name, argv[i], len)) { - if (!dst_factory) { return help(); } - switch (argv[i][len]) { - case ':': len++; - case '\0': dst_factory = [=]{ - return v.factory(Options{argv[i]+len}, dst_factory()); - }; - } - } - } - } - if (!stream) { return help(); } - - if (!engine) { engine = engine_types.back().factory(Options{}); } - - // If we know engine->spawn() will never crash, we can defer logging until we exit. - if (engine->crashproof()) { - defer_logging(); - } - - int ok = 0, failed = 0, crashed = 0, skipped = 0; - - auto update_stats = [&](Status s) { - switch (s) { - case Status::OK: ok++; break; - case Status::Failed: failed++; break; - case Status::Crashed: crashed++; break; - case Status::Skipped: skipped++; break; - case Status::None: return; - } - const char* leader = "\r"; - auto print = [&](int count, const char* label) { - if (count) { - printf("%s%d %s", leader, count, label); - leader = ", "; - } - }; - print(ok, "ok"); - print(failed, "failed"); - print(crashed, "crashed"); - print(skipped, "skipped"); - fflush(stdout); - }; - - std::list<std::future<Status>> live; - const auto the_past = std::chrono::steady_clock::now(); - - auto wait_one = [&] { - if (live.empty()) { - return Status::None; - } - - for (;;) { - for (auto it = live.begin(); it != live.end(); it++) { - if (it->wait_until(the_past) != std::future_status::timeout) { - Status s = it->get(); - live.erase(it); - return s; - } - } - } - }; - - auto spawn = [&](std::function<Status(void)> fn) { - std::future<Status> status; - for (;;) { - status = engine->spawn(fn); - if (status.valid()) { - break; - } - update_stats(wait_one()); - } - live.push_back(std::move(status)); - }; - - for (std::unique_ptr<Src> owned = stream->next(); owned; owned = stream->next()) { - Src* raw = owned.release(); // Can't move std::unique_ptr into a lambda in C++11. :( - spawn([=] { - std::unique_ptr<Src> src{raw}; - - std::string name = src->name(); - tls_currently_running = name.c_str(); - - return dst_factory()->draw(src.get()); - }); - } - - for (Status s = Status::OK; s != Status::None; ) { - s = wait_one(); - update_stats(s); - } - printf("\n"); - return (failed || crashed) ? 1 : 0; -} - - -Register::Register(const char* name, const char* help, - std::unique_ptr<Engine> (*factory)(Options)) { - engine_types.push_back(EngineType{name, help, factory}); -} -Register::Register(const char* name, const char* help, - std::unique_ptr<Stream> (*factory)(Options)) { - stream_types.push_back(StreamType{name, help, factory}); -} -Register::Register(const char* name, const char* help, - std::unique_ptr<Dst> (*factory)(Options)) { - dst_types.push_back(DstType{name, help, factory}); -} -Register::Register(const char* name, const char* help, - std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>)) { - via_types.push_back(ViaType{name, help, factory}); -} - -Options::Options(std::string str) { - std::string k,v, *curr = &k; - for (auto c : str) { - switch(c) { - case ',': (*this)[k] = v; - curr = &(k = ""); - break; - case '=': curr = &(v = ""); - break; - default: *curr += c; - } - } - (*this)[k] = v; -} - -std::string& Options::operator[](std::string k) { return this->kv[k]; } - -std::string Options::operator()(std::string k, std::string fallback) const { - for (auto it = kv.find(k); it != kv.end(); ) { - return it->second; - } - return fallback; -} diff --git a/chromium/third_party/skia/tools/ok.h b/chromium/third_party/skia/tools/ok.h deleted file mode 100644 index 502df23b4aa..00000000000 --- a/chromium/third_party/skia/tools/ok.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef ok_DEFINED -#define ok_DEFINED - -#include "SkCanvas.h" -#include <functional> -#include <future> -#include <map> -#include <memory> -#include <string> - -// Not really ok-specific, but just kind of generally handy. -template <typename T> -static std::unique_ptr<T> move_unique(T& v) { - return std::unique_ptr<T>{new T{std::move(v)}}; -} - -void ok_log(const char*); - -enum class Status { OK, Failed, Crashed, Skipped, None }; - -struct Engine { - virtual ~Engine() {} - virtual bool crashproof() = 0; - virtual std::future<Status> spawn(std::function<Status(void)>) = 0; -}; - -struct Src { - virtual ~Src() {} - virtual std::string name() = 0; - virtual SkISize size() = 0; - virtual Status draw(SkCanvas*) = 0; -}; - -struct Stream { - virtual ~Stream() {} - virtual std::unique_ptr<Src> next() = 0; -}; - -struct Dst { - virtual ~Dst() {} - virtual Status draw(Src*) = 0; - virtual sk_sp<SkImage> image() = 0; -}; - -class Options { - std::map<std::string, std::string> kv; -public: - explicit Options(std::string = ""); - std::string& operator[](std::string k); - std::string operator()(std::string k, std::string fallback = "") const; -}; - -// Create globals to register your new type of Stream or Dst. -struct Register { - Register(const char* name, const char* help, std::unique_ptr<Engine> (*factory)(Options)); - Register(const char* name, const char* help, std::unique_ptr<Stream> (*factory)(Options)); - Register(const char* name, const char* help, std::unique_ptr<Dst> (*factory)(Options)); - Register(const char* name, const char* help, - std::unique_ptr<Dst>(*factory)(Options, std::unique_ptr<Dst>)); -}; - -#endif//ok_DEFINED diff --git a/chromium/third_party/skia/tools/ok_dsts.cpp b/chromium/third_party/skia/tools/ok_dsts.cpp deleted file mode 100644 index 1f4fe42a4b6..00000000000 --- a/chromium/third_party/skia/tools/ok_dsts.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "ok.h" -#include "SkSurface.h" - -struct SWDst : Dst { - SkImageInfo info; - sk_sp<SkSurface> surface; - - static std::unique_ptr<Dst> Create(Options options) { - SkImageInfo info = SkImageInfo::MakeN32Premul(0,0); - if (options("ct") == "a8") { info = info.makeColorType(kAlpha_8_SkColorType); } - if (options("ct") == "565") { info = info.makeColorType(kRGB_565_SkColorType); } - if (options("ct") == "f16") { info = info.makeColorType(kRGBA_F16_SkColorType); } - - if (options("cs") == "srgb") { - auto cs = info.colorType() == kRGBA_F16_SkColorType ? SkColorSpace::MakeSRGBLinear() - : SkColorSpace::MakeSRGB(); - info = info.makeColorSpace(std::move(cs)); - } - - SWDst dst; - dst.info = info; - return move_unique(dst); - } - - Status draw(Src* src) override { - auto size = src->size(); - surface = SkSurface::MakeRaster(info.makeWH(size.width(), size.height())); - return src->draw(surface->getCanvas()); - } - - sk_sp<SkImage> image() override { - return surface->makeImageSnapshot(); - } -}; -static Register sw{"sw", "draw with the software backend", SWDst::Create}; -static Register _8888{"8888", "alias for sw", SWDst::Create}; - -static Register a8{"a8", "alias for sw:ct=a8", [](Options options) { - options["ct"] = "a8"; - return SWDst::Create(options); -}}; - -static Register _565{"565", "alias for sw:ct=565", [](Options options) { - options["ct"] = "565"; - return SWDst::Create(options); -}}; - -static Register srgb{"srgb", "alias for sw:cs=srgb", [](Options options) { - options["cs"] = "srgb"; - return SWDst::Create(options); -}}; - -static Register f16{"f16", "alias for sw:ct=f16,cs=srgb", [](Options options) { - options["ct"] = "f16"; - options["cs"] = "srgb"; - return SWDst::Create(options); -}}; - -extern bool gSkForceRasterPipelineBlitter; -static Register rp{"rp", "draw forcing SkRasterPipelineBlitter", [](Options options) { - gSkForceRasterPipelineBlitter = true; - return SWDst::Create(options); -}}; diff --git a/chromium/third_party/skia/tools/ok_engines.cpp b/chromium/third_party/skia/tools/ok_engines.cpp deleted file mode 100644 index 3304f3e22ca..00000000000 --- a/chromium/third_party/skia/tools/ok_engines.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "ok.h" -#include <stdlib.h> - -struct SerialEngine : Engine { - static std::unique_ptr<Engine> Factory(Options) { - SerialEngine engine; - return move_unique(engine); - } - - bool crashproof() override { return false; } - - std::future<Status> spawn(std::function<Status(void)> fn) override { - return std::async(std::launch::deferred, fn); - } -}; -static Register serial("serial", - "Run tasks serially on the main thread of a single process.", - SerialEngine::Factory); - -struct ThreadEngine : Engine { - static std::unique_ptr<Engine> Factory(Options) { - ThreadEngine engine; - return move_unique(engine); - } - - bool crashproof() override { return false; } - - std::future<Status> spawn(std::function<Status(void)> fn) override { - return std::async(std::launch::async, fn); - } -}; -static Register thread("thread", - "Run each task on its own thread of a single process.", - ThreadEngine::Factory); - -#if !defined(_MSC_VER) - #include <sys/wait.h> - #include <unistd.h> - - struct ForkEngine : Engine { - int limit; // How many concurrent subprocesses do we allow to run at max? - int alive = 0; // How many concurrent subprocesses do we have running right now? - - static std::unique_ptr<Engine> Factory(Options options) { - ForkEngine engine; - engine.limit = atoi(options("limit", "0").c_str()); - if (engine.limit < 1) { - engine.limit = std::thread::hardware_concurrency(); - } - return move_unique(engine); - } - - bool crashproof() override { return true; } - - std::future<Status> spawn(std::function<Status(void)> fn) override { - if (alive == limit) { - // The caller will wait for a child process to finish then try again. - return std::future<Status>(); - } - - switch (fork()) { - case 0: - // We are the spawned child process. - // Run fn() and exit() with its Status as our return code. - _exit((int)fn()); - - case -1: - // The OS won't let us fork() another process right now. - // We'll need to wait for at least one live task to finish and try again. - return std::future<Status>(); - - default: - // We succesfully spawned a child process! - // This will wait for any spawned process to finish and return its Status. - alive++; - return std::async(std::launch::deferred, [&] { - do { - int status; - if (wait(&status) > 0) { - alive--; - return WIFEXITED(status) ? (Status)WEXITSTATUS(status) - : Status::Crashed; - } - } while (errno == EINTR); - return Status::None; - }); - } - } - }; - static Register _fork("fork", - "Run each task in an independent process with fork(), limit=ncpus.", - ForkEngine::Factory); -#endif diff --git a/chromium/third_party/skia/tools/ok_srcs.cpp b/chromium/third_party/skia/tools/ok_srcs.cpp deleted file mode 100644 index b909be8a293..00000000000 --- a/chromium/third_party/skia/tools/ok_srcs.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Benchmark.h" -#include "SkData.h" -#include "SkOSFile.h" -#include "SkPicture.h" -#include "Timer.h" -#include "gm.h" -#include "ok.h" -#include <algorithm> -#include <chrono> -#include <limits> -#include <stdlib.h> -#include <vector> - -struct GMStream : Stream { - const skiagm::GMRegistry* registry = skiagm::GMRegistry::Head(); - - static std::unique_ptr<Stream> Create(Options) { - GMStream stream; - return move_unique(stream); - } - - struct GMSrc : Src { - skiagm::GM* (*factory)(void*); - std::unique_ptr<skiagm::GM> gm; - - void init() { - if (gm) { return; } - gm.reset(factory(nullptr)); - } - - std::string name() override { - this->init(); - return gm->getName(); - } - - SkISize size() override { - this->init(); - return gm->getISize(); - } - - Status draw(SkCanvas* canvas) override { - this->init(); - canvas->clear(0xffffffff); - gm->draw(canvas); - return Status::OK; - } - }; - - std::unique_ptr<Src> next() override { - if (!registry) { - return nullptr; - } - GMSrc src; - src.factory = registry->factory(); - registry = registry->next(); - return move_unique(src); - } -}; -static Register gm{"gm", "draw GMs linked into this binary", GMStream::Create}; - -struct SKPStream : Stream { - std::string dir; - std::vector<std::string> skps; - - static std::unique_ptr<Stream> Create(Options options) { - SKPStream stream; - stream.dir = options("dir", "skps"); - SkOSFile::Iter it{stream.dir.c_str(), ".skp"}; - for (SkString path; it.next(&path); ) { - stream.skps.push_back(path.c_str()); - } - return move_unique(stream); - } - - struct SKPSrc : Src { - std::string dir, path; - sk_sp<SkPicture> pic; - - void init() { - if (pic) { return; } - auto skp = SkData::MakeFromFileName((dir+"/"+path).c_str()); - pic = SkPicture::MakeFromData(skp.get()); - } - - std::string name() override { - return path; - } - - SkISize size() override { - this->init(); - return pic->cullRect().roundOut().size(); - } - - Status draw(SkCanvas* canvas) override { - this->init(); - canvas->clear(0xffffffff); - pic->playback(canvas); - return Status::OK; - } - }; - - std::unique_ptr<Src> next() override { - if (skps.empty()) { - return nullptr; - } - SKPSrc src; - src.dir = dir; - src.path = skps.back(); - skps.pop_back(); - return move_unique(src); - } -}; -static Register skp{"skp", "draw SKPs from dir=skps", SKPStream::Create}; - -struct BenchStream : Stream { - const BenchRegistry* registry = BenchRegistry::Head(); - int samples; - - static std::unique_ptr<Stream> Create(Options options) { - BenchStream stream; - stream.samples = std::max(1, atoi(options("samples", "20").c_str())); - return move_unique(stream); - } - - struct BenchSrc : Src { - Benchmark* (*factory)(void*); - std::unique_ptr<Benchmark> bench; - int samples; - - void init() { - if (bench) { return; } - bench.reset(factory(nullptr)); - } - - std::string name() override { - this->init(); - return bench->getName(); - } - - SkISize size() override { - this->init(); - return { bench->getSize().x(), bench->getSize().y() }; - } - - Status draw(SkCanvas* canvas) override { - this->init(); - - using ms = std::chrono::duration<double, std::milli>; - std::vector<ms> sample(samples); - - bench->delayedSetup(); - if (canvas) { - bench->perCanvasPreDraw(canvas); - } - for (int i = 0; i < samples; i++) { - using clock = std::chrono::high_resolution_clock; - for (int loops = 1; loops < 1000000000; loops *= 2) { - bench->preDraw(canvas); - auto start = clock::now(); - bench->draw(loops, canvas); - ms elapsed = clock::now() - start; - bench->postDraw(canvas); - - if (elapsed.count() < 10) { - continue; - } - - sample[i] = elapsed / loops; - break; - } - } - if (canvas) { - bench->perCanvasPostDraw(canvas); - } - - std::sort(sample.begin(), sample.end()); - - SkString msg = SkStringPrintf("%s\t@0", HumanizeMs(sample[0].count()).c_str()); - if (samples > 2) { - msg.appendf("\t%s\t@%g", HumanizeMs(sample[samples-2].count()).c_str() - , 100.0*(samples-1) / samples); - } - if (samples > 1) { - msg.appendf("\t%s\t@100", HumanizeMs(sample[samples-1].count()).c_str()); - } - ok_log(msg.c_str()); - - return Status::OK; - } - }; - - std::unique_ptr<Src> next() override { - if (!registry) { - return nullptr; - } - BenchSrc src; - src.factory = registry->factory(); - src.samples = samples; - registry = registry->next(); - return move_unique(src); - } -}; -static Register bench{ - "bench", - "time benchmarks linked into this binary samples=20 times each", - BenchStream::Create, -}; diff --git a/chromium/third_party/skia/tools/ok_test.cpp b/chromium/third_party/skia/tools/ok_test.cpp deleted file mode 100644 index 232904209d4..00000000000 --- a/chromium/third_party/skia/tools/ok_test.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "ok.h" -#include "Test.h" - -#if SK_SUPPORT_GPU - #include "GrContextFactory.h" -#else -struct GrContextOptions {}; -#endif - -struct TestStream : Stream { - const skiatest::TestRegistry* registry = skiatest::TestRegistry::Head(); - bool extended = false, verbose = false; - - static std::unique_ptr<Stream> Create(Options options) { - TestStream stream; - if (options("extended") != "") { stream.extended = true; } - if (options("verbose" ) != "") { stream.verbose = true; } - - return move_unique(stream); - } - - struct TestSrc : Src { - skiatest::Test test {"", false, nullptr}; - bool extended, verbose; - - std::string name() override { return test.name; } - SkISize size() override { return {0,0}; } - - Status draw(SkCanvas*) override { - struct : public skiatest::Reporter { - Status status = Status::OK; - bool extended, verbose_; - - void reportFailed(const skiatest::Failure& failure) override { - ok_log(failure.toString().c_str()); - status = Status::Failed; - } - bool allowExtendedTest() const override { return extended; } - bool verbose() const override { return verbose_; } - } reporter; - reporter.extended = extended; - reporter.verbose_ = verbose; - - GrContextOptions options; - test.run(&reporter, options); - return reporter.status; - } - }; - - std::unique_ptr<Src> next() override { - if (!registry) { - return nullptr; - } - TestSrc src; - src.test = registry->factory(); - src.extended = extended; - src.verbose = verbose; - registry = registry->next(); - return move_unique(src); - } -}; -static Register test{"test", "run unit tests linked into this binary", TestStream::Create}; - -// Hey, now why were these defined in DM.cpp? That's kind of weird. -namespace skiatest { -#if SK_SUPPORT_GPU - bool IsGLContextType(sk_gpu_test::GrContextFactory::ContextType type) { - return kOpenGL_GrBackend == sk_gpu_test::GrContextFactory::ContextTypeBackend(type); - } - bool IsVulkanContextType(sk_gpu_test::GrContextFactory::ContextType type) { - return kVulkan_GrBackend == sk_gpu_test::GrContextFactory::ContextTypeBackend(type); - } - bool IsRenderingGLContextType(sk_gpu_test::GrContextFactory::ContextType type) { - return IsGLContextType(type) && sk_gpu_test::GrContextFactory::IsRenderingContext(type); - } - bool IsNullGLContextType(sk_gpu_test::GrContextFactory::ContextType type) { - return type == sk_gpu_test::GrContextFactory::kNullGL_ContextType; - } -#else - bool IsGLContextType (int) { return false; } - bool IsVulkanContextType (int) { return false; } - bool IsRenderingGLContextType(int) { return false; } - bool IsNullGLContextType (int) { return false; } -#endif - - void RunWithGPUTestContexts(GrContextTestFn* test, GrContextTypeFilterFn* contextTypeFilter, - Reporter* reporter, const GrContextOptions& options) { - // TODO(bsalomon) - } -} diff --git a/chromium/third_party/skia/tools/ok_vias.cpp b/chromium/third_party/skia/tools/ok_vias.cpp deleted file mode 100644 index c1f8d9365e7..00000000000 --- a/chromium/third_party/skia/tools/ok_vias.cpp +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "../dm/DMFontMgr.h" -#include "../src/core/SkFontMgrPriv.h" -#include "ProcStats.h" -#include "SkColorFilter.h" -#include "SkEventTracingPriv.h" -#include "SkImage.h" -#include "SkOSFile.h" -#include "SkPictureRecorder.h" -#include "SkPngEncoder.h" -#include "SkTraceEvent.h" -#include "SkTypeface.h" -#include "Timer.h" -#include "ok.h" -#include "sk_tool_utils.h" -#include <chrono> -#include <regex> - -static std::unique_ptr<Src> proxy(Src* original, std::function<Status(SkCanvas*)> fn) { - struct : Src { - Src* original; - std::function<Status(SkCanvas*)> fn; - - std::string name() override { return original->name(); } - SkISize size() override { return original->size(); } - Status draw(SkCanvas* canvas) override { return fn(canvas); } - } src; - src.original = original; - src.fn = fn; - return move_unique(src); -} - -struct ViaPic : Dst { - std::unique_ptr<Dst> target; - bool rtree = false; - - static std::unique_ptr<Dst> Create(Options options, std::unique_ptr<Dst> dst) { - ViaPic via; - via.target = std::move(dst); - if (options("bbh") == "rtree") { via.rtree = true; } - return move_unique(via); - } - - Status draw(Src* src) override { - TRACE_EVENT0("ok", TRACE_FUNC); - SkRTreeFactory factory; - SkPictureRecorder rec; - rec.beginRecording(SkRect::MakeSize(SkSize::Make(src->size())), - rtree ? &factory : nullptr); - - for (Status status = src->draw(rec.getRecordingCanvas()); status != Status::OK; ) { - return status; - } - sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(); - - return target->draw(proxy(src, [=](SkCanvas* canvas) { - pic->playback(canvas); - return Status::OK; - }).get()); - } - - sk_sp<SkImage> image() override { - return target->image(); - } -}; -static Register via_pic{"via_pic", "record then play back an SkPicture", ViaPic::Create}; - -// When deserializing, we need to hook this to intercept "Toy Liberation ..." -// typefaces and return our portable test typeface. -extern sk_sp<SkTypeface> (*gCreateTypefaceDelegate)(const char[], SkFontStyle); - -struct ViaSkp : Dst { - std::unique_ptr<Dst> target; - bool rtree = false; - - static std::unique_ptr<Dst> Create(Options options, std::unique_ptr<Dst> dst) { - gCreateTypefaceDelegate = [](const char name[], SkFontStyle style) -> sk_sp<SkTypeface> { - if (name == strstr(name, "Toy Liberation")) { - return sk_tool_utils::create_portable_typeface(name, style); - } - return nullptr; - }; - - ViaSkp via; - via.target = std::move(dst); - if (options("bbh") == "rtree") { via.rtree = true; } - return move_unique(via); - } - - Status draw(Src* src) override { - TRACE_EVENT0("ok", TRACE_FUNC); - SkRTreeFactory factory; - SkPictureRecorder rec; - rec.beginRecording(SkRect::MakeSize(SkSize::Make(src->size())), - rtree ? &factory : nullptr); - - for (Status status = src->draw(rec.getRecordingCanvas()); status != Status::OK; ) { - return status; - } - sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(); - - // Serialize and deserialize. - pic = SkPicture::MakeFromData(pic->serialize().get()); - - return target->draw(proxy(src, [=](SkCanvas* canvas) { - pic->playback(canvas); - return Status::OK; - }).get()); - } - - sk_sp<SkImage> image() override { - return target->image(); - } -}; -static Register via_skp{"via_skp", "serialize and deserialize an .skp", ViaSkp::Create}; - -struct Png : Dst { - std::unique_ptr<Dst> target; - std::string dir; - - static std::unique_ptr<Dst> Create(Options options, std::unique_ptr<Dst> dst) { - Png via; - via.target = std::move(dst); - via.dir = options("dir", "ok"); - return move_unique(via); - } - - Status draw(Src* src) override { - TRACE_EVENT0("ok", TRACE_FUNC); - for (auto status = target->draw(src); status != Status::OK; ) { - return status; - } - - SkBitmap bm; - if (!target->image()->asLegacyBitmap(&bm, SkImage::kRO_LegacyBitmapMode)) { - return Status::Failed; - } - - // SkPngEncoder can't encode A8 .pngs, and even if it could, they'd be a pain to look at. - if (bm.colorType() == kAlpha_8_SkColorType) { - SkPaint paint; - SkScalar alpha_to_opaque_gray[20] = { - 0,0,0,1, 0, // red = alpha - 0,0,0,1, 0, // green = alpha - 0,0,0,1, 0, // blue = alpha - 0,0,0,0,255, // alpha = 255 - }; - paint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(alpha_to_opaque_gray)); - paint.setBlendMode(SkBlendMode::kSrc); - - SkBitmap dst; - dst.allocN32Pixels(bm.width(), bm.height(), /*isOpaque=*/true); - SkCanvas canvas(dst); - canvas.drawBitmap(bm, 0,0, &paint); - - bm = dst; - } - - SkPixmap pm; - if (!bm.peekPixels(&pm)) { - return Status::Failed; - } - - sk_mkdir(dir.c_str()); - SkFILEWStream dst{(dir + "/" + src->name() + ".png").c_str()}; - - SkPngEncoder::Options options; - options.fFilterFlags = SkPngEncoder::FilterFlag::kNone; - options.fZLibLevel = 1; - options.fUnpremulBehavior = pm.colorSpace() ? SkTransferFunctionBehavior::kRespect - : SkTransferFunctionBehavior::kIgnore; - return SkPngEncoder::Encode(&dst, pm, options) ? Status::OK - : Status::Failed; - } - - sk_sp<SkImage> image() override { - return target->image(); - } -}; -static Register png{"png", "dump PNGs to dir=ok" , Png::Create}; - -struct Filter : Dst { - std::unique_ptr<Dst> target; - std::regex match, search; - - static std::unique_ptr<Dst> Create(Options options, std::unique_ptr<Dst> dst) { - Filter via; - via.target = std::move(dst); - via.match = options("match", ".*"); - via.search = options("search", ".*"); - return move_unique(via); - } - - Status draw(Src* src) override { - auto name = src->name(); - if (!std::regex_match (name, match) || - !std::regex_search(name, search)) { - return Status::Skipped; - } - return target->draw(src); - } - - sk_sp<SkImage> image() override { - return target->image(); - } -}; -static Register filter{"filter", - "run only srcs matching match=.* exactly and search=.* somewhere", - Filter::Create}; - -struct Time : Dst { - std::unique_ptr<Dst> target; - - static std::unique_ptr<Dst> Create(Options options, std::unique_ptr<Dst> dst) { - Time via; - via.target = std::move(dst); - return move_unique(via); - } - - Status draw(Src* src) override { - auto start = std::chrono::steady_clock::now(); - Status status = target->draw(src); - std::chrono::duration<double, std::milli> elapsed = std::chrono::steady_clock::now() - - start; - - if (status != Status::Skipped) { - auto msg = HumanizeMs(elapsed.count()); - ok_log(msg.c_str()); - } - return status; - } - - sk_sp<SkImage> image() override { - return target->image(); - } -}; -static Register _time{"time", "print wall run time", Time::Create}; - -struct Memory : Dst { - std::unique_ptr<Dst> target; - - static std::unique_ptr<Dst> Create(Options options, std::unique_ptr<Dst> dst) { - Memory via; - via.target = std::move(dst); - return move_unique(via); - } - - Status draw(Src* src) override { - Status status = target->draw(src); - - if (status != Status::Skipped) { - auto msg = SkStringPrintf("%dMB", sk_tools::getMaxResidentSetSizeMB()); - ok_log(msg.c_str()); - } - - return status; - } - - sk_sp<SkImage> image() override { - return target->image(); - } -}; -static Register memory{"memory", "print process maximum memory usage", Memory::Create}; - -struct Trace : Dst { - std::unique_ptr<Dst> target; - std::string trace_mode; - - static std::unique_ptr<Dst> Create(Options options, std::unique_ptr<Dst> dst) { - Trace via; - via.target = std::move(dst); - via.trace_mode = options("mode", "trace.json"); - return move_unique(via); - } - - Status draw(Src* src) override { - static SkOnce once; - once([&] { initializeEventTracingForTools(trace_mode.c_str()); }); - return target->draw(src); - } - - sk_sp<SkImage> image() override { - return target->image(); - } -}; -static Register trace{"trace", - "enable tracing in mode=atrace, mode=debugf, or mode=trace.json", - Trace::Create}; - -struct PortableFonts : Dst { - std::unique_ptr<Dst> target; - - static std::unique_ptr<Dst> Create(Options options, std::unique_ptr<Dst> dst) { - PortableFonts via; - via.target = std::move(dst); - return move_unique(via); - } - - Status draw(Src* src) override { - static SkOnce once; - once([]{ gSkFontMgr_DefaultFactory = &DM::MakeFontMgr; }); - return target->draw(src); - } - - sk_sp<SkImage> image() override { - return target->image(); - } -}; -static Register portable_fonts{"portable_fonts", - "use DM::FontMgr to make fonts more portable", - PortableFonts::Create}; diff --git a/chromium/third_party/skia/tools/pathops_sorter.htm b/chromium/third_party/skia/tools/pathops_sorter.htm index 42708c428c3..22054955125 100644 --- a/chromium/third_party/skia/tools/pathops_sorter.htm +++ b/chromium/third_party/skia/tools/pathops_sorter.htm @@ -1,4 +1,4 @@ -<!DOCTYPE html> +<!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> @@ -6,16 +6,10 @@ <title></title> <div style="height:0"> -<div id="cubics"> -{{{152, 16}, {152, 16.0685501}, {91.06044, 16.1242027}, {16, 16.1242027}}}, id=0 -{{{16, 16.1242027}, {-59.06044, 16.1242027}, {-120, 16.0685501}, {-120, 16}}}, id=1 -{{{-120, 16}, {-120, 15.9314508}, {-59.06044, 15.8757973}, {16, 15.8757973}}}, id=2 -{{{16, 15.8757973}, {91.06044, 15.8757973}, {152, 15.9314508}, {152, 16}}}, id=3 -{{{16, 16}, {152, 16}}}, id=4 -{{{16, 17}, {152, 17}}}, id=5 -{{{16, 16}, {16, 17}}}, id=6 -{{{152, 16}, {152, 17}}}, id=7 -</div> + <div id="cubics"> + {{{0.00000000000000000, 0.00000000000000000 }, {0.00022939755581319332, 0.00022927834652364254 },{0.00022930106206331402, 0.00022929999977350235 }, {0.00022930069826543331, 0.00022913678549230099}}}, + {{{0.00022930069826543331, 0.00022930069826543331 }, {0.00011465034913271666, 0.00011465034913271666 },{0.00011465061106719077, 0.00011460937093943357 }, {0.00014331332931760699, 0.00014325146912597120}}}, + </div> </div> diff --git a/chromium/third_party/skia/tools/remote_demo.cpp b/chromium/third_party/skia/tools/remote_demo.cpp new file mode 100644 index 00000000000..53aa6917eb0 --- /dev/null +++ b/chromium/third_party/skia/tools/remote_demo.cpp @@ -0,0 +1,396 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCanvas.h" +#include "SkPathEffect.h" +#include "SkMaskFilter.h" +#include "SkData.h" +#include "SkDescriptor.h" +#include "SkGraphics.h" +#include "SkSemaphore.h" +#include "SkPictureRecorder.h" +#include "SkSerialProcs.h" +#include "SkSurface.h" +#include "SkTypeface.h" +#include "SkWriteBuffer.h" + +#include <chrono> +#include <ctype.h> +#include <err.h> +#include <memory> +#include <stdio.h> +#include <thread> +#include <iostream> +#include <unordered_map> + +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <sys/mman.h> +#include "SkTypeface_remote.h" +#include "SkRemoteGlyphCache.h" +#include "SkMakeUnique.h" + +static const size_t kPageSize = 4096; + +static bool gUseGpu = true; +static bool gPurgeFontCaches = true; +static bool gUseProcess = true; + +enum class OpCode : int32_t { + kFontMetrics = 0, + kGlyphMetrics = 1, + kGlyphImage = 2, + kGlyphPath = 3, + kGlyphMetricsAndImage = 4, +}; + +class Op { +public: + Op(OpCode opCode, SkFontID typefaceId, const SkScalerContextRec& rec) + : opCode{opCode} + , typefaceId{typefaceId} + , descriptor{rec} { } + const OpCode opCode; + const SkFontID typefaceId; + const SkScalerContextRecDescriptor descriptor; + union { + // op 0 + SkPaint::FontMetrics fontMetrics; + // op 1 and 2 + SkGlyph glyph; + // op 3 + struct { + SkGlyphID glyphId; + size_t pathSize; + }; + }; +}; + +class RemoteScalerContextFIFO : public SkRemoteScalerContext { +public: + explicit RemoteScalerContextFIFO(int readFd, int writeFd) + : fReadFd{readFd} + , fWriteFd{writeFd} { } + void generateFontMetrics(const SkTypefaceProxy& tf, + const SkScalerContextRec& rec, + SkPaint::FontMetrics* metrics) override { + Op* op = this->createOp(OpCode::kFontMetrics, tf, rec); + write(fWriteFd, fBuffer, sizeof(*op)); + read(fReadFd, fBuffer, sizeof(fBuffer)); + memcpy(metrics, &op->fontMetrics, sizeof(op->fontMetrics)); + op->~Op(); + } + + void generateMetrics(const SkTypefaceProxy& tf, + const SkScalerContextRec& rec, + SkGlyph* glyph) override { + Op* op = this->createOp(OpCode::kGlyphMetrics, tf, rec); + memcpy(&op->glyph, glyph, sizeof(*glyph)); + write(fWriteFd, fBuffer, sizeof(*op)); + read(fReadFd, fBuffer, sizeof(fBuffer)); + memcpy(glyph, &op->glyph, sizeof(op->glyph)); + op->~Op(); + } + + void generateImage(const SkTypefaceProxy& tf, + const SkScalerContextRec& rec, + const SkGlyph& glyph) override { + SK_ABORT("generateImage should not be called."); + Op* op = this->createOp(OpCode::kGlyphImage, tf, rec); + memcpy(&op->glyph, &glyph, sizeof(glyph)); + write(fWriteFd, fBuffer, sizeof(*op)); + read(fReadFd, fBuffer, sizeof(fBuffer)); + memcpy(glyph.fImage, fBuffer + sizeof(Op), glyph.rowBytes() * glyph.fHeight); + op->~Op(); + } + + void generateMetricsAndImage(const SkTypefaceProxy& tf, + const SkScalerContextRec& rec, + SkArenaAlloc* alloc, + SkGlyph* glyph) override { + Op* op = this->createOp(OpCode::kGlyphMetricsAndImage, tf, rec); + memcpy(&op->glyph, glyph, sizeof(op->glyph)); + write(fWriteFd, fBuffer, sizeof(*op)); + read(fReadFd, fBuffer, sizeof(fBuffer)); + memcpy(glyph, &op->glyph, sizeof(*glyph)); + glyph->allocImage(alloc); + memcpy(glyph->fImage, fBuffer + sizeof(Op), glyph->rowBytes() * glyph->fHeight); + op->~Op(); + } + + void generatePath(const SkTypefaceProxy& tf, + const SkScalerContextRec& rec, + SkGlyphID glyph, SkPath* path) override { + Op* op = this->createOp(OpCode::kGlyphPath, tf, rec); + op->glyphId = glyph; + write(fWriteFd, fBuffer, sizeof(*op)); + read(fReadFd, fBuffer, sizeof(fBuffer)); + path->readFromMemory(fBuffer + sizeof(Op), op->pathSize); + op->~Op(); + } + +private: + Op* createOp(OpCode opCode, const SkTypefaceProxy& tf, + const SkScalerContextRec& rec) { + Op* op = new (fBuffer) Op(opCode, tf.fontID(), rec); + + return op; + } + + const int fReadFd, + fWriteFd; + uint8_t fBuffer[1024 * kPageSize]; +}; + +static void final_draw(std::string outFilename, + SkDeserialProcs* procs, + uint8_t* picData, + size_t picSize) { + + auto pic = SkPicture::MakeFromData(picData, picSize, procs); + + auto cullRect = pic->cullRect(); + auto r = cullRect.round(); + + auto s = SkSurface::MakeRasterN32Premul(r.width(), r.height()); + auto c = s->getCanvas(); + auto picUnderTest = SkPicture::MakeFromData(picData, picSize, procs); + + + std::chrono::duration<double> total_seconds{0.0}; + for (int i = 0; i < 20; i++) { + if (gPurgeFontCaches) { + SkGraphics::PurgeFontCache(); + } + auto start = std::chrono::high_resolution_clock::now(); + c->drawPicture(picUnderTest); + auto end = std::chrono::high_resolution_clock::now(); + std::chrono::duration<double> elapsed_seconds = end-start; + total_seconds += elapsed_seconds; + + } + + std::cout << "useProcess: " << gUseProcess + << " useGPU: " << gUseGpu + << " purgeCache: " << gPurgeFontCaches << std::endl; + std::cerr << "elapsed time: " << total_seconds.count() << "s\n"; + + auto i = s->makeImageSnapshot(); + auto data = i->encodeToData(); + SkFILEWStream f(outFilename.c_str()); + f.write(data->data(), data->size()); +} + +static void gpu(int readFd, int writeFd) { + + size_t picSize = 0; + ssize_t r = read(readFd, &picSize, sizeof(picSize)); + if (r > 0) { + + static constexpr size_t kBufferSize = 10 * 1024 * kPageSize; + std::unique_ptr<uint8_t[]> picBuffer{new uint8_t[kBufferSize]}; + + size_t readSoFar = 0; + while (readSoFar < picSize) { + ssize_t readSize; + if ((readSize = read(readFd, &picBuffer[readSoFar], kBufferSize - readSoFar)) <= 0) { + if (readSize == 0) return; + err(1, "gpu pic read error %d", errno); + } + readSoFar += readSize; + } + + SkRemoteGlyphCacheGPU rc{ + skstd::make_unique<RemoteScalerContextFIFO>(readFd, writeFd) + }; + + SkDeserialProcs procs; + rc.prepareDeserializeProcs(&procs); + + final_draw("test.png", &procs, picBuffer.get(), picSize); + + } + + close(writeFd); + close(readFd); +} + +static int renderer( + const std::string& skpName, int readFd, int writeFd) +{ + std::string prefix{"skps/"}; + std::string fileName{prefix + skpName + ".skp"}; + + auto skp = SkData::MakeFromFileName(fileName.c_str()); + std::cout << "skp stream is " << skp->size() << " bytes long " << std::endl; + + SkRemoteGlyphCacheRenderer rc; + SkSerialProcs procs; + sk_sp<SkData> stream; + if (gUseGpu) { + auto pic = SkPicture::MakeFromData(skp.get()); + rc.prepareSerializeProcs(&procs); + stream = pic->serialize(&procs); + } else { + stream = skp; + } + + std::cout << "stream is " << stream->size() << " bytes long" << std::endl; + + size_t picSize = stream->size(); + uint8_t* picBuffer = (uint8_t*) stream->data(); + + if (!gUseGpu) { + final_draw("test-direct.png", nullptr, picBuffer, picSize); + close(writeFd); + close(readFd); + return 0; + } + + write(writeFd, &picSize, sizeof(picSize)); + + size_t writeSoFar = 0; + while (writeSoFar < picSize) { + ssize_t writeSize = write(writeFd, &picBuffer[writeSoFar], picSize - writeSoFar); + if (writeSize <= 0) { + if (writeSize == 0) { + std::cout << "Exit" << std::endl; + return 1; + } + perror("Can't write picture from render to GPU "); + return 1; + } + writeSoFar += writeSize; + } + std::cout << "Waiting for scaler context ops." << std::endl; + + static constexpr size_t kBufferSize = 1024 * kPageSize; + std::unique_ptr<uint8_t[]> glyphBuffer{new uint8_t[kBufferSize]}; + + Op* op = (Op*)glyphBuffer.get(); + while (true) { + ssize_t size = read(readFd, glyphBuffer.get(), sizeof(*op)); + if (size <= 0) { std::cout << "Exit op loop" << std::endl; break;} + size_t writeSize = sizeof(*op); + + auto sc = rc.generateScalerContext(op->descriptor, op->typefaceId); + switch (op->opCode) { + case OpCode::kFontMetrics : { + sc->getFontMetrics(&op->fontMetrics); + break; + } + case OpCode::kGlyphMetrics : { + sc->getMetrics(&op->glyph); + break; + } + case OpCode::kGlyphImage : { + // TODO: check for buffer overflow. + op->glyph.fImage = &glyphBuffer[sizeof(Op)]; + sc->getImage(op->glyph); + writeSize += op->glyph.rowBytes() * op->glyph.fHeight; + break; + } + case OpCode::kGlyphPath : { + // TODO: check for buffer overflow. + SkPath path; + sc->getPath(op->glyphId, &path); + op->pathSize = path.writeToMemory(&glyphBuffer[sizeof(Op)]); + writeSize += op->pathSize; + break; + } + case OpCode::kGlyphMetricsAndImage : { + // TODO: check for buffer overflow. + sc->getMetrics(&op->glyph); + if (op->glyph.fWidth <= 0 || op->glyph.fWidth >= kMaxGlyphWidth) { + op->glyph.fImage = nullptr; + break; + } + op->glyph.fImage = &glyphBuffer[sizeof(Op)]; + sc->getImage(op->glyph); + writeSize += op->glyph.rowBytes() * op->glyph.fHeight; + break; + } + default: + SK_ABORT("Bad op"); + } + + write(writeFd, glyphBuffer.get(), writeSize); + } + + close(readFd); + close(writeFd); + + std::cout << "Returning from render" << std::endl; + + return 0; +} + +enum direction : int {kRead = 0, kWrite = 1}; + +static void start_gpu(int render_to_gpu[2], int gpu_to_render[2]) { + std::cout << "gpu - Starting GPU" << std::endl; + close(gpu_to_render[kRead]); + close(render_to_gpu[kWrite]); + gpu(render_to_gpu[kRead], gpu_to_render[kWrite]); +} + +static void start_render(std::string& skpName, int render_to_gpu[2], int gpu_to_render[2]) { + std::cout << "renderer - Starting Renderer" << std::endl; + close(render_to_gpu[kRead]); + close(gpu_to_render[kWrite]); + renderer(skpName, gpu_to_render[kRead], render_to_gpu[kWrite]); +} + +int main(int argc, char** argv) { + std::string skpName = argc > 1 ? std::string{argv[1]} : std::string{"desk_nytimes"}; + int mode = argc > 2 ? atoi(argv[2]) : -1; + printf("skp: %s\n", skpName.c_str()); + + int render_to_gpu[2], + gpu_to_render[2]; + + for (int m = 0; m < 8; m++) { + int r = pipe(render_to_gpu); + if (r < 0) { + perror("Can't write picture from render to GPU "); + return 1; + } + r = pipe(gpu_to_render); + if (r < 0) { + perror("Can't write picture from render to GPU "); + return 1; + } + + gPurgeFontCaches = (m & 4) == 4; + gUseGpu = (m & 2) == 2; + gUseProcess = (m & 1) == 1; + + if (mode >= 0 && mode < 8 && mode != m) { + continue; + } + + if (gUseProcess) { + pid_t child = fork(); + SkGraphics::Init(); + + if (child == 0) { + start_gpu(render_to_gpu, gpu_to_render); + } else { + start_render(skpName, render_to_gpu, gpu_to_render); + waitpid(child, nullptr, 0); + } + } else { + SkGraphics::Init(); + std::thread(gpu, render_to_gpu[kRead], gpu_to_render[kWrite]).detach(); + renderer(skpName, gpu_to_render[kRead], render_to_gpu[kWrite]); + } + } + + return 0; +} + diff --git a/chromium/third_party/skia/tools/shape/SkShaper.h b/chromium/third_party/skia/tools/shape/SkShaper.h index a2a301e792e..190a4d834e1 100644 --- a/chromium/third_party/skia/tools/shape/SkShaper.h +++ b/chromium/third_party/skia/tools/shape/SkShaper.h @@ -28,12 +28,13 @@ public: ~SkShaper(); bool good() const; - SkScalar shape(SkTextBlobBuilder* dest, + SkPoint shape(SkTextBlobBuilder* dest, const SkPaint& srcPaint, const char* utf8text, size_t textBytes, bool leftToRight, - SkPoint point) const; + SkPoint point, + SkScalar width) const; private: SkShaper(const SkShaper&) = delete; diff --git a/chromium/third_party/skia/tools/shape/SkShaper_harfbuzz.cpp b/chromium/third_party/skia/tools/shape/SkShaper_harfbuzz.cpp index 36fe0a247c9..40414d9a79d 100644 --- a/chromium/third_party/skia/tools/shape/SkShaper_harfbuzz.cpp +++ b/chromium/third_party/skia/tools/shape/SkShaper_harfbuzz.cpp @@ -6,20 +6,26 @@ */ #include <hb-ot.h> +#include <unicode/brkiter.h> +#include <unicode/locid.h> #include <unicode/stringpiece.h> #include <unicode/ubidi.h> +#include <unicode/uchriter.h> #include <unicode/unistr.h> +#include <unicode/uscript.h> #include "SkFontMgr.h" +#include "SkLoadICU.h" +#include "SkOnce.h" #include "SkShaper.h" #include "SkStream.h" +#include "SkTDPQueue.h" +#include "SkTLazy.h" #include "SkTemplates.h" #include "SkTextBlob.h" #include "SkTypeface.h" #include "SkUtils.h" -static const int FONT_SIZE_SCALE = 512; - namespace { template <class T, void(*P)(T*)> using resource = std::unique_ptr<T, SkFunctionWrapper<void, T, P>>; using HBBlob = resource<hb_blob_t , hb_blob_destroy >; @@ -46,15 +52,8 @@ HBBlob stream_to_blob(std::unique_ptr<SkStreamAsset> asset) { hb_blob_make_immutable(blob.get()); return blob; } -} // namespace -struct SkShaper::Impl { - HBFont fHarfBuzzFont; - HBBuffer fBuffer; - sk_sp<SkTypeface> fTypeface; -}; - -static HBFont create_hb_font(SkTypeface* tf) { +HBFont create_hb_font(SkTypeface* tf) { int index; HBBlob blob(stream_to_blob(std::unique_ptr<SkStreamAsset>(tf->openStream(&index)))); HBFace face(hb_face_create(blob.get(), (unsigned)index)); @@ -70,7 +69,6 @@ static HBFont create_hb_font(SkTypeface* tf) { if (!font) { return nullptr; } - hb_font_set_scale(font.get(), FONT_SIZE_SCALE, FONT_SIZE_SCALE); hb_ot_font_set_funcs(font.get()); int axis_count = tf->getVariationDesignPosition(nullptr, 0); if (axis_count > 0) { @@ -84,181 +82,647 @@ static HBFont create_hb_font(SkTypeface* tf) { return font; } +class RunIterator { +public: + virtual ~RunIterator() {} + virtual void consume() = 0; + // Pointer one past the last (utf8) element in the current run. + virtual const char* endOfCurrentRun() const = 0; + virtual bool atEnd() const = 0; + bool operator<(const RunIterator& that) const { + return this->endOfCurrentRun() < that.endOfCurrentRun(); + } +}; + +class BiDiRunIterator : public RunIterator { +public: + static SkTLazy<BiDiRunIterator> Make(const char* utf8, size_t utf8Bytes, UBiDiLevel level) { + SkTLazy<BiDiRunIterator> ret; + + // ubidi only accepts utf16 (though internally it basically works on utf32 chars). + // We want an ubidi_setPara(UBiDi*, UText*, UBiDiLevel, UBiDiLevel*, UErrorCode*); + if (!SkTFitsIn<int32_t>(utf8Bytes)) { + SkDebugf("Bidi error: text too long"); + return ret; + } + icu::UnicodeString utf16 = icu::UnicodeString::fromUTF8(icu::StringPiece(utf8, utf8Bytes)); + + UErrorCode status = U_ZERO_ERROR; + ICUBiDi bidi(ubidi_openSized(utf16.length(), 0, &status)); + if (U_FAILURE(status)) { + SkDebugf("Bidi error: %s", u_errorName(status)); + return ret; + } + SkASSERT(bidi); + + // The required lifetime of utf16 isn't well documented. + // It appears it isn't used after ubidi_setPara except through ubidi_getText. + ubidi_setPara(bidi.get(), utf16.getBuffer(), utf16.length(), level, nullptr, &status); + if (U_FAILURE(status)) { + SkDebugf("Bidi error: %s", u_errorName(status)); + return ret; + } + + ret.init(utf8, std::move(bidi)); + return ret; + } + BiDiRunIterator(const char* utf8, ICUBiDi bidi) + : fBidi(std::move(bidi)) + , fEndOfCurrentRun(utf8) + , fUTF16LogicalPosition(0) + , fLevel(UBIDI_DEFAULT_LTR) + {} + void consume() override { + SkASSERT(fUTF16LogicalPosition < ubidi_getLength(fBidi.get())); + int32_t endPosition = ubidi_getLength(fBidi.get()); + fLevel = ubidi_getLevelAt(fBidi.get(), fUTF16LogicalPosition); + SkUnichar u = SkUTF8_NextUnichar(&fEndOfCurrentRun); + fUTF16LogicalPosition += SkUTF16_FromUnichar(u); + UBiDiLevel level; + while (fUTF16LogicalPosition < endPosition) { + level = ubidi_getLevelAt(fBidi.get(), fUTF16LogicalPosition); + if (level != fLevel) { + break; + } + u = SkUTF8_NextUnichar(&fEndOfCurrentRun); + fUTF16LogicalPosition += SkUTF16_FromUnichar(u); + } + } + const char* endOfCurrentRun() const override { + return fEndOfCurrentRun; + } + bool atEnd() const override { + return fUTF16LogicalPosition == ubidi_getLength(fBidi.get()); + } + + UBiDiLevel currentLevel() const { + return fLevel; + } +private: + ICUBiDi fBidi; + const char* fEndOfCurrentRun; + int32_t fUTF16LogicalPosition; + UBiDiLevel fLevel; +}; + +class ScriptRunIterator : public RunIterator { +public: + static SkTLazy<ScriptRunIterator> Make(const char* utf8, size_t utf8Bytes, + hb_unicode_funcs_t* hbUnicode) + { + SkTLazy<ScriptRunIterator> ret; + ret.init(utf8, utf8Bytes, hbUnicode); + return ret; + } + ScriptRunIterator(const char* utf8, size_t utf8Bytes, hb_unicode_funcs_t* hbUnicode) + : fCurrent(utf8), fEnd(fCurrent + utf8Bytes) + , fHBUnicode(hbUnicode) + , fCurrentScript(HB_SCRIPT_UNKNOWN) + {} + void consume() override { + SkASSERT(fCurrent < fEnd); + SkUnichar u = SkUTF8_NextUnichar(&fCurrent); + fCurrentScript = hb_unicode_script(fHBUnicode, u); + while (fCurrent < fEnd) { + const char* prev = fCurrent; + u = SkUTF8_NextUnichar(&fCurrent); + const hb_script_t script = hb_unicode_script(fHBUnicode, u); + if (script != fCurrentScript) { + if (fCurrentScript == HB_SCRIPT_INHERITED || fCurrentScript == HB_SCRIPT_COMMON) { + fCurrentScript = script; + } else if (script == HB_SCRIPT_INHERITED || script == HB_SCRIPT_COMMON) { + continue; + } else { + fCurrent = prev; + break; + } + } + } + if (fCurrentScript == HB_SCRIPT_INHERITED) { + fCurrentScript = HB_SCRIPT_COMMON; + } + } + const char* endOfCurrentRun() const override { + return fCurrent; + } + bool atEnd() const override { + return fCurrent == fEnd; + } + + hb_script_t currentScript() const { + return fCurrentScript; + } +private: + const char* fCurrent; + const char* fEnd; + hb_unicode_funcs_t* fHBUnicode; + hb_script_t fCurrentScript; +}; + +class FontRunIterator : public RunIterator { +public: + static SkTLazy<FontRunIterator> Make(const char* utf8, size_t utf8Bytes, + sk_sp<SkTypeface> typeface, + hb_font_t* hbFace, + sk_sp<SkFontMgr> fallbackMgr) + { + SkTLazy<FontRunIterator> ret; + ret.init(utf8, utf8Bytes, std::move(typeface), hbFace, std::move(fallbackMgr)); + return ret; + } + FontRunIterator(const char* utf8, size_t utf8Bytes, sk_sp<SkTypeface> typeface, + hb_font_t* hbFace, sk_sp<SkFontMgr> fallbackMgr) + : fCurrent(utf8), fEnd(fCurrent + utf8Bytes) + , fFallbackMgr(std::move(fallbackMgr)) + , fHBFont(hbFace), fTypeface(std::move(typeface)) + , fFallbackHBFont(nullptr), fFallbackTypeface(nullptr) + , fCurrentHBFont(fHBFont), fCurrentTypeface(fTypeface.get()) + {} + void consume() override { + SkASSERT(fCurrent < fEnd); + SkUnichar u = SkUTF8_NextUnichar(&fCurrent); + // If the starting typeface can handle this character, use it. + if (fTypeface->charsToGlyphs(&u, SkTypeface::kUTF32_Encoding, nullptr, 1)) { + fFallbackTypeface.reset(); + // If not, try to find a fallback typeface + } else { + fFallbackTypeface.reset(fFallbackMgr->matchFamilyStyleCharacter( + nullptr, fTypeface->fontStyle(), nullptr, 0, u)); + } + + if (fFallbackTypeface) { + fFallbackHBFont = create_hb_font(fFallbackTypeface.get()); + fCurrentTypeface = fFallbackTypeface.get(); + fCurrentHBFont = fFallbackHBFont.get(); + } else { + fFallbackHBFont.reset(); + fCurrentTypeface = fTypeface.get(); + fCurrentHBFont = fHBFont; + } + + while (fCurrent < fEnd) { + const char* prev = fCurrent; + u = SkUTF8_NextUnichar(&fCurrent); + + // If using a fallback and the initial typeface has this character, stop fallback. + if (fFallbackTypeface && + fTypeface->charsToGlyphs(&u, SkTypeface::kUTF32_Encoding, nullptr, 1)) + { + fCurrent = prev; + return; + } + // If the current typeface cannot handle this character, stop using it. + if (!fCurrentTypeface->charsToGlyphs(&u, SkTypeface::kUTF32_Encoding, nullptr, 1)) { + fCurrent = prev; + return; + } + } + } + const char* endOfCurrentRun() const override { + return fCurrent; + } + bool atEnd() const override { + return fCurrent == fEnd; + } + + SkTypeface* currentTypeface() const { + return fCurrentTypeface; + } + hb_font_t* currentHBFont() const { + return fCurrentHBFont; + } +private: + const char* fCurrent; + const char* fEnd; + sk_sp<SkFontMgr> fFallbackMgr; + hb_font_t* fHBFont; + sk_sp<SkTypeface> fTypeface; + HBFont fFallbackHBFont; + sk_sp<SkTypeface> fFallbackTypeface; + hb_font_t* fCurrentHBFont; + SkTypeface* fCurrentTypeface; +}; + +class RunIteratorQueue { +public: + void insert(RunIterator* runIterator) { + fRunIterators.insert(runIterator); + } + + bool advanceRuns() { + const RunIterator* leastRun = fRunIterators.peek(); + if (leastRun->atEnd()) { + SkASSERT(this->allRunsAreAtEnd()); + return false; + } + const char* leastEnd = leastRun->endOfCurrentRun(); + RunIterator* currentRun = nullptr; + SkDEBUGCODE(const char* previousEndOfCurrentRun); + while ((currentRun = fRunIterators.peek())->endOfCurrentRun() <= leastEnd) { + fRunIterators.pop(); + SkDEBUGCODE(previousEndOfCurrentRun = currentRun->endOfCurrentRun()); + currentRun->consume(); + SkASSERT(previousEndOfCurrentRun < currentRun->endOfCurrentRun()); + fRunIterators.insert(currentRun); + } + return true; + } + + const char* endOfCurrentRun() const { + return fRunIterators.peek()->endOfCurrentRun(); + } + +private: + bool allRunsAreAtEnd() const { + for (int i = 0; i < fRunIterators.count(); ++i) { + if (!fRunIterators.at(i)->atEnd()) { + return false; + } + } + return true; + } + + static bool CompareRunIterator(RunIterator* const& a, RunIterator* const& b) { + return *a < *b; + } + SkTDPQueue<RunIterator*, CompareRunIterator> fRunIterators; +}; + +struct ShapedGlyph { + SkGlyphID fID; + uint32_t fCluster; + SkPoint fOffset; + SkVector fAdvance; + bool fMayLineBreakBefore; + bool fMustLineBreakBefore; + bool fHasVisual; +}; +struct ShapedRun { + ShapedRun(const char* utf8Start, const char* utf8End, int numGlyphs, const SkPaint& paint, + UBiDiLevel level, std::unique_ptr<ShapedGlyph[]> glyphs) + : fUtf8Start(utf8Start), fUtf8End(utf8End), fNumGlyphs(numGlyphs), fPaint(paint) + , fLevel(level), fGlyphs(std::move(glyphs)) + {} + + const char* fUtf8Start; + const char* fUtf8End; + int fNumGlyphs; + SkPaint fPaint; + UBiDiLevel fLevel; + std::unique_ptr<ShapedGlyph[]> fGlyphs; +}; + +static constexpr bool is_LTR(UBiDiLevel level) { + return (level & 1) == 0; +} + +static void append(SkTextBlobBuilder* b, const ShapedRun& run, int start, int end, SkPoint* p) { + unsigned len = end - start; + auto runBuffer = b->allocRunTextPos(run.fPaint, len, run.fUtf8End - run.fUtf8Start, SkString()); + memcpy(runBuffer.utf8text, run.fUtf8Start, run.fUtf8End - run.fUtf8Start); + + for (unsigned i = 0; i < len; i++) { + // Glyphs are in logical order, but output ltr since PDF readers seem to expect that. + const ShapedGlyph& glyph = run.fGlyphs[is_LTR(run.fLevel) ? start + i : end - 1 - i]; + runBuffer.glyphs[i] = glyph.fID; + runBuffer.clusters[i] = glyph.fCluster; + reinterpret_cast<SkPoint*>(runBuffer.pos)[i] = + SkPoint::Make(p->fX + glyph.fOffset.fX, p->fY - glyph.fOffset.fY); + p->fX += glyph.fAdvance.fX; + p->fY += glyph.fAdvance.fY; + } +} + +struct ShapedRunGlyphIterator { + ShapedRunGlyphIterator(const SkTArray<ShapedRun>& origRuns) + : fRuns(&origRuns), fRunIndex(0), fGlyphIndex(0) + { } + + ShapedRunGlyphIterator(const ShapedRunGlyphIterator& that) = default; + ShapedRunGlyphIterator& operator=(const ShapedRunGlyphIterator& that) = default; + bool operator==(const ShapedRunGlyphIterator& that) const { + return fRuns == that.fRuns && + fRunIndex == that.fRunIndex && + fGlyphIndex == that.fGlyphIndex; + } + bool operator!=(const ShapedRunGlyphIterator& that) const { + return fRuns != that.fRuns || + fRunIndex != that.fRunIndex || + fGlyphIndex != that.fGlyphIndex; + } + + ShapedGlyph* next() { + const SkTArray<ShapedRun>& runs = *fRuns; + SkASSERT(fRunIndex < runs.count()); + SkASSERT(fGlyphIndex < runs[fRunIndex].fNumGlyphs); + + ++fGlyphIndex; + if (fGlyphIndex == runs[fRunIndex].fNumGlyphs) { + fGlyphIndex = 0; + ++fRunIndex; + if (fRunIndex >= runs.count()) { + return nullptr; + } + } + return &runs[fRunIndex].fGlyphs[fGlyphIndex]; + } + + ShapedGlyph* current() { + const SkTArray<ShapedRun>& runs = *fRuns; + if (fRunIndex >= runs.count()) { + return nullptr; + } + return &runs[fRunIndex].fGlyphs[fGlyphIndex]; + } + + const SkTArray<ShapedRun>* fRuns; + int fRunIndex; + int fGlyphIndex; +}; + +} // namespace + +struct SkShaper::Impl { + HBFont fHarfBuzzFont; + HBBuffer fBuffer; + sk_sp<SkTypeface> fTypeface; + std::unique_ptr<icu::BreakIterator> fBreakIterator; +}; + SkShaper::SkShaper(sk_sp<SkTypeface> tf) : fImpl(new Impl) { + SkOnce once; + once([] { SkLoadICU(); }); + fImpl->fTypeface = tf ? std::move(tf) : SkTypeface::MakeDefault(); fImpl->fHarfBuzzFont = create_hb_font(fImpl->fTypeface.get()); SkASSERT(fImpl->fHarfBuzzFont); fImpl->fBuffer.reset(hb_buffer_create()); + SkASSERT(fImpl->fBuffer); + + icu::Locale thai("th"); + UErrorCode status = U_ZERO_ERROR; + fImpl->fBreakIterator.reset(icu::BreakIterator::createLineInstance(thai, status)); + if (U_FAILURE(status)) { + SkDebugf("Could not create break iterator: %s", u_errorName(status)); + SK_ABORT(""); + } } SkShaper::~SkShaper() {} -bool SkShaper::good() const { return fImpl->fHarfBuzzFont != nullptr; } +bool SkShaper::good() const { + return fImpl->fHarfBuzzFont && + fImpl->fBuffer && + fImpl->fTypeface && + fImpl->fBreakIterator; +} -SkScalar SkShaper::shape(SkTextBlobBuilder* builder, - const SkPaint& srcPaint, - const char* utf8text, - size_t textBytes, - bool leftToRight, - SkPoint point) const { +SkPoint SkShaper::shape(SkTextBlobBuilder* builder, + const SkPaint& srcPaint, + const char* utf8, + size_t utf8Bytes, + bool leftToRight, + SkPoint point, + SkScalar width) const { sk_sp<SkFontMgr> fontMgr = SkFontMgr::RefDefault(); SkASSERT(builder); - UBiDiLevel bidiLevel = leftToRight ? UBIDI_DEFAULT_LTR : UBIDI_DEFAULT_RTL; + UBiDiLevel defaultLevel = leftToRight ? UBIDI_DEFAULT_LTR : UBIDI_DEFAULT_RTL; //hb_script_t script = ... - UErrorCode status = U_ZERO_ERROR; - double x = point.x(); - double y = point.y(); - // This function only accepts utf8. - // ubidi only accepts utf16 (though internally it basically works on utf32 chars). - // Internally, harfbuzz is all utf32, but always makes a copy. + SkTArray<ShapedRun> runs; +{ + RunIteratorQueue runSegmenter; - if (!SkTFitsIn<int32_t>(textBytes)) { - SkDebugf("Bidi error: text too long"); - return (SkScalar)x; + SkTLazy<BiDiRunIterator> maybeBidi(BiDiRunIterator::Make(utf8, utf8Bytes, defaultLevel)); + BiDiRunIterator* bidi = maybeBidi.getMaybeNull(); + if (!bidi) { + return point; } - icu::UnicodeString utf16 = icu::UnicodeString::fromUTF8(icu::StringPiece(utf8text, textBytes)); + runSegmenter.insert(bidi); - ICUBiDi bidi(ubidi_openSized(utf16.length(), 0, &status)); - if (U_FAILURE(status)) { - SkDebugf("Bidi error: %s", u_errorName(status)); - return (SkScalar)x; + hb_unicode_funcs_t* hbUnicode = hb_buffer_get_unicode_funcs(fImpl->fBuffer.get()); + SkTLazy<ScriptRunIterator> maybeScript(ScriptRunIterator::Make(utf8, utf8Bytes, hbUnicode)); + ScriptRunIterator* script = maybeScript.getMaybeNull(); + if (!script) { + return point; } - SkASSERT(bidi); + runSegmenter.insert(script); - ubidi_setPara(bidi.get(), utf16.getBuffer(), utf16.length(), bidiLevel, nullptr, &status); - if (U_FAILURE(status)) { - SkDebugf("Bidi error: %s", u_errorName(status)); - return (SkScalar)x; + SkTLazy<FontRunIterator> maybeFont(FontRunIterator::Make(utf8, utf8Bytes, + fImpl->fTypeface, + fImpl->fHarfBuzzFont.get(), + std::move(fontMgr))); + FontRunIterator* font = maybeFont.getMaybeNull(); + if (!font) { + return point; } + runSegmenter.insert(font); - int32_t runCount = ubidi_countRuns(bidi.get(), &status); - if (U_FAILURE(status)) { - SkDebugf("Bidi error: %s", u_errorName(status)); - return (SkScalar)x; + icu::BreakIterator& breakIterator = *fImpl->fBreakIterator; + { + UErrorCode status = U_ZERO_ERROR; + UText utf8UText = UTEXT_INITIALIZER; + utext_openUTF8(&utf8UText, utf8, utf8Bytes, &status); + std::unique_ptr<UText, SkFunctionWrapper<UText*, UText, utext_close>> autoClose(&utf8UText); + if (U_FAILURE(status)) { + SkDebugf("Could not create utf8UText: %s", u_errorName(status)); + return point; + } + breakIterator.setText(&utf8UText, status); + //utext_close(&utf8UText); + if (U_FAILURE(status)) { + SkDebugf("Could not setText on break iterator: %s", u_errorName(status)); + return point; + } } - const UChar* utf16End = utf16.getBuffer(); - const char* utf8End = utf8text; - for (int32_t i = 0; i < runCount; ++i) { - int32_t start; - int32_t length; - UBiDiDirection direction = ubidi_getVisualRun(bidi.get(), i, &start, &length); + const char* utf8Start = nullptr; + const char* utf8End = utf8; + while (runSegmenter.advanceRuns()) { + utf8Start = utf8End; + utf8End = runSegmenter.endOfCurrentRun(); + + hb_buffer_t* buffer = fImpl->fBuffer.get(); + SkAutoTCallVProc<hb_buffer_t, hb_buffer_clear_contents> autoClearBuffer(buffer); + hb_buffer_set_content_type(buffer, HB_BUFFER_CONTENT_TYPE_UNICODE); + hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); + + // Populate the hb_buffer directly with utf8 cluster indexes. + const char* utf8Current = utf8Start; + while (utf8Current < utf8End) { + unsigned int cluster = utf8Current - utf8Start; + hb_codepoint_t u = SkUTF8_NextUnichar(&utf8Current); + hb_buffer_add(buffer, u, cluster); + } + + size_t utf8runLength = utf8End - utf8Start; + if (!SkTFitsIn<int>(utf8runLength)) { + SkDebugf("Shaping error: utf8 too long"); + return point; + } + hb_buffer_set_script(buffer, script->currentScript()); + hb_direction_t direction = is_LTR(bidi->currentLevel()) ? HB_DIRECTION_LTR:HB_DIRECTION_RTL; + hb_buffer_set_direction(buffer, direction); + // TODO: language + hb_buffer_guess_segment_properties(buffer); + // TODO: features + hb_shape(font->currentHBFont(), buffer, nullptr, 0); + unsigned len = hb_buffer_get_length(buffer); + if (len == 0) { + continue; + } + + if (direction == HB_DIRECTION_RTL) { + // Put the clusters back in logical order. + // Note that the advances remain ltr. + hb_buffer_reverse(buffer); + } + hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, nullptr); + hb_glyph_position_t* pos = hb_buffer_get_glyph_positions(buffer, nullptr); + + if (!SkTFitsIn<int>(len)) { + SkDebugf("Shaping error: too many glyphs"); + return point; + } SkPaint paint(srcPaint); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - paint.setTypeface(fImpl->fTypeface); - hb_font_t* hbfont = fImpl->fHarfBuzzFont.get(); - HBFont fallbackHBFont; - sk_sp<SkTypeface> fallback; - while (utf16End < utf16.getBuffer() + start + length) { - hb_buffer_t* buffer = fImpl->fBuffer.get(); - SkAutoTCallVProc<hb_buffer_t, hb_buffer_clear_contents> autoClearBuffer(buffer); - - // The difficulty here is the cluster mapping. - // If the hb_buffer is created with utf16, clusters will point to utf16 indexes, - // but the SkTextBlob can only take utf8 and utf8 cluster indexes. - // So populate the hb_buffer directly with utf32 and utf8 cluster indexes. - // Since this steps through the visual runs in order, it is expected that each run will - // start just after the previous one ended. - const UChar* utf16Start = utf16.getBuffer() + start; - const char* utf8Start; - if (utf16End == utf16Start) { - utf16Start = utf16End; - utf8Start = utf8End; - } else { - SkDEBUGFAIL("Did not expect to ever get here."); - utf16Start = utf16.getBuffer(); - utf8Start = utf8text; - while (utf16Start < utf16.getBuffer() + start) { - SkUTF16_NextUnichar(&utf16Start); - SkUTF8_NextUnichar(&utf8Start); - } - } - const char* utf8Current = utf8Start; - const UChar* utf16Current = utf16Start; - utf16End = utf16Current + length; - while (utf16Current < utf16End) { - const UChar* utf16Prev = utf16Current; - hb_codepoint_t u = SkUTF16_NextUnichar(&utf16Current); - bool doPartialRun = false; - - // If using a fallback and the initial typeface has this character, stop fallback. - if (fallbackHBFont && - fImpl->fTypeface->charsToGlyphs(&u, SkTypeface::kUTF32_Encoding, nullptr, 1)) - { - fallback.reset(); - doPartialRun = true; - - // If the current typeface does not have this character, try a fallback. - } else if (!paint.getTypeface()->charsToGlyphs(&u, SkTypeface::kUTF32_Encoding, - nullptr, 1)) - { - fallback.reset(fontMgr->matchFamilyStyleCharacter(nullptr, - fImpl->fTypeface->fontStyle(), - nullptr, 0, - u)); - if (fallback) { - doPartialRun = true; - } - } - if (doPartialRun) { - utf16End = utf16Prev; - int32_t oldStart = start; - start = utf16End - utf16.getBuffer(); - length -= start - oldStart; - break; - } - hb_buffer_add(buffer, u, utf8Current - utf8Start); - SkUTF8_NextUnichar(&utf8Current); - } - hb_buffer_set_content_type(buffer, HB_BUFFER_CONTENT_TYPE_UNICODE); - utf8End = utf8Current; - size_t utf8runLength = utf8End - utf8Start; - if (!SkTFitsIn<int>(utf8runLength)) { - SkDebugf("Shaping error: utf8 too long"); - return (SkScalar)x; - } - hb_buffer_guess_segment_properties(buffer); - //hb_buffer_set_script(buffer, script); - hb_buffer_set_direction(buffer, direction ? HB_DIRECTION_RTL : HB_DIRECTION_LTR); - hb_shape(hbfont, buffer, nullptr, 0); - unsigned len = hb_buffer_get_length(buffer); - if (len > 0) { - hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, nullptr); - hb_glyph_position_t* pos = hb_buffer_get_glyph_positions(buffer, nullptr); - - if (!SkTFitsIn<int>(len)) { - SkDebugf("Shaping error: too many glyphs"); - return (SkScalar)x; - } - auto runBuffer = builder->allocRunTextPos(paint, len, utf8runLength, SkString()); - memcpy(runBuffer.utf8text, utf8Start, utf8runLength); - - double textSizeY = paint.getTextSize() / (double)FONT_SIZE_SCALE; - double textSizeX = textSizeY * paint.getTextScaleX(); - - for (unsigned i = 0; i < len; i++) { - runBuffer.glyphs[i] = info[i].codepoint; - runBuffer.clusters[i] = info[i].cluster; - reinterpret_cast<SkPoint*>(runBuffer.pos)[i] = - SkPoint::Make(SkDoubleToScalar(x + pos[i].x_offset * textSizeX), - SkDoubleToScalar(y - pos[i].y_offset * textSizeY)); - x += pos[i].x_advance * textSizeX; - y += pos[i].y_advance * textSizeY; - } - } + paint.setTypeface(sk_ref_sp(font->currentTypeface())); + ShapedRun& run = runs.emplace_back(utf8Start, utf8End, len, paint, bidi->currentLevel(), + std::unique_ptr<ShapedGlyph[]>(new ShapedGlyph[len])); + int scaleX, scaleY; + hb_font_get_scale(font->currentHBFont(), &scaleX, &scaleY); + double textSizeY = run.fPaint.getTextSize() / scaleY; + double textSizeX = run.fPaint.getTextSize() / scaleX * run.fPaint.getTextScaleX(); + for (unsigned i = 0; i < len; i++) { + ShapedGlyph& glyph = run.fGlyphs[i]; + glyph.fID = info[i].codepoint; + glyph.fCluster = info[i].cluster; + glyph.fOffset.fX = pos[i].x_offset * textSizeX; + glyph.fOffset.fY = pos[i].y_offset * textSizeY; + glyph.fAdvance.fX = pos[i].x_advance * textSizeX; + glyph.fAdvance.fY = pos[i].y_advance * textSizeY; + glyph.fHasVisual = true; //!font->currentTypeface()->glyphBoundsAreZero(glyph.fID); + //info->mask safe_to_break; + glyph.fMustLineBreakBefore = false; + } - if (fallback) { - paint.setTypeface(std::move(fallback)); - fallbackHBFont = create_hb_font(paint.getTypeface()); - hbfont = fallbackHBFont.get(); - } else { - paint.setTypeface(fImpl->fTypeface); - fallbackHBFont = nullptr; - hbfont = fImpl->fHarfBuzzFont.get(); + int32_t clusterOffset = utf8Start - utf8; + uint32_t previousCluster = 0xFFFFFFFF; + for (unsigned i = 0; i < len; ++i) { + ShapedGlyph& glyph = run.fGlyphs[i]; + int32_t glyphCluster = glyph.fCluster + clusterOffset; + int32_t breakIteratorCurrent = breakIterator.current(); + while (breakIteratorCurrent != icu::BreakIterator::DONE && + breakIteratorCurrent < glyphCluster) + { + breakIteratorCurrent = breakIterator.next(); } + glyph.fMayLineBreakBefore = glyph.fCluster != previousCluster && + breakIteratorCurrent == glyphCluster; + previousCluster = glyph.fCluster; } } - return (SkScalar)x; +} + +// Iterate over the glyphs in logical order to mark line endings. +{ + SkScalar widthSoFar = 0; + bool previousBreakValid = false; // Set when previousBreak is set to a valid candidate break. + bool canAddBreakNow = false; // Disallow line breaks before the first glyph of a run. + ShapedRunGlyphIterator previousBreak(runs); + ShapedRunGlyphIterator glyphIterator(runs); + while (ShapedGlyph* glyph = glyphIterator.current()) { + if (canAddBreakNow && glyph->fMayLineBreakBefore) { + previousBreakValid = true; + previousBreak = glyphIterator; + } + SkScalar glyphWidth = glyph->fAdvance.fX; + if (widthSoFar + glyphWidth < width) { + widthSoFar += glyphWidth; + glyphIterator.next(); + canAddBreakNow = true; + continue; + } + + if (widthSoFar == 0) { + // Adding just this glyph is too much, just break with this glyph + glyphIterator.next(); + previousBreak = glyphIterator; + } else if (!previousBreakValid) { + // No break opprotunity found yet, just break without this glyph + previousBreak = glyphIterator; + } + glyphIterator = previousBreak; + glyph = glyphIterator.current(); + if (glyph) { + glyph->fMustLineBreakBefore = true; + } + widthSoFar = 0; + previousBreakValid = false; + canAddBreakNow = false; + } +} + +// Reorder the runs and glyphs per line and write them out. + SkPoint currentPoint = point; +{ + ShapedRunGlyphIterator previousBreak(runs); + ShapedRunGlyphIterator glyphIterator(runs); + SkScalar maxAscent = 0; + SkScalar maxDescent = 0; + SkScalar maxLeading = 0; + int previousRunIndex = -1; + while (glyphIterator.current()) { + int runIndex = glyphIterator.fRunIndex; + int glyphIndex = glyphIterator.fGlyphIndex; + ShapedGlyph* nextGlyph = glyphIterator.next(); + + if (previousRunIndex != runIndex) { + SkPaint::FontMetrics metrics; + runs[runIndex].fPaint.getFontMetrics(&metrics); + maxAscent = SkTMin(maxAscent, metrics.fAscent); + maxDescent = SkTMax(maxDescent, metrics.fDescent); + maxLeading = SkTMax(maxLeading, metrics.fLeading); + previousRunIndex = runIndex; + } + + // Nothing can be written until the baseline is known. + if (!(nextGlyph == nullptr || nextGlyph->fMustLineBreakBefore)) { + continue; + } + + currentPoint.fY -= maxAscent; + + int numRuns = runIndex - previousBreak.fRunIndex + 1; + SkAutoSTMalloc<4, UBiDiLevel> runLevels(numRuns); + for (int i = 0; i < numRuns; ++i) { + runLevels[i] = runs[previousBreak.fRunIndex + i].fLevel; + } + SkAutoSTMalloc<4, int32_t> logicalFromVisual(numRuns); + ubidi_reorderVisual(runLevels, numRuns, logicalFromVisual); + + for (int i = 0; i < numRuns; ++i) { + int logicalIndex = previousBreak.fRunIndex + logicalFromVisual[i]; + + int startGlyphIndex = (logicalIndex == previousBreak.fRunIndex) + ? previousBreak.fGlyphIndex + : 0; + int endGlyphIndex = (logicalIndex == runIndex) + ? glyphIndex + 1 + : runs[logicalIndex].fNumGlyphs; + append(builder, runs[logicalIndex], startGlyphIndex, endGlyphIndex, ¤tPoint); + } + + currentPoint.fY += maxDescent + maxLeading; + currentPoint.fX = point.fX; + maxAscent = 0; + maxDescent = 0; + maxLeading = 0; + previousRunIndex = -1; + previousBreak = glyphIterator; + } +} + + return currentPoint; } diff --git a/chromium/third_party/skia/tools/shape/SkShaper_primitive.cpp b/chromium/third_party/skia/tools/shape/SkShaper_primitive.cpp index 750c51621e8..06a8bec41c7 100644 --- a/chromium/third_party/skia/tools/shape/SkShaper_primitive.cpp +++ b/chromium/third_party/skia/tools/shape/SkShaper_primitive.cpp @@ -29,12 +29,13 @@ unsigned utf8_lead_byte_to_count(const char* ptr) { return (((0xE5 << 24) >> ((unsigned)c >> 4 << 1)) & 3) + 1; } -SkScalar SkShaper::shape(SkTextBlobBuilder* builder, - const SkPaint& srcPaint, - const char* utf8text, - size_t textBytes, - bool leftToRight, - SkPoint point) const { +SkPoint SkShaper::shape(SkTextBlobBuilder* builder, + const SkPaint& srcPaint, + const char* utf8text, + size_t textBytes, + bool leftToRight, + SkPoint point, + SkScalar width) const { sk_ignore_unused_variable(leftToRight); SkPaint paint(srcPaint); @@ -42,9 +43,12 @@ SkScalar SkShaper::shape(SkTextBlobBuilder* builder, paint.setTextEncoding(SkPaint::kUTF8_TextEncoding); int glyphCount = paint.countText(utf8text, textBytes); if (glyphCount <= 0) { - return 0; + return point; } SkRect bounds; + SkPaint::FontMetrics metrics; + paint.getFontMetrics(&metrics); + point.fY -= metrics.fAscent; (void)paint.measureText(utf8text, textBytes, &bounds); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); const SkTextBlobBuilder::RunBuffer& runBuffer = @@ -66,5 +70,7 @@ SkScalar SkShaper::shape(SkTextBlobBuilder* builder, runBuffer.pos[i] = x; x += w; } - return (SkScalar)x; + point.fY += metrics.fDescent + metrics.fLeading; + + return point; } diff --git a/chromium/third_party/skia/tools/shape/using_skia_and_harfbuzz.cpp b/chromium/third_party/skia/tools/shape/using_skia_and_harfbuzz.cpp index 533fd1e10d0..5a748667949 100644 --- a/chromium/third_party/skia/tools/shape/using_skia_and_harfbuzz.cpp +++ b/chromium/third_party/skia/tools/shape/using_skia_and_harfbuzz.cpp @@ -112,7 +112,7 @@ struct Config { DoubleOption font_size = DoubleOption("-z", "Font size", 8.0f); DoubleOption left_margin = DoubleOption("-m", "Left margin", 20.0f); DoubleOption line_spacing_ratio = - DoubleOption("-h", "Line spacing ratio", 1.5f); + DoubleOption("-h", "Line spacing ratio", 0.25f); StringOption output_file_name = StringOption("-o", ".pdf output file name", "out-skiahf.pdf"); @@ -138,7 +138,16 @@ public: } void WriteLine(const SkShaper& shaper, const char *text, size_t textBytes) { - if (!pageCanvas || current_y > config->page_height.value) { + SkTextBlobBuilder textBlobBuilder; + SkPoint endPoint = shaper.shape(&textBlobBuilder, glyph_paint, text, textBytes, true, + SkPoint{0, 0}, + config->page_width.value - 2*config->left_margin.value); + sk_sp<const SkTextBlob> blob = textBlobBuilder.make(); + // If we don't have a page, or if we're not at the start of the page and the blob won't fit + if (!pageCanvas || + (current_y > config->line_spacing_ratio.value * config->font_size.value && + current_y + endPoint.y() > config->page_height.value) + ) { if (pageCanvas) { document->endPage(); } @@ -149,14 +158,11 @@ public: current_x = config->left_margin.value; current_y = config->line_spacing_ratio.value * config->font_size.value; } - SkTextBlobBuilder textBlobBuilder; - shaper.shape(&textBlobBuilder, glyph_paint, text, textBytes, true, SkPoint{0, 0}); - sk_sp<const SkTextBlob> blob = textBlobBuilder.make(); pageCanvas->drawTextBlob( blob.get(), SkDoubleToScalar(current_x), SkDoubleToScalar(current_y), glyph_paint); // Advance to the next line. - current_y += config->line_spacing_ratio.value * config->font_size.value; + current_y += endPoint.y() + config->line_spacing_ratio.value * config->font_size.value; } private: @@ -205,6 +211,7 @@ int main(int argc, char **argv) { SkShaper shaper(typeface); assert(shaper.good()); //SkString line("This is هذا هو الخط a line."); + //SkString line("This is a line هذا هو الخط."); for (std::string line; std::getline(std::cin, line);) { placement.WriteLine(shaper, line.c_str(), line.size()); } diff --git a/chromium/third_party/skia/tools/sk_app/CommandSet.cpp b/chromium/third_party/skia/tools/sk_app/CommandSet.cpp index d0154d6e617..9684ef26241 100644 --- a/chromium/third_party/skia/tools/sk_app/CommandSet.cpp +++ b/chromium/third_party/skia/tools/sk_app/CommandSet.cpp @@ -78,7 +78,7 @@ void CommandSet::addCommand(Window::Key k, const char* keyName, const char* grou fCommands.push_back(Command(k, keyName, group, description, function)); } -#if defined(SK_BUILD_FOR_WIN32) +#if defined(SK_BUILD_FOR_WIN) #define SK_strcasecmp _stricmp #else #define SK_strcasecmp strcasecmp diff --git a/chromium/third_party/skia/tools/sk_app/DisplayParams.h b/chromium/third_party/skia/tools/sk_app/DisplayParams.h index 959735e8ff2..203e8bdeca8 100644 --- a/chromium/third_party/skia/tools/sk_app/DisplayParams.h +++ b/chromium/third_party/skia/tools/sk_app/DisplayParams.h @@ -13,10 +13,7 @@ namespace sk_app { struct DisplayParams { - DisplayParams() - : fColorType(kN32_SkColorType) - , fColorSpace(nullptr) - , fMSAASampleCount(0) {} + DisplayParams() : fColorType(kN32_SkColorType), fColorSpace(nullptr), fMSAASampleCount(1) {} SkColorType fColorType; sk_sp<SkColorSpace> fColorSpace; diff --git a/chromium/third_party/skia/tools/sk_app/GLWindowContext.cpp b/chromium/third_party/skia/tools/sk_app/GLWindowContext.cpp index 9ef5141feec..9d042cf19ed 100644 --- a/chromium/third_party/skia/tools/sk_app/GLWindowContext.cpp +++ b/chromium/third_party/skia/tools/sk_app/GLWindowContext.cpp @@ -24,9 +24,7 @@ GLWindowContext::GLWindowContext(const DisplayParams& params) : WindowContext(params) , fBackendContext(nullptr) , fSurface(nullptr) { - fDisplayParams.fMSAASampleCount = fDisplayParams.fMSAASampleCount ? - GrNextPow2(fDisplayParams.fMSAASampleCount) : - 0; + fDisplayParams.fMSAASampleCount = GrNextPow2(fDisplayParams.fMSAASampleCount); } void GLWindowContext::initializeContext() { @@ -34,7 +32,7 @@ void GLWindowContext::initializeContext() { fBackendContext = this->onInitializeContext(); fContext = GrContext::MakeGL(fBackendContext, fDisplayParams.fGrContextOptions); - if (!fContext && fDisplayParams.fMSAASampleCount) { + if (!fContext && fDisplayParams.fMSAASampleCount > 1) { fDisplayParams.fMSAASampleCount /= 2; this->initializeContext(); return; diff --git a/chromium/third_party/skia/tools/sk_app/VulkanWindowContext.cpp b/chromium/third_party/skia/tools/sk_app/VulkanWindowContext.cpp index 6237ee09aed..d7839bc1894 100644 --- a/chromium/third_party/skia/tools/sk_app/VulkanWindowContext.cpp +++ b/chromium/third_party/skia/tools/sk_app/VulkanWindowContext.cpp @@ -287,7 +287,7 @@ void VulkanWindowContext::createBuffers(VkFormat format, SkColorType colorType) GrVkImageInfo info; info.fImage = fImages[i]; - info.fAlloc = { VK_NULL_HANDLE, 0, 0, 0 }; + info.fAlloc = GrVkAlloc(); info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED; info.fImageTiling = VK_IMAGE_TILING_OPTIMAL; info.fFormat = format; diff --git a/chromium/third_party/skia/tools/sk_app/Window.cpp b/chromium/third_party/skia/tools/sk_app/Window.cpp index 29e4864ba1e..1694beaac84 100644 --- a/chromium/third_party/skia/tools/sk_app/Window.cpp +++ b/chromium/third_party/skia/tools/sk_app/Window.cpp @@ -117,7 +117,7 @@ void Window::setRequestedDisplayParams(const DisplayParams& params, bool /* allo int Window::sampleCount() const { if (!fWindowContext) { - return -1; + return 0; } return fWindowContext->sampleCount(); } diff --git a/chromium/third_party/skia/tools/sk_app/WindowContext.h b/chromium/third_party/skia/tools/sk_app/WindowContext.h index 5e7d76d4bad..71c21ac0891 100644 --- a/chromium/third_party/skia/tools/sk_app/WindowContext.h +++ b/chromium/third_party/skia/tools/sk_app/WindowContext.h @@ -21,11 +21,11 @@ namespace sk_app { class WindowContext { public: WindowContext(const DisplayParams& params) - : fContext(nullptr) - , fDisplayParams(params) - , fSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType) - , fSampleCount(0) - , fStencilBits(0) {} + : fContext(nullptr) + , fDisplayParams(params) + , fSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType) + , fSampleCount(1) + , fStencilBits(0) {} virtual ~WindowContext() {} diff --git a/chromium/third_party/skia/tools/sk_app/android/GLWindowContext_android.cpp b/chromium/third_party/skia/tools/sk_app/android/GLWindowContext_android.cpp index acdb5872300..51111141158 100644 --- a/chromium/third_party/skia/tools/sk_app/android/GLWindowContext_android.cpp +++ b/chromium/third_party/skia/tools/sk_app/android/GLWindowContext_android.cpp @@ -70,6 +70,8 @@ sk_sp<const GrGLInterface> GLWindowContext_android::onInitializeContext() { SkAssertResult(eglBindAPI(EGL_OPENGL_ES_API)); EGLint numConfigs = 0; + EGLint eglSampleCnt = fDisplayParams.fMSAASampleCount > 1 ? fDisplayParams.fMSAASampleCount > 1 + : 0; const EGLint configAttribs[] = { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, @@ -78,8 +80,8 @@ sk_sp<const GrGLInterface> GLWindowContext_android::onInitializeContext() { EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_STENCIL_SIZE, 8, - EGL_SAMPLE_BUFFERS, fDisplayParams.fMSAASampleCount ? 1 : 0, - EGL_SAMPLES, fDisplayParams.fMSAASampleCount, + EGL_SAMPLE_BUFFERS, eglSampleCnt ? 1 : 0, + EGL_SAMPLES, eglSampleCnt, EGL_NONE }; @@ -131,6 +133,7 @@ sk_sp<const GrGLInterface> GLWindowContext_android::onInitializeContext() { eglGetConfigAttrib(fDisplay, surfaceConfig, EGL_STENCIL_SIZE, &fStencilBits); eglGetConfigAttrib(fDisplay, surfaceConfig, EGL_SAMPLES, &fSampleCount); + fSampleCount = SkTMax(fSampleCount, 1); return GrGLMakeNativeInterface(); } diff --git a/chromium/third_party/skia/tools/sk_app/ios/GLWindowContext_ios.cpp b/chromium/third_party/skia/tools/sk_app/ios/GLWindowContext_ios.cpp index f4c0d6b3c03..aac7e20fd92 100644 --- a/chromium/third_party/skia/tools/sk_app/ios/GLWindowContext_ios.cpp +++ b/chromium/third_party/skia/tools/sk_app/ios/GLWindowContext_ios.cpp @@ -27,7 +27,7 @@ public: void onSwapBuffers() override; sk_sp<const GrGLInterface> onInitializeContext() override; - void onDestroyContext() override; + void onDestroyContext() override {} private: SDL_Window* fWindow; @@ -39,7 +39,7 @@ private: GLWindowContext_ios::GLWindowContext_ios(const IOSWindowInfo& info, const DisplayParams& params) : INHERITED(params) , fWindow(info.fWindow) - , fGLContext(nullptr) { + , fGLContext(info.fGLContext) { // any config code here (particularly for msaa)? @@ -52,12 +52,7 @@ GLWindowContext_ios::~GLWindowContext_ios() { sk_sp<const GrGLInterface> GLWindowContext_ios::onInitializeContext() { SkASSERT(fWindow); - - fGLContext = SDL_GL_CreateContext(fWindow); - if (!fGLContext) { - SkDebugf("%s\n", SDL_GetError()); - return nullptr; - } + SkASSERT(fGLContext); if (0 == SDL_GL_MakeCurrent(fWindow, fGLContext)) { glClearStencil(0); @@ -67,6 +62,7 @@ sk_sp<const GrGLInterface> GLWindowContext_ios::onInitializeContext() { SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &fStencilBits); SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &fSampleCount); + fSampleCount = SkTMax(fSampleCount, 1); SDL_GL_GetDrawableSize(fWindow, &fWidth, &fHeight); glViewport(0, 0, fWidth, fHeight); @@ -76,15 +72,6 @@ sk_sp<const GrGLInterface> GLWindowContext_ios::onInitializeContext() { return GrGLMakeNativeInterface(); } -void GLWindowContext_ios::onDestroyContext() { - if (!fWindow || !fGLContext) { - return; - } - SDL_GL_DeleteContext(fGLContext); - fGLContext = nullptr; -} - - void GLWindowContext_ios::onSwapBuffers() { if (fWindow && fGLContext) { SDL_GL_SwapWindow(fWindow); diff --git a/chromium/third_party/skia/tools/sk_app/ios/RasterWindowContext_ios.cpp b/chromium/third_party/skia/tools/sk_app/ios/RasterWindowContext_ios.cpp index cae5774c28f..18a6c36014e 100644 --- a/chromium/third_party/skia/tools/sk_app/ios/RasterWindowContext_ios.cpp +++ b/chromium/third_party/skia/tools/sk_app/ios/RasterWindowContext_ios.cpp @@ -64,12 +64,7 @@ RasterWindowContext_ios::~RasterWindowContext_ios() { sk_sp<const GrGLInterface> RasterWindowContext_ios::onInitializeContext() { SkASSERT(fWindow); - - fGLContext = SDL_GL_CreateContext(fWindow); - if (!fGLContext) { - SkDebugf("%s\n", SDL_GetError()); - return nullptr; - } + SkASSERT(fGLContext); if (0 == SDL_GL_MakeCurrent(fWindow, fGLContext)) { glClearStencil(0); @@ -79,6 +74,7 @@ sk_sp<const GrGLInterface> RasterWindowContext_ios::onInitializeContext() { SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &fStencilBits); SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &fSampleCount); + fSampleCount = SkTMax(fSampleCount, 1); SDL_GL_GetDrawableSize(fWindow, &fWidth, &fHeight); glViewport(0, 0, fWidth, fHeight); @@ -94,12 +90,7 @@ sk_sp<const GrGLInterface> RasterWindowContext_ios::onInitializeContext() { } void RasterWindowContext_ios::onDestroyContext() { - if (!fWindow || !fGLContext) { - return; - } fBackbufferSurface.reset(nullptr); - SDL_GL_DeleteContext(fGLContext); - fGLContext = nullptr; } sk_sp<SkSurface> RasterWindowContext_ios::getBackbufferSurface() { return fBackbufferSurface; } diff --git a/chromium/third_party/skia/tools/sk_app/ios/WindowContextFactory_ios.h b/chromium/third_party/skia/tools/sk_app/ios/WindowContextFactory_ios.h index 09999c4c83d..93c6fbf4507 100644 --- a/chromium/third_party/skia/tools/sk_app/ios/WindowContextFactory_ios.h +++ b/chromium/third_party/skia/tools/sk_app/ios/WindowContextFactory_ios.h @@ -19,7 +19,8 @@ struct DisplayParams; namespace window_context_factory { struct IOSWindowInfo { - SDL_Window* fWindow; + SDL_Window* fWindow; + SDL_GLContext fGLContext; }; inline WindowContext* NewVulkanForIOS(const IOSWindowInfo&, const DisplayParams&) { diff --git a/chromium/third_party/skia/tools/sk_app/ios/Window_ios.cpp b/chromium/third_party/skia/tools/sk_app/ios/Window_ios.cpp index c1bdeae5fc7..9b2339d19e7 100644 --- a/chromium/third_party/skia/tools/sk_app/ios/Window_ios.cpp +++ b/chromium/third_party/skia/tools/sk_app/ios/Window_ios.cpp @@ -49,7 +49,7 @@ bool Window_ios::initWindow() { SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); - if (fRequestedDisplayParams.fMSAASampleCount > 0) { + if (fRequestedDisplayParams.fMSAASampleCount > 1) { SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, fRequestedDisplayParams.fMSAASampleCount); } else { @@ -71,10 +71,22 @@ bool Window_ios::initWindow() { fWindowID = SDL_GetWindowID(fWindow); gWindowMap.add(this); + fGLContext = SDL_GL_CreateContext(fWindow); + if (!fGLContext) { + SkDebugf("%s\n", SDL_GetError()); + this->closeWindow(); + return false; + } + return true; } void Window_ios::closeWindow() { + if (fGLContext) { + SDL_GL_DeleteContext(fGLContext); + fGLContext = nullptr; + } + if (fWindow) { gWindowMap.remove(fWindowID); SDL_DestroyWindow(fWindow); @@ -251,6 +263,7 @@ bool Window_ios::attach(BackendType attachType) { window_context_factory::IOSWindowInfo info; info.fWindow = fWindow; + info.fGLContext = fGLContext; switch (attachType) { case kRaster_BackendType: fWindowContext = NewRasterForIOS(info, fRequestedDisplayParams); diff --git a/chromium/third_party/skia/tools/sk_app/ios/Window_ios.h b/chromium/third_party/skia/tools/sk_app/ios/Window_ios.h index 667fa74e825..260b133ff97 100644 --- a/chromium/third_party/skia/tools/sk_app/ios/Window_ios.h +++ b/chromium/third_party/skia/tools/sk_app/ios/Window_ios.h @@ -19,10 +19,11 @@ namespace sk_app { class Window_ios : public Window { public: Window_ios() - : INHERITED() - , fWindow(nullptr) - , fWindowID(0) - , fMSAASampleCount(0) {} + : INHERITED() + , fWindow(nullptr) + , fWindowID(0) + , fGLContext(nullptr) + , fMSAASampleCount(1) {} ~Window_ios() override { this->closeWindow(); } bool initWindow(); @@ -51,8 +52,9 @@ private: static SkTDynamicHash<Window_ios, Uint32> gWindowMap; - SDL_Window* fWindow; - Uint32 fWindowID; + SDL_Window* fWindow; + Uint32 fWindowID; + SDL_GLContext fGLContext; int fMSAASampleCount; diff --git a/chromium/third_party/skia/tools/sk_app/mac/GLWindowContext_mac.cpp b/chromium/third_party/skia/tools/sk_app/mac/GLWindowContext_mac.cpp index 005fc07df46..f7e56ccb629 100644 --- a/chromium/third_party/skia/tools/sk_app/mac/GLWindowContext_mac.cpp +++ b/chromium/third_party/skia/tools/sk_app/mac/GLWindowContext_mac.cpp @@ -27,7 +27,7 @@ public: void onSwapBuffers() override; sk_sp<const GrGLInterface> onInitializeContext() override; - void onDestroyContext() override; + void onDestroyContext() override {} private: SDL_Window* fWindow; @@ -39,7 +39,7 @@ private: GLWindowContext_mac::GLWindowContext_mac(const MacWindowInfo& info, const DisplayParams& params) : INHERITED(params) , fWindow(info.fWindow) - , fGLContext(nullptr) { + , fGLContext(info.fGLContext) { // any config code here (particularly for msaa)? @@ -52,12 +52,7 @@ GLWindowContext_mac::~GLWindowContext_mac() { sk_sp<const GrGLInterface> GLWindowContext_mac::onInitializeContext() { SkASSERT(fWindow); - - fGLContext = SDL_GL_CreateContext(fWindow); - if (!fGLContext) { - SkDebugf("%s\n", SDL_GetError()); - return nullptr; - } + SkASSERT(fGLContext); if (0 == SDL_GL_MakeCurrent(fWindow, fGLContext)) { glClearStencil(0); @@ -67,6 +62,7 @@ sk_sp<const GrGLInterface> GLWindowContext_mac::onInitializeContext() { SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &fStencilBits); SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &fSampleCount); + fSampleCount = SkTMax(fSampleCount, 1); SDL_GetWindowSize(fWindow, &fWidth, &fHeight); glViewport(0, 0, fWidth, fHeight); @@ -76,15 +72,6 @@ sk_sp<const GrGLInterface> GLWindowContext_mac::onInitializeContext() { return GrGLMakeNativeInterface(); } -void GLWindowContext_mac::onDestroyContext() { - if (!fWindow || !fGLContext) { - return; - } - SDL_GL_DeleteContext(fGLContext); - fGLContext = nullptr; -} - - void GLWindowContext_mac::onSwapBuffers() { if (fWindow && fGLContext) { SDL_GL_SwapWindow(fWindow); diff --git a/chromium/third_party/skia/tools/sk_app/mac/RasterWindowContext_mac.cpp b/chromium/third_party/skia/tools/sk_app/mac/RasterWindowContext_mac.cpp index 67022af7fef..b66ba407fa4 100644 --- a/chromium/third_party/skia/tools/sk_app/mac/RasterWindowContext_mac.cpp +++ b/chromium/third_party/skia/tools/sk_app/mac/RasterWindowContext_mac.cpp @@ -51,7 +51,7 @@ RasterWindowContext_mac::RasterWindowContext_mac(const MacWindowInfo& info, const DisplayParams& params) : INHERITED(params) , fWindow(info.fWindow) - , fGLContext(nullptr) { + , fGLContext(info.fGLContext) { // any config code here (particularly for msaa)? @@ -64,12 +64,7 @@ RasterWindowContext_mac::~RasterWindowContext_mac() { sk_sp<const GrGLInterface> RasterWindowContext_mac::onInitializeContext() { SkASSERT(fWindow); - - fGLContext = SDL_GL_CreateContext(fWindow); - if (!fGLContext) { - SkDebugf("%s\n", SDL_GetError()); - return nullptr; - } + SkASSERT(fGLContext); if (0 == SDL_GL_MakeCurrent(fWindow, fGLContext)) { glClearStencil(0); @@ -79,6 +74,7 @@ sk_sp<const GrGLInterface> RasterWindowContext_mac::onInitializeContext() { SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &fStencilBits); SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &fSampleCount); + fSampleCount = SkTMax(fSampleCount, 1); SDL_GetWindowSize(fWindow, &fWidth, &fHeight); glViewport(0, 0, fWidth, fHeight); @@ -94,12 +90,7 @@ sk_sp<const GrGLInterface> RasterWindowContext_mac::onInitializeContext() { } void RasterWindowContext_mac::onDestroyContext() { - if (!fWindow || !fGLContext) { - return; - } fBackbufferSurface.reset(nullptr); - SDL_GL_DeleteContext(fGLContext); - fGLContext = nullptr; } sk_sp<SkSurface> RasterWindowContext_mac::getBackbufferSurface() { return fBackbufferSurface; } diff --git a/chromium/third_party/skia/tools/sk_app/mac/WindowContextFactory_mac.h b/chromium/third_party/skia/tools/sk_app/mac/WindowContextFactory_mac.h index 3adc68bbc23..c195c24daa0 100644 --- a/chromium/third_party/skia/tools/sk_app/mac/WindowContextFactory_mac.h +++ b/chromium/third_party/skia/tools/sk_app/mac/WindowContextFactory_mac.h @@ -19,7 +19,8 @@ struct DisplayParams; namespace window_context_factory { struct MacWindowInfo { - SDL_Window* fWindow; + SDL_Window* fWindow; + SDL_GLContext fGLContext; }; inline WindowContext* NewVulkanForMac(const MacWindowInfo&, const DisplayParams&) { diff --git a/chromium/third_party/skia/tools/sk_app/mac/Window_mac.cpp b/chromium/third_party/skia/tools/sk_app/mac/Window_mac.cpp index 8de5b104500..309c8bbf362 100644 --- a/chromium/third_party/skia/tools/sk_app/mac/Window_mac.cpp +++ b/chromium/third_party/skia/tools/sk_app/mac/Window_mac.cpp @@ -49,7 +49,7 @@ bool Window_mac::initWindow() { SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); - if (fRequestedDisplayParams.fMSAASampleCount > 0) { + if (fRequestedDisplayParams.fMSAASampleCount > 1) { SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, fRequestedDisplayParams.fMSAASampleCount); } else { @@ -71,10 +71,22 @@ bool Window_mac::initWindow() { fWindowID = SDL_GetWindowID(fWindow); gWindowMap.add(this); + fGLContext = SDL_GL_CreateContext(fWindow); + if (!fGLContext) { + SkDebugf("%s\n", SDL_GetError()); + this->closeWindow(); + return false; + } + return true; } void Window_mac::closeWindow() { + if (fGLContext) { + SDL_GL_DeleteContext(fGLContext); + fGLContext = nullptr; + } + if (fWindow) { gWindowMap.remove(fWindowID); SDL_DestroyWindow(fWindow); @@ -251,6 +263,7 @@ bool Window_mac::attach(BackendType attachType) { window_context_factory::MacWindowInfo info; info.fWindow = fWindow; + info.fGLContext = fGLContext; switch (attachType) { case kRaster_BackendType: fWindowContext = NewRasterForMac(info, fRequestedDisplayParams); diff --git a/chromium/third_party/skia/tools/sk_app/mac/Window_mac.h b/chromium/third_party/skia/tools/sk_app/mac/Window_mac.h index aa5c8df6962..8b34b7c902e 100644 --- a/chromium/third_party/skia/tools/sk_app/mac/Window_mac.h +++ b/chromium/third_party/skia/tools/sk_app/mac/Window_mac.h @@ -19,10 +19,11 @@ namespace sk_app { class Window_mac : public Window { public: Window_mac() - : INHERITED() - , fWindow(nullptr) - , fWindowID(0) - , fMSAASampleCount(0) {} + : INHERITED() + , fWindow(nullptr) + , fWindowID(0) + , fGLContext(nullptr) + , fMSAASampleCount(1) {} ~Window_mac() override { this->closeWindow(); } bool initWindow(); @@ -51,8 +52,9 @@ private: static SkTDynamicHash<Window_mac, Uint32> gWindowMap; - SDL_Window* fWindow; - Uint32 fWindowID; + SDL_Window* fWindow; + Uint32 fWindowID; + SDL_GLContext fGLContext; int fMSAASampleCount; diff --git a/chromium/third_party/skia/tools/sk_app/unix/GLWindowContext_unix.cpp b/chromium/third_party/skia/tools/sk_app/unix/GLWindowContext_unix.cpp index 25ec95cdd64..3a3a4b16d45 100644 --- a/chromium/third_party/skia/tools/sk_app/unix/GLWindowContext_unix.cpp +++ b/chromium/third_party/skia/tools/sk_app/unix/GLWindowContext_unix.cpp @@ -99,6 +99,7 @@ sk_sp<const GrGLInterface> GLWindowContext_xlib::onInitializeContext() { glXGetConfig(fDisplay, fVisualInfo, GLX_STENCIL_SIZE, &fStencilBits); glXGetConfig(fDisplay, fVisualInfo, GLX_SAMPLES_ARB, &fSampleCount); + fSampleCount = SkTMax(fSampleCount, 1); XWindow root; int x, y; diff --git a/chromium/third_party/skia/tools/sk_app/unix/Window_unix.cpp b/chromium/third_party/skia/tools/sk_app/unix/Window_unix.cpp index f5ca5ee0736..745b4dd3fb0 100644 --- a/chromium/third_party/skia/tools/sk_app/unix/Window_unix.cpp +++ b/chromium/third_party/skia/tools/sk_app/unix/Window_unix.cpp @@ -72,7 +72,7 @@ bool Window_unix::initWindow(Display* display) { None }; SkASSERT(nullptr == fVisualInfo); - if (fRequestedDisplayParams.fMSAASampleCount > 0) { + if (fRequestedDisplayParams.fMSAASampleCount > 1) { static const GLint kChooseFBConifgAttCnt = SK_ARRAY_COUNT(kChooseFBConfigAtt); GLint msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 4]; memcpy(msaaChooseFBConfigAtt, kChooseFBConfigAtt, sizeof(kChooseFBConfigAtt)); diff --git a/chromium/third_party/skia/tools/sk_app/unix/Window_unix.h b/chromium/third_party/skia/tools/sk_app/unix/Window_unix.h index b59f502eb95..62a27950980 100644 --- a/chromium/third_party/skia/tools/sk_app/unix/Window_unix.h +++ b/chromium/third_party/skia/tools/sk_app/unix/Window_unix.h @@ -20,13 +20,14 @@ namespace sk_app { class Window_unix : public Window { public: - Window_unix() : Window() - , fDisplay(nullptr) - , fWindow(0) - , fGC(nullptr) - , fFBConfig(nullptr) - , fVisualInfo(nullptr) - , fMSAASampleCount(0) {} + Window_unix() + : Window() + , fDisplay(nullptr) + , fWindow(0) + , fGC(nullptr) + , fFBConfig(nullptr) + , fVisualInfo(nullptr) + , fMSAASampleCount(1) {} ~Window_unix() override { this->closeWindow(); } bool initWindow(Display* display); diff --git a/chromium/third_party/skia/tools/sk_app/win/ANGLEWindowContext_win.cpp b/chromium/third_party/skia/tools/sk_app/win/ANGLEWindowContext_win.cpp index 649528d6cd4..452b462ba9f 100644 --- a/chromium/third_party/skia/tools/sk_app/win/ANGLEWindowContext_win.cpp +++ b/chromium/third_party/skia/tools/sk_app/win/ANGLEWindowContext_win.cpp @@ -76,7 +76,8 @@ sk_sp<const GrGLInterface> ANGLEGLWindowContext_win::onInitializeContext() { } EGLint numConfigs; fSampleCount = this->getDisplayParams().fMSAASampleCount; - const int sampleBuffers = fSampleCount > 0 ? 1 : 0; + const int sampleBuffers = fSampleCount > 1 ? 1 : 0; + const int eglSampleCnt = fSampleCount > 1 ? fSampleCount : 0; const EGLint configAttribs[] = {EGL_RENDERABLE_TYPE, // We currently only support ES3. EGL_OPENGL_ES3_BIT, @@ -91,7 +92,7 @@ sk_sp<const GrGLInterface> ANGLEGLWindowContext_win::onInitializeContext() { EGL_SAMPLE_BUFFERS, sampleBuffers, EGL_SAMPLES, - fSampleCount, + eglSampleCnt, EGL_NONE}; EGLConfig surfaceConfig; diff --git a/chromium/third_party/skia/tools/sk_app/win/GLWindowContext_win.cpp b/chromium/third_party/skia/tools/sk_app/win/GLWindowContext_win.cpp index 7e43d2b544a..1c583cb1ed9 100644 --- a/chromium/third_party/skia/tools/sk_app/win/GLWindowContext_win.cpp +++ b/chromium/third_party/skia/tools/sk_app/win/GLWindowContext_win.cpp @@ -96,8 +96,9 @@ sk_sp<const GrGLInterface> GLWindowContext_win::onInitializeContext() { 1, &kSampleCountAttr, &fSampleCount); + fSampleCount = SkTMax(fSampleCount, 1); } else { - fSampleCount = 0; + fSampleCount = 1; } RECT rect; diff --git a/chromium/third_party/skia/tools/sk_app/win/Window_win.cpp b/chromium/third_party/skia/tools/sk_app/win/Window_win.cpp index 10db0ec675c..c442493b215 100644 --- a/chromium/third_party/skia/tools/sk_app/win/Window_win.cpp +++ b/chromium/third_party/skia/tools/sk_app/win/Window_win.cpp @@ -303,8 +303,8 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) std::unique_ptr<TOUCHINPUT[]> inputs(new TOUCHINPUT[numInputs]); if (GetTouchInputInfo((HTOUCHINPUT)lParam, numInputs, inputs.get(), sizeof(TOUCHINPUT))) { - RECT rect; - GetClientRect(hWnd, &rect); + POINT topLeft = {0, 0}; + ClientToScreen(hWnd, &topLeft); for (uint16_t i = 0; i < numInputs; ++i) { TOUCHINPUT ti = inputs[i]; Window::InputState state; @@ -319,8 +319,8 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } // TOUCHINPUT coordinates are in 100ths of pixels // Adjust for that, and make them window relative - LONG tx = (ti.x / 100) - rect.left; - LONG ty = (ti.y / 100) - rect.top; + LONG tx = (ti.x / 100) - topLeft.x; + LONG ty = (ti.y / 100) - topLeft.y; eventHandled = window->onTouch(ti.dwID, state, tx, ty) || eventHandled; } } diff --git a/chromium/third_party/skia/tools/sk_pixel_iter.h b/chromium/third_party/skia/tools/sk_pixel_iter.h new file mode 100644 index 00000000000..8bf5a55647f --- /dev/null +++ b/chromium/third_party/skia/tools/sk_pixel_iter.h @@ -0,0 +1,61 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef sk_pixel_iter_DEFINED +#define sk_pixel_iter_DEFINED + +#include "SkPixmap.h" +#include "SkSurface.h" + +namespace sk_tool_utils { + + class PixelIter { + public: + PixelIter(); + PixelIter(SkSurface* surf) { + SkPixmap pm; + if (!surf->peekPixels(&pm)) { + pm.reset(); + } + this->reset(pm); + } + + void reset(const SkPixmap& pm) { + fPM = pm; + fLoc = { -1, 0 }; + } + + void* next(SkIPoint* loc = nullptr) { + if (!fPM.addr()) { + return nullptr; + } + fLoc.fX += 1; + if (fLoc.fX >= fPM.width()) { + fLoc.fX = 0; + if (++fLoc.fY >= fPM.height()) { + this->setDone(); + return nullptr; + } + } + if (loc) { + *loc = fLoc; + } + return fPM.writable_addr(fLoc.fX, fLoc.fY); + } + + void setDone() { + fPM.reset(); + } + + private: + SkPixmap fPM; + SkIPoint fLoc; + }; + +} // namespace sk_tool_utils + +#endif // sk_tool_utils_DEFINED diff --git a/chromium/third_party/skia/tools/sk_tool_utils.cpp b/chromium/third_party/skia/tools/sk_tool_utils.cpp index 9d1b4df56c8..2eb8dba8337 100644 --- a/chromium/third_party/skia/tools/sk_tool_utils.cpp +++ b/chromium/third_party/skia/tools/sk_tool_utils.cpp @@ -10,9 +10,6 @@ #include "Resources.h" #include "SkBitmap.h" #include "SkCanvas.h" -#include "SkCommonFlags.h" -#include "SkFontMgr.h" -#include "SkFontStyle.h" #include "SkImage.h" #include "SkPixelRef.h" #include "SkPM4f.h" @@ -24,88 +21,33 @@ namespace sk_tool_utils { -static const char* platform_os_name() { - for (int index = 0; index < FLAGS_key.count(); index += 2) { - if (!strcmp("os", FLAGS_key[index])) { - return FLAGS_key[index + 1]; - } - } - return ""; -} - -sk_sp<SkTypeface> emoji_typeface() { -#if defined(SK_BUILD_FOR_WIN) - sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); - const char *colorEmojiFontName = "Segoe UI Emoji"; - sk_sp<SkTypeface> typeface(fm->matchFamilyStyle(colorEmojiFontName, SkFontStyle())); - if (typeface) { - return typeface; - } - sk_sp<SkTypeface> fallback(fm->matchFamilyStyleCharacter( - colorEmojiFontName, SkFontStyle(), nullptr /* bcp47 */, 0 /* bcp47Count */, - 0x1f4b0 /* character: 💰 */)); - if (fallback) { - return fallback; - } - // If we don't have Segoe UI Emoji and can't find a fallback, try Segoe UI Symbol. - // Windows 7 does not have Segoe UI Emoji; Segoe UI Symbol has the (non - color) emoji. - return SkTypeface::MakeFromName("Segoe UI Symbol", SkFontStyle()); - -#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) - return SkTypeface::MakeFromName("Apple Color Emoji", SkFontStyle()); - -#else - return MakeResourceAsTypeface("fonts/Funkster.ttf"); - -#endif -} - -const char* emoji_sample_text() { -#if defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) - return "\xF0\x9F\x92\xB0" "\xF0\x9F\x8F\xA1" "\xF0\x9F\x8E\x85" // 💰🏡🎅 - "\xF0\x9F\x8D\xAA" "\xF0\x9F\x8D\x95" "\xF0\x9F\x9A\x80" // 🍪🍕🚀 - "\xF0\x9F\x9A\xBB" "\xF0\x9F\x92\xA9" "\xF0\x9F\x93\xB7" // 🚻💩📷 - "\xF0\x9F\x93\xA6" // 📦 - "\xF0\x9F\x87\xBA" "\xF0\x9F\x87\xB8" "\xF0\x9F\x87\xA6"; // 🇺🇸🇦 -#else - return "Hamburgefons"; -#endif -} - -static bool extra_config_contains(const char* substring) { - for (int index = 0; index < FLAGS_key.count(); index += 2) { - if (0 == strcmp("extra_config", FLAGS_key[index]) - && strstr(FLAGS_key[index + 1], substring)) { - return true; - } - } - return false; -} - -const char* platform_font_manager() { - if (extra_config_contains("GDI")) { - return "GDI"; +const char* alphatype_name(SkAlphaType at) { + switch (at) { + case kUnknown_SkAlphaType: return "Unknown"; + case kOpaque_SkAlphaType: return "Opaque"; + case kPremul_SkAlphaType: return "Premul"; + case kUnpremul_SkAlphaType: return "Unpremul"; } - if (extra_config_contains("NativeFonts")){ - return platform_os_name(); - } - return ""; + SkASSERT(false); + return "unexpected alphatype"; } - const char* colortype_name(SkColorType ct) { switch (ct) { case kUnknown_SkColorType: return "Unknown"; case kAlpha_8_SkColorType: return "Alpha_8"; - case kARGB_4444_SkColorType: return "ARGB_4444"; case kRGB_565_SkColorType: return "RGB_565"; + case kARGB_4444_SkColorType: return "ARGB_4444"; case kRGBA_8888_SkColorType: return "RGBA_8888"; + case kRGB_888x_SkColorType: return "RGB_888x"; case kBGRA_8888_SkColorType: return "BGRA_8888"; + case kRGBA_1010102_SkColorType: return "RGBA_1010102"; + case kRGB_101010x_SkColorType: return "RGB_101010x"; + case kGray_8_SkColorType: return "Gray_8"; case kRGBA_F16_SkColorType: return "RGBA_F16"; - default: - SkASSERT(false); - return "unexpected colortype"; } + SkASSERT(false); + return "unexpected colortype"; } SkColor color_to_565(SkColor color) { @@ -114,14 +56,6 @@ SkColor color_to_565(SkColor color) { return SkPixel16ToColor(color16); } -sk_sp<SkTypeface> create_portable_typeface(const char* name, SkFontStyle style) { - return create_font(name, style); -} - -void set_portable_typeface(SkPaint* paint, const char* name, SkFontStyle style) { - paint->setTypeface(create_font(name, style)); -} - void write_pixels(SkCanvas* canvas, const SkBitmap& bitmap, int x, int y, SkColorType colorType, SkAlphaType alphaType) { SkBitmap tmp(bitmap); @@ -130,6 +64,12 @@ void write_pixels(SkCanvas* canvas, const SkBitmap& bitmap, int x, int y, canvas->writePixels(info, tmp.getPixels(), tmp.rowBytes(), x, y); } +void write_pixels(SkSurface* surface, const SkBitmap& src, int x, int y, + SkColorType colorType, SkAlphaType alphaType) { + const SkImageInfo info = SkImageInfo::Make(src.width(), src.height(), colorType, alphaType); + surface->writePixels({info, src.getPixels(), src.rowBytes()}, x, y); +} + sk_sp<SkShader> create_checkerboard_shader(SkColor c1, SkColor c2, int size) { SkBitmap bm; bm.allocPixels(SkImageInfo::MakeS32(2 * size, 2 * size, kPremul_SkAlphaType)); diff --git a/chromium/third_party/skia/tools/sk_tool_utils.h b/chromium/third_party/skia/tools/sk_tool_utils.h index 9081e3c56f0..6c1adf8e262 100644 --- a/chromium/third_party/skia/tools/sk_tool_utils.h +++ b/chromium/third_party/skia/tools/sk_tool_utils.h @@ -12,6 +12,7 @@ #include "SkImageEncoder.h" #include "SkImageInfo.h" #include "SkRandom.h" +#include "SkRefCnt.h" #include "SkStream.h" #include "SkTDArray.h" #include "SkTypeface.h" @@ -31,6 +32,7 @@ class SkTextBlobBuilder; namespace sk_tool_utils { + const char* alphatype_name(SkAlphaType); const char* colortype_name(SkColorType); /** @@ -68,10 +70,11 @@ namespace sk_tool_utils { void release_portable_typefaces(); /** - * Call canvas->writePixels() by using the pixels from bitmap, but with an info that claims + * Call writePixels() by using the pixels from bitmap, but with an info that claims * the pixels are colorType + alphaType */ void write_pixels(SkCanvas*, const SkBitmap&, int x, int y, SkColorType, SkAlphaType); + void write_pixels(SkSurface*, const SkBitmap&, int x, int y, SkColorType, SkAlphaType); /** * Returns true iff all of the pixels between the two images differ by <= the maxDiff value @@ -147,7 +150,7 @@ namespace sk_tool_utils { SkRect compute_tallest_occluder(const SkRRect& rr); // A helper object to test the topological sorting code (TopoSortBench.cpp & TopoSortTest.cpp) - class TopoTestNode { + class TopoTestNode : public SkRefCnt { public: TopoTestNode(int id) : fID(id), fOutputPos(-1), fTempMark(false) { } @@ -194,37 +197,29 @@ namespace sk_tool_utils { } // Helper functions for TopoSortBench & TopoSortTest - static void AllocNodes(SkTDArray<TopoTestNode*>* graph, int num) { - graph->setReserve(num); + static void AllocNodes(SkTArray<sk_sp<sk_tool_utils::TopoTestNode>>* graph, int num) { + graph->reserve(num); for (int i = 0; i < num; ++i) { - *graph->append() = new TopoTestNode(i); + graph->push_back(sk_sp<TopoTestNode>(new TopoTestNode(i))); } } - static void DeallocNodes(SkTDArray<TopoTestNode*>* graph) { - for (int i = 0; i < graph->count(); ++i) { - delete (*graph)[i]; - } - } - - #ifdef SK_DEBUG - static void Print(const SkTDArray<TopoTestNode*>& graph) { +#ifdef SK_DEBUG + static void Print(const SkTArray<TopoTestNode*>& graph) { for (int i = 0; i < graph.count(); ++i) { SkDebugf("%d, ", graph[i]->id()); } SkDebugf("\n"); } - #endif +#endif // randomize the array - static void Shuffle(SkTDArray<TopoTestNode*>* graph, SkRandom* rand) { + static void Shuffle(SkTArray<sk_sp<TopoTestNode>>* graph, SkRandom* rand) { for (int i = graph->count()-1; i > 0; --i) { int swap = rand->nextU() % (i+1); - TopoTestNode* tmp = (*graph)[i]; - (*graph)[i] = (*graph)[swap]; - (*graph)[swap] = tmp; + (*graph)[i].swap((*graph)[swap]); } } diff --git a/chromium/third_party/skia/tools/sk_tool_utils_font.cpp b/chromium/third_party/skia/tools/sk_tool_utils_font.cpp deleted file mode 100644 index 8b2041ad1e6..00000000000 --- a/chromium/third_party/skia/tools/sk_tool_utils_font.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Resources.h" -#include "SkFontMgr.h" -#include "SkMutex.h" -#include "SkOSFile.h" -#include "SkTestScalerContext.h" -#include "SkUtils.h" -#include "sk_tool_utils.h" - -namespace sk_tool_utils { - -#include "test_font_monospace.inc" -#include "test_font_sans_serif.inc" -#include "test_font_serif.inc" -#include "test_font_index.inc" - -void release_portable_typefaces() { - for (int index = 0; index < gTestFontsCount; ++index) { - SkTestFontData& fontData = gTestFonts[index]; - fontData.fCachedFont.reset(); - } -} - -SK_DECLARE_STATIC_MUTEX(gTestFontMutex); - -sk_sp<SkTypeface> create_font(const char* name, SkFontStyle style) { - SkTestFontData* fontData = nullptr; - const SubFont* sub; - if (name) { - for (int index = 0; index < gSubFontsCount; ++index) { - sub = &gSubFonts[index]; - if (!strcmp(name, sub->fName) && sub->fStyle == style) { - fontData = &sub->fFont; - break; - } - } - if (!fontData) { - // Once all legacy callers to portable fonts are converted, replace this with - // SK_ABORT(); - SkDebugf("missing %s weight %d, width %d, slant %d\n", - name, style.weight(), style.width(), style.slant()); - // If we called SkTypeface::CreateFromName() here we'd recurse infinitely, - // so we reimplement its core logic here inline without the recursive aspect. - sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); - return fm->legacyMakeTypeface(name, style); - } - } else { - sub = &gSubFonts[gDefaultFontIndex]; - fontData = &sub->fFont; - } - sk_sp<SkTestFont> font; - { - SkAutoMutexAcquire ac(gTestFontMutex); - if (fontData->fCachedFont) { - font = fontData->fCachedFont; - } else { - font = sk_make_sp<SkTestFont>(*fontData); - fontData->fCachedFont = font; - } - } - return sk_make_sp<SkTestTypeface>(std::move(font), style); -} - -} diff --git a/chromium/third_party/skia/tools/skdiff/skdiff.h b/chromium/third_party/skia/tools/skdiff/skdiff.h index 6bdaadc28d1..1d4e8ace2a4 100644 --- a/chromium/third_party/skia/tools/skdiff/skdiff.h +++ b/chromium/third_party/skia/tools/skdiff/skdiff.h @@ -14,7 +14,7 @@ #include "SkString.h" #include "../private/SkTDArray.h" -#if defined(SK_BUILD_FOR_WIN32) +#if defined(SK_BUILD_FOR_WIN) #define PATH_DIV_STR "\\" #define PATH_DIV_CHAR '\\' #else diff --git a/chromium/third_party/skia/tools/skdiff/skdiff_html.cpp b/chromium/third_party/skia/tools/skdiff/skdiff_html.cpp index 6f3c3b09e15..e0476bc005e 100644 --- a/chromium/third_party/skia/tools/skdiff/skdiff_html.cpp +++ b/chromium/third_party/skia/tools/skdiff/skdiff_html.cpp @@ -245,7 +245,7 @@ void print_diff_page(const int matchCount, if (outputDir.size() > 0 && PATH_DIV_CHAR == outputDir[0]) { isPathAbsolute = true; } -#ifdef SK_BUILD_FOR_WIN32 +#ifdef SK_BUILD_FOR_WIN // On Windows, absolute paths can also start with "x:", where x is any // drive letter. if (outputDir.size() > 1 && ':' == outputDir[1]) { diff --git a/chromium/third_party/skia/tools/skdiff/skdiff_main.cpp b/chromium/third_party/skia/tools/skdiff/skdiff_main.cpp index 93807852802..59e9b0a6b76 100644 --- a/chromium/third_party/skia/tools/skdiff/skdiff_main.cpp +++ b/chromium/third_party/skia/tools/skdiff/skdiff_main.cpp @@ -330,16 +330,10 @@ public: SkASSERT(drp != nullptr); } ~AutoReleasePixels() { -#if 0 fDrp->fBase.fBitmap.setPixelRef(nullptr, 0, 0); fDrp->fComparison.fBitmap.setPixelRef(nullptr, 0, 0); fDrp->fDifference.fBitmap.setPixelRef(nullptr, 0, 0); fDrp->fWhite.fBitmap.setPixelRef(nullptr, 0, 0); -#endif - fDrp->fBase.fBitmap.reset(); - fDrp->fComparison.fBitmap.reset(); - fDrp->fDifference.fBitmap.reset(); - fDrp->fWhite.fBitmap.reset(); } private: diff --git a/chromium/third_party/skia/tools/skp/webpages_playback.py b/chromium/third_party/skia/tools/skp/webpages_playback.py index dad9a399290..41181607d7a 100644 --- a/chromium/third_party/skia/tools/skp/webpages_playback.py +++ b/chromium/third_party/skia/tools/skp/webpages_playback.py @@ -106,7 +106,7 @@ CHROMIUM_PAGE_SETS_TO_PREFIX = { PAGE_SETS_TO_EXCLUSIONS = { # See skbug.com/7348 - 'key_mobile_sites_smooth.py': '"(digg|worldjournal)"', + 'key_mobile_sites_smooth.py': '"(digg|worldjournal|Twitter)"', # See skbug.com/7421 'top_25_smooth.py': '"(mail\.google\.com)"', } diff --git a/chromium/third_party/skia/tools/skpbench/skpbench.cpp b/chromium/third_party/skia/tools/skpbench/skpbench.cpp index fcab2d3af8f..562c48165fe 100644 --- a/chromium/third_party/skia/tools/skpbench/skpbench.cpp +++ b/chromium/third_party/skia/tools/skpbench/skpbench.cpp @@ -292,7 +292,12 @@ int main(int argc, char** argv) { GrPixelConfig grPixConfig = SkImageInfo2GrPixelConfig(config->getColorType(), config->getColorSpace(), *ctx->caps()); - int supportedSampleCount = ctx->caps()->getSampleCount(config->getSamples(), grPixConfig); + if (kUnknown_GrPixelConfig == grPixConfig) { + exitf(ExitErr::kUnavailable, "failed to get GrPixelConfig from SkColorType: %d", + config->getColorType()); + } + int supportedSampleCount = + ctx->caps()->getRenderTargetSampleCount(config->getSamples(), grPixConfig); if (supportedSampleCount != config->getSamples()) { exitf(ExitErr::kUnavailable, "sample count %i not supported by platform", config->getSamples()); diff --git a/chromium/third_party/skia/tools/skqp/README.md b/chromium/third_party/skia/tools/skqp/README.md index e78e6edc6ed..fe2f54e9313 100644 --- a/chromium/third_party/skia/tools/skqp/README.md +++ b/chromium/third_party/skia/tools/skqp/README.md @@ -1,86 +1,104 @@ - SkQP ==== -**Motivation**: Test an Android device's GPU and OpenGLES & Vulkan drivers with -Skia and Skia's existing unit & rendering tests. +SkQP (Skia Quality Program) is a component of the Android CTS (Compatablity +Test Suite) that tests an Android device's GPU and OpenGLES & Vulkan drivers +using Skia's existing unit & rendering tests. + +See https://skia.org/dev/testing/skqp for pre-build APKs. + +How to build and run the SkQP tests +----------------------------------- + +1. Get the dependencies: + + - You will need Java JDK 8, `git`, and `python`. + + - Install Chromium's [depot\_tools](http://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html). Add it to your `PATH`. + + git clone 'https://chromium.googlesource.com/chromium/tools/depot_tools.git' + export PATH="${PWD}/depot_tools:${PATH}" + + - Install the [Android NDK](https://developer.android.com/ndk/downloads/). + + ( cd ~; unzip ~/Downloads/android-ndk-*.zip ) + ANDROID_NDK=$(ls -d ~/android-ndk-*) # Or wherever you installed the Android NDK. + + - Install the [Android SDK](https://developer.android.com/studio/#command-tools). + Set the `ANDROID_HOME` environment variable. + + mkdir ~/android-sdk + ( cd ~/android-sdk; unzip ~/Downloads/sdk-tools-*.zip ) + yes | ~/android-sdk/tools/bin/sdkmanager --licenses + export ANDROID_HOME=~/android-sdk # Or wherever you installed the Android SDK. + + Put `adb` in your `PATH`. + + export PATH="${PATH}:${ANDROID_HOME}/platform-tools" + +2. Get the right version of Skia: + + git clone https://skia.googlesource.com/skia.git + cd skia + git checkout origin/skqp/dev # or whatever release tag you need -How To Use SkQP on your Android device: +3. Download dependencies and the model: -1. To build SkQP you need to install the - [Android NDK](https://developer.android.com/ndk/). + python tools/skqp/download_model + python tools/skqp/setup_resources + python tools/git-sync-deps -2. [Checkout depot\_tools and Skia](https://skia.org/user/download), - then go to Skia's source directory: +4. Configure the build: - export PATH="${DEPOT_TOOLS_PATH}:$PATH" - cd $SKIA_SOURCE_DIRECTORY + python tools/skqp/generate_gn_args out/skqp-arm "$ANDROID_NDK" \ + --arch arm \ + --api_level 26 + bin/gn gen out/skqp-arm -3. Configure and build Skia for your device's architecture: + If you want to test another architecture, replace `arm` with `x86`, `x64` + or `arm64`. Run 'python tools/skqp/generate_gn_args -h' for + all options. - arch='arm64' # Also valid: 'arm', 'x68', 'x64' - android_ndk="${HOME}/ndk" # Or wherever you installed the NDK. - mkdir -p out/${arch}-rel - cat > out/${arch}-rel/args.gn << EOF - ndk = "$android_ndk" - ndk_api = 24 - target_cpu = "$arch" - skia_embed_resources = true - is_debug = false - EOF - tools/git-sync-deps - bin/gn gen out/${arch}-rel - ninja -C out/${arch}-rel skqp_lib +5. Build, install, and run. -4. Download meta.json from [https://goo.gl/jBw3Dd](https://goo.gl/jBw3Dd) . - This is the data used to build the validation model. + platform_tools/android/bin/android_build_app -C out/skqp-arm skqp + adb install -r out/skqp-arm/skqp.apk + adb logcat -c + adb shell am instrument -w org.skia.skqp -5. Generate the validation model data: +6. Monitor the output with: - go get go.skia.org/infra/golden/go/search - go run tools/skqp/make_gmkb.go ~/Downloads/meta.json \ - platform_tools/android/apps/skqp/src/main/assets/gmkb + adb logcat org.skia.skqp skia "*:S" -Run as an executable --------------------- + Note the test's output path on the device. It will look something like this: -1. Build the SkQP program, load files on the device, and run skqp: + 01-23 15:22:12.688 27158 27173 I org.skia.skqp: + output written to "/storage/emulated/0/Android/data/org.skia.skqp/files/output" - ninja -C out/${arch}-rel skqp - adb shell "cd /data/local/tmp; rm -rf gmkb report" - adb push platform_tools/android/apps/skqp/src/main/assets/gmkb \ - /data/local/tmp/ - adb push out/${arch}-rel/skqp /data/local/tmp/ - adb shell "cd /data/local/tmp; ./skqp gmkb report" +7. Retrieve and view the report with: -2. Get the error report if there are errors: + OUTPUT_LOCATION="/storage/emulated/0/Android/data/org.skia.skqp/files/output" + adb pull $OUTPUT_LOCATION /tmp/ + tools/skqp/sysopen.py /tmp/output/skqp_report/report.html - if adb shell test -d /data/local/tmp/report; then - adb pull /data/local/tmp/report /tmp/ - tools/skqp/sysopen.py /tmp/report/report.html - fi +Running a single test +--------------------- -Run as an APK -------------- +To run a single test, for example `gles/aarectmodes`: -0. Install the [Android SDK](https://developer.android.com/studio/#command-tools). + adb shell am instrument -e class 'org.skia.skqp.SkQPRunner#gles/aarectmodes' -w org.skia.skqp - mkdir ~/android-sdk - ( cd ~/android-sdk; unzip ~/Downloads/sdk-tools-*.zip ) - yes | ~/android-sdk/tools/bin/sdkmanager --licenses - export ANDROID_HOME=~/android-sdk +Unit tests can be run with the `unitTest/` prefix: -1. Build the skqp.apk, load it on the device, and run the tests + adb shell am instrument -e class 'org.skia.skqp.SkQPRunner#unitTest/GrSurface -w org.skia.skqp - platform_tools/android/bin/android_build_app -C out/${arch}-rel skqp - adb install -r out/${arch}-rel/skqp.apk - adb shell am instrument -w \ - org.skia.skqp/android.support.test.runner.AndroidJUnitRunner +Run as a non-APK executable +--------------------------- -2. Retrieve the report if there are any errors: +1. Follow steps 1-3 as above. - adb backup -f /tmp/skqp.ab org.skia.skqp - # Must unlock phone and verify backup. - tools/skqp/extract_report.py /tmp/skqp.ab +2. Build the SkQP program, load files on the device, and run skqp: + ninja -C out/skqp-arm skqp + python tools/skqp/run_skqp_exe out/skqp-arm diff --git a/chromium/third_party/skia/tools/skqp/download_model b/chromium/third_party/skia/tools/skqp/download_model new file mode 100755 index 00000000000..209f5505905 --- /dev/null +++ b/chromium/third_party/skia/tools/skqp/download_model @@ -0,0 +1,71 @@ +#! /usr/bin/env python + +# Copyright 2018 Google Inc. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import hashlib +import multiprocessing +import os +import shutil +import sys +import tempfile +import urllib2 + +def checksum(path): + if not os.path.exists(path): + return None + m = hashlib.md5() + with open(path, 'rb') as f: + while True: + buf = f.read(4096) + if 0 == len(buf): + return m.hexdigest() + m.update(buf) + +def download(md5, path): + if not md5 == checksum(path): + dirname = os.path.dirname(path) + if dirname and not os.path.exists(dirname): + try: + os.makedirs(dirname) + except: + # ignore race condition + if not os.path.exists(dirname): + raise + url = 'https://storage.googleapis.com/skia-skqp-assets/' + md5 + with open(path, 'wb') as o: + shutil.copyfileobj(urllib2.urlopen(url), o) + +def tmp(prefix): + fd, path = tempfile.mkstemp(prefix=prefix) + os.close(fd) + return path + +def main(): + target_dir = os.path.join('platform_tools', 'android', 'apps', 'skqp', 'src', 'main', 'assets') + os.chdir(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, target_dir)) + checksum_path = 'files.checksum' + if not os.path.isfile(checksum_path): + sys.stderr.write('Error: "%s" is missing.\n' % os.path.join(target_dir, checksum_path)) + sys.exit(1) + file_list_file = tmp('files_') + with open(checksum_path, 'r') as f: + md5 = f.read().strip() + assert(len(md5) == 32) + download(md5, file_list_file) + with open(file_list_file, 'r') as f: + records = [] + for line in f: + md5, path = line.strip().split(';', 1) + records.append((md5, path)) + sys.stderr.write('Downloading %d files.\n' % len(records)) + pool = multiprocessing.Pool(processes=multiprocessing.cpu_count() * 2) + for record in records: + pool.apply_async(download, record, callback=lambda x: sys.stderr.write('.')) + pool.close() + pool.join() + sys.stderr.write('\n') + +if __name__ == '__main__': + main() diff --git a/chromium/third_party/skia/tools/skqp/extract_report.py b/chromium/third_party/skia/tools/skqp/extract_report.py deleted file mode 100755 index 5fc3bf4ebe4..00000000000 --- a/chromium/third_party/skia/tools/skqp/extract_report.py +++ /dev/null @@ -1,29 +0,0 @@ -#! /usr/bin/env python2 -# Copyright 2017 Google Inc. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import StringIO -import os -import sys -import sysopen -import tarfile -import tempfile -import zlib - -if __name__ == '__main__': - if len(sys.argv) != 2: - print 'usage: %s FILE.ab\n' % sys.argv[0] - exit (1) - with open(sys.argv[1], 'rb') as f: - f.read(24) - t = tarfile.open(fileobj=StringIO.StringIO(zlib.decompress(f.read()))) - d = tempfile.mkdtemp(prefix='skqp_') - t.extractall(d) - p = os.path.join(d, 'apps/org.skia.skqp/f/skqp_report/report.html') - assert os.path.isfile(p) - print p - sysopen.sysopen(p) - - - diff --git a/chromium/third_party/skia/tools/skqp/generate_gn_args b/chromium/third_party/skia/tools/skqp/generate_gn_args new file mode 100755 index 00000000000..c9b1891c475 --- /dev/null +++ b/chromium/third_party/skia/tools/skqp/generate_gn_args @@ -0,0 +1,58 @@ +#! /usr/bin/env python + +# Copyright 2018 Google Inc. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import argparse +import os +import sys + +fmt = ''' +target_cpu = "{arch}" +is_debug = {debug} +ndk = "{android_ndk_dir}" +ndk_api = {api_level} +skia_enable_fontmgr_empty = true +skia_enable_pdf = false +skia_skqp_global_error_tolerance = 4 +skia_use_dng_sdk = false +skia_use_expat = false +skia_use_icu = false +skia_use_libheif = false +skia_use_lua = false +skia_use_piex = false +skia_use_skcms = false +skia_skqp_enable_driver_correctness_workarounds = {enable_workarounds} +''' + +def parse_args(): + parser = argparse.ArgumentParser(description='Generate args.gn file.') + parser.add_argument('target_build_dir') + parser.add_argument('android_ndk_dir' ) + parser.add_argument('--arch', metavar='architecture', default='arm', + help='defaults to "arm", valid values: "arm" "arm64" "x86" "x64"') + parser.add_argument('--api_level', type=int, metavar='api_level', + default=26, help='android API level, defaults to 26') + parser.add_argument('--enable_workarounds', default=False, + action='store_true', help="enable GPU work-arounds, defaults to false") + parser.add_argument('--debug', default=False, action='store_true', + help='compile native code in debug mode, defaults to false') + + # parse the args and convert bools to strings. + args = parser.parse_args() + gn_bool = lambda b : 'true' if b else 'false' + args.enable_workarounds = gn_bool(args.enable_workarounds) + args.debug = gn_bool(args.debug) + args.android_ndk_dir = os.path.abspath(args.android_ndk_dir) + return args + +def make_args_gn(out_dir, args): + if not os.path.exists(out_dir): + os.makedirs(out_dir) + with open(os.path.join(out_dir, 'args.gn'), 'w') as o: + o.write(fmt.format(**args)) + +if __name__ == '__main__': + args = parse_args() + make_args_gn(args.target_build_dir, vars(args)) diff --git a/chromium/third_party/skia/tools/skqp/gm_knowledge.cpp b/chromium/third_party/skia/tools/skqp/gm_knowledge.cpp index 05a6a5ccd0a..df1ded0660b 100644 --- a/chromium/third_party/skia/tools/skqp/gm_knowledge.cpp +++ b/chromium/third_party/skia/tools/skqp/gm_knowledge.cpp @@ -26,11 +26,17 @@ #include "skqp_asset_manager.h" +#define IMAGES_DIRECTORY_PATH "images" #define PATH_MAX_PNG "max.png" #define PATH_MIN_PNG "min.png" #define PATH_IMG_PNG "image.png" #define PATH_ERR_PNG "errors.png" #define PATH_REPORT "report.html" +#define PATH_CSV "out.csv" + +#ifndef SK_SKQP_GLOBAL_ERROR_TOLERANCE +#define SK_SKQP_GLOBAL_ERROR_TOLERANCE 0 +#endif //////////////////////////////////////////////////////////////////////////////// @@ -46,6 +52,60 @@ static int get_error(uint32_t value, uint32_t value_max, uint32_t value_min) { error = std::max(vmin - v, error); } } + return std::max(0, error - SK_SKQP_GLOBAL_ERROR_TOLERANCE); +} + +static int get_error_with_nearby(int x, int y, const SkPixmap& pm, + const SkPixmap& pm_max, const SkPixmap& pm_min) { + struct NearbyPixels { + const int x, y, w, h; + struct Iter { + const int x, y, w, h; + int8_t curr; + SkIPoint operator*() const { return this->get(); } + SkIPoint get() const { + switch (curr) { + case 0: return {x - 1, y - 1}; + case 1: return {x , y - 1}; + case 2: return {x + 1, y - 1}; + case 3: return {x - 1, y }; + case 4: return {x + 1, y }; + case 5: return {x - 1, y + 1}; + case 6: return {x , y + 1}; + case 7: return {x + 1, y + 1}; + default: SkASSERT(false); return {0, 0}; + } + } + void skipBad() { + while (curr < 8) { + SkIPoint p = this->get(); + if (p.x() >= 0 && p.y() >= 0 && p.x() < w && p.y() < h) { + return; + } + ++curr; + } + curr = -1; + } + void operator++() { + if (-1 == curr) { return; } + ++curr; + this->skipBad(); + } + bool operator!=(const Iter& other) const { return curr != other.curr; } + }; + Iter begin() const { Iter i{x, y, w, h, 0}; i.skipBad(); return i; } + Iter end() const { return Iter{x, y, w, h, -1}; } + }; + + uint32_t c = *pm.addr32(x, y); + int error = get_error(c, *pm_max.addr32(x, y), *pm_min.addr32(x, y)); + for (SkIPoint p : NearbyPixels{x, y, pm.width(), pm.height()}) { + if (error == 0) { + return 0; + } + error = SkTMin(error, get_error( + c, *pm_max.addr32(p.x(), p.y()), *pm_min.addr32(p.x(), p.y()))); + } return error; } @@ -57,13 +117,6 @@ static float set_error_code(gmkb::Error* error_out, gmkb::Error error) { return FLT_MAX; } -static SkPixmap to_pixmap(const SkBitmap& bitmap) { - SkPixmap pixmap; - SkAssertResult(bitmap.peekPixels(&pixmap)); - return pixmap; -} - - static bool WritePixmapToFile(const SkPixmap& pixmap, const char* path) { SkFILEWStream wStream(path); SkPngEncoder::Options options; @@ -79,10 +132,6 @@ static SkPixmap rgba8888_to_pixmap(const uint32_t* pixels, int width, int height return SkPixmap(info, pixels, width * sizeof(uint32_t)); } -static bool asset_exists(skqp::AssetManager* mgr, const char* path) { - return mgr && nullptr != mgr->open(path); -} - static bool copy(skqp::AssetManager* mgr, const char* path, const char* dst) { if (mgr) { if (auto stream = mgr->open(path)) { @@ -101,7 +150,7 @@ static SkBitmap ReadPngRgba8888FromFile(skqp::AssetManager* assetManager, const SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), kColorType, kAlphaType); bitmap.allocPixels(info); SkASSERT(bitmap.rowBytes() == (unsigned)bitmap.width() * sizeof(uint32_t)); - if (SkCodec::kSuccess != codec->getPixels(to_pixmap(bitmap))) { + if (SkCodec::kSuccess != codec->getPixels(bitmap.pixmap())) { bitmap.reset(); } } @@ -120,14 +169,16 @@ struct Run { static std::vector<Run> gErrors; static std::mutex gMutex; -namespace gmkb { -bool IsGoodGM(const char* name, skqp::AssetManager* assetManager) { - return asset_exists(assetManager, SkOSPath::Join(name, PATH_MAX_PNG).c_str()) - && asset_exists(assetManager, SkOSPath::Join(name, PATH_MIN_PNG).c_str()); +static SkString make_path(const SkString& images_directory, + const char* backend, + const char* gm_name, + const char* thing) { + auto path = SkStringPrintf("%s_%s_%s", backend, gm_name, thing); + return SkOSPath::Join(images_directory.c_str(), path.c_str()); } -// Assumes that for each GM foo, asset_manager has files foo/{max,min}.png and -// that the report_directory_path already exists on disk. + +namespace gmkb { float Check(const uint32_t* pixels, int width, int height, @@ -136,19 +187,24 @@ float Check(const uint32_t* pixels, skqp::AssetManager* assetManager, const char* report_directory_path, Error* error_out) { + if (report_directory_path && report_directory_path[0]) { + SkASSERT_RELEASE(sk_isdir(report_directory_path)); + } if (width <= 0 || height <= 0) { return set_error_code(error_out, Error::kBadInput); } - size_t N = (unsigned)width * (unsigned)height; - SkString max_path = SkOSPath::Join(name, PATH_MAX_PNG); - SkString min_path = SkOSPath::Join(name, PATH_MIN_PNG); + constexpr char PATH_ROOT[] = "gmkb"; + SkString img_path = SkOSPath::Join(PATH_ROOT, name); + SkString max_path = SkOSPath::Join(img_path.c_str(), PATH_MAX_PNG); + SkString min_path = SkOSPath::Join(img_path.c_str(), PATH_MIN_PNG); SkBitmap max_image = ReadPngRgba8888FromFile(assetManager, max_path.c_str()); - if (max_image.isNull()) { - return set_error_code(error_out, Error::kBadData); - } SkBitmap min_image = ReadPngRgba8888FromFile(assetManager, min_path.c_str()); - if (min_image.isNull()) { - return set_error_code(error_out, Error::kBadData); + if (max_image.isNull() || min_image.isNull()) { + // No data. + if (error_out) { + *error_out = Error::kNone; + } + return 0; } if (max_image.width() != min_image.width() || max_image.height() != min_image.height()) @@ -158,18 +214,23 @@ float Check(const uint32_t* pixels, if (max_image.width() != width || max_image.height() != height) { return set_error_code(error_out, Error::kBadInput); } + int badness = 0; int badPixelCount = 0; - const uint32_t* max_pixels = (uint32_t*)max_image.getPixels(); - const uint32_t* min_pixels = (uint32_t*)min_image.getPixels(); - - for (size_t i = 0; i < N; ++i) { - int error = get_error(pixels[i], max_pixels[i], min_pixels[i]); - if (error > 0) { - badness = SkTMax(error, badness); - ++badPixelCount; + SkPixmap pm(SkImageInfo::Make(width, height, kColorType, kAlphaType), + pixels, width * sizeof(uint32_t)); + SkPixmap pm_max = max_image.pixmap(); + SkPixmap pm_min = min_image.pixmap(); + for (int y = 0; y < pm.height(); ++y) { + for (int x = 0; x < pm.width(); ++x) { + int error = get_error_with_nearby(x, y, pm, pm_max, pm_min) ; + if (error > 0) { + badness = SkTMax(error, badness); + ++badPixelCount; + } } } + if (badness == 0) { std::lock_guard<std::mutex> lock(gMutex); gErrors.push_back(Run{SkString(backend), SkString(name), 0, 0}); @@ -178,27 +239,28 @@ float Check(const uint32_t* pixels, if (!backend) { backend = "skia"; } - SkString report_directory = SkOSPath::Join(report_directory_path, backend); - sk_mkdir(report_directory.c_str()); - SkString report_subdirectory = SkOSPath::Join(report_directory.c_str(), name); - sk_mkdir(report_subdirectory.c_str()); - SkString error_path = SkOSPath::Join(report_subdirectory.c_str(), PATH_IMG_PNG); + SkString images_directory = SkOSPath::Join(report_directory_path, IMAGES_DIRECTORY_PATH); + sk_mkdir(images_directory.c_str()); + + SkString image_path = make_path(images_directory, backend, name, PATH_IMG_PNG); + SkString error_path = make_path(images_directory, backend, name, PATH_ERR_PNG); + SkString max_path_out = make_path(images_directory, backend, name, PATH_MAX_PNG); + SkString min_path_out = make_path(images_directory, backend, name, PATH_MIN_PNG); + SkAssertResult(WritePixmapToFile(rgba8888_to_pixmap(pixels, width, height), - error_path.c_str())); + image_path.c_str())); + SkBitmap errorBitmap; errorBitmap.allocPixels(SkImageInfo::Make(width, height, kColorType, kAlphaType)); - uint32_t* errors = (uint32_t*)errorBitmap.getPixels(); - for (size_t i = 0; i < N; ++i) { - int error = get_error(pixels[i], max_pixels[i], min_pixels[i]); - errors[i] = error > 0 ? 0xFF000000 + (unsigned)error : 0x00000000; + for (int y = 0; y < pm.height(); ++y) { + for (int x = 0; x < pm.width(); ++x) { + int error = get_error_with_nearby(x, y, pm, pm_max, pm_min); + *errorBitmap.getAddr32(x, y) = + error > 0 ? 0xFF000000 + (unsigned)error : 0xFFFFFFFF; + } } - error_path = SkOSPath::Join(report_subdirectory.c_str(), PATH_ERR_PNG); - SkAssertResult(WritePixmapToFile(to_pixmap(errorBitmap), error_path.c_str())); - - SkString report_path = SkOSPath::Join(report_subdirectory.c_str(), PATH_REPORT); + SkAssertResult(WritePixmapToFile(errorBitmap.pixmap(), error_path.c_str())); - SkString max_path_out = SkOSPath::Join(report_subdirectory.c_str(), PATH_MAX_PNG); - SkString min_path_out = SkOSPath::Join(report_subdirectory.c_str(), PATH_MIN_PNG); (void)copy(assetManager, max_path.c_str(), max_path_out.c_str()); (void)copy(assetManager, min_path.c_str(), min_path_out.c_str()); @@ -211,65 +273,140 @@ float Check(const uint32_t* pixels, return (float)badness; } -bool MakeReport(const char* report_directory_path) { - std::lock_guard<std::mutex> lock(gMutex); - { - SkFILEWStream csvOut(SkOSPath::Join(report_directory_path, "out.csv").c_str()); - if (!csvOut.isValid()) { - return false; - } - for (const Run& run : gErrors) { - SkString line = SkStringPrintf("\"%s\",\"%s\",%d,%d\n", - run.fBackend.c_str(), run.fGM.c_str(), - run.fMaxerror, run.fBadpixels); - csvOut.write(line.c_str(), line.size()); - } +static constexpr char kDocHead[] = + "<!doctype html>\n" + "<html lang=\"en\">\n" + "<head>\n" + "<meta charset=\"UTF-8\">\n" + "<title>SkQP Report</title>\n" + "<style>\n" + "img { max-width:48%; border:1px green solid;\n" + " image-rendering: pixelated;\n" + " background-image:url('data:image/png;base64,iVBORw0KGgoA" + "AAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAAXNSR0IArs4c6QAAAAJiS0dEAP+H" + "j8y/AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAB3RJTUUH3gUBEi4DGRAQYgAAAB1J" + "REFUGNNjfMoAAVJQmokBDdBHgPE/lPFsYN0BABdaAwN6tehMAAAAAElFTkSuQmCC" + "'); }\n" + "</style>\n" + "<script>\n" + "function ce(t) { return document.createElement(t); }\n" + "function ct(n) { return document.createTextNode(n); }\n" + "function ac(u,v) { return u.appendChild(v); }\n" + "function br(u) { ac(u, ce(\"br\")); }\n" + "function ma(s, c) { var a = ce(\"a\"); a.href = s; ac(a, c); return a; }\n" + "function f(backend, gm, e1, e2) {\n" + " var b = ce(\"div\");\n" + " var x = ce(\"h2\");\n" + " var t = backend + \"/\" + gm;\n" + " ac(x, ct(t));\n" + " ac(b, x);\n" + " ac(b, ct(\"backend: \" + backend));\n" + " br(b);\n" + " ac(b, ct(\"gm name: \" + gm));\n" + " br(b);\n" + " ac(b, ct(\"maximum error: \" + e1));\n" + " br(b);\n" + " ac(b, ct(\"bad pixel counts: \" + e2));\n" + " br(b);\n" + " var q = \"" IMAGES_DIRECTORY_PATH "/\" + backend + \"_\" + gm + \"_\";\n" + " var i = ce(\"img\");\n" + " i.src = q + \"" PATH_IMG_PNG "\";\n" + " i.alt = \"img\";\n" + " ac(b, ma(i.src, i));\n" + " i = ce(\"img\");\n" + " i.src = q + \"" PATH_ERR_PNG "\";\n" + " i.alt = \"err\";\n" + " ac(b, ma(i.src, i));\n" + " br(b);\n" + " ac(b, ct(\"Expectation: \"));\n" + " ac(b, ma(q + \"" PATH_MAX_PNG "\", ct(\"max\")));\n" + " ac(b, ct(\" | \"));\n" + " ac(b, ma(q + \"" PATH_MIN_PNG "\", ct(\"min\")));\n" + " ac(b, ce(\"hr\"));\n" + " b.id = backend + \":\" + gm;\n" + " ac(document.body, b);\n" + " l = ce(\"li\");\n" + " ac(l, ct(\"[\" + e1 + \"] \"));\n" + " ac(l, ma(\"#\" + backend +\":\"+ gm , ct(t)));\n" + " ac(document.getElementById(\"toc\"), l);\n" + "}\n" + "function main() {\n"; + +static constexpr char kDocMiddle[] = + "}\n" + "</script>\n" + "</head>\n" + "<body onload=\"main()\">\n" + "<h1>SkQP Report</h1>\n"; + +static constexpr char kDocTail[] = + "<ul id=\"toc\"></ul>\n" + "<hr>\n" + "<p>Left image: test result<br>\n" + "Right image: errors (white = no error, black = smallest error, red = biggest error)</p>\n" + "<hr>\n" + "</body>\n" + "</html>\n"; + +static void write(SkWStream* wStream, const SkString& text) { + wStream->write(text.c_str(), text.size()); +} + +enum class Backend { + kUnknown, + kGLES, + kVulkan, +}; + +static Backend get_backend(const SkString& s) { + if (s.equals("gles")) { + return Backend::kGLES; + } else if (s.equals("vk")) { + return Backend::kVulkan; } + return Backend::kUnknown; +} + - SkFILEWStream out(SkOSPath::Join(report_directory_path, PATH_REPORT).c_str()); - if (!out.isValid()) { +bool MakeReport(const char* report_directory_path) { + int glesErrorCount = 0, vkErrorCount = 0, gles = 0, vk = 0; + + SkASSERT_RELEASE(sk_isdir(report_directory_path)); + std::lock_guard<std::mutex> lock(gMutex); + SkFILEWStream csvOut(SkOSPath::Join(report_directory_path, PATH_CSV).c_str()); + SkFILEWStream htmOut(SkOSPath::Join(report_directory_path, PATH_REPORT).c_str()); + SkASSERT_RELEASE(csvOut.isValid()); + if (!csvOut.isValid() || !htmOut.isValid()) { return false; } - out.writeText( - "<!doctype html>\n" - "<html lang=\"en\">\n" - "<head>\n" - "<meta charset=\"UTF-8\">\n" - "<title>SkQP Report</title>\n" - "<style>\n" - "img { max-width:48%; border:1px green solid; }\n" - "</style>\n" - "</head>\n" - "<body>\n" - "<h1>SkQP Report</h1>\n" - "<hr>\n"); + htmOut.writeText(kDocHead); for (const Run& run : gErrors) { - const SkString& backend = run.fBackend; - const SkString& gm = run.fGM; - int maxerror = run.fMaxerror; - int badpixels = run.fBadpixels; - if (maxerror == 0 && badpixels == 0) { + auto backend = get_backend(run.fBackend); + switch (backend) { + case Backend::kGLES: ++gles; break; + case Backend::kVulkan: ++vk; break; + default: break; + } + write(&csvOut, SkStringPrintf("\"%s\",\"%s\",%d,%d\n", + run.fBackend.c_str(), run.fGM.c_str(), + run.fMaxerror, run.fBadpixels)); + if (run.fMaxerror == 0 && run.fBadpixels == 0) { continue; } - SkString rdir = SkOSPath::Join(backend.c_str(), gm.c_str()); - SkString text = SkStringPrintf( - "<h2>%s</h2>\n" - "backend: %s\n<br>\n" - "gm name: %s\n<br>\n" - "maximum error: %d\n<br>\n" - "bad pixel counts: %d\n<br>\n" - "<a href=\"%s/" PATH_IMG_PNG "\">" - "<img src=\"%s/" PATH_IMG_PNG "\" alt='img'></a>\n" - "<a href=\"%s/" PATH_ERR_PNG "\">" - "<img src=\"%s/" PATH_ERR_PNG "\" alt='err'></a>\n<br>\n" - "<a href=\"%s/" PATH_MAX_PNG "\">max</a>\n<br>\n" - "<a href=\"%s/" PATH_MIN_PNG "\">min</a>\n<hr>\n\n", - rdir.c_str(), backend.c_str(), gm.c_str(), maxerror, badpixels, - rdir.c_str(), rdir.c_str(), rdir.c_str(), - rdir.c_str(), rdir.c_str(), rdir.c_str()); - out.write(text.c_str(), text.size()); + write(&htmOut, SkStringPrintf(" f(\"%s\", \"%s\", %d, %d);\n", + run.fBackend.c_str(), run.fGM.c_str(), + run.fMaxerror, run.fBadpixels)); + switch (backend) { + case Backend::kGLES: ++glesErrorCount; break; + case Backend::kVulkan: ++vkErrorCount; break; + default: break; + } } - out.writeText("</body>\n</html>\n"); + htmOut.writeText(kDocMiddle); + write(&htmOut, SkStringPrintf("<p>gles errors: %d (of %d)</br>\n" + "vk errors: %d (of %d)</p>\n", + glesErrorCount, gles, vkErrorCount, vk)); + htmOut.writeText(kDocTail); return true; } } // namespace gmkb diff --git a/chromium/third_party/skia/tools/skqp/gm_knowledge.h b/chromium/third_party/skia/tools/skqp/gm_knowledge.h index 25399c4c0d0..6a19a870346 100644 --- a/chromium/third_party/skia/tools/skqp/gm_knowledge.h +++ b/chromium/third_party/skia/tools/skqp/gm_knowledge.h @@ -57,16 +57,6 @@ float Check(const uint32_t* pixels, Error* error_out); /** -Check to see if the given test has expected results. - -@param name The name of a rendering test. -@param assetManager GM KnowledgeBase data files - -@return true of expected results are known for the given test. -*/ -bool IsGoodGM(const char* name, skqp::AssetManager* assetManager); - -/** Call this after running all checks. @param report_directory_path locatation to write report to. diff --git a/chromium/third_party/skia/tools/skqp/gm_runner.cpp b/chromium/third_party/skia/tools/skqp/gm_runner.cpp index 3c3885ef5c3..778c2bc6650 100644 --- a/chromium/third_party/skia/tools/skqp/gm_runner.cpp +++ b/chromium/third_party/skia/tools/skqp/gm_runner.cpp @@ -9,12 +9,13 @@ #include <algorithm> -#include "../dm/DMFontMgr.h" +#include "../tools/fonts/SkTestFontMgr.h" #include "GrContext.h" #include "GrContextOptions.h" #include "SkFontMgrPriv.h" #include "SkFontStyle.h" #include "SkGraphics.h" +#include "SkImageInfoPriv.h" #include "SkSurface.h" #include "Test.h" #include "gl/GLTestContext.h" @@ -22,6 +23,47 @@ #include "gm_knowledge.h" #include "vk/VkTestContext.h" +static SkTHashSet<SkString> gDoNotScoreInCompatibilityTestMode; +static SkTHashSet<SkString> gDoNotExecuteInExperimentalMode; +static SkTHashSet<SkString> gKnownGpuUnitTests; +static SkTHashSet<SkString> gKnownGMs; +static gm_runner::Mode gMode = gm_runner::Mode::kCompatibilityTestMode; + +static bool is_empty(const SkTHashSet<SkString>& set) { + return 0 == set.count(); +} +static bool in_set(const char* s, const SkTHashSet<SkString>& set) { + return !is_empty(set) && nullptr != set.find(SkString(s)); +} + +static void readlist(skqp::AssetManager* mgr, const char* path, SkTHashSet<SkString>* dst) { + auto asset = mgr->open(path); + if (!asset || asset->getLength() == 0) { + return; // missing file same as empty file. + } + std::vector<char> buffer(asset->getLength() + 1); + asset->read(buffer.data(), buffer.size()); + buffer.back() = '\0'; + const char* ptr = buffer.data(); + const char* end = &buffer.back(); + SkASSERT(ptr < end); + while (true) { + while (*ptr == '\n' && ptr < end) { + ++ptr; + } + if (ptr == end) { + return; + } + const char* find = strchr(ptr, '\n'); + if (!find) { + find = end; + } + SkASSERT(find > ptr); + dst->add(SkString(ptr, find - ptr)); + ptr = find; + } +} + namespace gm_runner { const char* GetErrorString(Error e) { @@ -44,6 +86,7 @@ std::vector<std::string> ExecuteTest(UnitTest test) { } } r; GrContextOptions options; + // options.fDisableDriverCorrectnessWorkarounds = true; if (test->fContextOptionsProc) { test->fContextOptionsProc(&options); } @@ -57,10 +100,15 @@ std::vector<UnitTest> GetUnitTests() { std::vector<UnitTest> tests; for (const skiatest::TestRegistry* r = skiatest::TestRegistry::Head(); r; r = r->next()) { const skiatest::Test& test = r->factory(); - if (test.needsGpu) { + if ((is_empty(gKnownGpuUnitTests) || in_set(test.name, gKnownGpuUnitTests)) + && test.needsGpu) { tests.push_back(&test); } } + struct { + bool operator()(UnitTest u, UnitTest v) const { return strcmp(u->name, v->name) < 0; } + } less; + std::sort(tests.begin(), tests.end(), less); return tests; } @@ -94,6 +142,9 @@ static GrContextOptions context_options(skiagm::GM* gm = nullptr) { GrContextOptions grContextOptions; grContextOptions.fAllowPathMaskCaching = true; grContextOptions.fSuppressPathRendering = true; + #ifndef SK_SKQP_ENABLE_DRIVER_CORRECTNESS_WORKAROUNDS + grContextOptions.fDisableDriverCorrectnessWorkarounds = true; + #endif if (gm) { gm->modifyGrContextOptions(&grContextOptions); } @@ -118,6 +169,7 @@ std::vector<SkiaBackend> GetSupportedBackends() { } } } + SkASSERT_RELEASE(result.size() > 0); return result; } @@ -167,14 +219,21 @@ std::tuple<float, Error> EvaluateGM(SkiaBackend backend, skqp::AssetManager* assetManager, const char* reportDirectoryPath) { std::vector<uint32_t> pixels; + SkASSERT(gmFact); std::unique_ptr<skiagm::GM> gm(gmFact(nullptr)); + SkASSERT(gm); + const char* name = gm->getName(); int width = 0, height = 0; if (!evaluate_gm(backend, gm.get(), &width, &height, &pixels)) { return std::make_tuple(FLT_MAX, Error::SkiaFailure); } + if (Mode::kCompatibilityTestMode == gMode && in_set(name, gDoNotScoreInCompatibilityTestMode)) { + return std::make_tuple(0, Error::None); + } + gmkb::Error e; float value = gmkb::Check(pixels.data(), width, height, - gm->getName(), GetBackendName(backend), assetManager, + name, GetBackendName(backend), assetManager, reportDirectoryPath, &e); Error error = gmkb::Error::kBadInput == e ? Error::BadSkiaOutput : gmkb::Error::kBadData == e ? Error::BadGMKBData @@ -182,19 +241,29 @@ std::tuple<float, Error> EvaluateGM(SkiaBackend backend, return std::make_tuple(value, error); } -void InitSkia() { +void InitSkia(Mode mode, skqp::AssetManager* mgr) { SkGraphics::Init(); - gSkFontMgr_DefaultFactory = &DM::MakeFontMgr; + gSkFontMgr_DefaultFactory = &sk_tool_utils::MakePortableFontMgr; + + gMode = mode; + readlist(mgr, "skqp/DoNotScoreInCompatibilityTestMode.txt", + &gDoNotScoreInCompatibilityTestMode); + readlist(mgr, "skqp/DoNotExecuteInExperimentalMode.txt", &gDoNotExecuteInExperimentalMode); + readlist(mgr, "skqp/KnownGpuUnitTests.txt", &gKnownGpuUnitTests); + readlist(mgr, "skqp/KnownGMs.txt", &gKnownGMs); } std::vector<GMFactory> GetGMFactories(skqp::AssetManager* assetManager) { std::vector<GMFactory> result; for (const skiagm::GMRegistry* r = skiagm::GMRegistry::Head(); r; r = r->next()) { GMFactory f = r->factory(); - - if (gmkb::IsGoodGM(GetGMName(f).c_str(), assetManager)) { - result.push_back(r->factory()); - SkASSERT(result.back()); + SkASSERT(f); + auto name = GetGMName(f); + if ((is_empty(gKnownGMs) || in_set(name.c_str(), gKnownGMs)) && + !(Mode::kExperimentalMode == gMode && + in_set(name.c_str(), gDoNotExecuteInExperimentalMode))) + { + result.push_back(f); } } struct { diff --git a/chromium/third_party/skia/tools/skqp/gm_runner.h b/chromium/third_party/skia/tools/skqp/gm_runner.h index 690b3714e64..2707966c9c5 100644 --- a/chromium/third_party/skia/tools/skqp/gm_runner.h +++ b/chromium/third_party/skia/tools/skqp/gm_runner.h @@ -37,10 +37,20 @@ enum class SkiaBackend { kVulkan, }; +enum class Mode { + /** This mode is set when used by Android CTS. All known tests are executed. */ + kCompatibilityTestMode, + /** This mode is set when used in the test lab. Some tests are skipped, if + they are known to cause crashes in older devices. All GMs are evaluated + with stricter requirements. */ + kExperimentalMode, + +}; + /** Initialize Skia */ -void InitSkia(); +void InitSkia(Mode, skqp::AssetManager*); std::vector<SkiaBackend> GetSupportedBackends(); diff --git a/chromium/third_party/skia/tools/skqp/gn_to_bp.py b/chromium/third_party/skia/tools/skqp/gn_to_bp.py new file mode 100644 index 00000000000..299b1c979d0 --- /dev/null +++ b/chromium/third_party/skia/tools/skqp/gn_to_bp.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python +# +# Copyright 2016 Google Inc. +# +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Generate Android.bp for Skia from GN configuration. + +import argparse +import json +import os +import pprint +import string +import subprocess +import sys +import tempfile + +root_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), + os.pardir, os.pardir) +skia_gn_dir = os.path.join(root_dir, 'gn') +sys.path.insert(0, skia_gn_dir) + +import gn_to_bp_utils + +# First we start off with a template for Android.bp, +# with holes for source lists and include directories. +bp = string.Template('''// This file is autogenerated by tools/skqp/gn_to_bp.py. + +cc_library_shared { + name: "libskqp_app", + sdk_version: "26", + stl: "libc++_static", + tags: ["tests", "optional"], + + cflags: [ + $cflags + "-Wno-unused-parameter", + "-Wno-unused-variable", + ], + + cppflags:[ + $cflags_cc + ], + + local_include_dirs: [ + $local_includes + ], + + srcs: [ + $srcs + ], + + arch: { + arm: { + srcs: [ + $arm_srcs + ], + + neon: { + srcs: [ + $arm_neon_srcs + ], + }, + }, + + arm64: { + srcs: [ + $arm64_srcs + ], + }, + + mips: { + srcs: [ + $none_srcs + ], + }, + + mips64: { + srcs: [ + $none_srcs + ], + }, + + x86: { + srcs: [ + $x86_srcs + ], + cflags: [ + // Clang seems to think new/malloc will only be 4-byte aligned + // on x86 Android. We're pretty sure it's actually 8-byte + // alignment. tests/OverAlignedTest.cpp has more information, + // and should fail if we're wrong. + "-Wno-over-aligned" + ], + }, + + x86_64: { + srcs: [ + $x86_srcs + ], + }, + }, + + shared_libs: [ + "libandroid", + "libEGL", + "libGLESv2", + "liblog", + "libvulkan", + "libz", + ], + static_libs: [ + "libjpeg_static_ndk", + "libjsoncpp", + "libpng_ndk", + "libwebp-decode", + "libwebp-encode", + ] +}''') + +# We'll run GN to get the main source lists and include directories for Skia. +gn_args = { + 'target_cpu': '"none"', + 'target_os': '"android"', + + # setup skqp + 'is_debug': 'false', + 'ndk_api': '26', + 'skia_skqp_global_error_tolerance': '4', + + # setup vulkan + 'skia_use_vulkan': 'true', + 'skia_vulkan_header': '"Skia_Vulkan_Android.h"', + + # enable/disable skia subsystems + 'skia_enable_fontmgr_empty': 'true', + 'skia_enable_pdf': 'false', + 'skia_use_expat': 'false', + 'skia_use_dng_sdk': 'false', + 'skia_use_icu': 'false', + 'skia_use_lua': 'false', + 'skia_use_piex': 'false', + 'skia_use_skcms': 'false', + + # specify that the Android.bp will supply the necessary components + 'skia_use_system_expat': 'true', # removed this when gn is fixed + 'skia_use_system_libpng': 'true', + 'skia_use_system_jsoncpp': 'true', + 'skia_use_system_libwebp': 'true', + 'skia_use_system_libjpeg_turbo': 'true', + 'skia_use_system_zlib': 'true', +} + +js = gn_to_bp_utils.GenerateJSONFromGN(gn_args) + +def strip_slashes(lst): + return {str(p.lstrip('/')) for p in lst} + +srcs = strip_slashes(js['targets']['//:libskqp_app']['sources']) +cflags = strip_slashes(js['targets']['//:libskqp_app']['cflags']) +cflags_cc = strip_slashes(js['targets']['//:libskqp_app']['cflags_cc']) +local_includes = strip_slashes(js['targets']['//:libskqp_app']['include_dirs']) +defines = {str(d) for d in js['targets']['//:libskqp_app']['defines']} + +gn_to_bp_utils.GrabDependentValues(js, '//:libskqp_app', 'sources', srcs, None) +gn_to_bp_utils.GrabDependentValues(js, '//:libskqp_app', 'include_dirs', + local_includes, 'freetype') +gn_to_bp_utils.GrabDependentValues(js, '//:libskqp_app', 'defines', + defines, None) + +# No need to list headers or other extra flags. +srcs = {s for s in srcs if not s.endswith('.h')} +cflags = gn_to_bp_utils.CleanupCFlags(cflags) +cflags_cc = gn_to_bp_utils.CleanupCCFlags(cflags_cc) + +# We need to add the include path to the vulkan defines and header file set in +# then skia_vulkan_header gn arg that is used for framework builds. +local_includes.add("platform_tools/android/vulkan") + +# Get architecture specific source files +defs = gn_to_bp_utils.GetArchSources(os.path.join(skia_gn_dir, 'opts.gni')) + +# Add source file until fix lands in +# https://skia-review.googlesource.com/c/skia/+/101820 +srcs.add("src/ports/SkFontMgr_empty_factory.cpp") + +# Turn a list of strings into the style bpfmt outputs. +def bpfmt(indent, lst, sort=True): + if sort: + lst = sorted(lst) + return ('\n' + ' '*indent).join('"%s",' % v for v in lst) + +# Most defines go into SkUserConfig.h, where they're seen by Skia and its users. +gn_to_bp_utils.WriteUserConfig('include/config/SkUserConfig.h', defines) + +# OK! We have everything to fill in Android.bp... +with open('Android.bp', 'w') as f: + print >>f, bp.substitute({ + 'local_includes': bpfmt(8, local_includes), + 'srcs': bpfmt(8, srcs), + 'cflags': bpfmt(8, cflags, False), + 'cflags_cc': bpfmt(8, cflags_cc), + + 'arm_srcs': bpfmt(16, defs['armv7']), + 'arm_neon_srcs': bpfmt(20, defs['neon']), + 'arm64_srcs': bpfmt(16, defs['arm64'] + + defs['crc32']), + 'none_srcs': bpfmt(16, defs['none']), + 'x86_srcs': bpfmt(16, defs['sse2'] + + defs['ssse3'] + + defs['sse41'] + + defs['sse42'] + + defs['avx' ]), + }) + diff --git a/chromium/third_party/skia/tools/skqp/jni/org_skia_skqp_SkQPRunner.cpp b/chromium/third_party/skia/tools/skqp/jni/org_skia_skqp_SkQPRunner.cpp index 7347f28ff9b..b59af6a8d4c 100644 --- a/chromium/third_party/skia/tools/skqp/jni/org_skia_skqp_SkQPRunner.cpp +++ b/chromium/third_party/skia/tools/skqp/jni/org_skia_skqp_SkQPRunner.cpp @@ -8,18 +8,21 @@ #include <mutex> #include <vector> -#include <jni.h> #include <android/asset_manager.h> #include <android/asset_manager_jni.h> +#include <jni.h> +#include <sys/stat.h> -#include "gm_runner.h" +#include "ResourceFactory.h" +#include "SkOSPath.h" +#include "SkStream.h" #include "gm_knowledge.h" +#include "gm_runner.h" #include "skqp_asset_manager.h" -#include "SkStream.h" //////////////////////////////////////////////////////////////////////////////// extern "C" { -JNIEXPORT void JNICALL Java_org_skia_skqp_SkQP_nInit(JNIEnv*, jobject, jobject, jstring); +JNIEXPORT void JNICALL Java_org_skia_skqp_SkQP_nInit(JNIEnv*, jobject, jobject, jstring, jboolean); JNIEXPORT jfloat JNICALL Java_org_skia_skqp_SkQP_nExecuteGM(JNIEnv*, jobject, jint, jint); JNIEXPORT jobjectArray JNICALL Java_org_skia_skqp_SkQP_nExecuteUnitTest(JNIEnv*, jobject, jint); @@ -74,13 +77,13 @@ struct AndroidAssetManager : public skqp::AssetManager { return dup; } }; + // SkDebugf("AndroidAssetManager::open(\"%s\");", path); AAsset* asset = AndroidAssetManager::OpenAsset(fMgr, path); return asset ? std::unique_ptr<SkStreamAsset>(new AAStrm(fMgr, std::string(path), asset)) : nullptr; } static AAsset* OpenAsset(AAssetManager* mgr, const char* path) { - std::string fullPath = std::string("gmkb/") + path; - return mgr ? AAssetManager_open(mgr, fullPath.c_str(), AASSET_MODE_STREAMING) : nullptr; + return mgr ? AAssetManager_open(mgr, path, AASSET_MODE_STREAMING) : nullptr; } }; } @@ -107,6 +110,25 @@ static jclass gStringClass = nullptr; //////////////////////////////////////////////////////////////////////////////// +sk_sp<SkData> get_resource(const char* resource) { + AAssetManager* mgr = gAssetManager.fMgr; + if (!mgr) { + return nullptr; + } + SkString path = SkOSPath::Join("resources", resource); + AAsset* asset = AAssetManager_open(mgr, path.c_str(), AASSET_MODE_STREAMING); + if (!asset) { + return nullptr; + } + size_t size = SkToSizeT(AAsset_getLength(asset)); + sk_sp<SkData> data = SkData::MakeUninitialized(size); + (void)AAsset_read(asset, data->writable_data(), size); + AAsset_close(asset); + return data; +} + +//////////////////////////////////////////////////////////////////////////////// + template <typename T, typename F> jobjectArray to_java_string_array(JNIEnv* env, const std::vector<T>& array, @@ -119,24 +141,35 @@ jobjectArray to_java_string_array(JNIEnv* env, } void Java_org_skia_skqp_SkQP_nInit(JNIEnv* env, jobject object, jobject assetManager, - jstring dataDir) { + jstring dataDir, jboolean experimentalMode) { jclass clazz = env->GetObjectClass(object); jassert(env, assetManager); - gm_runner::InitSkia(); - std::lock_guard<std::mutex> lock(gMutex); gAssetManager.fMgr = AAssetManager_fromJava(env, assetManager); jassert(env, gAssetManager.fMgr); + gm_runner::InitSkia(experimentalMode ? gm_runner::Mode::kExperimentalMode + : gm_runner::Mode::kCompatibilityTestMode, + &gAssetManager); + gResourceFactory = &get_resource; + const char* dataDirString = env->GetStringUTFChars(dataDir, nullptr); + jassert(env, dataDirString && dataDirString[0]); gReportDirectory = std::string(dataDirString) + "/skqp_report"; + int mkdirRetval = mkdir(gReportDirectory.c_str(), 0777); + SkASSERT_RELEASE(0 == mkdirRetval); + env->ReleaseStringUTFChars(dataDir, dataDirString); gBackends = gm_runner::GetSupportedBackends(); + jassert(env, gBackends.size() > 0); gGMs = gm_runner::GetGMFactories(&gAssetManager); + jassert(env, gGMs.size() > 0); gUnitTests = gm_runner::GetUnitTests(); + jassert(env, gUnitTests.size() > 0); gStringClass = env->FindClass("java/lang/String"); + jassert(env, gStringClass); constexpr char stringArrayType[] = "[Ljava/lang/String;"; env->SetObjectField(object, env->GetFieldID(clazz, "mBackends", stringArrayType), @@ -186,7 +219,9 @@ jobjectArray Java_org_skia_skqp_SkQP_nExecuteUnitTest(JNIEnv* env, if (errors.size() == 0) { return nullptr; } - jobjectArray array = env->NewObjectArray(errors.size(), gStringClass, nullptr); + jclass stringClass = env->FindClass("java/lang/String"); + jassert(env, stringClass); + jobjectArray array = env->NewObjectArray(errors.size(), stringClass, nullptr); for (unsigned i = 0; i < errors.size(); ++i) { set_string_array_element(env, array, errors[i].c_str(), i); } diff --git a/chromium/third_party/skia/tools/skqp/make_apk.sh b/chromium/third_party/skia/tools/skqp/make_apk.sh new file mode 100755 index 00000000000..6da2063fe35 --- /dev/null +++ b/chromium/third_party/skia/tools/skqp/make_apk.sh @@ -0,0 +1,49 @@ +#! /bin/sh + +# Copyright 2018 Google Inc. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e + +ANDROID_NDK="$1" + +if ! [ -d "$ANDROID_NDK" ] || ! [ -x "${ANDROID_NDK}/ndk-build" ]; then + printf "\nUsage:\n %s ANDROID_NDK_PATH\n" "$0" >&2 + exit 1 +fi + +case ":${PATH}:" in + */depot_tools:*) ;; + *) + printf '\ndepot_tools should be in your $PATH.\n' >&2 + exit 1;; +esac + +if ! [ -d "$ANDROID_HOME" ] || ! [ -x "${ANDROID_HOME}/platform-tools/adb" ]; then + printf '\n$ANDROID_HOME not set or is broken.\n' >&2 + exit 1 +fi + +set -x + +ARCH=${SKQP_ARCH:-arm} + +cd "$(dirname "$0")/../.." + +BUILD=out/skqp-${ARCH} + +python tools/skqp/generate_gn_args $BUILD "$ANDROID_NDK" $ARCH + +GIT_SYNC_DEPS_QUIET=Y tools/git-sync-deps + +bin/gn gen $BUILD + +rm -rf $BUILD/gen + +platform_tools/android/bin/android_build_app -C $BUILD skqp + +set +x + +printf '\n\nAPK built: "%s/skqp.apk"\n\n' "$(pwd)/$BUILD" + diff --git a/chromium/third_party/skia/tools/skqp/make_gmkb.go b/chromium/third_party/skia/tools/skqp/make_gmkb.go index f839375d837..a445f6df801 100644 --- a/chromium/third_party/skia/tools/skqp/make_gmkb.go +++ b/chromium/third_party/skia/tools/skqp/make_gmkb.go @@ -44,38 +44,14 @@ func in(v string, a []string) bool { return false } -// TODO(halcanary): clean up this blacklist. -var blacklist = []string{ - "circular-clips", - "colorcomposefilter_wacky", - "coloremoji_blendmodes", - "colormatrix", - "complexclip_bw", - "complexclip_bw_invert", - "complexclip_bw_layer", - "complexclip_bw_layer_invert", - "convex-lineonly-paths-stroke-and-fill", - "dftext", - "downsamplebitmap_image_high_mandrill_512.png", - "downsamplebitmap_image_medium_mandrill_512.png", - "filterbitmap_image_mandrill_16.png", - "filterbitmap_image_mandrill_64.png", - "filterbitmap_image_mandrill_64.png_g8", - "gradients_degenerate_2pt", - "gradients_degenerate_2pt_nodither", - "gradients_local_perspective", - "gradients_local_perspective_nodither", - "imagefilterstransformed", - "image_scale_aligned", - "lattice", - "linear_gradient", - "mipmap_srgb", - "mixedtextblobs", - "OverStroke", - "simple-offsetimagefilter", - "strokerect", - "textblobmixedsizes", - "textblobmixedsizes_df"} +func clampU8(v int) uint8 { + if v < 0 { + return 0 + } else if v > 255 { + return 255 + } + return uint8(v) +} func processTest(testName string, imgUrls []string, output string) error { if strings.ContainsRune(testName, '/') { @@ -117,6 +93,7 @@ func processTest(testName string, imgUrls []string, output string) error { if img_max.Rect.Max.X == 0 { return nil } + if err := os.Mkdir(output_directory, os.ModePerm); err != nil && !os.IsExist(err) { return err } @@ -190,9 +167,6 @@ func main() { var wg sync.WaitGroup for _, record := range records { - if in(record.TestName, blacklist) { - continue - } var goodUrls []string for _, digest := range record.Digests { if (in("vk", digest.ParamSet["config"]) || diff --git a/chromium/third_party/skia/tools/skqp/make_known_tests.sh b/chromium/third_party/skia/tools/skqp/make_known_tests.sh new file mode 100755 index 00000000000..7242db625df --- /dev/null +++ b/chromium/third_party/skia/tools/skqp/make_known_tests.sh @@ -0,0 +1,26 @@ +#! /bin/sh + +# Copyright 2018 Google Inc. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e -x + +cd "$(dirname "$0")/../.." + +BUILD=out/default + +python tools/git-sync-deps + +bin/gn gen $BUILD + +ninja -C $BUILD list_gms list_gpu_unit_tests + +DIR=platform_tools/android/apps/skqp/src/main/assets/skqp + +mkdir -p $DIR + +$BUILD/list_gms > $DIR/KnownGMs.txt + +$BUILD/list_gpu_unit_tests > $DIR/KnownGpuUnitTests.txt + diff --git a/chromium/third_party/skia/tools/skqp/make_model.sh b/chromium/third_party/skia/tools/skqp/make_model.sh new file mode 100755 index 00000000000..3946dd9e852 --- /dev/null +++ b/chromium/third_party/skia/tools/skqp/make_model.sh @@ -0,0 +1,20 @@ +#! /bin/sh + +# Copyright 2018 Google Inc. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if ! [ -f "$1" ]; then + printf 'Usage:\n %s META_JSON_FILE_PATH\n\n' "$0" >&2 + exit 1 +fi + +set -e -x + +SKIA="$(dirname "$0")/../.." + +go get -u go.skia.org/infra/golden/go/search + +go run "${SKIA}/tools/skqp/make_gmkb.go" "$1" \ + "${SKIA}/platform_tools/android/apps/skqp/src/main/assets/gmkb" + diff --git a/chromium/third_party/skia/tools/skqp/make_universal_apk b/chromium/third_party/skia/tools/skqp/make_universal_apk new file mode 100755 index 00000000000..56b725380b3 --- /dev/null +++ b/chromium/third_party/skia/tools/skqp/make_universal_apk @@ -0,0 +1,97 @@ +#! /bin/sh + +# Copyright 2018 Google Inc. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +usage() { + cat >&2 <<EOM + +This script can be run with no arguments, in which case it will produce an +APK with native libraries for all four architectures: arm, arm64, x86, and +x64. You can instead list the architectures you want as arguments to this +script. For example: + + $0 arm x86 + +The environment variables ANDROID_NDK and ANDROID_HOME must be set to the +locations of the Android NDK and SDK. Current values: + + ANDROID_NDK="$ANDROID_NDK" + ANDROID_HOME="$ANDROID_HOME" + +Additionally, \`python\` and \`ninja\` should be in your path. + +If SKQP_EXTRA_MODELS is non-empty, assets unneeded by the CTS tests will be +included for experimental mode. + +EOM + exit 1 +} + +[ -d "$ANDROID_NDK" ] || usage +[ -d "$ANDROID_HOME" ] || usage +command -v ninja > /dev/null || usage +command -v python > /dev/null || usage +for ARCH in $*; do case $ARCH in arm|arm64|x86|x64);; *) usage;; esac; done + +set -x # Verbose +set -e # Exit immediately + +cd "$(dirname "$0")/../.." + +( + cd platform_tools/android/apps + git clean -fxd skqp/build \ + skqp/src/main/assets/gmkb \ + skqp/src/main/assets/resources \ + skqp/src/main/libs \ + .gradle build viewer/build +) +python tools/skqp/download_model +if [ -z "$SKQP_EXTRA_MODELS" ]; then + python tools/skqp/remove_unneeded_assets +fi + +python tools/skqp/setup_resources +python tools/git-sync-deps + +APP=skqp +LIB=libskqp_app.so + +find platform_tools/android/apps/$APP -name $LIB -exec rm {} + + +if [ $# -eq 0 ]; then + set -- arm arm64 x86 x64 +fi + +for ARCH in $*; do + if [ "$SKQP_DEBUG" ]; then + BUILD=out/skqp-${ARCH}-debug + python tools/skqp/generate_gn_args $BUILD "$ANDROID_NDK" --arch "$ARCH" --debug + else + BUILD=out/skqp-$ARCH + python tools/skqp/generate_gn_args $BUILD "$ANDROID_NDK" --arch "$ARCH" + fi + bin/gn gen $BUILD + ninja -C $BUILD $LIB + case $ARCH in + arm) NATIVE=armeabi-v7a ;; + arm64) NATIVE=arm64-v8a ;; + x86) NATIVE=x86 ;; + x64) NATIVE=x86_64 ;; + *) usage ;; + esac + DST=platform_tools/android/apps/$APP/src/main/libs/$NATIVE + mkdir -p $DST + cp -a $BUILD/$LIB $DST/$LIB +done + +( + cd platform_tools/android + apps/gradlew -p apps/$APP -P suppressNativeBuild :$APP:assembleUniversalDebug +) + +mkdir -p out/skqp +cp platform_tools/android/apps/$APP/build/outputs/apk/$APP-universal-debug.apk out/skqp/ + diff --git a/chromium/third_party/skia/tools/skqp/remove_unneeded_assets b/chromium/third_party/skia/tools/skqp/remove_unneeded_assets new file mode 100755 index 00000000000..67bf9cff037 --- /dev/null +++ b/chromium/third_party/skia/tools/skqp/remove_unneeded_assets @@ -0,0 +1,34 @@ +#! /usr/bin/env python + +# Copyright 2018 Google Inc. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import os +import shutil +import sys + +def gset(path): + s = set() + if os.path.isfile(path): + with open(path, 'r') as f: + for line in f: + s.add(line.strip()) + return s + +def main(): + assets = os.path.join('platform_tools', 'android', 'apps', 'skqp', 'src', 'main', 'assets') + os.chdir(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, assets)) + known = gset('skqp/KnownGMs.txt') + nope = gset('skqp/DoNotScoreInCompatibilityTestMode.txt') + present = set(os.listdir('gmkb')) + to_delete = present & nope + if (known): + to_delete |= (present - known) + for x in to_delete: + shutil.rmtree(os.path.join('gmkb', x)) + sys.stdout.write('%s: %d of %d models removed\n' %(sys.argv[0], len(to_delete), len(present))) + +if __name__ == '__main__': + main() + diff --git a/chromium/third_party/skia/tools/skqp/run_skqp_exe b/chromium/third_party/skia/tools/skqp/run_skqp_exe new file mode 100755 index 00000000000..7b17425dc31 --- /dev/null +++ b/chromium/third_party/skia/tools/skqp/run_skqp_exe @@ -0,0 +1,53 @@ +#! /usr/bin/env python + +# Copyright 2018 Google Inc. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import os +import subprocess +import tempfile +import sysopen +import sys + +def skqp(build): + def adb(*args): + sys.stdout.write("adb '" + "' '".join(args) + "'\n") + subprocess.check_call(['adb'] + list(args)) + + assert os.path.isdir(build) + build = os.path.abspath(build) + + os.chdir(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)) + + adb('shell', 'rm -rf /data/local/tmp/skqp; mkdir -p /data/local/tmp/skqp') + + adb('push', + os.path.join(*'platform_tools/android/apps/skqp/src/main/assets'.split('/')), + '/data/local/tmp/skqp/skqp_assets') + + adb('push', os.path.join(build, 'skqp'), '/data/local/tmp/skqp/skqp') + + cmd = "cd /data/local/tmp/skqp; ./skqp skqp_assets report" + sys.stdout.write("adb 'shell' '%s'\n" % cmd) + ret = subprocess.call(['adb', 'shell', cmd]) + + tmpdir = tempfile.mkdtemp(prefix='skqp') + + adb('pull', "/data/local/tmp/report", tmpdir) + + return ret, os.path.join(tmpdir, 'report') + +if __name__ == '__main__': + if len(sys.argv) != 2 or not os.path.isdir(sys.argv[1]): + sys.stderr.write('Usage\n %s BUILD_DIR\n\n' % sys.argv[0]) + sys.exit(1) + try: + ret, report = skqp(sys.argv[1]) + except subprocess.CalledProcessError: + sys.stderr.write('Command failed.\n') + sys.exit(1) + + sys.stdout.write('\nReturn code: %d\nOutput written to "%s"\n' % (ret, report)) + sysopen.sysopen(os.path.join(report, 'report.html')) + diff --git a/chromium/third_party/skia/tools/skqp/setup_resources b/chromium/third_party/skia/tools/skqp/setup_resources new file mode 100755 index 00000000000..22f27a85dd3 --- /dev/null +++ b/chromium/third_party/skia/tools/skqp/setup_resources @@ -0,0 +1,20 @@ +#! /usr/bin/env python + +# Copyright 2018 Google Inc. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import os +import shutil +import sys + +if __name__ == '__main__': + skia = os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir) + dst = os.path.join(skia, 'platform_tools', 'android', 'apps', 'skqp', + 'src', 'main', 'assets', 'resources') + if os.path.isdir(dst) and not os.path.islink(dst): + shutil.rmtree(dst) + elif os.path.exists(dst): + os.remove(dst) + shutil.copytree(os.path.join(skia, 'resources'), dst) + diff --git a/chromium/third_party/skia/tools/skqp/skqp.cpp b/chromium/third_party/skia/tools/skqp/skqp.cpp index fbd4c1e0063..cba52de6be4 100644 --- a/chromium/third_party/skia/tools/skqp/skqp.cpp +++ b/chromium/third_party/skia/tools/skqp/skqp.cpp @@ -5,6 +5,8 @@ * found in the LICENSE file. */ +#include <sys/stat.h> + #include "gm_knowledge.h" #include "gm_runner.h" @@ -19,6 +21,7 @@ #pragma clang diagnostic pop #endif +#include "Resources.h" #include "SkStream.h" #include "SkString.h" @@ -92,7 +95,7 @@ static void reg_test(const char* test, const char* testCase, void register_skia_tests() { - gm_runner::InitSkia(); + gm_runner::InitSkia(gm_runner::Mode::kCompatibilityTestMode, gAssetMgr.get()); // Rendering Tests std::vector<gm_runner::SkiaBackend> backends = gm_runner::GetSupportedBackends(); @@ -132,9 +135,11 @@ int main(int argc, char** argv) { << " [GTEST_ARGUMENTS] GMKB_DIRECTORY_PATH GMKB_REPORT_PATH\n\n"; return 1; } + SetResourcePath((std::string(argv[1]) + "/resources").c_str()); gAssetMgr.reset(new StdAssetManager(argv[1])); if (argc > 2) { gReportDirectoryPath = argv[2]; + (void)mkdir(gReportDirectoryPath.c_str(), 0777); } register_skia_tests(); int ret = RUN_ALL_TESTS(); diff --git a/chromium/third_party/skia/tools/skqp/upload_model b/chromium/third_party/skia/tools/skqp/upload_model new file mode 100755 index 00000000000..cd9554c6712 --- /dev/null +++ b/chromium/third_party/skia/tools/skqp/upload_model @@ -0,0 +1,62 @@ +#! /bin/sh + +# Copyright 2018 Google Inc. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e + +BASE_DIR='platform_tools/android/apps/skqp/src/main/assets' +PATH_LIST='gmkb skqp' +BUCKET=skia-skqp-assets + +EXTANT="$(mktemp "${TMPDIR:-/tmp}/extant.XXXXXXXXXX")" +gsutil ls gs://$BUCKET/ | sed "s|^gs://$BUCKET/||" > "$EXTANT" + +upload() { + MD5=$(md5sum < "$1" | head -c 32) + if ! grep -q "$MD5" "$EXTANT"; then + URL="gs://${BUCKET}/$MD5" + gsutil cp "$1" "$URL" > /dev/null 2>&1 & + fi + echo $MD5 +} + +size() { gsutil du -s gs://$BUCKET | awk '{print $1}'; } + +cd "$(dirname "$0")/../../$BASE_DIR" + +rm -f files.checksum + +FILES="$(mktemp "${TMPDIR:-/tmp}/files.XXXXXXXXXX")" + +: > "$FILES" + +COUNT=$(find $PATH_LIST -type f | wc -l) +INDEX=1 +SHARD_COUNT=32 + +SIZE=$(size) +find $PATH_LIST -type f | sort | while IFS= read -r FILENAME; do + printf '\r %d / %d ' "$INDEX" "$COUNT" + if ! [ -f "$FILENAME" ]; then + echo error [${FILENAME}] >&2; + exit 1; + fi + case "$FILENAME" in *\;*) echo bad filename: $FILENAME >&2; exit 1;; esac + MD5=$(upload "$FILENAME") + printf '%s;%s\n' "$MD5" "$FILENAME" >> "$FILES" + + if [ $(($INDEX % $SHARD_COUNT)) = 0 ]; then + wait + fi + INDEX=$(( $INDEX + 1)) +done +printf '\rdone \n' +upload "$FILES" > files.checksum +wait + +D=$(( $(size) - $SIZE )) +printf 'Added %d bytes to %s, %d%%\n' $D $BUCKET $(( $D * 100 / $SIZE )) + +rm "$EXTANT" diff --git a/chromium/third_party/skia/tools/test_gpuveto.py b/chromium/third_party/skia/tools/test_gpuveto.py deleted file mode 100755 index 589b403b591..00000000000 --- a/chromium/third_party/skia/tools/test_gpuveto.py +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2014 Google Inc. -# -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Script to test out suitableForGpuRasterization (via gpuveto)""" - -import argparse -import glob -import os -import re -import subprocess -import sys - -# Set the PYTHONPATH to include the tools directory. -sys.path.append( - os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir)) -import find_run_binary - -def list_files(dir_or_file): - """Returns a list of all the files from the provided argument - - @param dir_or_file: either a directory or skp file - - @returns a list containing the files in the directory or a single file - """ - files = [] - for globbedpath in glob.iglob(dir_or_file): # useful on win32 - if os.path.isdir(globbedpath): - for filename in os.listdir(globbedpath): - newpath = os.path.join(globbedpath, filename) - if os.path.isfile(newpath): - files.append(newpath) - elif os.path.isfile(globbedpath): - files.append(globbedpath) - return files - - -def execute_program(args): - """Executes a process and waits for it to complete. - - @param args: is passed into subprocess.Popen(). - - @returns a tuple of the process output (returncode, output) - """ - proc = subprocess.Popen(args, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - output, _ = proc.communicate() - errcode = proc.returncode - return (errcode, output) - - -class GpuVeto(object): - - def __init__(self): - self.bench_pictures = find_run_binary.find_path_to_program( - 'bench_pictures') - sys.stdout.write('Running: %s\n' % (self.bench_pictures)) - self.gpuveto = find_run_binary.find_path_to_program('gpuveto') - assert os.path.isfile(self.bench_pictures) - assert os.path.isfile(self.gpuveto) - self.indeterminate = 0 - self.truePositives = 0 - self.falsePositives = 0 - self.trueNegatives = 0 - self.falseNegatives = 0 - - def process_skps(self, dir_or_file): - for skp in enumerate(dir_or_file): - self.process_skp(skp[1]) - - sys.stdout.write('TP %d FP %d TN %d FN %d IND %d\n' % ( - self.truePositives, - self.falsePositives, - self.trueNegatives, - self.falseNegatives, - self.indeterminate)) - - - def process_skp(self, skp_file): - assert os.path.isfile(skp_file) - #print skp_file - - # run gpuveto on the skp - args = [self.gpuveto, '-r', skp_file] - returncode, output = execute_program(args) - if (returncode != 0): - return - - if ('unsuitable' in output): - suitable = False - else: - assert 'suitable' in output - suitable = True - - # run raster config - args = [self.bench_pictures, '-r', skp_file, - '--repeat', '20', - '--timers', 'w', - '--config', '8888'] - returncode, output = execute_program(args) - if (returncode != 0): - return - - matches = re.findall('[\d]+\.[\d]+', output) - if len(matches) != 1: - return - - rasterTime = float(matches[0]) - - # run gpu config - args2 = [self.bench_pictures, '-r', skp_file, - '--repeat', '20', - '--timers', 'w', - '--config', 'gpu'] - returncode, output = execute_program(args2) - if (returncode != 0): - return - - matches = re.findall('[\d]+\.[\d]+', output) - if len(matches) != 1: - return - - gpuTime = float(matches[0]) - - # happens if page is too big it will not render - if 0 == gpuTime: - return - - tolerance = 0.05 - tol_range = tolerance * gpuTime - - - if gpuTime - tol_range < rasterTime < gpuTime + tol_range: - result = "NONE" - self.indeterminate += 1 - elif suitable: - if gpuTime < rasterTime: - self.truePositives += 1 - result = "TP" - else: - self.falsePositives += 1 - result = "FP" - else: - if gpuTime < rasterTime: - self.falseNegatives += 1 - result = "FN" - else: - self.trueNegatives += 1 - result = "TN" - - - sys.stdout.write('%s: gpuveto: %d raster %.2f gpu: %.2f Result: %s\n' - % (skp_file, suitable, rasterTime, gpuTime, result)) - -def main(main_argv): - parser = argparse.ArgumentParser() - parser.add_argument('--skp_path', - help='Path to the SKP(s). Can either be a directory ' \ - 'containing SKPs or a single SKP.', - required=True) - - args = parser.parse_args() - GpuVeto().process_skps(list_files(args.skp_path)) - -if __name__ == '__main__': - sys.exit(main(sys.argv[1])) diff --git a/chromium/third_party/skia/tools/viewer/BisectSlide.cpp b/chromium/third_party/skia/tools/viewer/BisectSlide.cpp new file mode 100644 index 00000000000..63e69d278ce --- /dev/null +++ b/chromium/third_party/skia/tools/viewer/BisectSlide.cpp @@ -0,0 +1,130 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "BisectSlide.h" + +#include "SkDOM.h" +#include "SkOSPath.h" +#include "SkPicture.h" +#include "SkStream.h" +#include "../experimental/svg/model/SkSVGDOM.h" + +sk_sp<BisectSlide> BisectSlide::Create(const char filepath[]) { + SkFILEStream stream(filepath); + if (!stream.isValid()) { + SkDebugf("BISECT: invalid input file at \"%s\"\n", filepath); + return nullptr; + } + + sk_sp<BisectSlide> bisect(new BisectSlide(filepath)); + if (bisect->fFilePath.endsWith(".svg")) { + SkDOM xml; + if (!xml.build(stream)) { + SkDebugf("BISECT: XML parsing failed: \"%s\"\n", filepath); + return nullptr; + } + sk_sp<SkSVGDOM> svg = SkSVGDOM::MakeFromDOM(xml); + if (!svg) { + SkDebugf("BISECT: couldn't load svg at \"%s\"\n", filepath); + return nullptr; + } + svg->setContainerSize(SkSize::Make(bisect->getDimensions())); + svg->render(bisect.get()); + } else { + sk_sp<SkPicture> skp = SkPicture::MakeFromStream(&stream); + if (!skp) { + SkDebugf("BISECT: couldn't load skp at \"%s\"\n", filepath); + return nullptr; + } + skp->playback(bisect.get()); + } + + return bisect; +} + +BisectSlide::BisectSlide(const char filepath[]) + : SkCanvas(4096, 4096, nullptr) + , fFilePath(filepath) { + const char* basename = strrchr(fFilePath.c_str(), SkOSPath::SEPARATOR); + fName.printf("BISECT_%s", basename ? basename + 1 : fFilePath.c_str()); +} + +// Called through SkPicture::playback only during creation. +void BisectSlide::onDrawPath(const SkPath& path, const SkPaint& paint) { + SkRect bounds; + SkIRect ibounds; + this->getTotalMatrix().mapRect(&bounds, path.getBounds()); + bounds.roundOut(&ibounds); + fDrawBounds.join(ibounds); + fFoundPaths.push_back() = {path, paint, this->getTotalMatrix()}; +} + +bool BisectSlide::onChar(SkUnichar c) { + switch (c) { + case 'X': + if (!fTossedPaths.empty()) { + SkTSwap(fFoundPaths, fTossedPaths); + if ('X' == fTrail.back()) { + fTrail.pop_back(); + } else { + fTrail.push_back('X'); + } + } + return true; + + case 'x': + if (fFoundPaths.count() > 1) { + int midpt = (fFoundPaths.count() + 1) / 2; + fPathHistory.emplace(fFoundPaths, fTossedPaths); + fTossedPaths.reset(fFoundPaths.begin() + midpt, fFoundPaths.count() - midpt); + fFoundPaths.resize_back(midpt); + fTrail.push_back('x'); + } + return true; + + case 'Z': { + if (!fPathHistory.empty()) { + fFoundPaths = fPathHistory.top().first; + fTossedPaths = fPathHistory.top().second; + fPathHistory.pop(); + char ch; + do { + ch = fTrail.back(); + fTrail.pop_back(); + } while (ch != 'x'); + } + return true; + } + + case 'D': + SkDebugf("viewer --bisect %s", fFilePath.c_str()); + if (!fTrail.empty()) { + SkDebugf(" "); + for (char ch : fTrail) { + SkDebugf("%c", ch); + } + } + SkDebugf("\n"); + for (const FoundPath& foundPath : fFoundPaths) { + foundPath.fPath.dump(); + } + return true; + } + + return false; +} + +void BisectSlide::draw(SkCanvas* canvas) { + SkAutoCanvasRestore acr(canvas, true); + canvas->translate(-fDrawBounds.left(), -fDrawBounds.top()); + + for (const FoundPath& path : fFoundPaths) { + SkAutoCanvasRestore acr(canvas, true); + canvas->concat(path.fViewMatrix); + canvas->drawPath(path.fPath, path.fPaint); + } +} diff --git a/chromium/third_party/skia/tools/viewer/BisectSlide.h b/chromium/third_party/skia/tools/viewer/BisectSlide.h new file mode 100644 index 00000000000..c5300c6396a --- /dev/null +++ b/chromium/third_party/skia/tools/viewer/BisectSlide.h @@ -0,0 +1,54 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef BisectSlide_DEFINED +#define BisectSlide_DEFINED + +#include "SkCanvas.h" +#include "SkPath.h" +#include "Slide.h" +#include <stack> + +/** + * This is a simple utility designed to extract the paths from an SKP file and then isolate a single + * one of them via bisect. Use the 'x' and 'X' keys to guide a binary search: + * + * 'x': Throw out half the paths. + * 'X': Toggle which half gets tossed and which half is kept. + * 'Z': Back up one level. + * 'D': Dump the path. + */ +class BisectSlide : public Slide, public SkCanvas { +public: + static sk_sp<BisectSlide> Create(const char filepath[]); + + // Slide overrides. + SkISize getDimensions() const override { return fDrawBounds.size(); } + bool onChar(SkUnichar c) override; + void draw(SkCanvas* canvas) override; + +private: + BisectSlide(const char filepath[]); + + // SkCanvas override called only during creation. + void onDrawPath(const SkPath& path, const SkPaint& paint) override; + + struct FoundPath { + SkPath fPath; + SkPaint fPaint; + SkMatrix fViewMatrix; + }; + + SkString fFilePath; + SkIRect fDrawBounds = SkIRect::MakeEmpty(); + SkTArray<FoundPath> fFoundPaths; + SkTArray<FoundPath> fTossedPaths; + SkTArray<char> fTrail; + std::stack<std::pair<SkTArray<FoundPath>, SkTArray<FoundPath>>> fPathHistory; +}; + +#endif diff --git a/chromium/third_party/skia/tools/viewer/SkottieSlide.cpp b/chromium/third_party/skia/tools/viewer/SkottieSlide.cpp index 8880916a16e..77501d8a9f9 100644 --- a/chromium/third_party/skia/tools/viewer/SkottieSlide.cpp +++ b/chromium/third_party/skia/tools/viewer/SkottieSlide.cpp @@ -16,9 +16,10 @@ SkottieSlide::SkottieSlide(const SkString& name, const SkString& path) fName = name; } -void SkottieSlide::load(SkScalar, SkScalar) { - fAnimation = skottie::Animation::MakeFromFile(fPath.c_str()); - fTimeBase = 0; // force a time reset +void SkottieSlide::load(SkScalar w, SkScalar h) { + fAnimation = skottie::Animation::MakeFromFile(fPath.c_str()); + fWinSize = SkSize::Make(w, h); + fTimeBase = 0; // force a time reset if (fAnimation) { fAnimation->setShowInval(fShowAnimationInval); @@ -37,13 +38,14 @@ void SkottieSlide::unload() { } SkISize SkottieSlide::getDimensions() const { - return fAnimation? fAnimation->size().toCeil() : SkISize::Make(0, 0); + // We always scale to fill the window. + return fWinSize.toCeil(); } void SkottieSlide::draw(SkCanvas* canvas) { if (fAnimation) { SkAutoCanvasRestore acr(canvas, true); - const SkRect dstR = SkRect::Make(canvas->imageInfo().bounds()); + const auto dstR = SkRect::MakeSize(fWinSize); fAnimation->render(canvas, &dstR); } } @@ -75,3 +77,16 @@ bool SkottieSlide::onChar(SkUnichar c) { return INHERITED::onChar(c); } + +bool SkottieSlide::onMouse(SkScalar x, SkScalar y, sk_app::Window::InputState state, uint32_t) { + switch (state) { + case sk_app::Window::kUp_InputState: + fShowAnimationInval = !fShowAnimationInval; + fAnimation->setShowInval(fShowAnimationInval); + break; + default: + break; + } + + return false; +} diff --git a/chromium/third_party/skia/tools/viewer/SkottieSlide.h b/chromium/third_party/skia/tools/viewer/SkottieSlide.h index 6c278d484f1..b5770a0cf84 100644 --- a/chromium/third_party/skia/tools/viewer/SkottieSlide.h +++ b/chromium/third_party/skia/tools/viewer/SkottieSlide.h @@ -11,6 +11,7 @@ #include "Slide.h" namespace skottie { class Animation; } +namespace sksg { class Scene; } class SkottieSlide : public Slide { public: @@ -26,46 +27,14 @@ public: bool animate(const SkAnimTimer&) override; bool onChar(SkUnichar) override; - -private: - SkString fPath; - std::unique_ptr<skottie::Animation> fAnimation; - SkMSec fTimeBase = 0; - bool fShowAnimationInval = false; - - typedef Slide INHERITED; -}; - -class SkottieSlide2 : public Slide { -public: - SkottieSlide2(const SkString& path); - ~SkottieSlide2() override = default; - - void load(SkScalar winWidth, SkScalar winHeight) override; - void unload() override; - - SkISize getDimensions() const override; - - void draw(SkCanvas*) override; - bool animate(const SkAnimTimer&) override; bool onMouse(SkScalar x, SkScalar y, sk_app::Window::InputState, uint32_t modifiers) override; -private: - struct Rec { - std::unique_ptr<skottie::Animation> fAnimation; - SkMSec fTimeBase = 0; - SkString fName; - bool fShowAnimationInval = false; - - Rec(std::unique_ptr<skottie::Animation> anim); - Rec(Rec&& o); - }; - - int findCell(float x, float y) const; - SkString fPath; - SkTArray<Rec> fAnims; - - int fTrackingCell = -1; +private: + SkString fPath; + sk_sp<skottie::Animation> fAnimation; + SkSize fWinSize = SkSize::MakeEmpty(); + SkMSec fTimeBase = 0; + bool fShowAnimationInval = false; typedef Slide INHERITED; }; diff --git a/chromium/third_party/skia/tools/viewer/SkottieSlide2.cpp b/chromium/third_party/skia/tools/viewer/SkottieSlide2.cpp deleted file mode 100644 index 0c643b724e9..00000000000 --- a/chromium/third_party/skia/tools/viewer/SkottieSlide2.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkottieSlide.h" - -#include "SkAnimTimer.h" -#include "SkCanvas.h" -#include "Skottie.h" -#include "SkOSFile.h" -#include "SkOSPath.h" -#include "SkStream.h" - -const int CELL_WIDTH = 240; -const int CELL_HEIGHT = 160; -const int COL_COUNT = 4; -const int SPACER_X = 12; -const int SPACER_Y = 24; -const int MARGIN = 8; - -SkottieSlide2::Rec::Rec(std::unique_ptr<skottie::Animation> anim) : fAnimation(std::move(anim)) -{} - -SkottieSlide2::Rec::Rec(Rec&& o) - : fAnimation(std::move(o.fAnimation)) - , fTimeBase(o.fTimeBase) - , fName(o.fName) - , fShowAnimationInval(o.fShowAnimationInval) -{} - -SkottieSlide2::SkottieSlide2(const SkString& path) - : fPath(path) -{ - fName.set("skottie-dir"); -} - -void SkottieSlide2::load(SkScalar, SkScalar) { - SkString name; - SkOSFile::Iter iter(fPath.c_str(), "json"); - while (iter.next(&name)) { - SkString path = SkOSPath::Join(fPath.c_str(), name.c_str()); - if (auto anim = skottie::Animation::MakeFromFile(path.c_str())) { - fAnims.push_back(Rec(std::move(anim))).fName = name; - } - } -} - -void SkottieSlide2::unload() { - fAnims.reset(); -} - -SkISize SkottieSlide2::getDimensions() const { - const int rows = (fAnims.count() + COL_COUNT - 1) / COL_COUNT; - return { - MARGIN + (COL_COUNT - 1) * SPACER_X + COL_COUNT * CELL_WIDTH + MARGIN, - MARGIN + (rows - 1) * SPACER_Y + rows * CELL_HEIGHT + MARGIN, - }; -} - -void SkottieSlide2::draw(SkCanvas* canvas) { - SkPaint paint; - paint.setTextSize(12); - paint.setAntiAlias(true); - paint.setTextAlign(SkPaint::kCenter_Align); - - const SkRect dst = SkRect::MakeIWH(CELL_WIDTH, CELL_HEIGHT); - int x = 0, y = 0; - - canvas->translate(MARGIN, MARGIN); - for (const auto& rec : fAnims) { - SkAutoCanvasRestore acr(canvas, true); - canvas->translate(x * (CELL_WIDTH + SPACER_X), y * (CELL_HEIGHT + SPACER_Y)); - canvas->drawText(rec.fName.c_str(), rec.fName.size(), - dst.centerX(), dst.bottom() + paint.getTextSize(), paint); - rec.fAnimation->render(canvas, &dst); - if (++x == COL_COUNT) { - x = 0; - y += 1; - } - } -} - -bool SkottieSlide2::animate(const SkAnimTimer& timer) { - for (auto& rec : fAnims) { - if (rec.fTimeBase == 0) { - // Reset the animation time. - rec.fTimeBase = timer.msec(); - } - rec.fAnimation->animationTick(timer.msec() - rec.fTimeBase); - } - return true; -} - -bool SkottieSlide2::onMouse(SkScalar x, SkScalar y, sk_app::Window::InputState state, - uint32_t modifiers) { - if (fTrackingCell < 0 && state == sk_app::Window::kDown_InputState) { - fTrackingCell = this->findCell(x, y); - } - if (fTrackingCell >= 0 && state == sk_app::Window::kUp_InputState) { - int index = this->findCell(x, y); - if (fTrackingCell == index) { - fAnims[index].fShowAnimationInval = !fAnims[index].fShowAnimationInval; - fAnims[index].fAnimation->setShowInval(fAnims[index].fShowAnimationInval); - } - fTrackingCell = -1; - } - return fTrackingCell >= 0; -} - -int SkottieSlide2::findCell(float x, float y) const { - x -= MARGIN; - y -= MARGIN; - int index = -1; - if (x >= 0 && y >= 0) { - int ix = (int)x; - int iy = (int)y; - int col = ix / (CELL_WIDTH + SPACER_X); - int row = iy / (CELL_HEIGHT + SPACER_Y); - index = row * COL_COUNT + col; - if (index >= fAnims.count()) { - index = -1; - } - } - return index; -} diff --git a/chromium/third_party/skia/tools/viewer/SlideDir.cpp b/chromium/third_party/skia/tools/viewer/SlideDir.cpp new file mode 100644 index 00000000000..ba3bbbae127 --- /dev/null +++ b/chromium/third_party/skia/tools/viewer/SlideDir.cpp @@ -0,0 +1,418 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SlideDir.h" + +#include "SkAnimTimer.h" +#include "SkCanvas.h" +#include "SkCubicMap.h" +#include "SkMakeUnique.h" +#include "SkSGColor.h" +#include "SkSGDraw.h" +#include "SkSGGroup.h" +#include "SkSGPlane.h" +#include "SkSGRect.h" +#include "SkSGRenderNode.h" +#include "SkSGScene.h" +#include "SkSGText.h" +#include "SkSGTransform.h" +#include "SkTypeface.h" + +#include <cmath> + +namespace { + +static constexpr float kAspectRatio = 1.5f; +static constexpr float kLabelSize = 12.0f; +static constexpr SkSize kPadding = { 12.0f , 24.0f }; + +static constexpr float kFocusDuration = 500; +static constexpr SkSize kFocusInset = { 100.0f, 100.0f }; +static constexpr SkPoint kFocusCtrl0 = { 0.3f, 1.0f }; +static constexpr SkPoint kFocusCtrl1 = { 0.0f, 1.0f }; +static constexpr SkColor kFocusShade = 0xa0000000; + +// TODO: better unfocus binding? +static constexpr SkUnichar kUnfocusKey = ' '; + +class SlideAdapter final : public sksg::RenderNode { +public: + explicit SlideAdapter(sk_sp<Slide> slide) + : fSlide(std::move(slide)) { + SkASSERT(fSlide); + } + + std::unique_ptr<sksg::Animator> makeForwardingAnimator() { + // Trivial sksg::Animator -> skottie::Animation tick adapter + class ForwardingAnimator final : public sksg::Animator { + public: + explicit ForwardingAnimator(sk_sp<SlideAdapter> adapter) + : fAdapter(std::move(adapter)) {} + + protected: + void onTick(float t) override { + fAdapter->tick(SkScalarRoundToInt(t)); + } + + private: + sk_sp<SlideAdapter> fAdapter; + }; + + return skstd::make_unique<ForwardingAnimator>(sk_ref_sp(this)); + } + +protected: + SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override { + const auto isize = fSlide->getDimensions(); + return SkRect::MakeIWH(isize.width(), isize.height()); + } + + void onRender(SkCanvas* canvas) const override { + fSlide->draw(canvas); + } + +private: + void tick(SkMSec t) { + fSlide->animate(SkAnimTimer(0, t * 1e6, SkAnimTimer::kRunning_State)); + this->invalidate(); + } + + const sk_sp<Slide> fSlide; + + using INHERITED = sksg::RenderNode; +}; + +SkMatrix SlideMatrix(const sk_sp<Slide>& slide, const SkRect& dst) { + const auto slideSize = slide->getDimensions(); + return SkMatrix::MakeRectToRect(SkRect::MakeIWH(slideSize.width(), slideSize.height()), + dst, + SkMatrix::kCenter_ScaleToFit); +} + +} // namespace + +struct SlideDir::Rec { + sk_sp<Slide> fSlide; + sk_sp<sksg::Transform> fTransform; + SkRect fRect; +}; + +class SlideDir::FocusController final : public sksg::Animator { +public: + FocusController(const SlideDir* dir, const SkRect& focusRect) + : fDir(dir) + , fRect(focusRect) + , fTarget(nullptr) + , fState(State::kIdle) { + fMap.setPts(kFocusCtrl1, kFocusCtrl0); + + fShadePaint = sksg::Color::Make(kFocusShade); + fShade = sksg::Draw::Make(sksg::Plane::Make(), fShadePaint); + } + + bool hasFocus() const { return fState == State::kFocused; } + + void startFocus(const Rec* target) { + if (fState != State::kIdle) + return; + + fTarget = target; + + // Move the shade & slide to front. + fDir->fRoot->removeChild(fTarget->fTransform); + fDir->fRoot->addChild(fShade); + fDir->fRoot->addChild(fTarget->fTransform); + + fM0 = SlideMatrix(fTarget->fSlide, fTarget->fRect); + fM1 = SlideMatrix(fTarget->fSlide, fRect); + + fOpacity0 = 0; + fOpacity1 = 1; + + fTimeBase = 0; + fState = State::kFocusing; + + // Push initial state to the scene graph. + this->onTick(fTimeBase); + } + + void startUnfocus() { + SkASSERT(fTarget); + + SkTSwap(fM0, fM1); + SkTSwap(fOpacity0, fOpacity1); + + fTimeBase = 0; + fState = State::kUnfocusing; + } + + bool onMouse(SkScalar x, SkScalar y, sk_app::Window::InputState state, uint32_t modifiers) { + SkASSERT(fTarget); + + if (!fRect.contains(x, y)) { + this->startUnfocus(); + return true; + } + + // Map coords to slide space. + const auto xform = SkMatrix::MakeRectToRect(fRect, + SkRect::MakeSize(fDir->fWinSize), + SkMatrix::kCenter_ScaleToFit); + const auto pt = xform.mapXY(x, y); + + return fTarget->fSlide->onMouse(pt.x(), pt.y(), state, modifiers); + } + + bool onChar(SkUnichar c) { + SkASSERT(fTarget); + + return fTarget->fSlide->onChar(c); + } + +protected: + void onTick(float t) { + if (!this->isAnimating()) + return; + + if (!fTimeBase) { + fTimeBase = t; + } + + const auto rel_t = (t - fTimeBase) / kFocusDuration, + map_t = SkTPin(fMap.computeYFromX(rel_t), 0.0f, 1.0f); + + SkMatrix m; + for (int i = 0; i < 9; ++i) { + m[i] = fM0[i] + map_t * (fM1[i] - fM0[i]); + } + + SkASSERT(fTarget); + fTarget->fTransform->getMatrix()->setMatrix(m); + + const auto shadeOpacity = fOpacity0 + map_t * (fOpacity1 - fOpacity0); + fShadePaint->setOpacity(shadeOpacity); + + if (rel_t < 1) + return; + + switch (fState) { + case State::kFocusing: + fState = State::kFocused; + break; + case State::kUnfocusing: + fState = State::kIdle; + fDir->fRoot->removeChild(fShade); + break; + + case State::kIdle: + case State::kFocused: + SkASSERT(false); + break; + } + } + +private: + enum class State { + kIdle, + kFocusing, + kUnfocusing, + kFocused, + }; + + bool isAnimating() const { return fState == State::kFocusing || fState == State::kUnfocusing; } + + const SlideDir* fDir; + const SkRect fRect; + const Rec* fTarget; + + SkCubicMap fMap; + sk_sp<sksg::RenderNode> fShade; + sk_sp<sksg::PaintNode> fShadePaint; + + SkMatrix fM0 = SkMatrix::I(), + fM1 = SkMatrix::I(); + float fOpacity0 = 0, + fOpacity1 = 1, + fTimeBase = 0; + State fState = State::kIdle; + + using INHERITED = sksg::Animator; +}; + +SlideDir::SlideDir(const SkString& name, SkTArray<sk_sp<Slide>, true>&& slides, int columns) + : fSlides(std::move(slides)) + , fColumns(columns) { + fName = name; +} + +static sk_sp<sksg::RenderNode> MakeLabel(const SkString& txt, + const SkPoint& pos, + const SkMatrix& dstXform) { + const auto size = kLabelSize / std::sqrt(dstXform.getScaleX() * dstXform.getScaleY()); + auto text = sksg::Text::Make(nullptr, txt); + text->setFlags(SkPaint::kAntiAlias_Flag); + text->setSize(size); + text->setAlign(SkPaint::kCenter_Align); + text->setPosition(pos + SkPoint::Make(0, size)); + + return sksg::Draw::Make(std::move(text), sksg::Color::Make(SK_ColorBLACK)); +} + +void SlideDir::load(SkScalar winWidth, SkScalar winHeight) { + // Build a global scene using transformed animation fragments: + // + // [Group(root)] + // [Transform] + // [Group] + // [AnimationWrapper] + // [Draw] + // [Text] + // [Color] + // [Transform] + // [Group] + // [AnimationWrapper] + // [Draw] + // [Text] + // [Color] + // ... + // + + fWinSize = SkSize::Make(winWidth, winHeight); + const auto cellWidth = winWidth / fColumns; + fCellSize = SkSize::Make(cellWidth, cellWidth / kAspectRatio); + + sksg::AnimatorList sceneAnimators; + fRoot = sksg::Group::Make(); + + for (int i = 0; i < fSlides.count(); ++i) { + const auto& slide = fSlides[i]; + slide->load(winWidth, winHeight); + + const auto slideSize = slide->getDimensions(); + const auto cell = SkRect::MakeXYWH(fCellSize.width() * (i % fColumns), + fCellSize.height() * (i / fColumns), + fCellSize.width(), + fCellSize.height()), + slideRect = cell.makeInset(kPadding.width(), kPadding.height()); + + auto slideMatrix = SlideMatrix(slide, slideRect); + auto adapter = sk_make_sp<SlideAdapter>(slide); + auto slideGrp = sksg::Group::Make(); + slideGrp->addChild(sksg::Draw::Make(sksg::Rect::Make(SkRect::MakeIWH(slideSize.width(), + slideSize.height())), + sksg::Color::Make(0xfff0f0f0))); + slideGrp->addChild(adapter); + slideGrp->addChild(MakeLabel(slide->getName(), + SkPoint::Make(slideSize.width() / 2, slideSize.height()), + slideMatrix)); + auto slideTransform = sksg::Transform::Make(std::move(slideGrp), slideMatrix); + + sceneAnimators.push_back(adapter->makeForwardingAnimator()); + + fRoot->addChild(slideTransform); + fRecs.push_back({ slide, slideTransform, slideRect }); + } + + fScene = sksg::Scene::Make(fRoot, std::move(sceneAnimators)); + + const auto focusRect = SkRect::MakeSize(fWinSize).makeInset(kFocusInset.width(), + kFocusInset.height()); + fFocusController = skstd::make_unique<FocusController>(this, focusRect); +} + +void SlideDir::unload() { + for (const auto& slide : fSlides) { + slide->unload(); + } + + fRecs.reset(); + fScene.reset(); + fFocusController.reset(); + fRoot.reset(); + fTimeBase = 0; +} + +SkISize SlideDir::getDimensions() const { + return SkSize::Make(fWinSize.width(), + fCellSize.height() * (1 + (fSlides.count() - 1) / fColumns)).toCeil(); +} + +void SlideDir::draw(SkCanvas* canvas) { + fScene->render(canvas); +} + +bool SlideDir::animate(const SkAnimTimer& timer) { + if (fTimeBase == 0) { + // Reset the animation time. + fTimeBase = timer.msec(); + } + + const auto t = timer.msec() - fTimeBase; + fScene->animate(t); + fFocusController->tick(t); + + return true; +} + +bool SlideDir::onChar(SkUnichar c) { + if (fFocusController->hasFocus()) { + if (c == kUnfocusKey) { + fFocusController->startUnfocus(); + return true; + } + return fFocusController->onChar(c); + } + + return false; +} + +bool SlideDir::onMouse(SkScalar x, SkScalar y, sk_app::Window::InputState state, + uint32_t modifiers) { + if (state == sk_app::Window::kMove_InputState || modifiers) + return false; + + if (fFocusController->hasFocus()) { + return fFocusController->onMouse(x, y, state, modifiers); + } + + const auto* cell = this->findCell(x, y); + if (!cell) + return false; + + static constexpr SkScalar kClickMoveTolerance = 4; + + switch (state) { + case sk_app::Window::kDown_InputState: + fTrackingCell = cell; + fTrackingPos = SkPoint::Make(x, y); + break; + case sk_app::Window::kUp_InputState: + if (cell == fTrackingCell && + SkPoint::Distance(fTrackingPos, SkPoint::Make(x, y)) < kClickMoveTolerance) { + fFocusController->startFocus(cell); + } + break; + default: + break; + } + + return false; +} + +const SlideDir::Rec* SlideDir::findCell(float x, float y) const { + // TODO: use SG hit testing instead of layout info? + const auto size = this->getDimensions(); + if (x < 0 || y < 0 || x >= size.width() || y >= size.height()) { + return nullptr; + } + + const int col = static_cast<int>(x / fCellSize.width()), + row = static_cast<int>(y / fCellSize.height()), + idx = row * fColumns + col; + + return idx < fRecs.count() ? &fRecs[idx] : nullptr; +} diff --git a/chromium/third_party/skia/tools/viewer/SlideDir.h b/chromium/third_party/skia/tools/viewer/SlideDir.h new file mode 100644 index 00000000000..f391a245993 --- /dev/null +++ b/chromium/third_party/skia/tools/viewer/SlideDir.h @@ -0,0 +1,67 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SlideDir_DEFINED +#define SlideDir_DEFINED + +#include "Slide.h" + +#include "SkTArray.h" + +class SkString; + +namespace sksg { + +class Group; +class Scene; + +} + +class SlideDir final : public Slide { +public: + SlideDir(const SkString& name, SkTArray<sk_sp<Slide>, true>&&, + int columns = kDefaultColumnCount); + +protected: + void load(SkScalar winWidth, SkScalar winHeight) override; + void unload() override; + + SkISize getDimensions() const override; + + void draw(SkCanvas*) override; + bool animate(const SkAnimTimer&) override; + + bool onChar(SkUnichar) override; + bool onMouse(SkScalar x, SkScalar y, sk_app::Window::InputState, uint32_t modifiers) override; + +private: + struct Rec; + class FocusController; + + static constexpr int kDefaultColumnCount = 4; + + const Rec* findCell(float x, float y) const; + + const SkTArray<sk_sp<Slide>, true> fSlides; + std::unique_ptr<FocusController> fFocusController; + const int fColumns; + + SkTArray<Rec, true> fRecs; + std::unique_ptr<sksg::Scene> fScene; + sk_sp<sksg::Group> fRoot; + + SkSize fWinSize = SkSize::MakeEmpty(); + SkSize fCellSize = SkSize::MakeEmpty(); + SkMSec fTimeBase = 0; + + const Rec* fTrackingCell = nullptr; + SkPoint fTrackingPos = SkPoint::Make(0, 0); + + using INHERITED = Slide; +}; + +#endif // SlideDir_DEFINED diff --git a/chromium/third_party/skia/tools/viewer/Viewer.cpp b/chromium/third_party/skia/tools/viewer/Viewer.cpp index f9d1c1b3729..8aff1cedb0d 100644 --- a/chromium/third_party/skia/tools/viewer/Viewer.cpp +++ b/chromium/third_party/skia/tools/viewer/Viewer.cpp @@ -7,29 +7,35 @@ #include "Viewer.h" +#include "BisectSlide.h" #include "GMSlide.h" #include "ImageSlide.h" #include "Resources.h" #include "SampleSlide.h" #include "SkottieSlide.h" #include "SKPSlide.h" +#include "SlideDir.h" #include "GrContext.h" #include "SkCanvas.h" #include "SkColorSpacePriv.h" #include "SkColorSpaceXformCanvas.h" +#include "SkCommonFlags.h" #include "SkCommandLineFlags.h" #include "SkCommonFlagsGpu.h" #include "SkEventTracingPriv.h" +#include "SkFontMgrPriv.h" #include "SkGraphics.h" #include "SkImagePriv.h" #include "SkOSFile.h" #include "SkOSPath.h" +#include "SkPaintFilterCanvas.h" #include "SkPictureRecorder.h" #include "SkScan.h" #include "SkStream.h" #include "SkSurface.h" #include "SkTaskGroup.h" +#include "SkTestFontMgr.h" #include "SkThreadedBMPDevice.h" #include "imgui.h" @@ -47,16 +53,6 @@ Application* Application::Create(int argc, char** argv, void* platformData) { return new Viewer(argc, argv, platformData); } -static DEFINE_string2(match, m, nullptr, - "[~][^]substring[$] [...] of bench name to run.\n" - "Multiple matches may be separated by spaces.\n" - "~ causes a matching bench to always be skipped\n" - "^ requires the start of the bench to match\n" - "$ requires the end of the bench to match\n" - "^ and $ requires an exact match\n" - "If a bench does not match any list entry,\n" - "it is skipped unless some list entry starts with ~"); - static DEFINE_string(slide, "", "Start on this sample."); static DEFINE_bool(list, false, "List samples?"); @@ -66,19 +62,11 @@ static DEFINE_bool(list, false, "List samples?"); # define BACKENDS_STR "\"sw\" and \"gl\"" #endif -#ifdef SK_BUILD_FOR_ANDROID -static DEFINE_string(skps, "/data/local/tmp/skps", "Directory to read skps from."); -static DEFINE_string(jpgs, "/data/local/tmp/resources", "Directory to read jpgs from."); -static DEFINE_string(jsons, "/data/local/tmp/jsons", "Directory to read (Bodymovin) jsons from."); -#else -static DEFINE_string(skps, "skps", "Directory to read skps from."); -static DEFINE_string(jpgs, "jpgs", "Directory to read jpgs from."); -static DEFINE_string(jsons, "jsons", "Directory to read (Bodymovin) jsons from."); -#endif - static DEFINE_string2(backend, b, "sw", "Backend to use. Allowed values are " BACKENDS_STR "."); -static DEFINE_int32(msaa, 0, "Number of subpixel samples. 0 for no HW antialiasing."); +static DEFINE_int32(msaa, 1, "Number of subpixel samples. 0 for no HW antialiasing."); + +DEFINE_string(bisect, "", "Path to a .skp or .svg file to bisect."); DECLARE_int32(threads) @@ -214,6 +202,10 @@ Viewer::Viewer(int argc, char** argv, void* platformData) SetResourcePath("/data/local/tmp/resources"); #endif + if (!FLAGS_nativeFonts) { + gSkFontMgr_DefaultFactory = &sk_tool_utils::MakePortableFontMgr; + } + initializeEventTracingForTools(); static SkTaskGroup::Enabler kTaskGroupEnabler(FLAGS_threads); @@ -315,30 +307,6 @@ Viewer::Viewer(int argc, char** argv, void* platformData) #endif this->setBackend(newBackend); }); - - fCommands.addCommand('A', "AA", "Toggle analytic AA", [this]() { - if (!gSkUseAnalyticAA) { - gSkUseAnalyticAA = true; - } else if (!gSkForceAnalyticAA) { - gSkForceAnalyticAA = true; - } else { - gSkUseAnalyticAA = gSkForceAnalyticAA = false; - } - this->updateTitle(); - fWindow->inval(); - }); - fCommands.addCommand('D', "AA", "Toggle delta AA", [this]() { - if (!gSkUseDeltaAA) { - gSkUseDeltaAA = true; - } else if (!gSkForceDeltaAA) { - gSkForceDeltaAA = true; - } else { - gSkUseDeltaAA = gSkForceDeltaAA = false; - } - this->updateTitle(); - fWindow->inval(); - }); - fCommands.addCommand('+', "Threaded Backend", "Increase tile count", [this]() { fTileCnt++; if (fThreadCnt == 0) { @@ -377,10 +345,109 @@ Viewer::Viewer(int argc, char** argv, void* platformData) fSaveToSKP = true; fWindow->inval(); }); + fCommands.addCommand('H', "Paint", "Hinting mode", [this]() { + if (!fPaintOverrides.fHinting) { + fPaintOverrides.fHinting = true; + fPaint.setHinting(SkPaint::kNo_Hinting); + } else { + switch (fPaint.getHinting()) { + case SkPaint::kNo_Hinting: + fPaint.setHinting(SkPaint::kSlight_Hinting); + break; + case SkPaint::kSlight_Hinting: + fPaint.setHinting(SkPaint::kNormal_Hinting); + break; + case SkPaint::kNormal_Hinting: + fPaint.setHinting(SkPaint::kFull_Hinting); + break; + case SkPaint::kFull_Hinting: + fPaint.setHinting(SkPaint::kNo_Hinting); + fPaintOverrides.fHinting = false; + break; + } + } + this->updateTitle(); + fWindow->inval(); + }); + fCommands.addCommand('A', "Paint", "Antialias Mode", [this]() { + if (!(fPaintOverrides.fFlags & SkPaint::kAntiAlias_Flag)) { + fPaintOverrides.fAntiAlias = SkPaintFields::AntiAliasState::Alias; + fPaintOverrides.fFlags |= SkPaint::kAntiAlias_Flag; + fPaint.setAntiAlias(false); + fPaintOverrides.fOriginalSkUseAnalyticAA = gSkUseAnalyticAA; + fPaintOverrides.fOriginalSkForceAnalyticAA = gSkForceAnalyticAA; + fPaintOverrides.fOriginalSkUseDeltaAA = gSkUseDeltaAA; + fPaintOverrides.fOriginalSkForceDeltaAA = gSkForceDeltaAA; + gSkUseAnalyticAA = gSkForceAnalyticAA = false; + gSkUseDeltaAA = gSkForceDeltaAA = false; + } else { + fPaint.setAntiAlias(true); + switch (fPaintOverrides.fAntiAlias) { + case SkPaintFields::AntiAliasState::Alias: + fPaintOverrides.fAntiAlias = SkPaintFields::AntiAliasState::Normal; + break; + case SkPaintFields::AntiAliasState::Normal: + fPaintOverrides.fAntiAlias = SkPaintFields::AntiAliasState::AnalyticAAEnabled; + gSkUseDeltaAA = gSkForceDeltaAA = false; + gSkUseAnalyticAA = true; + break; + case SkPaintFields::AntiAliasState::AnalyticAAEnabled: + fPaintOverrides.fAntiAlias = SkPaintFields::AntiAliasState::AnalyticAAForced; + gSkForceAnalyticAA = true; + break; + case SkPaintFields::AntiAliasState::AnalyticAAForced: + fPaintOverrides.fAntiAlias = SkPaintFields::AntiAliasState::DeltaAAEnabled; + gSkUseAnalyticAA = gSkForceAnalyticAA = false; + gSkUseDeltaAA = true; + break; + case SkPaintFields::AntiAliasState::DeltaAAEnabled: + fPaintOverrides.fAntiAlias = SkPaintFields::AntiAliasState::DeltaAAForced; + gSkForceDeltaAA = true; + break; + case SkPaintFields::AntiAliasState::DeltaAAForced: + fPaintOverrides.fAntiAlias = SkPaintFields::AntiAliasState::Alias; + fPaintOverrides.fFlags &= ~SkPaint::kAntiAlias_Flag; + gSkUseAnalyticAA = fPaintOverrides.fOriginalSkUseAnalyticAA; + gSkForceAnalyticAA = fPaintOverrides.fOriginalSkForceAnalyticAA; + gSkUseDeltaAA = fPaintOverrides.fOriginalSkUseDeltaAA; + gSkForceDeltaAA = fPaintOverrides.fOriginalSkForceDeltaAA; + break; + } + } + this->updateTitle(); + fWindow->inval(); + }); + fCommands.addCommand('L', "Paint", "Subpixel Antialias Mode", [this]() { + if (!(fPaintOverrides.fFlags & SkPaint::kLCDRenderText_Flag)) { + fPaintOverrides.fFlags |= SkPaint::kLCDRenderText_Flag; + fPaint.setLCDRenderText(false); + } else { + if (!fPaint.isLCDRenderText()) { + fPaint.setLCDRenderText(true); + } else { + fPaintOverrides.fFlags &= ~SkPaint::kLCDRenderText_Flag; + } + } + this->updateTitle(); + fWindow->inval(); + }); + fCommands.addCommand('S', "Paint", "Subpixel Position Mode", [this]() { + if (!(fPaintOverrides.fFlags & SkPaint::kSubpixelText_Flag)) { + fPaintOverrides.fFlags |= SkPaint::kSubpixelText_Flag; + fPaint.setSubpixelText(false); + } else { + if (!fPaint.isSubpixelText()) { + fPaint.setSubpixelText(true); + } else { + fPaintOverrides.fFlags &= ~SkPaint::kSubpixelText_Flag; + } + } + this->updateTitle(); + fWindow->inval(); + }); // set up slides this->initSlides(); - this->setCurrentSlide(this->startupSlide()); if (FLAGS_list) { this->listNames(); } @@ -395,28 +462,42 @@ Viewer::Viewer(int argc, char** argv, void* platformData) fImGuiGamutPaint.setFilterQuality(kLow_SkFilterQuality); fWindow->attach(backend_type_for_window(fBackendType)); + this->setCurrentSlide(this->startupSlide()); } void Viewer::initSlides() { fAllSlideNames = Json::Value(Json::arrayValue); + // Bisect slide. + if (!FLAGS_bisect.isEmpty()) { + sk_sp<BisectSlide> bisect = BisectSlide::Create(FLAGS_bisect[0]); + if (bisect && !SkCommandLineFlags::ShouldSkip(FLAGS_match, bisect->getName().c_str())) { + if (FLAGS_bisect.count() >= 2) { + for (const char* ch = FLAGS_bisect[1]; *ch; ++ch) { + bisect->onChar(*ch); + } + } + fSlides.push_back(std::move(bisect)); + } + } + + // GMs + int firstGM = fSlides.count(); const skiagm::GMRegistry* gms(skiagm::GMRegistry::Head()); while (gms) { std::unique_ptr<skiagm::GM> gm(gms->factory()(nullptr)); if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, gm->getName())) { sk_sp<Slide> slide(new GMSlide(gm.release())); - fSlides.push_back(slide); + fSlides.push_back(std::move(slide)); } gms = gms->next(); } - - // reverse array - for (int i = 0; i < fSlides.count()/2; ++i) { - sk_sp<Slide> temp = fSlides[i]; - fSlides[i] = fSlides[fSlides.count() - i - 1]; - fSlides[fSlides.count() - i - 1] = temp; + // reverse gms + int numGMs = fSlides.count() - firstGM; + for (int i = 0; i < numGMs/2; ++i) { + std::swap(fSlides[firstGM + i], fSlides[fSlides.count() - i - 1]); } // samples @@ -477,7 +558,7 @@ void Viewer::initSlides() { // JSONs for (const auto& json : FLAGS_jsons) { - fSlides.push_back(sk_make_sp<SkottieSlide2>(json)); + SkTArray<sk_sp<Slide>, true> dirSlides; SkOSFile::Iter it(json.c_str(), ".json"); SkString jsonName; @@ -485,8 +566,15 @@ void Viewer::initSlides() { if (SkCommandLineFlags::ShouldSkip(FLAGS_match, jsonName.c_str())) { continue; } - fSlides.push_back(sk_make_sp<SkottieSlide>(jsonName, SkOSPath::Join(json.c_str(), - jsonName.c_str()))); + auto slide = sk_make_sp<SkottieSlide>(jsonName, SkOSPath::Join(json.c_str(), + jsonName.c_str())); + dirSlides.push_back(slide); + fSlides.push_back(std::move(slide)); + } + + if (!dirSlides.empty()) { + fSlides.push_back(sk_make_sp<SlideDir>(SkStringPrintf("skottie-dir[%s]", json.c_str()), + std::move(dirSlides))); } } } @@ -497,11 +585,31 @@ Viewer::~Viewer() { delete fWindow; } +struct SkPaintTitleUpdater { + SkPaintTitleUpdater(SkString* title) : fTitle(title), fCount(0) {} + void append(const char* s) { + if (fCount == 0) { + fTitle->append(" {"); + } else { + fTitle->append(", "); + } + fTitle->append(s); + ++fCount; + } + void done() { + if (fCount > 0) { + fTitle->append("}"); + } + } + SkString* fTitle; + int fCount; +}; + void Viewer::updateTitle() { if (!fWindow) { return; } - if (fWindow->sampleCount() < 0) { + if (fWindow->sampleCount() < 1) { return; // Surface hasn't been created yet. } @@ -522,6 +630,46 @@ void Viewer::updateTitle() { } } + SkPaintTitleUpdater paintTitle(&title); + if (fPaintOverrides.fFlags & SkPaint::kAntiAlias_Flag) { + if (fPaint.isAntiAlias()) { + paintTitle.append("Antialias"); + } else { + paintTitle.append("Alias"); + } + } + if (fPaintOverrides.fFlags & SkPaint::kLCDRenderText_Flag) { + if (fPaint.isLCDRenderText()) { + paintTitle.append("LCD"); + } else { + paintTitle.append("lcd"); + } + } + if (fPaintOverrides.fFlags & SkPaint::kSubpixelText_Flag) { + if (fPaint.isSubpixelText()) { + paintTitle.append("Subpixel Glyphs"); + } else { + paintTitle.append("Pixel Glyphs"); + } + } + if (fPaintOverrides.fHinting) { + switch (fPaint.getHinting()) { + case SkPaint::kNo_Hinting: + paintTitle.append("No Hinting"); + break; + case SkPaint::kSlight_Hinting: + paintTitle.append("Slight Hinting"); + break; + case SkPaint::kNormal_Hinting: + paintTitle.append("Normal Hinting"); + break; + case SkPaint::kFull_Hinting: + paintTitle.append("Full Hinting"); + break; + } + } + paintTitle.done(); + if (fTileCnt > 0) { title.appendf(" T%d", fTileCnt); if (fThreadCnt > 0) { @@ -561,7 +709,8 @@ void Viewer::updateTitle() { title.append(" ["); title.append(kBackendTypeStrings[fBackendType]); - if (int msaa = fWindow->sampleCount()) { + int msaa = fWindow->sampleCount(); + if (msaa > 1) { title.appendf(" MSAA: %i", msaa); } title.append("]"); @@ -616,30 +765,32 @@ void Viewer::setCurrentSlide(int slide) { } void Viewer::setupCurrentSlide() { - // prepare dimensions for image slides - fGesture.resetTouchState(); - fDefaultMatrix.reset(); - - const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions(); - const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height()); - const SkRect windowRect = SkRect::MakeIWH(fWindow->width(), fWindow->height()); - - // Start with a matrix that scales the slide to the available screen space - if (fWindow->scaleContentToFit()) { - if (windowRect.width() > 0 && windowRect.height() > 0) { - fDefaultMatrix.setRectToRect(slideBounds, windowRect, SkMatrix::kStart_ScaleToFit); + if (fCurrentSlide >= 0) { + // prepare dimensions for image slides + fGesture.resetTouchState(); + fDefaultMatrix.reset(); + + const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions(); + const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height()); + const SkRect windowRect = SkRect::MakeIWH(fWindow->width(), fWindow->height()); + + // Start with a matrix that scales the slide to the available screen space + if (fWindow->scaleContentToFit()) { + if (windowRect.width() > 0 && windowRect.height() > 0) { + fDefaultMatrix.setRectToRect(slideBounds, windowRect, SkMatrix::kStart_ScaleToFit); + } } - } - // Prevent the user from dragging content so far outside the window they can't find it again - fGesture.setTransLimit(slideBounds, windowRect, fDefaultMatrix); + // Prevent the user from dragging content so far outside the window they can't find it again + fGesture.setTransLimit(slideBounds, windowRect, fDefaultMatrix); - this->updateTitle(); - this->updateUIState(); + this->updateTitle(); + this->updateUIState(); - fStatsLayer.resetMeasurements(); + fStatsLayer.resetMeasurements(); - fWindow->inval(); + fWindow->inval(); + } } #define MAX_ZOOM_LEVEL 8 @@ -711,6 +862,30 @@ void Viewer::setColorMode(ColorMode colorMode) { fWindow->inval(); } +class OveridePaintFilterCanvas : public SkPaintFilterCanvas { +public: + OveridePaintFilterCanvas(SkCanvas* canvas, SkPaint* paint, Viewer::SkPaintFields* fields) + : SkPaintFilterCanvas(canvas), fPaint(paint), fPaintOverrides(fields) + { } + bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type) const override { + if (fPaintOverrides->fHinting) { + paint->writable()->setHinting(fPaint->getHinting()); + } + if (fPaintOverrides->fFlags & SkPaint::kAntiAlias_Flag) { + paint->writable()->setAntiAlias(fPaint->isAntiAlias()); + } + if (fPaintOverrides->fFlags & SkPaint::kLCDRenderText_Flag) { + paint->writable()->setLCDRenderText(fPaint->isLCDRenderText()); + } + if (fPaintOverrides->fFlags & SkPaint::kSubpixelText_Flag) { + paint->writable()->setSubpixelText(fPaint->isSubpixelText()); + } + return true; + } + SkPaint* fPaint; + Viewer::SkPaintFields* fPaintOverrides; +}; + void Viewer::drawSlide(SkCanvas* canvas) { SkAutoCanvasRestore autorestore(canvas, false); @@ -795,7 +970,8 @@ void Viewer::drawSlide(SkCanvas* canvas) { slideCanvas->concat(computeMatrix()); // Time the painting logic of the slide fStatsLayer.beginTiming(fPaintTimer); - fSlides[fCurrentSlide]->draw(slideCanvas); + OveridePaintFilterCanvas filterCanvas(slideCanvas, &fPaint, &fPaintOverrides); + fSlides[fCurrentSlide]->draw(&filterCanvas); fStatsLayer.endTiming(fPaintTimer); slideCanvas->restoreToCount(count); @@ -819,12 +995,8 @@ void Viewer::drawSlide(SkCanvas* canvas) { } void Viewer::onBackendCreated() { - this->updateTitle(); - this->updateUIState(); this->setupCurrentSlide(); - fStatsLayer.resetMeasurements(); fWindow->show(); - fWindow->inval(); } void Viewer::onPaint(SkCanvas* canvas) { @@ -838,10 +1010,26 @@ void Viewer::onPaint(SkCanvas* canvas) { this->updateUIState(); } +SkPoint Viewer::mapEvent(float x, float y) { + const auto m = this->computeMatrix(); + SkMatrix inv; + + SkAssertResult(m.invert(&inv)); + + return inv.mapXY(x, y); +} + bool Viewer::onTouch(intptr_t owner, Window::InputState state, float x, float y) { if (GestureDevice::kMouse == fGestureDevice) { return false; } + + const auto slidePt = this->mapEvent(x, y); + if (fSlides[fCurrentSlide]->onMouse(slidePt.x(), slidePt.y(), state, 0)) { + fWindow->inval(); + return true; + } + void* castedOwner = reinterpret_cast<void*>(owner); switch (state) { case Window::kUp_InputState: { @@ -867,7 +1055,8 @@ bool Viewer::onMouse(int x, int y, Window::InputState state, uint32_t modifiers) return false; } - if (fSlides[fCurrentSlide]->onMouse(x, y, state, modifiers)) { + const auto slidePt = this->mapEvent(x, y); + if (fSlides[fCurrentSlide]->onMouse(slidePt.x(), slidePt.y(), state, modifiers)) { fWindow->inval(); return true; } @@ -996,7 +1185,7 @@ void Viewer::drawImGui() { if (ctx) { int sampleCount = fWindow->sampleCount(); ImGui::Text("MSAA: "); ImGui::SameLine(); - ImGui::RadioButton("0", &sampleCount, 0); ImGui::SameLine(); + ImGui::RadioButton("1", &sampleCount, 1); ImGui::SameLine(); ImGui::RadioButton("4", &sampleCount, 4); ImGui::SameLine(); ImGui::RadioButton("8", &sampleCount, 8); ImGui::SameLine(); ImGui::RadioButton("16", &sampleCount, 16); @@ -1020,7 +1209,7 @@ void Viewer::drawImGui() { if (!ctx) { ImGui::RadioButton("Software", true); - } else if (fWindow->sampleCount()) { + } else if (fWindow->sampleCount() > 1) { prButton(GpuPathRenderers::kDefault); prButton(GpuPathRenderers::kAll); if (ctx->caps()->shaderCaps()->pathRenderingSupport()) { @@ -1200,7 +1389,7 @@ void Viewer::updateUIState() { if (!fWindow) { return; } - if (fWindow->sampleCount() < 0) { + if (fWindow->sampleCount() < 1) { return; // Surface hasn't been created yet. } @@ -1246,7 +1435,7 @@ void Viewer::updateUIState() { const GrContext* ctx = fWindow->getGrContext(); if (!ctx) { prState[kOptions].append("Software"); - } else if (fWindow->sampleCount()) { + } else if (fWindow->sampleCount() > 1) { prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kDefault]); prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kAll]); if (ctx->caps()->shaderCaps()->pathRenderingSupport()) { diff --git a/chromium/third_party/skia/tools/viewer/Viewer.h b/chromium/third_party/skia/tools/viewer/Viewer.h index 50679524e05..0443399f9ab 100644 --- a/chromium/third_party/skia/tools/viewer/Viewer.h +++ b/chromium/third_party/skia/tools/viewer/Viewer.h @@ -37,6 +37,45 @@ public: bool onKey(sk_app::Window::Key key, sk_app::Window::InputState state, uint32_t modifiers) override; bool onChar(SkUnichar c, uint32_t modifiers) override; + struct SkPaintFields { + bool fTypeface = false; + bool fPathEffect = false; + bool fShader = false; + bool fMaskFilter = false; + bool fColorFilter = false; + bool fDrawLooper = false; + bool fImageFilter = false; + + bool fTextSize = false; + bool fTextScaleX = false; + bool fTextSkewX = false; + bool fColor = false; + bool fWidth = false; + bool fMiterLimit = false; + bool fBlendMode = false; + + uint32_t fFlags = 0; + enum class AntiAliasState { + Alias, + Normal, + AnalyticAAEnabled, + AnalyticAAForced, + DeltaAAEnabled, + DeltaAAForced, + } fAntiAlias = AntiAliasState::Alias; + bool fOriginalSkUseAnalyticAA = false; + bool fOriginalSkForceAnalyticAA = false; + bool fOriginalSkUseDeltaAA = false; + bool fOriginalSkForceDeltaAA = false; + + bool fTextAlign = false; + bool fCapType = false; + bool fJoinType = false; + bool fStyle = false; + bool fTextEncoding = false; + bool fHinting = false; + bool fFilterQuality = false; + }; private: enum class ColorMode { kLegacy, // N32, no color management @@ -61,6 +100,7 @@ private: void changeZoomLevel(float delta); SkMatrix computeMatrix(); + SkPoint mapEvent(float x, float y); void resetExecutor() { fExecutor = SkExecutor::MakeFIFOThreadPool(fThreadCnt == 0 ? fTileCnt : fThreadCnt); @@ -121,6 +161,9 @@ private: int fTileCnt; int fThreadCnt; std::unique_ptr<SkExecutor> fExecutor; + + SkPaint fPaint; + SkPaintFields fPaintOverrides; }; diff --git a/chromium/third_party/skia/tools/win_dbghelp.h b/chromium/third_party/skia/tools/win_dbghelp.h index d334318ad4b..226249f4e64 100644 --- a/chromium/third_party/skia/tools/win_dbghelp.h +++ b/chromium/third_party/skia/tools/win_dbghelp.h @@ -8,7 +8,7 @@ #ifndef win_dbghelp_DEFINED #define win_dbghelp_DEFINED -#ifdef SK_BUILD_FOR_WIN32 +#ifdef SK_BUILD_FOR_WIN #include <dbghelp.h> #include <shellapi.h> @@ -30,6 +30,6 @@ void setUpDebuggingFromArgs(const char* vargs0); int GenerateDumpAndPrintCallstack(EXCEPTION_POINTERS* pExceptionPointers); -#endif // SK_BUILD_FOR_WIN32 +#endif // SK_BUILD_FOR_WIN #endif // win_dbghelp_DEFINED diff --git a/chromium/third_party/skia/tools/xsan.blacklist b/chromium/third_party/skia/tools/xsan.blacklist deleted file mode 100644 index a917e2d3d46..00000000000 --- a/chromium/third_party/skia/tools/xsan.blacklist +++ /dev/null @@ -1,2 +0,0 @@ -# Suppress third_party/externals. We mostly care about our own code. -src:*third_party/externals* |