blob: e88e82e5c16e1fe5f81f2779d300c77dedd96096 [file] [log] [blame]
/*
* Copyright 2022 Rive
*/
#include "viewer/viewer_host.hpp"
#include "viewer/viewer_content.hpp"
#ifdef RIVE_RENDERER_SKIA
#ifdef RIVE_BUILD_FOR_APPLE
#include "cg_skia_factory.hpp"
static rive::CGSkiaFactory skiaFactory;
#else
#include "skia_factory.hpp"
static rive::SkiaFactory skiaFactory;
#endif
#include "skia_renderer.hpp"
#include "include/core/SkSurface.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkPaint.h"
#include "include/core/SkSize.h"
#include "GrDirectContext.h"
sk_sp<GrDirectContext> makeSkiaContext();
sk_sp<SkSurface> makeSkiaSurface(GrDirectContext* context, int width, int height);
void skiaPresentSurface(sk_sp<SkSurface> surface);
// Experimental flag, until we complete coregraphics_host
//#define TEST_CG_RENDERER
//#define SW_SKIA_MODE
#ifdef TEST_CG_RENDERER
#include "cg_factory.hpp"
#include "cg_renderer.hpp"
#include "mac_utils.hpp"
static void render_with_cg(SkCanvas* canvas, int w, int h, ViewerContent* content, double elapsed) {
// cons up a CGContext
auto pixels = SkData::MakeUninitialized(w * h * 4);
auto bytes = (uint8_t*)pixels->writable_data();
std::fill(bytes, bytes + pixels->size(), 0);
AutoCF space = CGColorSpaceCreateDeviceRGB();
auto info = kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast;
AutoCF ctx = CGBitmapContextCreate(bytes, w, h, 8, w * 4, space, info);
// Wrap it with our renderer
rive::CGRenderer renderer(ctx, w, h);
content->handleDraw(&renderer, elapsed);
CGContextFlush(ctx);
// Draw the pixels into the canvas
auto img = SkImage::MakeRasterData(SkImageInfo::MakeN32Premul(w, h), pixels, w * 4);
canvas->drawImage(img, 0, 0, SkSamplingOptions(SkFilterMode::kNearest), nullptr);
}
#endif
class SkiaViewerHost : public ViewerHost {
public:
sk_sp<GrDirectContext> m_context;
SkISize m_dimensions;
bool init(sg_pass_action* action, int width, int height) override {
m_dimensions = {width, height};
#if defined(SK_METAL)
// Skia is layered behind the Sokol view, so we need to make sure Sokol
// clears transparent. Skia will draw the background.
*action = (sg_pass_action){.colors[0] = {
.action = SG_ACTION_CLEAR,
.value =
{ 0.0f,
0.0,
0.0f,
0.0 }
}};
#elif defined(SK_GL)
// Skia commands are issued to the same GL context before Sokol, so we need
// to make sure Sokol does not clear the buffer.
*action = (sg_pass_action){.colors[0] = {.action = SG_ACTION_DONTCARE }};
#endif
m_context = makeSkiaContext();
return m_context != nullptr;
}
void handleResize(int width, int height) override { m_dimensions = {width, height}; }
void beforeDefaultPass(ViewerContent* content, double elapsed) override {
m_context->resetContext();
auto surf = makeSkiaSurface(m_context.get(), m_dimensions.width(), m_dimensions.height());
SkCanvas* canvas = surf->getCanvas();
SkPaint paint;
paint.setColor(0xFF161616);
canvas->drawPaint(paint);
if (content) {
#ifdef TEST_CG_RENDERER
render_with_cg(canvas, m_dimensions.width(), m_dimensions.height(), content, elapsed);
#elif defined(SW_SKIA_MODE)
auto info = SkImageInfo::MakeN32Premul(m_dimensions.width(), m_dimensions.height());
auto swsurf = SkSurface::MakeRaster(info);
rive::SkiaRenderer skiaRenderer(swsurf->getCanvas());
content->handleDraw(&skiaRenderer, elapsed);
auto img = swsurf->makeImageSnapshot();
canvas->drawImage(img, 0, 0, SkSamplingOptions(SkFilterMode::kNearest), nullptr);
#else
rive::SkiaRenderer skiaRenderer(canvas);
content->handleDraw(&skiaRenderer, elapsed);
#endif
}
canvas->flush();
skiaPresentSurface(surf);
sg_reset_state_cache();
}
};
std::unique_ptr<ViewerHost> ViewerHost::Make() { return std::make_unique<SkiaViewerHost>(); }
rive::Factory* ViewerHost::Factory() {
#ifdef TEST_CG_RENDERER
static rive::CGFactory gFactory;
return &gFactory;
#else
return &skiaFactory;
#endif
}
#endif