Lift tess decoders into a static lib

Add a packages/runtime/decoders project that can be reused by more projects than just the tess renderer.

Diffs=
b69ae1312 Lift tess decoders into a static lib (#5709)

Co-authored-by: Chris Dalton <99840794+csmartdalton@users.noreply.github.com>
diff --git a/.rive_head b/.rive_head
index db6aa2b..89affb8 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-baefd620c53ec3e11584bf95c10efe1377348446
+b69ae131259dd46e67c0be905b86b89d4f2ad20e
diff --git a/decoders/build/premake5.lua b/decoders/build/premake5.lua
new file mode 100644
index 0000000..b9ec487
--- /dev/null
+++ b/decoders/build/premake5.lua
@@ -0,0 +1,41 @@
+workspace 'rive'
+configurations {"debug", "release"}
+
+require 'setup_compiler'
+
+rive = path.getabsolute("../../")
+
+dofile(rive .. '/dependencies/premake5_libpng.lua')
+
+project 'rive_decoders'
+    dependson 'libpng'
+    kind 'StaticLib'
+    language "C++"
+    cppdialect "C++17"
+    exceptionhandling "Off"
+    rtti "Off"
+    targetdir "%{cfg.buildcfg}"
+    objdir "obj/%{cfg.buildcfg}"
+    flags { "FatalWarnings" }
+
+    includedirs {
+        '../include',
+        libpng,
+    }
+
+    files {
+        '../src/**.cpp'
+    }
+
+    filter "configurations:debug"
+    do
+        defines {"DEBUG"}
+        symbols "On"
+    end
+
+    filter "configurations:release"
+    do
+        defines {"RELEASE"}
+        defines {"NDEBUG"}
+        optimize "On"
+    end
diff --git a/viewer/include/viewer/tess/bitmap_decoder.hpp b/decoders/include/rive/decoders/bitmap_decoder.hpp
similarity index 88%
rename from viewer/include/viewer/tess/bitmap_decoder.hpp
rename to decoders/include/rive/decoders/bitmap_decoder.hpp
index 0213a71..94a69f5 100644
--- a/viewer/include/viewer/tess/bitmap_decoder.hpp
+++ b/decoders/include/rive/decoders/bitmap_decoder.hpp
@@ -1,8 +1,11 @@
+/*
+ * Copyright 2023 Rive
+ */
+
 #ifndef _RIVE_BITMAP_DECODER_HPP_
 #define _RIVE_BITMAP_DECODER_HPP_
 
-#include "rive/rive_types.hpp"
-#include "rive/span.hpp"
+#include <memory>
 
 /// Bitmap will always take ownership of the bytes it is constructed with.
 class Bitmap
@@ -37,10 +40,10 @@
     size_t byteSize(PixelFormat format) const;
     size_t bytesPerPixel(PixelFormat format) const;
 
-    static std::unique_ptr<Bitmap> decode(rive::Span<const uint8_t> bytes);
+    static std::unique_ptr<Bitmap> decode(const uint8_t bytes[], size_t byteCount);
 
     // Change the pixel format (note this will resize bytes).
     void pixelFormat(PixelFormat format);
 };
 
-#endif
\ No newline at end of file
+#endif
diff --git a/viewer/src/tess/bitmap_decoder.cpp b/decoders/src/bitmap_decoder.cpp
similarity index 78%
rename from viewer/src/tess/bitmap_decoder.cpp
rename to decoders/src/bitmap_decoder.cpp
index b438740..48ef58d 100644
--- a/viewer/src/tess/bitmap_decoder.cpp
+++ b/decoders/src/bitmap_decoder.cpp
@@ -1,5 +1,8 @@
-#ifdef RIVE_RENDERER_TESS
-#include "viewer/tess/bitmap_decoder.hpp"
+/*
+ * Copyright 2023 Rive
+ */
+
+#include "rive/decoders/bitmap_decoder.hpp"
 #include <vector>
 
 Bitmap::Bitmap(uint32_t width,
@@ -33,11 +36,11 @@
 
 size_t Bitmap::byteSize() const { return byteSize(m_PixelFormat); }
 
-std::unique_ptr<Bitmap> DecodePng(rive::Span<const uint8_t> bytes);
-std::unique_ptr<Bitmap> DecodeJpeg(rive::Span<const uint8_t> bytes) { return nullptr; }
-std::unique_ptr<Bitmap> DecodeWebP(rive::Span<const uint8_t> bytes) { return nullptr; }
+std::unique_ptr<Bitmap> DecodePng(const uint8_t bytes[], size_t byteCount);
+std::unique_ptr<Bitmap> DecodeJpeg(const uint8_t bytes[], size_t byteCount) { return nullptr; }
+std::unique_ptr<Bitmap> DecodeWebP(const uint8_t bytes[], size_t byteCount) { return nullptr; }
 
-using BitmapDecoder = std::unique_ptr<Bitmap> (*)(rive::Span<const uint8_t> bytes);
+using BitmapDecoder = std::unique_ptr<Bitmap> (*)(const uint8_t bytes[], size_t byteCount);
 struct ImageFormat
 {
     const char* name;
@@ -45,7 +48,7 @@
     BitmapDecoder decodeImage;
 };
 
-std::unique_ptr<Bitmap> Bitmap::decode(rive::Span<const uint8_t> bytes)
+std::unique_ptr<Bitmap> Bitmap::decode(const uint8_t bytes[], size_t byteCount)
 {
     static ImageFormat decoders[] = {
         {
@@ -71,19 +74,19 @@
 
         // Immediately discard decoders with fingerprints that are longer than
         // the file buffer.
-        if (recognizer.fingerprint.size() > bytes.size())
+        if (recognizer.fingerprint.size() > byteCount)
         {
             continue;
         }
 
         // If the fingerprint doesn't match, discrd this decoder. These are all bytes so .size() is
         // fine here.
-        if (std::memcmp(fingerprint.data(), bytes.data(), fingerprint.size()) != 0)
+        if (std::memcmp(fingerprint.data(), bytes, fingerprint.size()) != 0)
         {
             continue;
         }
 
-        auto bitmap = recognizer.decodeImage(bytes);
+        auto bitmap = recognizer.decodeImage(bytes, byteCount);
         if (!bitmap)
         {
             fprintf(stderr, "Bitmap::decode - failed to decode a %s.\n", recognizer.name);
@@ -117,4 +120,3 @@
     m_Bytes = std::move(nextBytes);
     m_PixelFormat = format;
 }
-#endif
\ No newline at end of file
diff --git a/viewer/src/tess/decode_png.cpp b/decoders/src/decode_png.cpp
similarity index 91%
rename from viewer/src/tess/decode_png.cpp
rename to decoders/src/decode_png.cpp
index 63998f4..58a7a30 100644
--- a/viewer/src/tess/decode_png.cpp
+++ b/decoders/src/decode_png.cpp
@@ -1,6 +1,10 @@
-#ifdef RIVE_RENDERER_TESS
-#include "viewer/tess/bitmap_decoder.hpp"
+/*
+ * Copyright 2023 Rive
+ */
+
+#include "rive/decoders/bitmap_decoder.hpp"
 #include "png.h"
+#include <algorithm>
 
 struct EncodedImageBuffer
 {
@@ -28,7 +32,7 @@
     }
 }
 
-std::unique_ptr<Bitmap> DecodePng(rive::Span<const uint8_t> bytes)
+std::unique_ptr<Bitmap> DecodePng(const uint8_t bytes[], size_t byteCount)
 {
     png_structp png_ptr;
     png_infop info_ptr;
@@ -52,8 +56,8 @@
     }
 
     EncodedImageBuffer stream = {
-        .bytes = bytes.data(),
-        .size = bytes.size(),
+        .bytes = bytes,
+        .size = byteCount,
         .position = 0,
     };
 
@@ -135,6 +139,5 @@
             pixelFormat = Bitmap::PixelFormat::R;
             break;
     }
-    return rivestd::make_unique<Bitmap>(width, height, pixelFormat, pixelBuffer);
+    return std::make_unique<Bitmap>(width, height, pixelFormat, pixelBuffer);
 }
-#endif
\ No newline at end of file
diff --git a/viewer/build/premake5_viewer.lua b/viewer/build/premake5_viewer.lua
index 3e5f9fb..0d100c0 100644
--- a/viewer/build/premake5_viewer.lua
+++ b/viewer/build/premake5_viewer.lua
@@ -12,7 +12,7 @@
 skia = dependencies .. '/skia'
 
 if _OPTIONS.renderer == 'tess' then
-    dofile(path.join(path.getabsolute(dependencies) .. '/../..', 'premake5_libpng.lua'))
+    dofile(rive .. '/decoders/build/premake5.lua')
     dofile(path.join(path.getabsolute(rive_tess) .. '/build', 'premake5_tess.lua'))
 else
     -- tess renderer includes this
@@ -22,7 +22,7 @@
 project 'rive_viewer'
 do
     if _OPTIONS.renderer == 'tess' then
-        dependson 'libpng'
+        dependson 'rive_decoders'
     end
     kind 'ConsoleApp'
     language 'C++'
@@ -112,13 +112,14 @@
     do
         includedirs {
             rive_tess .. '/include',
-            libpng
+            rive .. '/decoders/include',
         }
         defines {
             'RIVE_RENDERER_TESS'
         }
         links {
             'rive_tess_renderer',
+            'rive_decoders',
             'libpng',
             'zlib'
         }
diff --git a/viewer/src/sample_tools/sample_atlas_packer.cpp b/viewer/src/sample_tools/sample_atlas_packer.cpp
index 5848ef7..9f74cbe 100644
--- a/viewer/src/sample_tools/sample_atlas_packer.cpp
+++ b/viewer/src/sample_tools/sample_atlas_packer.cpp
@@ -1,7 +1,7 @@
 #ifdef RIVE_RENDERER_TESS
 #include "viewer/sample_tools/sample_atlas_packer.hpp"
 #include "utils/no_op_factory.hpp"
-#include "viewer/tess/bitmap_decoder.hpp"
+#include "rive/decoders/bitmap_decoder.hpp"
 #include "rive/file.hpp"
 #include "rive/assets/image_asset.hpp"
 #include "rive/tess/sokol/sokol_tess_renderer.hpp"
@@ -29,7 +29,7 @@
 {
     std::unique_ptr<RenderImage> decodeImage(Span<const uint8_t> bytes) override
     {
-        auto bitmap = Bitmap::decode(bytes);
+        auto bitmap = Bitmap::decode(bytes.data(), bytes.size());
         if (bitmap)
         {
             // We have a bitmap, let's make an image.
@@ -243,4 +243,4 @@
         }
     }
 }
-#endif
\ No newline at end of file
+#endif
diff --git a/viewer/src/tess/viewer_sokol_factory.cpp b/viewer/src/tess/viewer_sokol_factory.cpp
index a403693..90a8feb 100644
--- a/viewer/src/tess/viewer_sokol_factory.cpp
+++ b/viewer/src/tess/viewer_sokol_factory.cpp
@@ -1,12 +1,12 @@
 #ifdef RIVE_RENDERER_TESS
 #include "viewer/tess/viewer_sokol_factory.hpp"
-#include "viewer/tess/bitmap_decoder.hpp"
+#include "rive/decoders/bitmap_decoder.hpp"
 #include "rive/tess/sokol/sokol_tess_renderer.hpp"
 #include "sokol_gfx.h"
 
 std::unique_ptr<rive::RenderImage> ViewerSokolFactory::decodeImage(rive::Span<const uint8_t> bytes)
 {
-    auto bitmap = Bitmap::decode(bytes);
+    auto bitmap = Bitmap::decode(bytes.data(), bytes.size());
     if (bitmap)
     {
         // We have a bitmap, let's make an image.
@@ -33,4 +33,4 @@
     }
     return nullptr;
 }
-#endif
\ No newline at end of file
+#endif