Move over goldens_gpu
diff --git a/skia/goldens/build/premake5.lua b/skia/goldens/build/premake5.lua
index d6c3786..8a26cd4 100644
--- a/skia/goldens/build/premake5.lua
+++ b/skia/goldens/build/premake5.lua
@@ -93,6 +93,107 @@
defines {"NDEBUG"}
optimize "On"
+project "goldens_gpu"
+ kind "ConsoleApp"
+ language "C++"
+ cppdialect "C++17"
+ toolset "clang"
+ targetdir "bin/%{cfg.buildcfg}"
+ objdir "obj/%{cfg.buildcfg}"
+ includedirs {
+ "../../utils",
+ "../../dependencies/glfw/include",
+ "../src",
+ "../../../include",
+ "../../renderer/include",
+ "../../dependencies/",
+ "../../dependencies/skia",
+ "../../dependencies/skia/include/core",
+ "../../dependencies/skia/third_party/libpng",
+ }
+
+ if os.host() == 'macosx' then
+ links {
+ "Cocoa.framework",
+ "CoreFoundation.framework",
+ "CoreMedia.framework",
+ "CoreServices.framework",
+ "IOKit.framework",
+ "Security.framework",
+ "OpenGL.framework",
+ "bz2",
+ "iconv",
+ "lzma",
+ "rive_skia_renderer",
+ "rive",
+ "skia",
+ "glfw3",
+ "z" -- lib av format
+ }
+ libdirs {
+ "../dependencies/glfw/build/src",
+ }
+ elseif os.host() == "windows" then
+ architecture "x64"
+ links {
+ "rive_skia_renderer",
+ "rive",
+ "skia.lib",
+ "glfw3.lib",
+ "opengl32.lib"
+ }
+ libdirs {
+ "../dependencies/glfw/build/src/Release",
+ }
+ defines {"_USE_MATH_DEFINES", "_CRT_SECURE_NO_WARNINGS"}
+ buildoptions {WINDOWS_CLANG_CL_SUPPRESSED_WARNINGS}
+ staticruntime "on" -- Match Skia's /MT flag for link compatibility
+ runtime "Release" -- Use /MT even in debug (/MTd is incompatible with Skia)
+ else
+ links {
+ "m",
+ "rive_skia_renderer",
+ "rive",
+ "skia",
+ "glfw3",
+ "z",
+ "dl",
+ "pthread",
+ "GL"
+ }
+ libdirs {
+ "../dependencies/glfw/build/src",
+ }
+ end
+
+ libdirs {
+ "../build/%{cfg.system}/bin/%{cfg.buildcfg}",
+ "../../dependencies/skia/out/static",
+ "../../renderer/build/%{cfg.system}/bin/%{cfg.buildcfg}",
+ }
+
+ files {
+ "../../utils/*.cpp",
+ "../src/goldens_gpu.cpp",
+ "../src/goldens_grid.cpp",
+ }
+
+ buildoptions {"-Wall", "-fno-rtti"}
+
+ defines {
+ "SK_GL",
+ "GL_SILENCE_DEPRECATION", -- For glReadPixels()
+ }
+
+ filter "configurations:debug"
+ defines {"DEBUG"}
+ symbols "On"
+
+ filter "configurations:release"
+ defines {"RELEASE"}
+ defines {"NDEBUG"}
+ optimize "On"
+
-- Clean Function --
newaction {
diff --git a/skia/goldens/src/goldens_gpu.cpp b/skia/goldens/src/goldens_gpu.cpp
index 6de6425..3e8c3c9 100644
--- a/skia/goldens/src/goldens_gpu.cpp
+++ b/skia/goldens/src/goldens_gpu.cpp
@@ -13,7 +13,7 @@
#include "skia/include/gpu/gl/GrGLInterface.h"
#include "skia/include/gpu/gl/GrGLAssembleInterface.h"
#include "skia/third_party/externals/libpng/png.h"
-#include "tools/write_png_file.hpp"
+#include "write_png_file.hpp"
#include <iostream>
#include <string>
diff --git a/skia/utils/renderer_target.cpp b/skia/utils/renderer_target.cpp
new file mode 100644
index 0000000..55fca40
--- /dev/null
+++ b/skia/utils/renderer_target.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2022 Rive
+ */
+
+#ifdef TESTING
+
+// Don't compile this file as part of the "tests" project.
+
+#else
+
+#include "renderer_target.hpp"
+
+#include "GLFW/glfw3.h"
+#include "skia_renderer.hpp"
+#include "skia/include/core/SkImage.h"
+#include "skia/include/core/SkPixmap.h"
+#include "skia/include/core/SkStream.h"
+#include "skia/include/core/SkSurface.h"
+#include "skia/include/gpu/GrDirectContext.h"
+#include "skia/include/gpu/gl/GrGLInterface.h"
+#include "skia/include/gpu/gl/GrGLAssembleInterface.h"
+#include "write_png_file.hpp"
+#include <vector>
+
+class SkiaRasterTarget : public RendererTarget {
+public:
+ std::unique_ptr<rive::Renderer> reset(int width, int height) override {
+ auto info = SkImageInfo::MakeN32Premul(width, height);
+ m_Surface = SkSurface::MakeRaster(info);
+ return std::make_unique<rive::SkiaRenderer>(m_Surface->getCanvas());
+ }
+
+ void flush() const override { m_Surface->getCanvas()->flush(); }
+
+ void dumpToPNG(const std::string& filepath) const override {
+ assert(m_Surface);
+ flush();
+ auto img = m_Surface->makeImageSnapshot();
+ std::vector<uint8_t> pixels(img->height() * img->width() * 4);
+ SkColorInfo colorInfo(kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
+ img->readPixels(nullptr,
+ SkPixmap(SkImageInfo::Make({img->width(), img->height()}, colorInfo),
+ pixels.data(),
+ img->width() * 4),
+ 0,
+ 0);
+ WritePNGFile(pixels.data(), img->width(), img->height(), false, filepath.c_str());
+ }
+
+private:
+ sk_sp<SkSurface> m_Surface;
+};
+
+class SkiaGLTarget : public RendererTarget {
+public:
+ static GrGLFuncPtr GetGLProcAddress(void* ctx, const char name[]) {
+ return glfwGetProcAddress(name);
+ }
+
+ SkiaGLTarget() {
+ if (!glfwInit()) {
+ fprintf(stderr, "Failed to initialize GLFW.\n");
+ exit(-1);
+ }
+
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+ glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
+
+ m_Window = glfwCreateWindow(256, 256, "Rive", nullptr, nullptr);
+ if (!m_Window) {
+ glfwTerminate();
+ fprintf(stderr, "Failed to create GLFW m_Window.\n");
+ exit(-1);
+ }
+
+ glfwMakeContextCurrent(m_Window);
+
+ m_GrContext =
+ GrDirectContext::MakeGL(GrGLMakeAssembledInterface(nullptr, GetGLProcAddress));
+ }
+
+ ~SkiaGLTarget() override {
+#if 0
+ // FIXME: This hangs on my Windows system ¯\_(ツ)_/¯
+ glfwPollEvents();
+ glfwDestroyWindow(m_Window);
+ glfwTerminate();
+#endif
+ }
+
+ std::unique_ptr<rive::Renderer> reset(int width, int height) override {
+ if (width != m_Width || height != m_Height) {
+ glfwSetWindowSize(m_Window, width, height);
+ }
+
+ glViewport(0, 0, width, height);
+
+ GrBackendRenderTarget backendRT(
+ width, height, 1 /*samples*/, 0 /*stencilBits*/, {0 /*fbo 0*/, GL_RGBA8});
+
+ SkSurfaceProps surfProps(0, kUnknown_SkPixelGeometry);
+
+ m_Surface = SkSurface::MakeFromBackendRenderTarget(m_GrContext.get(),
+ backendRT,
+ kBottomLeft_GrSurfaceOrigin,
+ kRGBA_8888_SkColorType,
+ nullptr,
+ &surfProps);
+ m_Width = width;
+ m_Height = height;
+
+ return std::make_unique<rive::SkiaRenderer>(m_Surface->getCanvas());
+ }
+
+ void flush() const override { m_Surface->getCanvas()->flush(); }
+
+ void dumpToPNG(const std::string& filepath) const override {
+ flush();
+ std::vector<uint8_t> pixels(m_Height * m_Width * 4);
+ glReadPixels(0, 0, m_Width, m_Height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
+ glfwSwapBuffers(m_Window);
+ WritePNGFile(pixels.data(), m_Width, m_Height, true, filepath.c_str());
+ }
+
+private:
+ GLFWwindow* m_Window = nullptr;
+ sk_sp<GrDirectContext> m_GrContext;
+ sk_sp<SkSurface> m_Surface;
+ int m_Width = 0;
+ int m_Height = 0;
+};
+
+static std::unique_ptr<RendererTarget> s_RendererTarget;
+
+void RendererTarget::Init(Type type) {
+ assert(!s_RendererTarget);
+ switch (type) {
+ case Type::skia_cpu:
+ s_RendererTarget = std::make_unique<SkiaRasterTarget>();
+ break;
+ case Type::skia_gl:
+ s_RendererTarget = std::make_unique<SkiaGLTarget>();
+ break;
+ }
+}
+
+RendererTarget* RendererTarget::Get() {
+ assert(s_RendererTarget); // Call Init() first!
+ return s_RendererTarget.get();
+}
+
+#endif
diff --git a/skia/utils/renderer_target.hpp b/skia/utils/renderer_target.hpp
new file mode 100644
index 0000000..542b636
--- /dev/null
+++ b/skia/utils/renderer_target.hpp
@@ -0,0 +1,34 @@
+#ifndef RENDERER_TARGET_HPP
+#define RENDERER_TARGET_HPP
+
+#include <memory>
+#include <string>
+
+namespace rive {
+ class Renderer;
+};
+
+// Wraps a factory for rive::Renderer and a singleton target for it to render into (GL window, HTML
+// canvas, software buffer, etc.):
+//
+// RendererTarget::Init(type);
+// renderer = RendererTarget::Get()->reset(width, height);
+// ...
+//
+class RendererTarget {
+public:
+ enum class Type {
+ skia_cpu,
+ skia_gl
+ };
+
+ static void Init(Type);
+ static RendererTarget* Get();
+
+ virtual std::unique_ptr<rive::Renderer> reset(int width, int height) = 0;
+ virtual void flush() const = 0;
+ virtual void dumpToPNG(const std::string& filepath) const = 0;
+ virtual ~RendererTarget() {}
+};
+
+#endif
diff --git a/skia/utils/write_png_file.cpp b/skia/utils/write_png_file.cpp
new file mode 100644
index 0000000..3dd3135
--- /dev/null
+++ b/skia/utils/write_png_file.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2022 Rive
+ */
+
+#ifdef TESTING
+
+// Don't compile this file as part of the "tests" project.
+
+#else
+
+#include "write_png_file.hpp"
+
+#include "skia/third_party/externals/libpng/png.h"
+#include <stdio.h>
+#include <vector>
+
+void WritePNGFile(uint8_t* pixels, int width, int height, bool flipY, const char* file_name) {
+ FILE* fp = fopen(file_name, "wb");
+ if (!fp) {
+ fprintf(stderr, "WritePNGFile: File %s could not be opened for writing", file_name);
+ return;
+ }
+
+ auto png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+
+ if (!png) {
+ fprintf(stderr, "WritePNGFile: png_create_write_struct failed");
+ return;
+ }
+
+ // Use pngcrush method 12 for better compression performance.
+ // https://stackoverflow.com/questions/22167397/improve-performance-with-libpng
+ png_set_compression_level(png, 2);
+ png_set_compression_strategy(png, 2);
+ png_set_filter(png, 0, PNG_FILTER_SUB);
+
+ auto info = png_create_info_struct(png);
+ if (!info) {
+ fprintf(stderr, "WritePNGFile: png_create_info_struct failed");
+ return;
+ }
+
+ if (setjmp(png_jmpbuf(png))) {
+ fprintf(stderr, "WritePNGFile: Error during init_io");
+ return;
+ }
+
+ png_init_io(png, fp);
+
+ // Write header.
+ if (setjmp(png_jmpbuf(png))) {
+ fprintf(stderr, "WritePNGFile: Error during writing header");
+ return;
+ }
+
+ png_set_IHDR(png,
+ info,
+ width,
+ height,
+ 8,
+ PNG_COLOR_TYPE_RGB_ALPHA,
+ PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_BASE,
+ PNG_FILTER_TYPE_BASE);
+
+ png_write_info(png, info);
+
+ // Write bytes.
+ if (setjmp(png_jmpbuf(png))) {
+ fprintf(stderr, "WritePNGFile: Error during writing bytes");
+ return;
+ }
+
+ std::vector<uint8_t*> rows(height);
+ for (int y = 0; y < height; ++y) {
+ rows[y] = pixels + (flipY ? height - 1 - y : y) * width * 4;
+ }
+ png_write_image(png, rows.data());
+
+ // End write.
+ if (setjmp(png_jmpbuf(png))) {
+ fprintf(stderr, "WritePNGFile: Error during end of write");
+ return;
+ }
+
+ png_write_end(png, NULL);
+
+ fclose(fp);
+}
+
+#endif
diff --git a/skia/utils/write_png_file.hpp b/skia/utils/write_png_file.hpp
new file mode 100644
index 0000000..4d6e60c
--- /dev/null
+++ b/skia/utils/write_png_file.hpp
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2022 Rive
+ */
+
+#ifndef WRITE_PNG_FILE_HPP
+#define WRITE_PNG_FILE_HPP
+
+#include <stdint.h>
+
+void WritePNGFile(uint8_t* pixels, int width, int height, bool flipY, const char* file_name);
+
+#endif