| OLD | NEW |
| (Empty) |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "cc/paint/paint_op_buffer.h" | |
| 6 #include "cc/test/test_skcanvas.h" | |
| 7 #include "testing/gtest/include/gtest/gtest.h" | |
| 8 | |
| 9 namespace { | |
| 10 | |
| 11 template <typename T> | |
| 12 void CheckRefCnt(const T& obj, int32_t count) { | |
| 13 // Skia doesn't define getRefCnt in all builds. | |
| 14 #ifdef SK_DEBUG | |
| 15 EXPECT_EQ(obj->getRefCnt(), count); | |
| 16 #endif | |
| 17 } | |
| 18 | |
| 19 } // namespace | |
| 20 | |
| 21 namespace cc { | |
| 22 | |
| 23 TEST(PaintOpBufferTest, Empty) { | |
| 24 PaintOpBuffer buffer; | |
| 25 EXPECT_EQ(buffer.approximateOpCount(), 0); | |
| 26 EXPECT_EQ(buffer.approximateBytesUsed(), sizeof(PaintOpBuffer)); | |
| 27 EXPECT_EQ(PaintOpBuffer::Iterator(&buffer), false); | |
| 28 | |
| 29 buffer.Reset(); | |
| 30 EXPECT_EQ(buffer.approximateOpCount(), 0); | |
| 31 EXPECT_EQ(buffer.approximateBytesUsed(), sizeof(PaintOpBuffer)); | |
| 32 EXPECT_EQ(PaintOpBuffer::Iterator(&buffer), false); | |
| 33 } | |
| 34 | |
| 35 TEST(PaintOpBufferTest, SimpleAppend) { | |
| 36 SkRect rect = SkRect::MakeXYWH(2, 3, 4, 5); | |
| 37 PaintFlags flags; | |
| 38 flags.setColor(SK_ColorMAGENTA); | |
| 39 flags.setAlpha(100); | |
| 40 SkColor draw_color = SK_ColorRED; | |
| 41 SkBlendMode blend = SkBlendMode::kSrc; | |
| 42 | |
| 43 PaintOpBuffer buffer; | |
| 44 buffer.push<SaveLayerOp>(&rect, &flags); | |
| 45 buffer.push<SaveOp>(); | |
| 46 buffer.push<DrawColorOp>(draw_color, blend); | |
| 47 buffer.push<RestoreOp>(); | |
| 48 | |
| 49 EXPECT_EQ(buffer.approximateOpCount(), 4); | |
| 50 | |
| 51 PaintOpBuffer::Iterator iter(&buffer); | |
| 52 ASSERT_EQ(iter->GetType(), PaintOpType::SaveLayer); | |
| 53 SaveLayerOp* save_op = static_cast<SaveLayerOp*>(*iter); | |
| 54 EXPECT_EQ(save_op->bounds, rect); | |
| 55 EXPECT_TRUE(save_op->flags == flags); | |
| 56 ++iter; | |
| 57 | |
| 58 ASSERT_EQ(iter->GetType(), PaintOpType::Save); | |
| 59 ++iter; | |
| 60 | |
| 61 ASSERT_EQ(iter->GetType(), PaintOpType::DrawColor); | |
| 62 DrawColorOp* op = static_cast<DrawColorOp*>(*iter); | |
| 63 EXPECT_EQ(op->color, draw_color); | |
| 64 EXPECT_EQ(op->mode, blend); | |
| 65 ++iter; | |
| 66 | |
| 67 ASSERT_EQ(iter->GetType(), PaintOpType::Restore); | |
| 68 ++iter; | |
| 69 | |
| 70 EXPECT_FALSE(iter); | |
| 71 } | |
| 72 | |
| 73 // PaintOpBuffer has a special case for first ops stored locally, so | |
| 74 // make sure that appending different kind of ops as a first op works | |
| 75 // properly, as well as resetting and reusing the first local op. | |
| 76 TEST(PaintOpBufferTest, FirstOpWithAndWithoutData) { | |
| 77 PaintOpBuffer buffer; | |
| 78 char text[] = "asdf"; | |
| 79 | |
| 80 // Use a color filter and its ref count to verify that the destructor | |
| 81 // is called on ops after reset. | |
| 82 PaintFlags flags; | |
| 83 sk_sp<SkColorFilter> filter = | |
| 84 SkColorFilter::MakeModeFilter(SK_ColorMAGENTA, SkBlendMode::kSrcOver); | |
| 85 flags.setColorFilter(filter); | |
| 86 CheckRefCnt(filter, 2); | |
| 87 | |
| 88 buffer.push_with_data<DrawTextOp>(text, arraysize(text), 0.f, 0.f, flags); | |
| 89 CheckRefCnt(filter, 3); | |
| 90 | |
| 91 // Verify that when the first op has data, which may not fit in the | |
| 92 // PaintRecord internal buffer, that it adds a noop as the first op | |
| 93 // and then appends the "op with data" into the heap buffer. | |
| 94 ASSERT_EQ(buffer.approximateOpCount(), 2); | |
| 95 EXPECT_EQ(buffer.GetFirstOp()->GetType(), PaintOpType::Noop); | |
| 96 | |
| 97 // Verify iteration behavior and brief smoke test of op state. | |
| 98 { | |
| 99 PaintOpBuffer::Iterator iter(&buffer); | |
| 100 PaintOp* noop = *iter; | |
| 101 EXPECT_EQ(buffer.GetFirstOp(), noop); | |
| 102 ++iter; | |
| 103 | |
| 104 PaintOp* op = *iter; | |
| 105 ASSERT_EQ(op->GetType(), PaintOpType::DrawText); | |
| 106 DrawTextOp* draw_text_op = static_cast<DrawTextOp*>(op); | |
| 107 EXPECT_EQ(draw_text_op->bytes, arraysize(text)); | |
| 108 | |
| 109 void* data = paint_op_data(draw_text_op); | |
| 110 EXPECT_EQ(memcmp(data, text, arraysize(text)), 0); | |
| 111 | |
| 112 ++iter; | |
| 113 EXPECT_FALSE(iter); | |
| 114 } | |
| 115 | |
| 116 // Reset, verify state, and append an op that will fit in the first slot. | |
| 117 buffer.Reset(); | |
| 118 CheckRefCnt(filter, 2); | |
| 119 | |
| 120 ASSERT_EQ(buffer.approximateOpCount(), 0); | |
| 121 EXPECT_EQ(PaintOpBuffer::Iterator(&buffer), false); | |
| 122 | |
| 123 SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4); | |
| 124 buffer.push<DrawRectOp>(rect, flags); | |
| 125 CheckRefCnt(filter, 3); | |
| 126 | |
| 127 ASSERT_EQ(buffer.approximateOpCount(), 1); | |
| 128 EXPECT_EQ(buffer.GetFirstOp()->GetType(), PaintOpType::DrawRect); | |
| 129 | |
| 130 PaintOpBuffer::Iterator iter(&buffer); | |
| 131 ASSERT_EQ(iter->GetType(), PaintOpType::DrawRect); | |
| 132 DrawRectOp* draw_rect_op = static_cast<DrawRectOp*>(*iter); | |
| 133 EXPECT_EQ(draw_rect_op->rect, rect); | |
| 134 | |
| 135 ++iter; | |
| 136 EXPECT_FALSE(iter); | |
| 137 | |
| 138 buffer.Reset(); | |
| 139 ASSERT_EQ(buffer.approximateOpCount(), 0); | |
| 140 CheckRefCnt(filter, 2); | |
| 141 } | |
| 142 | |
| 143 TEST(PaintOpBufferTest, Peek) { | |
| 144 PaintOpBuffer buffer; | |
| 145 | |
| 146 uint8_t alpha = 100; | |
| 147 buffer.push<SaveLayerAlphaOp>(nullptr, alpha); | |
| 148 PaintFlags draw_flags; | |
| 149 buffer.push<DrawRectOp>(SkRect::MakeXYWH(1, 2, 3, 4), draw_flags); | |
| 150 buffer.push<RestoreOp>(); | |
| 151 buffer.push<SaveOp>(); | |
| 152 buffer.push<NoopOp>(); | |
| 153 buffer.push<RestoreOp>(); | |
| 154 | |
| 155 PaintOpBuffer::Iterator init_iter(&buffer); | |
| 156 PaintOp* peek[2] = {*init_iter, init_iter.peek1()}; | |
| 157 | |
| 158 // Expect that while iterating that next = current.peek1() and that | |
| 159 // next.peek1() == current.peek2(). | |
| 160 for (PaintOpBuffer::Iterator iter(&buffer); iter; ++iter) { | |
| 161 EXPECT_EQ(*iter, peek[0]) << iter.op_idx(); | |
| 162 EXPECT_EQ(iter.peek1(), peek[1]) << iter.op_idx(); | |
| 163 | |
| 164 peek[0] = iter.peek1(); | |
| 165 peek[1] = iter.peek2(); | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 TEST(PaintOpBufferTest, PeekEmpty) { | |
| 170 PaintOpBuffer empty; | |
| 171 PaintOpBuffer::Iterator empty_iter(&empty); | |
| 172 EXPECT_EQ(nullptr, empty_iter.peek1()); | |
| 173 EXPECT_EQ(nullptr, empty_iter.peek2()); | |
| 174 } | |
| 175 | |
| 176 // Verify that a SaveLayerAlpha / Draw / Restore can be optimized to just | |
| 177 // a draw with opacity. | |
| 178 TEST(PaintOpBufferTest, SaveDrawRestore) { | |
| 179 PaintOpBuffer buffer; | |
| 180 | |
| 181 uint8_t alpha = 100; | |
| 182 buffer.push<SaveLayerAlphaOp>(nullptr, alpha); | |
| 183 | |
| 184 PaintFlags draw_flags; | |
| 185 draw_flags.setColor(SK_ColorMAGENTA); | |
| 186 draw_flags.setAlpha(50); | |
| 187 EXPECT_TRUE(draw_flags.SupportsFoldingAlpha()); | |
| 188 SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4); | |
| 189 buffer.push<DrawRectOp>(rect, draw_flags); | |
| 190 buffer.push<RestoreOp>(); | |
| 191 | |
| 192 SaveCountingCanvas canvas; | |
| 193 buffer.playback(&canvas); | |
| 194 | |
| 195 EXPECT_EQ(0, canvas.save_count_); | |
| 196 EXPECT_EQ(0, canvas.restore_count_); | |
| 197 EXPECT_EQ(rect, canvas.draw_rect_); | |
| 198 | |
| 199 // Expect the alpha from the draw and the save layer to be folded together. | |
| 200 // Since alpha is stored in a uint8_t and gets rounded, so use tolerance. | |
| 201 float expected_alpha = alpha * 50 / 255.f; | |
| 202 EXPECT_LE(std::abs(expected_alpha - canvas.paint_.getAlpha()), 1.f); | |
| 203 } | |
| 204 | |
| 205 // The same as SaveDrawRestore, but test that the optimization doesn't apply | |
| 206 // when the drawing op's flags are not compatible with being folded into the | |
| 207 // save layer with opacity. | |
| 208 TEST(PaintOpBufferTest, SaveDrawRestoreFail_BadFlags) { | |
| 209 PaintOpBuffer buffer; | |
| 210 | |
| 211 uint8_t alpha = 100; | |
| 212 buffer.push<SaveLayerAlphaOp>(nullptr, alpha); | |
| 213 | |
| 214 PaintFlags draw_flags; | |
| 215 draw_flags.setColor(SK_ColorMAGENTA); | |
| 216 draw_flags.setAlpha(50); | |
| 217 draw_flags.setBlendMode(SkBlendMode::kSrc); | |
| 218 EXPECT_FALSE(draw_flags.SupportsFoldingAlpha()); | |
| 219 SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4); | |
| 220 buffer.push<DrawRectOp>(rect, draw_flags); | |
| 221 buffer.push<RestoreOp>(); | |
| 222 | |
| 223 SaveCountingCanvas canvas; | |
| 224 buffer.playback(&canvas); | |
| 225 | |
| 226 EXPECT_EQ(1, canvas.save_count_); | |
| 227 EXPECT_EQ(1, canvas.restore_count_); | |
| 228 EXPECT_EQ(rect, canvas.draw_rect_); | |
| 229 EXPECT_EQ(draw_flags.getAlpha(), canvas.paint_.getAlpha()); | |
| 230 } | |
| 231 | |
| 232 // The same as SaveDrawRestore, but test that the optimization doesn't apply | |
| 233 // when there are more than one ops between the save and restore. | |
| 234 TEST(PaintOpBufferTest, SaveDrawRestoreFail_TooManyOps) { | |
| 235 PaintOpBuffer buffer; | |
| 236 | |
| 237 uint8_t alpha = 100; | |
| 238 buffer.push<SaveLayerAlphaOp>(nullptr, alpha); | |
| 239 | |
| 240 PaintFlags draw_flags; | |
| 241 draw_flags.setColor(SK_ColorMAGENTA); | |
| 242 draw_flags.setAlpha(50); | |
| 243 draw_flags.setBlendMode(SkBlendMode::kSrcOver); | |
| 244 EXPECT_TRUE(draw_flags.SupportsFoldingAlpha()); | |
| 245 SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4); | |
| 246 buffer.push<DrawRectOp>(rect, draw_flags); | |
| 247 buffer.push<NoopOp>(); | |
| 248 buffer.push<RestoreOp>(); | |
| 249 | |
| 250 SaveCountingCanvas canvas; | |
| 251 buffer.playback(&canvas); | |
| 252 | |
| 253 EXPECT_EQ(1, canvas.save_count_); | |
| 254 EXPECT_EQ(1, canvas.restore_count_); | |
| 255 EXPECT_EQ(rect, canvas.draw_rect_); | |
| 256 EXPECT_EQ(draw_flags.getAlpha(), canvas.paint_.getAlpha()); | |
| 257 } | |
| 258 | |
| 259 } // namespace cc | |
| OLD | NEW |