// Return nullptr if invalid or there an empty drawable, which is represented by nullptr. if (!buffer.isValid() || pictureData->size() == 0) { return nullptr;
}
// Propagate the outer buffer's allow-SkSL setting to the picture decoder, using the flag on // the deserial procs.
SkDeserialProcs procs;
procs.fAllowSkSL = buffer.allowSkSL();
sk_sp<SkPicture> picture = SkPicture::MakeFromData(pictureData.get(), &procs); if (!buffer.validate(picture != nullptr)) { return nullptr;
}
sk_sp<SkPicture> picture = drawable->makePictureSnapshot(); // These drawables should not have SkImages, SkTypefaces or SkPictures inside of them, so // the default SkSerialProcs are sufficient.
sk_sp<SkData> data = picture->serialize();
// If the picture is too big, or there is no picture, then drop by sending an empty byte array. if (!SkTFitsIn<uint32_t>(data->size()) || data->size() == 0) {
buffer.writeByteArray(nullptr, 0); return;
}
bool SkGlyph::setImage(SkArenaAlloc* alloc, SkScalerContext* scalerContext) { if (!this->setImageHasBeenCalled()) { // It used to be that getImage() could change the fMaskFormat. Extra checking to make // sure there are no regressions.
SkDEBUGCODE(SkMask::Format oldFormat = this->maskFormat());
this->allocImage(alloc);
scalerContext->getImage(*this);
SkASSERT(oldFormat == this->maskFormat()); returntrue;
} returnfalse;
}
size_t SkGlyph::setMetricsAndImage(SkArenaAlloc* alloc, const SkGlyph& from) { // Since the code no longer tries to find replacement glyphs, the image should always be // nullptr.
SkASSERT(fImage == nullptr || from.fImage == nullptr);
// TODO(herb): remove "if" when we are sure there are no colliding glyphs. if (fImage == nullptr) {
fAdvanceX = from.fAdvanceX;
fAdvanceY = from.fAdvanceY;
fWidth = from.fWidth;
fHeight = from.fHeight;
fTop = from.fTop;
fLeft = from.fLeft;
fScalerContextBits = from.fScalerContextBits;
fMaskFormat = from.fMaskFormat;
// From glyph may not have an image because the glyph is too large. if (from.fImage != nullptr && this->setImage(alloc, from.image())) { return this->imageSize();
}
const SkPath* SkGlyph::path() const { // setPath must have been called previously.
SkASSERT(this->setPathHasBeenCalled()); if (fPathData->fHasPath) { return &fPathData->fPath;
} return nullptr;
}
bool SkGlyph::pathIsHairline() const { // setPath must have been called previously.
SkASSERT(this->setPathHasBeenCalled()); return fPathData->fHairline;
}
bool SkGlyph::pathIsModified() const { // setPath must have been called previously.
SkASSERT(this->setPathHasBeenCalled()); return fPathData->fModified;
}
SkDrawable* SkGlyph::drawable() const { // setDrawable must have been called previously.
SkASSERT(this->setDrawableHasBeenCalled()); if (fDrawableData->fHasDrawable) { return fDrawableData->fDrawable.get();
} return nullptr;
}
void SkGlyph::flattenMetrics(SkWriteBuffer& buffer) const {
buffer.writeUInt(fID.value());
buffer.writePoint({fAdvanceX, fAdvanceY});
buffer.writeUInt(fWidth << 16 | fHeight); // Note: << has undefined behavior for negative values, so convert everything to the bit // values of uint16_t. Using the cast keeps the signed values fLeft and fTop from sign // extending. const uint32_t left = static_cast<uint16_t>(fLeft); const uint32_t top = static_cast<uint16_t>(fTop);
buffer.writeUInt(left << 16 | top);
buffer.writeUInt(SkTo<uint32_t>(fMaskFormat));
}
// If the glyph is empty or too big, then no image data is sent. if (!this->isEmpty() && SkGlyphDigest::FitsInAtlas(*this)) {
buffer.writeByteArray(this->image(), this->imageSize());
}
}
size_t memoryIncrease = 0; constbool hasPath = buffer.readBool(); // Check if the buffer is invalid, so as to not make a logical decision on invalid data. if (!buffer.isValid()) { return 0;
} if (hasPath) { constbool pathIsHairline = buffer.readBool(); constbool pathIsModified = buffer.readBool();
SkPath path;
buffer.readPath(&path); if (buffer.isValid()) { if (this->setPath(alloc, &path, pathIsHairline, pathIsModified)) {
memoryIncrease += path.approximateBytesUsed();
}
}
} else {
this->setPath(alloc, nullptr, false, false);
}
// Left and Right of an ever expanding gap around the path.
SkScalar left = SK_ScalarMax,
right = SK_ScalarMin;
auto expandGap = [&left, &right](SkScalar v) {
left = std::min(left, v);
right = std::max(right, v);
};
// Handle all the different verbs for the path.
SkPoint pts[4]; auto addLine = [&](SkScalar offset) {
SkScalar t = sk_ieee_float_divide(offset - pts[0].fY, pts[1].fY - pts[0].fY); if (0 <= t && t < 1) { // this handles divide by zero above
expandGap(pts[0].fX + t * (pts[1].fX - pts[0].fX));
}
};
auto addQuad = [&](SkScalar offset) {
SkScalar intersectionStorage[2]; auto intersections = SkBezierQuad::IntersectWithHorizontalLine(
SkSpan(pts, 3), offset, intersectionStorage); for (SkScalar x : intersections) {
expandGap(x);
}
};
auto addCubic = [&](SkScalar offset) { float intersectionStorage[3]; auto intersections = SkBezierCubic::IntersectWithHorizontalLine(
SkSpan{pts, 4}, offset, intersectionStorage);
// Handle when a verb's points are in the gap between top and bottom. auto addPts = [&expandGap, &pts, topOffset, bottomOffset](int ptCount) { for (int i = 0; i < ptCount; ++i) { if (topOffset < pts[i].fY && pts[i].fY < bottomOffset) {
expandGap(pts[i].fX);
}
}
};
SkPath::Iter iter(path, false);
SkPath::Verb verb; while (SkPath::kDone_Verb != (verb = iter.next(pts))) { switch (verb) { case SkPath::kMove_Verb: { break;
} case SkPath::kLine_Verb: { auto [lineTop, lineBottom] = std::minmax({pts[0].fY, pts[1].fY});
// The y-coordinates of the points intersect the top and bottom offsets. if (topOffset <= lineBottom && lineTop <= bottomOffset) {
addLine(topOffset);
addLine(bottomOffset);
addPts(2);
} break;
} case SkPath::kQuad_Verb: { auto [quadTop, quadBottom] = std::minmax({pts[0].fY, pts[1].fY, pts[2].fY});
// The y-coordinates of the points intersect the top and bottom offsets. if (topOffset <= quadBottom && quadTop <= bottomOffset) {
addQuad(topOffset);
addQuad(bottomOffset);
addPts(3);
} break;
} case SkPath::kConic_Verb: {
SkDEBUGFAIL("There should be no conic primitives in glyph outlines."); break;
} case SkPath::kCubic_Verb: { auto [cubicTop, cubicBottom] =
std::minmax({pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY});
// The y-coordinates of the points intersect the top and bottom offsets. if (topOffset <= cubicBottom && cubicTop <= bottomOffset) {
addCubic(topOffset);
addCubic(bottomOffset);
addPts(4);
} break;
} case SkPath::kClose_Verb: { break;
} default: {
SkDEBUGFAIL("Unknown path verb generating glyph underline."); break;
}
}
}
void SkGlyphDigest::setActionFor(skglyph::ActionType actionType,
SkGlyph* glyph,
StrikeForGPU* strike) { // We don't have to do any more if the glyph is marked as kDrop because it was isEmpty(). if (this->actionFor(actionType) == GlyphAction::kUnset) {
GlyphAction action = GlyphAction::kReject; switch (actionType) { case kDirectMask: { if (this->fitsInAtlasDirect()) {
action = GlyphAction::kAccept;
} break;
} case kDirectMaskCPU: { if (strike->prepareForImage(glyph)) {
SkASSERT(!glyph->isEmpty());
action = GlyphAction::kAccept;
} break;
} case kMask: { if (this->fitsInAtlasInterpolated()) {
action = GlyphAction::kAccept;
} break;
} case kSDFT: { if (this->fitsInAtlasDirect() &&
this->maskFormat() == SkMask::Format::kSDF_Format) {
action = GlyphAction::kAccept;
} break;
} case kPath: { if (strike->prepareForPath(glyph)) {
action = GlyphAction::kAccept;
} break;
} case kDrawable: { if (strike->prepareForDrawable(glyph)) {
action = GlyphAction::kAccept;
} break;
}
}
this->setAction(actionType, action);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.