From bbe334b6c9249fea57dee53a0804693dab46f03c Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Fri, 2 Mar 2018 18:01:31 -0500 Subject: Added overlay shadow, achievement title wrapping, and date The canonical title font is Roboto Bold and the date font is Roboto Medium. --- database.cpp | 54 +++++++++++++++++++++++++++ database.h | 7 ++++ lunatic.cpp | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 166 insertions(+), 15 deletions(-) diff --git a/database.cpp b/database.cpp index 2885d1f..953c217 100644 --- a/database.cpp +++ b/database.cpp @@ -1,6 +1,8 @@ #include "database.h" #include #include +#include +#include database::database(std::string path) { @@ -120,3 +122,55 @@ std::string database::getRandomImageForGame(int gameId) const return result; } + +did database::getRandomDidForAchievement(int achievementId) const +{ + std::string queryString = "SELECT profile_id, DATETIME(achieved_at) FROM dids WHERE achievement_id = ? ORDER BY RANDOM() LIMIT 1"; + + sqlite3_stmt* ppstmt; + if (sqlite3_prepare_v2( + ppdb_, + queryString.c_str(), + queryString.length(), + &ppstmt, + NULL) != SQLITE_OK) + { + std::string errorMsg = sqlite3_errmsg(ppdb_); + sqlite3_finalize(ppstmt); + + throw std::logic_error(errorMsg); + } + + if (sqlite3_bind_int(ppstmt, 1, achievementId) != SQLITE_OK) + { + std::string errorMsg = sqlite3_errmsg(ppdb_); + sqlite3_finalize(ppstmt); + + throw std::logic_error(errorMsg); + } + + if (sqlite3_step(ppstmt) != SQLITE_ROW) + { + std::string errorMsg = sqlite3_errmsg(ppdb_); + sqlite3_finalize(ppstmt); + + throw std::logic_error(errorMsg); + } + + did result; + result.profileId = sqlite3_column_int(ppstmt, 0); + + std::tm achievedAt = {}; + + std::stringstream dateParser; + dateParser << reinterpret_cast(sqlite3_column_text(ppstmt, 1)); + dateParser >> std::get_time(&achievedAt, "%Y-%m-%d %H:%M:%S"); + + std::stringstream dateFormatter; + dateFormatter << std::put_time(&achievedAt, "%m/%d/%Y"); + result.date = dateFormatter.str(); + + sqlite3_finalize(ppstmt); + + return result; +} diff --git a/database.h b/database.h index a616318..9dcb118 100644 --- a/database.h +++ b/database.h @@ -12,6 +12,11 @@ struct achievement { std::string color; }; +struct did { + int profileId; + std::string date; +}; + class database { public: @@ -43,6 +48,8 @@ public: std::string getRandomImageForGame(int gameId) const; + did getRandomDidForAchievement(int achievementId) const; + private: database() = default; diff --git a/lunatic.cpp b/lunatic.cpp index 497c648..291bd09 100644 --- a/lunatic.cpp +++ b/lunatic.cpp @@ -6,8 +6,34 @@ #include #include #include +#include +#include +#include #include "database.h" +template +Container split(std::string input, std::string delimiter) +{ + Container result; + + while (!input.empty()) + { + int divider = input.find(delimiter); + if (divider == std::string::npos) + { + result.push_back(input); + + input = ""; + } else { + result.push_back(input.substr(0, divider)); + + input = input.substr(divider+delimiter.length()); + } + } + + return result; +} + int main(int argc, char** argv) { if (argc != 2) @@ -43,15 +69,88 @@ int main(int argc, char** argv) std::string imagePath = config["images"].as() + "/" + imageName; - Magick::Image overlay; - overlay.read("res/overlay.png"); - Magick::Image moonColor; moonColor.read("res/" + ach.color + ".png"); try { - // Start with the game image + // Start with the Odyssey text overlay + Magick::Image overlay; + overlay.read("res/overlay.png"); + + // Add the moon image + overlay.composite(moonColor, 672, 85, Magick::OverCompositeOp); + + // Add the name of the achievement + overlay.fontPointsize(54); + overlay.fillColor("white"); + overlay.font("@" + config["title_font"].as()); + + std::list words = split>( + ach.title, + " "); + std::ostringstream wrappingStream; + std::string curline; + int lines = 1; + + Magick::TypeMetric metric; + while (!words.empty()) + { + std::string temp = curline; + + if (!curline.empty()) + { + temp += " "; + } + + temp += words.front(); + + overlay.fontTypeMetrics(temp, &metric); + + if (metric.textWidth() > 1200) + { + wrappingStream << std::endl; + curline = words.front(); + + lines++; + } else { + if (!curline.empty()) + { + wrappingStream << " "; + } + + curline = temp; + } + + wrappingStream << words.front(); + words.pop_front(); + } + + std::string wrapped = wrappingStream.str(); + + overlay.annotate( + wrapped, + Magick::Geometry(1600, 228, 0, 710), + Magick::GravityType::NorthGravity); + + // Add the achievement date + did theDid = db.getRandomDidForAchievement(ach.achievementId); + + overlay.fontTypeMetrics(wrapped, &metric); + + overlay.fontPointsize(20); + overlay.font("@" + config["date_font"].as()); + overlay.annotate( + theDid.date, + Magick::Geometry(1600, 228, 0, 710 + metric.textHeight() * lines - 22), + Magick::GravityType::NorthGravity); + + // Make a shadow copy + Magick::Image shadow(overlay); + shadow.negate(); + shadow.blur(0, 12); + + // Read the game image Magick::Image image; image.read(imagePath); @@ -60,18 +159,9 @@ int main(int argc, char** argv) image.scale("80x45"); image.scale("1600x900"); - // Add the text and moon image from Odyssey + // Add the generated overlay to it + image.composite(shadow, 0, 0, Magick::OverCompositeOp); image.composite(overlay, 0, 0, Magick::OverCompositeOp); - image.composite(moonColor, 672, 85, Magick::OverCompositeOp); - - // Add the name of the achievement - image.fontPointsize(36); - image.fillColor("white"); - image.font("@" + config["font"].as()); - image.annotate( - ach.title, - Magick::Geometry(0, 0, 0, 672), - Magick::GravityType::NorthGravity); // Output for debug image.magick("png"); -- cgit 1.4.1