blob: 43f09923b46330a7bb232f86d3c735c4df04007e [file]
/*
* Copyright 2025 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/capture/SkCaptureManager.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkPicture.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkSurface.h"
#include "src/capture/SkCapture.h"
#include "src/capture/SkCaptureCanvas.h"
#include "src/image/SkSurface_Base.h"
#include <algorithm>
#include <memory>
SkCaptureManager::SkCaptureManager() {}
SkCanvas* SkCaptureManager::makeCaptureCanvas(SkCanvas* canvas) {
auto newCanvas = std::make_unique<SkCaptureCanvas>(canvas, this);
auto rawCanvasPtr = newCanvas.get();
fTrackedCanvases.emplace_back(std::move(newCanvas));
return rawCanvasPtr;
}
void SkCaptureManager::deregisterCaptureCanvas(SkCanvas* canvas) {
if (!canvas) {
return;
}
for (int i = 0; i < fTrackedCanvases.size(); ++i) {
if (fTrackedCanvases[i].get() != canvas) { continue; }
// Make sure we gather any draws that haven't been inserted into a recording
// before removing the canvas.
if (fIsCurrentlyCapturing) {
auto picture = this->snapAndIncrement(fTrackedCanvases[i].get());
if (picture && fActiveCapture) {
fActiveCapture->addAsset(std::move(picture));
}
}
fTrackedCanvases.removeShuffle(i);
return;
}
}
sk_sp<SkPicture> SkCaptureManager::snapAndIncrement(SkCaptureCanvas* canvas) {
auto picture = canvas->snapPicture();
if (picture) {
if (auto storage = asSB(canvas->getBaseCanvasSurface())->getPixelStorage()) {
storage->incrementContentId();
}
}
return picture;
}
void SkCaptureManager::captureUninsertedDrawTasks() {
for (auto& canvas : fTrackedCanvases) {
if (canvas) {
auto picture = this->snapAndIncrement(canvas.get());
if (picture && fActiveCapture) {
fActiveCapture->addAsset(std::move(picture));
}
}
}
}
// TODO: make thread safe by using exchange() and a mutex.
void SkCaptureManager::toggleCapture(bool capturing) {
if (capturing != fIsCurrentlyCapturing) {
if (capturing) {
fActiveCapture = SkCapture::MakeEmpty();
} else {
// on capture stop, save the capture and reset
this->captureUninsertedDrawTasks();
fLastCapture = std::move(fActiveCapture);
}
}
fIsCurrentlyCapturing = capturing;
}
sk_sp<SkPicture> SkCaptureManager::snapPicture(SkSurface* surface) {
for (auto& canvas : fTrackedCanvases) {
if (canvas) {
if (canvas->getBaseCanvasSurface() == surface) {
return this->snapAndIncrement(canvas.get());
}
}
}
return nullptr;
}
sk_sp<SkCapture> SkCaptureManager::getLastCapture() const {
return fLastCapture;
}
skia_private::TArray<sk_sp<SkPicture>> SkCaptureManager::snapDrawTasksForStorageIDs(
SkSpan<const uint32_t> storageIds) {
skia_private::TArray<sk_sp<SkPicture>> snapped;
if (!fIsCurrentlyCapturing || storageIds.empty()) {
return snapped;
}
for (auto& canvas : fTrackedCanvases) {
if (!canvas) {
continue;
}
SkSurface* surface = canvas->getBaseCanvasSurface();
if (!surface) {
continue;
}
auto storage = asSB(surface)->getPixelStorage();
if (!storage) {
continue;
}
uint32_t id = storage->getPixelStorageId();
if (std::find(storageIds.begin(), storageIds.end(), id) != storageIds.end()) {
if (auto picture = this->snapAndIncrement(canvas.get())) {
snapped.push_back(std::move(picture));
}
}
}
return snapped;
}
void SkCaptureManager::onInsertRecording(const skia_private::TArray<sk_sp<SkPicture>>& capturedPictures) {
if (!fIsCurrentlyCapturing || !fActiveCapture) return;
skia_private::TArray<SkCapture::DrawTask> drawTasks;
for (const auto& pic : capturedPictures) {
fActiveCapture->addAsset(pic);
uint32_t assetIdx = fActiveCapture->getMetadata().numAssets - 1;
drawTasks.push_back({assetIdx});
}
SkCapture::RecordingCapture rec = {
std::move(drawTasks)
};
fActiveCapture->addRecordingCapture(std::move(rec));
}