Pokered Save Editor 2
Pokemon Red & Blue save file editor - Qt 6 C++/QML
Loading...
Searching...
No Matches
fontpreviewprovider.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2020 Twilight
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15*/
16
22
23#include <QImage>
24#include <QtMath>
25#include <QPainter>
26#include <QBitmap>
27#include <QDebug>
28
30#include "./tilesetengine.h"
31#include <pse-db/fontsdb.h>
32#include <pse-common/utility.h>
33
38
40 QStringList idParts,
42 QSize* size,
43 const QSize& requestedSize)
44
45 : size(size),
48{
49 // Initial setup and unfolding of idParts
51
52 // Get Names from save file if present
55
56 // Now retrieve the tiles from the tileset engine
57 getTiles();
58
59 // Now retrieve tile codes for the whole thing and the lines they need to be
60 // on
62
63 // Figure out dimensions
66
67 // Create base image and copy over to bgImg, boxImg, and fgImage
68 // make bgImage the bgColor
69 getBaseImg();
70 bgImg = baseImg;
71 bgImg.fill(bgColor);
72
74 fgImg = baseImg;
75 resultingImg = QPixmap::fromImage(baseImg);
76
77 // If using box, then draw it
78 if(box)
79 drawBox();
80
81 // Draw the Foreground now
82 drawFg();
83
84 // Finish the image by merging fg and box if applicable then tinting the
85 // image
86 finishImg();
87
88 // Post process the image, this sort of finalizes the provider request by
89 // setting requested size and scaling to requested size which could be the
90 // providers requested size or my requested size.
92}
93
95{
96 // Breakdown idParts into variables
97 this->idParts = idParts;
98
100 type = idParts.at(IdPartType);
101 frame = idParts.at(IdPartFrame).toInt();
102
103 toWidth = idParts.at(IdPartWidth).toInt();
104 toHeight = idParts.at(IdPartHeight).toInt();
105
106 box = idParts.at(IdPartBox) == "box";
107 lines2 = idParts.at(IdPart2Line) == "2-lines";
108 chopLen = idParts.at(IdPartChop).toInt();
109 bgColor = QColor(idParts.at(IdPartBGColor));
110
111 useFg = idParts.at(IdPartFGColor) != "none";
112 fgColor = (useFg)
113 ? QColor(idParts.at(IdPartFGColor))
114 : QColor();
115
117
118 getInputStr(); // Somewhat more complicated
119}
120
122{
123 // Get str
124 // If the player inserts slashes then we want the rest of it
126}
127
129{
130 // Pass though 3 of 4 arguments, we also make sure to tell to include font
131 // tiles obviosuly, that's mandatory
132 // <tileset>/<type>/<font>/<frame>
133 QString tileUrl = tileset + "/" + type + "/font/" + QString::number(frame);
135}
136
138{
139 // Form the final text by inserting user string into placeholder string and
140 // converting to code and back in a way that simulates how the game would
141 // display it. Then split the string lines into seperate lines. Re-encode
142 // those lines into tile numbers.
143 QString resToConvert = placeholder.replace("%%", inputStr);
144
145 auto res = FontsDB::inst()->expandStr(resToConvert, 255, rivalsName, playersName)
146 .split("\n", Qt::SkipEmptyParts);
147
148 QVector<QVector<var8>> ret;
149 for(const auto& entry : res) {
150 QVector<int> tmp = FontsDB::inst()->convertToCode(entry, 255, false);
151 QVector<var8> tmpBytes;
152 tmpBytes.reserve(tmp.size());
153 for (int v : tmp) tmpBytes.append(static_cast<var8>(v));
154 ret.append(tmpBytes);
155 }
156
157 resultingText = ret;
158}
159
161{
162 if(!box && chopLen > 0)
164 else
166
167 if(imgWidth > drawWidth)
169
171}
172
174{
175 // Large enough for 2 lines only if 2 lines are present and 2 lines are
176 // allowed or if box is enabled, use that
177 if(box)
179 else {
180 imgHeight = ((resultingText.size() > 1) && lines2)
183 }
184
186}
187
189{
190 baseImg = QImage(imgWidth, imgHeight, QImage::Format_ARGB32);
191 baseImg.fill(QColor("transparent"));
192}
193
195{
196 QPainter p(&boxImg);
197 p.setCompositionMode(QPainter::CompositionMode_SourceOver);
198
199 int counter = 0;
200
201 for(int y = 0; y < drawHeightBoxTiles; y++) {
202 for(int x = 0; x < drawWidthTiles; x++) {
203 int boxTile = boxTiles[counter];
204 p.drawPixmap(x * tileSize, y * tileSize, tiles.at(boxTile));
205 counter++;
206 }
207 }
208}
209
211{
212 int startX = (box)
213 ? 1
214 : 0;
215
216 int startY = (box)
217 ? 2
218 : 0;
219
220 int yCounter = startY;
221 int xCounter = startX;
222
223 QPainter p(&fgImg);
224 p.setCompositionMode(QPainter::CompositionMode_SourceOver);
225 p.setBrush(bgColor);
226 p.setPen(QColor("transparent"));
227
228 for(const auto& yLine : resultingText) {
229 for(auto xTile : yLine) {
230
231 if(box)
232 p.drawRect(xCounter * tileSize, yCounter * tileSize, tileSize, tileSize);
233
234 p.drawPixmap(xCounter * tileSize, yCounter * tileSize, tiles.at(xTile));
235 xCounter++;
236 }
237
238 xCounter = startX;
239 yCounter += 2; // Keep space between lines
240 }
241}
242
244{
245 QPainter p;
246
247 // Foreground is disabled because I could never get it to work correctly
248 // The issue is I have 4-color images and it seems to want to colorize
249 // non-B&W or B&W only but not both so I give up unless someone else can
250 // help me.
251
252 p.begin(&resultingImg);
253 p.setCompositionMode(QPainter::CompositionMode_SourceOver);
254 p.drawImage(0, 0, bgImg);
255 p.drawImage(0, 0, boxImg);
256 p.drawImage(0, 0, fgImg);
257 p.end();
258}
259
261{
262 // Requested size
263 QSize actualSize = QSize(toWidth, toHeight);
264
265 // Set requested size if asked
266 if(size != nullptr)
267 *size = actualSize;
268
269 // Scale to requested size
270 // Either Qt Quick requested size or my requested size, either way the image
271 // will be scaled to someones requested size
272 resultingImg = resultingImg.scaled((requestedSize.width() > 0) ? requestedSize.width() : actualSize.width(),
273 (requestedSize.height() > 0) ? requestedSize.height() : actualSize.height());
274}
275
277{
278 playersName = (expanded == nullptr || expanded->player->basics->playerName == "")
279 ? "RED"
280 : expanded->player->basics->playerName;
281}
282
284{
285 rivalsName = (expanded == nullptr || expanded->rival->name == "")
286 ? "BLUE"
287 : expanded->rival->name;
288}
289
291 : QQuickImageProvider(QQuickImageProvider::Pixmap),
293{}
294
296 const QString& id, QSize* size, const QSize& requestedSize)
297{
298 // Check to make sure it's a properly formed request
299 auto idParts = id.split("/", Qt::KeepEmptyParts);
300
301 // Has to have all 10 parts unconditionally
302 if(idParts.size() < FontPreviewInstance::IdPart_END)
303 return getErrorImg(size, requestedSize);
304
305 // With that out of the way, create an instance of the struct that will do
306 // the work.
307 auto inst = FontPreviewInstance(idParts, expanded, size, requestedSize);
308
309 return inst.resultingImg;
310}
311
312QPixmap FontPreviewProvider::getErrorImg(QSize* size, const QSize& requestedSize)
313{
314 // Create an error "red" blank tile to indicate issue
315 QSize actualSize = QSize(FontPreviewInstance::drawWidth,
317
318 if(size != nullptr)
319 *size = actualSize;
320
321 auto img = QImage(actualSize.width(),
322 actualSize.height(),
323 QImage::Format::Format_ARGB32);
324
325 img.fill(QColor(255, 0, 0, 255)); // Fill with error "red" color
326
327 auto ret = QPixmap::fromImage(img);
328
329 ret = ret.scaled(
330 (requestedSize.width() > 0) ? requestedSize.width() : img.width(),
331 (requestedSize.height() > 0) ? requestedSize.height() : img.height());
332
333 return ret;
334}
virtual QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override
Render the preview for id (format documented above).
FontPreviewProvider(SaveFileExpanded *expanded)
SaveFileExpanded * expanded
Live save (for name substitution).
QPixmap getErrorImg(QSize *size, const QSize &requestedSize)
Fallback image when an id is invalid.
static FontsDB * inst()
< Number of font glyphs.
Definition fontsdb.cpp:367
const QVector< int > convertToCode(QString str, int maxLen=11, const bool autoEnd=true) const
String -> font codes (see note; expensive).
Definition fontsdb.cpp:109
const QString expandStr(const QString msg, const int maxLen, const QString rival, const QString player) const
Expand English text to in-game form (substitutes rival/player).
Definition fontsdb.cpp:205
Root of the editable object tree – the friendly mirror of a raw save.
static QVector< QPixmap > buildTileset(QString id)
Build the per-tile pixmaps for id (format above).
const QString decodeAfterUrl(QString beforeStr) const
Decode the space-separated hex produced by encodeBeforeUrl() back to text.
Definition utility.cpp:57
static Utility * inst()
< The shared Random instance, reachable from QML as pseCommon.random.
Definition utility.cpp:31
var8e var8
Everyday 8-bit alias. Exact (not "fastest") to dodge the pointer-width bug noted above.
Definition types.h:124
The render pipeline for a single font-preview request.
int toWidth
Target width (id part).
QPixmap resultingImg
The finished pixmap.
QVector< QVector< var8 > > resultingText
Text as tile-code rows.
void getBaseImg()
Allocate the base image.
void getImageWidth()
Compute output width.
QString tileset
Tileset name (id part).
SaveFileExpanded * expanded
Live save (for name substitution).
int imgWidthTiles
Output width (tiles).
FontPreviewInstance(QStringList idParts, SaveFileExpanded *expanded, QSize *size, const QSize &requestedSize)
static constexpr int drawWidth
void setup(QStringList idParts)
Parse the id parts into the fields below.
void getRivalsName()
Read the rival's name from the save (for substitution).
static constexpr int drawWidthTiles
const QSize & requestedSize
Size QML requested.
QString type
Type, e.g. "outdoor" (id part).
static constexpr int drawHeightLines2
bool box
Render a dialogue box (id part).
QImage fgImg
Foreground layer.
void drawFg()
Draw the foreground text.
int toHeight
Target height (id part).
static constexpr int boxTiles[]
Tilemap for a 20x6 dialogue box with arrow (see note above).
QImage bgImg
Background layer.
QColor bgColor
Background colour (id part).
void postProcess()
Apply any post-processing.
QImage baseImg
Base canvas.
void getPlayersName()
Read the player's name from the save (for substitution).
static constexpr int drawHeightBox
QString placeholder
Template the str is inserted into (id part).
static constexpr int drawHeightBoxTiles
int imgWidth
Output width (px).
void getInputStr()
Resolve the final input string (placeholder + str + names).
int imgHeightTiles
Output height (tiles).
QSize * size
Out-param size handed back to QML.
void drawBox()
Draw the dialogue box background, if requested.
QString rivalsName
Resolved rival name.
int imgHeight
Output height (px).
QStringList idParts
The raw split id parts.
void getTiles()
Render the per-tile pixmaps for the text.
void getResultingText()
Convert the string to tile-code rows via FontsDB.
bool useFg
Whether the foreground is painted.
static constexpr int tileSize
Pixels per tile edge.
bool lines2
Render on two lines (id part).
int frame
Animation frame (id part).
void finishImg()
Composite into the final pixmap.
static constexpr int drawHeightLines1
int chopLen
Max line length before chopping (id part).
QColor fgColor
Foreground/text colour (id part; "none" = don't paint).
QString inputStr
The raw font string to render (id part).
QString playersName
Resolved player name.
void getImageHeight()
Compute output height.
QVector< QPixmap > tiles
Per-character tile pixmaps.