blob: 1fb00cc51802df59e09139548adad55d863478fe [file] [log] [blame]
/*
* 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 "experimental/rust_png/ffi/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 DecodingState {
// `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 `fDstRowSize`) 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 fDstRowSize = 0;
// Stashed `dstInfo.bytesPerPixel()`
size_t fBytesPerPixel = 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 row-by-row decoding which is used from `onGetPixels` and/or
// `onIncrementalDecode`.
Result incrementalDecode(DecodingState& decodingState, int* rowsDecoded);
// 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;
const SkFrameHolder* getFrameHolder() const override;
// SkPngCodecBase overrides:
std::optional<SkSpan<const PaletteColorEntry>> onTryGetPlteChunk() override;
std::optional<SkSpan<const uint8_t>> onTryGetTrnsChunk() override;
rust::Box<rust_png::Reader> fReader;
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;
size_t size() const;
void appendNewFrame(const rust_png::Reader& reader, const SkEncodedInfo& info);
private:
const SkFrame* onGetFrame(int i) const override;
void setLastFrameInfoFromCurrentFctlChunk(const rust_png::Reader& reader);
class PngFrame;
std::vector<PngFrame> fFrames;
};
FrameHolder fFrameHolder;
size_t fNumOfFullyReceivedFrames = 0;
};
#endif // SkPngRustCodec_DEFINED