| /* |
| * Copyright 2024 Google LLC. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| #ifndef SkPngRustCodec_DEFINED |
| #define SkPngRustCodec_DEFINED |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "rust/png/FFI.rs.h" |
| #include "src/codec/SkFrameHolder.h" |
| #include "src/codec/SkPngCodecBase.h" |
| #include "third_party/rust/cxx/v1/cxx.h" |
| |
| struct SkEncodedInfo; |
| class SkFrame; |
| class SkStream; |
| template <typename T> class SkSpan; |
| |
| // This class provides the Skia image decoding API (`SkCodec`) on top of: |
| // * The third-party `png` crate (PNG decompression and decoding implemented in |
| // Rust) |
| // * Skia's `SkSwizzler` and `skcms_Transform` (pixel format and color space |
| // transformations implemented in C++). |
| class SkPngRustCodec final : public SkPngCodecBase { |
| public: |
| static std::unique_ptr<SkPngRustCodec> MakeFromStream(std::unique_ptr<SkStream>, Result*); |
| |
| // `public` to support `std::make_unique<SkPngRustCodec>(...)`. |
| SkPngRustCodec(SkEncodedInfo&&, std::unique_ptr<SkStream>, rust::Box<rust_png::Reader>); |
| |
| ~SkPngRustCodec() override; |
| |
| private: |
| struct DecodingDstInfo { |
| // `fDst` is based on `pixels` passed to `onGetPixels` or |
| // `onStartIncrementalDecode`. For interlaced and non-interlaced |
| // images, `startDecoding` initializes `fDst` to start at the (0,0) |
| // (top-left) pixel of the current frame (which may be offset from |
| // `pixels` if the current frame is a sub-rect of the full image). |
| // After decoding a non-interlaced row this moves (by `fDstRowStride`) |
| // to the next row. |
| SkSpan<uint8_t> fDst; |
| |
| // Size of a row (in bytes) in the full image. Based on `rowBytes` |
| // passed to `onGetPixels` or `onStartIncrementalDecode`. |
| size_t fDstRowStride = 0; |
| |
| // Size of a row (in bytes) in the current frame. |
| size_t fDstRowSize = 0; |
| |
| // Bytes per pixel of fDst. |
| uint8_t fDstBytesPerPixel = 0; |
| }; |
| |
| struct DecodingState { |
| // The info and pixels we will be decoding into. |
| DecodingDstInfo fDecodingDstInfo; |
| |
| // Intermediate buffer that holds color-transformed pixels that are |
| // ready to be blended with the destination. Used only when this frame |
| // uses `SkCodecAnimation::Blend::kSrcOver`. For interlaced images this |
| // buffer holds the whole frame; otherwise it holds only a single row. |
| |
| // This is also used in the case of subsets for interlaced images. We use |
| // this buffer as a full sized encoded image, which we then take the subset |
| // from. |
| // TODO: Subsets of APNG not supported, but if we need to, we would need |
| // a separate fInterlacedBuffer along with fPreblendBuffer. |
| std::vector<uint8_t> fPreblendBuffer; |
| |
| int fFirstRow = 0; |
| int fLastRow = 0; |
| // The y offset for a subset in the encoded color type, not the dst color type. |
| // Only used for interlaced images. |
| size_t fYByteOffset = 0; |
| }; |
| |
| // Helper for validating parameters of `onGetPixels` and/or |
| // `onStartIncrementalDecode`. If `kSuccess` is returned then |
| // `decodingState` output parameter got populated. |
| Result startDecoding(const SkImageInfo& dstInfo, |
| void* pixels, |
| size_t rowBytes, |
| const Options& options, |
| DecodingState* decodingState); |
| |
| // Helper for taking a decoded interlaced `srcRow`, applying color |
| // transformations, and then expanding it into the `frame`. |
| void expandDecodedInterlacedRow(SkSpan<uint8_t> dstFrame, |
| SkSpan<const uint8_t> srcRow, |
| const DecodingDstInfo& decodingState, |
| bool xFormNeeded); |
| |
| // Helper for row-by-row decoding which is used from `onGetPixels` and/or |
| // `onIncrementalDecode`. |
| Result incrementalDecode(DecodingState& decodingState, int* rowsDecoded); |
| // The same as incrementalDecode but uses `applyXFormRow()`. Should only |
| // be used if this->canReadRows() is false. |
| Result incrementalDecodeXForm(DecodingState& decodingState, int* rowsDecoded); |
| |
| // Helper for reading until the start of the next `fdAT` sequence. |
| Result readToStartOfNextFrame(); |
| |
| // Helper for seeking to the start of image data for the given frame. |
| Result seekToStartOfFrame(int index); |
| |
| // The number of frames calculated based on 1) the presence, and 2) the |
| // contents of an `acTL` chunk. "raw" in the sense that it reports all the |
| // frames, while `SkCodec::getFrameCount` and |
| // `SkPngRustCodec::onGetFrameCount` only report frames for which we have |
| // successfully populated `fFrameHolder` with frame info parsed from `IHDR` |
| // and/or `fcTL` chunks. |
| int getRawFrameCount() const; |
| |
| // Attempts to read through the input stream to parse the additional `fcTL` |
| // chunks. |
| Result parseAdditionalFrameInfos(); |
| |
| // SkCodec overrides: |
| Result onGetPixels(const SkImageInfo& dstInfo, |
| void* pixels, |
| size_t rowBytes, |
| const Options&, |
| int* rowsDecoded) override; |
| Result onStartIncrementalDecode(const SkImageInfo& dstInfo, |
| void* pixels, |
| size_t rowBytes, |
| const Options&) override; |
| Result onIncrementalDecode(int* rowsDecoded) override; |
| int onGetFrameCount() override; |
| bool onGetFrameInfo(int, FrameInfo*) const override; |
| int onGetRepetitionCount() override; |
| IsAnimated onIsAnimated() override; |
| const SkFrameHolder* getFrameHolder() const override; |
| std::unique_ptr<SkStream> getEncodedData() const override; |
| // Determines whether or not we can read from the rust decoder directly into dst. |
| bool canReadRow(); |
| |
| // SkPngCodecBase overrides: |
| std::optional<SkSpan<const PaletteColorEntry>> onTryGetPlteChunk() override; |
| std::optional<SkSpan<const uint8_t>> onTryGetTrnsChunk() override; |
| |
| rust::Box<rust_png::Reader> fReader; |
| |
| // `-1` means that `IDAT` is not part of animation and wasn't skipped yet. |
| int fFrameAtCurrentStreamPosition = -1; |
| bool fStreamIsPositionedAtStartOfFrameData = false; |
| const std::unique_ptr<SkStream> fPrivStream; |
| // TODO(https://crbug.com/371060427): Once fast seeking is available, we can |
| // remove the field that tracks the stream length. |
| std::optional<size_t> fMaxStreamLengthSeenWhenParsingAdditionalFrameInfos; |
| |
| std::optional<DecodingState> fIncrementalDecodingState; |
| |
| class FrameHolder final : public SkFrameHolder { |
| public: |
| FrameHolder(int width, int height); |
| ~FrameHolder() override; |
| |
| FrameHolder(const FrameHolder&) = delete; |
| FrameHolder(FrameHolder&&) = delete; |
| FrameHolder& operator=(const FrameHolder&) = delete; |
| FrameHolder& operator=(FrameHolder&&) = delete; |
| |
| // Returning an `int` (rather than `size_t`) for easier interop with |
| // other parts of the SkCodec API. |
| int size() const; |
| |
| Result appendNewFrame(const rust_png::Reader& reader, const SkEncodedInfo& info); |
| void markFrameAsFullyReceived(size_t index); |
| bool getFrameInfo(int index, FrameInfo* info) const; |
| |
| private: |
| class PngFrame; |
| |
| const SkFrame* onGetFrame(int unverifiedIndex) const override; |
| Result setFrameInfoFromCurrentFctlChunk(const rust_png::Reader& reader, |
| PngFrame* out_frame); |
| |
| std::vector<PngFrame> fFrames; |
| }; |
| FrameHolder fFrameHolder; |
| |
| // Whether there may still be additional `fcTL` chunks to discover and parse. |
| // |
| // `true` if the stream hasn't been fully received (i.e. only |
| // `kIncompleteInput` errors so far, no hard errors) and `fFrameHolder` |
| // doesn't yet contain frame info for all `num_frames` declared in an `acTL` |
| // chunk. |
| bool fCanParseAdditionalFrameInfos = true; |
| |
| // When decoding interlaced subsets, decode the full image into a buffer in |
| // the encoded colortype and extract a rect subset for the final dst using |
| // this function. This function will use applyXFormRow() for each row. |
| void getSubsetFromFullImage(SkSpan<const uint8_t> fullImageBuffer, |
| SkSpan<uint8_t> dst, |
| size_t dstRowStride, |
| size_t offset); |
| }; |
| |
| #endif // SkPngRustCodec_DEFINED |