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