mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-15 01:54:14 +02:00
Merge pull request #1477 from ispyisail/perf/parallel-picture-decode
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 2m6s
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 2m6s
Speed up opening folios with photos: decode pictures in parallel
This commit is contained in:
+32
-9
@@ -53,6 +53,8 @@
|
||||
#include "foliogrid.h"
|
||||
#include <QGraphicsView>
|
||||
#include <QTextStream>
|
||||
#include <QThreadPool>
|
||||
#include <QtConcurrentMap>
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <cassert>
|
||||
@@ -2006,16 +2008,37 @@ bool Diagram::fromXml(QDomElement &document,
|
||||
}
|
||||
|
||||
// Load image
|
||||
QList<DiagramImageItem *> added_images;
|
||||
for (auto image_xml : QET::findInDomElement(root,
|
||||
QStringLiteral("images"),
|
||||
QStringLiteral("image"))) {
|
||||
DiagramImageItem *dii = new DiagramImageItem ();
|
||||
// Decoding is most of the time of a folio with photos (a quarter of
|
||||
// a second each), so the pictures are decoded first, in parallel,
|
||||
// and the items built afterwards in this thread.
|
||||
const QList<QDomElement> images_xml = QET::findInDomElement(
|
||||
root, QStringLiteral("images"), QStringLiteral("image"));
|
||||
QList<DiagramImageItem::Picture> pictures;
|
||||
pictures.reserve(images_xml.size());
|
||||
for (const QDomElement &image_xml : images_xml) {
|
||||
const QString picture_file = QetContainer::pictureFile(image_xml);
|
||||
if (!picture_file.isEmpty() && project())
|
||||
dii -> fromXml(image_xml, project() -> containerPicture(picture_file));
|
||||
else
|
||||
dii -> fromXml(image_xml);
|
||||
pictures << DiagramImageItem::pictureOf(
|
||||
image_xml,
|
||||
!picture_file.isEmpty() && project()
|
||||
? project() -> containerPicture(picture_file)
|
||||
: QByteArray());
|
||||
}
|
||||
if (pictures.size() > 1) {
|
||||
QThreadPool pool;
|
||||
pool.setMaxThreadCount(std::min<int>(QThread::idealThreadCount(),
|
||||
pictures.size()));
|
||||
QtConcurrent::blockingMap(&pool, pictures,
|
||||
&DiagramImageItem::decodePicture);
|
||||
} else {
|
||||
for (DiagramImageItem::Picture &picture : pictures)
|
||||
DiagramImageItem::decodePicture(picture);
|
||||
}
|
||||
|
||||
QList<DiagramImageItem *> added_images;
|
||||
for (qsizetype i = 0 ; i < images_xml.size() ; ++i) {
|
||||
const QDomElement &image_xml = images_xml.at(i);
|
||||
DiagramImageItem *dii = new DiagramImageItem ();
|
||||
dii -> fromXml(image_xml, std::move(pictures[i]));
|
||||
settle_uuid(dii, image_xml, QStringLiteral("image"), added_images.size());
|
||||
ItemGroups::setGroup(dii, ItemGroups::read(image_xml));
|
||||
addItem(dii);
|
||||
|
||||
@@ -1819,6 +1819,60 @@ bool DiagramImageItem::fromXml(const QDomElement &e)
|
||||
pictures as files (QetContainer::join()).
|
||||
*/
|
||||
bool DiagramImageItem::fromXml(const QDomElement &e, const QByteArray &picture)
|
||||
{
|
||||
Picture p = pictureOf(e, picture);
|
||||
decodePicture(p);
|
||||
return fromXml(e, std::move(p));
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DiagramImageItem::pictureOf
|
||||
@return the picture of @p e, @p picture unless it is empty, else the
|
||||
base64 text of @p e, still encoded : decodePicture() does the slow part.
|
||||
Reads the DOM, so GUI thread only.
|
||||
*/
|
||||
DiagramImageItem::Picture DiagramImageItem::pictureOf(const QDomElement &e,
|
||||
const QByteArray &picture)
|
||||
{
|
||||
Picture p;
|
||||
p.bytes = picture;
|
||||
if (p.bytes.isEmpty())
|
||||
{
|
||||
// From the first text node only, not e.text(): that also collects
|
||||
// the text of every child element, so on a cropped or colour-keyed
|
||||
// picture it carries <image_base>'s bytes too.
|
||||
const QDomNode image_node = e.firstChild();
|
||||
if (image_node.isText())
|
||||
p.base64 = image_node.toText().data();
|
||||
else
|
||||
p.found = false;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DiagramImageItem::decodePicture
|
||||
Decodes the base64 text and the picture file of @p picture. Touches
|
||||
nothing else, so it can run in any thread : a large photo takes about
|
||||
a quarter of a second, and Diagram::fromXml() decodes a folio's
|
||||
pictures in parallel.
|
||||
*/
|
||||
void DiagramImageItem::decodePicture(Picture &picture)
|
||||
{
|
||||
if (picture.bytes.isEmpty() && !picture.base64.isEmpty())
|
||||
{
|
||||
picture.bytes = QByteArray::fromBase64(picture.base64.toLatin1());
|
||||
picture.base64.clear();
|
||||
}
|
||||
picture.image = QImage::fromData(picture.bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DiagramImageItem::fromXml
|
||||
Load this image from xml element e, its picture already decoded by
|
||||
decodePicture().
|
||||
*/
|
||||
bool DiagramImageItem::fromXml(const QDomElement &e, Picture picture)
|
||||
{
|
||||
if (e.tagName() != "image") {
|
||||
return (false);
|
||||
@@ -1828,23 +1882,13 @@ bool DiagramImageItem::fromXml(const QDomElement &e, const QByteArray &picture)
|
||||
const QUuid uuid(e.attribute(QStringLiteral("uuid")));
|
||||
if (!uuid.isNull() && uuid != m_uuid) setUuid(uuid);
|
||||
|
||||
// From the first text node only, not e.text(): that also collects the
|
||||
// text of every child element, so on a cropped or colour-keyed picture
|
||||
// it carries <image_base>'s bytes too. Decoded once: on a project with
|
||||
// large pictures each decoding costs seconds.
|
||||
QByteArray array = picture;
|
||||
if (array.isEmpty())
|
||||
{
|
||||
QDomNode image_node = e.firstChild();
|
||||
if (!image_node.isText()) {
|
||||
return (false);
|
||||
}
|
||||
array = QByteArray::fromBase64(image_node.toText().data().toLatin1());
|
||||
if (!picture.found) {
|
||||
return (false);
|
||||
}
|
||||
const QByteArray array = picture.bytes;
|
||||
|
||||
//Set QPixmap from the array
|
||||
QPixmap pixmap;
|
||||
pixmap.loadFromData(array);
|
||||
//Set QPixmap from the decoded picture
|
||||
QPixmap pixmap = QPixmap::fromImage(std::move(picture.image));
|
||||
setPixmap(pixmap);
|
||||
if (isPng(array) && !pixmap_.isNull())
|
||||
{
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <QColor>
|
||||
#include <QFont>
|
||||
#include <QImage>
|
||||
#include <QList>
|
||||
#include <QUuid>
|
||||
#include <QStyleOptionGraphicsItem>
|
||||
@@ -119,8 +120,20 @@ class DiagramImageItem : public QetGraphicsItem {
|
||||
*/
|
||||
int type() const override { return Type; }
|
||||
|
||||
/// A picture as read from its <image> element, before and after
|
||||
/// decodePicture(), which may run outside the GUI thread.
|
||||
struct Picture {
|
||||
QString base64; ///< text of <image>, decoded when bytes is empty
|
||||
QByteArray bytes; ///< the picture file (PNG, JPEG...)
|
||||
QImage image; ///< bytes, decoded
|
||||
bool found = true; ///< false : neither bytes nor text
|
||||
};
|
||||
static Picture pictureOf(const QDomElement &, const QByteArray &picture);
|
||||
static void decodePicture(Picture &picture);
|
||||
|
||||
virtual bool fromXml(const QDomElement &);
|
||||
bool fromXml(const QDomElement &, const QByteArray &picture);
|
||||
bool fromXml(const QDomElement &, Picture picture);
|
||||
virtual QDomElement toXml(QDomDocument &) const;
|
||||
QUuid uuid() const {return m_uuid;}
|
||||
void setUuid(const QUuid &uuid) {m_uuid = uuid; emit uuidChanged();}
|
||||
|
||||
Reference in New Issue
Block a user