1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
cc / paint / paint_op_buffer_iterator.cc [blame]
// 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"
namespace cc {
namespace {
// 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 ? absl::variant<Iterator, OffsetIterator>(
std::in_place_type<Iterator>,
buffer)
: absl::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 (draw_op->IsPaintOpWithFlags() &&
// SkPaint::drawTextBlob() applies alpha on each glyph so we don't
// fold kSaveLayerAlpha into DrwaTextBlob to ensure correct alpha
// even if some glyphs overlap.
draw_op->GetType() != PaintOpType::kDrawTextBlob) {
auto* flags_op = static_cast<const PaintOpWithFlags*>(draw_op);
if (flags_op->flags.SupportsFoldingAlpha()) {
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