[rust jpeg] Final raw output and IDCT parity
Treat immutable JPEG data as final input for raw YUV decoding. Preserve
best-effort progressive output at final EOF without changing resumable
stream behavior, and cover missing-EOI decoding and rewind.
Pin zune-jpeg to the revision from PR494 containing final-input,
accurate-IDCT, and progressive block-smoothing fixes. Remove the
temporary local crate patch and its Bazel/GN patch wiring.
https://github.com/etemesi254/zune-image/pull/494
Preserve incomplete-input status when progressive color components have
not received DC data, without publishing incomplete color output.
Preserve existing best-effort packed JPEG decoding.
Target GPU half-scan, missing-EOI, non-interleaved DC,
grayscale-profile, and YUV color-profile failures with Rust JPEG
enabled.
Chromium CL porting zune-jpeg fixes as patch which requires to be landed
before this CL:
https://chromium-review.googlesource.com/c/chromium/src/+/8617799
NOTE FOR REVIEWERS - original patch and result patch are not identical.
PLEASE REVIEW CAREFULLY.
Diffs between the patches:
fn test_raw_input_finality_is_preserved_on_decode_reset() {
> + for raw_input_complete in [false, true] {
> + let mut reader = new_reader(cxx::UniquePtr::null(), 0, raw_input_complete);
> + assert_eq!(reader.raw_input_complete(), raw_input_complete);
> + reader.reset_decode_state();
> + assert_eq!(reader.raw_input_complete(), raw_input_complete);
> + }
> + }
> +
> + #[test]
> --- tests/SkJpegRustCodecTest.cpp
> +++ tests/SkJpegRustCodecTest.cpp
> - streamLength);
> + streamLength,
> + /*raw_input_complete=*/false);
> +DEF_TEST(RustJpegCodec_FinalRawInputWithoutEOI, r) {
> + sk_sp<SkData> data = GetResourceAsData("images/four-colors-limited-range-420-8bpc.jpg");
> + REPORTER_ASSERT(r, data && data->size() > 2);
> + if (!data || data->size() <= 2) {
> + return;
> + }
> + const auto* bytes = static_cast<const uint8_t*>(data->data());
> + REPORTER_ASSERT(r, bytes[data->size() - 2] == 0xFF &&
> + bytes[data->size() - 1] == 0xD9);
> + sk_sp<SkData> truncated = SkData::MakeSubset(data.get(), 0, data->size() - 2);
> + auto codec = SkJpegRustDecoder::Decode(truncated, nullptr);
> + auto reference = SkJpegRustDecoder::Decode(data, nullptr);
> + // A known stream length is not an explicit final-input signal.
> + auto streamCodec = SkJpegRustDecoder::Decode(SkMemoryStream::Make(truncated), nullptr);
> + REPORTER_ASSERT(r, codec && reference && streamCodec);
> + if (!codec || !reference || !streamCodec) {
> + return;
> + }
> + SkYUVAPixmapInfo info;
> + REPORTER_ASSERT(
> + r, codec->queryYUVAInfo(SkYUVAPixmapInfo::SupportedDataTypes::All(), &info));
> + SkYUVAPixmaps actual = SkYUVAPixmaps::Allocate(info);
> + SkYUVAPixmaps expected = SkYUVAPixmaps::Allocate(info);
> + SkYUVAPixmaps suspended = SkYUVAPixmaps::Allocate(info);
> + REPORTER_ASSERT(r, actual.isValid() && expected.isValid() && suspended.isValid());
> + if (!actual.isValid() || !expected.isValid() || !suspended.isValid()) {
> + return;
> + }
> + REPORTER_ASSERT_SUCCESSFUL_CODEC_RESULT(r, reference->getYUVAPlanes(expected));
> + for (int attempt = 0; attempt < 2; ++attempt) {
> + REPORTER_ASSERT(r, streamCodec->getYUVAPlanes(suspended) == SkCodec::kIncompleteInput);
> + REPORTER_ASSERT_SUCCESSFUL_CODEC_RESULT(r, codec->getYUVAPlanes(actual));
> + for (int plane = 0; plane < actual.numPlanes(); ++plane) {
> + for (int row = 0; row < actual.plane(plane).height(); ++row) {
> + REPORTER_ASSERT(
> + r, !memcmp(actual.plane(plane).addr(0, row),
> + expected.plane(plane).addr(0, row),
> + static_cast<size_t>(actual.plane(plane).width())));
> + }
> + }
> + }
> +}
> +
> +DEF_TEST(RustJpegCodec_FinalRawNeedsAllComponents, r) {
> + // A three-component progressive JPEG containing only a Y DC scan.
> + std::vector<uint8_t> encoded = {0xFF, 0xD8, 0xFF, 0xDB, 0, 67, 0};
> + encoded.insert(encoded.end(), 64, 1);
> + encoded.insert(encoded.end(), {
> + 0xFF, 0xC2, 0, 17, 8, 0, 8, 0, 8, 3,
> + 1, 0x11, 0, 2, 0x11, 0, 3, 0x11, 0,
> + 0xFF, 0xC4, 0, 20, 0,
> + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> + 0xFF, 0xDA, 0, 8, 1, 1, 0, 0, 0, 0, 0x7F,
> + });
> + auto codec = SkJpegRustDecoder::Decode(
> + SkData::MakeWithCopy(encoded.data(), encoded.size()), nullptr);
> + REPORTER_ASSERT(r, codec);
> + if (!codec) {
> + return;
> + }
> + SkYUVAPixmapInfo info;
> + REPORTER_ASSERT(
> + r, codec->queryYUVAInfo(SkYUVAPixmapInfo::SupportedDataTypes::All(), &info));
> + REPORTER_ASSERT(r, info.isValid());
> + if (!info.isValid()) {
> + return;
> + }
> + std::array<std::vector<uint8_t>, SkYUVAPixmaps::kMaxPlanes> storage;
> + std::array<SkPixmap, SkYUVAPixmaps::kMaxPlanes> pixmaps;
> + for (int plane = 0; plane < info.numPlanes(); ++plane) {
> + const size_t rowBytes = info.rowBytes(plane);
> + storage[plane].assign(rowBytes * info.planeInfo(plane).height(), 0xCD);
> + pixmaps[plane] =
> + SkPixmap(info.planeInfo(plane), storage[plane].data(), rowBytes);
> + }
> + SkYUVAPixmaps planes = SkYUVAPixmaps::FromExternalPixmaps(info.yuvaInfo(), pixmaps.data());
> + REPORTER_ASSERT(r, codec->getYUVAPlanes(planes) == SkCodec::kIncompleteInput);
> + for (const auto& plane : storage) {
> + REPORTER_ASSERT(r, std::all_of(plane.begin(), plane.end(), [](uint8_t value) {
> + return value == 0xCD;
> + }));
> + }
> +}
> +
Original patch:
diff --git a/MODULE.bazel b/MODULE.bazel
index 2dea1ec17e2ae4637aa88128e0545928bc917e9d..949b7f7ed833dd51dc26681fb5d741366382990b 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -168,7 +168,7 @@ crate.spec(
# and is not shipping in Chromium or other Skia clients.
git = "https://github.com/etemesi254/zune-image.git",
package = "zune-jpeg",
- rev = "a1010cf1faa91980f011c32585b4f582caf10745",
+ rev = "7d3e74c34b2252b6a4477399c7c52ccc9cf0fcab",
)
crate.spec(
features = ["std"],
diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock
index 0e3e32d45992972af15f075580841c86c8b8f066..90eb30a53e8282e3dd825e5c26d2266ede177e44 100644
--- a/MODULE.bazel.lock
+++ b/MODULE.bazel.lock
@@ -4733,7 +4733,7 @@
"@@rules_rust+//crate_universe:extensions.bzl%crate": {
"general": {
"bzlTransitiveDigest": "SEbwyK0Bpc6UhNRduMkh2PMWmn8uyfpuHzW108IxnWU=",
- "usagesDigest": "98RPnHOLfzauNznKr0YpAvMMRTSEjC+UJm4SprUFD2A=",
+ "usagesDigest": "0RXMAi1/tILyCX0fcy7tuQ5iSOFw66zCZNQtx2ek
[[[Original patch trimmed due to size. Decoded string size: 266525. Decoded string SHA1: 0d8af89253f1d9656139637c6fbc951977303607.]]]
Result patch:
diff --git a/MODULE.bazel b/MODULE.bazel
index 2dea1ec..949b7f7 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -168,7 +168,7 @@
# and is not shipping in Chromium or other Skia clients.
git = "https://github.com/etemesi254/zune-image.git",
package = "zune-jpeg",
- rev = "a1010cf1faa91980f011c32585b4f582caf10745",
+ rev = "7d3e74c34b2252b6a4477399c7c52ccc9cf0fcab",
)
crate.spec(
features = ["std"],
diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock
index 0e3e32d..90eb30a 100644
--- a/MODULE.bazel.lock
+++ b/MODULE.bazel.lock
@@ -4733,7 +4733,7 @@
"@@rules_rust+//crate_universe:extensions.bzl%crate": {
"general": {
"bzlTransitiveDigest": "SEbwyK0Bpc6UhNRduMkh2PMWmn8uyfpuHzW108IxnWU=",
- "usagesDigest": "98RPnHOLfzauNznKr0YpAvMMRTSEjC+UJm4SprUFD2A=",
+ "usagesDigest": "0RXMAi1/tILyCX0fcy7tuQ5iSOFw66zCZNQtx2ekATY=",
"recordedFileInputs": {
"@@//bazel/external/cxx/BUILD.bazel.skia": "39b51a9bd87483932d67ad5e0707ec339de993688df60ec69
[[[Result patch trimmed due to size. Decoded string size: 265416. Decoded string SHA1: 27918433f29fb9d62e46ea24178c8b4139aded72.]]]
Bug: 493315750
Change-Id: I7131c5762c98c6aa5269042507ede514aab546e6
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1400056
Commit-Queue: Sergio Gonzalez Martin <sergiog@microsoft.com>
Reviewed-by: Florin Malita <fmalita@google.com>
Reviewed-by: Ćukasz Anforowicz <lukasza@google.com>
8 files changed