Chromium Code Reviews| 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 | |
| 7 #include "base/template_util.h" | |
| 8 #include "cc/paint/display_item_list.h" | |
| 9 #include "cc/paint/paint_record.h" | |
| 10 #include "third_party/skia/include/core/SkAnnotation.h" | |
| 11 | |
| 12 namespace cc { | |
| 13 | |
| 14 #define TYPES(M) \ | |
| 15 M(AnnotateOp) \ | |
| 16 M(ClipPathOp) \ | |
| 17 M(ClipRectOp) \ | |
| 18 M(ClipRRectOp) \ | |
| 19 M(ConcatOp) \ | |
| 20 M(DrawArcOp) \ | |
| 21 M(DrawCircleOp) \ | |
| 22 M(DrawColorOp) \ | |
| 23 M(DrawDisplayItemListOp) \ | |
| 24 M(DrawDRRectOp) \ | |
| 25 M(DrawImageOp) \ | |
| 26 M(DrawImageRectOp) \ | |
| 27 M(DrawIRectOp) \ | |
| 28 M(DrawLineOp) \ | |
| 29 M(DrawOvalOp) \ | |
| 30 M(DrawPathOp) \ | |
| 31 M(DrawPosTextOp) \ | |
| 32 M(DrawRecordOp) \ | |
| 33 M(DrawRectOp) \ | |
| 34 M(DrawRRectOp) \ | |
| 35 M(DrawTextOp) \ | |
| 36 M(DrawTextBlobOp) \ | |
| 37 M(NoopOp) \ | |
| 38 M(RestoreOp) \ | |
| 39 M(RotateOp) \ | |
| 40 M(SaveOp) \ | |
| 41 M(SaveLayerOp) \ | |
| 42 M(SaveLayerAlphaOp) \ | |
| 43 M(ScaleOp) \ | |
| 44 M(SetMatrixOp) \ | |
| 45 M(TranslateOp) | |
| 46 | |
| 47 // Helper template to share common code for RasterWithAlpha when paint ops | |
| 48 // have or don't have PaintFlags. | |
| 49 template <typename T, bool HasFlags> | |
| 50 struct Rasterizer { | |
| 51 static void Raster(const T* op, | |
| 52 SkCanvas* canvas, | |
| 53 const SkMatrix& original_ctm) { | |
| 54 // Paint ops with kHasPaintFlags need to declare RasterWithPaintFlags | |
| 55 // otherwise, the paint op needs its own Raster function. Without its | |
| 56 // own, this becomes an infinite loop as PaintOp::Raster calls itself. | |
| 57 static_assert( | |
| 58 !std::is_same<decltype(&PaintOp::Raster), decltype(&T::Raster)>::value, | |
| 59 "No Raster function"); | |
| 60 | |
| 61 op->Raster(canvas); | |
| 62 } | |
| 63 static void RasterWithAlpha(const T* op, SkCanvas* canvas, uint8_t alpha) { | |
| 64 DCHECK(T::kIsDrawOp); | |
| 65 // TODO(enne): is it ok to just drop the bounds here? | |
| 66 canvas->saveLayerAlpha(nullptr, alpha); | |
| 67 op->Raster(canvas); | |
| 68 canvas->restore(); | |
| 69 } | |
| 70 }; | |
| 71 | |
| 72 template <typename T> | |
| 73 struct Rasterizer<T, true> { | |
| 74 static void Raster(const T* op, | |
| 75 SkCanvas* canvas, | |
| 76 const SkMatrix& original_ctm) { | |
| 77 op->RasterWithFlags(canvas, op->flags); | |
| 78 } | |
| 79 static void RasterWithAlpha(const T* op, SkCanvas* canvas, uint8_t alpha) { | |
| 80 DCHECK(T::kIsDrawOp); | |
| 81 SkMatrix unused_matrix; | |
| 82 if (alpha == 255) { | |
| 83 Raster(op, canvas, unused_matrix); | |
| 84 } else if (op->flags.SupportsFoldingAlpha()) { | |
| 85 PaintFlags flags = op->flags; | |
| 86 flags.setAlpha(SkMulDiv255Round(flags.getAlpha(), alpha)); | |
| 87 op->RasterWithFlags(canvas, flags); | |
| 88 } else { | |
| 89 canvas->saveLayerAlpha(nullptr, alpha); | |
| 90 op->RasterWithFlags(canvas, op->flags); | |
| 91 canvas->restore(); | |
| 92 } | |
| 93 } | |
| 94 }; | |
| 95 | |
| 96 template <> | |
| 97 struct Rasterizer<SetMatrixOp, false> { | |
| 98 static void Raster(const SetMatrixOp* op, | |
| 99 SkCanvas* canvas, | |
| 100 const SkMatrix& original_ctm) { | |
| 101 op->Raster(canvas, original_ctm); | |
| 102 } | |
| 103 static void RasterWithAlpha(const SetMatrixOp* op, | |
| 104 SkCanvas* canvas, | |
| 105 uint8_t alpha) { | |
| 106 NOTREACHED(); | |
| 107 } | |
| 108 }; | |
| 109 | |
| 110 template <> | |
| 111 struct Rasterizer<DrawRecordOp, false> { | |
| 112 static void Raster(const DrawRecordOp* op, | |
| 113 SkCanvas* canvas, | |
| 114 const SkMatrix& original_ctm) { | |
| 115 op->Raster(canvas); | |
| 116 } | |
| 117 static void RasterWithAlpha(const DrawRecordOp* op, | |
| 118 SkCanvas* canvas, | |
| 119 uint8_t alpha) { | |
| 120 // This looking into pictures optimization is done here instead of | |
| 121 // in the PaintOpBuffer::Raster function as DisplayItemList calls | |
| 122 // into RasterWithAlpha directly. | |
| 123 if (op->record->approximateOpCount() == 1) { | |
| 124 op->record->GetFirstOp()->RasterWithAlpha(canvas, alpha); | |
| 125 return; | |
| 126 } | |
| 127 | |
| 128 canvas->saveLayerAlpha(nullptr, alpha); | |
| 129 op->Raster(canvas); | |
| 130 canvas->restore(); | |
| 131 } | |
| 132 }; | |
| 133 | |
| 134 // TODO(enne): partially specialize RasterWithAlpha for draw color? | |
| 135 | |
| 136 static constexpr size_t kNumOpTypes = | |
| 137 static_cast<size_t>(PaintOpType::LastPaintOpType) + 1; | |
| 138 | |
| 139 // Verify that every op is in the TYPES macro. | |
| 140 #define M(T) 1 + | |
| 141 static_assert(kNumOpTypes == TYPES(M) 0, "Missing op in list"); | |
| 142 #undef M | |
| 143 | |
| 144 typedef void (*RasterFunction)(const PaintOp* op, | |
| 145 SkCanvas* canvas, | |
| 146 const SkMatrix& original_ctm); | |
| 147 #define M(T) \ | |
| 148 [](const PaintOp* op, SkCanvas* canvas, const SkMatrix& original_ctm) { \ | |
| 149 Rasterizer<T, T::kHasPaintFlags>::Raster(static_cast<const T*>(op), \ | |
| 150 canvas, original_ctm); \ | |
| 151 }, | |
| 152 static const RasterFunction g_raster_functions[kNumOpTypes] = {TYPES(M)}; | |
| 153 #undef M | |
| 154 | |
| 155 typedef void (*RasterAlphaFunction)(const PaintOp* op, | |
| 156 SkCanvas* canvas, | |
| 157 uint8_t alpha); | |
| 158 #define M(T) \ | |
| 159 T::kIsDrawOp ? \ | |
| 160 [](const PaintOp* op, SkCanvas* canvas, uint8_t alpha) { \ | |
| 161 Rasterizer<T, T::kHasPaintFlags>::RasterWithAlpha( \ | |
| 162 static_cast<const T*>(op), canvas, alpha); \ | |
| 163 } : static_cast<RasterAlphaFunction>(nullptr), | |
| 164 static const RasterAlphaFunction g_raster_alpha_functions[kNumOpTypes] = { | |
| 165 TYPES(M)}; | |
| 166 #undef M | |
| 167 | |
| 168 // Most state ops (matrix, clip, save, restore) have a trivial destructor. | |
| 169 typedef void (*VoidFunction)(PaintOp* op); | |
| 170 #define M(T) \ | |
| 171 !base::is_trivially_destructible<T>::value \ | |
|
mtklein_C
2017/04/05 15:26:43
In retrospect, I'd bet this doesn't matter. It's
| |
| 172 ? [](PaintOp* op) { static_cast<T*>(op)->~T(); } \ | |
| 173 : static_cast<VoidFunction>(nullptr), | |
| 174 static const VoidFunction g_destructor_functions[kNumOpTypes] = {TYPES(M)}; | |
| 175 #undef M | |
| 176 | |
| 177 #define M(T) T::kIsDrawOp, | |
| 178 static bool g_is_draw_op[kNumOpTypes] = {TYPES(M)}; | |
| 179 #undef M | |
| 180 | |
| 181 #define M(T) \ | |
| 182 static_assert(sizeof(T) <= sizeof(LargestPaintOp), \ | |
| 183 #T " must be no bigger than LargestPaintOp"); | |
| 184 TYPES(M); | |
| 185 #undef M | |
| 186 | |
| 187 #undef TYPES | |
| 188 | |
| 189 SkRect PaintOp::kUnsetRect = {SK_ScalarInfinity, 0, 0, 0}; | |
| 190 | |
| 191 void AnnotateOp::Raster(SkCanvas* canvas) const { | |
| 192 switch (annotation_type) { | |
| 193 case PaintCanvas::AnnotationType::URL: | |
| 194 SkAnnotateRectWithURL(canvas, rect, data.get()); | |
| 195 break; | |
| 196 case PaintCanvas::AnnotationType::LINK_TO_DESTINATION: | |
| 197 SkAnnotateLinkToDestination(canvas, rect, data.get()); | |
| 198 break; | |
| 199 case PaintCanvas::AnnotationType::NAMED_DESTINATION: { | |
| 200 SkPoint point = SkPoint::Make(rect.x(), rect.y()); | |
| 201 SkAnnotateNamedDestination(canvas, point, data.get()); | |
| 202 break; | |
| 203 } | |
| 204 } | |
| 205 } | |
| 206 | |
| 207 void ClipPathOp::Raster(SkCanvas* canvas) const { | |
| 208 canvas->clipPath(path, op, antialias); | |
| 209 } | |
| 210 | |
| 211 void ClipRectOp::Raster(SkCanvas* canvas) const { | |
| 212 canvas->clipRect(rect, op, antialias); | |
| 213 } | |
| 214 | |
| 215 void ClipRRectOp::Raster(SkCanvas* canvas) const { | |
| 216 canvas->clipRRect(rrect, op, antialias); | |
| 217 } | |
| 218 | |
| 219 void ConcatOp::Raster(SkCanvas* canvas) const { | |
| 220 canvas->concat(matrix); | |
| 221 } | |
| 222 | |
| 223 void DrawArcOp::RasterWithFlags(SkCanvas* canvas, | |
| 224 const PaintFlags& flags) const { | |
| 225 canvas->drawArc(oval, start_angle, sweep_angle, use_center, ToSkPaint(flags)); | |
| 226 } | |
| 227 | |
| 228 void DrawCircleOp::RasterWithFlags(SkCanvas* canvas, | |
| 229 const PaintFlags& flags) const { | |
| 230 canvas->drawCircle(cx, cy, radius, ToSkPaint(flags)); | |
| 231 } | |
| 232 | |
| 233 void DrawColorOp::Raster(SkCanvas* canvas) const { | |
| 234 canvas->drawColor(color, mode); | |
| 235 } | |
| 236 | |
| 237 void DrawDisplayItemListOp::Raster(SkCanvas* canvas) const { | |
| 238 list->Raster(canvas, nullptr); | |
| 239 } | |
| 240 | |
| 241 void DrawDRRectOp::RasterWithFlags(SkCanvas* canvas, | |
| 242 const PaintFlags& flags) const { | |
| 243 canvas->drawDRRect(outer, inner, ToSkPaint(flags)); | |
| 244 } | |
| 245 | |
| 246 void DrawImageOp::RasterWithFlags(SkCanvas* canvas, | |
| 247 const PaintFlags& flags) const { | |
| 248 canvas->drawImage(image.get(), left, top, ToSkPaint(&flags)); | |
| 249 } | |
| 250 | |
| 251 void DrawImageRectOp::RasterWithFlags(SkCanvas* canvas, | |
| 252 const PaintFlags& flags) const { | |
| 253 // TODO(enne): Probably PaintCanvas should just use the skia enum directly. | |
| 254 SkCanvas::SrcRectConstraint skconstraint = | |
| 255 static_cast<SkCanvas::SrcRectConstraint>(constraint); | |
| 256 canvas->drawImageRect(image.get(), src, dst, ToSkPaint(&flags), skconstraint); | |
| 257 } | |
| 258 | |
| 259 void DrawIRectOp::RasterWithFlags(SkCanvas* canvas, | |
| 260 const PaintFlags& flags) const { | |
| 261 canvas->drawIRect(rect, ToSkPaint(flags)); | |
| 262 } | |
| 263 | |
| 264 void DrawLineOp::RasterWithFlags(SkCanvas* canvas, | |
| 265 const PaintFlags& flags) const { | |
| 266 canvas->drawLine(x0, y0, x1, y1, ToSkPaint(flags)); | |
| 267 } | |
| 268 | |
| 269 void DrawOvalOp::RasterWithFlags(SkCanvas* canvas, | |
| 270 const PaintFlags& flags) const { | |
| 271 canvas->drawOval(oval, ToSkPaint(flags)); | |
| 272 } | |
| 273 | |
| 274 void DrawPathOp::RasterWithFlags(SkCanvas* canvas, | |
| 275 const PaintFlags& flags) const { | |
| 276 canvas->drawPath(path, ToSkPaint(flags)); | |
| 277 } | |
| 278 | |
| 279 void DrawPosTextOp::RasterWithFlags(SkCanvas* canvas, | |
| 280 const PaintFlags& flags) const { | |
| 281 canvas->drawPosText(paint_op_data(this), bytes, paint_op_array<SkPoint>(this), | |
| 282 ToSkPaint(flags)); | |
| 283 } | |
| 284 | |
| 285 void DrawRecordOp::Raster(SkCanvas* canvas) const { | |
| 286 record->playback(canvas); | |
| 287 } | |
| 288 | |
| 289 void DrawRectOp::RasterWithFlags(SkCanvas* canvas, | |
| 290 const PaintFlags& flags) const { | |
| 291 canvas->drawRect(rect, ToSkPaint(flags)); | |
| 292 } | |
| 293 | |
| 294 void DrawRRectOp::RasterWithFlags(SkCanvas* canvas, | |
| 295 const PaintFlags& flags) const { | |
| 296 canvas->drawRRect(rrect, ToSkPaint(flags)); | |
| 297 } | |
| 298 | |
| 299 void DrawTextOp::RasterWithFlags(SkCanvas* canvas, | |
| 300 const PaintFlags& flags) const { | |
| 301 canvas->drawText(paint_op_data(this), bytes, x, y, ToSkPaint(flags)); | |
| 302 } | |
| 303 | |
| 304 void DrawTextBlobOp::RasterWithFlags(SkCanvas* canvas, | |
| 305 const PaintFlags& flags) const { | |
| 306 canvas->drawTextBlob(blob.get(), x, y, ToSkPaint(flags)); | |
| 307 } | |
| 308 | |
| 309 void RestoreOp::Raster(SkCanvas* canvas) const { | |
| 310 canvas->restore(); | |
| 311 } | |
| 312 | |
| 313 void RotateOp::Raster(SkCanvas* canvas) const { | |
| 314 canvas->rotate(degrees); | |
| 315 } | |
| 316 | |
| 317 void SaveOp::Raster(SkCanvas* canvas) const { | |
| 318 canvas->save(); | |
| 319 } | |
| 320 | |
| 321 void SaveLayerOp::RasterWithFlags(SkCanvas* canvas, | |
| 322 const PaintFlags& flags) const { | |
| 323 // See PaintOp::kUnsetRect | |
| 324 bool unset = bounds.left() == SK_ScalarInfinity; | |
| 325 | |
| 326 canvas->saveLayer(unset ? nullptr : &bounds, ToSkPaint(&flags)); | |
| 327 } | |
| 328 | |
| 329 void SaveLayerAlphaOp::Raster(SkCanvas* canvas) const { | |
| 330 // See PaintOp::kUnsetRect | |
| 331 bool unset = bounds.left() == SK_ScalarInfinity; | |
| 332 canvas->saveLayerAlpha(unset ? nullptr : &bounds, alpha); | |
| 333 } | |
| 334 | |
| 335 void ScaleOp::Raster(SkCanvas* canvas) const { | |
| 336 canvas->scale(sx, sy); | |
| 337 } | |
| 338 | |
| 339 void SetMatrixOp::Raster(SkCanvas* canvas, const SkMatrix& original_ctm) const { | |
| 340 canvas->setMatrix(SkMatrix::Concat(original_ctm, matrix)); | |
| 341 } | |
| 342 | |
| 343 void TranslateOp::Raster(SkCanvas* canvas) const { | |
| 344 canvas->translate(dx, dy); | |
| 345 } | |
| 346 | |
| 347 bool PaintOp::IsDrawOp() const { | |
| 348 return g_is_draw_op[type]; | |
| 349 } | |
| 350 | |
| 351 void PaintOp::Raster(SkCanvas* canvas, const SkMatrix& original_ctm) const { | |
| 352 g_raster_functions[type](this, canvas, original_ctm); | |
| 353 } | |
| 354 | |
| 355 void PaintOp::RasterWithAlpha(SkCanvas* canvas, uint8_t alpha) const { | |
| 356 g_raster_alpha_functions[type](this, canvas, alpha); | |
| 357 } | |
| 358 | |
| 359 DrawDisplayItemListOp::DrawDisplayItemListOp( | |
| 360 scoped_refptr<DisplayItemList> list) | |
| 361 : list(list) {} | |
| 362 | |
| 363 int ClipPathOp::CountSlowPaths() const { | |
| 364 return antialias && !path.isConvex() ? 1 : 0; | |
| 365 } | |
| 366 | |
| 367 int DrawLineOp::CountSlowPaths() const { | |
| 368 if (const SkPathEffect* effect = flags.getPathEffect()) { | |
| 369 SkPathEffect::DashInfo info; | |
| 370 SkPathEffect::DashType dashType = effect->asADash(&info); | |
| 371 if (flags.getStrokeCap() != PaintFlags::kRound_Cap && | |
| 372 dashType == SkPathEffect::kDash_DashType && info.fCount == 2) { | |
| 373 // The PaintFlags will count this as 1, so uncount that here as | |
| 374 // this kind of line is special cased and not slow. | |
| 375 return -1; | |
| 376 } | |
| 377 } | |
| 378 return 0; | |
| 379 } | |
| 380 | |
| 381 int DrawPathOp::CountSlowPaths() const { | |
| 382 // TODO(enne): It's not the best to have these Skia details exposed here, | |
| 383 // but hopefully the veto is shortlived and this can go away. | |
| 384 if (flags.isAntiAlias() && !path.isConvex()) { | |
| 385 PaintFlags::Style paintStyle = flags.getStyle(); | |
| 386 const SkRect& pathBounds = path.getBounds(); | |
| 387 if (paintStyle == PaintFlags::kStroke_Style && | |
| 388 flags.getStrokeWidth() == 0) { | |
| 389 // AA hairline concave path is not slow. | |
| 390 } else if (paintStyle == PaintFlags::kFill_Style && | |
| 391 pathBounds.width() < 64.f && pathBounds.height() < 64.f && | |
| 392 !path.isVolatile()) { | |
| 393 // AADF eligible concave path is not slow. | |
| 394 } else { | |
| 395 return 1; | |
| 396 } | |
| 397 } | |
| 398 return 0; | |
| 399 } | |
| 400 | |
| 401 AnnotateOp::AnnotateOp(PaintCanvas::AnnotationType annotation_type, | |
| 402 const SkRect& rect, | |
| 403 sk_sp<SkData> data) | |
| 404 : annotation_type(annotation_type), rect(rect), data(data) {} | |
| 405 | |
| 406 AnnotateOp::~AnnotateOp() = default; | |
| 407 | |
| 408 DrawDisplayItemListOp::~DrawDisplayItemListOp() = default; | |
| 409 | |
| 410 DrawImageOp::DrawImageOp(sk_sp<const SkImage> image, | |
| 411 SkScalar left, | |
| 412 SkScalar top, | |
| 413 const PaintFlags* flags) | |
| 414 : image(std::move(image)), left(left), top(top) { | |
| 415 if (flags) | |
| 416 this->flags = *flags; | |
| 417 } | |
| 418 | |
| 419 DrawImageOp::~DrawImageOp() = default; | |
| 420 | |
| 421 DrawImageRectOp::DrawImageRectOp(sk_sp<const SkImage> image, | |
| 422 const SkRect& src, | |
| 423 const SkRect& dst, | |
| 424 const PaintFlags* flags, | |
| 425 PaintCanvas::SrcRectConstraint constraint) | |
| 426 : image(image), src(src), dst(dst), constraint(constraint) { | |
| 427 if (flags) | |
| 428 this->flags = *flags; | |
| 429 } | |
| 430 | |
| 431 DrawImageRectOp::~DrawImageRectOp() = default; | |
| 432 | |
| 433 DrawPosTextOp::DrawPosTextOp(size_t bytes, | |
| 434 size_t count, | |
| 435 const PaintFlags& flags) | |
| 436 : PaintOpWithDataArray(bytes, count), flags(flags) {} | |
| 437 | |
| 438 DrawPosTextOp::~DrawPosTextOp() = default; | |
| 439 | |
| 440 DrawRecordOp::DrawRecordOp(sk_sp<const PaintRecord> record) : record(record) {} | |
| 441 | |
| 442 DrawRecordOp::~DrawRecordOp() = default; | |
| 443 | |
| 444 DrawTextBlobOp::DrawTextBlobOp(sk_sp<SkTextBlob> blob, | |
| 445 SkScalar x, | |
| 446 SkScalar y, | |
| 447 const PaintFlags& flags) | |
| 448 : blob(blob), x(x), y(y), flags(flags) {} | |
| 449 | |
| 450 DrawTextBlobOp::~DrawTextBlobOp() = default; | |
| 451 | |
| 452 PaintOpBuffer::PaintOpBuffer() : cull_rect_(SkRect::MakeEmpty()) {} | |
| 453 | |
| 454 PaintOpBuffer::PaintOpBuffer(const SkRect& cull_rect) : cull_rect_(cull_rect) {} | |
| 455 | |
| 456 PaintOpBuffer::~PaintOpBuffer() { | |
| 457 Reset(); | |
| 458 } | |
| 459 | |
| 460 void PaintOpBuffer::Reset() { | |
| 461 for (auto* op : Iterator(this)) { | |
| 462 auto func = g_destructor_functions[op->type]; | |
| 463 if (func) | |
| 464 func(op); | |
| 465 } | |
| 466 | |
| 467 // Leave data_ allocated, reserved_ unchanged. | |
| 468 used_ = 0; | |
| 469 op_count_ = 0; | |
| 470 num_slow_paths_ = 0; | |
| 471 } | |
| 472 | |
| 473 void PaintOpBuffer::playback(SkCanvas* canvas) const { | |
| 474 // TODO(enne): a PaintRecord that contains a SetMatrix assumes that the | |
| 475 // SetMatrix is local to that PaintRecord itself. Said differently, if you | |
| 476 // translate(x, y), then draw a paint record with a SetMatrix(identity), | |
| 477 // the translation should be preserved instead of clobbering the top level | |
| 478 // transform. This could probably be done more efficiently. | |
| 479 SkMatrix original = canvas->getTotalMatrix(); | |
| 480 | |
| 481 for (Iterator iter(this); iter; ++iter) { | |
| 482 // Optimize out save/restores or save/draw/restore that can be a single | |
| 483 // draw. See also: similar code in SkRecordOpts and cc's DisplayItemList. | |
| 484 // TODO(enne): consider making this recursive? | |
| 485 const PaintOp* op = *iter; | |
| 486 if (op->GetType() == PaintOpType::SaveLayerAlpha) { | |
| 487 const PaintOp* second = iter.peek1(); | |
| 488 if (second) { | |
| 489 if (second->GetType() == PaintOpType::Restore) { | |
| 490 ++iter; | |
| 491 continue; | |
| 492 } | |
| 493 if (second->IsDrawOp()) { | |
| 494 const PaintOp* third = iter.peek2(); | |
| 495 if (third && third->GetType() == PaintOpType::Restore) { | |
| 496 const SaveLayerAlphaOp* save_op = | |
| 497 static_cast<const SaveLayerAlphaOp*>(op); | |
| 498 second->RasterWithAlpha(canvas, save_op->alpha); | |
| 499 ++iter; | |
| 500 ++iter; | |
| 501 continue; | |
| 502 } | |
| 503 } | |
| 504 } | |
| 505 } | |
| 506 // TODO(enne): skip SaveLayer followed by restore with nothing in | |
| 507 // between, however SaveLayer with image filters on it (or maybe | |
| 508 // other PaintFlags options) are not a noop. Figure out what these | |
| 509 // are so we can skip them correctly. | |
| 510 | |
| 511 op->Raster(canvas, original); | |
| 512 } | |
| 513 } | |
| 514 | |
| 515 void PaintOpBuffer::playback(SkCanvas* canvas, | |
| 516 SkPicture::AbortCallback* callback) const { | |
| 517 // The abort callback is only used for analysis, in general, so | |
| 518 // this playback code can be more straightforward and not do the | |
| 519 // optimizations in the other function. | |
| 520 if (!callback) { | |
| 521 playback(canvas); | |
| 522 return; | |
| 523 } | |
| 524 | |
| 525 SkMatrix original = canvas->getTotalMatrix(); | |
| 526 | |
| 527 // TODO(enne): ideally callers would just iterate themselves and we | |
| 528 // can remove the entire notion of an abort callback. | |
| 529 for (auto* op : Iterator(this)) { | |
| 530 op->Raster(canvas, original); | |
| 531 if (callback && callback->abort()) | |
| 532 return; | |
| 533 } | |
| 534 } | |
| 535 | |
| 536 } // namespace cc | |
| OLD | NEW |