Add skia tools util to set deserial procs for SKP decoding.

This specifically sets default values for decoding images and typefaces.
We had many of our tools missing one or both of these procs which was
causing us not to render the full SKPs. This makes sure all our tools
are deserializing skps correctly and has one shared place in code to
set these default values.

Change-Id: I242b9efead3e9fba349664f3c78c0df9f877ba44
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1141777
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
Auto-Submit: Greg Daniel <egdaniel@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index aaadf42..64a80bd 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -2494,6 +2494,8 @@
       "tools/AutoreleasePool.h",
       "tools/DecodeUtils.cpp",
       "tools/DecodeUtils.h",
+      "tools/DeserialProcsUtils.cpp",
+      "tools/DeserialProcsUtils.h",
       "tools/EncodeUtils.cpp",
       "tools/EncodeUtils.h",
       "tools/GpuToolUtils.h",
diff --git a/bench/BUILD.bazel b/bench/BUILD.bazel
index 37366ed8..b9e70a6 100644
--- a/bench/BUILD.bazel
+++ b/bench/BUILD.bazel
@@ -37,6 +37,7 @@
     "//src/base",
     "//src/core:core_priv",
     "//tools:autorelease_pool",
+    "//tools:deserial_procs_utils",
     "//tools:registry",
     "//tools:resources",
     "//tools:stats",
diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp
index d2cbc08..d8b3063 100644
--- a/bench/nanobench.cpp
+++ b/bench/nanobench.cpp
@@ -46,6 +46,7 @@
 #include "src/utils/SkShaderUtils.h"
 #include "tools/AutoreleasePool.h"
 #include "tools/CrashHandler.h"
+#include "tools/DeserialProcsUtils.h"
 #include "tools/MSKPPlayer.h"
 #include "tools/ProcStats.h"
 #include "tools/Stats.h"
@@ -860,21 +861,7 @@
             SkDebugf("Could not read %s.\n", path);
             return nullptr;
         }
-        SkDeserialProcs procs;
-        procs.fImageDataProc =
-                [](sk_sp<SkData> data, std::optional<SkAlphaType>, void* ctx) -> sk_sp<SkImage> {
-#if defined(SK_CODEC_DECODES_PNG_WITH_RUST)
-            std::unique_ptr<SkStream> stream = SkMemoryStream::Make(data);
-            auto codec = SkPngRustDecoder::Decode(std::move(stream), nullptr, nullptr);
-#else
-            auto codec = SkPngDecoder::Decode(data, nullptr, nullptr);
-#endif
-            if (!codec) {
-                SkDebugf("Invalid png data detected\n");
-                return nullptr;
-            }
-            return std::get<0>(codec->getImage());
-        };
+        SkDeserialProcs procs = ToolUtils::get_default_skp_deserial_procs();
         return SkPicture::MakeFromStream(stream.get(), &procs);
     }
 
diff --git a/dm/BUILD.bazel b/dm/BUILD.bazel
index ce9ac3c..f6760a5 100644
--- a/dm/BUILD.bazel
+++ b/dm/BUILD.bazel
@@ -38,6 +38,7 @@
         "//src/utils:multi_picture_document",
         "//tools:autorelease_pool",
         "//tools:codec_utils",
+        "//tools:deserial_procs_utils",
         "//tools:gpu_tool_utils",
         "//tools:hash_and_encode",
         "//tools:runtime_blend_utils",
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 965bcf9..9ac7c79 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -57,6 +57,7 @@
 #include "src/utils/SkJSONWriter.h"
 #include "src/utils/SkMultiPictureDocumentPriv.h"
 #include "src/utils/SkOSPath.h"
+#include "tools/DeserialProcsUtils.h"
 #include "tools/EncodeUtils.h"
 #include "tools/GpuToolUtils.h"
 #include "tools/Resources.h"
@@ -1176,32 +1177,27 @@
 #endif
     };
 
-    SkDeserialProcs procs;
-    procs.fImageProc = [](const void* data, size_t size, void* ctx) -> sk_sp<SkImage> {
-        sk_sp<SkData> tmpData = SkData::MakeWithoutCopy(data, size);
-        sk_sp<SkImage> image = SkImages::DeferredFromEncodedData(std::move(tmpData));
-        image = image->makeRasterImage(nullptr); // force decoding
+    SkDeserialProcs procs = ToolUtils::get_default_skp_deserial_procs();
+
+    // We override the default fImageDataProc set above
+    procs.fImageDataProc =
+        [](sk_sp<SkData> data, std::optional<SkAlphaType> at, void* ctx) -> sk_sp<SkImage> {
+            sk_sp<SkImage> image = SkImages::DeferredFromEncodedData(std::move(data));
+            image = image->makeRasterImage(nullptr); // force decoding
 
 #if defined(SK_GANESH)
-        if (image) {
-            DeserializationContext* context = reinterpret_cast<DeserializationContext*>(ctx);
-            if (context->fDirectContext) {
-                return SkImages::TextureFromImage(context->fDirectContext, image);
+            if (image) {
+                DeserializationContext* context = reinterpret_cast<DeserializationContext*>(ctx);
+                if (context->fDirectContext) {
+                    return SkImages::TextureFromImage(context->fDirectContext, image);
+                }
             }
-        }
 #endif
-        return image;
-    };
+            return image;
+        };
+
     procs.fImageCtx = &ctx;
 
-    // SKPs may have typefaces encoded in them (e.g. with FreeType). We can try falling back
-    // to the Test FontMgr (possibly a native one) if we have do not have FreeType built-in.
-    procs.fTypefaceProc = [](const void* data, size_t size, void*) -> sk_sp<SkTypeface> {
-        SkStream** stream = reinterpret_cast<SkStream**>(const_cast<void*>(data));
-        return SkTypeface::MakeDeserialize(*stream, ToolUtils::TestFontMgr());
-    };
-
-
     std::unique_ptr<SkStream> stream = SkStream::MakeFromFile(fPath.c_str());
     if (!stream) {
         return Result::Fatal("Couldn't read %s.", fPath.c_str());
diff --git a/tools/BUILD.bazel b/tools/BUILD.bazel
index bd13b5f..6defd5c 100644
--- a/tools/BUILD.bazel
+++ b/tools/BUILD.bazel
@@ -201,6 +201,28 @@
 )
 
 skia_cc_library(
+    name = "deserial_procs_utils",
+    testonly = True,
+    srcs = [
+        "DeserialProcsUtils.cpp",
+    ],
+    hdrs = [
+        "DeserialProcsUtils.h",
+    ],
+    visibility = [
+        "//bench:__subpackages__",
+        "//dm:__pkg__",
+        "//gm:__subpackages__",
+        "//tools/viewer:__pkg__",
+    ],
+    deps = [
+        "//:core",
+        "//:png_decode_libpng",
+        "//tools/fonts:font_tool_utils",
+    ],
+)
+
+skia_cc_library(
     name = "resources",
     testonly = True,
     srcs = [
diff --git a/tools/DeserialProcsUtils.cpp b/tools/DeserialProcsUtils.cpp
new file mode 100644
index 0000000..843638b
--- /dev/null
+++ b/tools/DeserialProcsUtils.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "tools/DeserialProcsUtils.h"
+
+#include "include/codec/SkCodec.h"
+#include "include/core/SkFontMgr.h"
+#include "include/core/SkImage.h"
+#include "include/core/SkStream.h"
+#include "tools/fonts/FontToolUtils.h"
+
+#if defined(SK_CODEC_DECODES_PNG_WITH_RUST)
+#include "include/codec/SkPngRustDecoder.h"
+#else
+#include "include/codec/SkPngDecoder.h"
+#endif
+
+namespace ToolUtils {
+
+SkDeserialProcs get_default_skp_deserial_procs() {
+    SkDeserialProcs procs;
+    procs.fImageDataProc =
+            [](sk_sp<SkData> data, std::optional<SkAlphaType> at, void*) -> sk_sp<SkImage> {
+#if defined(SK_CODEC_DECODES_PNG_WITH_RUST)
+        std::unique_ptr<SkStream> stream = SkMemoryStream::Make(data);
+        auto codec = SkPngRustDecoder::Decode(std::move(stream), nullptr, nullptr);
+#else
+        auto codec = SkPngDecoder::Decode(data, nullptr, nullptr);
+#endif
+        if (!codec) {
+            SkDebugf("Invalid png data detected\n");
+            return nullptr;
+        }
+        if (auto lazyImage = SkCodecs::DeferredImage(std::move(codec), at)) {
+            return lazyImage->makeRasterImage(/*GrDirectContext=*/nullptr);
+        }
+        return nullptr;
+    };
+
+    // SKPs may have typefaces encoded in them (e.g. with FreeType). We can try falling back
+    // to the Test FontMgr (possibly a native one) if we have do not have FreeType built-in.
+    procs.fTypefaceProc = [](const void* data, size_t size, void*) -> sk_sp<SkTypeface> {
+        SkStream** stream = reinterpret_cast<SkStream**>(const_cast<void*>(data));
+        return SkTypeface::MakeDeserialize(*stream, ToolUtils::TestFontMgr());
+    };
+    return procs;
+}
+
+}  // namespace ToolUtils
+
diff --git a/tools/DeserialProcsUtils.h b/tools/DeserialProcsUtils.h
new file mode 100644
index 0000000..6f49c2d
--- /dev/null
+++ b/tools/DeserialProcsUtils.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef DeserialProcsUtils_DEFINED
+#define DeserialProcsUtils_DEFINED
+
+#include "include/core/SkSerialProcs.h"
+
+namespace ToolUtils {
+
+// Returns the default SkDeserialProcs used by Skia's tools when Deserializing Skps. This adds
+// default values for the SkDeserialImageProc and SkDeserialTypefaceProc..
+SkDeserialProcs get_default_skp_deserial_procs();
+
+}  // namespace ToolUtils
+
+#endif  // ToolUtils_DEFINED
diff --git a/tools/skp_parser.cpp b/tools/skp_parser.cpp
index 84a9d21..e3fb057 100644
--- a/tools/skp_parser.cpp
+++ b/tools/skp_parser.cpp
@@ -6,9 +6,11 @@
  */
 
 #include "include/core/SkPicture.h"
+#include "include/core/SkSerialProcs.h"
 #include "include/core/SkStream.h"
 #include "include/utils/SkNullCanvas.h"
 #include "src/utils/SkJSONWriter.h"
+#include "tools/DeserialProcsUtils.h"
 #include "tools/UrlDataManager.h"
 #include "tools/debugger/DebugCanvas.h"
 
@@ -47,7 +49,8 @@
         SkDebugf("Bad file: '%s'\n", argv[1]);
         return 2;
     }
-    sk_sp<SkPicture> pic = SkPicture::MakeFromStream(&input);
+    SkDeserialProcs procs = ToolUtils::get_default_skp_deserial_procs();
+    sk_sp<SkPicture> pic = SkPicture::MakeFromStream(&input, &procs);
     if (!pic) {
         SkDebugf("Bad skp: '%s'\n", argv[1]);
         return 3;
diff --git a/tools/skpbench/skpbench.cpp b/tools/skpbench/skpbench.cpp
index f80dd0d..bdeff42 100644
--- a/tools/skpbench/skpbench.cpp
+++ b/tools/skpbench/skpbench.cpp
@@ -27,6 +27,7 @@
 #include "src/gpu/ganesh/SkGr.h"
 #include "src/gpu/ganesh/image/GrImageUtils.h"
 #include "src/utils/SkOSPath.h"
+#include "tools/DeserialProcsUtils.h"
 #include "tools/EncodeUtils.h"
 #include "tools/SkSharingProc.h"
 #include "tools/flags/CommandLineFlags.h"
@@ -556,7 +557,8 @@
             // populate skp with it's first frame, for width height determination.
             skp = mskp->frame(0);
         } else {
-            skp = SkPicture::MakeFromStream(srcstream.get());
+            SkDeserialProcs procs = ToolUtils::get_default_skp_deserial_procs();
+            skp = SkPicture::MakeFromStream(srcstream.get(), &procs);
         }
         if (!skp) {
             exitf(ExitErr::kData, "failed to parse file %s", srcfile.c_str());
diff --git a/tools/viewer/BUILD.bazel b/tools/viewer/BUILD.bazel
index aea91b4..39ee1f4 100644
--- a/tools/viewer/BUILD.bazel
+++ b/tools/viewer/BUILD.bazel
@@ -122,6 +122,7 @@
         "//modules/sksg",
         "//src/sksl/tracing:player",
         "//tools:codec_utils",
+        "//tools:deserial_procs_utils",
         "//tools:gpu_tool_utils",
         "//tools:mskp_player",
         "//tools:registry",
diff --git a/tools/viewer/SKPSlide.cpp b/tools/viewer/SKPSlide.cpp
index a03088d..8aee352 100644
--- a/tools/viewer/SKPSlide.cpp
+++ b/tools/viewer/SKPSlide.cpp
@@ -7,23 +7,15 @@
 
 #include "tools/viewer/SKPSlide.h"
 
-#include "include/codec/SkCodec.h"
 #include "include/core/SkCanvas.h"
-#include "include/core/SkImage.h"
 #include "include/core/SkPicture.h"
 #include "include/core/SkSerialProcs.h"
 #include "include/core/SkStream.h"
 #include "include/core/SkString.h"
 #include "include/private/base/SkDebug.h"
 #include "include/private/base/SkTo.h"
+#include "tools/DeserialProcsUtils.h"
 
-#if defined(SK_CODEC_DECODES_PNG_WITH_RUST)
-#include "include/codec/SkPngRustDecoder.h"
-#else
-#include "include/codec/SkPngDecoder.h"
-#endif
-
-#include <memory>
 #include <utility>
 
 SKPSlide::SKPSlide(const SkString& name, const SkString& path)
@@ -60,21 +52,8 @@
     }
     fStream->rewind();
 
-    SkDeserialProcs procs;
-    procs.fImageDataProc =
-            [](sk_sp<SkData> data, std::optional<SkAlphaType>, void*) -> sk_sp<SkImage> {
-#if defined(SK_CODEC_DECODES_PNG_WITH_RUST)
-        std::unique_ptr<SkStream> stream = SkMemoryStream::Make(data);
-        auto codec = SkPngRustDecoder::Decode(std::move(stream), nullptr, nullptr);
-#else
-        auto codec = SkPngDecoder::Decode(data, nullptr, nullptr);
-#endif
-        if (!codec) {
-            SkDebugf("Invalid png data detected\n");
-            return nullptr;
-        }
-        return std::get<0>(codec->getImage());
-    };
+    SkDeserialProcs procs = ToolUtils::get_default_skp_deserial_procs();
+
     fPic = SkPicture::MakeFromStream(fStream.get(), &procs);
     if (!fPic) {
         SkDebugf("Could not parse SkPicture from skp stream for slide %s.\n", fName.c_str());
diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp
index 06c421d7..d927c07 100644
--- a/tools/viewer/Viewer.cpp
+++ b/tools/viewer/Viewer.cpp
@@ -57,6 +57,7 @@
 #include "src/utils/SkShaderUtils.h"
 #include "tools/CodecUtils.h"
 #include "tools/DecodeUtils.h"
+#include "tools/DeserialProcsUtils.h"
 #include "tools/Resources.h"
 #include "tools/RuntimeBlendUtils.h"
 #include "tools/SkMetaData.h"
@@ -1879,25 +1880,6 @@
     return sProcs;
 }
 
-static SkDeserialProcs deserial_procs_using_png() {
-    SkDeserialProcs dProcs;
-    dProcs.fImageDataProc =
-            [](sk_sp<SkData> data, std::optional<SkAlphaType>, void*) -> sk_sp<SkImage> {
-#if defined(SK_CODEC_DECODES_PNG_WITH_RUST)
-        std::unique_ptr<SkStream> stream = SkMemoryStream::Make(data);
-        auto codec = SkPngRustDecoder::Decode(std::move(stream), nullptr, nullptr);
-#else
-        auto codec = SkPngDecoder::Decode(data, nullptr, nullptr);
-#endif
-        if (!codec) {
-            SkDebugf("Invalid png data detected\n");
-            return nullptr;
-        }
-        return std::get<0>(codec->getImage());
-    };
-    return dProcs;
-}
-
 void Viewer::drawSlide(SkSurface* surface) {
     if (fCurrentSlide < 0) {
         return;
@@ -2046,7 +2028,7 @@
         SkSerialProcs sProcs = serial_procs_using_png();
         auto data = picture->serialize(&sProcs);
         slideCanvas = recorderRestoreCanvas;
-        SkDeserialProcs dProcs = deserial_procs_using_png();
+        SkDeserialProcs dProcs = ToolUtils::get_default_skp_deserial_procs();
         slideCanvas->drawPicture(SkPicture::MakeFromData(data.get(), &dProcs));
     }