diff --git a/CMakeLists.txt b/CMakeLists.txt index c5a1f39..6463723 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,8 @@ project(TheBartender VERSION 0.3) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) # Create an executable -add_executable(${PROJECT_NAME} src/Base/Entry.cpp src/Commands.cpp) +add_executable(${PROJECT_NAME} src/Base/Entry.cpp src/Commands.cpp + src/Utility/Utility.cpp) # Find our pre-installed DPP package (using FindDPP.cmake). find_package(DPP REQUIRED) diff --git a/assets/report_bumper.png b/assets/report_bumper.png new file mode 100644 index 0000000..50fd430 Binary files /dev/null and b/assets/report_bumper.png differ diff --git a/src/Commands/GenerativeCommands.cpp b/src/Commands/GenerativeCommands.cpp index 6229e9c..b59c703 100644 --- a/src/Commands/GenerativeCommands.cpp +++ b/src/Commands/GenerativeCommands.cpp @@ -1,4 +1,5 @@ #include "../Common.hpp" +#include "../Utility/CairoTools.hpp" #include #include @@ -9,24 +10,10 @@ #include #include -#include - #include #include #include -void pngToCairoSurface(std::string data) { - cairo_surface_t *image = - cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 300, 200); -} -void jpegToCairoSurface(std::string data) {} -void webpToCairoSurface(std::string data) {} - -std::unordered_map> - supportedImageFileTypes{{"image/png", pngToCairoSurface}, - {"image/jpeg", jpegToCairoSurface}, - {"image/webp", webpToCairoSurface}}; - void commandGenerateReport(const dpp::slashcommand_t &event, dpp::cluster &bot) { event.thinking(); @@ -38,7 +25,14 @@ void commandGenerateReport(const dpp::slashcommand_t &event, bot.request( file.url, dpp::http_method::m_get, [event, &bot, fileType](const dpp::http_request_completion_t &result) { - fileType->second(result.body); + std::string responseData = GenerateReportImage( + fileType->second( + result.body), // Image itself processed for cario to handle + std::get(event.get_parameter("headline"))); + + dpp::message response(event.command.channel_id, ""); + response.add_file("report.png", responseData); + event.edit_response(response); }); } else { event.edit_response( @@ -46,3 +40,7 @@ void commandGenerateReport(const dpp::slashcommand_t &event, " is not allowed. Only PNG, JPEG, and WEBP are allowed!"); } } + +// +// ACTUAL GENERATION STARTS HERE +// diff --git a/src/Utility/CairoGenerate.cpp b/src/Utility/CairoGenerate.cpp new file mode 100644 index 0000000..87c34af --- /dev/null +++ b/src/Utility/CairoGenerate.cpp @@ -0,0 +1,80 @@ +#include "CairoTools.hpp" + +std::string GenerateReportImage(cairo_surface_t *background, + std::string headline) { + cairo_surface_t *surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, REPORT_WIDTH, REPORT_HEIGHT); + + cairo_t *ctx = cairo_create(surface); + cairo_set_source_rgb(ctx, 0, 0, 0); + cairo_paint(ctx); + int imgW = 0, imgH = 0; + + // Background Image + cairo_save(ctx); + imgW = cairo_image_surface_get_width(background); + imgH = cairo_image_surface_get_height(background); + + cairo_scale(ctx, (double)REPORT_WIDTH / imgW, + (double)(REPORT_HEIGHT - REPORT_HEIGHT_OFFSET) / imgH); + cairo_set_source_surface(ctx, background, 0, 0); + cairo_paint(ctx); + cairo_restore(ctx); + + // Gradient + cairo_pattern_t *gradient = + cairo_pattern_create_linear(0, 0, 0, REPORT_HEIGHT); + cairo_pattern_add_color_stop_rgba(gradient, 0.85, 0, 0, 0, 1); + cairo_pattern_add_color_stop_rgba(gradient, 0, 0, 0, 0, 0); + cairo_rectangle(ctx, 0, 0, REPORT_WIDTH, REPORT_HEIGHT); + cairo_set_source(ctx, gradient); + cairo_fill(ctx); + + // Bumper + cairo_save(ctx); + cairo_surface_t *bumper = + cairo_image_surface_create_from_png(REPORT_RESOURCE_BUMPER_PATH); + + imgW = cairo_image_surface_get_width(bumper); + imgH = cairo_image_surface_get_height(bumper); + + cairo_scale(ctx, (double)REPORT_WIDTH / imgW, 1.5); + cairo_set_source_surface(ctx, bumper, 0, REPORT_HEIGHT - (imgH * 7.1)); + cairo_paint(ctx); + cairo_restore(ctx); + + // Text + cairo_select_font_face(ctx, "Steelfish", CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size(ctx, REPORT_TEXT_FONT_SIZE); + + // Iteration 3 + std::stringstream text(headline + " "); + + std::string output, part; + std::vector outputList; + while (text.tellp() == std::streampos(0)) { + std::getline(text, part, ' '); + if (output.length() + part.length() < REPORT_TEXT_LENGTH) { + output += part + ' '; + continue; + } + outputList.push_back(output); + output = part + ' '; + } + outputList.push_back(output); + + // Display Text + int lineCount = 0; + for (std::string line : outputList) { + cairo_move_to(ctx, REPORT_TEXT_START_X, + REPORT_HEIGHT - + (REPORT_TEXT_JUMP_Y * (outputList.size() - lineCount++))); + cairo_set_source_rgb(ctx, CAIRO_TEXT_WHITE); + cairo_show_text(ctx, line.c_str()); + } + + std::string data; + cairo_surface_write_to_png_stream(surface, cairoOutputAsPNGStream, &data); + return data; +} diff --git a/src/Utility/CairoTools.cpp b/src/Utility/CairoTools.cpp new file mode 100644 index 0000000..4091316 --- /dev/null +++ b/src/Utility/CairoTools.cpp @@ -0,0 +1,39 @@ +#include "CairoTools.hpp" + +std::unordered_map> + supportedImageFileTypes{{"image/png", pngToCairoSurface}, + {"image/jpeg", jpegToCairoSurface}, + {"image/webp", webpToCairoSurface}}; + +cairo_status_t cairoReadPNGdata(void *closure, unsigned char *data, + unsigned int length) { + std::string *imageData = static_cast(closure); + static size_t offset = 0; + + if (offset + length > imageData->size()) { + return CAIRO_STATUS_READ_ERROR; + } + + memcpy(data, imageData->data() + offset, length); + offset += length; + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t cairoOutputAsPNGStream(void *closure, const unsigned char *data, + unsigned int length) { + std::string *output = static_cast(closure); + output->append(reinterpret_cast(data), length); + return CAIRO_STATUS_SUCCESS; +} + +cairo_surface_t *pngToCairoSurface(const std::string &data) { + return cairo_image_surface_create_from_png_stream( + cairoReadPNGdata, const_cast(&data)); +} +cairo_surface_t *jpegToCairoSurface(std::string data) { + return cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 300, 200); +} +cairo_surface_t *webpToCairoSurface(std::string data) { + return cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 300, 200); +} diff --git a/src/Utility/CairoTools.hpp b/src/Utility/CairoTools.hpp new file mode 100644 index 0000000..1ac1dcf --- /dev/null +++ b/src/Utility/CairoTools.hpp @@ -0,0 +1,31 @@ +#include +#include +#include + +#define REPORT_WIDTH 1000 +#define REPORT_HEIGHT 1200 +#define REPORT_HEIGHT_OFFSET 200 +#define REPORT_TEXT_START_X 40 +#define REPORT_TEXT_JUMP_Y 120 +#define REPORT_TEXT_FONT_SIZE 120.0 +#define REPORT_TEXT_LENGTH 26 +#define REPORT_RESOURCE_BUMPER_PATH "./assets/report_bumper.png" +#define CAIRO_TEXT_WHITE 0.87, 0.87, 0.87 + +std::string GenerateReportImage(cairo_surface_t *background, + std::string headline); + +// File type management +cairo_status_t cairoReadPNGdata(void *closure, unsigned char *data, + unsigned int length); + +cairo_status_t cairoOutputAsPNGStream(void *closure, const unsigned char *data, + unsigned int length); + +cairo_surface_t *pngToCairoSurface(const std::string &data); +cairo_surface_t *jpegToCairoSurface(std::string data); +cairo_surface_t *webpToCairoSurface(std::string data); + +extern std::unordered_map> + supportedImageFileTypes; diff --git a/src/Utility/Utility.cpp b/src/Utility/Utility.cpp new file mode 100644 index 0000000..7a02739 --- /dev/null +++ b/src/Utility/Utility.cpp @@ -0,0 +1,3 @@ +// Unity Build +#include "CairoGenerate.cpp" +#include "CairoTools.cpp"