xref: /aosp_15_r20/external/skia/experimental/rust_png/decoder/impl/SkPngRustCodec.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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