papyros / qml-material

:book: Material Design implemented in QtQuick
GNU Lesser General Public License v2.1
2.57k stars 476 forks source link

Extras.Image blocks UI when calculating averageColor #337

Open nathanielhourt opened 8 years ago

nathanielhourt commented 8 years ago

If I load a large image into Extras.Image, the UI blocks (more than a second, in my case) while averageColor is being calculated. It would be good if this were made asynchronous somehow.

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

nathanielhourt commented 8 years ago

Might be useful to use http://doc.qt.io/qt-5/qml-workerscript.html -- the docs are a bit lacking, but it might be suitable for the task.

kingwill101 commented 8 years ago

Could u provide code that reproduces the issue On Nov 23, 2015 10:06 AM, "Nathan Hourt" notifications@github.com wrote:

Might be useful to use http://doc.qt.io/qt-5/qml-workerscript.html -- the docs are a bit lacking, but it might be suitable for the task.

— Reply to this email directly or view it on GitHub https://github.com/papyros/qml-material/issues/337#issuecomment-158962396 .

nathanielhourt commented 8 years ago

@glenfordwilliams Curiously enough, it seems to be specifically an issue when the image belongs to a ListView delegate that is being instantiated before it scrolls onto the screen. There may be other cases where it occurs (like a Loader, maybe?) but I haven't tested that.

Here's a trivial example:

import QtQuick 2.5
import Material.Extras 0.1 as Extras

Rectangle {
  width: 800
  height: 800

  Rectangle {
    anchors.centerIn: parent
    width: 200
    height: 200
    color: "green"
    RotationAnimation on rotation {
      loops: Animation.Infinite
      from: 0; to: 360
      duration: 1000
    }
  }
  ListView {
    width: 100
    height: parent.height
    model: 50
    delegate: Extras.Image {
      id: img
      width: 100
      height: 100
      fillMode: Image.PreserveAspectCrop
      source: "https://c278592.ssl.cf0.rackcdn.com/original/815426.jpg"
    }
  }
}

To see the blocking, scroll down on the ListView

nathanielhourt commented 8 years ago

The slow part is the script in Image.qml, specifically the image processing loop. If this section is placed in a WorkerScript, we might see better results:

      while ((i += pixelInterval * 4) < length) {
        count++;
        rgba.r += data[i];
        rgba.g += data[i+1];
        rgba.b += data[i+2];
        rgba.a += data[i+3];
      };
      rgba.r = Math.floor(rgba.r/count);
      rgba.g = Math.floor(rgba.g/count);
      rgba.b = Math.floor(rgba.b/count);
      rgba.a = Math.floor(rgba.a/count);

Unfortunately, my initial experimentation shows that WorkerScript has some initialization that isn't done by the time I tried to load the image (again, only occurs in a ListView delegate being rendered during scrolling). Since WorkerScript provides no apparent way of knowing when this initialization is complete, I can only assume this is a race condition in Qt, and thus makes WorkerScript less attractive an option. Perhaps if the WorkerScript was instantiated ahead of time in a singleton somewhere it would work, but that gets real ugly real fast.