Pokered Save Editor 2
Pokemon Red & Blue save file editor - Qt 6 C++/QML
Loading...
Searching...
No Matches
tilesetengine.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 "./tilesetengine.h"
24#include <QStringList>
25#include <QColor>
26#include <QVector>
27#include <QPainter>
28
29QImage TilesetEngine::getTileset(QString name)
30{
31 // Make lowercase and replace spaces
32 QString nameFixed = name.toLower().replace(" ", "_");
33 return QImage(":/assets/tilesets/" + nameFixed + ".png")
34 .convertToFormat(QImage::Format::Format_ARGB32);
35}
36
37QImage TilesetEngine::getFlower(int frame)
38{
39 // Frame 0: flower2
40 // Frame 1: flower3
41 // Frame 2: flower1
42 // Frame 3: flower1
43
44 // returns frame 0-3 no matter frame number
45 int subFrame = frame % 4;
46 int ind;
47
48 if(subFrame == 0)
49 ind = 2;
50 else if(subFrame == 1)
51 ind = 3;
52 else
53 ind = 1;
54
55 return QImage(":/assets/tilesets/_flower" + QString::number(ind) + ".png")
56 .convertToFormat(QImage::Format::Format_ARGB32);
57}
58
60{
61 return QImage(":/assets/tilesets/_font.png")
62 .convertToFormat(QImage::Format::Format_ARGB32);
63}
64
66{
67 auto idParts = id.split("/", Qt::SkipEmptyParts);
68
69 // Has to have all 4 parts unconditionally
70 if(idParts.size() < 4) {
71 // Return error red
72 auto tmp = blankImage();
73 tmp.fill(QColor(255, 0, 0, 0));
74 return QPixmap::fromImage(tmp);
75 }
76
77 // Is outdoor? and use font? Also get frame
78 bool outdoorType = idParts.at(1).toLower() == "outdoor";
79 bool useFont = idParts.at(2).toLower() == "font";
80 int frame = idParts.at(3).toInt();
81
82 // Get or create blank images as needed for each layer
83 auto tilesetImg = getTileset(idParts.at(0));
84
85 auto fontImg = (useFont)
86 ? getFont()
87 : blankImage();
88 auto flowerImg = (outdoorType)
89 ? getFlower(frame)
90 : blankImage();
91
92 // Prepare tileset image
93 QImage tileset = blankImage();
94 //tileset.fill(QColor("white"));
95
96 // Stack requested and retrieved tileset layers
97 QPainter p(&tileset);
98 p.setCompositionMode(QPainter::CompositionMode_SourceOver);
99 p.drawImage(0, 0, tilesetImg);
100 p.drawImage(0, 0, fontImg);
101 p.drawImage(0, 0, flowerImg);
102
103 // Post-Process if applicable
104 if(outdoorType) {
105 // Copy water tile out to it's own image
106 auto waterTile = tileset.copy(4 * tileWidth, 1 * tileHeight,
108
109 // Process water tile
110 waterTile = postProcessWave(waterTile, frame);
111
112 // Copy back into the tileset
113 p.drawImage(4 * tileWidth, 1 * tileHeight, waterTile);
114 }
115
116 return QPixmap::fromImage(tileset);
117}
118
119QVector<QPixmap> TilesetEngine::buildTileset(QString id)
120{
121 // Use internal debug function to manually and slowly create it
122 auto tileset = buildTilesetFullDebug(id).toImage();
123
124 // Convert to an array of tiles
125 QVector<QPixmap> ret = getTiles(tileset);
126
127 // Return a newly built array of tiles
128 return ret;
129}
130
131QVector<QPixmap> TilesetEngine::getTiles(QImage tilemap)
132{
133 QVector<QPixmap> ret;
134
135 int tilesX = width / 8;
136 int tilesY = height / 8;
137
138 for(int tileY = 0; tileY < tilesY; tileY++) {
139 for(int tileX = 0; tileX < tilesX; tileX++) {
140 int startX = tileX * 8;
141 int startY = tileY * 8;
142
143 auto tile = tilemap.copy(startX, startY, 8, 8);
144 ret.append(QPixmap::fromImage(tile));
145 }
146 }
147
148 return ret;
149}
150
152{
153 auto img = QImage(width, height, QImage::Format::Format_ARGB32);
154 img.fill(QColor(0, 0, 0, 0));
155 return img;
156}
157
159{
160 // Holds a line of pixels as a buffer
161 QVector<QColor> line;
162 QImage lineImg;
163
164 // Finished image, pre-fill white
165 QImage ret = QImage(tileWidth, tileHeight, QImage::Format::Format_ARGB32);
166 ret.fill(QColor("white"));
167
168 // Loop through each line of the tile
169 for(int y = 0; y < tileHeight; y++) {
170
171 // Copy out each line
172 lineImg = tile.copy(0, y, tileWidth, 1);
173
174 // Convert it to pixels
175 for(int x = 0; x < tileWidth; x++) {
176 line.append(lineImg.pixelColor(x, 0));
177 }
178
179 // Convert it back to an image offset by 1
180 for(int x = 0; x < tileWidth; x++) {
181
182 // Calculate a -1 offset
183 // We want to shift the row of pixels right by 1 and wrap around
184 // We start at pixel #0 and move forward, the first pixel though
185 // is replaced with the last pixel thus creating a left-shift wrap around
186
187 int xOff = x - 1;
188 if(xOff < 0)
189 xOff = tileWidth - 1;
190
191 ret.setPixelColor(x, y, line.at(xOff));
192 }
193
194 line.clear();
195 }
196
197 return ret;
198}
199
200QImage TilesetEngine::postProcessWave(QImage tile, int frame)
201{
202 // frame #0 = 0 shift
203 // frame #1 = 1 shift
204 // frame #2 = 2 shift
205 // frame #3 = 3 shift
206 // frame #4 = 4 shift
207 // frame #5 = 3 shift
208 // frame #6 = 2 shift
209 // frame #7 = 1 shift
210
211 // Get frame index 0-7 no matter frame number
212 int subFrame = frame % 8;
213
214 int count = subFrame;
215 if(subFrame > 4)
216 count = (8 - count);
217
218 QImage ret = tile;
219
220 for(int i = 0; i < count; i++)
221 ret = postProcessWaveOnce(ret);
222
223 return ret;
224}
static constexpr int width
Tileset image width (px).
static QImage blankImage()
A blank transparent tile-sized image.
static QImage postProcessWaveOnce(QImage tile)
One increment of the water-wave shift.
static QImage postProcessWave(QImage tile, int frame)
Apply the wave effect for frame (see note).
static constexpr int height
Tileset image height (px).
static QVector< QPixmap > buildTileset(QString id)
Build the per-tile pixmaps for id (format above).
static constexpr int tileWidth
Tile width (px).
static QPixmap buildTilesetFullDebug(QString id)
static QImage getFont()
The font overlay image.
static QImage getFlower(int frame)
The animated flower overlay for frame (see note).
static QVector< QPixmap > getTiles(QImage tilemap)
Slice a tilemap image into per-tile pixmaps.
static constexpr int tileHeight
Tile height (px).
static QImage getTileset(QString name)
Load a tileset image by name (case-insensitive; spaces -> underscores).