blob: ddcad418103b22f42e66808ff496ec0b6993e87f [file] [log] [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/skia_paint_canvas.h"
#include <cstdint>
#include <cstring>
#include <utility>
#include "base/containers/span.h"
#include "base/functional/bind.h"
#include "base/notimplemented.h"
#include "base/trace_event/trace_event.h"
#include "cc/paint/display_item_list.h"
#include "cc/paint/paint_filter.h"
#include "cc/paint/paint_flags.h"
#include "cc/paint/paint_op.h"
#include "cc/paint/paint_op_buffer.h"
#include "cc/paint/paint_recorder.h"
#include "cc/paint/scoped_raster_flags.h"
#include "cc/paint/skottie_wrapper.h"
#include "skia/ext/legacy_display_globals.h"
#include "third_party/skia/include/core/SkAnnotation.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkPoint.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/core/SkTextBlob.h"
#include "third_party/skia/include/docs/SkPDFDocument.h"
#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
#include "third_party/skia/include/gpu/ganesh/GrRecordingContext.h"
#include "third_party/skia/src/core/SkCanvasPriv.h"
namespace cc {
SkiaPaintCanvas::ContextFlushes::ContextFlushes()
: enable(false), max_draws_before_flush(-1) {}
SkiaPaintCanvas::SkiaPaintCanvas(SkCanvas* canvas,
ImageProvider* image_provider,
ContextFlushes context_flushes)
: canvas_(canvas),
image_provider_(image_provider),
context_flushes_(context_flushes) {}
SkiaPaintCanvas::SkiaPaintCanvas(const SkBitmap& bitmap,
ImageProvider* image_provider)
: canvas_(new SkCanvas(bitmap,
skia::LegacyDisplayGlobals::GetSkSurfaceProps())),
bitmap_(bitmap),
owned_(canvas_),
image_provider_(image_provider) {}
SkiaPaintCanvas::SkiaPaintCanvas(const SkBitmap& bitmap,
const SkSurfaceProps& props)
: canvas_(new SkCanvas(bitmap, props)), bitmap_(bitmap), owned_(canvas_) {}
SkiaPaintCanvas::~SkiaPaintCanvas() {
canvas_ = nullptr;
image_provider_ = nullptr;
}
SkImageInfo SkiaPaintCanvas::imageInfo() const {
return canvas_->imageInfo();
}
void* SkiaPaintCanvas::accessTopLayerPixels(SkImageInfo* info,
size_t* rowBytes,
SkIPoint* origin) {
if (bitmap_.isNull() || bitmap_.isImmutable())
return nullptr;
return canvas_->accessTopLayerPixels(info, rowBytes, origin);
}
void SkiaPaintCanvas::flush() {
if (GrDirectContext* direct_context =
GrAsDirectContext(canvas_->recordingContext())) {
direct_context->flushAndSubmit();
}
}
bool SkiaPaintCanvas::NeedsFlush() const {
// Since flush() is always capable of flushing immediately with
// SkiaPaintCanvas, there is never any need for deferred flushing.
return false;
}
int SkiaPaintCanvas::save() {
return canvas_->save();
}
int SkiaPaintCanvas::saveLayer(const PaintFlags& flags) {
SkPaint paint = flags.ToSkPaint();
return canvas_->saveLayer(nullptr, &paint);
}
int SkiaPaintCanvas::saveLayer(const SkRect& bounds, const PaintFlags& flags) {
SkPaint paint = flags.ToSkPaint();
return canvas_->saveLayer(&bounds, &paint);
}
int SkiaPaintCanvas::saveLayerAlphaf(float alpha) {
return canvas_->saveLayerAlphaf(nullptr, alpha);
}
int SkiaPaintCanvas::saveLayerAlphaf(const SkRect& bounds, float alpha) {
return canvas_->saveLayerAlphaf(&bounds, alpha);
}
int SkiaPaintCanvas::saveLayerFilters(
base::span<const sk_sp<PaintFilter>> filters,
const PaintFlags& flags) {
SkPaint paint = flags.ToSkPaint();
return canvas_->saveLayer(SkCanvasPriv::ScaledBackdropLayer(
/*bounds=*/nullptr, &paint, /*backdrop=*/nullptr, /*backdropScale=*/1.0f,
/*saveLayerFlags=*/0, PaintFilter::ToSkImageFilters(filters)));
}
void SkiaPaintCanvas::restore() {
canvas_->restore();
}
int SkiaPaintCanvas::getSaveCount() const {
return canvas_->getSaveCount();
}
void SkiaPaintCanvas::restoreToCount(int save_count) {
canvas_->restoreToCount(save_count);
}
void SkiaPaintCanvas::translate(SkScalar dx, SkScalar dy) {
canvas_->translate(dx, dy);
}
void SkiaPaintCanvas::scale(SkScalar sx, SkScalar sy) {
canvas_->scale(sx, sy);
}
void SkiaPaintCanvas::rotate(SkScalar degrees) {
canvas_->rotate(degrees);
}
void SkiaPaintCanvas::setMatrix(const SkM44& matrix) {
canvas_->setMatrix(matrix);
}
void SkiaPaintCanvas::concat(const SkM44& matrix) {
canvas_->concat(matrix);
}
void SkiaPaintCanvas::clipRect(const SkRect& rect,
SkClipOp op,
bool do_anti_alias) {
canvas_->clipRect(rect, op, do_anti_alias);
}
void SkiaPaintCanvas::clipRRect(const SkRRect& rrect,
SkClipOp op,
bool do_anti_alias) {
canvas_->clipRRect(rrect, op, do_anti_alias);
}
void SkiaPaintCanvas::clipPath(const SkPath& path,
SkClipOp op,
bool do_anti_alias,
UsePaintCache) {
canvas_->clipPath(path, op, do_anti_alias);
}
bool SkiaPaintCanvas::getLocalClipBounds(SkRect* bounds) const {
return canvas_->getLocalClipBounds(bounds);
}
bool SkiaPaintCanvas::getDeviceClipBounds(SkIRect* bounds) const {
return canvas_->getDeviceClipBounds(bounds);
}
void SkiaPaintCanvas::drawColor(SkColor4f color, SkBlendMode mode) {
canvas_->drawColor(color, mode);
}
void SkiaPaintCanvas::clear(SkColor4f color) {
canvas_->clear(color);
}
void SkiaPaintCanvas::drawLine(SkScalar x0,
SkScalar y0,
SkScalar x1,
SkScalar y1,
const PaintFlags& flags) {
ScopedRasterFlags raster_flags(&flags, image_provider_,
canvas_->getTotalMatrix(), GetMaxTextureSize(),
1.0f);
if (!raster_flags.flags())
return;
raster_flags.flags()->DrawToSk(
canvas_, [x0, y0, x1, y1](SkCanvas* c, const SkPaint& p) {
c->drawLine(x0, y0, x1, y1, p);
});
FlushAfterDrawIfNeeded();
}
void SkiaPaintCanvas::drawArc(const SkRect& oval,
SkScalar start_angle_degrees,
SkScalar sweep_angle_degrees,
const PaintFlags& flags) {
ScopedRasterFlags raster_flags(&flags, image_provider_,
canvas_->getTotalMatrix(), GetMaxTextureSize(),
1.0f);
if (!raster_flags.flags()) {
return;
}
DrawArcOp op(oval, start_angle_degrees, sweep_angle_degrees, flags);
op.RasterWithFlagsImpl(raster_flags.flags(), canvas_);
FlushAfterDrawIfNeeded();
}
void SkiaPaintCanvas::drawRect(const SkRect& rect, const PaintFlags& flags) {
ScopedRasterFlags raster_flags(&flags, image_provider_,
canvas_->getTotalMatrix(), GetMaxTextureSize(),
1.0f);
if (!raster_flags.flags())
return;
raster_flags.flags()->DrawToSk(
canvas_,
[&rect](SkCanvas* c, const SkPaint& p) { c->drawRect(rect, p); });
FlushAfterDrawIfNeeded();
}
void SkiaPaintCanvas::drawIRect(const SkIRect& rect, const PaintFlags& flags) {
ScopedRasterFlags raster_flags(&flags, image_provider_,
canvas_->getTotalMatrix(), GetMaxTextureSize(),
1.0f);
if (!raster_flags.flags())
return;
raster_flags.flags()->DrawToSk(
canvas_,
[&rect](SkCanvas* c, const SkPaint& p) { c->drawIRect(rect, p); });
FlushAfterDrawIfNeeded();
}
void SkiaPaintCanvas::drawOval(const SkRect& oval, const PaintFlags& flags) {
ScopedRasterFlags raster_flags(&flags, image_provider_,
canvas_->getTotalMatrix(), GetMaxTextureSize(),
1.0f);
if (!raster_flags.flags())
return;
raster_flags.flags()->DrawToSk(
canvas_,
[&oval](SkCanvas* c, const SkPaint& p) { c->drawOval(oval, p); });
FlushAfterDrawIfNeeded();
}
void SkiaPaintCanvas::drawRRect(const SkRRect& rrect, const PaintFlags& flags) {
ScopedRasterFlags raster_flags(&flags, image_provider_,
canvas_->getTotalMatrix(), GetMaxTextureSize(),
1.0f);
if (!raster_flags.flags())
return;
raster_flags.flags()->DrawToSk(
canvas_,
[&rrect](SkCanvas* c, const SkPaint& p) { c->drawRRect(rrect, p); });
FlushAfterDrawIfNeeded();
}
void SkiaPaintCanvas::drawDRRect(const SkRRect& outer,
const SkRRect& inner,
const PaintFlags& flags) {
ScopedRasterFlags raster_flags(&flags, image_provider_,
canvas_->getTotalMatrix(), GetMaxTextureSize(),
1.0f);
if (!raster_flags.flags())
return;
raster_flags.flags()->DrawToSk(
canvas_, [&outer, &inner](SkCanvas* c, const SkPaint& p) {
c->drawDRRect(outer, inner, p);
});
FlushAfterDrawIfNeeded();
}
void SkiaPaintCanvas::drawRoundRect(const SkRect& rect,
SkScalar rx,
SkScalar ry,
const PaintFlags& flags) {
ScopedRasterFlags raster_flags(&flags, image_provider_,
canvas_->getTotalMatrix(), GetMaxTextureSize(),
1.0f);
if (!raster_flags.flags())
return;
raster_flags.flags()->DrawToSk(
canvas_, [&rect, rx, ry](SkCanvas* c, const SkPaint& p) {
c->drawRoundRect(rect, rx, ry, p);
});
FlushAfterDrawIfNeeded();
}
void SkiaPaintCanvas::drawPath(const SkPath& path,
const PaintFlags& flags,
UsePaintCache) {
ScopedRasterFlags raster_flags(&flags, image_provider_,
canvas_->getTotalMatrix(), GetMaxTextureSize(),
1.0f);
if (!raster_flags.flags())
return;
raster_flags.flags()->DrawToSk(
canvas_,
[&path](SkCanvas* c, const SkPaint& p) { c->drawPath(path, p); });
FlushAfterDrawIfNeeded();
}
void SkiaPaintCanvas::drawImage(const PaintImage& image,
SkScalar left,
SkScalar top,
const SkSamplingOptions& sampling,
const PaintFlags* flags) {
DCHECK(!image.IsPaintWorklet());
std::optional<ScopedRasterFlags> scoped_flags;
if (flags) {
scoped_flags.emplace(flags, image_provider_, canvas_->getTotalMatrix(),
GetMaxTextureSize(), 1.0f);
if (!scoped_flags->flags())
return;
}
const PaintFlags* raster_flags = scoped_flags ? scoped_flags->flags() : flags;
PlaybackParams params(image_provider_, canvas_->getLocalToDevice());
DrawImageOp draw_image_op(image, left, top, sampling, nullptr);
DrawImageOp::RasterWithFlags(&draw_image_op, raster_flags, canvas_, params);
FlushAfterDrawIfNeeded();
}
void SkiaPaintCanvas::drawImageRect(const PaintImage& image,
const SkRect& src,
const SkRect& dst,
const SkSamplingOptions& sampling,
const PaintFlags* flags,
SkCanvas::SrcRectConstraint constraint) {
std::optional<ScopedRasterFlags> scoped_flags;
if (flags) {
scoped_flags.emplace(flags, image_provider_, canvas_->getTotalMatrix(),
GetMaxTextureSize(), 1.0f);
if (!scoped_flags->flags())
return;
}
const PaintFlags* raster_flags = scoped_flags ? scoped_flags->flags() : flags;
PlaybackParams params(image_provider_, canvas_->getLocalToDevice());
DrawImageRectOp draw_image_rect_op(image, src, dst, sampling, flags,
constraint);
DrawImageRectOp::RasterWithFlags(&draw_image_rect_op, raster_flags, canvas_,
params);
FlushAfterDrawIfNeeded();
}
void SkiaPaintCanvas::drawVertices(
scoped_refptr<RefCountedBuffer<SkPoint>> vertices,
scoped_refptr<RefCountedBuffer<SkPoint>> uvs,
scoped_refptr<RefCountedBuffer<uint16_t>> indices,
const PaintFlags& flags) {
ScopedRasterFlags raster_flags(&flags, image_provider_,
canvas_->getTotalMatrix(), GetMaxTextureSize(),
/*alpha=*/1.0f);
DrawVerticesOp op(std::move(vertices), std::move(uvs), std::move(indices),
flags);
if (!raster_flags.flags() || !op.IsValid()) {
return;
}
PlaybackParams params(image_provider_);
DrawVerticesOp::RasterWithFlags(&op, raster_flags.flags(), canvas_, params);
FlushAfterDrawIfNeeded();
}
void SkiaPaintCanvas::drawSkottie(scoped_refptr<SkottieWrapper> skottie,
const SkRect& dst,
float t,
SkottieFrameDataMap images,
const SkottieColorMap& color_map,
SkottieTextPropertyValueMap text_map) {
if (!images.empty()) {
// This is not implemented solely because there's no use case yet. To
// implement, we could retrieve the underlying SkImage from each
// PaintImage in |images| here and call SkottieWrapper::SetImageForAsset().
NOTIMPLEMENTED()
<< "Rendering skottie frames with image assets directly to a "
"SkiaPaintCanvas is currently not supported.";
}
skottie->Draw(canvas_, t, dst, SkottieWrapper::FrameDataCallback(), color_map,
std::move(text_map));
}
void SkiaPaintCanvas::drawTextBlob(sk_sp<SkTextBlob> blob,
SkScalar x,
SkScalar y,
const PaintFlags& flags) {
ScopedRasterFlags raster_flags(&flags, image_provider_,
canvas_->getTotalMatrix(), GetMaxTextureSize(),
1.0f);
if (!raster_flags.flags())
return;
raster_flags.flags()->DrawToSk(canvas_,
[&blob, x, y](SkCanvas* c, const SkPaint& p) {
c->drawTextBlob(blob, x, y, p);
});
FlushAfterDrawIfNeeded();
}
void SkiaPaintCanvas::drawTextBlob(sk_sp<SkTextBlob> blob,
SkScalar x,
SkScalar y,
NodeId node_id,
const PaintFlags& flags) {
if (node_id)
SkPDF::SetNodeId(canvas_, node_id);
drawTextBlob(blob, x, y, flags);
if (node_id)
SkPDF::SetNodeId(canvas_, 0);
}
void SkiaPaintCanvas::drawPicture(PaintRecord record) {
drawPicture(std::move(record), PlaybackCallbacks::CustomDataRasterCallback(),
/*local_ctm=*/true);
}
void SkiaPaintCanvas::drawPicture(PaintRecord record, bool local_ctm) {
drawPicture(std::move(record), PlaybackCallbacks::CustomDataRasterCallback(),
local_ctm);
}
SkM44 SkiaPaintCanvas::getLocalToDevice() const {
return canvas_->getLocalToDevice();
}
void SkiaPaintCanvas::Annotate(AnnotationType type,
const SkRect& rect,
sk_sp<SkData> data) {
switch (type) {
case AnnotationType::kUrl:
SkAnnotateRectWithURL(canvas_, rect, data.get());
break;
case AnnotationType::kLinkToDestination:
SkAnnotateLinkToDestination(canvas_, rect, data.get());
break;
case AnnotationType::kNameDestination: {
SkPoint point = SkPoint::Make(rect.x(), rect.y());
SkAnnotateNamedDestination(canvas_, point, data.get());
break;
}
}
}
void SkiaPaintCanvas::setNodeId(int node_id) {
SkPDF::SetNodeId(canvas_, node_id);
}
void SkiaPaintCanvas::drawPicture(
PaintRecord record,
PlaybackCallbacks::CustomDataRasterCallback custom_raster_callback,
bool local_ctm) {
PlaybackCallbacks callbacks;
callbacks.custom_callback = custom_raster_callback;
if (context_flushes_.enable) {
callbacks.did_draw_op_callback = base::BindRepeating(
&SkiaPaintCanvas::FlushAfterDrawIfNeeded, base::Unretained(this));
}
PlaybackParams params(image_provider_, canvas_->getLocalToDevice(),
callbacks);
record.Playback(canvas_, params, local_ctm);
}
void SkiaPaintCanvas::FlushAfterDrawIfNeeded() {
if (!context_flushes_.enable)
return;
if (++num_of_ops_ > context_flushes_.max_draws_before_flush) {
num_of_ops_ = 0;
TRACE_EVENT0("cc",
"SkiaPaintCanvas::FlushAfterDrawIfNeeded::FlushGrContext");
if (GrDirectContext* direct_context =
GrAsDirectContext(canvas_->recordingContext())) {
direct_context->flushAndSubmit();
}
}
}
int SkiaPaintCanvas::GetMaxTextureSize() const {
auto* context = canvas_->recordingContext();
return context ? context->maxTextureSize() : 0;
}
} // namespace cc