Ver Fonte

video still problematic

Nicolas Winkler há 5 anos atrás
pai
commit
bbea34181b
6 ficheiros alterados com 434 adições e 82 exclusões
  1. 168 12
      Gradient.cpp
  2. 11 4
      Gradient.h
  3. 147 42
      MandelWidget.cpp
  4. 48 9
      MandelWidget.h
  5. 54 14
      VideoStream.cpp
  6. 6 1
      VideoStream.h

+ 168 - 12
Gradient.cpp

@@ -1,7 +1,9 @@
 #include "Gradient.h"
 
 #include <cmath>
+#include <algorithm>
 
+/*
 Gradient::Gradient()
 {
     //addColor(RGBColor{255, 0, 0}, -100);
@@ -109,18 +111,153 @@ Gradient::Gradient()
     max = 10000;
     //addColor(RGBColor{255, 0, 255}, 1000000000.0f);
 }
+*/
 
-void Gradient::addColor(RGBColor c, float value)
+Gradient::Gradient(void) :
+    max{ 1.0 }
 {
-    //colors.push_back({ c, value }); return;
-    for (std::vector<std::pair<RGBColor, float>>::iterator it = colors.begin();
-         it != colors.end(); ++it) {
-        if (it->second > value) {
-            colors.insert(it, { c, value });
-            return;
+}
+
+
+Gradient::Gradient(const std::vector<std::pair<RGBColor, float>>& colors, int precalcSteps)
+{
+    if(colors.empty())
+        return;
+    /*std::sort(colors.begin(), colors.end(),
+              [] (const std::pair<RGBColor, float>& a, const std::pair<RGBColor, float>& b) {
+        return a.second < b.second;
+    });*/
+
+    max = colors.at(colors.size() - 1).second;
+    for (int i = 0; i < precalcSteps; i++) {
+        float position = i * max / precalcSteps;
+        RGBColor left = RGBColor{ 0, 0, 0 };
+        RGBColor right = RGBColor{ 0, 0, 0 };
+        float lerp = 0.0f;
+        RGBColor atPosition = RGBColor{ 0, 0, 0 };
+        // slow, but not in any critical path
+        for (auto it = colors.begin(); it != colors.end(); ++it) {
+            if (it->second > position) {
+                if (it == colors.begin()) {
+                    atPosition = it->first;
+                    break;
+                }
+                else {
+                    float lerp = (position - (it - 1)->second) / (it->second - (it - 1)->second);
+                    atPosition = lerpColors((it - 1)->first, it->first, lerp);
+                    break;
+                }
+            }
         }
+        this->colors.push_back(atPosition);
     }
-    colors.push_back({ c, value });
+}
+
+
+Gradient Gradient::defaultGradient(void)
+{
+    std::vector<std::pair<RGBColor, float>> colors = {
+        { RGBColor{0, 0, 0}, 0 },
+        { RGBColor{ 250, 70, 24 }, 40 },
+        { RGBColor{ 200, 230, 30 }, 104 },
+        { RGBColor{ 70, 223, 30 }, 190 },
+        { RGBColor{ 14, 20, 150 }, 295 },
+        { RGBColor{ 36, 155, 169 }, 418 },
+        { RGBColor{ 233, 33, 79 }, 558 },
+        { RGBColor{ 254, 169, 63 }, 714 },
+        { RGBColor{ 227, 93, 201 }, 885 },
+        { RGBColor{ 188, 24, 161 }, 1071 },
+        { RGBColor{ 45, 225, 44 }, 1271 },
+        { RGBColor{ 52, 58, 250 }, 1485 },
+        { RGBColor{ 87, 93, 241 }, 1712 },
+        { RGBColor{ 6, 37, 208 }, 1952 },
+        { RGBColor{ 193, 164, 162 }, 2205 },
+        { RGBColor{ 49, 8, 90 }, 2471 },
+        { RGBColor{ 124, 149, 120 }, 2749 },
+        { RGBColor{ 56, 69, 6 }, 3039 },
+        { RGBColor{ 231, 177, 252 }, 3341 },
+        { RGBColor{ 36, 76, 95 }, 3655 },
+        { RGBColor{ 1, 99, 184 }, 3980 },
+        { RGBColor{ 74, 223, 199 }, 4316 },
+        { RGBColor{ 249, 125, 31 }, 4664 },
+        { RGBColor{ 28, 93, 214 }, 5023 },
+        { RGBColor{ 232, 117, 145 }, 5393 },
+        { RGBColor{ 208, 158, 49 }, 5773 },
+        { RGBColor{ 218, 109, 177 }, 6164 },
+        { RGBColor{ 139, 83, 177 }, 6565 },
+        { RGBColor{ 16, 36, 59 }, 6977 },
+        { RGBColor{ 194, 157, 26 }, 7399 },
+        { RGBColor{ 77, 236, 12 }, 7831 },
+        { RGBColor{ 124, 244, 151 }, 8273 },
+        { RGBColor{ 128, 195, 162 }, 8725 },
+        { RGBColor{ 66, 36, 194 }, 9187 },
+        { RGBColor{ 81, 151, 185 }, 9659 },
+        { RGBColor{ 173, 75, 175 }, 10140 },
+        { RGBColor{ 43, 182, 52 }, 10631 },
+        { RGBColor{ 14, 242, 141 }, 11131 },
+        { RGBColor{ 156, 203, 87 }, 11641 },
+        { RGBColor{ 147, 89, 150 }, 12160 },
+        { RGBColor{ 213, 199, 183 }, 12689 },
+        { RGBColor{ 186, 255, 52 }, 13227 },
+        { RGBColor{ 28, 158, 154 }, 13774 },
+        { RGBColor{ 5, 5, 116 }, 14330 },
+        { RGBColor{ 126, 123, 232 }, 14895 },
+        { RGBColor{ 43, 162, 251 }, 15469 },
+        { RGBColor{ 198, 143, 125 }, 16052 },
+        { RGBColor{ 201, 157, 178 }, 16644 },
+        { RGBColor{ 213, 151, 189 }, 17245 },
+        { RGBColor{ 188, 117, 169 }, 17854 },
+        { RGBColor{ 156, 189, 249 }, 18472 },
+        { RGBColor{ 62, 23, 33 }, 19099 },
+        { RGBColor{ 167, 205, 74 }, 19734 },
+        { RGBColor{ 161, 181, 210 }, 20378 },
+        { RGBColor{ 179, 167, 215 }, 21030 },
+        { RGBColor{ 204, 102, 126 }, 21691 },
+        { RGBColor{ 123, 49, 127 }, 22360 },
+        { RGBColor{ 178, 48, 136 }, 23037 },
+        { RGBColor{ 108, 112, 99 }, 23723 },
+        { RGBColor{ 250, 152, 78 }, 24417 },
+        { RGBColor{ 79, 167, 196 }, 25119 },
+        { RGBColor{ 149, 167, 8 }, 25829 },
+        { RGBColor{ 196, 29, 159 }, 26548 },
+        { RGBColor{ 128, 26, 20 }, 27275 },
+        { RGBColor{ 69, 49, 66 }, 28010 },
+        { RGBColor{ 12, 42, 198 }, 28753 },
+        { RGBColor{ 61, 62, 36 }, 29504 },
+        { RGBColor{ 27, 94, 114 }, 30263 },
+        { RGBColor{ 54, 218, 7 }, 31030 },
+        { RGBColor{ 105, 89, 170 }, 31804 },
+        { RGBColor{ 100, 110, 2 }, 32586 },
+        { RGBColor{ 208, 198, 148 }, 33376 },
+        { RGBColor{ 80, 208, 131 }, 34174 },
+        { RGBColor{ 176, 89, 59 }, 34980 },
+        { RGBColor{ 255, 64, 243 }, 35793 },
+        { RGBColor{ 39, 226, 232 }, 36614 },
+        { RGBColor{ 154, 100, 238 }, 37443 },
+        { RGBColor{ 53, 103, 192 }, 38279 },
+        { RGBColor{ 187, 41, 136 }, 39123 },
+        { RGBColor{ 33, 84, 227 }, 39974 },
+        { RGBColor{ 71, 167, 211 }, 40833 },
+        { RGBColor{ 55, 191, 255 }, 41699 },
+        { RGBColor{ 60, 165, 201 }, 42573 },
+        { RGBColor{ 231, 206, 192 }, 43454 },
+        { RGBColor{ 233, 224, 197 }, 44343 },
+        { RGBColor{ 255, 129, 13 }, 45239 },
+        { RGBColor{ 131, 222, 95 }, 46143 },
+        { RGBColor{ 155, 249, 72 }, 47054 },
+        { RGBColor{ 248, 129, 30 }, 47972 },
+        { RGBColor{ 48, 239, 206 }, 48898 },
+        { RGBColor{ 176, 224, 64 }, 49831 },
+        { RGBColor{ 155, 12, 162 }, 50771 },
+        { RGBColor{ 6, 144, 149 }, 51718 },
+        { RGBColor{ 231, 208, 16 }, 52672 },
+        { RGBColor{ 190, 66, 231 }, 53634 },
+        { RGBColor{ 19, 17, 253 }, 54603 },
+        { RGBColor{ 4, 34, 60 }, 55579 },
+        { RGBColor{ 101, 23, 88 }, 56562 },
+        { RGBColor{ 9, 191, 235 }, 57552 }
+    };
+    return Gradient{ colors };
 }
 
 
@@ -128,9 +265,28 @@ RGBColor Gradient::get(float x) const
 {
     if (colors.empty())
         return RGBColor();
-    const auto [left, right, lerp] = getNeighbors(x);
+    /*const auto [left, right, lerp] = getNeighbors(x);
     RGBColor lerped = lerpColors(left, right, lerp);
-    return lerped;
+    return lerped;*/
+
+    float pos = x * colors.size() / max;
+    if (pos < 0) {
+        pos = 0;
+    }
+    if (pos > colors.size() - 1) {
+        pos = colors.size() - 1;
+    }
+
+    int left = int(pos);
+    int right = int(pos + 1);
+    float lerp = pos - left;
+
+    if (lerp < 1e-5) {
+        return colors.at(left);
+    }
+    else {
+        return lerpColors(colors.at(left), colors.at(right), lerp);
+    }
 }
 
 
@@ -151,7 +307,7 @@ RGBColor Gradient::lerpColors(RGBColor a, RGBColor b, float val)
 }
 
 
-std::tuple<RGBColor, RGBColor, float> Gradient::getNeighbors(float x) const
+/*std::tuple<RGBColor, RGBColor, float> Gradient::getNeighbors(float x) const
 {
     for (auto it = colors.begin(); it != colors.end(); ++it) {
         if (it->second > x) {
@@ -165,4 +321,4 @@ std::tuple<RGBColor, RGBColor, float> Gradient::getNeighbors(float x) const
         }
     }
     return { (colors.end() - 1)->first, (colors.end() - 1)->first, 0 };
-}
+}*/

+ 11 - 4
Gradient.h

@@ -9,15 +9,22 @@
 
 class Gradient
 {
-    std::vector<std::pair<RGBColor, float>> colors;
+    /// the colors of this gradient stored in linear RGB format
+    /// so they can be easily interpolated
+    std::vector<RGBColor> colors;
     float max;
 public:
-    Gradient();
+    Gradient(void);
+    Gradient(const std::vector<std::pair<RGBColor, float>>&, int precalcSteps = 10000);
 
-    void addColor(RGBColor c, float value);
+    static Gradient defaultGradient(void);
 
+    /*!
+     * \brief get a color at a specific position in this gradient
+     * \param x the position
+     * \return the color in sRGB format
+     */
     RGBColor get(float x) const;
-
 private:
     static RGBColor lerpColors(RGBColor a, RGBColor b, float val);
     std::tuple<RGBColor, RGBColor, float> getNeighbors(float x) const;

+ 147 - 42
MandelWidget.cpp

@@ -78,6 +78,16 @@ void Texture::drawRect(float x, float y, float width, float height)
 }
 
 
+CellImage::~CellImage(void)
+{
+}
+
+
+
+TextureClip::~TextureClip(void)
+{
+}
+
 void TextureClip::drawRect(float x, float y, float width, float height)
 {
     glColor3ub(255, 255, 255);
@@ -108,6 +118,44 @@ TextureClip TextureClip::clip(float x, float y, float w, float h)
 }
 
 
+std::shared_ptr<CellImage> TextureClip::clip(short i, short j)
+{
+    return std::make_shared<TextureClip>(clip(i * 0.5, j * 0.5, 0.5, 0.5));
+}
+
+
+int TextureClip::getRecalcPriority() const
+{
+    return int(1.0 / tw);
+}
+
+
+QuadImage::~QuadImage(void)
+{
+}
+
+
+void QuadImage::drawRect(float x, float y, float width, float height)
+{
+    for (int i = 0; i < 2; i++) {
+        for (int j = 0; j < 2; j++) {
+            this->cells[i][j]->drawRect(x + i * 0.5 * width, y + j * 0.5 * height, width * 0.5, height * 0.5);
+        }
+    }
+}
+
+
+std::shared_ptr<CellImage> QuadImage::clip(short i, short j)
+{
+    return cells[i][j];
+}
+
+
+int QuadImage::getRecalcPriority() const
+{
+    return 1;
+}
+
 TexGrid::TexGrid(MandelV& owner, int level) :
     owner{ owner },
     level{ level },
@@ -213,7 +261,9 @@ void Calcer::setCurrentLevel(int level)
         }
         jobsMutex.unlock();
         for (auto* job : toCancel) {
-            threadPool->cancel(job);
+            if (threadPool->tryTake(job)) {
+                delete job;
+            }
         }
     }
 }
@@ -300,10 +350,11 @@ void MandelV::clear(void)
 }
 
 
-void MandelV::garbageCollect(int level)
+void MandelV::garbageCollect(int level, GridIndex i, GridIndex j)
 {
     for(auto& [l, grid] : levels) {
         int dist = ::abs(l - level);
+
         if (dist > 20) {
             grid.clearCells();
         }
@@ -345,7 +396,7 @@ GridElement* MandelV::searchAbove(int level, GridIndex i, GridIndex j, int recur
 
     if (above != nullptr) {
         auto newElement = std::make_unique<GridElement>(
-            false, above->img.clip((i & 1) * 0.5f, (j & 1) * 0.5f, 0.5f, 0.5f)
+            false, above->img->clip((i & 1), (j & 1))
         );
         GridElement* ret = newElement.get();
         grid.setCell(i, j, std::move(newElement));
@@ -357,35 +408,46 @@ GridElement* MandelV::searchAbove(int level, GridIndex i, GridIndex j, int recur
 }
 
 
-std::unique_ptr<GridElement> MandelV::searchUnder(int level, GridIndex i, GridIndex j, int recursionLevel)
+GridElement* MandelV::searchUnder(int level, GridIndex i, GridIndex j, int recursionLevel)
 {
-    /*if (recursionLevel == 0)
+    if (recursionLevel == 0)
         return nullptr;
 
+    auto& grid = getGrid(level);
     auto& gridUnder = getGrid(level - 1);
 
-    int ai = i * 2;
-    int aj = j * 2;
+    GridIndex ai = i * 2;
+    GridIndex aj = j * 2;
 
     GridElement* u00 = gridUnder.getCell(ai, aj);
     GridElement* u01 = gridUnder.getCell(ai, aj + 1);
     GridElement* u10 = gridUnder.getCell(ai + 1, aj);
     GridElement* u11 = gridUnder.getCell(ai + 1, aj + 1);
 
+    /*if (   u00 == nullptr
+        || u01 == nullptr
+        || u10 == nullptr
+        || u11 == nullptr) {
+        auto abFound = searchUnder(level + 1, ai, aj, recursionLevel - 1);
+        if (abFound)
+            above = abFound;
+    }*/
+
     if (   u00 != nullptr
         && u01 != nullptr
         && u10 != nullptr
         && u11 != nullptr) {
         GLuint FramebufferName = 0;
         auto newElement = std::make_unique<GridElement>(
-            false, above->img.clip((i & 1) * 0.5f, (j & 1) * 0.5f, 0.5f, 0.5f)
+            false, std::make_shared<QuadImage>(u00->img, u01->img, u10->img, u11->img)
         );
-        return newElement;
+        GridElement* ret = newElement.get();
+        grid.setCell(i, j, std::move(newElement));
+        return ret;
     }
     else {
-        return searchAbove(level - 1, ai, aj, recursionLevel - 1);
-    }*/
-    return nullptr;
+        return nullptr;
+    }
 }
 
 
@@ -393,18 +455,16 @@ void MandelV::paint(const mnd::MandelViewport& mvp)
 {
     double dpp = mvp.width / width;
     int level = getLevel(dpp) - 1;
-    garbageCollect(level);
-    emit calcer.setCurrentLevel(level);
-
     auto& grid = getGrid(level);
     double gw = getDpp(level) * chunkSize;
+    auto [left, top] = grid.getCellIndices(mvp.x, mvp.y);
+    auto [right, bottom] = grid.getCellIndices(mvp.right(), mvp.bottom());
+
+    garbageCollect(level, (left + right) / 2, (top + bottom) / 2);
+    emit calcer.setCurrentLevel(level);
 
     double w = width * gw / mvp.width;
-    //double h = height * gw / mvp.height;
-    //printf("level: %d, dpp: %f, width: %f\n", level, dpp, w);
 
-    auto [left, top] = grid.getCellIndices(mvp.x, mvp.y);
-    auto [right, bottom] = grid.getCellIndices(mvp.right(), mvp.bottom());
     auto [realXLeft, realYTop] = grid.getPositions(left, top);
     realXLeft = (realXLeft - mvp.x) * width / mvp.width;
     realYTop = (realYTop - mvp.y) * height / mvp.height;
@@ -416,14 +476,20 @@ void MandelV::paint(const mnd::MandelViewport& mvp)
             GridElement* t = grid.getCell(i, j);
 
             if (t == nullptr) {
-                auto above = searchAbove(level, i, j, 2);
-                if (above) {
-                    t = above;
+                auto under = searchUnder(level, i, j, 1);
+                if (under) {
+                    t = under;
+                }
+                else {
+                    auto above = searchAbove(level, i, j, 2);
+                    if (above) {
+                        t = above;
+                    }
                 }
             }
 
             if (t != nullptr) {
-                t->img.drawRect(x, y, w, w);
+                t->img->drawRect(x, y, w, w);
                 /*glBegin(GL_LINE_LOOP);
                 glVertex2f(x, y);
                 glVertex2f(x + w, y);
@@ -432,7 +498,7 @@ void MandelV::paint(const mnd::MandelViewport& mvp)
                 glEnd();*/
 
                 if (!t->enoughResolution) {
-                    calcer.calc(grid, level, i, j, 100);
+                    calcer.calc(grid, level, i, j, t->img->getRecalcPriority());
                 }
             }
             else {
@@ -447,7 +513,7 @@ void MandelV::cellReady(int level, GridIndex i, GridIndex j, Bitmap<RGBColor>* b
 {
 
     this->getGrid(level).setCell(i, j,
-        std::make_unique<GridElement>(true, TextureClip{ std::make_shared<Texture>(*bmp) }));
+        std::make_unique<GridElement>(true, std::make_shared<TextureClip>(std::make_shared<Texture>(*bmp))));
     delete bmp;
     emit redrawRequested();
 }
@@ -455,12 +521,24 @@ void MandelV::cellReady(int level, GridIndex i, GridIndex j, Bitmap<RGBColor>* b
 
 MandelWidget::MandelWidget(mnd::MandelContext& ctxt, QWidget* parent) :
     QOpenGLWidget{ parent },
-    mndContext{ ctxt }
+    mndContext{ ctxt },
+    gradient{ Gradient::defaultGradient() }
 {
     this->setContentsMargins(0, 0, 0, 0);
     this->setSizePolicy(QSizePolicy::Expanding,
         QSizePolicy::Expanding);
     qRegisterMetaType<GridIndex>("GridIndex");
+    this->format().setSwapInterval(1);
+
+    /*gradient = Gradient {
+        {
+            { RGBColor{ 0, 0, 0 }, 0 },
+            { RGBColor{ 180, 20, 10 }, 30 },
+            { RGBColor{ 210, 180, 15 }, 70 },
+            { RGBColor{ 160, 220, 45 }, 170 },
+            { RGBColor{ 50, 150, 170 }, 300 },
+        }
+    };*/
 }
 
 
@@ -512,13 +590,37 @@ void MandelWidget::paintGL(void)
     glClear(GL_COLOR_BUFFER_BIT);
     glLoadIdentity();
 
-    v->paint(this->viewport);
+    updateAnimations();
+    v->paint(this->currentViewport);
 
     if (rubberbanding)
         drawRubberband();
 }
 
 
+void MandelWidget::updateAnimations(void)
+{
+    auto now = std::chrono::high_resolution_clock::now();
+    auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(now - lastAnimUpdate).count();
+    double factor = ::pow(0.97, millis);
+
+    currentViewport.x = currentViewport.x * factor + targetViewport.x * (1.0 - factor);
+    currentViewport.y = currentViewport.y * factor + targetViewport.y * (1.0 - factor);
+    currentViewport.width = currentViewport.width * factor + targetViewport.width * (1.0 - factor);
+    currentViewport.height = currentViewport.height * factor + targetViewport.height * (1.0 - factor);
+
+    lastAnimUpdate = now;
+
+    if (::abs(currentViewport.width / targetViewport.width - 1.0) < 0.1e-5
+            && ::abs(currentViewport.height / targetViewport.height - 1.0) < 0.1e-5) {
+        // animation finished
+    }
+    else {
+        emit update();
+    }
+}
+
+
 void MandelWidget::drawRubberband(void)
 {
     glColor3ub(10, 200, 10);
@@ -544,8 +646,9 @@ void MandelWidget::drawRubberband(void)
 
 void MandelWidget::zoom(float scale, float x, float y)
 {
-    viewport.zoom(scale, x, y);
-    //viewport.zoomCenter(scale);
+    targetViewport.zoom(scale, x, y);
+    lastAnimUpdate = std::chrono::high_resolution_clock::now();
+    //currentViewport.zoom(scale, x, y);
     requestRecalc();
 }
 
@@ -594,7 +697,8 @@ void MandelWidget::resizeEvent(QResizeEvent* re)
     double aspect = double(geometry().width()) / geometry().height();
 
     //if (viewport.width > viewport.height * aspect)
-        viewport.height = (viewport.width / aspect);
+    currentViewport.height = (currentViewport.width / aspect);
+    targetViewport = currentViewport;
     //else
     //    viewport.width = (viewport.height * aspect);
 
@@ -613,8 +717,8 @@ void MandelWidget::mousePressEvent(QMouseEvent* me)
     QOpenGLWidget::mousePressEvent(me);
     if (me->button() == Qt::RightButton) {
         rubberbanding = true;
-        rubberband.setCoords(me->x(), me->y(), 0, 0);
-        //emit repaint();
+        rubberband.setCoords(me->x(), me->y(), me->x(), me->y());
+        emit repaint();
         me->accept();
     }
     else if (me->button() == Qt::LeftButton) {
@@ -644,8 +748,9 @@ void MandelWidget::mouseMoveEvent(QMouseEvent* me)
         double deltaX = me->x() - dragX;
         double deltaY = me->y() - dragY;
 
-        this->viewport.x -= deltaX * viewport.width / this->width();
-        this->viewport.y -= deltaY * viewport.height / this->height();
+        this->currentViewport.x -= deltaX * currentViewport.width / this->width();
+        this->currentViewport.y -= deltaY * currentViewport.height / this->height();
+        targetViewport = currentViewport;
         dragX = me->x(); dragY = me->y();
 
         emit repaint();
@@ -661,11 +766,12 @@ void MandelWidget::mouseReleaseEvent(QMouseEvent* me)
         QRect rect = rubberband.toRect();
         QRect full = this->geometry();
 
-        viewport.x += double(rect.left()) * viewport.width / full.width();
-        viewport.y += double(rect.top()) * viewport.height / full.height();
-        viewport.width *= double(rect.width()) / full.width();
-        viewport.height *= double(rect.height()) / full.height();
-        viewport.normalize();
+        targetViewport.x += double(rect.left()) * targetViewport.width / full.width();
+        targetViewport.y += double(rect.top()) * targetViewport.height / full.height();
+        targetViewport.width *= double(rect.width()) / full.width();
+        targetViewport.height *= double(rect.height()) / full.height();
+        targetViewport.normalize();
+        currentViewport = targetViewport;
         requestRecalc();
         rubberbanding = false;
     }
@@ -686,11 +792,10 @@ void MandelWidget::wheelEvent(QWheelEvent* we)
 }
 
 
-void MandelWidget::viewUpdated(Bitmap<RGBColor>* bitmap)
+/*void MandelWidget::viewUpdated(Bitmap<RGBColor>* bitmap)
 {
     if (bitmap != nullptr) {
-        tex = std::make_unique<Texture>(*bitmap);
         delete bitmap;
         emit repaint();
     }
-}
+}*/

+ 48 - 9
MandelWidget.h

@@ -18,6 +18,7 @@
 #include <atomic>
 #include <tuple>
 #include <deque>
+#include <chrono>
 #include <unordered_map>
 #include <unordered_set>
 
@@ -47,7 +48,18 @@ public:
 };
 
 
-class TextureClip
+class CellImage
+{
+public:
+    virtual ~CellImage(void);
+
+    virtual void drawRect(float x, float y, float width, float height) = 0;
+    virtual std::shared_ptr<CellImage> clip(short i, short j) = 0;
+    virtual int getRecalcPriority(void) const = 0;
+};
+
+
+class TextureClip : public CellImage
 {
     std::shared_ptr<Texture> texture;
     float tx, ty, tw, th;
@@ -56,10 +68,31 @@ public:
         texture{ std::move(tex) },
         tx{ 0 }, ty{ 0 }, tw{ 1 }, th{ 1 }
     {}
+    virtual ~TextureClip(void);
 
     void drawRect(float x, float y, float width, float height);
 
     TextureClip clip(float x, float y, float w, float h);
+    std::shared_ptr<CellImage> clip(short i, short j);
+    int getRecalcPriority(void) const;
+};
+
+
+class QuadImage : public CellImage
+{
+    std::shared_ptr<CellImage> cells[2][2];
+public:
+    inline QuadImage(std::shared_ptr<CellImage> i00,
+                     std::shared_ptr<CellImage> i01,
+                     std::shared_ptr<CellImage> i10,
+                     std::shared_ptr<CellImage> i11) :
+        cells{ { std::move(i00), std::move(i01) }, { std::move(i10), std::move(i11) } }
+    {}
+    virtual ~QuadImage(void);
+
+    void drawRect(float x, float y, float width, float height);
+    std::shared_ptr<CellImage> clip(short i, short j);
+    int getRecalcPriority(void) const;
 };
 
 
@@ -86,8 +119,8 @@ struct TripleHash {
 struct GridElement
 {
     bool enoughResolution;
-    TextureClip img;
-    inline GridElement(bool enoughResolution, TextureClip img) :
+    std::shared_ptr<CellImage> img;
+    inline GridElement(bool enoughResolution, std::shared_ptr<CellImage> img) :
         enoughResolution{ enoughResolution },
         img{ std::move(img) }
     {}
@@ -210,9 +243,9 @@ public:
 
     void clear(void);
 
-    void garbageCollect(int level);
+    void garbageCollect(int level, GridIndex i, GridIndex j);
     GridElement* searchAbove(int level, GridIndex i, GridIndex j, int recursionLevel);
-    std::unique_ptr<GridElement> searchUnder(int level, GridIndex i, GridIndex j, int recursionLevel);
+    GridElement* searchUnder(int level, GridIndex i, GridIndex j, int recursionLevel);
     void paint(const mnd::MandelViewport& mvp);
 public slots:
     void cellReady(int level, GridIndex i, GridIndex j, Bitmap<RGBColor>* bmp);
@@ -238,8 +271,10 @@ private:
     volatile bool dragging = false;
     int dragX, dragY;
 
-    std::unique_ptr<Texture> tex;
-    mnd::MandelViewport viewport;
+    mnd::MandelViewport currentViewport;
+    mnd::MandelViewport targetViewport;
+    std::chrono::time_point<std::chrono::high_resolution_clock> lastAnimUpdate;
+
     std::unique_ptr<MandelV> v;
 public:
     MandelWidget(mnd::MandelContext& ctxt, QWidget* parent = nullptr);
@@ -253,7 +288,11 @@ public:
 
     void paintGL() override;
 
+private:
+    void updateAnimations(void);
+
     void drawRubberband(void);
+public:
 
     void zoom(float scale, float x = 0.5f, float y = 0.5f);
     void setMaxIterations(int maxIter);
@@ -268,10 +307,10 @@ public:
     void mouseReleaseEvent(QMouseEvent* me) override;
     void wheelEvent(QWheelEvent * we) override;
 
-    inline const mnd::MandelViewport& getViewport(void) const { return viewport; }
+    inline const mnd::MandelViewport& getViewport(void) const { return targetViewport; }
 signals:
     void needsUpdate(const mnd::MandelInfo vp);
 public slots:
-    void viewUpdated(Bitmap<RGBColor>* bitmap);
+    //void viewUpdated(Bitmap<RGBColor>* bitmap);
 };
 

+ 54 - 14
VideoStream.cpp

@@ -27,13 +27,17 @@ VideoStream::VideoStream(int width, int height, const std::string& filename) :
         exit(1);
     }
 
+    AVOutputFormat* oformat = av_guess_format("mp4", NULL, NULL);
+    if (oformat == nullptr)
+        throw "invalid format";
+
     codecContext = avcodec_alloc_context3(codec);
 
     pkt = av_packet_alloc();
     if (!pkt)
         exit(1);
 
-    codecContext->bit_rate = 50 * 1000 * 1000;
+    codecContext->bit_rate = 4 * 1000 * 1000;
     codecContext->width = width;
     codecContext->height = height;
     codecContext->time_base = AVRational{ 1, 60 };
@@ -43,21 +47,47 @@ VideoStream::VideoStream(int width, int height, const std::string& filename) :
     codecContext->max_b_frames = 1;
     codecContext->pix_fmt = AV_PIX_FMT_YUV420P;
 
+    formatContext = avformat_alloc_context();
+    formatContext->oformat = oformat;
+    formatContext->video_codec_id = oformat->video_codec;
+
+    stream = avformat_new_stream(formatContext, codec);
+    if (!stream)
+        throw "error";
+
+    params = avcodec_parameters_alloc();
+    avcodec_parameters_from_context(params, codecContext);
+    stream->codecpar = params;
+
+    /*AVCPBProperties *props;
+    props = (AVCPBProperties*) av_stream_new_side_data(
+        stream, AV_PKT_DATA_CPB_PROPERTIES, sizeof(*props));
+    props->buffer_size = 1024 * 1024;
+    props->max_bitrate = 0;
+    props->min_bitrate = 0;
+    props->avg_bitrate = 0;
+    props->vbv_delay = UINT64_MAX;*/
+
     if (codec->id == AV_CODEC_ID_H264)
-        av_opt_set(codecContext->priv_data, "preset", "slow", 0);
+        av_opt_set(codecContext->priv_data, "preset", "veryfast", 0);
 
     if (avcodec_open2(codecContext, codec, nullptr) < 0) {
         fprintf(stderr, "could not open codec\n");
         exit(1);
     }
+    avio_open(&formatContext->pb, filename.c_str(), AVIO_FLAG_WRITE);
 
-    file = fopen(filename.c_str(), "wb");
+    if (avformat_write_header(formatContext, NULL) < 0) {
+        throw "error";
+    }
+    /*file = fopen(filename.c_str(), "wb");
     if (!file) {
         fprintf(stderr, "could not open %s\n", filename.c_str());
         exit(1);
-    }
+    }*/
 
     picture = av_frame_alloc();
+    av_frame_make_writable(picture);
     picture->format = codecContext->pix_fmt;
     picture->width  = codecContext->width;
     picture->height = codecContext->height;
@@ -75,20 +105,20 @@ VideoStream::VideoStream(int width, int height, const std::string& filename) :
 }
 
 
-static void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,
-    FILE *outfile)
+void VideoStream::encode(AVFrame* frame)
 {
     int ret;
 
     /* send the frame to the encoder */
-    ret = avcodec_send_frame(enc_ctx, frame);
+    ret = avcodec_send_frame(codecContext, frame);
     if (ret < 0) {
         fprintf(stderr, "error sending a frame for encoding\n");
         exit(1);
     }
 
     while (ret >= 0) {
-        ret = avcodec_receive_packet(enc_ctx, pkt);
+        ret = avcodec_receive_packet(codecContext, pkt);
+        //ret = avcodec_encode_video2(codecContext, pkt, picture, &gotPacket);
         if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
             return;
         else if (ret < 0) {
@@ -97,7 +127,9 @@ static void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,
         }
 
         printf("encoded frame %3d\"PRId64\" (size=%5d)\n", pkt->pts, pkt->size);
-        fwrite(pkt->data, 1, pkt->size, outfile);
+        //fwrite(pkt->data, 1, pkt->size, outfile);
+        //av_interleaved_write_frame(formatContext, pkt);
+        av_write_frame(formatContext, pkt);
         av_packet_unref(pkt);
     }
 }
@@ -106,12 +138,18 @@ static void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,
 VideoStream::~VideoStream()
 {
     /* flush the encoder */
-    encode(codecContext, nullptr, pkt, file);
+    encode(nullptr);
+    av_write_trailer(this->formatContext);
 
     /* add sequence end code to have a real MPEG file */
-    fwrite(endcode, 1, sizeof(endcode), file);
-    fclose(file);
-
+    //fwrite(endcode, 1, sizeof(endcode), file);
+    //fclose(file);
+
+    avcodec_close(codecContext);
+    avio_close(formatContext->pb);
+    av_frame_unref(picture);
+    //av_free(codecContext);
+    avcodec_parameters_free(&params);
     avcodec_free_context(&codecContext);
     av_frame_free(&picture);
     av_packet_free(&pkt);
@@ -146,6 +184,8 @@ VideoStream::~VideoStream()
 
 void VideoStream::addFrame(const Bitmap<RGBColor>& frame)
 {
+    //av_frame_free(&picture);
+    //picture = av_frame_alloc();
     int retval = av_frame_make_writable(picture);
     if (retval < 0)
         exit(1);
@@ -186,7 +226,7 @@ void VideoStream::addFrame(const Bitmap<RGBColor>& frame)
     picture->pts = frameIndex++;
 
     /* encode the image */
-    encode(codecContext, picture, pkt, file);
+    encode(picture);
 }
 
 #endif // FFMPEG_ENABLED

+ 6 - 1
VideoStream.h

@@ -23,9 +23,12 @@ class VideoStream
 {
     const AVCodec* codec;
     AVCodecContext* codecContext;
-    FILE* file;
+    AVFormatContext* formatContext;
+    AVCodecParameters* params;
+    //FILE* file;
     AVFrame* picture;
     AVPacket* pkt;
+    AVStream* stream;
     SwsContext* swsContext;
     static const uint8_t endcode[];
 
@@ -37,6 +40,8 @@ public:
     VideoStream(int width, int height, const std::string& filename);
     ~VideoStream(void);
 
+    void encode(AVFrame* frame);
+
     void addFrame(const Bitmap<RGBColor>& frame);
 };