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 @@
+
+
@@ -1071,8 +1078,6 @@
-
-
-
+
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;