initial commit
This commit is contained in:
137
src/layout/layout_tree.cxx
Normal file
137
src/layout/layout_tree.cxx
Normal 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;
|
||||
}
|
65
src/layout/layout_tree.hxx
Normal file
65
src/layout/layout_tree.hxx
Normal 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
149
src/main.cxx
Normal 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
99
src/rendering.cxx
Normal 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
34
src/rendering.hxx
Normal 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
7
src/util/random.cxx
Normal 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
11
src/util/random.hxx
Normal 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
|
Reference in New Issue
Block a user