• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     IsAnimated onIsAnimated() override;
114     const SkFrameHolder* getFrameHolder() const override;
115     std::unique_ptr<SkStream> getEncodedData() const override;
116 
117     // SkPngCodecBase overrides:
118     std::optional<SkSpan<const PaletteColorEntry>> onTryGetPlteChunk() override;
119     std::optional<SkSpan<const uint8_t>> onTryGetTrnsChunk() override;
120 
121     rust::Box<rust_png::Reader> fReader;
122 
123     // `-1` means that `IDAT` is not part of animation and wasn't skipped yet.
124     int fFrameAtCurrentStreamPosition = -1;
125     bool fStreamIsPositionedAtStartOfFrameData = false;
126     const std::unique_ptr<SkStream> fPrivStream;
127     // TODO(https://crbug.com/371060427): Once fast seeking is available, we can
128     // remove the field that tracks the stream length.
129     std::optional<size_t> fStreamLengthDuringLastCallToParseAdditionalFrameInfos;
130 
131     std::optional<DecodingState> fIncrementalDecodingState;
132 
133     class FrameHolder final : public SkFrameHolder {
134     public:
135         FrameHolder(int width, int height);
136         ~FrameHolder() override;
137 
138         FrameHolder(const FrameHolder&) = delete;
139         FrameHolder(FrameHolder&&) = delete;
140         FrameHolder& operator=(const FrameHolder&) = delete;
141         FrameHolder& operator=(FrameHolder&&) = delete;
142 
143         // Returning an `int` (rather than `size_t`) for easier interop with
144         // other parts of the SkCodec API.
145         int size() const;
146 
147         Result appendNewFrame(const rust_png::Reader& reader, const SkEncodedInfo& info);
148         void markFrameAsFullyReceived(size_t index);
149         bool getFrameInfo(int index, FrameInfo* info) const;
150 
151     private:
152         class PngFrame;
153 
154         const SkFrame* onGetFrame(int unverifiedIndex) const override;
155         Result setFrameInfoFromCurrentFctlChunk(const rust_png::Reader& reader,
156                                                 PngFrame* out_frame);
157 
158         std::vector<PngFrame> fFrames;
159     };
160     FrameHolder fFrameHolder;
161 
162     // Whether there may still be additional `fcTL` chunks to discover and parse.
163     //
164     // `true` if the stream hasn't been fully received (i.e. only
165     // `kIncompleteInput` errors so far, no hard errors) and `fFrameHolder`
166     // doesn't yet contain frame info for all `num_frames` declared in an `acTL`
167     // chunk.
168     bool fCanParseAdditionalFrameInfos = true;
169 };
170 
171 #endif  // SkPngRustCodec_DEFINED
172