src/platform/qt/ObjView.cpp (view raw)
1/* Copyright (c) 2013-2016 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "ObjView.h"
7
8#include "CoreController.h"
9#include "GBAApp.h"
10
11#include <QAction>
12#include <QClipboard>
13#include <QFontDatabase>
14#include <QTimer>
15
16#include "LogController.h"
17#include "VFileDevice.h"
18
19#ifdef M_CORE_GBA
20#include <mgba/internal/gba/gba.h>
21#endif
22#ifdef M_CORE_GB
23#include <mgba/internal/gb/gb.h>
24#endif
25#include <mgba-util/vfs.h>
26
27using namespace QGBA;
28
29ObjView::ObjView(std::shared_ptr<CoreController> controller, QWidget* parent)
30 : AssetView(controller, parent)
31 , m_controller(controller)
32{
33 m_ui.setupUi(this);
34 m_ui.tile->setController(controller);
35
36 const QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
37
38 m_ui.x->setFont(font);
39 m_ui.y->setFont(font);
40 m_ui.w->setFont(font);
41 m_ui.h->setFont(font);
42 m_ui.address->setFont(font);
43 m_ui.priority->setFont(font);
44 m_ui.palette->setFont(font);
45 m_ui.transform->setFont(font);
46 m_ui.mode->setFont(font);
47
48 connect(m_ui.tiles, &TilePainter::indexPressed, this, &ObjView::translateIndex);
49 connect(m_ui.tiles, &TilePainter::needsRedraw, this, [this]() {
50 updateTiles(true);
51 });
52 connect(m_ui.objId, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ObjView::selectObj);
53 connect(m_ui.magnification, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this]() {
54 updateTiles(true);
55 });
56 connect(m_ui.exportButton, &QAbstractButton::clicked, this, &ObjView::exportObj);
57 connect(m_ui.copyButton, &QAbstractButton::clicked, this, &ObjView::copyObj);
58
59 QAction* exportAction = new QAction(this);
60 exportAction->setShortcut(QKeySequence::Save);
61 connect(exportAction, &QAction::triggered, this, &ObjView::exportObj);
62 addAction(exportAction);
63
64 QAction* copyAction = new QAction(this);
65 copyAction->setShortcut(QKeySequence::Copy);
66 connect(copyAction, &QAction::triggered, this, &ObjView::copyObj);
67 addAction(copyAction);
68}
69
70void ObjView::selectObj(int obj) {
71 m_objId = obj;
72 updateTiles(true);
73}
74
75void ObjView::translateIndex(int index) {
76 unsigned x = index % m_objInfo.width;
77 unsigned y = index / m_objInfo.width;
78 m_ui.tile->selectIndex(x + y * m_objInfo.stride + m_tileOffset + m_boundary);
79}
80
81#ifdef M_CORE_GBA
82void ObjView::updateTilesGBA(bool force) {
83 m_ui.objId->setMaximum(127);
84 const GBA* gba = static_cast<const GBA*>(m_controller->thread()->core->board);
85 const GBAObj* obj = &gba->video.oam.obj[m_objId];
86
87 ObjInfo newInfo;
88 lookupObj(m_objId, &newInfo);
89
90 m_ui.tiles->setTileCount(newInfo.width * newInfo.height);
91 m_ui.tiles->setMinimumSize(QSize(newInfo.width * 8, newInfo.height * 8) * m_ui.magnification->value());
92 m_ui.tiles->resize(QSize(newInfo.width * 8, newInfo.height * 8) * m_ui.magnification->value());
93 unsigned tileBase = newInfo.tile;
94 unsigned tile = newInfo.tile;
95 if (GBAObjAttributesAIs256Color(obj->a)) {
96 m_ui.palette->setText("256-color");
97 m_ui.tile->setBoundary(1024, 1, 3);
98 m_ui.tile->setPalette(0);
99 m_boundary = 1024;
100 tileBase *= 2;
101 } else {
102 m_ui.palette->setText(QString::number(newInfo.paletteId));
103 m_ui.tile->setBoundary(2048, 0, 2);
104 m_ui.tile->setPalette(newInfo.paletteId);
105 }
106 if (newInfo != m_objInfo) {
107 force = true;
108 }
109 m_objInfo = newInfo;
110 m_tileOffset = newInfo.tile;
111 mTileCache* tileCache = mTileCacheSetGetPointer(&m_cacheSet->tiles, newInfo.paletteSet);
112
113 int i = 0;
114 for (int y = 0; y < newInfo.height; ++y) {
115 for (int x = 0; x < newInfo.width; ++x, ++i, ++tile, ++tileBase) {
116 const color_t* data = mTileCacheGetTileIfDirty(tileCache, &m_tileStatus[16 * tileBase], tile, newInfo.paletteId);
117 if (data) {
118 m_ui.tiles->setTile(i, data);
119 } else if (force) {
120 m_ui.tiles->setTile(i, mTileCacheGetTile(tileCache, tile, newInfo.paletteId));
121 }
122 }
123 tile += newInfo.stride - newInfo.width;
124 tileBase += newInfo.stride - newInfo.width;
125 }
126
127 m_ui.x->setText(QString::number(newInfo.x));
128 m_ui.y->setText(QString::number(newInfo.y));
129 m_ui.w->setText(QString::number(newInfo.width * 8));
130 m_ui.h->setText(QString::number(newInfo.height * 8));
131
132 m_ui.address->setText(tr("0x%0").arg(BASE_OAM + m_objId * sizeof(*obj), 8, 16, QChar('0')));
133 m_ui.priority->setText(QString::number(newInfo.priority));
134 m_ui.flippedH->setChecked(newInfo.hflip);
135 m_ui.flippedV->setChecked(newInfo.vflip);
136 m_ui.enabled->setChecked(newInfo.enabled);
137 m_ui.doubleSize->setChecked(GBAObjAttributesAIsDoubleSize(obj->a) && GBAObjAttributesAIsTransformed(obj->a));
138 m_ui.mosaic->setChecked(GBAObjAttributesAIsMosaic(obj->a));
139
140 if (GBAObjAttributesAIsTransformed(obj->a)) {
141 m_ui.transform->setText(QString::number(GBAObjAttributesBGetMatIndex(obj->b)));
142 } else {
143 m_ui.transform->setText(tr("Off"));
144 }
145
146 switch (GBAObjAttributesAGetMode(obj->a)) {
147 case OBJ_MODE_NORMAL:
148 m_ui.mode->setText(tr("Normal"));
149 break;
150 case OBJ_MODE_SEMITRANSPARENT:
151 m_ui.mode->setText(tr("Trans"));
152 break;
153 case OBJ_MODE_OBJWIN:
154 m_ui.mode->setText(tr("OBJWIN"));
155 break;
156 default:
157 m_ui.mode->setText(tr("Invalid"));
158 break;
159 }
160}
161#endif
162
163#ifdef M_CORE_GB
164void ObjView::updateTilesGB(bool force) {
165 m_ui.objId->setMaximum(39);
166 const GB* gb = static_cast<const GB*>(m_controller->thread()->core->board);
167 const GBObj* obj = &gb->video.oam.obj[m_objId];
168
169 ObjInfo newInfo;
170 lookupObj(m_objId, &newInfo);
171
172 mTileCache* tileCache = mTileCacheSetGetPointer(&m_cacheSet->tiles, 0);
173 unsigned tile = newInfo.tile;
174 m_ui.tiles->setTileCount(newInfo.height);
175 m_ui.tile->setBoundary(1024, 0, 0);
176 m_ui.tiles->setMinimumSize(QSize(8, newInfo.height * 8) * m_ui.magnification->value());
177 m_ui.tiles->resize(QSize(8, newInfo.height * 8) * m_ui.magnification->value());
178 m_ui.palette->setText(QString::number(newInfo.paletteId - 8));
179
180 if (newInfo != m_objInfo) {
181 force = true;
182 }
183 m_objInfo = newInfo;
184 m_tileOffset = tile;
185 m_boundary = 1024;
186
187 int i = 0;
188 m_ui.tile->setPalette(newInfo.paletteId);
189 for (int y = 0; y < newInfo.height; ++y, ++i) {
190 unsigned t = tile + i;
191 const color_t* data = mTileCacheGetTileIfDirty(tileCache, &m_tileStatus[8 * t], t, newInfo.paletteId);
192 if (data) {
193 m_ui.tiles->setTile(i, data);
194 } else if (force) {
195 m_ui.tiles->setTile(i, mTileCacheGetTile(tileCache, t, newInfo.paletteId));
196 }
197 }
198
199 m_ui.x->setText(QString::number(newInfo.x));
200 m_ui.y->setText(QString::number(newInfo.y));
201 m_ui.w->setText(QString::number(8));
202 m_ui.h->setText(QString::number(newInfo.height * 8));
203
204 m_ui.address->setText(tr("0x%0").arg(GB_BASE_OAM + m_objId * sizeof(*obj), 4, 16, QChar('0')));
205 m_ui.priority->setText(QString::number(newInfo.priority));
206 m_ui.flippedH->setChecked(newInfo.hflip);
207 m_ui.flippedV->setChecked(newInfo.vflip);
208 m_ui.enabled->setChecked(newInfo.enabled);
209 m_ui.doubleSize->setChecked(false);
210 m_ui.mosaic->setChecked(false);
211 m_ui.transform->setText(tr("N/A"));
212 m_ui.mode->setText(tr("N/A"));
213}
214#endif
215
216void ObjView::exportObj() {
217 QString filename = GBAApp::app()->getSaveFileName(this, tr("Export sprite"),
218 tr("Portable Network Graphics (*.png)"));
219 if (filename.isNull()) {
220 return;
221 }
222 CoreController::Interrupter interrupter(m_controller);
223 QImage obj = compositeObj(m_objInfo);
224 obj.save(filename, "PNG");
225}
226
227void ObjView::copyObj() {
228 CoreController::Interrupter interrupter(m_controller);
229 GBAApp::app()->clipboard()->setImage(compositeObj(m_objInfo));
230}