Add a Cairo rendering backend.
This backend will be used for reproducible offscreen rendering in integration tests.
This commit is contained in:
parent
6963c7e25b
commit
65e2cccde0
|
@ -160,6 +160,7 @@ set(solvespace_SOURCES
|
|||
render/render.cpp
|
||||
render/rendergl1.cpp
|
||||
render/render2d.cpp
|
||||
render/rendercairo.cpp
|
||||
srf/boolean.cpp
|
||||
srf/curve.cpp
|
||||
srf/merge.cpp
|
||||
|
|
|
@ -491,7 +491,7 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
|
|||
|
||||
Canvas::Fill fill = {};
|
||||
fill.layer = stroke.layer;
|
||||
fill.zIndex = IsPoint() ? zIndex + 1 : 0;
|
||||
fill.zIndex = IsPoint() ? 5 : 0;
|
||||
fill.color = stroke.color;
|
||||
Canvas::hFill hcf = canvas->GetFill(fill);
|
||||
|
||||
|
|
|
@ -270,6 +270,33 @@ public:
|
|||
void OutputBezierAsNonrationalCubic(const SBezier &b, hStroke hcs);
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 2d renderers.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class CairoRenderer : public SurfaceRenderer {
|
||||
public:
|
||||
cairo_t *context;
|
||||
// Renderer state.
|
||||
struct {
|
||||
hStroke hcs;
|
||||
} current;
|
||||
|
||||
CairoRenderer() : context(), current() {}
|
||||
|
||||
void SelectStroke(hStroke hcs);
|
||||
void MoveTo(Vector p);
|
||||
void FinishPath();
|
||||
|
||||
bool CanOutputCurves() const override { return true; }
|
||||
bool CanOutputTriangles() const override { return true; }
|
||||
|
||||
void OutputStart() override;
|
||||
void OutputBezier(const SBezier &b, hStroke hcs) override;
|
||||
void OutputTriangle(const STriangle &tr) override;
|
||||
void OutputEnd() override;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 3d renderers.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
111
src/render/rendercairo.cpp
Normal file
111
src/render/rendercairo.cpp
Normal file
|
@ -0,0 +1,111 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// A rendering backend that draws on a Cairo surface.
|
||||
//
|
||||
// Copyright 2016 whitequark
|
||||
//-----------------------------------------------------------------------------
|
||||
#include <cairo.h>
|
||||
#include "solvespace.h"
|
||||
|
||||
namespace SolveSpace {
|
||||
|
||||
void CairoRenderer::OutputStart() {
|
||||
cairo_save(context);
|
||||
|
||||
RgbaColor bgColor = lighting.backgroundColor;
|
||||
cairo_rectangle(context, 0.0, 0.0, camera.width, camera.height);
|
||||
cairo_set_source_rgba(context, bgColor.redF(), bgColor.greenF(), bgColor.blueF(),
|
||||
bgColor.alphaF());
|
||||
cairo_fill(context);
|
||||
|
||||
cairo_translate(context, camera.width / 2.0, camera.height / 2.0);
|
||||
|
||||
cairo_set_line_join(context, CAIRO_LINE_JOIN_ROUND);
|
||||
cairo_set_line_cap(context, CAIRO_LINE_CAP_ROUND);
|
||||
}
|
||||
|
||||
void CairoRenderer::OutputEnd() {
|
||||
FinishPath();
|
||||
|
||||
cairo_restore(context);
|
||||
cairo_surface_flush(cairo_get_target(context));
|
||||
}
|
||||
|
||||
void CairoRenderer::SelectStroke(hStroke hcs) {
|
||||
if(current.hcs.v == hcs.v) return;
|
||||
FinishPath();
|
||||
|
||||
Stroke *stroke = strokes.FindById(hcs);
|
||||
current.hcs = hcs;
|
||||
|
||||
RgbaColor color = stroke->color;
|
||||
std::vector<double> dashes =
|
||||
StipplePatternDashes(stroke->stipplePattern, stroke->stippleScale * camera.scale);
|
||||
cairo_set_line_width(context, stroke->width);
|
||||
cairo_set_dash(context, dashes.data(), dashes.size(), 0);
|
||||
cairo_set_source_rgba(context, color.redF(), color.greenF(), color.blueF(),
|
||||
color.alphaF());
|
||||
cairo_set_antialias(context, CAIRO_ANTIALIAS_BEST);
|
||||
}
|
||||
|
||||
void CairoRenderer::MoveTo(Vector p) {
|
||||
Point2d pos;
|
||||
cairo_get_current_point(context, &pos.x, &pos.y);
|
||||
if(cairo_has_current_point(context) && pos.Equals(p.ProjectXy())) return;
|
||||
FinishPath();
|
||||
|
||||
cairo_move_to(context, p.x, p.y);
|
||||
}
|
||||
|
||||
void CairoRenderer::FinishPath() {
|
||||
if(!cairo_has_current_point(context)) return;
|
||||
|
||||
cairo_stroke(context);
|
||||
}
|
||||
|
||||
void CairoRenderer::OutputBezier(const SBezier &b, hStroke hcs) {
|
||||
SelectStroke(hcs);
|
||||
|
||||
Vector c, n = Vector::From(0, 0, 1);
|
||||
double r;
|
||||
if(b.deg == 1) {
|
||||
MoveTo(b.ctrl[0]);
|
||||
cairo_line_to(context,
|
||||
b.ctrl[1].x, b.ctrl[1].y);
|
||||
} else if(b.IsCircle(n, &c, &r)) {
|
||||
MoveTo(b.ctrl[0]);
|
||||
double theta0 = atan2(b.ctrl[0].y - c.y, b.ctrl[0].x - c.x),
|
||||
theta1 = atan2(b.ctrl[2].y - c.y, b.ctrl[2].x - c.x),
|
||||
dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI);
|
||||
if(dtheta > 0) {
|
||||
cairo_arc(context,
|
||||
c.x, c.y, r, theta0, theta1);
|
||||
} else {
|
||||
cairo_arc_negative(context,
|
||||
c.x, c.y, r, theta0, theta1);
|
||||
}
|
||||
} else if(b.deg == 3 && !b.IsRational()) {
|
||||
MoveTo(b.ctrl[0]);
|
||||
cairo_curve_to(context,
|
||||
b.ctrl[1].x, b.ctrl[1].y,
|
||||
b.ctrl[2].x, b.ctrl[2].y,
|
||||
b.ctrl[3].x, b.ctrl[3].y);
|
||||
} else {
|
||||
OutputBezierAsNonrationalCubic(b, hcs);
|
||||
}
|
||||
}
|
||||
|
||||
void CairoRenderer::OutputTriangle(const STriangle &tr) {
|
||||
FinishPath();
|
||||
current.hcs = {};
|
||||
|
||||
RgbaColor color = tr.meta.color;
|
||||
cairo_set_source_rgba(context, color.redF(), color.greenF(), color.blueF(),
|
||||
color.alphaF());
|
||||
cairo_set_antialias(context, CAIRO_ANTIALIAS_NONE);
|
||||
cairo_move_to(context, tr.a.x, tr.a.y);
|
||||
cairo_line_to(context, tr.b.x, tr.b.y);
|
||||
cairo_line_to(context, tr.c.x, tr.c.y);
|
||||
cairo_fill(context);
|
||||
}
|
||||
|
||||
}
|
|
@ -35,6 +35,8 @@
|
|||
struct FT_LibraryRec_;
|
||||
struct FT_FaceRec_;
|
||||
|
||||
typedef struct _cairo cairo_t;
|
||||
|
||||
// The few floating-point equality comparisons in SolveSpace have been
|
||||
// carefully considered, so we disable the -Wfloat-equal warning for them
|
||||
#ifdef __clang__
|
||||
|
|
Loading…
Reference in New Issue
Block a user