• 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 
8 #include "experimental/rust_png/encoder/impl/SkPngRustEncoderImpl.h"
9 
10 #include <limits>
11 #include <memory>
12 #include <utility>
13 
14 #include "experimental/rust_png/encoder/SkPngRustEncoder.h"
15 #include "experimental/rust_png/ffi/FFI.rs.h"
16 #include "experimental/rust_png/ffi/UtilsForFFI.h"
17 #include "include/core/SkSpan.h"
18 #include "include/core/SkStream.h"
19 #include "include/private/SkEncodedInfo.h"
20 #include "include/private/base/SkAssert.h"
21 #include "src/base/SkSafeMath.h"
22 #include "src/encode/SkImageEncoderPriv.h"
23 #include "third_party/rust/cxx/v1/cxx.h"
24 
25 #ifdef __clang__
26 #pragma clang diagnostic error "-Wconversion"
27 #endif
28 
29 namespace {
30 
ToColorType(SkEncodedInfo::Color color)31 rust_png::ColorType ToColorType(SkEncodedInfo::Color color) {
32     switch (color) {
33         case SkEncodedInfo::kRGB_Color:
34             return rust_png::ColorType::Rgb;
35         case SkEncodedInfo::kRGBA_Color:
36             return rust_png::ColorType::Rgba;
37         case SkEncodedInfo::kGray_Color:
38             return rust_png::ColorType::Grayscale;
39         case SkEncodedInfo::kGrayAlpha_Color:
40             return rust_png::ColorType::GrayscaleAlpha;
41         default:
42             SkUNREACHABLE;
43     }
44 }
45 
ToCompression(SkPngRustEncoder::CompressionLevel level)46 rust_png::Compression ToCompression(SkPngRustEncoder::CompressionLevel level) {
47     switch (level) {
48         case SkPngRustEncoder::CompressionLevel::kLow:
49             return rust_png::Compression::Fast;
50         case SkPngRustEncoder::CompressionLevel::kMedium:
51             return rust_png::Compression::Default;
52         case SkPngRustEncoder::CompressionLevel::kHigh:
53             return rust_png::Compression::Best;
54     }
55     SkUNREACHABLE;
56 }
57 
getDataTableEntry(const SkDataTable & table,int index)58 rust::Slice<const uint8_t> getDataTableEntry(const SkDataTable& table, int index) {
59     SkASSERT((0 <= index) && (index < table.count()));
60 
61     size_t size = 0;
62     const uint8_t* entry = table.atT<uint8_t>(index, &size);
63     while (size > 0 && entry[size - 1] == 0) {
64         // Ignore trailing NUL characters - these are *not* part of Rust `&str`.
65         size--;
66     }
67 
68     return rust::Slice<const uint8_t>(entry, size);
69 }
70 
EncodeComments(rust_png::Writer & writer,const sk_sp<SkDataTable> & comments)71 rust_png::EncodingResult EncodeComments(rust_png::Writer& writer,
72                                         const sk_sp<SkDataTable>& comments) {
73     if (comments != nullptr) {
74         if (comments->count() % 2 != 0) {
75             return rust_png::EncodingResult::ParameterError;
76         }
77 
78         for (int i = 0; i < comments->count() / 2; ++i) {
79             rust::Slice<const uint8_t> keyword = getDataTableEntry(*comments, 2 * i);
80             rust::Slice<const uint8_t> text = getDataTableEntry(*comments, 2 * i + 1);
81             rust_png::EncodingResult result = writer.write_text_chunk(keyword, text);
82             if (result != rust_png::EncodingResult::Success) {
83                 return result;
84             }
85         }
86     }
87 
88     return rust_png::EncodingResult::Success;
89 }
90 
91 // This helper class adapts `SkWStream` to expose the API required by Rust FFI
92 // (i.e. the `WriteTrait` API).
93 class WriteTraitAdapterForSkWStream final : public rust_png::WriteTrait {
94 public:
95     // SAFETY: The caller needs to guarantee that `stream` will be alive for
96     // as long as `WriteTraitAdapterForSkWStream`.
WriteTraitAdapterForSkWStream(SkWStream * stream)97     explicit WriteTraitAdapterForSkWStream(SkWStream* stream) : fStream(stream) {
98         SkASSERT(fStream);
99     }
100 
101     ~WriteTraitAdapterForSkWStream() override = default;
102 
103     // Non-copyable and non-movable.
104     WriteTraitAdapterForSkWStream(const WriteTraitAdapterForSkWStream&) = delete;
105     WriteTraitAdapterForSkWStream& operator=(const WriteTraitAdapterForSkWStream&) = delete;
106     WriteTraitAdapterForSkWStream(WriteTraitAdapterForSkWStream&&) = delete;
107     WriteTraitAdapterForSkWStream& operator=(WriteTraitAdapterForSkWStream&&) = delete;
108 
109     // Implementation of the `std::io::Read::read` method.  See `RustTrait`'s
110     // doc comments and
111     // https://doc.rust-lang.org/nightly/std/io/trait.Read.html#tymethod.read
112     // for guidance on the desired implementation and behavior of this method.
write(rust::Slice<const uint8_t> buffer)113     bool write(rust::Slice<const uint8_t> buffer) override {
114         SkSpan<const uint8_t> span = ToSkSpan(buffer);
115         return fStream->write(span.data(), span.size());
116     }
117 
flush()118     void flush() override { fStream->flush(); }
119 
120 private:
121     SkWStream* fStream = nullptr;  // Non-owning pointer.
122 };
123 
124 }  // namespace
125 
126 // static
Make(SkWStream * dst,const SkPixmap & src,const SkPngRustEncoder::Options & options)127 std::unique_ptr<SkEncoder> SkPngRustEncoderImpl::Make(SkWStream* dst,
128                                                       const SkPixmap& src,
129                                                       const SkPngRustEncoder::Options& options) {
130     if (!SkPixmapIsValid(src)) {
131         return nullptr;
132     }
133 
134     std::optional<TargetInfo> maybeTargetInfo = SkPngEncoderBase::getTargetInfo(src.info());
135     if (!maybeTargetInfo.has_value()) {
136         return nullptr;
137     }
138     const SkEncodedInfo& dstInfo = maybeTargetInfo->fDstInfo;
139 
140     SkSafeMath safe;
141     uint32_t width = safe.castTo<uint32_t>(dstInfo.width());
142     uint32_t height = safe.castTo<uint32_t>(dstInfo.height());
143     if (!safe.ok()) {
144         return nullptr;
145     }
146 
147     auto writeTraitAdapter = std::make_unique<WriteTraitAdapterForSkWStream>(dst);
148     rust::Box<rust_png::ResultOfWriter> resultOfWriter =
149             rust_png::new_writer(std::move(writeTraitAdapter),
150                                  width,
151                                  height,
152                                  ToColorType(dstInfo.color()),
153                                  dstInfo.bitsPerComponent(),
154                                  ToCompression(options.fCompressionLevel));
155     if (resultOfWriter->err() != rust_png::EncodingResult::Success) {
156         return nullptr;
157     }
158     rust::Box<rust_png::Writer> writer = resultOfWriter->unwrap();
159 
160     if (EncodeComments(*writer, options.fComments) != rust_png::EncodingResult::Success) {
161         return nullptr;
162     }
163 
164     rust::Box<rust_png::ResultOfStreamWriter> resultOfStreamWriter =
165             rust_png::convert_writer_into_stream_writer(std::move(writer));
166     if (resultOfStreamWriter->err() != rust_png::EncodingResult::Success) {
167         return nullptr;
168     }
169     rust::Box<rust_png::StreamWriter> stream_writer = resultOfStreamWriter->unwrap();
170 
171     return std::make_unique<SkPngRustEncoderImpl>(
172             std::move(*maybeTargetInfo), src, std::move(stream_writer));
173 }
174 
SkPngRustEncoderImpl(TargetInfo targetInfo,const SkPixmap & src,rust::Box<rust_png::StreamWriter> stream_writer)175 SkPngRustEncoderImpl::SkPngRustEncoderImpl(TargetInfo targetInfo,
176                                            const SkPixmap& src,
177                                            rust::Box<rust_png::StreamWriter> stream_writer)
178         : SkPngEncoderBase(std::move(targetInfo), src), fStreamWriter(std::move(stream_writer)) {}
179 
180 SkPngRustEncoderImpl::~SkPngRustEncoderImpl() = default;
181 
onEncodeRow(SkSpan<const uint8_t> row)182 bool SkPngRustEncoderImpl::onEncodeRow(SkSpan<const uint8_t> row) {
183     return fStreamWriter->write(rust::Slice<const uint8_t>(row)) ==
184            rust_png::EncodingResult::Success;
185 }
186 
onFinishEncoding()187 bool SkPngRustEncoderImpl::onFinishEncoding() {
188     return rust_png::finish_encoding(std::move(fStreamWriter)) == rust_png::EncodingResult::Success;
189 }
190