diff --git a/CHANGELOG.md b/CHANGELOG.md index 84503118..9787f71d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project somewhat adheres to [Semantic Versioning](https://semver.org/sp ### Fixed - Fix rare crash while quitting Porymap. +- Fix exported images on macOS using a different color space than in Porymap. ## [6.2.0] - 2025-08-08 ### Added diff --git a/forms/preferenceeditor.ui b/forms/preferenceeditor.ui index 63017212..15584ae4 100644 --- a/forms/preferenceeditor.ui +++ b/forms/preferenceeditor.ui @@ -48,52 +48,74 @@ 0 0 493 - 374 + 408 - - - <html><head/><body><p>If checked, a prompt to reload your project will appear if relevant project files are edited</p></body></html> - - - Monitor project files - - - - - - - <html><head/><body><p>If checked, Porymap will automatically open your most recently opened project on startup</p></body></html> - - - Open recent project on launch - - - - - - - <html><head/><body><p>If checked, Porymap will automatically alert you on startup if a new release is available</p></body></html> - - - Automatically check for updates - - - - - - - - 0 - 0 - - - - Application Theme - - + + + + + <html><head/><body><p>If checked, Porymap will automatically alert you on startup if a new release is available</p></body></html> + + + Automatically check for updates + + + + + + + <html><head/><body><p>If checked, Porymap will automatically open your most recently opened project on startup</p></body></html> + + + Open recent project on launch + + + + + + + <html><head/><body><p>If checked, a prompt to reload your project will appear if relevant project files are edited</p></body></html> + + + Monitor project files + + + + + + + Application Theme + + + + + + + false + + + + + + + Image Export Color Space + + + + + + + <html><head/><body><p>The color space to use for exported images. If &quot;---&quot; is set, no color space will be used for the exported image. For details on each color space, see Qt's manual page for QColorSpace.</p></body></html> + + + false + + + + @@ -483,6 +505,13 @@ + + + NoScrollComboBox + QComboBox +
noscrollcombobox.h
+
+
diff --git a/include/config.h b/include/config.h index 1f2a1a14..bfa990f0 100644 --- a/include/config.h +++ b/include/config.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "events.h" @@ -145,6 +146,7 @@ public: std::set statusBarLogTypes; QFont applicationFont; QFont mapListFont; + int imageExportColorSpaceId; protected: virtual void parseConfigKeyValue(QString key, QString value) override; diff --git a/include/core/mapconnection.h b/include/core/mapconnection.h index ba7c95a8..40713d3a 100644 --- a/include/core/mapconnection.h +++ b/include/core/mapconnection.h @@ -42,6 +42,7 @@ public: MapConnection* createMirror(); QPixmap render() const; + QImage renderImage() const; QPoint relativePixelPos(bool clipped = false) const; static QPointer project; diff --git a/include/core/utility.h b/include/core/utility.h index 7270b552..ad8ce825 100644 --- a/include/core/utility.h +++ b/include/core/utility.h @@ -4,6 +4,7 @@ #include #include +#include namespace Util { void numericalModeSort(QStringList &list); @@ -17,6 +18,7 @@ namespace Util { void setErrorStylesheet(QLineEdit *lineEdit, bool isError); QString toStylesheetString(const QFont &font); void show(QWidget *w); + QColorSpace toColorSpace(int colorSpaceInt); } #endif // UTILITY_H diff --git a/include/ui/mapimageexporter.h b/include/ui/mapimageexporter.h index 789cf9b9..7bb9c027 100644 --- a/include/ui/mapimageexporter.h +++ b/include/ui/mapimageexporter.h @@ -58,6 +58,7 @@ private: QBuffer *m_timelapseBuffer = nullptr; QMovie *m_timelapseMovie = nullptr; QGraphicsPixmapItem *m_preview = nullptr; + QImage m_previewImage; ImageExporterSettings m_settings; ImageExporterMode m_mode = ImageExporterMode::Normal; @@ -77,15 +78,15 @@ private: void setConnectionDirectionEnabled(const QString &dir, bool enable); void saveImage(); QGifImage* createTimelapseGifImage(QProgressDialog *progress); - QPixmap getStitchedImage(QProgressDialog *progress); - QPixmap getFormattedMapPixmap(); + QImage getStitchedImage(QProgressDialog *progress); + QImage getFormattedMapImage(); void paintBorder(QPainter *painter, Layout *layout); void paintCollision(QPainter *painter, Layout *layout); void paintConnections(QPainter *painter, const Map *map); void paintEvents(QPainter *painter, const Map *map); void paintGrid(QPainter *painter, const Layout *layout = nullptr); QMargins getMargins(const Map *map); - QPixmap getExpandedPixmap(const QPixmap &pixmap, const QSize &targetSize, const QColor &fillColor); + QImage getExpandedImage(const QImage &image, const QSize &targetSize, const QColor &fillColor); bool currentHistoryAppliesToFrame(QUndoStack *historyStack); protected: diff --git a/include/ui/metatileimageexporter.h b/include/ui/metatileimageexporter.h index 1d486bd4..20077d80 100644 --- a/include/ui/metatileimageexporter.h +++ b/include/ui/metatileimageexporter.h @@ -81,6 +81,7 @@ private: CheckeredBgScene *m_scene = nullptr; QGraphicsPixmapItem *m_preview = nullptr; + QImage m_previewImage; bool m_previewUpdateQueued = false; QList m_layerOrder; ProjectConfig m_savedConfig; diff --git a/include/ui/preferenceeditor.h b/include/ui/preferenceeditor.h index 24b6253f..72b514c6 100644 --- a/include/ui/preferenceeditor.h +++ b/include/ui/preferenceeditor.h @@ -29,7 +29,6 @@ signals: private: Ui::PreferenceEditor *ui; - NoScrollComboBox *themeSelector; QFont applicationFont; QFont mapListFont; diff --git a/src/config.cpp b/src/config.cpp index 4322e4cd..efeb2ce1 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -373,6 +373,16 @@ void PorymapConfig::reset() { this->statusBarLogTypes = { LogType::LOG_ERROR, LogType::LOG_WARN }; this->applicationFont = QFont(); this->mapListFont = PorymapConfig::defaultMapListFont(); +#ifdef Q_OS_MACOS + // Since the release of the Retina display, Apple products use the Display P3 color space by default. + // If we don't use this for exported images (which by default will either have no color space or the sRGB + // color space) then they may appear to have different colors than the same image displayed in Porymap. + this->imageExportColorSpaceId = static_cast(QColorSpace::DisplayP3); +#else + // As of writing Qt has no way to get a reasonable color space from the user's environment, + // so we export images without one and let them handle it. + this->imageExportColorSpaceId = 0; +#endif } void PorymapConfig::parseConfigKeyValue(QString key, QString value) { @@ -563,6 +573,8 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) { } else if (key == "map_list_font") { this->mapListFont = QFont(); this->mapListFont.fromString(value); + } else if (key == "image_export_color_space_id") { + this->imageExportColorSpaceId = getConfigInteger(key, value, 0, 8); } else { logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->filepath()).arg(key)); } @@ -656,6 +668,7 @@ QMap PorymapConfig::getKeyValueMap() { map.insert("status_bar_log_types", logTypesStrings.join(",")); map.insert("application_font", this->applicationFont.toString()); map.insert("map_list_font", this->mapListFont.toString()); + map.insert("image_export_color_space_id", QString::number(this->imageExportColorSpaceId)); return map; } diff --git a/src/core/mapconnection.cpp b/src/core/mapconnection.cpp index e1478fad..01a597b8 100644 --- a/src/core/mapconnection.cpp +++ b/src/core/mapconnection.cpp @@ -68,6 +68,12 @@ QPixmap MapConnection::render() const { return map->renderConnection(m_direction, m_parentMap ? m_parentMap->layout() : nullptr); } +QImage MapConnection::renderImage() const { + render(); + auto map = targetMap(); + return (map && map->layout()) ? map->layout()->image : QImage(); +} + // Get the position of the target map relative to its parent map. // For right/down connections this is offset by the dimensions of the parent map. // For left/up connections this is offset by the dimensions of the target map. diff --git a/src/core/utility.cpp b/src/core/utility.cpp index 92cd1a4a..1c48f830 100644 --- a/src/core/utility.cpp +++ b/src/core/utility.cpp @@ -125,3 +125,23 @@ void Util::show(QWidget *w) { w->activateWindow(); } } + +// Safe conversion from an int representing a QColorSpace::NamedColorSpace to a QColorSpace. +// This lets us use 0 to mean "no color space". +QColorSpace Util::toColorSpace(int colorSpaceInt) { + QColorSpace colorSpace; + + int min = static_cast(QColorSpace::SRgb); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)) + // Qt 6.8.0 introduced additional color spaces + int max = static_cast(QColorSpace::Bt2100Hlg); +#else + int max = static_cast(QColorSpace::ProPhotoRgb); +#endif + + if (colorSpaceInt >= min && colorSpaceInt <= max) { + return QColorSpace(static_cast(colorSpaceInt)); + } else { + return QColorSpace(); + } +} diff --git a/src/ui/mapimageexporter.cpp b/src/ui/mapimageexporter.cpp index 30c6ff72..91a6fb5a 100644 --- a/src/ui/mapimageexporter.cpp +++ b/src/ui/mapimageexporter.cpp @@ -185,9 +185,9 @@ void MapImageExporter::updateMapSelection() { void MapImageExporter::saveImage() { // If the preview is empty (because progress was canceled) or if updates were disabled // then we should ensure the image in the preview is up-to-date before exporting. - if (m_preview->pixmap().isNull() || m_settings.disablePreviewUpdates) { + if (m_previewImage.isNull() || m_settings.disablePreviewUpdates) { updatePreview(true); - if (m_preview->pixmap().isNull()) + if (m_previewImage.isNull()) return; // Canceled } if (m_mode == ImageExporterMode::Timelapse && !m_timelapseGifImage) { @@ -221,7 +221,7 @@ void MapImageExporter::saveImage() { case ImageExporterMode::Normal: case ImageExporterMode::Stitch: // Normal and Stitch modes already have the image ready to go in the preview. - m_preview->pixmap().save(filepath); + m_previewImage.save(filepath); break; case ImageExporterMode::Timelapse: m_timelapseGifImage->save(filepath); @@ -285,19 +285,19 @@ bool MapImageExporter::currentHistoryAppliesToFrame(QUndoStack *historyStack) { } } -QPixmap MapImageExporter::getExpandedPixmap(const QPixmap &pixmap, const QSize &targetSize, const QColor &fillColor) { - if (pixmap.width() >= targetSize.width() && pixmap.height() >= targetSize.height()) - return pixmap; +QImage MapImageExporter::getExpandedImage(const QImage &image, const QSize &targetSize, const QColor &fillColor) { + if (image.width() >= targetSize.width() && image.height() >= targetSize.height()) + return image; - QPixmap resizedPixmap = QPixmap(targetSize); - QPainter painter(&resizedPixmap); - resizedPixmap.fill(fillColor); + QImage resizedImage(targetSize, QImage::Format_RGBA8888); + QPainter painter(&resizedImage); + resizedImage.fill(fillColor); - // Center the old pixmap in the new resized one. - int x = (targetSize.width() - pixmap.width()) / 2; - int y = (targetSize.height() - pixmap.height()) / 2; - painter.drawPixmap(x, y, pixmap.width(), pixmap.height(), pixmap); - return resizedPixmap; + // Center the old image in the new resized one. + int x = (targetSize.width() - image.width()) / 2; + int y = (targetSize.height() - image.height()) / 2; + painter.drawImage(x, y, image); + return resizedImage; } struct TimelapseStep { @@ -363,8 +363,7 @@ QGifImage* MapImageExporter::createTimelapseGifImage(QProgressDialog *progress) while (step.historyStack->canRedo() && step.historyStack->index() < step.initialStackIndex && !progress->wasCanceled()) { if (currentHistoryAppliesToFrame(step.historyStack) && --framesToSkip <= 0) { // Render frame, increasing its size if necessary to match the canvas. - QPixmap pixmap = getExpandedPixmap(getFormattedMapPixmap(), canvasSize, m_settings.fillColor); - timelapseImg->addFrame(pixmap.toImage()); + timelapseImg->addFrame(getExpandedImage(getFormattedMapImage(), canvasSize, m_settings.fillColor)); framesToSkip = m_settings.timelapseSkipAmount - 1; } step.historyStack->redo(); @@ -396,8 +395,7 @@ QGifImage* MapImageExporter::createTimelapseGifImage(QProgressDialog *progress) // Final frame should always be the current state of the map. if (timelapseImg) { - QPixmap finalFrame = getExpandedPixmap(getFormattedMapPixmap(), canvasSize, m_settings.fillColor); - timelapseImg->addFrame(finalFrame.toImage()); + timelapseImg->addFrame(getExpandedImage(getFormattedMapImage(), canvasSize, m_settings.fillColor)); } return timelapseImg; } @@ -408,7 +406,7 @@ struct StitchedMap { Map* map; }; -QPixmap MapImageExporter::getStitchedImage(QProgressDialog *progress) { +QImage MapImageExporter::getStitchedImage(QProgressDialog *progress) { // Do a breadth-first search to gather a collection of // all reachable maps with their relative offsets. QSet visited; @@ -419,7 +417,7 @@ QPixmap MapImageExporter::getStitchedImage(QProgressDialog *progress) { progress->setLabelText("Gathering stitched maps..."); while (!unvisited.isEmpty()) { if (progress->wasCanceled()) { - return QPixmap(); + return QImage(); } progress->setMaximum(visited.size() + unvisited.size()); progress->setValue(visited.size()); @@ -439,7 +437,7 @@ QPixmap MapImageExporter::getStitchedImage(QProgressDialog *progress) { } } if (stitchedMaps.isEmpty()) - return QPixmap(); + return QImage(); progress->setMaximum(stitchedMaps.size()); int numDrawn = 0; @@ -450,10 +448,10 @@ QPixmap MapImageExporter::getStitchedImage(QProgressDialog *progress) { dimensions |= (QRect(map.x, map.y, map.map->pixelWidth(), map.map->pixelHeight()) + getMargins(map.map)); } - QPixmap stitchedPixmap(dimensions.width(), dimensions.height()); - stitchedPixmap.fill(m_settings.fillColor); + QImage stitchedImage(dimensions.width(), dimensions.height(), QImage::Format_RGBA8888); + stitchedImage.fill(m_settings.fillColor); - QPainter painter(&stitchedPixmap); + QPainter painter(&stitchedImage); painter.translate(-dimensions.left(), -dimensions.top()); // Borders can occlude neighboring maps, so we draw all the borders before drawing any maps. @@ -469,7 +467,7 @@ QPixmap MapImageExporter::getStitchedImage(QProgressDialog *progress) { numDrawn = 0; for (const StitchedMap &map : stitchedMaps) { if (progress->wasCanceled()) { - return QPixmap(); + return QImage(); } painter.translate(map.x, map.y); paintBorder(&painter, map.map->layout()); @@ -485,10 +483,11 @@ QPixmap MapImageExporter::getStitchedImage(QProgressDialog *progress) { numDrawn = 0; for (const StitchedMap &map : stitchedMaps) { if (progress->wasCanceled()) { - return QPixmap(); + return QImage(); } + map.map->layout()->render(true); painter.translate(map.x, map.y); - painter.drawPixmap(0, 0, map.map->layout()->render(true)); + painter.drawImage(0, 0, map.map->layout()->image); paintCollision(&painter, map.map->layout()); painter.translate(-map.x, -map.y); @@ -504,7 +503,7 @@ QPixmap MapImageExporter::getStitchedImage(QProgressDialog *progress) { numDrawn = 0; for (const StitchedMap &map : stitchedMaps) { if (progress->wasCanceled()) { - return QPixmap(); + return QImage(); } painter.translate(map.x, map.y); paintEvents(&painter, map.map); @@ -515,7 +514,7 @@ QPixmap MapImageExporter::getStitchedImage(QProgressDialog *progress) { } } - return stitchedPixmap; + return stitchedImage; } void MapImageExporter::updatePreview(bool forceUpdate) { @@ -528,18 +527,17 @@ void MapImageExporter::updatePreview(bool forceUpdate) { progress.setModal(true); progress.setMinimumDuration(1000); - QPixmap previewPixmap; if (m_mode == ImageExporterMode::Normal) { - previewPixmap = getFormattedMapPixmap(); + m_previewImage = getFormattedMapImage(); } else if (m_mode == ImageExporterMode::Stitch) { - previewPixmap = getStitchedImage(&progress); + m_previewImage = getStitchedImage(&progress); } else if (m_mode == ImageExporterMode::Timelapse) { if (m_timelapseMovie) m_timelapseMovie->stop(); m_timelapseGifImage = createTimelapseGifImage(&progress); if (!m_timelapseGifImage) { - previewPixmap = QPixmap(); + m_previewImage = QImage(); } else { // We want to convert the QGifImage data into a QMovie for the preview display. // Both support input/output with a QIODevice, so we use a QBuffer to translate the data. @@ -556,12 +554,15 @@ void MapImageExporter::updatePreview(bool forceUpdate) { m_preview->setPixmap(m_timelapseMovie->currentPixmap()); }); m_timelapseMovie->start(); - previewPixmap = m_timelapseMovie->currentPixmap(); + m_previewImage = m_timelapseMovie->currentImage(); } + } else { + m_previewImage = QImage(); } progress.close(); - m_preview->setPixmap(previewPixmap); + m_previewImage.setColorSpace(Util::toColorSpace(porymapConfig.imageExportColorSpaceId)); + m_preview->setPixmap(QPixmap::fromImage(m_previewImage)); m_scene->setSceneRect(m_scene->itemsBoundingRect()); scalePreview(); } @@ -572,23 +573,24 @@ void MapImageExporter::scalePreview() { ui->graphicsView_Preview->fitInView(m_preview, Qt::KeepAspectRatioByExpanding); } -QPixmap MapImageExporter::getFormattedMapPixmap() { +QImage MapImageExporter::getFormattedMapImage() { if (!m_layout) - return QPixmap(); + return QImage(); m_layout->render(true); - // Create pixmap large enough to contain the map and the marginal elements (the border, grid, etc.) + // Create image large enough to contain the map and the marginal elements (the border, grid, etc.) QMargins margins = getMargins(m_map); - QPixmap pixmap = QPixmap(m_layout->pixmap.width() + margins.left() + margins.right(), - m_layout->pixmap.height() + margins.top() + margins.bottom()); - pixmap.fill(m_settings.fillColor); + QImage image(m_layout->image.width() + margins.left() + margins.right(), + m_layout->image.height() + margins.top() + margins.bottom(), + QImage::Format_RGBA8888); + image.fill(m_settings.fillColor); - QPainter painter(&pixmap); + QPainter painter(&image); painter.translate(margins.left(), margins.top()); paintBorder(&painter, m_layout); - painter.drawPixmap(0, 0, m_layout->pixmap); + painter.drawImage(0, 0, m_layout->image); paintCollision(&painter, m_layout); if (m_map) { paintConnections(&painter, m_map); @@ -596,7 +598,7 @@ QPixmap MapImageExporter::getFormattedMapPixmap() { } paintGrid(&painter, m_layout); - return pixmap; + return image; } QMargins MapImageExporter::getMargins(const Map *map) { @@ -630,9 +632,11 @@ void MapImageExporter::paintCollision(QPainter *painter, Layout *layout) { if (!m_settings.showCollision) return; + layout->renderCollision(true); + auto savedOpacity = painter->opacity(); painter->setOpacity(static_cast(porymapConfig.collisionOpacity) / 100); - painter->drawPixmap(0, 0, layout->renderCollision(true)); + painter->drawImage(0, 0, layout->collision_image); painter->setOpacity(savedOpacity); } @@ -652,7 +656,7 @@ void MapImageExporter::paintBorder(QPainter *painter, Layout *layout) { // Skip border painting if it would be fully covered by the rest of the map if (layout->isWithinBounds(QRect(x, y, layout->getBorderWidth(), layout->getBorderHeight()))) continue; - painter->drawPixmap(x * Metatile::pixelWidth(), y * Metatile::pixelHeight(), layout->border_pixmap); + painter->drawImage(x * Metatile::pixelWidth(), y * Metatile::pixelHeight(), layout->border_image); } painter->restore(); @@ -665,7 +669,7 @@ void MapImageExporter::paintConnections(QPainter *painter, const Map *map) { for (const auto &connection : map->getConnections()) { if (!m_settings.showConnections.contains(connection->direction())) continue; - painter->drawImage(connection->relativePixelPos(true), connection->render().toImage()); + painter->drawImage(connection->relativePixelPos(true), connection->renderImage()); } } diff --git a/src/ui/metatileimageexporter.cpp b/src/ui/metatileimageexporter.cpp index 51b953df..f7e99fa0 100644 --- a/src/ui/metatileimageexporter.cpp +++ b/src/ui/metatileimageexporter.cpp @@ -185,7 +185,7 @@ void MetatileImageExporter::reset() { QImage MetatileImageExporter::getImage() { tryUpdatePreview(); - return m_preview->pixmap().toImage(); + return m_previewImage; } bool MetatileImageExporter::saveImage(QString filepath) { @@ -197,7 +197,7 @@ bool MetatileImageExporter::saveImage(QString filepath) { return false; } } - return m_preview->pixmap().save(filepath); + return m_previewImage.save(filepath); } QString MetatileImageExporter::getDefaultFileName() const { @@ -244,7 +244,7 @@ void MetatileImageExporter::queuePreviewUpdate() { // For updating only when a change has been recorded. // Useful for something that might happen often, like an input widget losing focus. void MetatileImageExporter::tryUpdatePreview() { - if (m_preview->pixmap().isNull() || m_previewUpdateQueued) { + if (m_previewImage.isNull() || m_previewUpdateQueued) { updatePreview(); } } @@ -261,15 +261,14 @@ void MetatileImageExporter::updatePreview() { } } - 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_previewImage = getMetatileSheetImage(m_primaryTileset, m_secondaryTileset, ui->spinBox_WidthMetatiles->value(), m_layerOrder); } else { - previewImage = getMetatileSheetImage(m_primaryTileset, + m_previewImage = getMetatileSheetImage(m_primaryTileset, m_secondaryTileset, ui->spinBox_MetatileStart->value(), ui->spinBox_MetatileEnd->value(), @@ -277,7 +276,8 @@ void MetatileImageExporter::updatePreview() { m_layerOrder); } - m_preview->setPixmap(QPixmap::fromImage(previewImage)); + m_previewImage.setColorSpace(Util::toColorSpace(porymapConfig.imageExportColorSpaceId)); + m_preview->setPixmap(QPixmap::fromImage(m_previewImage)); m_scene->setSceneRect(m_scene->itemsBoundingRect()); m_previewUpdateQueued = false; diff --git a/src/ui/preferenceeditor.cpp b/src/ui/preferenceeditor.cpp index 5765ba01..326197e8 100644 --- a/src/ui/preferenceeditor.cpp +++ b/src/ui/preferenceeditor.cpp @@ -6,23 +6,20 @@ #include #include #include -#include #include #include PreferenceEditor::PreferenceEditor(QWidget *parent) : QMainWindow(parent), - ui(new Ui::PreferenceEditor), - themeSelector(nullptr) + ui(new Ui::PreferenceEditor) { ui->setupUi(this); - auto *formLayout = new QFormLayout(ui->groupBox_Themes); - themeSelector = new NoScrollComboBox(ui->groupBox_Themes); - themeSelector->setEditable(false); - themeSelector->setMinimumContentsLength(0); - formLayout->addRow("Themes", themeSelector); setAttribute(Qt::WA_DeleteOnClose); + + ui->comboBox_ColorSpace->setMinimumContentsLength(0); + ui->comboBox_ApplicationTheme->setMinimumContentsLength(0); + connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &PreferenceEditor::dialogButtonClicked); connect(ui->pushButton_CustomizeApplicationFont, &QPushButton::clicked, [this] { @@ -59,16 +56,35 @@ void PreferenceEditor::initFields() { themes.append(themeName); } } - themeSelector->addItems(themes); + ui->comboBox_ApplicationTheme->addItems(themes); + + static const QMap colorSpaces = { + {"---", 0}, + {"sRGB", QColorSpace::SRgb}, + {"sRGB Linear", QColorSpace::SRgbLinear}, + {"Adobe RGB", QColorSpace::AdobeRgb}, + {"Display P3", QColorSpace::DisplayP3}, + {"ProPhoto RGB", QColorSpace::ProPhotoRgb}, +#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)) + // Qt 6.8.0 introduced additional color spaces + {"BT.2020", QColorSpace::Bt2020}, + {"BT.2100 (PQ)", QColorSpace::Bt2100Pq}, + {"BT.2100 (HLG)", QColorSpace::Bt2100Hlg}, +#endif + }; + for (auto it = colorSpaces.constBegin(); it != colorSpaces.constEnd(); it++) { + ui->comboBox_ColorSpace->addItem(it.key(), it.value()); + } } void PreferenceEditor::updateFields() { - themeSelector->setTextItem(porymapConfig.theme); + ui->comboBox_ApplicationTheme->setTextItem(porymapConfig.theme); if (porymapConfig.eventSelectionShapeMode == QGraphicsPixmapItem::MaskShape) { ui->radioButton_OnSprite->setChecked(true); } else if (porymapConfig.eventSelectionShapeMode == QGraphicsPixmapItem::BoundingRectShape) { ui->radioButton_WithinRect->setChecked(true); } + ui->comboBox_ColorSpace->setNumberItem(porymapConfig.imageExportColorSpaceId); ui->lineEdit_TextEditorOpenFolder->setText(porymapConfig.textEditorOpenFolder); ui->lineEdit_TextEditorGotoLine->setText(porymapConfig.textEditorGotoLine); ui->checkBox_MonitorProjectFiles->setChecked(porymapConfig.monitorFiles); @@ -97,8 +113,8 @@ void PreferenceEditor::saveFields() { bool needsProjectReload = false; bool changedTheme = false; - if (themeSelector->currentText() != porymapConfig.theme) { - porymapConfig.theme = themeSelector->currentText(); + if (ui->comboBox_ApplicationTheme->currentText() != porymapConfig.theme) { + porymapConfig.theme = ui->comboBox_ApplicationTheme->currentText(); changedTheme = true; } @@ -113,6 +129,7 @@ void PreferenceEditor::saveFields() { emit scriptSettingsChanged(scriptAutocompleteMode); } + porymapConfig.imageExportColorSpaceId = ui->comboBox_ColorSpace->currentData().toInt(); porymapConfig.eventSelectionShapeMode = ui->radioButton_OnSprite->isChecked() ? QGraphicsPixmapItem::MaskShape : QGraphicsPixmapItem::BoundingRectShape; porymapConfig.textEditorOpenFolder = ui->lineEdit_TextEditorOpenFolder->text(); porymapConfig.textEditorGotoLine = ui->lineEdit_TextEditorGotoLine->text();