//////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2018 Leo Tenenbaum // This file is part of GraphColoring. // // GraphColoring is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // GraphColoring is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with GraphColoring. If not, see . //////////////////////////////////////////////////////////////////////////////// #define _USE_MATH_DEFINES #include #include #include #include #include FT_FREETYPE_H #include "utils/errors.hpp" #include "window.hpp" namespace gui { void GtkDrawCallback(GtkWidget* widget, cairo_t* c, gpointer data) { Window* win = (Window*) data; win->cr = c; win->Render(); } gboolean QueueDraw(gpointer w) { GtkWidget* window = (GtkWidget*) w; gtk_widget_queue_draw(window); return TRUE; } void Window::InitializeDrawingArea() { GtkWidget* drawing_area = gtk_drawing_area_new (); gtk_widget_set_size_request (drawing_area, 100, 100); g_signal_connect(G_OBJECT(drawing_area), "draw", G_CALLBACK(&GtkDrawCallback), this); g_timeout_add(1000/FPS, QueueDraw, drawing_area); gtk_container_add(GTK_CONTAINER(window), drawing_area); // Also initialize font FT_Library library; FT_Face font_face; if (FT_Init_FreeType(&library)) utils::errors::Die("Failed to initialize freetype."); if (FT_New_Face(library, "assets/fonts/DejaVuSans-ExtraLight.ttf", 0, &font_face)) utils::errors::Die("Failed to open font."); font = cairo_ft_font_face_create_for_ft_face(font_face, 0); const cairo_user_data_key_t key = {0}; auto status = cairo_font_face_set_user_data (font, &key, font_face, (cairo_destroy_func_t) FT_Done_Face); if (status) utils::errors::Die("Failed to load font."); } int Window::SetRenderCallback(callback_t callback) { render_callbacks.push_back(callback); is_render_callbacks_modified = true; return render_callbacks.size()-1; } void Window::RemoveRenderCallback(int id) { RemoveFromCallbackList(render_callbacks, id); is_render_callbacks_modified = true; } void Window::Render() { int width, height; gtk_window_get_size(GTK_WINDOW(window), &width, &height); size.SetPos(width, height); is_render_callbacks_modified = false; for (callback_t callback : render_callbacks) { if (!callback) continue; callback(this); // If a callback modifies the list of render callbacks, give up on // trying to read the whole list. if (is_render_callbacks_modified) break; } } void Window::SetDrawColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { cairo_set_source_rgba(cr, (double)r/255, (double)g/255, (double)b/255, (double)a/255); } void Window::SetDrawColor(Color color) { uint8_t r, g, b, a; colors::Unpack(color, &r, &g, &b, &a); SetDrawColor(r, g, b, a); } void Window::SetLineWidth(int line_width) { cairo_set_line_width(cr, line_width); } void Window::Clear() { DrawRectangle(0, 0, GetWidth(), GetHeight(), true); } void Window::DrawRectangle(int x, int y, int w, int h, bool filled) { cairo_rectangle(cr, x, y, w, h); if (filled) cairo_fill(cr); else cairo_stroke(cr); } void Window::DrawPoint(int x, int y) { DrawRectangle(x, y, 1, 1); } void Window::DrawLine(int x1, int y1, int x2, int y2) { cairo_move_to(cr, x1, y1); cairo_line_to(cr, x2, y2); cairo_stroke(cr); } void Window::DrawArc(int x, int y, int r, double startAngle, double endAngle, bool filled) { cairo_arc(cr, x, y, r, startAngle, endAngle); if (filled) cairo_fill(cr); else cairo_stroke(cr); } void Window::DrawCircle(int x, int y, int r, bool filled) { DrawArc(x, y, r, 0, 2*M_PI, filled); } void Window::DrawPolygon(std::vector> points, bool filled) { if (points.size() < 2) utils::errors::Die("Trying to create polygon with less than 2 points."); cairo_move_to(cr, points[0].first, points[0].second); for (unsigned i = 1; i < points.size(); i++) cairo_line_to(cr, points[i].first, points[i].second); if (filled) cairo_fill(cr); else cairo_stroke(cr); } void Window::SetTextSize(int size) { cairo_set_font_size(cr, size); } void Window::DrawText(const std::string& text, int x, int y) { cairo_set_font_face(cr, font); cairo_move_to(cr, x, y); cairo_show_text(cr, text.c_str()); cairo_stroke(cr); } void Window::DrawText(const std::string& text, Position pos, Alignment horizontal_align, Alignment vertical_align) { int w, h, x, y; GetTextSize(text, &w, &h); x = pos.X(); y = pos.Y(); switch (horizontal_align) { case Alignment::LEFT: break; case Alignment::CENTER: x -= w/2; break; case Alignment::RIGHT: x -= w; break; } switch (vertical_align) { case Alignment::TOP: y += h; break; case Alignment::CENTER: y += h/2; break; case Alignment::BOTTOM: break; } DrawText(text, x, y); } cairo_surface_t* Window::GetSurface(const std::string& filename) { cairo_surface_t* image; std::string path = "assets/images/" + filename + ".png"; if (images.count(path)) { image = images[path]; } else { image = cairo_image_surface_create_from_png(path.c_str()); if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) utils::errors::Die("Failed to load image: " + path); images[path] = image; } return image; } void Window::DrawImage(const std::string& filename, int x, int y) { cairo_surface_t* image = GetSurface(filename); cairo_set_source_surface(cr, image, x, y); cairo_paint(cr); } void Window::DrawImage(const std::string& filename, Position pos, Alignment horizontal_align, Alignment vertical_align) { int w, h; GetImageSize(filename, &w, &h); DrawImage(filename, pos.AlignedX(horizontal_align, w), pos.AlignedY(vertical_align, h)); } void Window::GetImageSize(const std::string& filename, int* w, int* h) { cairo_surface_t* image = GetSurface(filename); if (w != nullptr) *w = cairo_image_surface_get_width(image); if (h != nullptr) *h = cairo_image_surface_get_height(image); } void Window::GetTextSize(const std::string& text, int* w, int* h) { cairo_set_font_face(cr, font); cairo_text_extents_t extents; cairo_text_extents(cr, text.c_str(), &extents); if (w != nullptr) *w = extents.width; if (h != nullptr) *h = extents.height; } } // namespace gui