diff --git a/cubiomes b/cubiomes index 0933804..270ed51 160000 --- a/cubiomes +++ b/cubiomes @@ -1 +1 @@ -Subproject commit 0933804f15f4de107f0a1703f508521625292f87 +Subproject commit 270ed5162bd6ed31287688955311ce8e7928737a diff --git a/cubiomes-viewer.pro b/cubiomes-viewer.pro index 00f5592..ea03cc2 100644 --- a/cubiomes-viewer.pro +++ b/cubiomes-viewer.pro @@ -36,6 +36,7 @@ CONFIG += static SOURCES += \ src/aboutdialog.cpp \ + src/biomecolordialog.cpp \ src/collapsible.cpp \ src/conditiondialog.cpp \ src/configdialog.cpp \ @@ -62,6 +63,7 @@ HEADERS += \ $$CUPATH/layers.h \ $$CUPATH/util.h \ src/aboutdialog.h \ + src/biomecolordialog.h \ src/collapsible.h \ src/conditiondialog.h \ src/configdialog.h \ @@ -86,6 +88,7 @@ HEADERS += \ FORMS += \ src/aboutdialog.ui \ + src/biomecolordialog.ui \ src/conditiondialog.ui \ src/configdialog.ui \ src/extgendialog.ui \ diff --git a/src/aboutdialog.h b/src/aboutdialog.h index e1e0956..aad4b8e 100644 --- a/src/aboutdialog.h +++ b/src/aboutdialog.h @@ -6,7 +6,7 @@ #define VERS_MAJOR 2 #define VERS_MINOR 2 -#define VERS_PATCH -1 // negative patch number designates a development version +#define VERS_PATCH -2 // negative patch number designates a development version // returns +1 if newer, -1 if older and 0 if equal inline int cmpVers(int major, int minor, int patch) diff --git a/src/biomecolordialog.cpp b/src/biomecolordialog.cpp new file mode 100644 index 0000000..45a1521 --- /dev/null +++ b/src/biomecolordialog.cpp @@ -0,0 +1,303 @@ +#include "biomecolordialog.h" +#include "ui_biomecolordialog.h" +#include "mainwindow.h" +#include "cutil.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DIM_DIVIDER 6 + +static QIcon getColorIcon(const QColor& col) +{ + QPixmap pixmap(14,14); + pixmap.fill(QColor(0,0,0,0)); + QPainter p(&pixmap); + p.setRenderHint(QPainter::Antialiasing); + QPainterPath path; + path.addRoundedRect(QRectF(1, 1, 12, 12), 3, 3); + QPen pen(Qt::black, 1); + p.setPen(pen); + + p.fillPath(path, col); + p.drawPath(path); + return QIcon(pixmap); +} + +BiomeColorDialog::BiomeColorDialog(MainWindow *parent, QString initrc) + : QDialog(parent) + , ui(new Ui::BiomeColorDialog) + , parent(parent) + , modified() +{ + ui->setupUi(this); + ui->buttonOk->setIcon(style()->standardIcon(QStyle::SP_DialogOkButton)); + + memset(buttons, 0, sizeof(buttons)); + memcpy(colors, biomeColors, sizeof(colors)); + + unsigned char coldefault[256][3]; + initBiomeColors(coldefault); + + + QPushButton *button; + + button = new QPushButton(QIcon(), tr("All to default"), this); + connect(button, &QPushButton::clicked, this, &BiomeColorDialog::onAllToDefault); + ui->gridLayout->addWidget(button, 0, 1); + + button = new QPushButton(QIcon(), tr("All to dimmed"), this); + connect(button, &QPushButton::clicked, this, &BiomeColorDialog::onAllToDimmed); + ui->gridLayout->addWidget(button, 0, 2); + + for (int i = 0; i < 256; i++) + { + const char *bname = biome2str(MC_NEWEST, i); + if (!bname) + continue; + + QColor col; + col = QColor(colors[i][0], colors[i][1], colors[i][2]); + buttons[i] = button = new QPushButton(getColorIcon(col), bname, this); + connect(button, &QPushButton::clicked, [=]() { this->editBiomeColor(i); }); + ui->gridLayout->addWidget(button, i+1, 0); + + col = QColor(coldefault[i][0], coldefault[i][1], coldefault[i][2]); + button = new QPushButton(getColorIcon(col), tr("Default reset"), this); + connect(button, &QPushButton::clicked, [=]() { this->setBiomeColor(i, col); }); + ui->gridLayout->addWidget(button, i+1, 1); + + col = QColor(coldefault[i][0] / DIM_DIVIDER, coldefault[i][1] / DIM_DIVIDER, coldefault[i][2] / DIM_DIVIDER); + button = new QPushButton(getColorIcon(col), tr("Dimmed reset"), this); + connect(button, &QPushButton::clicked, [=]() { this->setBiomeColor(i, col); }); + ui->gridLayout->addWidget(button, i+1, 2); + } + + QString dir = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); + QDirIterator it(dir, QDirIterator::NoIteratorFlags); + while (it.hasNext()) + { + QString rc = it.next(); + if (!rc.endsWith(".colormap")) + continue; + QFile file(rc); + if (!file.open(QIODevice::ReadOnly)) + continue; + QTextStream stream(&file); + QString line = stream.readLine(); + if (line.length() < 2 || !line.startsWith(";")) + continue; + ui->comboColormaps->addItem(line.mid(1).trimmed(), rc); + } + ui->comboColormaps->model()->sort(0, Qt::AscendingOrder); + ui->comboColormaps->insertItem(0, tr("[default]")); + int index = 0; + for (int i = 0; i < ui->comboColormaps->count(); i++) + { + QString itemrc = qvariant_cast(ui->comboColormaps->itemData(i)); + if (initrc == itemrc) + { + index = i; + break; + } + } + ui->comboColormaps->setCurrentIndex(index); +} + +BiomeColorDialog::~BiomeColorDialog() +{ + delete ui; +} + +/// Saves the current colormap as the given rc. +/// If the rc already corresponds to an item, the description +/// will be updated only if the new desc is non-empty. +int BiomeColorDialog::saveColormap(QString rc, QString desc) +{ + if (rc.isEmpty()) + return -1; + QFile file(rc); + if (!file.open(QIODevice::WriteOnly)) + return -1; + + int index = -1; + for (int i = 0; i < ui->comboColormaps->count(); i++) + { + QString itemrc = qvariant_cast(ui->comboColormaps->itemData(i)); + if (rc == itemrc) + { + index = i; + if (desc.isEmpty()) + desc = ui->comboColormaps->itemText(i); + break; + } + } + + QTextStream stream(&file); + stream << ";" << desc << "\n"; + for (int i = 0; i < 256; i++) + { + const char *bname = biome2str(MC_NEWEST, i); + if (!bname) + continue; + stream << bname << " " << colors[i][0] << " " << colors[i][1] << " " << colors[i][2] << "\n"; + } + + modified = false; + if (index > 0) + { + ui->comboColormaps->setItemText(index, desc); + } + else + { + index = ui->comboColormaps->count(); + ui->comboColormaps->addItem(desc, rc); + } + return index; +} + +void BiomeColorDialog::setBiomeColor(int id, const QColor &col) +{ + buttons[id]->setIcon(getColorIcon(col)); + colors[id][0] = col.red(); + colors[id][1] = col.green(); + colors[id][2] = col.blue(); + modified = true; +} + +void BiomeColorDialog::editBiomeColor(int id) +{ + QColor col = QColor(colors[id][0], colors[id][1], colors[id][2]); + QColorDialog *dialog = new QColorDialog(col, this); + //dialog->setOption(QColorDialog::DontUseNativeDialog, true); + if (dialog->exec()) + { + setBiomeColor(id, dialog->selectedColor()); + } +} + +void BiomeColorDialog::on_comboColormaps_currentIndexChanged(int index) +{ + ui->buttonRemove->setEnabled(index != 0); + if (modified) + { + if (activerc.isEmpty()) + on_buttonSaveAs_clicked(); + else + saveColormap(activerc, ""); + } + + activerc = qvariant_cast(ui->comboColormaps->currentData()); + QFile file(activerc); + if (!activerc.isEmpty() && file.open(QIODevice::ReadOnly)) + { + char buf[32*1024]; + qint64 siz = file.read(buf, sizeof(buf)-1); + file.close(); + if (siz >= 0) + { + buf[siz] = 0; + initBiomeColors(colors); + parseBiomeColors(colors, buf); + } + } + else + { + initBiomeColors(colors); + } + + for (int i = 0; i < 256; i++) + { + if (buttons[i]) + { + QColor col(colors[i][0], colors[i][1], colors[i][2]); + buttons[i]->setIcon(getColorIcon(col)); + } + } +} + +void BiomeColorDialog::on_buttonSaveAs_clicked() +{ + QString rc; + int n; + QString path = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); + for (n = 1; n < 1000; n++) + { + rc = path + "/" + QString::number(n) + ".colormap"; + if (!QFile::exists(rc)) + break; + } + bool ok; + QString desc = QInputDialog::getText( + this, tr("Save biome colors as..."), + tr("Biome colors:"), QLineEdit::Normal, + QString("Colormap#%1").arg(n), &ok); + if (!ok || desc.isEmpty()) + return; + + int idx = saveColormap(rc, desc); + ui->comboColormaps->setCurrentIndex(idx); + activerc = rc; +} + +void BiomeColorDialog::on_buttonRemove_clicked() +{ + QString rc = qvariant_cast(ui->comboColormaps->currentData()); + if (!rc.isEmpty()) + { + modified = false; // discard changes + ui->comboColormaps->removeItem(ui->comboColormaps->currentIndex()); + QFile file(rc); + file.remove(); + } +} + +void BiomeColorDialog::on_buttonOk_clicked() +{ + on_comboColormaps_currentIndexChanged(ui->comboColormaps->currentIndex()); + parent->setBiomeColorRc(activerc); + accept(); +} + + +void BiomeColorDialog::onAllToDefault() +{ + initBiomeColors(colors); + for (int i = 0; i < 256; i++) + { + if (buttons[i]) + { + QColor col(colors[i][0], colors[i][1], colors[i][2]); + buttons[i]->setIcon(getColorIcon(col)); + } + } + modified = true; +} + +void BiomeColorDialog::onAllToDimmed() +{ + initBiomeColors(colors); + for (int i = 0; i < 256; i++) + { + colors[i][0] /= DIM_DIVIDER; + colors[i][1] /= DIM_DIVIDER; + colors[i][2] /= DIM_DIVIDER; + if (buttons[i]) + { + QColor col(colors[i][0], colors[i][1], colors[i][2]); + buttons[i]->setIcon(getColorIcon(col)); + } + } + modified = true; +} diff --git a/src/biomecolordialog.h b/src/biomecolordialog.h new file mode 100644 index 0000000..cb7e0af --- /dev/null +++ b/src/biomecolordialog.h @@ -0,0 +1,44 @@ +#ifndef BIOMECOLORDIALOG_H +#define BIOMECOLORDIALOG_H + +#include + +namespace Ui { +class BiomeColorDialog; +} + +class MainWindow; + +class BiomeColorDialog : public QDialog +{ + Q_OBJECT + +public: + explicit BiomeColorDialog(MainWindow *parent, QString initrc); + ~BiomeColorDialog(); + + int saveColormap(QString rc, QString desc); + +public slots: + void setBiomeColor(int id, const QColor &col); + void editBiomeColor(int id); + +private slots: + void on_comboColormaps_currentIndexChanged(int index); + void on_buttonSaveAs_clicked(); + void on_buttonRemove_clicked(); + void on_buttonOk_clicked(); + + void onAllToDefault(); + void onAllToDimmed(); + +private: + Ui::BiomeColorDialog *ui; + MainWindow *parent; + QPushButton *buttons[256]; + unsigned char colors[256][3]; + QString activerc; + bool modified; +}; + +#endif // BIOMECOLORDIALOG_H diff --git a/src/biomecolordialog.ui b/src/biomecolordialog.ui new file mode 100644 index 0000000..8cedb7d --- /dev/null +++ b/src/biomecolordialog.ui @@ -0,0 +1,87 @@ + + + BiomeColorDialog + + + + 0 + 0 + 580 + 600 + + + + Biome Color Editor + + + + :/icons/map.png:/icons/map.png + + + + + + + + + Biome color map: + + + + + + + + + Save as... + + + + + + + Remove + + + + + + + Ok + + + + + + + + + true + + + + + 0 + 0 + 562 + 521 + + + + text-align:left; + + + + + + + + + + + + + + + + diff --git a/src/cutil.h b/src/cutil.h index 899d40e..189293f 100644 --- a/src/cutil.h +++ b/src/cutil.h @@ -35,6 +35,7 @@ inline const char* struct2str(int stype) case Bastion: return "bastion"; case End_City: return "end_city"; case End_Gateway: return "end_gateway"; + case Ancient_City: return "ancient_city"; } return "?"; } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 2224420..beffa4f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -7,6 +7,7 @@ #include "aboutdialog.h" #include "conditiondialog.h" #include "extgendialog.h" +#include "biomecolordialog.h" #if WITH_UPDATER #include "updater.h" @@ -344,6 +345,12 @@ int MainWindow::getDim() return 0; } +void MainWindow::setBiomeColorRc(QString rc) +{ + config.biomeColorPath = rc; + onBiomeColorChange(); +} + void MainWindow::saveSettings() { QSettings settings("cubiomes-viewer", "cubiomes-viewer"); @@ -837,7 +844,7 @@ void MainWindow::on_actionPreferences_triggered() void MainWindow::onBiomeColorChange() { QFile file(config.biomeColorPath); - if (file.open(QIODevice::ReadOnly)) + if (!config.biomeColorPath.isEmpty() && file.open(QIODevice::ReadOnly)) { char buf[32*1024]; qint64 siz = file.read(buf, sizeof(buf)-1); @@ -853,7 +860,7 @@ void MainWindow::onBiomeColorChange() { initBiomeColors(biomeColors); } - ui->mapView->refresh(); + ui->mapView->refreshBiomeColors(); } void MainWindow::on_actionGo_to_triggered() @@ -878,6 +885,12 @@ void MainWindow::on_actionOpen_shadow_seed_triggered() } } +void MainWindow::on_actionBiome_colors_triggered() +{ + BiomeColorDialog *dialog = new BiomeColorDialog(this, config.biomeColorPath); + dialog->show(); +} + void MainWindow::on_actionPresetLoad_triggered() { WorldInfo wi; diff --git a/src/mainwindow.h b/src/mainwindow.h index 2034fb5..370f53b 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -50,6 +50,7 @@ public: bool setSeed(WorldInfo wi, int dim = INT_MAX); int getDim(); MapView *getMapView(); + void setBiomeColorRc(QString rc); protected: @@ -77,6 +78,7 @@ private slots: void on_actionGo_to_triggered(); void on_actionScan_seed_for_Quad_Huts_triggered(); void on_actionOpen_shadow_seed_triggered(); + void on_actionBiome_colors_triggered(); void on_actionPresetLoad_triggered(); void on_actionExamples_triggered(); void on_actionAbout_triggered(); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index f577ef8..418a165 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -773,6 +773,7 @@ QToolButton:checked { Map + @@ -903,6 +904,11 @@ QToolButton:checked { Load filter preset... + + + Biome colors... + + diff --git a/src/mapview.cpp b/src/mapview.cpp index da6b28f..276de68 100644 --- a/src/mapview.cpp +++ b/src/mapview.cpp @@ -135,6 +135,12 @@ void MapView::setConfig(const Config& c) update(2); } +void MapView::refreshBiomeColors() +{ + if (world) + world->refreshBiomeColors(); +} + void MapView::settingsToWorld() { if (!world) diff --git a/src/mapview.h b/src/mapview.h index 9669e6c..f78e8b7 100644 --- a/src/mapview.h +++ b/src/mapview.h @@ -44,6 +44,7 @@ public: bool getShow(int stype) { return stype >= 0 && stype < STRUCT_NUM ? sshow[stype] : false; } void setShow(int stype, bool v); void setConfig(const Config& config); + void refreshBiomeColors(); void timeout(); diff --git a/src/quad.cpp b/src/quad.cpp index 11e112e..88e5040 100644 --- a/src/quad.cpp +++ b/src/quad.cpp @@ -14,7 +14,7 @@ Quad::Quad(const Level* l, int i, int j) : wi(l->wi),dim(l->dim),g(&l->g),scale(l->scale) , ti(i),tj(j),blocks(l->blocks),pixs(l->pixs),sopt(l->sopt) - , rgb(),img(),spos() + , biomes(),rgb(),img(),spos() , done(),isdel(l->isdel) , prio(),stopped() { @@ -23,6 +23,7 @@ Quad::Quad(const Level* l, int i, int j) Quad::~Quad() { + if (biomes) free(biomes); delete img; delete spos; delete [] rgb; @@ -104,6 +105,7 @@ void getStructs(std::vector *out, const StructureConfig sconf, } } +QMutex g_mutex(QMutex::NonRecursive); void Quad::run() { @@ -115,10 +117,10 @@ void Quad::run() int y = (scale > 1) ? wi.y >> 2 : wi.y; int x = ti*pixs, z = tj*pixs, w = pixs+SEAM_BUF, h = pixs+SEAM_BUF; Range r = {scale, x, z, w, h, y, 1}; - int *b = allocCache(g, r); - if (!b) return; + biomes = allocCache(g, r); + if (!biomes) return; - int err = genBiomes(g, b, r); + int err = genBiomes(g, biomes, r); if (err) { fprintf( @@ -128,11 +130,15 @@ void Quad::run() mc2str(g->mc), g->seed, g->dim, x, z, w, h, scale); for (int i = 0; i < w*h; i++) - b[i] = -1; + biomes[i] = -1; } + + // sync biomeColors + g_mutex.lock(); + g_mutex.unlock(); + rgb = new uchar[w*h * 3]; - biomesToImage(rgb, biomeColors, b, w, h, 1, 1); - free(b); + biomesToImage(rgb, biomeColors, biomes, w, h, 1, 1); img = new QImage(rgb, w, h, 3*w, QImage::Format_RGB888); } else @@ -469,6 +475,23 @@ int QWorld::getBiome(Pos p) return id; } +void QWorld::refreshBiomeColors() +{ + g_mutex.lock(); + for (Level& l : lvb) + { + for (Quad *q : l.cells) + { + QImage *img = q->img; + if (!img) + continue; + biomesToImage(q->rgb, biomeColors, q->biomes, img->width(), img->height(), 1, 1); + } + } + g_mutex.unlock(); +} + + void QWorld::cleancache(std::vector& cache, unsigned int maxsize) { size_t n = cache.size(); @@ -910,7 +933,7 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz, } int pixs = lvb[0].pixs + SEAM_BUF; - unsigned int cachesize = memlimit / pixs / pixs / 3; + unsigned int cachesize = memlimit / pixs / pixs / 7; // sizeof(RGB) + sizeof(biome_id) cleancache(cachedbiomes, cachesize); cleancache(cachedstruct, cachesize); diff --git a/src/quad.h b/src/quad.h index 06ea197..258c6bd 100644 --- a/src/quad.h +++ b/src/quad.h @@ -160,6 +160,7 @@ public: int pixs; int sopt; + int *biomes; uchar *rgb; // img and spos act as an atomic gate (with NULL or non-NULL indicating available results) @@ -217,6 +218,8 @@ struct QWorld int getBiome(Pos p); + void refreshBiomeColors(); + WorldInfo wi; int dim; Generator g;