Prechádzať zdrojové kódy

adding better task handling

Nicolas Winkler 5 rokov pred
rodič
commit
6b861694a5

+ 40 - 7
Almond.cpp

@@ -4,7 +4,6 @@
 #include <QMessageBox>
 #include <QGradient>
 #include "gradientchoosedialog.h"
-#include "ImageExport.h"
 
 #include <cmath>
 
@@ -44,16 +43,37 @@ Almond::~Almond(void)
 
 void Almond::submitBackgroundTask(BackgroundTask* task)
 {
-    bool taken = QThreadPool::globalInstance()->tryTake(task->getRunnable());
-    if (taken) {
+    QObject::connect(task, &BackgroundTask::finished, this, &Almond::backgroundTaskFinished);
+    QObject::connect(task, &BackgroundTask::progress, this, &Almond::backgroundTaskProgress);
+    backgroundTasks.start(task);
+    //if (taken) {
         ui.backgroundProgress->setRange(0, 0);
         ui.backgroundProgress->setVisible(true);
-    }
+        ui.backgroundProgress->setFormat("");
+    //}
 }
 
 
-void Almond::backgroundTaskFinished(void)
+void Almond::backgroundTaskFinished(bool succ)
 {
+    ui.backgroundProgress->setVisible(false);
+    ui.backgroundProgress->setFormat("");
+}
+
+
+void Almond::backgroundTaskProgress(float percentage)
+{
+    QObject* task = QObject::sender();
+    if (auto* bt = qobject_cast<BackgroundTask*>(task)) {
+        ui.backgroundProgress->setFormat(QString::fromStdString(bt->getShortDescription() + ": %p%"));
+    }
+    if (percentage > 0) {
+        ui.backgroundProgress->setRange(0, 100);
+        ui.backgroundProgress->setValue(percentage);
+    }
+    else {
+        ui.backgroundProgress->setRange(0, 0);
+    }
 }
 
 
@@ -99,6 +119,11 @@ void Almond::on_exportVideo_clicked()
     if (response == 1) {
         mnd::MandelInfo mi;
         evi = dialog.getExportVideoInfo();
+        MandelVideoGenerator mvg(evi);
+        mnd::MandelGenerator& g = *mw->getGenerator();
+        submitBackgroundTask(new VideoExportTask(std::move(mvg), g));
+        //if (exportVideo(evi)) {
+
         //Video
         /*mi.maxIter = dialog.getMaxIterations();
         mi.view = mw->getViewport();
@@ -146,8 +171,16 @@ void Almond::on_exportImage_clicked()
         alm::ImageExportInfo iei;
         iei.drawInfo = mi;
         iei.generator = &g;
-        iei.gradient = &mw->getGradient();
-        alm::exportPng(dialog.getPath().toStdString(), iei);
+        iei.gradient = mw->getGradient();
+        iei.path = dialog.getPath().toStdString();
+        submitBackgroundTask(new ImageExportTask(iei));
+
+        /*auto exprt = [iei, path = dialog.getPath().toStdString()]() {
+            alm::exportPng(path, iei);
+        };
+
+        submitBackgroundTask();*/
+        
         /*auto fmap = Bitmap<float>(mi.bWidth, mi.bHeight);
         g.generate(mi, fmap.pixels.get());
         auto bitmap = fmap.map<RGBColor>([&mi, this] (float i) {

+ 3 - 1
Almond.h

@@ -34,6 +34,7 @@ class Almond : public QMainWindow
     Q_OBJECT
 private:
     mnd::MandelContext mandelContext;
+    QThreadPool backgroundTasks;
 public:
     std::unique_ptr<MandelWidget> mw;
 private:
@@ -72,7 +73,8 @@ private slots:
 
     void on_chooseGenerator_clicked();
 
-    void backgroundTaskFinished();
+    void backgroundTaskFinished(bool);
+    void backgroundTaskProgress(float percentage);
 
     void pointSelected(mnd::Real x, mnd::Real y);
 

+ 2 - 1
Almond.pro

@@ -123,7 +123,8 @@ unix|win32: LIBS += -L$FFMPEGPATH -lswscale
 #INCLUDEPATH += $$PWD/../libs/ffmpeg-4.1.1-win32-dev/include
 #DEPENDPATH += $$PWD/../libs/ffmpeg-4.1.1-win32-dev/include
 
-RESOURCES += Almond.qrc
+RESOURCES += Almond.qrc \
+    splash.qrc
 
 unix|win32: LIBS += -L$$PWD/libmandel/ -L$$PWD/libalmond/ -lmandel -lqd -lasmjit -lalmond -lpng
 unix: LIBS += -lrt

+ 36 - 4
BackgroundTask.cpp

@@ -1,14 +1,46 @@
 #include "BackgroundTask.h"
+#include <QMessageBox>
 
-BackgroundTask::BackgroundTask(QRunnable* q) :
-    task{ q }
+BackgroundTask::BackgroundTask(const std::string& shortDescription) :
+    shortDescription{ shortDescription }
 {
 }
 
 
-QRunnable* BackgroundTask::getRunnable(void)
+ImageExportTask::ImageExportTask(const alm::ImageExportInfo& iei) :
+    BackgroundTask{ "exporting image" },
+    iei{ iei }
 {
-    return nullptr;
 }
 
 
+void ImageExportTask::run(void)
+{
+    alm::exportPng(iei, [this](float percentage) {
+        emit progress(percentage);
+    });
+    emit finished(true);
+}
+
+
+VideoExportTask::VideoExportTask(MandelVideoGenerator mvg, mnd::MandelGenerator& generator) :
+    BackgroundTask{ "exporting video" },
+    mvg{ std::move(mvg) },
+    generator{ generator }
+{
+}
+
+
+void VideoExportTask::run(void)
+{
+
+    mvg.addProgressCallback([this](const MandelVideoProgressInfo& mvpi) {
+        emit progress(0);
+    });
+    mvg.generate(generator);
+    emit finished(true);
+    QMessageBox* msgBox = new QMessageBox;
+    msgBox->setText("Video successfully exported.");
+    msgBox->exec();
+}
+

+ 42 - 4
BackgroundTask.h

@@ -1,15 +1,53 @@
 #ifndef BACKGROUNDTASK_H
 #define BACKGROUNDTASK_H
 
+#include <QObject>
 #include <QRunnable>
+#include <string>
+#include <functional>
+#include "ImageExport.h"
+#include "MandelVideoGenerator.h"
 
-class BackgroundTask
+class BackgroundTask : public QObject, public QRunnable
 {
-    QRunnable* task;
+    Q_OBJECT
+protected:
+    std::string shortDescription;
 public:
-    BackgroundTask(QRunnable* task);
+    BackgroundTask(const std::string& shortDescription);
 
-    QRunnable* getRunnable(void);
+    void run(void) = 0;
+
+    inline const std::string& getShortDescription(void) const { return shortDescription; }
+
+signals:
+    void progress(float percentage);
+    void finished(bool success);
+};
+
+
+class ImageExportTask : public BackgroundTask
+{
+    Q_OBJECT
+private:
+    const alm::ImageExportInfo iei;
+public:
+    ImageExportTask(const alm::ImageExportInfo& iei);
+
+    void run(void) override;
+};
+
+
+class VideoExportTask : public BackgroundTask
+{
+    Q_OBJECT
+private:
+    MandelVideoGenerator mvg;
+    mnd::MandelGenerator& generator;
+public:
+    VideoExportTask(MandelVideoGenerator mvg, mnd::MandelGenerator& generator);
+
+    void run(void) override;
 };
 
 #endif // BACKGROUNDTASK_H

+ 2 - 2
CMakeLists.txt

@@ -25,9 +25,9 @@ FILE(GLOB AlmondHeaders *.h)
 
 
 IF (WIN32)
-    add_executable(Almond WIN32 ${AlmondSources} Almond.qrc icon.rc)
+    add_executable(Almond WIN32 ${AlmondSources} Almond.qrc splash.qrc icon.rc)
 ELSE()
-    add_executable(Almond ${AlmondSources} Almond.qrc)
+    add_executable(Almond ${AlmondSources} Almond.qrc splash.qrc)
 ENDIF()
 
 add_subdirectory(libalmond)

+ 0 - 6
exportdialogs.cpp

@@ -166,12 +166,6 @@ void ExportVideoDialog::on_buttonBox_accepted()
     evi.end.adjustAspectRatio(evi.width, evi.height);
     evi.gradient = almond->mw->getGradient();
 
-    MandelVideoGenerator mvg(evi);
-    mvg.generate(*almond->mw->getGenerator());
-    //if (exportVideo(evi)) {
-        QMessageBox* msgBox = new QMessageBox;
-        msgBox->setText("Video successfully exported.");
-        msgBox->exec();
     //}
 }
 

+ 13 - 3
libalmond/include/ImageExport.h

@@ -3,7 +3,7 @@
 
 #include "Mandel.h"
 #include "Gradient.h"
-#include "../../libmandel/include/Mandel.h"
+#include <functional>
 
 namespace alm
 {
@@ -11,10 +11,20 @@ namespace alm
     {
         mnd::MandelInfo drawInfo;
         mnd::MandelGenerator* generator;
-        const Gradient* gradient;
+        Gradient gradient;
+        std::string path;
     };
 
-    void exportPng(const std::string& name, const ImageExportInfo& iei);
+    /**
+     * \brief generates and saves a fractal image in png format.
+     * 
+     * \param iei               info to generate the image
+     * \param progressCallback  optional function that is called to
+     *                          report progress; the float parameter
+     *                          contains a value from 0 to 100
+     */
+    void exportPng(const ImageExportInfo& iei,
+        std::function<void(float)> progressCallback = [](float){});
 }
 
 

+ 14 - 10
libalmond/src/ImageExport.cpp

@@ -7,13 +7,16 @@ namespace alm
 {
 
 
-void exportPng(const std::string& name, const ImageExportInfo& iei)
+void exportPng(const ImageExportInfo& iei, std::function<void(float)> progressCallback)
 {
     if (iei.generator == nullptr) {
         throw "no generator";
     }
+
+    progressCallback(0.0f);
+
     mnd::MandelGenerator& generator = *iei.generator;
-    FILE* file = fopen(name.c_str(), "wb");
+    FILE* file = fopen(iei.path.c_str(), "wb");
     if(!file) exit(1);
 
     png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
@@ -40,13 +43,11 @@ void exportPng(const std::string& name, const ImageExportInfo& iei)
     );
     png_write_info(png, info);
 
-    long chunkHeight = 512;
-    if (width <= 1024 && height <= 1024) {
-        chunkHeight = 1024;
-    }
-    if (width >= 4096) {
-        chunkHeight = 128;
-    }
+    long chunkHeight = height / 20;
+    if (chunkHeight < 1)
+        chunkHeight = 1;
+    if (width >= 4096 && chunkHeight > 64)
+        chunkHeight = 64;
 
     auto rowPointers = std::make_unique<png_byte*[]>(chunkHeight);
     for (long chunkY = 0; chunkY < height; chunkY += chunkHeight) {
@@ -60,12 +61,14 @@ void exportPng(const std::string& name, const ImageExportInfo& iei)
         Bitmap<float> chunk(width, tmpHeight);
         generator.generate(chunkInfo, chunk.pixels.get());
         Bitmap<RGBColor> coloredChunk = chunk.map<RGBColor>([&iei] (float i) {
-            return i >= iei.drawInfo.maxIter ? RGBColor{ 0, 0, 0 } : iei.gradient->get(i);
+            return i >= iei.drawInfo.maxIter ? RGBColor{ 0, 0, 0 } : iei.gradient.get(i);
         });
         for (long i = 0; i < tmpHeight; i++) {
             rowPointers[i] = reinterpret_cast<png_byte*>(&coloredChunk.get(0, i));
         }
         png_write_rows(png, &rowPointers[0], tmpHeight);
+        if (chunkY < height)
+            progressCallback(100.0f * chunkY / height);
     }
 
     png_write_end(png, NULL);
@@ -73,6 +76,7 @@ void exportPng(const std::string& name, const ImageExportInfo& iei)
     fclose(file);
 
     png_destroy_write_struct(&png, &info);
+    progressCallback(100.0f);
 }
 
 

+ 61 - 0
libmandel/src/NaiveIRGenerator.cpp

@@ -13,6 +13,67 @@ namespace mnd
     template class NaiveIRGenerator<mnd::QuadDouble>;
 }
 
+namespace eval {
+    struct Load;
+    struct Store;
+    struct Add;
+    struct Sub;
+    struct Mul;
+    struct Div;
+    struct Neg;    
+    struct Atan;
+    struct Pow;
+    struct Cos;
+    struct Sin;
+    struct Exp;
+    struct Ln;
+
+    using EvalNode = std::variant<
+        Load,
+        Store,
+        Add,
+        Sub,
+        Mul,
+        Div,
+        Neg,
+        Atan,
+        Pow,
+        Cos,
+        Sin,
+        Exp,
+        Ln
+    >;
+
+    struct Load { int index; };
+    struct Store { int index; };
+
+    struct BinaryOperation
+    {
+        std::unique_ptr<EvalNode> a;
+        std::unique_ptr<EvalNode> b;
+    };
+
+    struct UnaryOperation
+    {
+        std::unique_ptr<EvalNode> a;
+    };
+
+    struct Add : BinaryOperation {};
+    struct Sub : BinaryOperation {};
+    struct Mul : BinaryOperation {};
+    struct Div : BinaryOperation {};
+
+    struct Neg : UnaryOperation {};
+
+
+    struct Atan : BinaryOperation {};
+    struct Pow : BinaryOperation {};
+    struct Cos : UnaryOperation {};
+    struct Sin : UnaryOperation {};
+    struct Exp : UnaryOperation {};
+    struct Ln : UnaryOperation {};
+}
+
 
 template<typename T>
 NaiveIRGenerator<T>::NaiveIRGenerator(const mnd::ir::Formula& irf,

+ 15 - 0
main.cpp

@@ -1,10 +1,25 @@
 #include "Almond.h"
 #include <QtWidgets/QApplication>
+#include <QPixmap>
+#include <QDesktopWidget>
+#include <QSplashScreen>
 
 int main(int argc, char *argv[])
 {
     QApplication a(argc, argv);
+
+    QRect screenDim = a.desktop()->screenGeometry();
+    int splashW = screenDim.width() * 2 / 11;
+
+    QPixmap splashImg(":/splash/splash");
+    QPixmap splashScaled = splashImg.scaled(splashW, splashW * splashImg.height() / splashImg.width());
+    QSplashScreen splash{ splashScaled };
+    splash.show();
+    a.processEvents();
+
     Almond w;
     w.show();
+    splash.finish(&w);
     return a.exec();
 }
+

BIN
splash.png


+ 5 - 0
splash.qrc

@@ -0,0 +1,5 @@
+<RCC>
+    <qresource prefix="/splash">
+        <file alias="splash">splash.png</file>
+    </qresource>
+</RCC>