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
8 #include "experimental/rust_png/encoder/impl/SkPngRustEncoderImpl.h"
9
10 #include <memory>
11 #include <utility>
12
13 #include "experimental/rust_png/ffi/FFI.rs.h"
14 #include "experimental/rust_png/ffi/UtilsForFFI.h"
15 #include "include/core/SkSpan.h"
16 #include "include/core/SkStream.h"
17 #include "include/private/SkEncodedInfo.h"
18 #include "include/private/base/SkAssert.h"
19 #include "src/base/SkSafeMath.h"
20 #include "src/encode/SkImageEncoderPriv.h"
21 #include "third_party/rust/cxx/v1/cxx.h"
22
23 #ifdef __clang__
24 #pragma clang diagnostic error "-Wconversion"
25 #endif
26
27 namespace {
28
ToColorType(SkEncodedInfo::Color color)29 rust_png::ColorType ToColorType(SkEncodedInfo::Color color) {
30 switch (color) {
31 case SkEncodedInfo::kRGB_Color:
32 return rust_png::ColorType::Rgb;
33 case SkEncodedInfo::kRGBA_Color:
34 return rust_png::ColorType::Rgba;
35 case SkEncodedInfo::kGray_Color:
36 return rust_png::ColorType::Grayscale;
37 case SkEncodedInfo::kGrayAlpha_Color:
38 return rust_png::ColorType::GrayscaleAlpha;
39 default:
40 SkUNREACHABLE;
41 }
42 }
43
44 // This helper class adapts `SkWStream` to expose the API required by Rust FFI
45 // (i.e. the `WriteTrait` API).
46 class WriteTraitAdapterForSkWStream final : public rust_png::WriteTrait {
47 public:
48 // SAFETY: The caller needs to guarantee that `stream` will be alive for
49 // as long as `WriteTraitAdapterForSkWStream`.
WriteTraitAdapterForSkWStream(SkWStream * stream)50 explicit WriteTraitAdapterForSkWStream(SkWStream* stream) : fStream(stream) {
51 SkASSERT(fStream);
52 }
53
54 ~WriteTraitAdapterForSkWStream() override = default;
55
56 // Non-copyable and non-movable.
57 WriteTraitAdapterForSkWStream(const WriteTraitAdapterForSkWStream&) = delete;
58 WriteTraitAdapterForSkWStream& operator=(const WriteTraitAdapterForSkWStream&) = delete;
59 WriteTraitAdapterForSkWStream(WriteTraitAdapterForSkWStream&&) = delete;
60 WriteTraitAdapterForSkWStream& operator=(WriteTraitAdapterForSkWStream&&) = delete;
61
62 // Implementation of the `std::io::Read::read` method. See `RustTrait`'s
63 // doc comments and
64 // https://doc.rust-lang.org/nightly/std/io/trait.Read.html#tymethod.read
65 // for guidance on the desired implementation and behavior of this method.
write(rust::Slice<const uint8_t> buffer)66 bool write(rust::Slice<const uint8_t> buffer) override {
67 SkSpan<const uint8_t> span = ToSkSpan(buffer);
68 return fStream->write(span.data(), span.size());
69 }
70
flush()71 void flush() override { fStream->flush(); }
72
73 private:
74 SkWStream* fStream = nullptr; // Non-owning pointer.
75 };
76
77 } // namespace
78
79 // static
Make(SkWStream * dst,const SkPixmap & src)80 std::unique_ptr<SkEncoder> SkPngRustEncoderImpl::Make(SkWStream* dst, const SkPixmap& src) {
81 if (!SkPixmapIsValid(src)) {
82 return nullptr;
83 }
84
85 std::optional<TargetInfo> maybeTargetInfo = SkPngEncoderBase::getTargetInfo(src.info());
86 if (!maybeTargetInfo.has_value()) {
87 return nullptr;
88 }
89 const SkEncodedInfo& dstInfo = maybeTargetInfo->fDstInfo;
90
91 SkSafeMath safe;
92 uint32_t width = safe.castTo<uint32_t>(dstInfo.width());
93 uint32_t height = safe.castTo<uint32_t>(dstInfo.height());
94 if (!safe.ok()) {
95 return nullptr;
96 }
97
98 auto writeTraitAdapter = std::make_unique<WriteTraitAdapterForSkWStream>(dst);
99 rust::Box<rust_png::ResultOfStreamWriter> resultOfStreamWriter =
100 rust_png::new_stream_writer(std::move(writeTraitAdapter),
101 width,
102 height,
103 ToColorType(dstInfo.color()),
104 dstInfo.bitsPerComponent());
105 if (resultOfStreamWriter->err() != rust_png::EncodingResult::Success) {
106 return nullptr;
107 }
108 rust::Box<rust_png::StreamWriter> stream_writer = resultOfStreamWriter->unwrap();
109
110 return std::make_unique<SkPngRustEncoderImpl>(
111 std::move(*maybeTargetInfo), src, std::move(stream_writer));
112 }
113
SkPngRustEncoderImpl(TargetInfo targetInfo,const SkPixmap & src,rust::Box<rust_png::StreamWriter> stream_writer)114 SkPngRustEncoderImpl::SkPngRustEncoderImpl(TargetInfo targetInfo,
115 const SkPixmap& src,
116 rust::Box<rust_png::StreamWriter> stream_writer)
117 : SkPngEncoderBase(std::move(targetInfo), src), fStreamWriter(std::move(stream_writer)) {}
118
119 SkPngRustEncoderImpl::~SkPngRustEncoderImpl() = default;
120
onEncodeRow(SkSpan<const uint8_t> row)121 bool SkPngRustEncoderImpl::onEncodeRow(SkSpan<const uint8_t> row) {
122 return fStreamWriter->write(rust::Slice<const uint8_t>(row)) ==
123 rust_png::EncodingResult::Success;
124 }
125
onFinishEncoding()126 bool SkPngRustEncoderImpl::onFinishEncoding() {
127 return rust_png::finish_encoding(std::move(fStreamWriter)) == rust_png::EncodingResult::Success;
128 }
129