all repos — mgba @ 1002dfd0dbd86950145493c82ff1af7744e60c81

mGBA Game Boy Advance Emulator

src/platform/qt/AssetView.cpp (view raw)

  1/* Copyright (c) 2013-2019 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 "AssetView.h"
  7
  8#include "CoreController.h"
  9
 10#include <QTimer>
 11
 12#ifdef M_CORE_GBA
 13#include <mgba/internal/gba/gba.h>
 14#endif
 15#ifdef M_CORE_GB
 16#include <mgba/internal/gb/gb.h>
 17#include <mgba/internal/gb/io.h>
 18#endif
 19
 20#include <mgba/core/map-cache.h>
 21
 22using namespace QGBA;
 23
 24AssetView::AssetView(std::shared_ptr<CoreController> controller, QWidget* parent)
 25	: QWidget(parent)
 26	, m_cacheSet(controller->graphicCaches())
 27	, m_controller(controller)
 28{
 29	m_updateTimer.setSingleShot(true);
 30	m_updateTimer.setInterval(1);
 31	connect(&m_updateTimer, &QTimer::timeout, this, static_cast<void(AssetView::*)()>(&AssetView::updateTiles));
 32
 33	connect(controller.get(), &CoreController::frameAvailable, &m_updateTimer,
 34	        static_cast<void(QTimer::*)()>(&QTimer::start));
 35	connect(controller.get(), &CoreController::stopping, this, &AssetView::close);
 36	connect(controller.get(), &CoreController::stopping, &m_updateTimer, &QTimer::stop);
 37}
 38
 39void AssetView::updateTiles() {
 40	updateTiles(false);
 41}
 42
 43void AssetView::updateTiles(bool force) {
 44	switch (m_controller->platform()) {
 45#ifdef M_CORE_GBA
 46	case PLATFORM_GBA:
 47		updateTilesGBA(force);
 48		break;
 49#endif
 50#ifdef M_CORE_GB
 51	case PLATFORM_GB:
 52		updateTilesGB(force);
 53		break;
 54#endif
 55	default:
 56		return;
 57	}
 58}
 59
 60void AssetView::resizeEvent(QResizeEvent*) {
 61	updateTiles(true);
 62}
 63
 64void AssetView::showEvent(QShowEvent*) {
 65	updateTiles(true);
 66}
 67
 68void AssetView::compositeTile(const void* tBuffer, void* buffer, size_t stride, size_t x, size_t y, int depth) {
 69	if (!tBuffer) {
 70		return;
 71	}
 72	const uint8_t* tile = static_cast<const uint8_t*>(tBuffer);
 73	uint8_t* pixels = static_cast<uint8_t*>(buffer);
 74	size_t base = stride * y + x;
 75	switch (depth) {
 76	case 2:
 77		for (size_t i = 0; i < 8; ++i) {
 78			uint8_t tileDataLower = tile[i * 2];
 79			uint8_t tileDataUpper = tile[i * 2 + 1];
 80			uint8_t pixel;
 81			pixel = ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7);
 82			pixels[base + i * stride] = pixel;
 83			pixel = ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6);
 84			pixels[base + i * stride + 1] = pixel;
 85			pixel = ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5);
 86			pixels[base + i * stride + 2] = pixel;
 87			pixel = ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4);
 88			pixels[base + i * stride + 3] = pixel;
 89			pixel = ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3);
 90			pixels[base + i * stride + 4] = pixel;
 91			pixel = ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2);
 92			pixels[base + i * stride + 5] = pixel;
 93			pixel = (tileDataUpper & 2) | ((tileDataLower & 2) >> 1);
 94			pixels[base + i * stride + 6] = pixel;
 95			pixel = ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
 96			pixels[base + i * stride + 7] = pixel;
 97		}
 98		break;
 99	case 4:
100		for (size_t j = 0; j < 8; ++j) {
101			for (size_t i = 0; i < 4; ++i) {
102				pixels[base + j * stride + i * 2] =  tile[j * 4 + i] & 0xF;
103				pixels[base + j * stride + i * 2 + 1] =  tile[j * 4 + i] >> 4;
104			}
105		}
106		break;
107	case 8:
108		for (size_t i = 0; i < 8; ++i) {
109			memcpy(&pixels[base + i * stride], &tile[i * 8], 8);
110		}
111		break;
112	}
113}
114
115QImage AssetView::compositeMap(int map, mMapCacheEntry* mapStatus) {
116	mMapCache* mapCache = mMapCacheSetGetPointer(&m_cacheSet->maps, map);
117	int tilesW = 1 << mMapCacheSystemInfoGetTilesWide(mapCache->sysConfig);
118	int tilesH = 1 << mMapCacheSystemInfoGetTilesHigh(mapCache->sysConfig);
119	QImage rawMap = QImage(QSize(tilesW * 8, tilesH * 8), QImage::Format_ARGB32);
120	uchar* bgBits = rawMap.bits();
121	for (int j = 0; j < tilesH; ++j) {
122		for (int i = 0; i < tilesW; ++i) {
123			mMapCacheCleanTile(mapCache, mapStatus, i, j);
124		}
125		for (int i = 0; i < 8; ++i) {
126			memcpy(static_cast<void*>(&bgBits[tilesW * 32 * (i + j * 8)]), mMapCacheGetRow(mapCache, i + j * 8), tilesW * 32);
127		}
128	}
129	return rawMap.rgbSwapped();
130}
131
132QImage AssetView::compositeObj(const ObjInfo& objInfo) {
133	mTileCache* tileCache = mTileCacheSetGetPointer(&m_cacheSet->tiles, objInfo.paletteSet);
134	const color_t* rawPalette = mTileCacheGetPalette(tileCache, objInfo.paletteId);
135	unsigned colors = 1 << objInfo.bits;
136	QVector<QRgb> palette;
137
138	palette.append(rawPalette[0] & 0xFFFFFF);
139	for (unsigned c = 1; c < colors && c < 256; ++c) {
140		palette.append(rawPalette[c] | 0xFF000000);
141	}
142
143	QImage image = QImage(QSize(objInfo.width * 8, objInfo.height * 8), QImage::Format_Indexed8);
144	image.setColorTable(palette);
145	uchar* bits = image.bits();
146	unsigned t = objInfo.tile;
147	for (int y = 0; y < objInfo.height; ++y) {
148		for (int x = 0; x < objInfo.width; ++x, ++t) {
149			compositeTile(static_cast<const void*>(mTileCacheGetVRAM(tileCache, t)), bits, objInfo.width * 8, x * 8, y * 8, objInfo.bits);
150		}
151		t += objInfo.stride - objInfo.width;
152	}
153	return image.rgbSwapped();
154}
155
156bool AssetView::lookupObj(int id, struct ObjInfo* info) {
157	switch (m_controller->platform()) {
158#ifdef M_CORE_GBA
159	case PLATFORM_GBA:
160		return lookupObjGBA(id, info);
161#endif
162#ifdef M_CORE_GB
163	case PLATFORM_GB:
164		return lookupObjGB(id, info);
165#endif
166	default:
167		return false;
168	}
169}
170
171#ifdef M_CORE_GBA
172bool AssetView::lookupObjGBA(int id, struct ObjInfo* info) {
173	if (id > 127) {
174		return false;
175	}
176
177	const GBA* gba = static_cast<const GBA*>(m_controller->thread()->core->board);
178	const GBAObj* obj = &gba->video.oam.obj[id];
179
180	unsigned shape = GBAObjAttributesAGetShape(obj->a);
181	unsigned size = GBAObjAttributesBGetSize(obj->b);
182	unsigned width = GBAVideoObjSizes[shape * 4 + size][0];
183	unsigned height = GBAVideoObjSizes[shape * 4 + size][1];
184	unsigned tile = GBAObjAttributesCGetTile(obj->c);
185	unsigned palette = GBAObjAttributesCGetPalette(obj->c);
186	unsigned tileBase = tile;
187	unsigned paletteSet;
188	unsigned bits;
189	if (GBAObjAttributesAIs256Color(obj->a)) {
190		paletteSet = 3;
191		palette = 0;
192		tile /= 2;
193		bits = 8;
194	} else {
195		paletteSet = 2;
196		bits = 4;
197	}
198	ObjInfo newInfo{
199		tile,
200		width / 8,
201		height / 8,
202		width / 8,
203		palette,
204		paletteSet,
205		bits,
206		!GBAObjAttributesAIsDisable(obj->a) || GBAObjAttributesAIsTransformed(obj->a),
207		GBAObjAttributesCGetPriority(obj->c),
208		GBAObjAttributesBGetX(obj->b),
209		GBAObjAttributesAGetY(obj->a),
210		false,
211		false,
212	};
213	if (GBAObjAttributesAIsTransformed(obj->a)) {
214		int matIndex = GBAObjAttributesBGetMatIndex(obj->b);
215		const GBAOAMMatrix* mat = &gba->video.oam.mat[matIndex];
216		QTransform invXform(mat->a / 256., mat->c / 256., mat->b / 256., mat->d / 256., 0, 0);
217		newInfo.xform = invXform.inverted();
218	} else {
219		newInfo.hflip = bool(GBAObjAttributesBIsHFlip(obj->b));
220		newInfo.vflip = bool(GBAObjAttributesBIsVFlip(obj->b));
221	}
222	GBARegisterDISPCNT dispcnt = gba->memory.io[0]; // FIXME: Register name can't be imported due to namespacing issues
223	if (!GBARegisterDISPCNTIsObjCharacterMapping(dispcnt)) {
224		newInfo.stride = 0x20 >> (GBAObjAttributesAGet256Color(obj->a));
225	};
226	*info = newInfo;
227	return true;
228}
229#endif
230
231#ifdef M_CORE_GB
232bool AssetView::lookupObjGB(int id, struct ObjInfo* info) {
233	if (id > 39) {
234		return false;
235	}
236
237	const GB* gb = static_cast<const GB*>(m_controller->thread()->core->board);
238	const GBObj* obj = &gb->video.oam.obj[id];
239
240	unsigned width = 8;
241	unsigned height = 8;
242	GBRegisterLCDC lcdc = gb->memory.io[REG_LCDC];
243	if (GBRegisterLCDCIsObjSize(lcdc)) {
244		height = 16;
245	}
246	unsigned tile = obj->tile;
247	unsigned palette = 0;
248	if (gb->model >= GB_MODEL_CGB) {
249		if (GBObjAttributesIsBank(obj->attr)) {
250			tile += 512;
251		}
252		palette = GBObjAttributesGetCGBPalette(obj->attr);
253	} else {
254		palette = GBObjAttributesGetPalette(obj->attr);
255	}
256	palette += 8;
257
258	ObjInfo newInfo{
259		tile,
260		1,
261		height / 8,
262		1,
263		palette,
264		0,
265		2,
266		obj->y != 0 && obj->y < 160 && obj->x != 0 && obj->x < 168,
267		GBObjAttributesGetPriority(obj->attr),
268		obj->x - 8,
269		obj->y - 16,
270		bool(GBObjAttributesIsXFlip(obj->attr)),
271		bool(GBObjAttributesIsYFlip(obj->attr)),
272	};
273	*info = newInfo;
274	return true;
275}
276#endif
277
278bool AssetView::ObjInfo::operator!=(const ObjInfo& other) const {
279	return other.tile != tile ||
280		other.width != width ||
281		other.height != height ||
282		other.stride != stride ||
283		other.paletteId != paletteId ||
284		other.paletteSet != paletteSet;
285}