Skip to content

Commit

Permalink
Merge pull request #655 from GriffinRichards/fix-transparency
Browse files Browse the repository at this point in the history
Add setting for how to render fully transparent pixels
  • Loading branch information
GriffinRichards authored Dec 28, 2024
2 parents 3ca1ee1 + 486d1b7 commit 7a36fcc
Show file tree
Hide file tree
Showing 10 changed files with 207 additions and 56 deletions.
107 changes: 96 additions & 11 deletions forms/projectsettingseditor.ui
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@
<x>0</x>
<y>0</y>
<width>559</width>
<height>548</height>
<height>560</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_16">
Expand Down Expand Up @@ -602,7 +602,7 @@
</widget>
</item>
<item row="0" column="1">
<widget class="UIntHexSpinBox" name="spinBox_MetatileIdMask" native="true">
<widget class="UIntHexSpinBox" name="spinBox_MetatileIdMask">
<property name="toolTip">
<string>The mask used to read/write metatile IDs in map data.</string>
</property>
Expand All @@ -616,7 +616,7 @@
</widget>
</item>
<item row="1" column="1">
<widget class="UIntHexSpinBox" name="spinBox_CollisionMask" native="true">
<widget class="UIntHexSpinBox" name="spinBox_CollisionMask">
<property name="toolTip">
<string>The mask used to read/write collision values in map data.</string>
</property>
Expand All @@ -630,7 +630,7 @@
</widget>
</item>
<item row="2" column="1">
<widget class="UIntHexSpinBox" name="spinBox_ElevationMask" native="true">
<widget class="UIntHexSpinBox" name="spinBox_ElevationMask">
<property name="toolTip">
<string>The mask used to read/write elevation values in map data.</string>
</property>
Expand Down Expand Up @@ -742,7 +742,7 @@
<x>0</x>
<y>0</y>
<width>559</width>
<height>568</height>
<height>798</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
Expand Down Expand Up @@ -775,6 +775,86 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_TransparentPixelRendering">
<property name="title">
<string>Transparent Pixel Rendering</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_22">
<item>
<widget class="QRadioButton" name="radioButton_RenderBlack">
<property name="toolTip">
<string>Fully transparent pixels will be rendered as black pixels (the Pokémon games do this by default)</string>
</property>
<property name="text">
<string>Render as black</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_RenderFirstPalColor">
<property name="toolTip">
<string>Fully transparent pixels will be rendered using the first palette color (this the default behavior for the GBA)</string>
</property>
<property name="text">
<string>Render using first palette color</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_UnusedLayerRendering">
<property name="title">
<string>Unused Layer Rendering</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_UnusedTileNorma">
<property name="text">
<string>Normal</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="UIntHexSpinBox" name="spinBox_UnusedTileNormal">
<property name="toolTip">
<string>This raw tile value will be used to fill the unused bottom layer of Normal metatiles</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_UnusedTileCovered">
<property name="text">
<string>Covered</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="UIntHexSpinBox" name="spinBox_UnusedTileCovered">
<property name="toolTip">
<string>This raw tile value will be used to fill the unused top layer of Covered metatiles</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_UnusedTileSplit">
<property name="text">
<string>Split</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="UIntHexSpinBox" name="spinBox_UnusedTileSplit">
<property name="toolTip">
<string>This raw tile value will be used to fill the unused middle layer of Split metatiles</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_WarningTilesetsTab">
<property name="styleSheet">
Expand Down Expand Up @@ -810,14 +890,14 @@
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="4" column="1">
<widget class="UIntHexSpinBox" name="spinBox_LayerTypeMask" native="true">
<widget class="UIntHexSpinBox" name="spinBox_LayerTypeMask">
<property name="toolTip">
<string>The mask used to read/write Layer Type from the metatile's attributes data. If 0, this attribute is disabled.</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="UIntHexSpinBox" name="spinBox_BehaviorMask" native="true">
<widget class="UIntHexSpinBox" name="spinBox_BehaviorMask">
<property name="toolTip">
<string>The mask used to read/write Metatile Behavior from the metatile's attributes data. If 0, this attribute is disabled.</string>
</property>
Expand Down Expand Up @@ -864,7 +944,7 @@
</widget>
</item>
<item row="7" column="1">
<widget class="UIntHexSpinBox" name="spinBox_TerrainTypeMask" native="true">
<widget class="UIntHexSpinBox" name="spinBox_TerrainTypeMask">
<property name="toolTip">
<string>The mask used to read/write Terrain Type from the metatile's attributes data. If 0, this attribute is disabled.</string>
</property>
Expand All @@ -891,7 +971,7 @@
</widget>
</item>
<item row="5" column="1">
<widget class="UIntHexSpinBox" name="spinBox_EncounterTypeMask" native="true">
<widget class="UIntHexSpinBox" name="spinBox_EncounterTypeMask">
<property name="toolTip">
<string>The mask used to read/write Encounter Type from the metatile's attributes data. If 0, this attribute is disabled.</string>
</property>
Expand Down Expand Up @@ -1549,10 +1629,15 @@
<header>noscrollspinbox.h</header>
</customwidget>
<customwidget>
<class>UIntHexSpinBox</class>
<extends>QWidget</extends>
<class>UIntSpinBox</class>
<extends>QAbstractSpinBox</extends>
<header>uintspinbox.h</header>
</customwidget>
<customwidget>
<class>UIntHexSpinBox</class>
<extends>UIntSpinBox</extends>
<header location="global">uintspinbox.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../resources/images.qrc"/>
Expand Down
8 changes: 8 additions & 0 deletions include/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ class ProjectConfig: public KeyValueConfigBase
this->prefabImportPrompted = false;
this->tilesetsHaveCallback = true;
this->tilesetsHaveIsCompressed = true;
this->setTransparentPixelsBlack = true;
this->filePaths.clear();
this->eventIconPaths.clear();
this->pokemonIconPaths.clear();
Expand All @@ -310,6 +311,9 @@ class ProjectConfig: public KeyValueConfigBase
this->blockMetatileIdMask = 0x03FF;
this->blockCollisionMask = 0x0C00;
this->blockElevationMask = 0xF000;
this->unusedTileNormal = 0x3014;
this->unusedTileCovered = 0x0000;
this->unusedTileSplit = 0x0000;
this->identifiers.clear();
this->readKeys.clear();
}
Expand Down Expand Up @@ -362,6 +366,7 @@ class ProjectConfig: public KeyValueConfigBase
bool prefabImportPrompted;
bool tilesetsHaveCallback;
bool tilesetsHaveIsCompressed;
bool setTransparentPixelsBlack;
int metatileAttributesSize;
uint32_t metatileBehaviorMask;
uint32_t metatileTerrainTypeMask;
Expand All @@ -370,6 +375,9 @@ class ProjectConfig: public KeyValueConfigBase
uint16_t blockMetatileIdMask;
uint16_t blockCollisionMask;
uint16_t blockElevationMask;
uint16_t unusedTileNormal;
uint16_t unusedTileCovered;
uint16_t unusedTileSplit;
bool mapAllowFlagsEnabled;
QString collisionSheetPath;
int collisionSheetWidth;
Expand Down
2 changes: 2 additions & 0 deletions include/core/tile.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class Tile
uint16_t rawValue() const;

static int getIndexInTileset(int);

static const uint16_t maxValue;
};

inline bool operator==(const Tile &a, const Tile &b) {
Expand Down
4 changes: 2 additions & 2 deletions include/ui/imageproviders.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

QImage getCollisionMetatileImage(Block);
QImage getCollisionMetatileImage(int, int);
QImage getMetatileImage(uint16_t, Tileset*, Tileset*, QList<int>, QList<float>, bool useTruePalettes = false);
QImage getMetatileImage(Metatile*, Tileset*, Tileset*, QList<int>, QList<float>, bool useTruePalettes = false);
QImage getMetatileImage(uint16_t, Tileset*, Tileset*, const QList<int>&, const QList<float>&, bool useTruePalettes = false);
QImage getMetatileImage(Metatile*, Tileset*, Tileset*, const QList<int>&, const QList<float>&, bool useTruePalettes = false);
QImage getTileImage(uint16_t, Tileset*, Tileset*);
QImage getPalettedTileImage(uint16_t, Tileset*, Tileset*, int, bool useTruePalettes = false);
QImage getGreyscaleTileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset);
Expand Down
12 changes: 12 additions & 0 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,12 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
this->blockCollisionMask = getConfigUint32(key, value, 0, Block::maxValue);
} else if (key == "block_elevation_mask") {
this->blockElevationMask = getConfigUint32(key, value, 0, Block::maxValue);
} else if (key == "unused_tile_normal") {
this->unusedTileNormal = getConfigUint32(key, value, 0, Tile::maxValue);
} else if (key == "unused_tile_covered") {
this->unusedTileCovered = getConfigUint32(key, value, 0, Tile::maxValue);
} else if (key == "unused_tile_split") {
this->unusedTileSplit = getConfigUint32(key, value, 0, Tile::maxValue);
} else if (key == "enable_map_allow_flags") {
this->mapAllowFlagsEnabled = getConfigBool(key, value);
#ifdef CONFIG_BACKWARDS_COMPATABILITY
Expand Down Expand Up @@ -752,6 +758,8 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
this->tilesetsHaveCallback = getConfigBool(key, value);
} else if (key == "tilesets_have_is_compressed") {
this->tilesetsHaveIsCompressed = getConfigBool(key, value);
} else if (key == "set_transparent_pixels_black") {
this->setTransparentPixelsBlack = getConfigBool(key, value);
} else if (key == "event_icon_path_object") {
this->eventIconPaths[Event::Group::Object] = value;
} else if (key == "event_icon_path_warp") {
Expand Down Expand Up @@ -839,6 +847,7 @@ QMap<QString, QString> ProjectConfig::getKeyValueMap() {
}
map.insert("tilesets_have_callback", QString::number(this->tilesetsHaveCallback));
map.insert("tilesets_have_is_compressed", QString::number(this->tilesetsHaveIsCompressed));
map.insert("set_transparent_pixels_black", QString::number(this->setTransparentPixelsBlack));
map.insert("metatile_attributes_size", QString::number(this->metatileAttributesSize));
map.insert("metatile_behavior_mask", "0x" + QString::number(this->metatileBehaviorMask, 16).toUpper());
map.insert("metatile_terrain_type_mask", "0x" + QString::number(this->metatileTerrainTypeMask, 16).toUpper());
Expand All @@ -847,6 +856,9 @@ QMap<QString, QString> ProjectConfig::getKeyValueMap() {
map.insert("block_metatile_id_mask", "0x" + QString::number(this->blockMetatileIdMask, 16).toUpper());
map.insert("block_collision_mask", "0x" + QString::number(this->blockCollisionMask, 16).toUpper());
map.insert("block_elevation_mask", "0x" + QString::number(this->blockElevationMask, 16).toUpper());
map.insert("unused_tile_normal", "0x" + QString::number(this->unusedTileNormal, 16).toUpper());
map.insert("unused_tile_covered", "0x" + QString::number(this->unusedTileCovered, 16).toUpper());
map.insert("unused_tile_split", "0x" + QString::number(this->unusedTileSplit, 16).toUpper());
map.insert("enable_map_allow_flags", QString::number(this->mapAllowFlagsEnabled));
map.insert("event_icon_path_object", this->eventIconPaths[Event::Group::Object]);
map.insert("event_icon_path_warp", this->eventIconPaths[Event::Group::Warp]);
Expand Down
29 changes: 20 additions & 9 deletions src/core/tile.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
#include "tile.h"
#include "project.h"
#include "bitpacker.h"

// Upper limit for raw value (i.e., uint16_t max).
const uint16_t Tile::maxValue = 0xFFFF;

// At the moment these are fixed, and not exposed to the user.
// We're only using them for convenience when converting between raw values.
// The actual job of clamping Tile's members to correct values is handled by the widths in the bit field.
const BitPacker bitsTileId = BitPacker(0x03FF);
const BitPacker bitsXFlip = BitPacker(0x0400);
const BitPacker bitsYFlip = BitPacker(0x0800);
const BitPacker bitsPalette = BitPacker(0xF000);

Tile::Tile() :
tileId(0),
Expand All @@ -16,18 +28,17 @@
{ }

Tile::Tile(uint16_t raw) :
tileId(raw & 0x3FF),
xflip((raw >> 10) & 1),
yflip((raw >> 11) & 1),
palette((raw >> 12) & 0xF)
tileId(bitsTileId.unpack(raw)),
xflip(bitsXFlip.unpack(raw)),
yflip(bitsYFlip.unpack(raw)),
palette(bitsPalette.unpack(raw))
{ }

uint16_t Tile::rawValue() const {
return static_cast<uint16_t>(
(this->tileId & 0x3FF)
| ((this->xflip & 1) << 10)
| ((this->yflip & 1) << 11)
| ((this->palette & 0xF) << 12));
return bitsTileId.pack(this->tileId)
| bitsXFlip.pack(this->xflip)
| bitsYFlip.pack(this->yflip)
| bitsPalette.pack(this->palette);
}

int Tile::getIndexInTileset(int tileId) {
Expand Down
28 changes: 19 additions & 9 deletions src/project.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2096,19 +2096,29 @@ bool Project::readFieldmapProperties() {
fileWatcher.addPath(root + "/" + filename);
const QMap<QString, int> defines = parser.readCDefinesByName(filename, names);

auto loadDefine = [defines](const QString name, int * dest) {
auto loadDefine = [defines](const QString name, int * dest, int min, int max) {
auto it = defines.find(name);
if (it != defines.end()) {
*dest = it.value();
if (*dest < min) {
logWarn(QString("Value for tileset property '%1' (%2) is below the minimum (%3). Defaulting to minimum.").arg(name).arg(*dest).arg(min));
*dest = min;
} else if (*dest > max) {
logWarn(QString("Value for tileset property '%1' (%2) is above the maximum (%3). Defaulting to maximum.").arg(name).arg(*dest).arg(max));
*dest = max;
}
} else {
logWarn(QString("Value for tileset property '%1' not found. Using default (%2) instead.").arg(name).arg(*dest));
}
};
loadDefine(numTilesPrimaryName, &Project::num_tiles_primary);
loadDefine(numTilesTotalName, &Project::num_tiles_total);
loadDefine(numMetatilesPrimaryName, &Project::num_metatiles_primary);
loadDefine(numPalsPrimaryName, &Project::num_pals_primary);
loadDefine(numPalsTotalName, &Project::num_pals_total);
loadDefine(numPalsTotalName, &Project::num_pals_total, 2, INT_MAX); // In reality the max would be 16, but as far as Porymap is concerned it doesn't matter.
loadDefine(numTilesTotalName, &Project::num_tiles_total, 2, 1024); // 1024 is fixed because we store tile IDs in a 10-bit field.
loadDefine(numPalsPrimaryName, &Project::num_pals_primary, 1, Project::num_pals_total - 1);
loadDefine(numTilesPrimaryName, &Project::num_tiles_primary, 1, Project::num_tiles_total - 1);

// This maximum is overly generous, because until we parse the appropriate masks from the project
// we don't actually know what the maximum number of metatiles is.
loadDefine(numMetatilesPrimaryName, &Project::num_metatiles_primary, 1, 0xFFFF - 1);

auto it = defines.find(maxMapSizeName);
if (it != defines.end()) {
Expand Down Expand Up @@ -3020,12 +3030,12 @@ void Project::applyParsedLimits() {
Block::setLayout();
Metatile::setLayout(this);

Project::num_metatiles_primary = qMin(Project::num_metatiles_primary, Block::getMaxMetatileId() + 1);
Project::num_metatiles_primary = qMin(qMax(Project::num_metatiles_primary, 1), Block::getMaxMetatileId() + 1);
projectConfig.defaultMetatileId = qMin(projectConfig.defaultMetatileId, Block::getMaxMetatileId());
projectConfig.defaultElevation = qMin(projectConfig.defaultElevation, Block::getMaxElevation());
projectConfig.defaultCollision = qMin(projectConfig.defaultCollision, Block::getMaxCollision());
projectConfig.collisionSheetHeight = qMin(projectConfig.collisionSheetHeight, Block::getMaxElevation() + 1);
projectConfig.collisionSheetWidth = qMin(projectConfig.collisionSheetWidth, Block::getMaxCollision() + 1);
projectConfig.collisionSheetHeight = qMin(qMax(projectConfig.collisionSheetHeight, 1), Block::getMaxElevation() + 1);
projectConfig.collisionSheetWidth = qMin(qMax(projectConfig.collisionSheetWidth, 1), Block::getMaxCollision() + 1);
}

bool Project::hasUnsavedChanges() {
Expand Down
Loading

0 comments on commit 7a36fcc

Please sign in to comment.