mirror of
https://github.com/rive-app/rive-cpp.git
synced 2026-01-18 21:21:17 +01:00
Fission the CG and Skia renderers
Diffs= c63346b93 Fission the CG and Skia renderers (#6071) Co-authored-by: Chris Dalton <99840794+csmartdalton@users.noreply.github.com>
This commit is contained in:
@@ -24,6 +24,7 @@ do
|
||||
objdir '%{cfg.system}/obj/%{cfg.buildcfg}'
|
||||
includedirs {
|
||||
'../include',
|
||||
'../../../cg_renderer/include',
|
||||
'../../../include'
|
||||
}
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Rive
|
||||
*/
|
||||
|
||||
#ifndef _RIVE_CG_FACTORY_HPP_
|
||||
#define _RIVE_CG_FACTORY_HPP_
|
||||
|
||||
#include "rive/factory.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace rive
|
||||
{
|
||||
|
||||
class CGFactory : public Factory
|
||||
{
|
||||
public:
|
||||
rcp<RenderBuffer> makeRenderBuffer(RenderBufferType, RenderBufferFlags, size_t) override;
|
||||
|
||||
rcp<RenderShader> makeLinearGradient(float sx,
|
||||
float sy,
|
||||
float ex,
|
||||
float ey,
|
||||
const ColorInt colors[], // [count]
|
||||
const float stops[], // [count]
|
||||
size_t count) override;
|
||||
|
||||
rcp<RenderShader> makeRadialGradient(float cx,
|
||||
float cy,
|
||||
float radius,
|
||||
const ColorInt colors[], // [count]
|
||||
const float stops[], // [count]
|
||||
size_t count) override;
|
||||
|
||||
std::unique_ptr<RenderPath> makeRenderPath(RawPath&, FillRule) override;
|
||||
|
||||
std::unique_ptr<RenderPath> makeEmptyRenderPath() override;
|
||||
|
||||
std::unique_ptr<RenderPaint> makeRenderPaint() override;
|
||||
|
||||
std::unique_ptr<RenderImage> decodeImage(Span<const uint8_t>) override;
|
||||
};
|
||||
|
||||
} // namespace rive
|
||||
#endif
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Rive
|
||||
*/
|
||||
|
||||
#ifndef _RIVE_CG_RENDERER_HPP_
|
||||
#define _RIVE_CG_RENDERER_HPP_
|
||||
|
||||
#include "rive/renderer.hpp"
|
||||
|
||||
#if defined(RIVE_BUILD_FOR_OSX)
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#elif defined(RIVE_BUILD_FOR_IOS)
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
#include <ImageIO/ImageIO.h>
|
||||
#endif
|
||||
|
||||
namespace rive
|
||||
{
|
||||
class CGRenderer : public Renderer
|
||||
{
|
||||
protected:
|
||||
CGContextRef m_ctx;
|
||||
|
||||
public:
|
||||
CGRenderer(CGContextRef ctx, int width, int height);
|
||||
~CGRenderer() override;
|
||||
|
||||
void save() override;
|
||||
void restore() override;
|
||||
void transform(const Mat2D& transform) override;
|
||||
void clipPath(RenderPath* path) override;
|
||||
void drawPath(RenderPath* path, RenderPaint* paint) override;
|
||||
void drawImage(const RenderImage*, BlendMode, float opacity) override;
|
||||
void drawImageMesh(const RenderImage*,
|
||||
rcp<RenderBuffer> vertices_f32,
|
||||
rcp<RenderBuffer> uvCoords_f32,
|
||||
rcp<RenderBuffer> indices_u16,
|
||||
uint32_t vertexCount,
|
||||
uint32_t indexCount,
|
||||
BlendMode,
|
||||
float opacity) override;
|
||||
};
|
||||
} // namespace rive
|
||||
#endif
|
||||
@@ -3,17 +3,11 @@
|
||||
|
||||
#include "rive/rive_types.hpp"
|
||||
#include "rive/span.hpp"
|
||||
#include "utils/auto_cf.hpp"
|
||||
#include <string>
|
||||
|
||||
#ifdef RIVE_BUILD_FOR_APPLE
|
||||
|
||||
#if defined(RIVE_BUILD_FOR_OSX)
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#elif defined(RIVE_BUILD_FOR_IOS)
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <CoreGraphics/CGImage.h>
|
||||
#endif
|
||||
|
||||
template <size_t N, typename T> class AutoSTArray
|
||||
{
|
||||
T m_storage[N];
|
||||
@@ -61,67 +55,6 @@ static inline std::string tag2str(uint32_t tag)
|
||||
return str;
|
||||
}
|
||||
|
||||
template <typename T> class AutoCF
|
||||
{
|
||||
T m_obj;
|
||||
|
||||
public:
|
||||
AutoCF(T obj = nullptr) : m_obj(obj) {}
|
||||
AutoCF(const AutoCF& other)
|
||||
{
|
||||
if (other.m_obj)
|
||||
{
|
||||
CFRetain(other.m_obj);
|
||||
}
|
||||
m_obj = other.m_obj;
|
||||
}
|
||||
AutoCF(AutoCF&& other)
|
||||
{
|
||||
m_obj = other.m_obj;
|
||||
other.m_obj = nullptr;
|
||||
}
|
||||
~AutoCF()
|
||||
{
|
||||
if (m_obj)
|
||||
{
|
||||
CFRelease(m_obj);
|
||||
}
|
||||
}
|
||||
|
||||
AutoCF& operator=(const AutoCF& other)
|
||||
{
|
||||
if (m_obj != other.m_obj)
|
||||
{
|
||||
if (other.m_obj)
|
||||
{
|
||||
CFRetain(other.m_obj);
|
||||
}
|
||||
if (m_obj)
|
||||
{
|
||||
CFRelease(m_obj);
|
||||
}
|
||||
m_obj = other.m_obj;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset(T obj)
|
||||
{
|
||||
if (obj != m_obj)
|
||||
{
|
||||
if (m_obj)
|
||||
{
|
||||
CFRelease(m_obj);
|
||||
}
|
||||
m_obj = obj;
|
||||
}
|
||||
}
|
||||
|
||||
operator T() const { return m_obj; }
|
||||
operator bool() const { return m_obj != nullptr; }
|
||||
T get() const { return m_obj; }
|
||||
};
|
||||
|
||||
static inline float find_float(CFDictionaryRef dict, const void* key)
|
||||
{
|
||||
auto num = (CFNumberRef)CFDictionaryGetValue(dict, key);
|
||||
@@ -155,11 +88,5 @@ static inline float number_as_float(CFNumberRef num)
|
||||
return value;
|
||||
}
|
||||
|
||||
namespace rive
|
||||
{
|
||||
AutoCF<CGImageRef> DecodeToCGImage(Span<const uint8_t>);
|
||||
AutoCF<CGImageRef> FlipCGImageInY(AutoCF<CGImageRef>);
|
||||
} // namespace rive
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,534 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Rive
|
||||
*/
|
||||
|
||||
#include "rive/rive_types.hpp"
|
||||
|
||||
#ifdef RIVE_BUILD_FOR_APPLE
|
||||
|
||||
#include "cg_factory.hpp"
|
||||
#include "cg_renderer.hpp"
|
||||
#include "mac_utils.hpp"
|
||||
|
||||
#if defined(RIVE_BUILD_FOR_OSX)
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#elif defined(RIVE_BUILD_FOR_IOS)
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
#include <ImageIO/ImageIO.h>
|
||||
#endif
|
||||
|
||||
#include "utils/factory_utils.hpp"
|
||||
#include "rive/math/vec2d.hpp"
|
||||
#include "rive/core/type_conversions.hpp"
|
||||
#include "rive/shapes/paint/color.hpp"
|
||||
|
||||
using namespace rive;
|
||||
|
||||
static CGAffineTransform convert(const Mat2D& m)
|
||||
{
|
||||
return CGAffineTransformMake(m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
}
|
||||
|
||||
static CGPathDrawingMode convert(FillRule rule)
|
||||
{
|
||||
return (rule == FillRule::nonZero) ? CGPathDrawingMode::kCGPathFill
|
||||
: CGPathDrawingMode::kCGPathEOFill;
|
||||
}
|
||||
|
||||
static CGLineJoin convert(StrokeJoin j)
|
||||
{
|
||||
const CGLineJoin cg[] = {
|
||||
CGLineJoin::kCGLineJoinMiter,
|
||||
CGLineJoin::kCGLineJoinRound,
|
||||
CGLineJoin::kCGLineJoinBevel,
|
||||
};
|
||||
return cg[(unsigned)j];
|
||||
}
|
||||
|
||||
static CGLineCap convert(StrokeCap c)
|
||||
{
|
||||
const CGLineCap cg[] = {
|
||||
CGLineCap::kCGLineCapButt,
|
||||
CGLineCap::kCGLineCapRound,
|
||||
CGLineCap::kCGLineCapSquare,
|
||||
};
|
||||
return cg[(unsigned)c];
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
static CGBlendMode convert(BlendMode mode) {
|
||||
CGBlendMode cg = kCGBlendModeNormal;
|
||||
switch (mode) {
|
||||
case BlendMode::srcOver: cg = kCGBlendModeNormal; break;
|
||||
case BlendMode::screen: cg = kCGBlendModeScreen; break;
|
||||
case BlendMode::overlay: cg = kCGBlendModeOverlay; break;
|
||||
case BlendMode::darken: cg = kCGBlendModeDarken; break;
|
||||
case BlendMode::lighten: cg = kCGBlendModeLighten; break;
|
||||
case BlendMode::colorDodge: cg = kCGBlendModeColorDodge; break;
|
||||
case BlendMode::colorBurn: cg = kCGBlendModeColorBurn; break;
|
||||
case BlendMode::hardLight: cg = kCGBlendModeHardLight; break;
|
||||
case BlendMode::softLight: cg = kCGBlendModeSoftLight; break;
|
||||
case BlendMode::difference: cg = kCGBlendModeDifference; break;
|
||||
case BlendMode::exclusion: cg = kCGBlendModeExclusion; break;
|
||||
case BlendMode::multiply: cg = kCGBlendModeMultiply; break;
|
||||
case BlendMode::hue: cg = kCGBlendModeHue; break;
|
||||
case BlendMode::saturation: cg = kCGBlendModeSaturation; break;
|
||||
case BlendMode::color: cg = kCGBlendModeColor; break;
|
||||
case BlendMode::luminosity: cg = kCGBlendModeLuminosity; break;
|
||||
}
|
||||
return cg;
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
static void convertColor(ColorInt c, CGFloat rgba[])
|
||||
{
|
||||
constexpr float kByteToUnit = 1.0f / 255;
|
||||
rgba[0] = colorRed(c) * kByteToUnit;
|
||||
rgba[1] = colorGreen(c) * kByteToUnit;
|
||||
rgba[2] = colorBlue(c) * kByteToUnit;
|
||||
rgba[3] = colorAlpha(c) * kByteToUnit;
|
||||
}
|
||||
|
||||
class CGRenderPath : public RenderPath
|
||||
{
|
||||
private:
|
||||
AutoCF<CGMutablePathRef> m_path = CGPathCreateMutable();
|
||||
CGPathDrawingMode m_fillMode = CGPathDrawingMode::kCGPathFill;
|
||||
|
||||
public:
|
||||
CGRenderPath() {}
|
||||
|
||||
CGRenderPath(Span<const Vec2D> pts, Span<const PathVerb> vbs, FillRule rule)
|
||||
{
|
||||
m_fillMode = convert(rule);
|
||||
|
||||
auto p = pts.data();
|
||||
for (auto v : vbs)
|
||||
{
|
||||
switch ((PathVerb)v)
|
||||
{
|
||||
case PathVerb::move:
|
||||
CGPathMoveToPoint(m_path, nullptr, p[0].x, p[0].y);
|
||||
p += 1;
|
||||
break;
|
||||
case PathVerb::line:
|
||||
CGPathAddLineToPoint(m_path, nullptr, p[0].x, p[0].y);
|
||||
p += 1;
|
||||
break;
|
||||
case PathVerb::quad:
|
||||
CGPathAddQuadCurveToPoint(m_path, nullptr, p[0].x, p[0].y, p[1].x, p[1].y);
|
||||
p += 2;
|
||||
break;
|
||||
case PathVerb::cubic:
|
||||
CGPathAddCurveToPoint(m_path,
|
||||
nullptr,
|
||||
p[0].x,
|
||||
p[0].y,
|
||||
p[1].x,
|
||||
p[1].y,
|
||||
p[2].x,
|
||||
p[2].y);
|
||||
p += 3;
|
||||
break;
|
||||
case PathVerb::close:
|
||||
CGPathCloseSubpath(m_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(p == pts.end());
|
||||
}
|
||||
|
||||
CGPathRef path() const { return m_path.get(); }
|
||||
CGPathDrawingMode drawingMode(bool isStroke) const
|
||||
{
|
||||
return isStroke ? CGPathDrawingMode::kCGPathStroke : m_fillMode;
|
||||
}
|
||||
|
||||
void rewind() override { m_path.reset(CGPathCreateMutable()); }
|
||||
void addRenderPath(RenderPath* path, const Mat2D& mx) override
|
||||
{
|
||||
auto transform = convert(mx);
|
||||
CGPathAddPath(m_path, &transform, ((CGRenderPath*)path)->path());
|
||||
}
|
||||
void fillRule(FillRule value) override
|
||||
{
|
||||
m_fillMode = (value == FillRule::nonZero) ? CGPathDrawingMode::kCGPathFill
|
||||
: CGPathDrawingMode::kCGPathEOFill;
|
||||
}
|
||||
void moveTo(float x, float y) override { CGPathMoveToPoint(m_path, nullptr, x, y); }
|
||||
void lineTo(float x, float y) override { CGPathAddLineToPoint(m_path, nullptr, x, y); }
|
||||
void cubicTo(float ox, float oy, float ix, float iy, float x, float y) override
|
||||
{
|
||||
CGPathAddCurveToPoint(m_path, nullptr, ox, oy, ix, iy, x, y);
|
||||
}
|
||||
void close() override { CGPathCloseSubpath(m_path); }
|
||||
};
|
||||
|
||||
class CGRenderShader : public RenderShader
|
||||
{
|
||||
public:
|
||||
CGRenderShader() {}
|
||||
|
||||
static constexpr int clampOptions =
|
||||
kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
|
||||
|
||||
virtual void draw(CGContextRef) {}
|
||||
};
|
||||
|
||||
class CGRenderPaint : public RenderPaint
|
||||
{
|
||||
private:
|
||||
bool m_isStroke = false;
|
||||
CGFloat m_rgba[4] = {0, 0, 0, 1};
|
||||
float m_width = 1;
|
||||
CGLineJoin m_join = kCGLineJoinMiter;
|
||||
CGLineCap m_cap = kCGLineCapButt;
|
||||
CGBlendMode m_blend = kCGBlendModeNormal;
|
||||
rcp<RenderShader> m_shader;
|
||||
|
||||
public:
|
||||
CGRenderPaint() {}
|
||||
|
||||
bool isStroke() const { return m_isStroke; }
|
||||
float opacity() const { return m_rgba[3]; }
|
||||
|
||||
CGRenderShader* shader() const { return static_cast<CGRenderShader*>(m_shader.get()); }
|
||||
|
||||
void apply(CGContextRef ctx)
|
||||
{
|
||||
if (m_isStroke)
|
||||
{
|
||||
CGContextSetRGBStrokeColor(ctx, m_rgba[0], m_rgba[1], m_rgba[2], m_rgba[3]);
|
||||
CGContextSetLineWidth(ctx, m_width);
|
||||
CGContextSetLineJoin(ctx, m_join);
|
||||
CGContextSetLineCap(ctx, m_cap);
|
||||
}
|
||||
else
|
||||
{
|
||||
CGContextSetRGBFillColor(ctx, m_rgba[0], m_rgba[1], m_rgba[2], m_rgba[3]);
|
||||
}
|
||||
CGContextSetBlendMode(ctx, m_blend);
|
||||
}
|
||||
|
||||
void style(RenderPaintStyle style) override
|
||||
{
|
||||
m_isStroke = (style == RenderPaintStyle::stroke);
|
||||
}
|
||||
void color(ColorInt value) override { convertColor(value, m_rgba); }
|
||||
void thickness(float value) override { m_width = value; }
|
||||
void join(StrokeJoin value) override { m_join = convert(value); }
|
||||
void cap(StrokeCap value) override { m_cap = convert(value); }
|
||||
void blendMode(BlendMode value) override { m_blend = convert(value); }
|
||||
void shader(rcp<RenderShader> sh) override { m_shader = std::move(sh); }
|
||||
void invalidateStroke() override {}
|
||||
};
|
||||
|
||||
static CGGradientRef convert(const ColorInt colors[], const float stops[], size_t count)
|
||||
{
|
||||
AutoCF space = CGColorSpaceCreateDeviceRGB();
|
||||
std::vector<CGFloat> floats(count * 5); // colors[4] + stops[1]
|
||||
auto c = &floats[0];
|
||||
auto s = &floats[count * 4];
|
||||
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
convertColor(colors[i], &c[i * 4]);
|
||||
|
||||
// Rive wants the colors to be premultiplied *after* interpolation
|
||||
// Unfortunately, CG doesn't know about this option, it just does
|
||||
// a straight interpolation and uses the result (thinking it is
|
||||
// in premul form already). This can lead to artifacts in the drawing
|
||||
// (e.g. sparkles) so as a hack, we premul our color stops up front.
|
||||
// Not exactly correct, but does remove the sparkles.
|
||||
// A better fix might be to write a custom Shading proc... but that
|
||||
// is likely to be be slower (but need to try/time it to know for sure).
|
||||
CGFloat* p = &c[i * 4];
|
||||
p[0] *= p[3];
|
||||
p[1] *= p[3];
|
||||
p[2] *= p[3];
|
||||
}
|
||||
if (stops)
|
||||
{
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
s[i] = stops[i];
|
||||
}
|
||||
}
|
||||
return CGGradientCreateWithColorComponents(space, c, s, count);
|
||||
}
|
||||
|
||||
class CGRadialGradientRenderShader : public CGRenderShader
|
||||
{
|
||||
AutoCF<CGGradientRef> m_grad;
|
||||
CGPoint m_center;
|
||||
CGFloat m_radius;
|
||||
|
||||
public:
|
||||
CGRadialGradientRenderShader(float cx,
|
||||
float cy,
|
||||
float radius,
|
||||
const ColorInt colors[],
|
||||
const float stops[],
|
||||
size_t count) :
|
||||
m_grad(convert(colors, stops, count))
|
||||
{
|
||||
m_center = CGPointMake(cx, cy);
|
||||
m_radius = radius;
|
||||
}
|
||||
|
||||
void draw(CGContextRef ctx) override
|
||||
{
|
||||
CGContextDrawRadialGradient(ctx, m_grad, m_center, 0, m_center, m_radius, clampOptions);
|
||||
}
|
||||
};
|
||||
|
||||
class CGLinearGradientRenderShader : public CGRenderShader
|
||||
{
|
||||
AutoCF<CGGradientRef> m_grad;
|
||||
CGPoint m_start, m_end;
|
||||
|
||||
public:
|
||||
CGLinearGradientRenderShader(float sx,
|
||||
float sy,
|
||||
float ex,
|
||||
float ey,
|
||||
const ColorInt colors[], // [count]
|
||||
const float stops[], // [count]
|
||||
size_t count) :
|
||||
m_grad(convert(colors, stops, count))
|
||||
{
|
||||
m_start = CGPointMake(sx, sy);
|
||||
m_end = CGPointMake(ex, ey);
|
||||
}
|
||||
|
||||
void draw(CGContextRef ctx) override
|
||||
{
|
||||
CGContextDrawLinearGradient(ctx, m_grad, m_start, m_end, clampOptions);
|
||||
}
|
||||
};
|
||||
|
||||
class CGRenderImage : public RenderImage
|
||||
{
|
||||
public:
|
||||
AutoCF<CGImageRef> m_image;
|
||||
|
||||
CGRenderImage(const Span<const uint8_t> span) : m_image(DecodeToCGImage(span))
|
||||
{
|
||||
if (m_image)
|
||||
{
|
||||
m_Width = rive::castTo<uint32_t>(CGImageGetWidth(m_image.get()));
|
||||
m_Height = rive::castTo<uint32_t>(CGImageGetHeight(m_image.get()));
|
||||
}
|
||||
}
|
||||
|
||||
Mat2D localM2D() const { return Mat2D(1, 0, 0, -1, 0, (float)m_Height); }
|
||||
|
||||
void applyLocalMatrix(CGContextRef ctx) const
|
||||
{
|
||||
CGContextConcatCTM(ctx, CGAffineTransformMake(1, 0, 0, -1, 0, (float)m_Height));
|
||||
}
|
||||
|
||||
static const CGRenderImage* Cast(const RenderImage* image)
|
||||
{
|
||||
return static_cast<const CGRenderImage*>(image);
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CGRenderer::CGRenderer(CGContextRef ctx, int width, int height) : m_ctx(ctx)
|
||||
{
|
||||
CGContextSaveGState(ctx);
|
||||
|
||||
Mat2D m(1, 0, 0, -1, 0, height);
|
||||
CGContextConcatCTM(ctx, convert(m));
|
||||
|
||||
CGContextSetInterpolationQuality(ctx, kCGInterpolationMedium);
|
||||
}
|
||||
|
||||
CGRenderer::~CGRenderer() { CGContextRestoreGState(m_ctx); }
|
||||
|
||||
void CGRenderer::save() { CGContextSaveGState(m_ctx); }
|
||||
|
||||
void CGRenderer::restore() { CGContextRestoreGState(m_ctx); }
|
||||
|
||||
void CGRenderer::transform(const Mat2D& m) { CGContextConcatCTM(m_ctx, convert(m)); }
|
||||
|
||||
void CGRenderer::drawPath(RenderPath* path, RenderPaint* paint)
|
||||
{
|
||||
auto cgpaint = static_cast<CGRenderPaint*>(paint);
|
||||
auto cgpath = static_cast<CGRenderPath*>(path);
|
||||
|
||||
cgpaint->apply(m_ctx);
|
||||
|
||||
CGContextBeginPath(m_ctx);
|
||||
CGContextAddPath(m_ctx, cgpath->path());
|
||||
if (auto sh = cgpaint->shader())
|
||||
{
|
||||
if (cgpaint->isStroke())
|
||||
{
|
||||
// so we can clip against the "stroke" of the path
|
||||
CGContextReplacePathWithStrokedPath(m_ctx);
|
||||
}
|
||||
CGContextSaveGState(m_ctx);
|
||||
CGContextClip(m_ctx);
|
||||
|
||||
// so the gradient modulates with the color's alpha
|
||||
CGContextSetAlpha(m_ctx, cgpaint->opacity());
|
||||
|
||||
sh->draw(m_ctx);
|
||||
CGContextRestoreGState(m_ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
CGContextDrawPath(m_ctx, cgpath->drawingMode(cgpaint->isStroke()));
|
||||
}
|
||||
|
||||
assert(CGContextIsPathEmpty(m_ctx));
|
||||
}
|
||||
|
||||
void CGRenderer::clipPath(RenderPath* path)
|
||||
{
|
||||
auto cgpath = static_cast<CGRenderPath*>(path);
|
||||
|
||||
CGContextBeginPath(m_ctx);
|
||||
CGContextAddPath(m_ctx, cgpath->path());
|
||||
CGContextClip(m_ctx);
|
||||
}
|
||||
|
||||
void CGRenderer::drawImage(const RenderImage* image, BlendMode blendMode, float opacity)
|
||||
{
|
||||
auto bounds = CGRectMake(0, 0, image->width(), image->height());
|
||||
|
||||
CGContextSaveGState(m_ctx);
|
||||
CGContextSetAlpha(m_ctx, opacity);
|
||||
CGContextSetBlendMode(m_ctx, convert(blendMode));
|
||||
auto cgimg = CGRenderImage::Cast(image);
|
||||
cgimg->applyLocalMatrix(m_ctx);
|
||||
CGContextDrawImage(m_ctx, bounds, cgimg->m_image);
|
||||
CGContextRestoreGState(m_ctx);
|
||||
}
|
||||
|
||||
static Mat2D basis_matrix(Vec2D p0, Vec2D p1, Vec2D p2)
|
||||
{
|
||||
auto e0 = p1 - p0;
|
||||
auto e1 = p2 - p0;
|
||||
return Mat2D(e0.x, e0.y, e1.x, e1.y, p0.x, p0.y);
|
||||
}
|
||||
|
||||
void CGRenderer::drawImageMesh(const RenderImage* image,
|
||||
rcp<RenderBuffer> vertices,
|
||||
rcp<RenderBuffer> uvCoords,
|
||||
rcp<RenderBuffer> indices,
|
||||
uint32_t vertexCount,
|
||||
uint32_t indexCount,
|
||||
BlendMode blendMode,
|
||||
float opacity)
|
||||
{
|
||||
auto cgimage = CGRenderImage::Cast(image);
|
||||
auto const localMatrix = cgimage->localM2D();
|
||||
|
||||
const float sx = image->width();
|
||||
const float sy = image->height();
|
||||
auto const bounds = CGRectMake(0, 0, sx, sy);
|
||||
|
||||
auto scale = [sx, sy](Vec2D v) { return Vec2D{v.x * sx, v.y * sy}; };
|
||||
|
||||
auto triangles = indexCount / 3;
|
||||
auto ndx = DataRenderBuffer::Cast(indices.get())->u16s();
|
||||
auto pts = DataRenderBuffer::Cast(vertices.get())->vecs();
|
||||
auto uvs = DataRenderBuffer::Cast(uvCoords.get())->vecs();
|
||||
|
||||
// We use the path to set the clip for each triangle. Since calling
|
||||
// CGContextClip() resets the path, we only need to this once at
|
||||
// the beginning.
|
||||
CGContextBeginPath(m_ctx);
|
||||
|
||||
CGContextSaveGState(m_ctx);
|
||||
CGContextSetAlpha(m_ctx, opacity);
|
||||
CGContextSetBlendMode(m_ctx, convert(blendMode));
|
||||
CGContextSetShouldAntialias(m_ctx, false);
|
||||
|
||||
for (size_t i = 0; i < triangles; ++i)
|
||||
{
|
||||
const auto index0 = *ndx++;
|
||||
const auto index1 = *ndx++;
|
||||
const auto index2 = *ndx++;
|
||||
|
||||
CGContextSaveGState(m_ctx);
|
||||
|
||||
const auto p0 = pts[index0];
|
||||
const auto p1 = pts[index1];
|
||||
const auto p2 = pts[index2];
|
||||
CGContextMoveToPoint(m_ctx, p0.x, p0.y);
|
||||
CGContextAddLineToPoint(m_ctx, p1.x, p1.y);
|
||||
CGContextAddLineToPoint(m_ctx, p2.x, p2.y);
|
||||
CGContextClip(m_ctx);
|
||||
|
||||
const auto v0 = scale(uvs[index0]);
|
||||
const auto v1 = scale(uvs[index1]);
|
||||
const auto v2 = scale(uvs[index2]);
|
||||
auto mx =
|
||||
basis_matrix(p0, p1, p2) * basis_matrix(v0, v1, v2).invertOrIdentity() * localMatrix;
|
||||
CGContextConcatCTM(m_ctx, convert(mx));
|
||||
CGContextDrawImage(m_ctx, bounds, cgimage->m_image);
|
||||
|
||||
CGContextRestoreGState(m_ctx);
|
||||
}
|
||||
|
||||
CGContextRestoreGState(m_ctx); // restore opacity, antialias, etc.
|
||||
}
|
||||
|
||||
// Factory
|
||||
|
||||
rcp<RenderBuffer> CGFactory::makeRenderBuffer(RenderBufferType type,
|
||||
RenderBufferFlags flags,
|
||||
size_t sizeInBytes)
|
||||
{
|
||||
return make_rcp<DataRenderBuffer>(type, flags, sizeInBytes);
|
||||
}
|
||||
|
||||
rcp<RenderShader> CGFactory::makeLinearGradient(float sx,
|
||||
float sy,
|
||||
float ex,
|
||||
float ey,
|
||||
const ColorInt colors[], // [count]
|
||||
const float stops[], // [count]
|
||||
size_t count)
|
||||
{
|
||||
return rcp<RenderShader>(
|
||||
new CGLinearGradientRenderShader(sx, sy, ex, ey, colors, stops, count));
|
||||
}
|
||||
|
||||
rcp<RenderShader> CGFactory::makeRadialGradient(float cx,
|
||||
float cy,
|
||||
float radius,
|
||||
const ColorInt colors[], // [count]
|
||||
const float stops[], // [count]
|
||||
size_t count)
|
||||
{
|
||||
return rcp<RenderShader>(
|
||||
new CGRadialGradientRenderShader(cx, cy, radius, colors, stops, count));
|
||||
}
|
||||
|
||||
std::unique_ptr<RenderPath> CGFactory::makeRenderPath(RawPath& rawPath, FillRule fillRule)
|
||||
{
|
||||
return std::make_unique<CGRenderPath>(rawPath.points(), rawPath.verbs(), fillRule);
|
||||
}
|
||||
|
||||
std::unique_ptr<RenderPath> CGFactory::makeEmptyRenderPath()
|
||||
{
|
||||
return std::make_unique<CGRenderPath>();
|
||||
}
|
||||
|
||||
std::unique_ptr<RenderPaint> CGFactory::makeRenderPaint()
|
||||
{
|
||||
return std::make_unique<CGRenderPaint>();
|
||||
}
|
||||
|
||||
std::unique_ptr<RenderImage> CGFactory::decodeImage(Span<const uint8_t> encoded)
|
||||
{
|
||||
return std::make_unique<CGRenderImage>(encoded);
|
||||
}
|
||||
|
||||
#endif // APPLE
|
||||
@@ -8,7 +8,7 @@
|
||||
#ifdef RIVE_BUILD_FOR_APPLE
|
||||
|
||||
#include "cg_skia_factory.hpp"
|
||||
#include "mac_utils.hpp"
|
||||
#include "cg_renderer.hpp"
|
||||
|
||||
#if defined(RIVE_BUILD_FOR_OSX)
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
@@ -24,7 +24,7 @@ std::vector<uint8_t> CGSkiaFactory::platformDecode(Span<const uint8_t> span,
|
||||
{
|
||||
std::vector<uint8_t> pixels;
|
||||
|
||||
AutoCF image = DecodeToCGImage(span);
|
||||
AutoCF image = CGRenderer::DecodeToCGImage(span);
|
||||
if (!image)
|
||||
{
|
||||
return pixels;
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Rive
|
||||
*/
|
||||
|
||||
#include "mac_utils.hpp"
|
||||
|
||||
#ifdef RIVE_BUILD_FOR_APPLE
|
||||
|
||||
#if defined(RIVE_BUILD_FOR_IOS)
|
||||
#include <CoreGraphics/CGImage.h>
|
||||
#include <ImageIO/CGImageSource.h>
|
||||
#endif
|
||||
|
||||
AutoCF<CGImageRef> rive::FlipCGImageInY(AutoCF<CGImageRef> image)
|
||||
{
|
||||
if (!image)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto w = CGImageGetWidth(image);
|
||||
auto h = CGImageGetHeight(image);
|
||||
AutoCF space = CGColorSpaceCreateDeviceRGB();
|
||||
auto info = kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast;
|
||||
AutoCF ctx = CGBitmapContextCreate(nullptr, w, h, 8, 0, space, info);
|
||||
CGContextConcatCTM(ctx, CGAffineTransformMake(1, 0, 0, -1, 0, h));
|
||||
CGContextDrawImage(ctx, CGRectMake(0, 0, w, h), image);
|
||||
return CGBitmapContextCreateImage(ctx);
|
||||
}
|
||||
|
||||
AutoCF<CGImageRef> rive::DecodeToCGImage(rive::Span<const uint8_t> span)
|
||||
{
|
||||
AutoCF data = CFDataCreate(nullptr, span.data(), span.size());
|
||||
if (!data)
|
||||
{
|
||||
printf("CFDataCreate failed\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AutoCF source = CGImageSourceCreateWithData(data, nullptr);
|
||||
if (!source)
|
||||
{
|
||||
printf("CGImageSourceCreateWithData failed\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AutoCF image = CGImageSourceCreateImageAtIndex(source, 0, nullptr);
|
||||
if (!image)
|
||||
{
|
||||
printf("CGImageSourceCreateImageAtIndex failed\n");
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user