| /* |
| * 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/SkCapture.h" |
| |
| #include "include/core/SkBitmap.h" |
| #include "include/core/SkCanvas.h" |
| #include "include/core/SkData.h" |
| #include "include/core/SkFourByteTag.h" |
| #include "include/core/SkImage.h" |
| #include "include/core/SkPicture.h" |
| #include "include/core/SkRefCnt.h" |
| #include "include/core/SkSerialProcs.h" |
| #include "include/core/SkStream.h" |
| #include "include/private/base/SkTArray.h" |
| #include "src/capture/SkCaptureManager.h" |
| |
| constexpr SkFourByteTag kMagic1 = SkSetFourByteTag('s','k','i','a'); |
| constexpr SkFourByteTag kMagic2 = SkSetFourByteTag('c','a','p','t'); |
| |
| sk_sp<SkCapture> SkCapture::MakeFromData(sk_sp<SkData> data) { |
| if (!data) { |
| return nullptr; |
| } |
| |
| // 1. Setup Stream |
| SkMemoryStream stream(data->data(), data->size()); |
| |
| // 2. Read and Validate Magic Number |
| uint32_t magic1; |
| uint32_t magic2; |
| if (!stream.readU32(&magic1) || !stream.readU32(&magic2) || |
| magic1 != kMagic1 || magic2 != kMagic2) { |
| SkDebugf("Invalid magic number for SkCapture.\n"); |
| return nullptr; |
| } |
| |
| // 3. Read and Validate Version |
| uint32_t version; |
| if (!stream.readU32(&version) || version != kVersion) { |
| SkDebugf("Unsupported SkCapture version: %u.\n", version); |
| return nullptr; |
| } |
| |
| // 4. Read Picture Count |
| uint32_t pictureCount; |
| if (!stream.readU32(&pictureCount)) { |
| SkDebugf("Failed to read picture count.\n"); |
| return nullptr; |
| } |
| |
| auto capture = sk_make_sp<SkCapture>(); |
| capture->fMetadata = {version, pictureCount}; |
| |
| // 5. Loop and Deserialize Each Picture |
| for (uint32_t i = 0; i < pictureCount; ++i) { |
| uint32_t pictureDataSize; |
| if (!stream.readU32(&pictureDataSize)) { |
| SkDebugf("Failed to read picture data size for picture %u.\n", i); |
| return nullptr; |
| } |
| |
| // Read the picture data into an SkData object |
| sk_sp<SkData> pictureData = SkData::MakeUninitialized(pictureDataSize); |
| |
| // Check if allocation failed *or* if the stream read failed. |
| if (!pictureData || stream.read(pictureData->writable_data(), |
| pictureDataSize) != pictureDataSize) { |
| SkDebugf("Failed to read picture data for picture %u or allocation failed.\n", i); |
| return nullptr; |
| } |
| |
| // Deserialize the SkPicture from its raw data |
| SkDeserialProcs procs; |
| procs.fImageDataProc = SkCapture::deserializeImageProc; |
| sk_sp<SkPicture> picture = SkPicture::MakeFromData(pictureData.get(), &procs); |
| if (!picture) { |
| SkDebugf("Failed to deserialize SkPicture for picture %u.\n", i); |
| return nullptr; |
| } |
| |
| // Add the deserialized picture to the SkCapture object |
| capture->fPictures.emplace_back(std::move(picture)); |
| } |
| |
| SkDebugf("Successfully read %d pictures into SkCapture.\n", capture->fPictures.size()); |
| return capture; |
| } |
| |
| sk_sp<SkCapture> SkCapture::MakeFromPictures(skia_private::TArray<sk_sp<SkPicture>> pictures) { |
| auto capture = sk_make_sp<SkCapture>(); |
| |
| capture->fMetadata = {SkCapture::kVersion, static_cast<uint32_t>(pictures.size())}; |
| capture->fPictures = pictures; |
| return capture; |
| } |
| |
| sk_sp<SkPicture> SkCapture::getPicture(int i) const { |
| if (i >= 0 && i < fPictures.size()) { |
| return fPictures[i]; |
| } |
| return nullptr; |
| } |
| |
| SkCapture::Metadata SkCapture::getMetadata() const { |
| return fMetadata; |
| } |
| |
| sk_sp<SkData> SkCapture::serializeCapture() { |
| SkDynamicMemoryWStream stream; |
| |
| stream.write32(kMagic1); |
| stream.write32(kMagic2); |
| stream.write32(SkCapture::kVersion); |
| |
| // Number of pictures |
| stream.write32(fPictures.size()); |
| |
| // TODO (b/412351769): Write metadata on each picture and assosiated canvas. This will be needed |
| // when we have multiple SkPictures per canvas and we want to track which ones are drawn into |
| // each other. |
| |
| for (const auto& picture : fPictures) { |
| SkDynamicMemoryWStream pictureStream; |
| SkSerialProcs procs; |
| procs.fImageProc = SkCapture::serializeImageProc; |
| picture->serialize(&pictureStream, &procs); |
| sk_sp<SkData> pictureData = pictureStream.detachAsData(); |
| |
| // Write size and then data |
| stream.write32(pictureData->size()); |
| stream.write(pictureData->data(), pictureData->size()); |
| } |
| |
| auto data = stream.detachAsData(); |
| SkDebugf("Wrote %d pictures to SkData block.\n", fPictures.size()); |
| return data; |
| } |
| |
| // TODO(b/412351769): When serializing our SkPictures, the images that are drawn through drawImage |
| // and similar functions will also need to be serialized. Instead of naively encoding all images as |
| // PNGs, we want the images that refer to content created from an SkSurface to point to its |
| // corresponding SkPicture. This means that we need to create a context for the proc to track these |
| // contentIDs. |
| sk_sp<SkData> SkCapture::serializeImageProc(SkImage* img, void* ctx) { |
| const int contentID = -1; // TODO: replace with real content ID. |
| sk_sp<SkData> data = SkData::MakeWithCopy(&contentID, sizeof(int)); |
| return data; |
| } |
| |
| sk_sp<SkImage> SkCapture::deserializeImageProc(sk_sp<SkData> data, std::optional<SkAlphaType> a, |
| void* ctx) { |
| // TODO: set up the SkCapture context and inspect it to grab SkPictures and pass them as images. |
| SkBitmap b; |
| b.allocN32Pixels(5, 5); |
| SkCanvas canvas(b); |
| canvas.drawColor(SK_ColorMAGENTA); |
| return b.asImage(); |
| } |