blob: 624829c5c422ba8561e50e9367703a982fdf80b8 [file] [log] [blame]
/*
* Copyright 2006 The Android Open Source Project
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkPath.h"
#include "include/core/SkPathBuilder.h"
#include "include/core/SkSpan.h"
#include "include/core/SkStream.h"
#include "src/base/SkFloatBits.h"
#include "src/core/SkStringUtils.h"
#include <functional>
char const * const gFillTypeStrs[] = {
"Winding",
"EvenOdd",
"InverseWinding",
"InverseEvenOdd",
};
constexpr float kSentinelConicWeight = -12345; // not a valid weight
static void append_params(SkString* str, const char label[], SkSpan<const SkPoint> pts,
SkScalarAsStringType strType, bool useSemicolon,
SkScalar conicWeight = kSentinelConicWeight) {
str->append(label);
str->append("(");
const SkScalar* values = &pts[0].fX;
size_t count = pts.size() * 2;
for (size_t i = 0; i < count; ++i) {
SkAppendScalar(str, values[i], strType);
if (i < count - 1) {
str->append(", ");
}
}
if (conicWeight != kSentinelConicWeight) {
str->append(", ");
SkAppendScalar(str, conicWeight, strType);
}
str->append(useSemicolon ? ");" : ")");
if (kHex_SkScalarAsStringType == strType) {
str->append(" // ");
for (size_t i = 0; i < count; ++i) {
SkAppendScalarDec(str, values[i]);
if (i < count - 1) {
str->append(", ");
}
}
if (conicWeight >= 0) {
str->append(", ");
SkAppendScalarDec(str, conicWeight);
}
}
str->append("\n");
}
static void dump_iter(SkPathIter iter, SkString* builder, const char cmdPrefix[],
SkScalarAsStringType strType, bool useSemicolon,
std::function<void()> postVerbProc) {
while (auto rec = iter.next()) {
SkString cmd(cmdPrefix);
SkSpan<const SkPoint> pts;
float cw = kSentinelConicWeight;
switch (rec->fVerb) {
case SkPathVerb::kMove:
cmd.append(".moveTo");
pts = {&rec->fPoints[0], 1};
break;
case SkPathVerb::kLine:
cmd.append(".lineTo");
pts = {&rec->fPoints[1], 1};
break;
case SkPathVerb::kQuad:
cmd.append(".quadTo");
pts = {&rec->fPoints[1], 2};
break;
case SkPathVerb::kConic:
cmd.append(".conicTo");
pts = {&rec->fPoints[1], 2};
cw = rec->conicWeight();
break;
case SkPathVerb::kCubic:
cmd.append(".cubicTo");
pts = {&rec->fPoints[1], 3};
break;
case SkPathVerb::kClose:
cmd.append(".close()");
if (useSemicolon) {
cmd.append(";");
}
cmd.append("\n");
builder->append(cmd.c_str());
break;
}
// we don't do this for kClose
if (pts.size()) {
append_params(builder, cmd.c_str(), pts, strType, useSemicolon, cw);
}
postVerbProc();
}
}
void SkPath::dump(SkWStream* wStream, bool dumpAsHex) const {
SkScalarAsStringType asType = dumpAsHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;
SkString builder;
builder.printf("path.setFillType(SkPathFillType::k%s);\n",
gFillTypeStrs[(int) this->getFillType()]);
dump_iter(this->iter(), &builder, "path", asType, true, [&]() {
if (!wStream && builder.size()) {
SkDebugf("%s", builder.c_str());
builder.reset();
}
});
if (wStream) {
wStream->writeText(builder.c_str());
}
}
SkString SkPathBuilder::dumpToString(DumpFormat format) const {
SkScalarAsStringType asType = format == DumpFormat::kHex ? kHex_SkScalarAsStringType
: kDec_SkScalarAsStringType;
SkString builder;
builder.printf("SkPathBuilder(SkPathFillType::k%s)\n",
gFillTypeStrs[(int) this->fillType()]);
dump_iter(this->iter(), &builder, "", asType, false, [](){});
return builder;
}
void SkPathBuilder::dump(DumpFormat format) const {
SkDebugf("%s", dumpToString().c_str());
}