blob: 1d8a649349b9768f5d6c827d87cee7449bb14bc7 [file]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/paint/paint_op_buffer_iterator.h"
#include <variant>
namespace cc {
namespace {
static bool SupportsFoldingAlpha(const PaintOp* op) {
if (!op->IsPaintOpWithFlags()) {
return false;
}
if (!static_cast<const PaintOpWithFlags*>(op)->flags.SupportsFoldingAlpha()) {
return false;
}
// SkPaint::drawTextBlob() applies alpha on each glyph so we don't
// fold kSaveLayerAlpha into DrawTextBlob to ensure correct alpha
// even if some glyphs overlap.
if (op->GetType() == PaintOpType::kDrawTextBlob) {
return false;
}
// Paint worklet ignores alpha on the flags.
if (op->GetType() == PaintOpType::kDrawImage &&
static_cast<const DrawImageOp*>(op)->image.IsPaintWorklet()) {
return false;
}
if (op->GetType() == PaintOpType::kDrawImageRect &&
static_cast<const DrawImageRectOp*>(op)->image.IsPaintWorklet()) {
return false;
}
return true;
}
// When |op| is a DrawRecordOp, this returns the PaintOp inside that record if
// it contains (recursively) a single drawing op, otherwise it returns |op| if
// it's a drawing op, or nullptr.
static const PaintOp* GetNestedSingleDrawingOp(const PaintOp* op) {
if (!op->IsDrawOp())
return nullptr;
while (op->GetType() == PaintOpType::kDrawRecord) {
auto* draw_record_op = static_cast<const DrawRecordOp*>(op);
if (draw_record_op->record.empty()) {
// We could omit this empty DrawRecordOp (as well as the enclosing
// SaveLayerAlphaOp/RestoreOp), but the case is very rare.
return nullptr;
}
if (draw_record_op->record.size() > 1) {
// If there's more than one op, then we need to keep the
// SaveLayer.
return nullptr;
}
// Recurse into the single-op DrawRecordOp and make sure it's a
// drawing op.
op = &draw_record_op->record.GetFirstOp();
if (!op->IsDrawOp())
return nullptr;
}
return op;
}
} // anonymous namespace
PaintOpBuffer::CompositeIterator::CompositeIterator(
const PaintOpBuffer& buffer,
const std::vector<size_t>* offsets)
: iter_(offsets == nullptr ? std::variant<Iterator, OffsetIterator>(
std::in_place_type<Iterator>,
buffer)
: std::variant<Iterator, OffsetIterator>(
std::in_place_type<OffsetIterator>,
buffer,
*offsets)) {}
PaintOpBuffer::CompositeIterator::CompositeIterator(
const CompositeIterator& other) = default;
PaintOpBuffer::CompositeIterator::CompositeIterator(CompositeIterator&& other) =
default;
PaintOpBuffer::PlaybackFoldingIterator::PlaybackFoldingIterator(
const PaintOpBuffer& buffer,
const std::vector<size_t>* offsets)
: iter_(buffer, offsets),
folded_draw_color_(SkColors::kTransparent, SkBlendMode::kSrcOver) {
FindNextOp();
}
PaintOpBuffer::PlaybackFoldingIterator::~PlaybackFoldingIterator() = default;
void PaintOpBuffer::PlaybackFoldingIterator::FindNextOp() {
current_alpha_ = 1.0f;
for (current_op_ = NextUnfoldedOp(); current_op_;
current_op_ = NextUnfoldedOp()) {
if (current_op_->GetType() != PaintOpType::kSaveLayerAlpha) {
break;
}
const PaintOp* second = NextUnfoldedOp();
if (!second)
break;
if (second->GetType() == PaintOpType::kRestore) {
// Drop a kSaveLayerAlpha/kRestore combo.
continue;
}
// Find a nested drawing PaintOp to replace |second| if possible, while
// holding onto the pointer to |second| in case we can't find a nested
// drawing op to replace it with.
const PaintOp* draw_op = GetNestedSingleDrawingOp(second);
const PaintOp* third = nullptr;
if (draw_op) {
third = NextUnfoldedOp();
if (third && third->GetType() == PaintOpType::kRestore) {
auto* save_op = static_cast<const SaveLayerAlphaOp*>(current_op_);
if (SupportsFoldingAlpha(draw_op)) {
current_alpha_ = save_op->alpha;
current_op_ = draw_op;
break;
} else if (draw_op->GetType() == PaintOpType::kDrawColor &&
static_cast<const DrawColorOp*>(draw_op)->mode ==
SkBlendMode::kSrcOver) {
auto* draw_color_op = static_cast<const DrawColorOp*>(draw_op);
SkColor4f color = draw_color_op->color;
folded_draw_color_.color = {color.fR, color.fG, color.fB,
save_op->alpha * color.fA};
current_op_ = &folded_draw_color_;
break;
}
}
}
// If we get here, then we could not find a foldable sequence after
// this kSaveLayerAlpha, so store any peeked at ops.
stack_.push_back(second);
if (third)
stack_.push_back(third);
break;
}
}
const PaintOp* PaintOpBuffer::PlaybackFoldingIterator::NextUnfoldedOp() {
if (stack_.size()) {
const PaintOp* op = stack_.front();
// Shift paintops forward.
stack_.erase(stack_.begin());
return op;
}
if (!iter_)
return nullptr;
const PaintOp& op = *iter_;
++iter_;
return &op;
}
} // namespace cc