Skip to content

Commit

Permalink
widget: updated cairo widget
Browse files Browse the repository at this point in the history
  • Loading branch information
rxdu committed Oct 26, 2024
1 parent d3fd227 commit 8ebdd4a
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 81 deletions.
4 changes: 4 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# To-Do List

## General

* Check memory leaks

## CairoCanvas

* Add functions to draw common geometry primitives
Expand Down
2 changes: 1 addition & 1 deletion src/imview/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ add_library(imview
# widgets
src/widget/cv_image_widget.cpp
src/widget/buffered_cv_image_widget.cpp
src/widget/cairo_widget.cpp
src/widget/cairo/cairo_context.cpp
src/widget/cairo/cairo_widget.cpp
src/widget/cairo/cairo_draw.cpp
# data buffer
src/buffer/buffer_registry.cpp
Expand Down
1 change: 1 addition & 0 deletions src/imview/include/imview/fonts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ enum class FontSize {
class Fonts {
public:
static void LoadFonts();
static void UnloadFonts();
static ImFont *GetFont(FontSize size);
};
} // namespace quickviz
Expand Down
9 changes: 5 additions & 4 deletions src/imview/include/imview/widget/cairo/cairo_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ class CairoContext {
public:
// default constructor: empty cairo surface
CairoContext() = default;
// normalize coordinate so that drawing area will be x,y in [0, 1]
CairoContext(uint32_t width, uint32_t height,
bool normalize_coordinate = false);
// unify coordinate so that drawing area will be defined as:
// x (width): [0, 1.0 * aspect_ratio]
// y (height): [0, 1.0]
CairoContext(uint32_t width, uint32_t height, bool unify_coordinate = false);

~CairoContext();

Expand Down Expand Up @@ -61,7 +62,7 @@ class CairoContext {

uint32_t width_;
uint32_t height_;
bool normalize_coordinate_;
bool unified_coordinate_;
std::stack<Scaler> scaler_stack_;

cairo_surface_t* surface_ = nullptr;
Expand Down
22 changes: 10 additions & 12 deletions src/imview/include/imview/widget/cairo_widget.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,49 +14,47 @@
#include <string>
#include <cstdint>
#include <memory>
#include <mutex>
#include <functional>
#include <unordered_map>

#include "imgui.h"

#include "imview/panel.hpp"
#include "imview/widget/cairo/cairo_context.hpp"
#include "imview/widget/cairo/cairo_draw.hpp"

namespace quickviz {
class CairoWidget : public Panel {
public:
CairoWidget(const std::string& widget_name, uint32_t width, uint32_t height,
CairoWidget(const std::string& widget_name,
bool normalize_coordinate = false);
~CairoWidget() override;
~CairoWidget() override = default;

void Draw() override;
void OnResize(float width, float height) override;

float GetAspectRatio() const;
// draw vector graphics with user function
using CairoDrawFunc = std::function<void(cairo_t*, float aspect_ratio)>;
void AttachDrawFunction(CairoDrawFunc DrawFunc);

// resize/fill cairo surface
void Resize(uint32_t width, uint32_t height);
private:
void Fill(ImVec4 color = {1, 1, 1, 0.6});
void Clear();

// draw vector graphics with user function
using CairoDrawFunc = std::function<void(cairo_t*)>;
void Draw(CairoDrawFunc DrawFunc);

// draw text to cairo surface
void DrawText(std::string text, double pos_x, double pos_y,
double angle = 0.0, ImVec4 color = {0, 0, 0, 1},
double size = 14.0, double line_width = 3.0,
const char* font = "Sans");

// render cairo content to OpenGL context to display with ImGUI
void Render(const ImVec2& uv0 = ImVec2(0, 0),
const ImVec2& uv1 = ImVec2(1, 1),
const ImVec4& tint_col = ImVec4(1, 1, 1, 1),
const ImVec4& border_col = ImVec4(0, 0, 0, 0));

private:
std::unique_ptr<CairoContext> ctx_;
std::mutex draw_func_mutex_;
CairoDrawFunc draw_func_;
};
} // namespace quickviz

Expand Down
13 changes: 13 additions & 0 deletions src/imview/src/fonts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

#include "imview/fonts.hpp"

#include <fontconfig/fontconfig.h>

#include <unordered_map>

#include "fonts/opensans_regular.hpp"
Expand Down Expand Up @@ -36,6 +38,17 @@ void Fonts::LoadFonts() {
OpenSansRegular_compressed_data, OpenSansRegular_compressed_size, 40.f);
}

void Fonts::UnloadFonts() {
ImGuiIO &io = ImGui::GetIO();
io.Fonts->ClearFonts();

// Reference:
// [1]
// https://stackoverflow.com/questions/51174295/cairo-show-text-memory-leak
// [2] https://gitlab.freedesktop.org/cairo/cairo/-/issues/393
FcFini();
}

ImFont *Fonts::GetFont(FontSize size) {
if (custom_fonts_.find(size) != custom_fonts_.end()) {
return custom_fonts_[size];
Expand Down
1 change: 1 addition & 0 deletions src/imview/src/viewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ Viewer::Viewer(std::string title, uint32_t width, uint32_t height,
}

Viewer::~Viewer() {
Fonts::UnloadFonts();
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImPlot::DestroyContext();
Expand Down
9 changes: 3 additions & 6 deletions src/imview/src/widget/cairo/cairo_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@

namespace quickviz {
CairoContext::CairoContext(uint32_t width, uint32_t height,
bool normalize_coordinate)
: width_(width),
height_(height),
normalize_coordinate_(normalize_coordinate) {
bool unify_coordinate)
: width_(width), height_(height), unified_coordinate_(unify_coordinate) {
// create cairo context
CreateSurface();
GenGlTexture();
Expand Down Expand Up @@ -58,8 +56,7 @@ void CairoContext::CreateSurface() {
"[ERROR] create_cairo_context() - Couldn't create context\n");
}

if (normalize_coordinate_) {
// auto min = std::min(width_, height_);
if (unified_coordinate_) {
cairo_scale(cr_, height_, height_);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,21 @@
#include <fontconfig/fontconfig.h>

namespace quickviz {
CairoWidget::CairoWidget(const std::string& widget_name, uint32_t width,
uint32_t height, bool normalize_coordinate)
: Panel(widget_name),
ctx_(new CairoContext(width, height, normalize_coordinate)) {}

CairoWidget::~CairoWidget() {
// font-related memory cleanup
// Reference:
// [1]
// https://stackoverflow.com/questions/51174295/cairo-show-text-memory-leak
// [2] https://gitlab.freedesktop.org/cairo/cairo/-/issues/393
cairo_debug_reset_static_data();
FcFini();
CairoWidget::CairoWidget(const std::string& widget_name,
bool normalize_coordinate)
: Panel(widget_name), ctx_(new CairoContext(0, 0, normalize_coordinate)) {}

void CairoWidget::Draw() {
Begin();
Render();
End();
}

void CairoWidget::Draw() {}

void CairoWidget::OnResize(float width, float height) {
Panel::OnResize(width, height);
ctx_->Resize(width, height);
}

float CairoWidget::GetAspectRatio() const { return ctx_->GetAspectRatio(); }

void CairoWidget::Fill(ImVec4 color) {
auto cr = ctx_->GetCairoObject();
cairo_set_source_rgba(cr, color.x, color.y, color.z, color.w);
Expand All @@ -52,15 +44,9 @@ void CairoWidget::Clear() {
cairo_paint(cr);
}

void CairoWidget::Draw(CairoDrawFunc DrawFunc) {
assert(ctx_ != nullptr && ctx_->GetCairoObject() != NULL);

// do actual paint with cairo
DrawFunc(ctx_->GetCairoObject());
}

void CairoWidget::Resize(uint32_t width, uint32_t height) {
ctx_->Resize(width, height);
void CairoWidget::AttachDrawFunction(CairoDrawFunc DrawFunc) {
std::lock_guard<std::mutex> lock(draw_func_mutex_);
draw_func_ = DrawFunc;
}

void CairoWidget::DrawText(std::string text, double pos_x, double pos_y,
Expand Down Expand Up @@ -88,6 +74,24 @@ void CairoWidget::DrawText(std::string text, double pos_x, double pos_y,

void CairoWidget::Render(const ImVec2& uv0, const ImVec2& uv1,
const ImVec4& tint_col, const ImVec4& border_col) {
assert(ctx_ != nullptr && ctx_->GetCairoObject() != NULL);

// check if window size has changed
if (width_ == 0 || height_ == 0) return;
auto current_size = ImGui::GetWindowSize();
if (current_size.x != width_ || current_size.y != height_) {
OnResize(current_size.x, current_size.y);
}

// draw user-defined graphics
CairoDrawFunc draw_func;
{
std::lock_guard<std::mutex> lock(draw_func_mutex_);
draw_func = draw_func_;
}
draw_func(ctx_->GetCairoObject(), ctx_->GetAspectRatio());

// render cairo content to OpenGL texture
GLuint image = ctx_->RenderToGlTexture();
ImGui::Image((void*)(intptr_t)image, ImGui::GetContentRegionAvail(), uv0, uv1,
tint_col, border_col);
Expand Down
86 changes: 54 additions & 32 deletions src/imview/test/feature/test_cairo_widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,54 +13,76 @@
#include <opencv2/opencv.hpp>

#include "imview/viewer.hpp"
#include "imview/widget/cv_image_widget.hpp"
#include "imview/widget/cairo_widget.hpp"

#include "scene_objects/gl_triangle_scene_object.hpp"

using namespace quickviz;

bool keep_running = true;
std::shared_ptr<CvImageWidget> image_widget;
void PaintUnifiedCoordinate(cairo_t* cr, float aspect_ratio) {
float pos_x = 0.5 * aspect_ratio;
float pos_y = 0.5;

void CaptureVideo() {
cv::VideoCapture cap(0); // Open the default camera
if (!cap.isOpened()) {
std::cerr << "Error: Could not open video capture device." << std::endl;
return;
}
DrawPoint(cr, {pos_x, pos_y}, 0.01, {1, 0.2, 0.2, 0.6});
DrawCircle(cr, {pos_x, pos_y}, 0.3, 0.002, {1, 0.2, 0.2, 0.6});
DrawRectangle(cr, {pos_x - 0.2f, pos_y - 0.2f}, {pos_x + 0.2f, pos_y + 0.2f},
0.002);
}

void Paint(cairo_t* cr, float aspect_ratio) {
DrawPoint(cr, {860, 200});
DrawPoint(cr, {1060, 200}, 10, {1, 0.2, 0.2, 0.6});

DrawLine(cr, {860, 150}, {1060, 150});
DrawLine(cr, {860, 250}, {1060, 250}, 2, {1, 0.2, 0.2, 0.6});

DrawCircle(cr, {960, 540}, 30);
DrawCircle(cr, {960, 540}, 50, 5, {1, 0.2, 0.2, 0.6});

DrawRing(cr, {960, 540}, 100, 130, 0, M_PI / 4.0);
DrawRing(cr, {960, 540}, 140, 180, 0, M_PI / 4.0, 5, colors[GREEN]);

while (keep_running) {
cv::Mat frame;
cap >> frame; // Capture a new frame
if (frame.empty()) {
break; // End of video stream
}
DrawRing(cr, {960, 540}, 100, 200, M_PI, M_PI * 1.5f, 5, colors[YELLOW],
true);
DrawRing(cr, {960, 540}, 140, 220, M_PI / 2.0, 2 * M_PI / 3.0, 5,
colors[GREEN], true);
DrawRing(cr, {960, 540}, 140, 220, M_PI / 2.0, 5 * M_PI / 6.0, 5,
colors[PURPLE], false);

if (image_widget != nullptr) image_widget->UpdateImage(frame);
std::this_thread::sleep_for(
std::chrono::milliseconds(30)); // Simulate frame rate
DrawArc(cr, {600, 800}, 60, M_PI, 2 * M_PI / 4.0);
DrawArc(cr, {600, 800}, 70, M_PI / 4.0, M_PI, 5, {1, 0.2, 0.2, 0.6});

DrawArcSector(cr, {1060, 800}, 60, 0, -M_PI / 4.0);
DrawArcSector(cr, {1060, 800}, 80, M_PI / 4.0, M_PI, 5, {1, 0.2, 0.2, 0.6});
DrawArcSector(cr, {1060, 800}, 85, 5.0 * M_PI / 4.0, 6.0 * M_PI / 4.0, 5,
{1, 0.2, 0.2, 0.3}, true);

DrawRectangle(cr, {400, 400}, {500, 600});
DrawRectangle(cr, {550, 400}, {600, 600}, 5, colors[MAGENTA]);
DrawRectangle(cr, {650, 400}, {700, 600}, 5, colors[CYAN], true);

for (int i = 0; i < COLOR_LAST; ++i) {
DrawLine(cr, {200.0f, 300.0f + 20 * i}, {250.0f, 300.0f + 20 * i}, 10,
colors[i]);
}
}

int main(int argc, char* argv[]) {
// set up video capture thread --> producer
std::thread capture_thread(CaptureVideo);

// set up viewer --> consumer
Viewer viewer;
auto gl_triangle = std::make_shared<GLTriangleSceneObject>();
viewer.AddSceneObject(gl_triangle);
// auto gl_triangle = std::make_shared<GLTriangleSceneObject>();
// viewer.AddSceneObject(gl_triangle);

image_widget = std::make_shared<CvImageWidget>("camera");
image_widget->OnResize(300, 200);
image_widget->SetPosition(0, 0);
viewer.AddSceneObject(image_widget);
auto cairo_widget = std::make_shared<CairoWidget>("cairo_unified", true);
cairo_widget->OnResize(300, 200);
cairo_widget->AttachDrawFunction(PaintUnifiedCoordinate);
viewer.AddSceneObject(cairo_widget);

viewer.Show();
auto cairo_widget2 = std::make_shared<CairoWidget>("cairo");
cairo_widget2->OnResize(300, 200);
cairo_widget2->AttachDrawFunction(Paint);
viewer.AddSceneObject(cairo_widget2);

// clean up
keep_running = false;
capture_thread.join();
viewer.Show();

return 0;
}

0 comments on commit 8ebdd4a

Please sign in to comment.