diff --git a/OpenSprite.pro b/OpenSprite.pro index ebb2407..919ffdb 100644 --- a/OpenSprite.pro +++ b/OpenSprite.pro @@ -23,6 +23,7 @@ SOURCES += \ main.cpp \ mainwindow.cpp \ palette.cpp \ + rotations/rotationdialog.cpp \ sprite.cpp \ spriteview.cpp @@ -34,6 +35,7 @@ HEADERS += \ fileio.h \ mainwindow.h \ palette.h \ + rotations/rotationdialog.h \ sprite.h \ spriteview.h @@ -41,7 +43,8 @@ FORMS += \ animations/animationdialog.ui \ animations/animationform.ui \ exportdialog.ui \ - mainwindow.ui + mainwindow.ui \ + rotations/rotationdialog.ui RC_ICONS = icons/opensprite96x96.ico diff --git a/exportdialog.ui b/exportdialog.ui index 2ee784b..b5f1fbc 100644 --- a/exportdialog.ui +++ b/exportdialog.ui @@ -16,6 +16,10 @@ OpenSprite Export + + + :/icons/opensprite96x96.png:/icons/opensprite96x96.png + diff --git a/mainwindow.cpp b/mainwindow.cpp index 1d66eb0..611d4b5 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -5,6 +5,7 @@ #include "exportdialog.h" #include "animations/animationdialog.h" +#include "rotations/rotationdialog.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) @@ -632,7 +633,7 @@ void MainWindow::on_actionAbout_triggered() { QMessageBox msgBox(this); msgBox.setTextFormat(Qt::RichText); - msgBox.setText("Version: 1.3 (02 / 2024)
Author: Johannes Winkler
License: GNU GPL License
https://github.com/jowin202/OpenSprite"); + msgBox.setText("Version: 1.4 (04 / 2024)
Author: Johannes Winkler
License: GNU GPL License
https://github.com/jowin202/OpenSprite"); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.exec(); } @@ -757,3 +758,13 @@ void MainWindow::on_actionMC_1_MC_2_triggered() this->ui->graphicsView->scene()->update(); } + +void MainWindow::on_actionRotate_triggered() +{ + RotationDialog *dialog = new RotationDialog(&opt); + dialog->show(); + connect(dialog, &RotationDialog::finished, [=](){ + this->ui->graphicsView->redraw(); + }); +} + diff --git a/mainwindow.h b/mainwindow.h index 23b6463..5c2f880 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -98,6 +98,8 @@ private slots: void on_actionMC_1_MC_2_triggered(); + void on_actionRotate_triggered(); + private: Ui::MainWindow *ui; AnimationDialog *animation_dialog = 0; diff --git a/mainwindow.ui b/mainwindow.ui index 52b3036..c38da4a 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -17,7 +17,7 @@ OpenSprite - + :/icons/opensprite96x96.png:/icons/opensprite96x96.png @@ -756,6 +756,8 @@ + + @@ -815,7 +817,7 @@ - + :/icons/cut.svg:/icons/cut.svg @@ -827,7 +829,7 @@ - + :/icons/copy.svg:/icons/copy.svg @@ -839,7 +841,7 @@ - + :/icons/paste.svg:/icons/paste.svg @@ -854,7 +856,7 @@ false - + :/icons/paste.svg:/icons/paste.svg @@ -871,7 +873,7 @@ - + :/icons/move-up.svg:/icons/move-up.svg @@ -883,7 +885,7 @@ - + :/icons/move-down.svg:/icons/move-down.svg @@ -895,7 +897,7 @@ - + :/icons/move-left.svg:/icons/move-left.svg @@ -907,7 +909,7 @@ - + :/icons/move-right.svg:/icons/move-right.svg @@ -919,7 +921,7 @@ - + :/icons/flip_top-bottom.svg:/icons/flip_top-bottom.svg @@ -928,7 +930,7 @@ - + :/icons/flip_left-right.svg:/icons/flip_left-right.svg @@ -937,7 +939,7 @@ - + :/icons/save.svg:/icons/save.svg @@ -949,7 +951,7 @@ - + :/icons/save.svg:/icons/save.svg @@ -979,7 +981,7 @@ - + :/icons/save_exp.svg:/icons/save_exp.svg @@ -991,7 +993,7 @@ - + :/icons/save_exp.svg:/icons/save_exp.svg @@ -1003,7 +1005,7 @@ - + :/icons/reflect_left_to_right.svg:/icons/reflect_left_to_right.svg @@ -1012,7 +1014,7 @@ - + :/icons/reflect_top_to_bottom.svg:/icons/reflect_top_to_bottom.svg @@ -1058,6 +1060,11 @@ MC 1 <==> MC 2 + + + Rotate + + @@ -1071,8 +1078,6 @@
palette.h
- - - + diff --git a/rotations/rotationdialog.cpp b/rotations/rotationdialog.cpp new file mode 100644 index 0000000..47fea81 --- /dev/null +++ b/rotations/rotationdialog.cpp @@ -0,0 +1,240 @@ +#include "rotationdialog.h" +#include "ui_rotationdialog.h" + +#include "sprite.h" + +RotationDialog::RotationDialog(options *opt, QWidget *parent) : + QWidget(parent), + ui(new Ui::RotationDialog) +{ + ui->setupUi(this); + this->opt = opt; + + this->base_sprite = opt->data.value("sprites").toArray().at(opt->current_sprite).toObject(); + connect(this->ui->spin_angle, SIGNAL(valueChanged(int)), this, SLOT(update_rotation())); + connect(this->ui->spin_steps, SIGNAL(valueChanged(int)), this, SLOT(update_rotation())); + + this->update_rotation(); +} + +RotationDialog::~RotationDialog() +{ + delete ui; +} + +void RotationDialog::update_rotation() +{ + this->result_list.clear(); + int angle = this->ui->spin_angle->value(); + int steps = this->ui->spin_steps->value(); + + for (int i = 0; i < steps; i++) + { + this->result_list.append(this->rotate_by(this->base_sprite,i*angle)); + } + + + QImage wallpaper(opt->sprite_spacing_x+(10*24+opt->sprite_spacing_x)*(opt->sprites_per_row),opt->sprite_spacing_y+(10*21+opt->sprite_spacing_y)*(steps/opt->sprites_per_row+1), QImage::Format_RGB16); + wallpaper.fill(opt->background); + QPainter painter; + painter.begin(&wallpaper); + for (int i = 0; i < this->result_list.count(); i++) + { + QImage sprite = this->draw_sprite(i); + painter.drawImage(opt->sprite_spacing_x+(10*24+opt->sprite_spacing_x)*(i% opt->sprites_per_row),opt->sprite_spacing_y+(10*21+opt->sprite_spacing_y)*(i/opt->sprites_per_row), sprite); + } + painter.end(); + wallpaper = wallpaper.scaledToHeight(wallpaper.height()/2, Qt::SmoothTransformation); + this->ui->preview->setPixmap(QPixmap::fromImage(wallpaper)); +} + +QJsonObject RotationDialog::rotate_by(QJsonObject sprite, int angle) +{ + Q_UNUSED(angle); + + //TODO actual rotation algorithm + + /* + //remove this + QJsonArray sprite_data1 = sprite.value("sprite_data").toArray(); + QJsonArray sprite_data2 = sprite_data1.at(0).toArray(); + sprite_data2.removeAt(angle); + sprite_data2.insert(angle,1); + sprite_data1.removeAt(0); + sprite_data1.insert(0,sprite_data2); + sprite.insert("sprite_data", sprite_data1); + */ + + return sprite; +} + + +int RotationDialog::get_sprite_bit(int sprite_id, int x, int y) +{ + if (x < 0 || y < 0 || x >= 24 || y >= 21) return false; + if (this->result_list.count() <= sprite_id) return false; + return this->result_list.at(sprite_id).value("sprite_data").toArray().at(y).toArray().at(x).toInt() > 0; +} + + +QImage RotationDialog::draw_sprite(int sprite_id) +{ + QPainter painter; + QImage img(10*24, 10*21, QImage::Format_RGB16); + painter.begin(&img); + bool expand_x = false; //ignore + bool expand_y = false; //ignore + + + if (this->result_list.at(sprite_id).value("mc_mode").toBool()) + { + int w = 20 * (expand_y ? 0.5 : 1); + int h = 10 * (expand_x ? 0.5 : 1); + for (int y = 0; y < 21; y++) + { + for (int x = 0; x < 12; x++) + { + if (this->get_sprite_bit(sprite_id,2*x,y)== 0 && this->get_sprite_bit(sprite_id,2*x+1,y) == 0) + { + painter.fillRect(x*w,y*h,w+1,h+1,this->opt->col_list.at(opt->data.value("background").toInt())); + } + else if (this->get_sprite_bit(sprite_id,2*x,y)== 1 && this->get_sprite_bit(sprite_id,2*x+1,y) == 0) + { + painter.fillRect(x*w,y*h,w+1,h+1,this->opt->col_list.at(this->result_list.at(sprite_id).value("sprite_color").toInt())); + } + else if (this->get_sprite_bit(sprite_id,2*x,y) == 0 && this->get_sprite_bit(sprite_id,2*x+1,y) == 1) + { + painter.fillRect(x*w,y*h,w+1,h+1,this->opt->col_list.at(this->opt->data.value("mc1").toInt())); + } + else if (this->get_sprite_bit(sprite_id,2*x,y) == 1 && this->get_sprite_bit(sprite_id,2*x+1,y) == 1) + { + painter.fillRect(x*w,y*h,w+1,h+1,this->opt->col_list.at(this->opt->data.value("mc2").toInt())); + } + + } + } + } + else //not multicol + { + int w = 10 * (expand_y ? 0.5 : 1); + int h = 10 * (expand_x ? 0.5 : 1); + for (int y = 0; y < 21; y++) + { + for (int x = 0; x < 24; x++) + { + if (this->get_sprite_bit(sprite_id,x,y) == 1) + { + painter.fillRect(w*x,h*y,w+1,h+1,this->opt->col_list.at(this->result_list.at(sprite_id).value("sprite_color").toInt())); + } + else + { + painter.fillRect(w*x,h*y,w+1,h+1,this->opt->col_list.at(opt->data.value("background").toInt())); + } + } + } + } + + + /* + if (this->result_list.at(sprite_id).value("overlay_next").toBool() && opt->sprite_list.length() > sprite_id+1) + { + if (this->result_list.at(sprite_id+1).value("mc_mode").toBool()) + { + int w = 20 * (expand_y ? 0.5 : 1); + int h = 10 * (expand_x ? 0.5 : 1); + for (int y = 0; y < 21; y++) + { + for (int x = 0; x < 12; x++) + { + if (opt->sprite_list.at(sprite_id+1)->get_bit(2*x,y)== 1 && opt->sprite_list.at(sprite_id+1)->get_bit(2*x+1,y) == 0) + { + painter.fillRect(x*w,y*h,w+1,h+1,this->opt->col_list.at(this->result_list.at(sprite_id+1).value("sprite_color").toInt())); + } + else if (opt->sprite_list.at(sprite_id+1)->get_bit(2*x,y) == 0 && opt->sprite_list.at(sprite_id+1)->get_bit(2*x+1,y) == 1) + { + painter.fillRect(x*w,y*h,w+1,h+1,this->opt->col_list.at(this->opt->data.value("mc1").toInt())); + } + else if (opt->sprite_list.at(sprite_id+1)->get_bit(2*x,y) == 1 && opt->sprite_list.at(sprite_id+1)->get_bit(2*x+1,y) == 1) + { + painter.fillRect(x*w,y*h,w+1,h+1,this->opt->col_list.at(this->opt->data.value("mc2").toInt())); + } + + } + } + } + else + { + int w = 10 * (expand_y ? 0.5 : 1); + int h = 10 * (expand_x ? 0.5 : 1); + for (int y = 0; y < 21; y++) + { + for (int x = 0; x < 24; x++) + { + if (opt->sprite_list.at(sprite_id+1)->get_bit(x,y) == 1) + { + painter.fillRect(w*x,h*y,w+1,h+1,this->opt->col_list.at(this->opt->data.value("sprites").toArray().at(sprite_id+1).toObject().value("sprite_color").toInt())); + } + } + } + } + } + */ + + + + if (opt->show_grid_lines) + { + + int w = 10 * (expand_y ? 0.5 : 1); + int h = 10 * (expand_x ? 0.5 : 1); + + QPen pen; + pen.setColor(Qt::gray); + pen.setWidthF(0.5); + painter.setPen(pen); + for (int y = 0; y < 21; y++) + painter.drawLine(0, h*y, 24*w, h*y); + for (int x = 0; x < 24; x+=2) + painter.drawLine(w*x, 0, w*x, h*21); + + if (!this->result_list.at(sprite_id).value("mc_mode").toBool()) + for (int x = 1; x < 24; x+=2) + painter.drawLine(w*x, 0, w*x, h*21); + } + return img; +} + + + + + +void RotationDialog::on_button_cancel_clicked() +{ + this->close(); +} + + +void RotationDialog::on_button_ok_clicked() +{ + opt->undoDB.append(opt->data); + + QJsonArray sprites = opt->data.value("sprites").toArray(); + + int skip = 1; //insert 1 later when original is kept + if (this->ui->check_replace->isChecked()) + { + sprites.removeAt(opt->current_sprite); + skip = 0; + } + + for (int i = 0; i < result_list.count(); i++) + { + sprites.insert(opt->current_sprite+skip+i, result_list.at(i)); + } + + opt->data.insert("sprites", sprites); + + emit finished(); + this->close(); +} + diff --git a/rotations/rotationdialog.h b/rotations/rotationdialog.h new file mode 100644 index 0000000..1696fcc --- /dev/null +++ b/rotations/rotationdialog.h @@ -0,0 +1,57 @@ +#ifndef ROTATIONDIALOG_H +#define ROTATIONDIALOG_H + +#include +#include +#include +#include + +#include +#include + +class Sprite; + +namespace Ui { +class RotationDialog; +} + +class options; + +class RotationDialog : public QWidget +{ + Q_OBJECT + +public: + explicit RotationDialog(options *opt, QWidget *parent = nullptr); + ~RotationDialog(); + + void keyPressEvent(QKeyEvent *e) { + if(e->key() == Qt::Key_Escape) + this->close(); + else if(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) + this->on_button_ok_clicked(); + } + +private slots: + void update_rotation(); + QJsonObject rotate_by(QJsonObject sprite, int angle); + + + QImage draw_sprite(int sprite_id); + int get_sprite_bit(int sprite_id, int x, int y); + + void on_button_cancel_clicked(); + void on_button_ok_clicked(); + + +signals: + void finished(); + +private: + QList result_list; + Ui::RotationDialog *ui; + options *opt; + QJsonObject base_sprite; +}; + +#endif // ROTATIONDIALOG_H diff --git a/rotations/rotationdialog.ui b/rotations/rotationdialog.ui new file mode 100644 index 0000000..5956e6d --- /dev/null +++ b/rotations/rotationdialog.ui @@ -0,0 +1,142 @@ + + + RotationDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 651 + 507 + + + + OpenSprite + + + + :/icons/opensprite96x96.png:/icons/opensprite96x96.png + + + + + + + + 1 + + + 360 + + + + + + + Steps: + + + + + + + Angle: + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + 360 + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Replace original + + + + + + + Cancel + + + + + + + Add Sprites + + + + + + + + + true + + + + + 0 + 0 + 522 + 449 + + + + + + + + + + + + + + + + + + + + + diff --git a/sprite.h b/sprite.h index 635dee4..40e057c 100644 --- a/sprite.h +++ b/sprite.h @@ -297,10 +297,7 @@ class Sprite : public QGraphicsItem private: int id; - //unsigned char sprite_data[64]; bool overlay_next = false; - //bool multi_color_mode = true; - bool left_pressed = false; bool right_pressed = false;