some refactoring ideas
- streamline source initialization so that each
kind of source only has to mention what's interesting
to it, with defaults for some obvious fields.
- have sources return a Result rather than a bool,
encapsulating Ok/Skip/Fail and any failure message,
but grab this in a middle draw() layer so each backend
doesn't have to care about anything but Ok/Skip.
Change-Id: I715278ba5e05892e42c8875bd607160b0cc594b5
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/206763
Auto-Submit: Mike Klein <mtklein@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/tools/fm/fm.cpp b/tools/fm/fm.cpp
index ab7f04a..6e385d4 100644
--- a/tools/fm/fm.cpp
+++ b/tools/fm/fm.cpp
@@ -98,119 +98,106 @@
exit(1);
}
+struct Result {
+ enum { Ok, Skip, Fail} status;
+ SkString failure;
+};
+static const Result ok = {Result::Ok, {}},
+ skip = {Result::Skip, {}};
+
+template <typename... Args>
+static Result fail(const char* why, Args... args) {
+ return { Result::Fail, SkStringPrintf(why, args...) };
+}
+
+
struct Source {
SkString name;
SkISize size;
- std::function<void(GrContextOptions*)> tweak;
- std::function<bool(SkCanvas*)> draw; // true -> ok, false -> skip;
- // failures should exit_with_failure()
+ std::function<Result(SkCanvas*)> draw;
+ std::function<void(GrContextOptions*)> tweak = [](GrContextOptions*){};
};
-static Source gm_source(std::shared_ptr<skiagm::GM> gm) {
- return {
- SkString{gm->getName()},
- gm->getISize(),
- [gm](GrContextOptions* options) { gm->modifyGrContextOptions(options); },
- [gm](SkCanvas* canvas) {
- SkString err;
- switch (gm->draw(canvas, &err)) {
- case skiagm::DrawResult::kOk: return true;
- case skiagm::DrawResult::kSkip: break;
- case skiagm::DrawResult::kFail:
- fprintf(stderr, "Drawing GM %s failed: %s\n", gm->getName(), err.c_str());
- exit_with_failure();
- }
- return false;
- },
+static void init(Source* source, std::shared_ptr<skiagm::GM> gm) {
+ source->size = gm->getISize();
+ source->tweak = [gm](GrContextOptions* options) { gm->modifyGrContextOptions(options); };
+ source->draw = [gm](SkCanvas* canvas) {
+ SkString err;
+ switch (gm->draw(canvas, &err)) {
+ case skiagm::DrawResult::kOk: break;
+ case skiagm::DrawResult::kSkip: return skip;
+ case skiagm::DrawResult::kFail: return fail(err.c_str());
+ }
+ return ok;
};
}
-static Source picture_source(SkString name, sk_sp<SkPicture> pic) {
- return {
- name,
- pic->cullRect().roundOut().size(),
- [](GrContextOptions*) {},
- [pic](SkCanvas* canvas) {
- canvas->drawPicture(pic);
- return true;
- },
+static void init(Source* source, sk_sp<SkPicture> pic) {
+ source->size = pic->cullRect().roundOut().size();
+ source->draw = [pic](SkCanvas* canvas) {
+ canvas->drawPicture(pic);
+ return ok;
};
}
-static Source codec_source(SkString name, std::shared_ptr<SkCodec> codec) {
- return {
- name,
- codec->dimensions(),
- [](GrContextOptions*) {},
- [codec](SkCanvas* canvas) {
- SkImageInfo info = codec->getInfo();
- if (FLAGS_decodeToDst) {
- info = canvas->imageInfo().makeWH(info.width(),
- info.height());
- }
+static void init(Source* source, std::shared_ptr<SkCodec> codec) {
+ source->size = codec->dimensions();
+ source->draw = [codec](SkCanvas* canvas) {
+ SkImageInfo info = codec->getInfo();
+ if (FLAGS_decodeToDst) {
+ info = canvas->imageInfo().makeWH(info.width(),
+ info.height());
+ }
- SkBitmap bm;
- bm.allocPixels(info);
-
- switch (auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes())) {
- case SkCodec::kSuccess:
- case SkCodec::kErrorInInput:
- case SkCodec::kIncompleteInput:
- canvas->drawBitmap(bm, 0,0);
- return true;
- default:
- fprintf(stderr, "SkCodec::getPixels failed: %d.", result);
- exit_with_failure();
- }
- return false;
- },
+ SkBitmap bm;
+ bm.allocPixels(info);
+ switch (SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes())) {
+ case SkCodec::kSuccess:
+ case SkCodec::kErrorInInput:
+ case SkCodec::kIncompleteInput: canvas->drawBitmap(bm, 0,0);
+ break;
+ default: return fail("codec->getPixels() failed: %d\n", result);
+ }
+ return ok;
};
}
-static Source svg_source(SkString name, sk_sp<SkSVGDOM> svg) {
- return {
- name,
- svg->containerSize().isEmpty() ? SkISize{1000,1000}
- : svg->containerSize().toCeil(),
- [](GrContextOptions*) {},
- [svg](SkCanvas* canvas) {
- svg->render(canvas);
- return true;
- },
+static void init(Source* source, sk_sp<SkSVGDOM> svg) {
+ source->size = svg->containerSize().isEmpty() ? SkISize{1000,1000}
+ : svg->containerSize().toCeil();
+ source->draw = [svg](SkCanvas* canvas) {
+ svg->render(canvas);
+ return ok;
};
}
-static Source skottie_source(SkString name, sk_sp<skottie::Animation> animation) {
- return {
- name,
- {1000,1000},
- [](GrContextOptions*) {},
- [animation](SkCanvas* canvas) {
- canvas->clear(SK_ColorWHITE);
+static void init(Source* source, sk_sp<skottie::Animation> animation) {
+ source->size = {1000,1000};
+ source->draw = [animation](SkCanvas* canvas) {
+ canvas->clear(SK_ColorWHITE);
- // Draw frames in a shuffled order to exercise nonlinear frame progression.
- // The film strip will still be in time order, just drawn out of order.
- const int order[] = { 4, 0, 3, 1, 2 };
- const int tiles = SK_ARRAY_COUNT(order);
- const float dim = 1000.0f / tiles;
+ // Draw frames in a shuffled order to exercise nonlinear frame progression.
+ // The film strip will still be in time order, just drawn out of order.
+ const int order[] = { 4, 0, 3, 1, 2 };
+ const int tiles = SK_ARRAY_COUNT(order);
+ const float dim = 1000.0f / tiles;
- const float dt = 1.0f / (tiles*tiles - 1);
+ const float dt = 1.0f / (tiles*tiles - 1);
- for (int y : order)
- for (int x : order) {
- SkRect dst = {x*dim, y*dim, (x+1)*dim, (y+1)*dim};
+ for (int y : order)
+ for (int x : order) {
+ SkRect dst = {x*dim, y*dim, (x+1)*dim, (y+1)*dim};
- SkAutoCanvasRestore _(canvas, true/*save now*/);
- canvas->clipRect(dst, /*aa=*/true);
- canvas->concat(SkMatrix::MakeRectToRect(SkRect::MakeSize(animation->size()),
- dst,
- SkMatrix::kCenter_ScaleToFit));
- float t = (y*tiles + x) * dt;
- animation->seek(t);
- animation->render(canvas);
- }
- return true;
- },
+ SkAutoCanvasRestore _(canvas, true/*save now*/);
+ canvas->clipRect(dst, /*aa=*/true);
+ canvas->concat(SkMatrix::MakeRectToRect(SkRect::MakeSize(animation->size()),
+ dst,
+ SkMatrix::kCenter_ScaleToFit));
+ float t = (y*tiles + x) * dt;
+ animation->seek(t);
+ animation->render(canvas);
+ }
+ return ok;
};
}
@@ -390,42 +377,45 @@
}
SkTArray<Source> sources;
- for (const SkString& source : FLAGS_sources) {
- if (skiagm::GMFactory* factory = gm_factories.find(source)) {
+ for (const SkString& name : FLAGS_sources) {
+ Source* source = &sources.push_back();
+
+ if (skiagm::GMFactory* factory = gm_factories.find(name)) {
std::shared_ptr<skiagm::GM> gm{(*factory)(nullptr)};
- sources.push_back(gm_source(gm));
+ source->name = name;
+ init(source, gm);
continue;
}
- if (sk_sp<SkData> blob = SkData::MakeFromFileName(source.c_str())) {
- const SkString dir = SkOSPath::Dirname (source.c_str()),
- name = SkOSPath::Basename(source.c_str());
+ if (sk_sp<SkData> blob = SkData::MakeFromFileName(name.c_str())) {
+ source->name = SkOSPath::Basename(name.c_str());
if (name.endsWith(".skp")) {
if (sk_sp<SkPicture> pic = SkPicture::MakeFromData(blob.get())) {
- sources.push_back(picture_source(name, pic));
+ init(source, pic);
continue;
}
} else if (name.endsWith(".svg")) {
SkMemoryStream stream{blob};
if (sk_sp<SkSVGDOM> svg = SkSVGDOM::MakeFromStream(stream)) {
- sources.push_back(svg_source(name, svg));
+ init(source, svg);
continue;
}
} else if (name.endsWith(".json")) {
+ const SkString dir = SkOSPath::Dirname(name.c_str());
if (sk_sp<skottie::Animation> animation = skottie::Animation::Builder()
.setResourceProvider(skottie_utils::FileResourceProvider::Make(dir))
.make((const char*)blob->data(), blob->size())) {
- sources.push_back(skottie_source(name, animation));
+ init(source, animation);
continue;
}
} else if (std::shared_ptr<SkCodec> codec = SkCodec::MakeFromData(blob)) {
- sources.push_back(codec_source(name, codec));
+ init(source, codec);
continue;
}
}
- fprintf(stderr, "Don't understand source '%s'... bailing out.\n", source.c_str());
+ fprintf(stderr, "Don't understand source '%s'... bailing out.\n", name.c_str());
return 1;
}
@@ -509,6 +499,18 @@
const SkImageInfo info = unsized_info.makeWH(source.size.width(),
source.size.height());
+ auto draw = [&source](SkCanvas* canvas) {
+ Result result = source.draw(canvas);
+ switch (result.status) {
+ case Result::Ok: break;
+ case Result::Skip: return false;
+ case Result::Fail:
+ fprintf(stderr, "%s failed: %s\n", source.name.c_str(), result.failure.c_str());
+ exit_with_failure();
+ }
+ return true;
+ };
+
GrContextOptions options = baseOptions;
source.tweak(&options);
GrContextFactory factory(options); // N.B. factory must outlive image
@@ -518,19 +520,18 @@
const char* ext = ".png";
switch (backend) {
case kCPU_Backend:
- image = draw_with_cpu(source.draw, info);
+ image = draw_with_cpu(draw, info);
break;
case kSKP_Backend:
- blob = draw_as_skp(source.draw, info);
+ blob = draw_as_skp(draw, info);
ext = ".skp";
break;
case kPDF_Backend:
- blob = draw_as_pdf(source.draw, info, source.name);
+ blob = draw_as_pdf(draw, info, source.name);
ext = ".pdf";
break;
default:
- image = draw_with_gpu(source.draw, info,
- (GrContextFactory::ContextType)backend, &factory);
+ image = draw_with_gpu(draw, info, (GrContextFactory::ContextType)backend, &factory);
break;
}