diff --git a/include/ui/imageproviders.h b/include/ui/imageproviders.h index 0cfe8511..4bdfe801 100644 --- a/include/ui/imageproviders.h +++ b/include/ui/imageproviders.h @@ -10,15 +10,35 @@ class Layout; QImage getCollisionMetatileImage(Block); QImage getCollisionMetatileImage(int, int); + QImage getMetatileImage(uint16_t, Layout*, bool useTruePalettes = false); QImage getMetatileImage(Metatile*, Layout*, bool useTruePalettes = false); QImage getMetatileImage(uint16_t, Tileset*, Tileset*, const QList&, const QList& = {}, bool useTruePalettes = false); QImage getMetatileImage(Metatile*, Tileset*, Tileset*, const QList&, const QList& = {}, bool useTruePalettes = false); -QImage getMetatileSheetImage(Layout *, int, bool useTruePalettes = false); -QImage getMetatileSheetImage(Tileset *, Tileset *, uint16_t, int, int, const QList &, const QList & = {}, const QSize &size = Metatile::pixelSize(), bool useTruePalettes = false); + +QImage getMetatileSheetImage(Layout *layout, int numMetatilesWIde, bool useTruePalettes = false); +QImage getMetatileSheetImage(Tileset *primaryTileset, + Tileset *secondaryTileset, + uint16_t metatileIdStart, + uint16_t metatileIdEnd, + int numMetatilesWIde, + const QList &layerOrder, + const QList &layerOpacity = {}, + const QSize &metatileSize = Metatile::pixelSize(), + bool useTruePalettes = false); +QImage getMetatileSheetImage(Tileset *primaryTileset, + Tileset *secondaryTileset, + int numMetatilesWide, + const QList &layerOrder, + const QList &layerOpacity = {}, + const QSize &metatileSize = Metatile::pixelSize(), + 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); + void flattenTo4bppImage(QImage * image); static QList greyscalePalette({ diff --git a/src/ui/imageproviders.cpp b/src/ui/imageproviders.cpp index 0997c1e1..ef966852 100644 --- a/src/ui/imageproviders.cpp +++ b/src/ui/imageproviders.cpp @@ -48,6 +48,12 @@ QImage getMetatileImage( useTruePalettes); } +// The color to use when we want to show some portion of the image request was invalid. +// Normally this is Qt::magenta, but we'll use Qt::transparent if we think the image allows it. +QColor getInvalidImageColor() { + return (projectConfig.transparencyColor == QColor(Qt::transparent)) ? QColor(Qt::transparent) : QColor(Qt::magenta); +} + QImage getMetatileImage( Metatile *metatile, Tileset *primaryTileset, @@ -58,7 +64,7 @@ QImage getMetatileImage( { QImage metatile_image(Metatile::pixelWidth(), Metatile::pixelHeight(), QImage::Format_RGBA8888); if (!metatile) { - metatile_image.fill(projectConfig.transparencyColor == QColor(Qt::transparent) ? projectConfig.transparencyColor : QColor(Qt::magenta)); + metatile_image.fill(getInvalidImageColor()); return metatile_image; } @@ -192,56 +198,93 @@ void flattenTo4bppImage(QImage * image) { *pixel %= 16; } -QImage getMetatileSheetImage(Layout *layout, int numMetatilesWide, bool useTruePalettes) { - return getMetatileSheetImage(layout->tileset_primary, - layout->tileset_secondary, - 0, - -1, - numMetatilesWide, - layout->metatileLayerOrder(), - layout->metatileLayerOpacity(), - Metatile::pixelSize(), - useTruePalettes); -} - +// Constructs a grid image of the metatiles in the specified ID range. QImage getMetatileSheetImage(Tileset *primaryTileset, Tileset *secondaryTileset, uint16_t metatileIdStart, - int numMetatilesToDraw, + uint16_t metatileIdEnd, int numMetatilesWide, const QList &layerOrder, const QList &layerOpacity, const QSize &metatileSize, bool useTruePalettes) { - // We round up the number of primary metatiles to keep the tilesets on separate rows. - int numPrimary = Util::roundUpToMultiple(primaryTileset ? primaryTileset->numMetatiles() : 0, numMetatilesWide); - int maxPrimary = Project::getNumMetatilesPrimary(); - bool includesPrimary = metatileIdStart < maxPrimary; + if (metatileIdEnd < metatileIdStart || numMetatilesWide == 0) + return QImage(); - // Negative values are used to indicate 'draw all metatiles' - if (numMetatilesToDraw < 0) { - numMetatilesToDraw = numPrimary + (secondaryTileset ? secondaryTileset->numMetatiles() : 0) - metatileIdStart; - } + int numMetatilesToDraw = metatileIdEnd - metatileIdStart + 1; - // Round up height for incomplete last row - int numMetatilesTall = ceil((double)numMetatilesToDraw / numMetatilesWide); + // Round up image height for incomplete last row. + int numMetatilesTall = Util::roundUpToMultiple(numMetatilesToDraw, numMetatilesWide) / numMetatilesWide; QImage image(numMetatilesWide * metatileSize.width(), numMetatilesTall * metatileSize.height(), QImage::Format_RGBA8888); - image.fill(projectConfig.transparencyColor == QColor(Qt::transparent) ? projectConfig.transparencyColor : QColor(Qt::magenta)); + image.fill(getInvalidImageColor()); QPainter painter(&image); for (int i = 0; i < numMetatilesToDraw; i++) { uint16_t metatileId = i + metatileIdStart; - if (includesPrimary && metatileId >= numPrimary) - metatileId += maxPrimary - numPrimary; // Skip over unused region of primary tileset - QImage metatile_image = getMetatileImage(metatileId, primaryTileset, secondaryTileset, layerOrder, layerOpacity, useTruePalettes) - .scaled(metatileSize.width(), metatileSize.height()); - int map_y = i / numMetatilesWide; - int map_x = i % numMetatilesWide; - QPoint metatile_origin = QPoint(map_x * metatileSize.width(), map_y * metatileSize.height()); - painter.drawImage(metatile_origin, metatile_image); + QImage metatileImage = getMetatileImage(metatileId, primaryTileset, secondaryTileset, layerOrder, layerOpacity, useTruePalettes) + .scaled(metatileSize); + + int x = (i % numMetatilesWide) * metatileSize.width(); + int y = (i / numMetatilesWide) * metatileSize.height(); + painter.drawImage(x, y, metatileImage); } painter.end(); return image; } + +// Constructs a grid image of the metatiles in the primary and secondary tileset, +// rounding as necessary to keep the two tilesets on separate rows. +// The unused metatiles (if any) between the primary and secondary tilesets are skipped. +QImage getMetatileSheetImage(Tileset *primaryTileset, + Tileset *secondaryTileset, + int numMetatilesWide, + const QList &layerOrder, + const QList &layerOpacity, + const QSize &metatileSize, + bool useTruePalettes) +{ + QImage primaryImage = getMetatileSheetImage(primaryTileset, + secondaryTileset, + 0, + primaryTileset ? primaryTileset->numMetatiles()-1 : 0, + numMetatilesWide, + layerOrder, + layerOpacity, + metatileSize, + useTruePalettes); + + uint16_t secondaryMetatileIdStart = Project::getNumMetatilesPrimary(); + QImage secondaryImage = getMetatileSheetImage(primaryTileset, + secondaryTileset, + secondaryMetatileIdStart, + secondaryMetatileIdStart + (secondaryTileset ? secondaryTileset->numMetatiles()-1 : 0), + numMetatilesWide, + layerOrder, + layerOpacity, + metatileSize, + useTruePalettes); + + QImage image(qMax(primaryImage.width(), secondaryImage.width()), primaryImage.height() + secondaryImage.height(), QImage::Format_RGBA8888); + image.fill(getInvalidImageColor()); + + QPainter painter(&image); + painter.drawImage(0, 0, primaryImage); + painter.drawImage(0, primaryImage.height(), secondaryImage); + painter.end(); + + return image; +} + +QImage getMetatileSheetImage(Layout *layout, int numMetatilesWide, bool useTruePalettes) { + if (!layout) + return QImage(); + return getMetatileSheetImage(layout->tileset_primary, + layout->tileset_secondary, + numMetatilesWide, + layout->metatileLayerOrder(), + layout->metatileLayerOpacity(), + Metatile::pixelSize(), + useTruePalettes); +} diff --git a/src/ui/metatileimageexporter.cpp b/src/ui/metatileimageexporter.cpp index d5e94f13..52a55694 100644 --- a/src/ui/metatileimageexporter.cpp +++ b/src/ui/metatileimageexporter.cpp @@ -211,10 +211,6 @@ void MetatileImageExporter::tryUpdatePreview() { void MetatileImageExporter::updatePreview() { copyRenderSettings(); - int numMetatilesWide = ui->spinBox_WidthMetatiles->value(); - int metatileStart = ui->spinBox_MetatileStart->value(); - int numMetatiles = Util::roundUpToMultiple(ui->spinBox_MetatileEnd->value() - metatileStart + 1, numMetatilesWide); - m_layerOrder.clear(); for (int i = 0; i < ui->listWidget_Layers->count(); i++) { auto item = ui->listWidget_Layers->item(i); @@ -224,12 +220,22 @@ void MetatileImageExporter::updatePreview() { } } - QImage previewImage = getMetatileSheetImage(m_primaryTileset, - m_secondaryTileset, - metatileStart, - numMetatiles, - numMetatilesWide, - m_layerOrder); + QImage previewImage; + if (ui->checkBox_PrimaryTileset->isChecked() && ui->checkBox_SecondaryTileset->isChecked()) { + // Special behavior to combine the two tilesets while skipping the unused region between tilesets. + previewImage = getMetatileSheetImage(m_primaryTileset, + m_secondaryTileset, + ui->spinBox_WidthMetatiles->value(), + m_layerOrder); + } else { + previewImage = getMetatileSheetImage(m_primaryTileset, + m_secondaryTileset, + ui->spinBox_MetatileStart->value(), + ui->spinBox_MetatileEnd->value(), + ui->spinBox_WidthMetatiles->value(), + m_layerOrder); + } + m_preview->setPixmap(QPixmap::fromImage(previewImage)); m_scene->setSceneRect(m_scene->itemsBoundingRect()); m_previewUpdateQueued = false; @@ -255,7 +261,6 @@ uint16_t MetatileImageExporter::getExpectedMetatileStart() { return ui->spinBox_MetatileStart->value(); } -// TODO: Combining tilesets is not rendering the correct range of metatiles uint16_t MetatileImageExporter::getExpectedMetatileEnd() { if (ui->checkBox_SecondaryTileset->isChecked()) return Project::getNumMetatilesPrimary() + (m_secondaryTileset ? (m_secondaryTileset->numMetatiles() - 1) : 0); if (ui->checkBox_PrimaryTileset->isChecked()) return m_primaryTileset ? (m_primaryTileset->numMetatiles() - 1) : 0; diff --git a/src/ui/tileseteditormetatileselector.cpp b/src/ui/tileseteditormetatileselector.cpp index 60f4b0f5..2d589195 100644 --- a/src/ui/tileseteditormetatileselector.cpp +++ b/src/ui/tileseteditormetatileselector.cpp @@ -57,8 +57,6 @@ void TilesetEditorMetatileSelector::drawSelectedMetatile() { void TilesetEditorMetatileSelector::updateBasePixmap() { this->baseImage = getMetatileSheetImage(this->primaryTileset, this->secondaryTileset, - 0, - this->numPrimaryMetatilesRounded() + this->secondaryTileset->numMetatiles(), this->numMetatilesWide, this->layout->metatileLayerOrder(), this->layout->metatileLayerOpacity(),