File indexing completed on 2024-05-19 07:55:16
0001 /* 0002 SPDX-FileCopyrightText: 2015 Jakob Gruber <jakob.gruber@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include <config.h> 0008 #include "renderer.h" 0009 0010 #include <QDir> 0011 #include <QFile> 0012 #include <QPainter> 0013 #include <QPixmapCache> 0014 #include <QStandardPaths> 0015 #include <assert.h> 0016 #include <iostream> 0017 0018 #include "src/constants.h" 0019 #include "src/outofboundsexception.h" 0020 #include "src/logic/settings.h" 0021 #include "src/systemexception.h" 0022 0023 #define MIN_STREAK_COUNT (4) 0024 0025 Renderer::Renderer() : m_tilesize(47), m_overview_tilesize(12), 0026 m_streak_grid_count(6) 0027 { 0028 loadResources(); 0029 0030 m_names << QStringLiteral("transparent") << QStringLiteral("background") << QStringLiteral("cellframe") 0031 << QStringLiteral("box") << QStringLiteral("cross") << QStringLiteral("highlight") << QStringLiteral("streak1") 0032 << QStringLiteral("streak2") << QStringLiteral("divider") << QStringLiteral("overview_box") 0033 << QStringLiteral("overview_cross"); 0034 } 0035 0036 void Renderer::loadResources() { 0037 0038 /* Fonts. */ 0039 0040 for (int i = 0; i < FontSizeLength; i++) { 0041 m_fonts[i].setPointSize(24); 0042 } 0043 0044 /* Tiles. */ 0045 0046 const QString prefix = QStringLiteral("themes/"); 0047 QList<QString> paths; 0048 paths << QString(prefix) 0049 << QStringLiteral(FILEPATH) + QStringLiteral("/") + prefix 0050 << QStandardPaths::locate(QStandardPaths::AppDataLocation, 0051 prefix, 0052 QStandardPaths::LocateOption::LocateDirectory); 0053 0054 /* try loading first from working directory, then the system directories */ 0055 for (int i = 0; i < paths.size(); i++) { 0056 const QString filenameSvg = QDir::toNativeSeparators(paths[i] + QStringLiteral("picmi.svg")); 0057 0058 if (!QFile::exists(filenameSvg)) { 0059 continue; 0060 } 0061 m_renderer = QSharedPointer<QSvgRenderer>(new QSvgRenderer(filenameSvg)); 0062 return; 0063 } 0064 0065 throw SystemException(QStringLiteral("Resources not found")); 0066 } 0067 0068 int Renderer::gridSize(const QSize &size, int board_width, int board_height) const { 0069 int grid = size.width() / (board_width + m_streak_grid_count); 0070 0071 if ((board_height + m_streak_grid_count) * grid > size.height()) { 0072 grid = size.height() / (board_height + m_streak_grid_count); 0073 } 0074 0075 return grid; 0076 } 0077 0078 bool Renderer::streaksFit(const QStringList &streaks) const { 0079 QFontMetrics fm(m_fonts[Regular]); 0080 0081 /* Subtract a little from real size to account for padding. */ 0082 const int len = m_streak_grid_count * m_tilesize - m_tilesize; 0083 const int limit = 8 * m_tilesize; 0084 const QRect limrect(0, 0, limit, limit); 0085 0086 for (const QString &str : streaks) { 0087 /* QFontMetrics.boundingRect defaults to Qt::SingleLine, which handles \n 0088 as a normal character instead of a line break. Manually specify flags. */ 0089 QRect rect = fm.boundingRect(limrect, Qt::AlignLeft | Qt::AlignTop, str); 0090 if (qMax(rect.width(), rect.height()) > len) { 0091 return false; 0092 } 0093 } 0094 0095 return true; 0096 } 0097 0098 void Renderer::setSize(const QSize &size, int board_width, int board_height, 0099 const QStringList &streaks) { 0100 if (board_width < 0 || board_height < 0) { 0101 throw OutOfBoundsException(); 0102 } 0103 0104 /* Calculate the tile size, given the window size, the board dimensions, 0105 and the list of streak strings. The tile size must be the largest value 0106 such that all streaks will still fit into 0107 m_streak_grid_count * m_tilesize. 0108 0109 Start with the default minimum grid size, and keep 0110 recalculating the tile size until all streaks fit. */ 0111 0112 m_streak_grid_count = MIN_STREAK_COUNT - 1; 0113 do { 0114 m_streak_grid_count++; 0115 m_tilesize = gridSize(size, board_width, board_height); 0116 setFontSize(); 0117 } while (!streaksFit(streaks)); 0118 0119 /* the overview is a square area at the top left of the field with dimensions 0120 getXOffset() x getYOffset(). using the same logic as for calculating the 0121 main tilesize, get the overview tilesize such that the entire board fits */ 0122 0123 const int buffer = 15; 0124 QSize overview_size(getXOffset() - buffer, getYOffset() - buffer); 0125 m_overview_tilesize = gridSize(overview_size, board_width, board_height); 0126 } 0127 0128 int Renderer::getOverviewTilesize() const { 0129 return m_overview_tilesize; 0130 } 0131 0132 const QFont &Renderer::getFont(enum FontSize size) const { 0133 return m_fonts[size]; 0134 } 0135 0136 #define MIN_FONT_SIZE (5) 0137 0138 void Renderer::setFontSize() { 0139 int size; 0140 0141 size = (m_tilesize - 10) * 0.5 + 5; 0142 m_fonts[Regular].setPointSize(qMax(MIN_FONT_SIZE, size)); 0143 0144 size = (m_tilesize - 10) * 0.75 + 7; 0145 m_fonts[Large].setPointSize(qMax(MIN_FONT_SIZE, size)); 0146 } 0147 0148 Renderer *Renderer::instance() { 0149 static Renderer instance; 0150 return &instance; 0151 } 0152 0153 QPixmap Renderer::getPixmap(Renderer::Resource resource) const { 0154 switch (resource) { 0155 case Background: return getCachedPixmap(resource, 1200, 1920); 0156 case Streak1: 0157 case Streak2: return getCachedPixmap(resource, m_tilesize, m_tilesize * m_streak_grid_count); 0158 case OverviewBox: 0159 case OverviewCross: return getCachedPixmap(resource, m_overview_tilesize, m_overview_tilesize); 0160 default: return getCachedPixmap(resource, m_tilesize, m_tilesize); 0161 } 0162 } 0163 0164 QPixmap Renderer::getCachedPixmap(Renderer::Resource resource, int h, int w) const 0165 { 0166 /* Special case for custom background. */ 0167 if (resource == Background) { 0168 if (Settings::instance()->customBgEnabled()) { 0169 return QPixmap(Settings::instance()->customBgPath()); 0170 } 0171 } 0172 0173 QString key = QStringLiteral("%1:%2x%3").arg(m_names[resource]).arg(w).arg(h); 0174 0175 QPixmap pixmap; 0176 if (!QPixmapCache::find(key, &pixmap)) { 0177 pixmap = QPixmap(w, h); 0178 pixmap.fill(Qt::transparent); 0179 QPainter painter(&pixmap); 0180 m_renderer->render(&painter, m_names[resource], QRectF(0, 0, w, h)); 0181 painter.end(); 0182 QPixmapCache::insert(key, pixmap); 0183 } 0184 0185 return pixmap; 0186 } 0187 0188 int Renderer::getTilesize() const { 0189 return m_tilesize; 0190 } 0191 0192 int Renderer::getYOffset() const { 0193 return m_streak_grid_count * m_tilesize; 0194 } 0195 0196 int Renderer::getXOffset() const { 0197 return m_streak_grid_count * m_tilesize; 0198 } 0199 0200 int Renderer::getStreakGridCount() const { 0201 return m_streak_grid_count; 0202 }