blob: 24e7819d212c22b66fde8bd98116fff399e8daf0 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/paint/record_paint_canvas.h"
#include "cc/paint/display_item_list.h"
#include "cc/paint/paint_image_builder.h"
#include "cc/paint/paint_record.h"
#include "cc/paint/paint_recorder.h"
#include "cc/paint/skottie_wrapper.h"
#include "third_party/skia/include/core/SkAnnotation.h"
#include "third_party/skia/include/core/SkMetaData.h"
#include "third_party/skia/include/utils/SkNWayCanvas.h"
namespace cc {
RecordPaintCanvas::RecordPaintCanvas(DisplayItemList* list,
const SkRect& bounds)
: list_(list), recording_bounds_(bounds) {
DCHECK(list_);
}
RecordPaintCanvas::~RecordPaintCanvas() = default;
SkMetaData& RecordPaintCanvas::getMetaData() {
// This could just be SkMetaData owned by RecordPaintCanvas, but since
// SkCanvas already has one, we might as well use it directly.
return GetCanvas()->getMetaData();
}
SkImageInfo RecordPaintCanvas::imageInfo() const {
return GetCanvas()->imageInfo();
}
void RecordPaintCanvas::flush() {
// This is a noop when recording.
}
int RecordPaintCanvas::save() {
list_->push<SaveOp>();
return GetCanvas()->save();
}
int RecordPaintCanvas::saveLayer(const SkRect* bounds,
const PaintFlags* flags) {
if (flags) {
if (flags->IsSimpleOpacity()) {
// TODO(enne): maybe more callers should know this and call
// saveLayerAlpha instead of needing to check here.
uint8_t alpha = SkColorGetA(flags->getColor());
return saveLayerAlpha(bounds, alpha);
}
// TODO(enne): it appears that image filters affect matrices and color
// matrices affect transparent flags on SkCanvas layers, but it's not clear
// whether those are actually needed and we could just skip ToSkPaint here.
list_->push<SaveLayerOp>(bounds, flags);
SkPaint paint = flags->ToSkPaint();
return GetCanvas()->saveLayer(bounds, &paint);
}
list_->push<SaveLayerOp>(bounds, flags);
return GetCanvas()->saveLayer(bounds, nullptr);
}
int RecordPaintCanvas::saveLayerAlpha(const SkRect* bounds, uint8_t alpha) {
list_->push<SaveLayerAlphaOp>(bounds, alpha);
return GetCanvas()->saveLayerAlpha(bounds, alpha);
}
void RecordPaintCanvas::restore() {
list_->push<RestoreOp>();
GetCanvas()->restore();
}
int RecordPaintCanvas::getSaveCount() const {
return GetCanvas()->getSaveCount();
}
void RecordPaintCanvas::restoreToCount(int save_count) {
if (!canvas_) {
DCHECK_EQ(save_count, 1);
return;
}
DCHECK_GE(save_count, 1);
int diff = GetCanvas()->getSaveCount() - save_count;
DCHECK_GE(diff, 0);
for (int i = 0; i < diff; ++i)
restore();
}
void RecordPaintCanvas::translate(SkScalar dx, SkScalar dy) {
list_->push<TranslateOp>(dx, dy);
GetCanvas()->translate(dx, dy);
}
void RecordPaintCanvas::scale(SkScalar sx, SkScalar sy) {
list_->push<ScaleOp>(sx, sy);
GetCanvas()->scale(sx, sy);
}
void RecordPaintCanvas::rotate(SkScalar degrees) {
list_->push<RotateOp>(degrees);
GetCanvas()->rotate(degrees);
}
void RecordPaintCanvas::concat(const SkMatrix& matrix) {
list_->push<ConcatOp>(matrix);
GetCanvas()->concat(matrix);
}
void RecordPaintCanvas::setMatrix(const SkMatrix& matrix) {
list_->push<SetMatrixOp>(matrix);
GetCanvas()->setMatrix(matrix);
}
void RecordPaintCanvas::clipRect(const SkRect& rect,
SkClipOp op,
bool antialias) {
list_->push<ClipRectOp>(rect, op, antialias);
GetCanvas()->clipRect(rect, op, antialias);
}
void RecordPaintCanvas::clipRRect(const SkRRect& rrect,
SkClipOp op,
bool antialias) {
// TODO(enne): does this happen? Should the caller know this?
if (rrect.isRect()) {
clipRect(rrect.getBounds(), op, antialias);
return;
}
list_->push<ClipRRectOp>(rrect, op, antialias);
GetCanvas()->clipRRect(rrect, op, antialias);
}
void RecordPaintCanvas::clipPath(const SkPath& path,
SkClipOp op,
bool antialias) {
if (!path.isInverseFillType() &&
GetCanvas()->getTotalMatrix().rectStaysRect()) {
// TODO(enne): do these cases happen? should the caller know that this isn't
// a path?
SkRect rect;
if (path.isRect(&rect)) {
clipRect(rect, op, antialias);
return;
}
SkRRect rrect;
if (path.isOval(&rect)) {
rrect.setOval(rect);
clipRRect(rrect, op, antialias);
return;
}
if (path.isRRect(&rrect)) {
clipRRect(rrect, op, antialias);
return;
}
}
list_->push<ClipPathOp>(path, op, antialias);
GetCanvas()->clipPath(path, op, antialias);
return;
}
SkRect RecordPaintCanvas::getLocalClipBounds() const {
DCHECK(InitializedWithRecordingBounds());
return GetCanvas()->getLocalClipBounds();
}
bool RecordPaintCanvas::getLocalClipBounds(SkRect* bounds) const {
DCHECK(InitializedWithRecordingBounds());
return GetCanvas()->getLocalClipBounds(bounds);
}
SkIRect RecordPaintCanvas::getDeviceClipBounds() const {
DCHECK(InitializedWithRecordingBounds());
return GetCanvas()->getDeviceClipBounds();
}
bool RecordPaintCanvas::getDeviceClipBounds(SkIRect* bounds) const {
DCHECK(InitializedWithRecordingBounds());
return GetCanvas()->getDeviceClipBounds(bounds);
}
void RecordPaintCanvas::drawColor(SkColor color, SkBlendMode mode) {
list_->push<DrawColorOp>(color, mode);
}
void RecordPaintCanvas::clear(SkColor color) {
list_->push<DrawColorOp>(color, SkBlendMode::kSrc);
}
void RecordPaintCanvas::drawLine(SkScalar x0,
SkScalar y0,
SkScalar x1,
SkScalar y1,
const PaintFlags& flags) {
list_->push<DrawLineOp>(x0, y0, x1, y1, flags);
}
void RecordPaintCanvas::drawRect(const SkRect& rect, const PaintFlags& flags) {
list_->push<DrawRectOp>(rect, flags);
}
void RecordPaintCanvas::drawIRect(const SkIRect& rect,
const PaintFlags& flags) {
list_->push<DrawIRectOp>(rect, flags);
}
void RecordPaintCanvas::drawOval(const SkRect& oval, const PaintFlags& flags) {
list_->push<DrawOvalOp>(oval, flags);
}
void RecordPaintCanvas::drawRRect(const SkRRect& rrect,
const PaintFlags& flags) {
list_->push<DrawRRectOp>(rrect, flags);
}
void RecordPaintCanvas::drawDRRect(const SkRRect& outer,
const SkRRect& inner,
const PaintFlags& flags) {
if (outer.isEmpty())
return;
if (inner.isEmpty()) {
drawRRect(outer, flags);
return;
}
list_->push<DrawDRRectOp>(outer, inner, flags);
}
void RecordPaintCanvas::drawRoundRect(const SkRect& rect,
SkScalar rx,
SkScalar ry,
const PaintFlags& flags) {
// TODO(enne): move this into base class?
if (rx > 0 && ry > 0) {
SkRRect rrect;
rrect.setRectXY(rect, rx, ry);
drawRRect(rrect, flags);
} else {
drawRect(rect, flags);
}
}
void RecordPaintCanvas::drawPath(const SkPath& path, const PaintFlags& flags) {
list_->push<DrawPathOp>(path, flags);
}
void RecordPaintCanvas::drawImage(const PaintImage& image,
SkScalar left,
SkScalar top,
const PaintFlags* flags) {
list_->push<DrawImageOp>(image, left, top, flags);
}
void RecordPaintCanvas::drawImageRect(const PaintImage& image,
const SkRect& src,
const SkRect& dst,
const PaintFlags* flags,
SrcRectConstraint constraint) {
list_->push<DrawImageRectOp>(image, src, dst, flags, constraint);
}
void RecordPaintCanvas::drawSkottie(scoped_refptr<SkottieWrapper> skottie,
const SkRect& dst,
float t) {
list_->push<DrawSkottieOp>(std::move(skottie), dst, t);
}
void RecordPaintCanvas::drawTextBlob(sk_sp<SkTextBlob> blob,
SkScalar x,
SkScalar y,
const PaintFlags& flags) {
list_->push<DrawTextBlobOp>(std::move(blob), x, y, flags);
}
void RecordPaintCanvas::drawPicture(sk_sp<const PaintRecord> record) {
// TODO(enne): If this is small, maybe flatten it?
list_->push<DrawRecordOp>(record);
}
bool RecordPaintCanvas::isClipEmpty() const {
DCHECK(InitializedWithRecordingBounds());
return GetCanvas()->isClipEmpty();
}
bool RecordPaintCanvas::isClipRect() const {
DCHECK(InitializedWithRecordingBounds());
return GetCanvas()->isClipRect();
}
const SkMatrix& RecordPaintCanvas::getTotalMatrix() const {
return GetCanvas()->getTotalMatrix();
}
void RecordPaintCanvas::Annotate(AnnotationType type,
const SkRect& rect,
sk_sp<SkData> data) {
list_->push<AnnotateOp>(type, rect, data);
}
void RecordPaintCanvas::recordCustomData(uint32_t id) {
list_->push<CustomDataOp>(id);
}
const SkNoDrawCanvas* RecordPaintCanvas::GetCanvas() const {
return const_cast<RecordPaintCanvas*>(this)->GetCanvas();
}
SkNoDrawCanvas* RecordPaintCanvas::GetCanvas() {
if (canvas_)
return &*canvas_;
// Size the canvas to be large enough to contain the |recording_bounds|, which
// may not be positioned at the origin.
SkIRect enclosing_rect = recording_bounds_.roundOut();
canvas_.emplace(enclosing_rect.right(), enclosing_rect.bottom());
// This is part of the "recording canvases have a size, but why" dance.
// By creating a canvas of size (right x bottom) and then clipping it,
// It makes getDeviceClipBounds return the original cull rect, which code
// in GraphicsContextCanvas on Mac expects. (Just creating an SkNoDrawCanvas
// with the recording_bounds_ makes a canvas of size (width x height) instead
// which is incorrect. SkRecorder cheats with private resetForNextCanvas.
canvas_->clipRect(recording_bounds_, SkClipOp::kIntersect, false);
return &*canvas_;
}
bool RecordPaintCanvas::InitializedWithRecordingBounds() const {
// If the RecordPaintCanvas is initialized with an empty bounds then
// the various clip related functions are not valid and should not
// be called.
return !recording_bounds_.isEmpty();
}
} // namespace cc