1 /* 2 * Copyright 2024 Google LLC. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 #ifndef SkPngRustCodec_DEFINED 8 #define SkPngRustCodec_DEFINED 9 10 #include <memory> 11 #include <vector> 12 13 #include "experimental/rust_png/ffi/FFI.rs.h" 14 #include "src/codec/SkFrameHolder.h" 15 #include "src/codec/SkPngCodecBase.h" 16 #include "third_party/rust/cxx/v1/cxx.h" 17 18 struct SkEncodedInfo; 19 class SkFrame; 20 class SkStream; 21 template <typename T> class SkSpan; 22 23 // This class provides the Skia image decoding API (`SkCodec`) on top of: 24 // * The third-party `png` crate (PNG decompression and decoding implemented in 25 // Rust) 26 // * Skia's `SkSwizzler` and `skcms_Transform` (pixel format and color space 27 // transformations implemented in C++). 28 class SkPngRustCodec final : public SkPngCodecBase { 29 public: 30 static std::unique_ptr<SkPngRustCodec> MakeFromStream(std::unique_ptr<SkStream>, Result*); 31 32 // `public` to support `std::make_unique<SkPngRustCodec>(...)`. 33 SkPngRustCodec(SkEncodedInfo&&, std::unique_ptr<SkStream>, rust::Box<rust_png::Reader>); 34 35 ~SkPngRustCodec() override; 36 37 private: 38 struct DecodingState { 39 // `fDst` is based on `pixels` passed to `onGetPixels` or 40 // `onStartIncrementalDecode`. For interlaced and non-interlaced 41 // images, `startDecoding` initializes `fDst` to start at the (0,0) 42 // (top-left) pixel of the current frame (which may be offset from 43 // `pixels` if the current frame is a sub-rect of the full image). 44 // After decoding a non-interlaced row this moves (by `fDstRowStride`) 45 // to the next row. 46 SkSpan<uint8_t> fDst; 47 48 // Size of a row (in bytes) in the full image. Based on `rowBytes` 49 // passed to `onGetPixels` or `onStartIncrementalDecode`. 50 size_t fDstRowStride = 0; 51 52 // Size of a row (in bytes) in the current frame. 53 size_t fDstRowSize = 0; 54 55 // Intermediate buffer that holds color-transformed pixels that are 56 // ready to be blended with the destination. Used only when this frame 57 // uses `SkCodecAnimation::Blend::kSrcOver`. For interlaced images this 58 // buffer holds the whole frame; otherwise it holds only a single row. 59 std::vector<uint8_t> fPreblendBuffer; 60 }; 61 62 // Helper for validating parameters of `onGetPixels` and/or 63 // `onStartIncrementalDecode`. If `kSuccess` is returned then 64 // `decodingState` output parameter got populated. 65 Result startDecoding(const SkImageInfo& dstInfo, 66 void* pixels, 67 size_t rowBytes, 68 const Options& options, 69 DecodingState* decodingState); 70 71 // Helper for taking a decoded interlaced `srcRow`, applying color 72 // transformations, and then expanding it into the `frame`. 73 void expandDecodedInterlacedRow(SkSpan<uint8_t> dstFrame, 74 SkSpan<const uint8_t> srcRow, 75 const DecodingState& decodingState); 76 77 // Helper for row-by-row decoding which is used from `onGetPixels` and/or 78 // `onIncrementalDecode`. 79 Result incrementalDecode(DecodingState& decodingState, int* rowsDecoded); 80 81 // Helper for reading until the start of the next `fdAT` sequence. 82 Result readToStartOfNextFrame(); 83 84 // Helper for seeking to the start of image data for the given frame. 85 Result seekToStartOfFrame(int index); 86 87 // The number of frames calculated based on 1) the presence, and 2) the 88 // contents of an `acTL` chunk. "raw" in the sense that it reports all the 89 // frames, while `SkCodec::getFrameCount` and 90 // `SkPngRustCodec::onGetFrameCount` only report frames for which we have 91 // successfully populated `fFrameHolder` with frame info parsed from `IHDR` 92 // and/or `fcTL` chunks. 93 int getRawFrameCount() const; 94 95 // Attempts to read through the input stream to parse the additional `fcTL` 96 // chunks. 97 Result parseAdditionalFrameInfos(); 98 99 // SkCodec overrides: 100 Result onGetPixels(const SkImageInfo& dstInfo, 101 void* pixels, 102 size_t rowBytes, 103 const Options&, 104 int* rowsDecoded) override; 105 Result onStartIncrementalDecode(const SkImageInfo& dstInfo, 106 void* pixels, 107 size_t rowBytes, 108 const Options&) override; 109 Result onIncrementalDecode(int* rowsDecoded) override; 110 int onGetFrameCount() override; 111 bool onGetFrameInfo(int, FrameInfo*) const override; 112 int onGetRepetitionCount() override; 113 const SkFrameHolder* getFrameHolder() const override; 114 std::unique_ptr<SkStream> getEncodedData() const override; 115 116 // SkPngCodecBase overrides: 117 std::optional<SkSpan<const PaletteColorEntry>> onTryGetPlteChunk() override; 118 std::optional<SkSpan<const uint8_t>> onTryGetTrnsChunk() override; 119 120 rust::Box<rust_png::Reader> fReader; 121 122 // `-1` means that `IDAT` is not part of animation and wasn't skipped yet. 123 int fFrameAtCurrentStreamPosition = -1; 124 bool fStreamIsPositionedAtStartOfFrameData = false; 125 const std::unique_ptr<SkStream> fPrivStream; 126 // TODO(https://crbug.com/371060427): Once fast seeking is available, we can 127 // remove the field that tracks the stream length. 128 std::optional<size_t> fStreamLengthDuringLastCallToParseAdditionalFrameInfos; 129 130 std::optional<DecodingState> fIncrementalDecodingState; 131 132 class FrameHolder final : public SkFrameHolder { 133 public: 134 FrameHolder(int width, int height); 135 ~FrameHolder() override; 136 137 FrameHolder(const FrameHolder&) = delete; 138 FrameHolder(FrameHolder&&) = delete; 139 FrameHolder& operator=(const FrameHolder&) = delete; 140 FrameHolder& operator=(FrameHolder&&) = delete; 141 142 // Returning an `int` (rather than `size_t`) for easier interop with 143 // other parts of the SkCodec API. 144 int size() const; 145 146 Result appendNewFrame(const rust_png::Reader& reader, const SkEncodedInfo& info); 147 void markFrameAsFullyReceived(size_t index); 148 bool getFrameInfo(int index, FrameInfo* info) const; 149 150 private: 151 class PngFrame; 152 153 const SkFrame* onGetFrame(int unverifiedIndex) const override; 154 Result setFrameInfoFromCurrentFctlChunk(const rust_png::Reader& reader, 155 PngFrame* out_frame); 156 157 std::vector<PngFrame> fFrames; 158 }; 159 FrameHolder fFrameHolder; 160 161 // Whether there may still be additional `fcTL` chunks to discover and parse. 162 // 163 // `true` if the stream hasn't been fully received (i.e. only 164 // `kIncompleteInput` errors so far, no hard errors) and `fFrameHolder` 165 // doesn't yet contain frame info for all `num_frames` declared in an `acTL` 166 // chunk. 167 bool fCanParseAdditionalFrameInfos = true; 168 }; 169 170 #endif // SkPngRustCodec_DEFINED 171