all repos — mgba @ 67a135e5e7a5d6366f516972f25e7c71bff91447

mGBA Game Boy Advance Emulator

Qt: Add layer placement features
Vicki Pfau vi@endrift.com
Thu, 26 Apr 2018 18:38:02 -0700
commit

67a135e5e7a5d6366f516972f25e7c71bff91447

parent

8ea524d9e61334d7d94dd12b9f8a1cd84f0b67ec

M CHANGESCHANGES

@@ -49,6 +49,7 @@ - Libretro: Add frameskip option

- GBA Memory: 64 MiB GBA Video cartridge support - PSP2: Use system enter key by default - 3DS: Remove deprecated CSND interface + - Qt: Options to mess around with layer placement 0.6.3: (2017-04-14) Bugfixes:
M include/mgba/core/core.hinclude/mgba/core/core.h

@@ -153,6 +153,7 @@ size_t (*listVideoLayers)(const struct mCore*, const struct mCoreChannelInfo**);

size_t (*listAudioChannels)(const struct mCore*, const struct mCoreChannelInfo**); void (*enableVideoLayer)(struct mCore*, size_t id, bool enable); void (*enableAudioChannel)(struct mCore*, size_t id, bool enable); + void (*adjustVideoLayer)(struct mCore*, size_t id, int32_t x, int32_t y); #ifndef MINIMAL_CORE void (*startVideoLog)(struct mCore*, struct mVideoLogContext*);
M include/mgba/internal/gb/renderers/software.hinclude/mgba/internal/gb/renderers/software.h

@@ -38,6 +38,13 @@

GBRegisterLCDC lcdc; enum GBModel model; + int16_t objOffsetX; + int16_t objOffsetY; + int16_t offsetScx; + int16_t offsetScy; + int16_t offsetWx; + int16_t offsetWy; + int sgbTransfer; uint8_t sgbPacket[128]; uint8_t sgbCommandHeader;
M include/mgba/internal/gba/renderers/video-software.hinclude/mgba/internal/gba/renderers/video-software.h

@@ -44,6 +44,8 @@ int32_t sx;

int32_t sy; int yCache; uint16_t mapCache[64]; + int32_t offsetX; + int32_t offsetY; }; enum BlendEffect {

@@ -159,6 +161,8 @@

int oamDirty; int oamMax; struct GBAVideoSoftwareSprite sprites[128]; + int16_t objOffsetX; + int16_t objOffsetY; uint32_t scanlineDirty[5]; uint16_t nextIo[REG_SOUND1CNT_LO];
M src/gb/core.csrc/gb/core.c

@@ -791,13 +791,17 @@ }

static size_t _GBCoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) { UNUSED(core); - *info = _GBVideoLayers; + if (info) { + *info = _GBVideoLayers; + } return sizeof(_GBVideoLayers) / sizeof(*_GBVideoLayers); } static size_t _GBCoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) { UNUSED(core); - *info = _GBAudioChannels; + if (info) { + *info = _GBAudioChannels; + } return sizeof(_GBAudioChannels) / sizeof(*_GBAudioChannels); }

@@ -829,6 +833,26 @@ gb->audio.forceDisableCh[id] = !enable;

break; default: break; + } +} + +static void _GBCoreAdjustVideoLayer(struct mCore* core, size_t id, int32_t x, int32_t y) { + struct GBCore* gbcore = (struct GBCore*) core; + switch (id) { + case 0: + gbcore->renderer.offsetScx = x; + gbcore->renderer.offsetScy = y; + break; + case 1: + gbcore->renderer.offsetWx = x; + gbcore->renderer.offsetWy = y; + break; + case 2: + gbcore->renderer.objOffsetX = x; + gbcore->renderer.objOffsetY = y; + break; + default: + return; } }

@@ -934,6 +958,7 @@ core->listVideoLayers = _GBCoreListVideoLayers;

core->listAudioChannels = _GBCoreListAudioChannels; core->enableVideoLayer = _GBCoreEnableVideoLayer; core->enableAudioChannel = _GBCoreEnableAudioChannel; + core->adjustVideoLayer = _GBCoreAdjustVideoLayer; #ifndef MINIMAL_CORE core->startVideoLog = _GBCoreStartVideoLog; core->endVideoLog = _GBCoreEndVideoLog;
M src/gb/renderers/software.csrc/gb/renderers/software.c

@@ -198,6 +198,13 @@ softwareRenderer->model = model;

softwareRenderer->sgbTransfer = 0; softwareRenderer->sgbCommandHeader = 0; softwareRenderer->sgbBorders = sgbBorders; + softwareRenderer->objOffsetX = 0; + softwareRenderer->objOffsetY = 0; + softwareRenderer->offsetScx = 0; + softwareRenderer->offsetScy = 0; + softwareRenderer->offsetWx = 0; + softwareRenderer->offsetWy = 0; + int i; for (i = 0; i < 64; ++i) { softwareRenderer->lookup[i] = i;

@@ -471,7 +478,7 @@ if (GBRegisterLCDCIsBgEnable(softwareRenderer->lcdc) || softwareRenderer->model >= GB_MODEL_CGB) {

int wy = softwareRenderer->wy + softwareRenderer->currentWy; if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && wy <= y && endX >= softwareRenderer->wx - 7) { if (softwareRenderer->wx - 7 > 0 && !softwareRenderer->d.disableBG) { - GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, softwareRenderer->wx - 7, softwareRenderer->scx, softwareRenderer->scy + y); + GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, softwareRenderer->wx - 7, softwareRenderer->scx - softwareRenderer->offsetScx, softwareRenderer->scy + y - softwareRenderer->offsetScy); } maps = &softwareRenderer->d.vram[GB_BASE_MAP];

@@ -479,10 +486,10 @@ if (GBRegisterLCDCIsWindowTileMap(softwareRenderer->lcdc)) {

maps += GB_SIZE_MAP; } if (!softwareRenderer->d.disableWIN) { - GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx, y - wy); + GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx - softwareRenderer->offsetWx, y - wy - softwareRenderer->offsetWy); } } else if (!softwareRenderer->d.disableBG) { - GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, softwareRenderer->scx, softwareRenderer->scy + y); + GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, softwareRenderer->scx - softwareRenderer->offsetScx, softwareRenderer->scy + y - softwareRenderer->offsetScy); } } else if (!softwareRenderer->d.disableBG) { memset(&softwareRenderer->row[startX], 0, endX - startX);

@@ -778,15 +785,16 @@ }

} static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y) { - int ix = obj->x - 8; + int objX = obj->x + renderer->objOffsetX; + int ix = objX - 8; if (endX < ix || startX >= ix + 8) { return; } - if (obj->x < endX) { - endX = obj->x; + if (objX < endX) { + endX = objX; } - if (obj->x - 8 > startX) { - startX = obj->x - 8; + if (objX - 8 > startX) { + startX = objX - 8; } if (startX < 0) { startX = 0;

@@ -794,14 +802,15 @@ }

uint8_t* data = renderer->d.vram; int tileOffset = 0; int bottomY; + int objY = obj->y + renderer->objOffsetY; if (GBObjAttributesIsYFlip(obj->attr)) { - bottomY = 7 - ((y - obj->y - 16) & 7); - if (GBRegisterLCDCIsObjSize(renderer->lcdc) && y - obj->y < -8) { + bottomY = 7 - ((y - objY - 16) & 7); + if (GBRegisterLCDCIsObjSize(renderer->lcdc) && y - objY < -8) { ++tileOffset; } } else { - bottomY = (y - obj->y - 16) & 7; - if (GBRegisterLCDCIsObjSize(renderer->lcdc) && y - obj->y >= -8) { + bottomY = (y - objY - 16) & 7; + if (GBRegisterLCDCIsObjSize(renderer->lcdc) && y - objY >= -8) { ++tileOffset; } }

@@ -825,12 +834,12 @@ p = (GBObjAttributesGetPalette(obj->attr) + 8) * 4;

} int bottomX; int x = startX; - if ((x - obj->x) & 7) { + if ((x - objX) & 7) { for (; x < endX; ++x) { if (GBObjAttributesIsXFlip(obj->attr)) { - bottomX = (x - obj->x) & 7; + bottomX = (x - objX) & 7; } else { - bottomX = 7 - ((x - obj->x) & 7); + bottomX = 7 - ((x - objX) & 7); } int objTile = obj->tile + tileOffset; uint8_t tileDataLower = data[(objTile * 8 + bottomY) * 2];
M src/gba/core.csrc/gba/core.c

@@ -795,13 +795,17 @@ }

static size_t _GBACoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) { UNUSED(core); - *info = _GBAVideoLayers; + if (info) { + *info = _GBAVideoLayers; + } return sizeof(_GBAVideoLayers) / sizeof(*_GBAVideoLayers); } static size_t _GBACoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) { UNUSED(core); - *info = _GBAAudioChannels; + if (info) { + *info = _GBAAudioChannels; + } return sizeof(_GBAAudioChannels) / sizeof(*_GBAAudioChannels); }

@@ -839,6 +843,27 @@ break;

default: break; } +} + +static void _GBACoreAdjustVideoLayer(struct mCore* core, size_t id, int32_t x, int32_t y) { + struct GBACore* gbacore = (struct GBACore*) core; + switch (id) { + case 0: + case 1: + case 2: + case 3: + gbacore->renderer.bg[id].offsetX = x; + gbacore->renderer.bg[id].offsetY = y; + break; + case 4: + gbacore->renderer.objOffsetX = x; + gbacore->renderer.objOffsetY = y; + gbacore->renderer.oamDirty = 1; + break; + default: + return; + } + memset(gbacore->renderer.scanlineDirty, 0xFFFFFFFF, sizeof(gbacore->renderer.scanlineDirty)); } #ifndef MINIMAL_CORE

@@ -946,6 +971,7 @@ core->listVideoLayers = _GBACoreListVideoLayers;

core->listAudioChannels = _GBACoreListAudioChannels; core->enableVideoLayer = _GBACoreEnableVideoLayer; core->enableAudioChannel = _GBACoreEnableAudioChannel; + core->adjustVideoLayer = _GBACoreAdjustVideoLayer; #ifndef MINIMAL_CORE core->startVideoLog = _GBACoreStartVideoLog; core->endVideoLog = _GBACoreEndVideoLog;
M src/gba/renderers/software-mode0.csrc/gba/renderers/software-mode0.c

@@ -446,13 +446,13 @@ return; \

} void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) { - int inX = (renderer->start + background->x) & 0x1FF; + int inX = (renderer->start + background->x - background->offsetX) & 0x1FF; int length = renderer->end - renderer->start; if (background->mosaic) { int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1; y -= y % mosaicV; } - int inY = y + background->y; + int inY = y + background->y - background->offsetY; uint16_t mapData; unsigned yBase = inY & 0xF8;
M src/gba/renderers/software-obj.csrc/gba/renderers/software-obj.c

@@ -147,6 +147,7 @@ return 0;

} int32_t x = (uint32_t) GBAObjAttributesBGetX(sprite->b) << 23; x >>= 23; + x += renderer->objOffsetX; uint16_t* vramBase = &renderer->d.vram[BASE_TILE >> 1]; bool align = GBAObjAttributesAIs256Color(sprite->a) && !GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt); unsigned charBase = (GBAObjAttributesCGetTile(sprite->c) & ~align) * 0x20;

@@ -185,7 +186,7 @@ objwinPalette = palette;

} } - int inY = y - (int) GBAObjAttributesAGetY(sprite->a); + int inY = y - ((int) GBAObjAttributesAGetY(sprite->a) + renderer->objOffsetY); int stride = GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? (width >> !GBAObjAttributesAIs256Color(sprite->a)) : 0x80; uint32_t current;
M src/gba/renderers/video-software.csrc/gba/renderers/video-software.c

@@ -114,6 +114,9 @@

softwareRenderer->mosaic = 0; softwareRenderer->nextY = 0; + softwareRenderer->objOffsetX = 0; + softwareRenderer->objOffsetY = 0; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); memset(softwareRenderer->cache, 0, sizeof(softwareRenderer->cache)); memset(softwareRenderer->nextIo, 0, sizeof(softwareRenderer->nextIo));

@@ -142,6 +145,8 @@ bg->dmy = 256;

bg->sx = 0; bg->sy = 0; bg->yCache = -1; + bg->offsetX = 0; + bg->offsetY = 0; } }

@@ -507,8 +512,9 @@ if (GBAObjAttributesAIsTransformed(obj.a)) {

height <<= GBAObjAttributesAGetDoubleSize(obj.a); } if (GBAObjAttributesAGetY(obj.a) < VIDEO_VERTICAL_PIXELS || GBAObjAttributesAGetY(obj.a) + height >= VIDEO_VERTICAL_TOTAL_PIXELS) { - renderer->sprites[oamMax].y = GBAObjAttributesAGetY(obj.a); - renderer->sprites[oamMax].endY = GBAObjAttributesAGetY(obj.a) + height; + int y = GBAObjAttributesAGetY(obj.a) + renderer->objOffsetY; + renderer->sprites[oamMax].y = y; + renderer->sprites[oamMax].endY = y + height; renderer->sprites[oamMax].obj = obj; ++oamMax; }
M src/platform/qt/CMakeLists.txtsrc/platform/qt/CMakeLists.txt

@@ -102,6 +102,7 @@ MultiplayerController.cpp

ObjView.cpp OverrideView.cpp PaletteView.cpp + PlacementControl.cpp PrinterView.cpp RegisterView.cpp ROMInfo.cpp

@@ -135,6 +136,7 @@ MemoryView.ui

ObjView.ui OverrideView.ui PaletteView.ui + PlacementControl.ui PrinterView.ui ROMInfo.ui SensorView.ui
A src/platform/qt/PlacementControl.cpp

@@ -0,0 +1,76 @@

+/* Copyright (c) 2013-2018 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "PlacementControl.h" + +#include "CoreController.h" + +#include <QGridLayout> + +#include <mgba/core/core.h> + +using namespace QGBA; + +PlacementControl::PlacementControl(std::shared_ptr<CoreController> controller, QWidget* parent) + : QDialog(parent) + , m_controller(controller) +{ + m_ui.setupUi(this); + + connect(m_ui.offsetX, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this](int x) { + adjustLayer(-1, x, m_ui.offsetY->value()); + }); + + connect(m_ui.offsetY, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this](int y) { + adjustLayer(-1, m_ui.offsetX->value(), y); + }); + + QGridLayout* grid = static_cast<QGridLayout*>(layout()); + CoreController::Interrupter interrupter(m_controller); + const mCoreChannelInfo* info; + size_t nVideo = m_controller->thread()->core->listVideoLayers(m_controller->thread()->core, &info); + for (size_t i = 0; i < nVideo; ++i) { + QSpinBox* offsetX = new QSpinBox; + QSpinBox* offsetY = new QSpinBox; + + offsetX->setWrapping(true); + offsetX->setMaximum(127); + offsetX->setMinimum(-128); + offsetX->setAccelerated(true); + + offsetY->setWrapping(true); + offsetY->setMaximum(127); + offsetY->setMinimum(-128); + offsetY->setAccelerated(true); + + m_layers.append(qMakePair(offsetX, offsetY)); + int row = grid->rowCount(); + grid->addWidget(new QLabel(QString(info[i].visibleName)), row, 0, Qt::AlignRight); + grid->addWidget(offsetX, row, 1); + grid->addWidget(offsetY, row, 2); + + connect(offsetX, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this, i, offsetY](int x) { + adjustLayer(i, x, offsetY->value()); + }); + + connect(offsetY, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this, i, offsetX](int y) { + adjustLayer(i, offsetX->value(), y); + }); + } +} + +void PlacementControl::adjustLayer(int layer, int32_t x, int32_t y) { + CoreController::Interrupter interrupter(m_controller); + mCore* core = m_controller->thread()->core; + size_t nVideo = core->listVideoLayers(core, nullptr); + + if (layer < 0) { + for (size_t i = 0; i < nVideo; ++i) { + core->adjustVideoLayer(core, i, x + m_layers[i].first->value(), y + m_layers[i].second->value()); + } + } else if ((size_t) layer < nVideo) { + core->adjustVideoLayer(core, layer, x + m_ui.offsetX->value(), y + m_ui.offsetY->value()); + } +}
A src/platform/qt/PlacementControl.h

@@ -0,0 +1,34 @@

+/* Copyright (c) 2013-2018 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#pragma once + +#include <QDialog> +#include <QList> + +#include <memory> + +#include "ui_PlacementControl.h" + +namespace QGBA { + +class CoreController; + +class PlacementControl : public QDialog { +Q_OBJECT + +public: + PlacementControl(std::shared_ptr<CoreController>, QWidget* parent = nullptr); + +private: + void adjustLayer(int layer, int32_t x, int32_t y); + + std::shared_ptr<CoreController> m_controller; + QList<QPair<QSpinBox*, QSpinBox*>> m_layers; + + Ui::PlacementControl m_ui; +}; + +}
A src/platform/qt/PlacementControl.ui

@@ -0,0 +1,87 @@

+<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>PlacementControl</class> + <widget class="QDialog" name="PlacementControl"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>202</width> + <height>72</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string>Adjust placement</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="1" column="0" alignment="Qt::AlignRight"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>All</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QSpinBox" name="offsetX"> + <property name="wrapping"> + <bool>true</bool> + </property> + <property name="accelerated"> + <bool>true</bool> + </property> + <property name="minimum"> + <number>-128</number> + </property> + <property name="maximum"> + <number>127</number> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QSpinBox" name="offsetY"> + <property name="wrapping"> + <bool>true</bool> + </property> + <property name="accelerated"> + <bool>true</bool> + </property> + <property name="minimum"> + <number>-128</number> + </property> + <property name="maximum"> + <number>127</number> + </property> + </widget> + </item> + <item row="0" column="0" alignment="Qt::AlignRight"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Offset</string> + </property> + </widget> + </item> + <item row="0" column="1" alignment="Qt::AlignHCenter"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>X</string> + </property> + </widget> + </item> + <item row="0" column="2" alignment="Qt::AlignHCenter"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Y</string> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui>
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -42,6 +42,7 @@ #include "MultiplayerController.h"

#include "OverrideView.h" #include "ObjView.h" #include "PaletteView.h" +#include "PlacementControl.h" #include "PrinterView.h" #include "ROMInfo.h" #include "SensorView.h"

@@ -1444,6 +1445,11 @@ m_shortcutController->addMenu(m_videoLayers, avMenu);

m_audioChannels = avMenu->addMenu(tr("Audio channels")); m_shortcutController->addMenu(m_audioChannels, avMenu); + + QAction* placementControl = new QAction(tr("Adjust layer placement..."), avMenu); + connect(placementControl, &QAction::triggered, openControllerTView<PlacementControl>()); + m_gameActions.append(placementControl); + addControlledAction(avMenu, placementControl, "placementControl"); QMenu* toolsMenu = menubar->addMenu(tr("&Tools")); m_shortcutController->addMenu(toolsMenu);