blob: 44eba136e4fb8619e98109fe094848c17e6aa5a9 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/paint_preview/common/serialized_recording.h"
#include <optional>
#include "base/notreached.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/trace_event/common/trace_event_common.h"
#include "base/trace_event/trace_event.h"
#include "components/paint_preview/common/file_stream.h"
#include "components/paint_preview/common/paint_preview_tracker.h"
#include "components/paint_preview/common/serial_utils.h"
#include "mojo/public/cpp/base/big_buffer.h"
#include "skia/ext/skia_utils_base.h"
#include "third_party/skia/include/core/SkStream.h"
namespace paint_preview {
namespace {
// Serializes |skp| to |out_stream| as an SkPicture of size |dimensions|.
// |tracker| supplies metadata required during serialization.
bool SerializeSkPicture(sk_sp<const SkPicture> skp,
PaintPreviewTracker* tracker,
SkWStream* out_stream) {
TypefaceSerializationContext typeface_context(tracker->GetTypefaceUsageMap());
ImageSerializationContext* image_context =
tracker->GetImageSerializationContext();
auto serial_procs = MakeSerialProcs(tracker->GetPictureSerializationContext(),
&typeface_context, image_context);
skp->serialize(out_stream, &serial_procs);
out_stream->flush();
// If the memory budget was exceeded while serializing images and it is not
// tolerated (inferred from setting a max decoded image size) then abort.
const bool tolerates_discarding =
image_context->max_decoded_image_size_bytes !=
std::numeric_limits<uint64_t>::max();
return tolerates_discarding || !image_context->memory_budget_exceeded;
}
} // namespace
SkpResult::SkpResult() = default;
SkpResult::~SkpResult() = default;
SkpResult::SkpResult(SkpResult&& other) = default;
SkpResult& SkpResult::operator=(SkpResult&& rhs) = default;
SerializedRecording::SerializedRecording()
: persistence_(RecordingPersistence::kFileSystem), file_(), buffer_() {}
SerializedRecording::SerializedRecording(base::FilePath path)
: SerializedRecording(
base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ)) {}
SerializedRecording::SerializedRecording(base::File file)
: persistence_(RecordingPersistence::kFileSystem),
file_(std::move(file)),
buffer_() {}
SerializedRecording::SerializedRecording(mojo_base::BigBuffer buffer)
: persistence_(RecordingPersistence::kMemoryBuffer),
file_(),
buffer_(std::move(buffer)) {}
SerializedRecording::SerializedRecording(SerializedRecording&&) = default;
SerializedRecording& SerializedRecording::operator=(SerializedRecording&&) =
default;
SerializedRecording::~SerializedRecording() {
if (is_file() && file_.IsValid()) {
base::ThreadPool::PostTask(
FROM_HERE,
{base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
base::BindOnce([](base::File file) { file.Close(); },
std::move(file_)));
}
}
bool SerializedRecording::IsValid() const {
if (is_file()) {
return file_.IsValid();
} else if (is_buffer()) {
return buffer_.has_value();
} else {
NOTREACHED();
}
}
std::optional<SkpResult> SerializedRecording::Deserialize() && {
TRACE_EVENT0("paint_preview", "SerializedRecording::Deserialize");
SkpResult result;
SkDeserialProcs procs = MakeDeserialProcs(&result.ctx);
if (is_file()) {
FileRStream stream(std::move(file_));
result.skp = SkPicture::MakeFromStream(&stream, &procs);
} else if (is_buffer()) {
CHECK(buffer_.has_value());
SkMemoryStream stream(buffer_->data(), buffer_->size(),
/*copyData=*/false);
result.skp = SkPicture::MakeFromStream(&stream, &procs);
} else {
NOTREACHED();
}
return {std::move(result)};
}
sk_sp<SkPicture> SerializedRecording::DeserializeWithContext(
LoadedFramesDeserialContext* ctx) && {
TRACE_EVENT0("paint_preview", "SerializedRecording::DeserializeWithContext");
SkDeserialProcs procs = MakeDeserialProcs(ctx);
if (is_file()) {
FileRStream stream(std::move(file_));
return SkPicture::MakeFromStream(&stream, &procs);
} else if (is_buffer()) {
CHECK(buffer_.has_value());
SkMemoryStream stream(buffer_->data(), buffer_->size(),
/*copyData=*/false);
return SkPicture::MakeFromStream(&stream, &procs);
} else {
NOTREACHED();
}
}
bool RecordToFile(base::File file,
sk_sp<const SkPicture> skp,
PaintPreviewTracker* tracker,
std::optional<size_t> max_capture_size,
size_t* serialized_size) {
if (!file.IsValid()) {
return false;
}
if (max_capture_size.has_value() && max_capture_size.value() == 0) {
return false;
}
FileWStream file_stream(std::move(file), max_capture_size.value_or(0));
if (!SerializeSkPicture(skp, tracker, &file_stream)) {
return false;
}
file_stream.Close();
*serialized_size = file_stream.ActualBytesWritten();
return !file_stream.DidWriteFail();
}
std::optional<mojo_base::BigBuffer> RecordToBuffer(
sk_sp<const SkPicture> skp,
PaintPreviewTracker* tracker,
std::optional<size_t> maybe_max_capture_size,
size_t* serialized_size) {
SkDynamicMemoryWStream memory_stream;
if (!SerializeSkPicture(skp, tracker, &memory_stream)) {
return std::nullopt;
}
size_t max_capture_size = maybe_max_capture_size.value_or(SIZE_MAX);
if (max_capture_size == 0) {
return std::nullopt;
}
TRACE_EVENT_BEGIN0("paint_preview", "CopyToBigBuffer");
sk_sp<SkData> data = memory_stream.detachAsData();
*serialized_size = std::min(data->size(), max_capture_size);
mojo_base::BigBuffer buffer(
skia::as_byte_span(*data).first(*serialized_size));
TRACE_EVENT_END0("paint_preview", "CopyToBigBuffer");
if (data->size() > max_capture_size) {
return std::nullopt;
}
return {std::move(buffer)};
}
} // namespace paint_preview