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