initial commit

This commit is contained in:
2024-10-12 18:54:19 +01:00
commit 030e8d51af
43 changed files with 4614 additions and 0 deletions

137
src/layout/layout_tree.cxx Normal file
View File

@@ -0,0 +1,137 @@
#include "layout_tree.hxx"
#include "include/core/SkFont.h"
#include "include/core/SkFontMgr.h"
#include "include/core/SkFontTypes.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkTypeface.h"
#include "rapidxml.hpp"
#include <cstdint>
#include <cstring>
#include <iostream>
#include <limits>
#include <random>
#include <stdexcept>
static std::uniform_int_distribution<std::mt19937::result_type>
random_id(0, std::numeric_limits<std::mt19937::result_type>::max());
static std::random_device dev;
static std::mt19937 rng(dev());
uint64_t __generate_random_node_id() { return random_id(rng); };
Clef::ContainerNode::ContainerNode() {
prev_sibiling = nullptr;
next_sibiling = nullptr;
parent = nullptr;
type = Clef::LayoutTree::NodeType::Container;
}
Clef::LayoutBound Clef::ContainerNode::measure() {
float max_child_width = -1;
float total_height = 0;
for (const auto &child : children) {
const auto bound = child->measure();
std::cout << "bound width" << bound.width << " height " << bound.height
<< std::endl;
if (bound.width > max_child_width) {
max_child_width = bound.width;
}
total_height += bound.height;
}
return {0, 0, max_child_width, total_height};
}
Clef::TextNode::TextNode(const char *content, SkFont font)
: content(content), font(font) {
prev_sibiling = nullptr;
next_sibiling = nullptr;
parent = nullptr;
type = Clef::LayoutTree::NodeType::Text;
}
Clef::LayoutBound Clef::TextNode::measure() {
SkRect rect;
font.measureText(content.c_str(), content.size(), SkTextEncoding::kUTF8,
&rect);
return {0, 0, rect.width(), rect.height()};
}
Clef::LayoutTree::LayoutTree(Clef::LayoutTree::Node *root) : root(root) {}
Clef::LayoutTree::Node *
Clef::LayoutTree::Node::from_xml_node(rapidxml::xml_node<char> *node,
const SkFontMgr &font_mgr) {
std::cout << "encountered node:" << node->name() << std::endl;
if (std::strncmp("box", node->name(), 3) == 0) {
auto container_node = new ContainerNode();
container_node->id = __generate_random_node_id();
std::cout << "assigned id " << container_node->id << std::endl;
auto current_node = node->first_node();
Node *last_node;
while (current_node) {
auto n = Node::from_xml_node(current_node, font_mgr);
if (!n) {
return nullptr;
}
if (last_node) {
n->prev_sibiling = last_node;
last_node->next_sibiling = n;
std::cout << n << std::endl;
std::cout << last_node << std::endl;
}
n->parent = container_node;
container_node->children.emplace_back(n);
last_node = n;
current_node = current_node->next_sibling();
}
return container_node;
}
if (std::strncmp("text", node->name(), 4) == 0) {
const auto content = node->value();
if (!content) {
return nullptr;
}
auto typeface =
font_mgr.matchFamilyStyle("Inter", SkFontStyle::Normal());
SkFont f(typeface, 20);
auto n = new TextNode(content, f);
n->id = __generate_random_node_id();
std::cout << "assigned id " << n->id << std::endl;
return n;
}
return nullptr;
}
Clef::LayoutTree Clef::LayoutTree::from_xml(const rapidxml::xml_document<> &doc,
const SkFontMgr &font_mgr) {
Clef::LayoutTree::Node *root;
auto xml_root = doc.first_node("box");
if (!xml_root) {
throw std::runtime_error(
"cml layout must start with a container node.");
}
root = Node::from_xml_node(xml_root, font_mgr);
if (!root) {
throw std::runtime_error("failed to parse cml");
}
std::cout << "cml parsed successfully" << std::endl;
return root;
}

View File

@@ -0,0 +1,65 @@
#ifndef __CLEF_LAYOUT_LAYOUT_TREE_HXX__
#define __CLEF_LAYOUT_LAYOUT_TREE_HXX__
#include "include/core/SkFont.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkTypeface.h"
#include "rapidxml.hpp"
#include <cstdint>
#include <string>
#include <vector>
namespace Clef {
struct LayoutBound {
float x;
float y;
float width;
float height;
};
struct LayoutTree {
enum class NodeType { Text, Container };
struct Node {
uint64_t id;
Node *parent;
Node *prev_sibiling;
Node *next_sibiling;
NodeType type;
static Node *from_xml_node(rapidxml::xml_node<char> *node,
const SkFontMgr &font_mgr);
virtual Clef::LayoutBound measure() = 0;
};
Node *root;
static LayoutTree from_xml(const rapidxml::xml_document<> &doc,
const SkFontMgr &font_mgr);
private:
LayoutTree(Node *root);
};
struct ContainerNode : LayoutTree::Node {
std::vector<Node *> children;
Clef::LayoutBound measure() override;
ContainerNode();
};
struct TextNode : LayoutTree::Node {
std::string content;
SkFont font;
Clef::LayoutBound measure() override;
TextNode(const char *content, SkFont font);
};
} // namespace Clef
#endif

149
src/main.cxx Normal file
View File

@@ -0,0 +1,149 @@
#include "GL/gl.h"
#include "GLFW/glfw3.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkColorType.h"
#include "include/core/SkFontStyle.h"
#include "include/core/SkPaint.h"
#include "include/core/SkTypeface.h"
#include "include/gpu/ganesh/GrDirectContext.h"
#include "include/gpu/ganesh/GrTypes.h"
#include "include/gpu/ganesh/gl/GrGLTypes.h"
#include "layout/layout_tree.hxx"
#include "rapidxml.hpp"
#include "rendering.hxx"
#include <GLFW/glfw3.h>
#include <cstdlib>
#include <fstream>
#include <include/core/SkColorSpace.h>
#include <include/core/SkFont.h>
#include <include/core/SkFontMgr.h>
#include <include/core/SkStream.h>
#include <include/gpu/ganesh/GrBackendSurface.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <include/gpu/ganesh/gl/GrGLAssembleInterface.h>
#include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
#include <include/gpu/ganesh/gl/GrGLInterface.h>
#include <include/ports/SkFontMgr_directory.h>
#include <include/utils/SkCustomTypeface.h>
#include <iostream>
#include <rapidxml_utils.hpp>
void framebuffer_size_callback(GLFWwindow *window, int width, int height) {
GLFWmonitor *mon = glfwGetPrimaryMonitor();
float xscale, yscale;
glfwGetMonitorContentScale(mon, &xscale, &yscale);
glViewport(0, 0, width, height);
}
int main(int argc, char *argv[]) {
if (argc <= 0) {
std::cerr << "expected a markup file" << std::endl;
exit(EXIT_FAILURE);
}
GLFWwindow *window;
if (!glfwInit()) {
std::cout << "failed to initialize glfw" << std::endl;
return -1;
}
window = glfwCreateWindow(200, 200, "Clef", nullptr, nullptr);
if (!window) {
std::cout << "window cannot be created" << std::endl;
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE);
glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwMakeContextCurrent(window);
auto interface = GrGLMakeNativeInterface();
if (!interface) {
interface = GrGLMakeAssembledInterface(
nullptr, (GrGLGetProc) * [](void *, const char *p) -> void * {
return (void *)glfwGetProcAddress(p);
});
if (!interface) {
std::cout << "failed to make native interface" << std::endl;
exit(EXIT_FAILURE);
}
}
auto context = GrDirectContexts::MakeGL(interface);
if (!context) {
std::cout << "failed to make DirectContext" << std::endl;
exit(EXIT_FAILURE);
}
std::cout << "gl direct context created" << std::endl;
GrGLFramebufferInfo framebufferInfo;
framebufferInfo.fFBOID = 0;
framebufferInfo.fFormat = GL_RGBA8;
std::cout << "frame buffer info created" << std::endl;
SkColorType colorType = kRGBA_8888_SkColorType;
auto target =
GrBackendRenderTargets::MakeGL(400, 400, 0, 0, framebufferInfo);
auto skSurface = SkSurfaces::WrapBackendRenderTarget(
context.get(), target, kBottomLeft_GrSurfaceOrigin, colorType, nullptr,
nullptr);
if (!skSurface) {
std::cout << "failed to create skia surface" << std::endl;
exit(EXIT_FAILURE);
}
std::cout << "skia surface created" << std::endl;
SkCanvas *canvas = skSurface->getCanvas();
if (!canvas) {
std::cout << "failed to obtain skia canvas" << std::endl;
exit(EXIT_FAILURE);
}
glfwSwapInterval(1);
std::cout << "canvas obtained. entering run loop..." << std::endl;
auto fontMgr = SkFontMgr_New_Custom_Directory("../resources/font/Inter");
std::cout << "fonts found: " << fontMgr->countFamilies() << std::endl;
auto interRegular =
fontMgr->matchFamilyStyle("Inter", SkFontStyle::Normal());
if (!interRegular) {
std::cout << "WARNING: inter regular not found" << std::endl;
}
rapidxml::file<> input_file(argv[1]);
rapidxml::xml_document<> doc;
doc.parse<rapidxml::parse_default>(input_file.data());
Clef::RenderingContext ctx{
.canvas = canvas,
.font_mgr = fontMgr,
};
Clef::LayoutTree tree = Clef::LayoutTree::from_xml(doc, *fontMgr.get());
while (!glfwWindowShouldClose(window)) {
Clef::render_tree(ctx, tree);
context->flush();
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}

99
src/rendering.cxx Normal file
View File

@@ -0,0 +1,99 @@
#include "rendering.hxx"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkFont.h"
#include "include/core/SkPaint.h"
#include "include/core/SkRect.h"
#include "include/core/SkSurfaceProps.h"
#include "layout/layout_tree.hxx"
#include <cstdint>
#include <iostream>
#include <limits>
#include <utility>
inline const auto NODE_TYPE_BOX = "box";
inline const auto NODE_TYPE_TEXT = "text";
Clef::RenderingContext new_rendering_context(SkCanvas *canvas,
sk_sp<SkFontMgr> font_mgr) {
return {
.canvas = canvas,
.layout_map{},
.font_mgr = font_mgr,
.random_id{0, std::numeric_limits<uint64_t>::max()},
};
}
Clef::LayoutBound __calculate_node_position(Clef::RenderingContext &ctx,
Clef::LayoutTree::Node *node) {
auto bounds = node->measure();
if (!node->parent) {
return {0, 0, bounds.width, bounds.height};
}
if (node->prev_sibiling != nullptr) {
const auto entry = ctx.layout_map.find(node->prev_sibiling->id);
if (entry == ctx.layout_map.end()) {
return {0, 0, bounds.width, bounds.height};
}
const auto prev_bound = entry->second;
return {prev_bound.x, prev_bound.y + prev_bound.height, bounds.width,
bounds.height};
}
const auto entry = ctx.layout_map.find(node->parent->id);
if (entry == ctx.layout_map.end()) {
return {0, 0, bounds.width, bounds.height};
}
const auto parent_bound = entry->second;
return {parent_bound.x, parent_bound.y, bounds.width, bounds.height};
}
void clef_render_node(Clef::RenderingContext &ctx,
Clef::LayoutTree::Node *node) {
switch (node->type) {
case Clef::LayoutTree::NodeType::Text: {
const auto pos = __calculate_node_position(ctx, node);
auto tn = static_cast<Clef::TextNode *>(node);
SkPaint paint;
paint.setColor(SK_ColorBLACK);
std::cout << "rendering text: " << tn->content << " at x = " << pos.x
<< " y = " << pos.y << std::endl;
ctx.canvas->drawString(tn->content.c_str(), pos.x, pos.y + pos.height,
tn->font, paint);
ctx.layout_map.insert({node->id, pos});
break;
}
case Clef::LayoutTree::NodeType::Container: {
auto cn = static_cast<Clef::ContainerNode *>(node);
const auto pos = __calculate_node_position(ctx, node);
SkRect rect{pos.x, pos.y, pos.x + pos.width, pos.y + pos.height};
SkPaint paint;
paint.setColor(SK_ColorCYAN);
ctx.canvas->drawRect(rect, paint);
ctx.layout_map.insert({node->id, pos});
for (const auto &child : cn->children) {
clef_render_node(ctx, child);
}
break;
}
}
}
void Clef::render_tree(Clef::RenderingContext &ctx, const LayoutTree &tree) {
SkPaint paint;
paint.setColor(SK_ColorWHITE);
ctx.canvas->drawPaint(paint);
paint.setColor(SK_ColorBLACK);
auto tf = ctx.font_mgr->matchFamilyStyle("Inter", SkFontStyle::Normal());
SkFont f(tf, 20);
ctx.canvas->drawString("hello", 0, 10, f, paint);
clef_render_node(ctx, tree.root);
}

34
src/rendering.hxx Normal file
View File

@@ -0,0 +1,34 @@
#ifndef __CLEF__RENDERING_HXX__
#define __CLEF__RENDERING_HXX__
#include "include/core/SkCanvas.h"
#include "include/core/SkFontMgr.h"
#include "include/core/SkFontStyle.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkScalar.h"
#include "layout/layout_tree.hxx"
#include "rapidxml.hpp"
#include <cstdint>
#include <random>
#include <unordered_map>
namespace Clef {
struct RenderingContext {
SkCanvas *canvas;
std::unordered_map<uint64_t, LayoutBound> layout_map;
sk_sp<SkFontMgr> font_mgr;
std::uniform_int_distribution<std::mt19937::result_type> random_id;
};
RenderingContext new_rendering_context(SkCanvas *canvas,
sk_sp<SkFontMgr> font_mgr);
void render_document(RenderingContext &ctx,
const rapidxml::xml_document<> &doc);
void render_tree(RenderingContext &ctx, const LayoutTree &tree);
}; // namespace Clef
#endif

7
src/util/random.cxx Normal file
View File

@@ -0,0 +1,7 @@
#include "random.hxx"
#include <random>
static std::random_device dev;
static std::mt19937 rng(dev());
std::mt19937 Clef::get_rng() { return rng; }

11
src/util/random.hxx Normal file
View File

@@ -0,0 +1,11 @@
#ifndef __CLEF_UTIL_RANDOM_HXX__
#define __CLEF_UTIL_RANDOM_HXX__
#include <random>
namespace Clef {
std::mt19937 get_rng();
} // namespace Clef
#endif