blob: e4ec8a5a18697db6732ac81a617e70065834c460 [file] [log] [blame]
// Copyright 2019 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 "components/paint_preview/renderer/paint_preview_recorder_utils.h"
#include <utility>
#include "base/bind.h"
#include "base/trace_event/common/trace_event_common.h"
#include "base/trace_event/trace_event.h"
#include "cc/paint/paint_image.h"
#include "cc/paint/paint_image_builder.h"
#include "components/paint_preview/common/file_stream.h"
#include "components/paint_preview/common/paint_preview_tracker.h"
#include "mojo/public/cpp/base/shared_memory_utils.h"
#include "third_party/skia/include/core/SkData.h"
#include "third_party/skia/include/core/SkRefCnt.h"
namespace paint_preview {
namespace {
// Converts a texture backed paint image in the PaintOpBuffer to one that is not
// texture backed.
cc::PaintImage MakeUnaccelerated(cc::PaintImage& paint_image) {
DCHECK(paint_image.IsTextureBacked());
auto sk_image = paint_image.GetSwSkImage();
if (sk_image->isLazyGenerated()) {
// Texture backed images should always be returned as SkImage_Raster type
// (bitmap). This is just a catchall in the event a lazy image is somehow
// returned in which case we should just raster it.
SkBitmap bitmap;
bitmap.allocPixels(sk_image->imageInfo(),
sk_image->imageInfo().minRowBytes());
if (!sk_image->readPixels(bitmap.pixmap(), 0, 0)) {
return paint_image;
}
// Make immutable to skip an extra copy.
bitmap.setImmutable();
sk_image = SkImage::MakeFromBitmap(bitmap);
}
return cc::PaintImageBuilder::WithDefault()
.set_id(cc::PaintImage::GetNextId())
.set_image(sk_image, cc::PaintImage::GetNextContentId())
.TakePaintImage();
}
} // namespace
void PreProcessPaintOpBuffer(const cc::PaintOpBuffer* buffer,
PaintPreviewTracker* tracker) {
for (cc::PaintOpBuffer::Iterator it(buffer); it; ++it) {
switch (it->GetType()) {
case cc::PaintOpType::DrawTextBlob: {
auto* text_blob_op = static_cast<cc::DrawTextBlobOp*>(*it);
tracker->AddGlyphs(text_blob_op->blob.get());
break;
}
case cc::PaintOpType::DrawRecord: {
// Recurse into nested records if they contain text blobs (equivalent to
// nested SkPictures).
auto* record_op = static_cast<cc::DrawRecordOp*>(*it);
PreProcessPaintOpBuffer(record_op->record.get(), tracker);
break;
}
case cc::PaintOpType::Annotate: {
auto* annotate_op = static_cast<cc::AnnotateOp*>(*it);
tracker->AnnotateLink(GURL(std::string(reinterpret_cast<const char*>(
annotate_op->data->data()),
annotate_op->data->size())),
annotate_op->rect);
// Delete the data. We no longer need it.
annotate_op->data.reset();
break;
}
case cc::PaintOpType::CustomData: {
auto* custom_op = static_cast<cc::CustomDataOp*>(*it);
tracker->TransformClipForFrame(custom_op->id);
break;
}
case cc::PaintOpType::Save: {
tracker->Save();
break;
}
case cc::PaintOpType::SaveLayer: {
tracker->Save();
break;
}
case cc::PaintOpType::SaveLayerAlpha: {
tracker->Save();
break;
}
case cc::PaintOpType::Restore: {
tracker->Restore();
break;
}
case cc::PaintOpType::SetMatrix: {
auto* matrix_op = static_cast<cc::SetMatrixOp*>(*it);
tracker->SetMatrix(matrix_op->matrix.asM33());
break;
}
case cc::PaintOpType::Concat: {
auto* concat_op = static_cast<cc::ConcatOp*>(*it);
tracker->Concat(concat_op->matrix.asM33());
break;
}
case cc::PaintOpType::Scale: {
auto* scale_op = static_cast<cc::ScaleOp*>(*it);
tracker->Scale(scale_op->sx, scale_op->sy);
break;
}
case cc::PaintOpType::Rotate: {
auto* rotate_op = static_cast<cc::RotateOp*>(*it);
tracker->Rotate(rotate_op->degrees);
break;
}
case cc::PaintOpType::Translate: {
auto* translate_op = static_cast<cc::TranslateOp*>(*it);
tracker->Translate(translate_op->dx, translate_op->dy);
break;
}
case cc::PaintOpType::DrawImage: {
auto* image_op = static_cast<cc::DrawImageOp*>(*it);
if (image_op->image.IsTextureBacked()) {
image_op->image = MakeUnaccelerated(image_op->image);
}
break;
}
case cc::PaintOpType::DrawImageRect: {
auto* image_op = static_cast<cc::DrawImageRectOp*>(*it);
if (image_op->image.IsTextureBacked()) {
image_op->image = MakeUnaccelerated(image_op->image);
}
break;
}
default:
continue;
}
}
}
sk_sp<const SkPicture> PaintRecordToSkPicture(
sk_sp<const cc::PaintRecord> recording,
PaintPreviewTracker* tracker,
const gfx::Rect& bounds) {
// base::Unretained is safe as |tracker| outlives the usage of
// |custom_callback|.
cc::PlaybackParams::CustomDataRasterCallback custom_callback =
base::BindRepeating(&PaintPreviewTracker::CustomDataToSkPictureCallback,
base::Unretained(tracker));
auto skp =
ToSkPicture(recording, SkRect::MakeWH(bounds.width(), bounds.height()),
nullptr, custom_callback);
if (!skp || skp->cullRect().width() == 0 || skp->cullRect().height() == 0)
return nullptr;
return skp;
}
void BuildResponse(PaintPreviewTracker* tracker,
mojom::PaintPreviewCaptureResponse* response) {
// Ensure these always exist.
DCHECK(tracker);
DCHECK(response);
response->embedding_token = tracker->EmbeddingToken();
tracker->MoveLinks(&response->links);
PictureSerializationContext* picture_context =
tracker->GetPictureSerializationContext();
if (!picture_context)
return;
for (const auto& id_pair : picture_context->content_id_to_embedding_token) {
response->content_id_to_embedding_token.insert(id_pair);
}
}
} // namespace paint_preview