if (bbh) { // Draw only ops that affect pixels in the canvas's current clip. // The SkRecord and BBH were recorded in identity space. This canvas // is not necessarily in that same space. getLocalClipBounds() returns us // this canvas' clip bounds transformed back into identity space, which // lets us query the BBH.
SkRect query = canvas->getLocalClipBounds();
std::vector<int> ops;
bbh->search(query, &ops);
SkRecords::Draw draw(canvas, drawablePicts, drawables, drawableCount); for (int i = 0; i < (int)ops.size(); i++) { if (callback && callback->abort()) { return;
} // This visit call uses the SkRecords::Draw::operator() to call // methods on the |canvas|, wrapped by methods defined with the // DRAW() macro.
record.visit(ops[i], draw);
}
} else { // Draw all ops.
SkRecords::Draw draw(canvas, drawablePicts, drawables, drawableCount); for (int i = 0; i < record.count(); i++) { if (callback && callback->abort()) { return;
} // This visit call uses the SkRecords::Draw::operator() to call // methods on the |canvas|, wrapped by methods defined with the // DRAW() macro.
record.visit(i, draw);
}
}
}
// This is an SkRecord visitor that fills an SkBBoxHierarchy. // // The interesting part here is how to calculate bounds for ops which don't // have intrinsic bounds. What is the bounds of a Save or a Translate? // // We answer this by thinking about a particular definition of bounds: if I // don't execute this op, pixels in this rectangle might draw incorrectly. So // the bounds of a Save, a Translate, a Restore, etc. are the union of the // bounds of Draw* ops that they might have an effect on. For any given // Save/Restore block, the bounds of the Save, the Restore, and any other // non-drawing ("control") ops inside are exactly the union of the bounds of // the drawing ops inside that block. // // To implement this, we keep a stack of active Save blocks. As we consume ops // inside the Save/Restore block, drawing ops are unioned with the bounds of // the block, and control ops are stashed away for later. When we finish the // block with a Restore, our bounds are complete, and we go back and fill them // in for all the control ops we stashed away. class FillBounds : SkNoncopyable { public:
FillBounds(const SkRect& cullRect, const SkRecord& record,
SkRect bounds[], SkBBoxHierarchy::Metadata meta[])
: fCullRect(cullRect)
, fBounds(bounds)
, fMeta(meta) {
fCTM = SkMatrix::I();
// We push an extra save block to track the bounds of any top-level control operations.
fSaveStack.push_back({ 0, Bounds::MakeEmpty(), nullptr, fCTM });
}
~FillBounds() { // If we have any lingering unpaired Saves, simulate restores to make // sure all ops in those Save blocks have their bounds calculated. while (!fSaveStack.empty()) {
this->popSaveBlock();
}
// Any control ops not part of any Save/Restore block draw everywhere. while (!fControlIndices.empty()) {
this->popControl(fCullRect);
}
}
// In this file, SkRect are in local coordinates, Bounds are translated back to identity space. typedef SkRect Bounds;
// Adjust rect for all paints that may affect its geometry, then map it to identity space.
Bounds adjustAndMap(SkRect rect, const SkPaint* paint) const { // Inverted rectangles really confuse our BBHs.
rect.sort();
// Adjust the rect for its own paint. if (!AdjustForPaint(paint, &rect)) { // The paint could do anything to our bounds. The only safe answer is the cull. return fCullRect;
}
// Adjust rect for all the paints from the SaveLayers we're inside. if (!this->adjustForSaveLayerPaints(&rect)) { // Same deal as above. return fCullRect;
}
// Map the rect back to identity space.
fCTM.mapRect(&rect);
// Nothing can draw outside the cull rect. if (!rect.intersect(fCullRect)) { return Bounds::MakeEmpty();
}
return rect;
}
private: struct SaveBounds { int controlOps; // Number of control ops in this Save block, including the Save.
Bounds bounds; // Bounds of everything in the block. const SkPaint* paint; // Unowned. If set, adjusts the bounds of all ops in this block.
SkMatrix ctm;
};
// The bounds of these ops must be calculated when we hit the Restore // from the bounds of the ops in the same Save block. void trackBounds(const Save&) {
this->pushSaveBlock(nullptr, /*hasBackdropFilter=*/false);
} void trackBounds(const SaveLayer& op) {
this->pushSaveBlock(op.paint, /*hasBackdropFilter=*/op.backdrop != nullptr);
} void trackBounds(const SaveBehind&) {
this->pushSaveBlock(nullptr, /*hasBackdropFilter=*/false);
} void trackBounds(const Restore&) { constbool isSaveLayer = fSaveStack.back().paint != nullptr;
fBounds[fCurrentOp] = this->popSaveBlock();
fMeta [fCurrentOp].isDraw = isSaveLayer;
}
// For all other ops, we can calculate and store the bounds directly now. template <typename T> void trackBounds(const T& op) {
fBounds[fCurrentOp] = this->bounds(op);
fMeta [fCurrentOp].isDraw = true;
this->updateSaveBounds(fBounds[fCurrentOp]);
}
void pushSaveBlock(const SkPaint* paint, bool hasBackdropFilter) { // Starting a new Save block. Push a new entry to represent that.
SaveBounds sb;
sb.controlOps = 0;
// If the paint affects transparent black, or we have a backdrop filter, // the bound shouldn't be smaller than the cull. bool affectsFullCullRect = hasBackdropFilter || PaintMayAffectTransparentBlack(paint);
sb.bounds = affectsFullCullRect ? fCullRect : Bounds::MakeEmpty();
sb.paint = paint;
sb.ctm = this->fCTM;
fSaveStack.push_back(sb);
this->pushControl();
}
staticbool PaintMayAffectTransparentBlack(const SkPaint* paint) { if (paint) { // FIXME: this is very conservative if ((paint->getImageFilter() &&
as_IFB(paint->getImageFilter())->affectsTransparentBlack()) ||
(paint->getColorFilter() &&
as_CFB(paint->getColorFilter())->affectsTransparentBlack())) { returntrue;
} constauto bm = paint->asBlendMode(); if (!bm) { returntrue; // can we query other blenders for this?
}
// Unusual blendmodes require us to process a saved layer // even with operations outisde the clip. // For example, DstIn is used by masking layers. // https://code.google.com/p/skia/issues/detail?id=1291 // https://crbug.com/401593 switch (bm.value()) { // For each of the following transfer modes, if the source // alpha is zero (our transparent black), the resulting // blended alpha is not necessarily equal to the original // destination alpha. case SkBlendMode::kClear: case SkBlendMode::kSrc: case SkBlendMode::kSrcIn: case SkBlendMode::kDstIn: case SkBlendMode::kSrcOut: case SkBlendMode::kDstATop: case SkBlendMode::kModulate: returntrue; default: break;
}
} returnfalse;
}
Bounds popSaveBlock() { // We're done the Save block. Apply the block's bounds to all control ops inside it.
SaveBounds sb = fSaveStack.back();
fSaveStack.pop_back();
while (sb.controlOps --> 0) {
this->popControl(sb.bounds);
}
// This whole Save block may be part another Save block.
this->updateSaveBounds(sb.bounds);
// If called from a real Restore (not a phony one for balance), it'll need the bounds. return sb.bounds;
}
void pushControl() {
fControlIndices.push_back(fCurrentOp); if (!fSaveStack.empty()) {
fSaveStack.back().controlOps++;
}
}
void updateSaveBounds(const Bounds& bounds) { // If we're in a Save block, expand its bounds to cover these bounds too. if (!fSaveStack.empty()) {
fSaveStack.back().bounds.join(bounds);
}
}
// Pad the bounding box a little to make sure hairline points' bounds aren't empty.
SkScalar stroke = std::max(op.paint.getStrokeWidth(), 0.01f);
dst.outset(stroke/2, stroke/2);
return this->adjustAndMap(dst, &op.paint);
}
Bounds bounds(const DrawPatch& op) const {
SkRect dst;
dst.setBounds(op.cubics, SkPatchUtils::kNumCtrlPts); return this->adjustAndMap(dst, &op.paint);
}
Bounds bounds(const DrawVertices& op) const { return this->adjustAndMap(op.vertices->bounds(), &op.paint);
}
Bounds bounds(const DrawMesh& op) const { return this->adjustAndMap(op.mesh.bounds(), &op.paint);
}
Bounds bounds(const DrawAtlas& op) const { if (op.cull) { // TODO: <reed> can we pass nullptr for the paint? Isn't cull already "correct" // for the paint (by the caller)? return this->adjustAndMap(*op.cull, op.paint);
} else { return fCullRect;
}
}
// Returns true if rect was meaningfully adjusted for the effects of paint, // false if the paint could affect the rect in unknown ways. staticbool AdjustForPaint(const SkPaint* paint, SkRect* rect) { if (paint) { if (paint->canComputeFastBounds()) {
*rect = paint->computeFastBounds(*rect, rect); returntrue;
} returnfalse;
} returntrue;
}
bool adjustForSaveLayerPaints(SkRect* rect, int savesToIgnore = 0) const { for (int i = fSaveStack.size() - 1 - savesToIgnore; i >= 0; i--) {
SkMatrix inverse; if (!fSaveStack[i].ctm.invert(&inverse)) { returnfalse;
}
inverse.mapRect(rect); if (!AdjustForPaint(fSaveStack[i].paint, rect)) { returnfalse;
}
fSaveStack[i].ctm.mapRect(rect);
} returntrue;
}
// We do not guarantee anything for operations outside of the cull rect const SkRect fCullRect;
// Conservative identity-space bounds for each op in the SkRecord.
Bounds* fBounds;
// Parallel array to fBounds, holding metadata for each bounds rect.
SkBBoxHierarchy::Metadata* fMeta;
// We walk fCurrentOp through the SkRecord, // as we go using updateCTM() to maintain the exact CTM (fCTM). int fCurrentOp;
SkMatrix fCTM;
// Used to track the bounds of Save/Restore blocks and the control ops inside them.
SkTDArray<SaveBounds> fSaveStack;
SkTDArray<int> fControlIndices;
};
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.