Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
VLC
Manage
Activity
Members
Labels
Plan
Issues
4k
Issue boards
Milestones
Code
Merge requests
447
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Model registry
Analyze
Contributor analytics
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
VideoLAN
VLC
Commits
4082222d
Commit
4082222d
authored
2 years ago
by
Prince Gupta
Committed by
Jean-Baptiste Kempf
2 years ago
Browse files
Options
Downloads
Patches
Plain Diff
qt: support image provider in RoundImage
parent
19cc1b35
No related branches found
Branches containing commit
No related tags found
Tags containing commit
1 merge request
!1983
qt: use in-memory caching for custom ml covers
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
modules/gui/qt/widgets/native/roundimage.cpp
+222
-38
222 additions, 38 deletions
modules/gui/qt/widgets/native/roundimage.cpp
modules/gui/qt/widgets/native/roundimage.hpp
+8
-2
8 additions, 2 deletions
modules/gui/qt/widgets/native/roundimage.hpp
with
230 additions
and
40 deletions
modules/gui/qt/widgets/native/roundimage.cpp
+
222
−
38
View file @
4082222d
...
...
@@ -31,10 +31,12 @@
#include
<QBuffer>
#include
<QCache>
#include
<QFile>
#include
<QImage>
#include
<QImageReader>
#include
<QPainter>
#include
<QPainterPath>
#include
<QQuickImageProvider>
#include
<QQuickWindow>
#include
<QGuiApplication>
#include
<QSGImageNode>
...
...
@@ -116,6 +118,161 @@ namespace
qWarning
()
<<
"Could not load source image:"
<<
url
<<
error
.
what
();
return
{};
}
QRectF
doPreserveAspectCrop
(
const
QSizeF
&
sourceSize
,
const
QSizeF
&
size
)
{
const
qreal
ratio
=
std
::
max
(
size
.
width
()
/
sourceSize
.
width
(),
size
.
height
()
/
sourceSize
.
height
());
const
QSizeF
imageSize
=
sourceSize
*
ratio
;
const
QPointF
alignedCenteredTopLeft
{(
size
.
width
()
-
imageSize
.
width
())
/
2.
,
(
size
.
height
()
-
imageSize
.
height
())
/
2.
};
return
{
alignedCenteredTopLeft
,
imageSize
};
}
class
ImageReader
:
public
AsyncTask
<
QImage
>
{
public:
// requestedSize is only taken as hint, the Image is resized with PreserveAspectCrop
ImageReader
(
const
QUrl
&
url
,
QSize
requestedSize
)
:
url
{
url
}
,
requestedSize
{
requestedSize
}
{
}
QString
errorString
()
const
{
return
errorStr
;
}
QImage
execute
()
{
auto
file
=
getReadable
(
url
);
if
(
!
file
||
!
file
->
isOpen
())
return
{};
QImageReader
reader
;
reader
.
setDevice
(
file
.
get
());
const
QSize
sourceSize
=
reader
.
size
();
if
(
requestedSize
.
isValid
())
reader
.
setScaledSize
(
doPreserveAspectCrop
(
sourceSize
,
requestedSize
).
size
().
toSize
());
auto
img
=
reader
.
read
();
errorStr
=
reader
.
errorString
();
return
img
;
}
private
:
QUrl
url
;
QSize
requestedSize
;
QString
errorStr
;
};
class
LocalImageResponse
:
public
QQuickImageResponse
{
public:
LocalImageResponse
(
const
QUrl
&
url
,
const
QSize
&
requestedSize
)
{
reader
.
reset
(
new
ImageReader
(
url
,
requestedSize
));
connect
(
reader
.
get
(),
&
ImageReader
::
result
,
this
,
&
LocalImageResponse
::
handleImageRead
);
reader
->
start
(
*
QThreadPool
::
globalInstance
());
}
QQuickTextureFactory
*
textureFactory
()
const
override
{
return
result
.
isNull
()
?
nullptr
:
QQuickTextureFactory
::
textureFactoryForImage
(
result
);
}
QString
errorString
()
const
override
{
return
errorStr
;
}
private
:
void
handleImageRead
()
{
result
=
reader
->
takeResult
();
errorStr
=
reader
->
errorString
();
reader
.
reset
();
emit
finished
();
}
QImage
result
;
TaskHandle
<
ImageReader
>
reader
;
QString
errorStr
;
};
class
ImageProviderAsyncAdaptor
:
public
QQuickImageResponse
{
public:
ImageProviderAsyncAdaptor
(
QQuickImageProvider
*
provider
,
const
QString
&
id
,
const
QSize
&
requestedSize
)
{
task
.
reset
(
new
ProviderImageGetter
(
provider
,
id
,
requestedSize
));
connect
(
task
.
get
(),
&
ProviderImageGetter
::
result
,
this
,
[
this
]()
{
result
=
task
->
takeResult
();
task
.
reset
();
emit
finished
();
});
task
->
start
(
*
QThreadPool
::
globalInstance
());
}
QQuickTextureFactory
*
textureFactory
()
const
override
{
return
result
.
isNull
()
?
nullptr
:
QQuickTextureFactory
::
textureFactoryForImage
(
result
);
}
private
:
class
ProviderImageGetter
:
public
AsyncTask
<
QImage
>
{
public:
ProviderImageGetter
(
QQuickImageProvider
*
provider
,
const
QString
&
id
,
const
QSize
&
requestedSize
)
:
provider
{
provider
}
,
id
{
id
}
,
requestedSize
{
requestedSize
}
{
}
QImage
execute
()
override
{
return
provider
->
requestImage
(
id
,
&
sourceSize
,
requestedSize
);
}
private
:
QQuickImageProvider
*
provider
;
QString
id
;
QSize
requestedSize
;
QSize
sourceSize
;
};
TaskHandle
<
ProviderImageGetter
>
task
;
QImage
result
;
};
QQuickImageResponse
*
getAsyncImageResponse
(
const
QUrl
&
url
,
const
QSize
&
requestedSize
,
QQmlEngine
*
engine
)
{
if
(
url
.
scheme
()
==
QStringLiteral
(
"image"
))
{
auto
provider
=
engine
->
imageProvider
(
url
.
host
());
if
(
!
provider
)
return
nullptr
;
assert
(
provider
->
imageType
()
==
QQmlImageProviderBase
::
Image
||
provider
->
imageType
()
==
QQmlImageProviderBase
::
ImageResponse
);
const
auto
imageId
=
url
.
toString
(
QUrl
::
RemoveScheme
|
QUrl
::
RemoveAuthority
).
mid
(
1
);;
if
(
provider
->
imageType
()
==
QQmlImageProviderBase
::
Image
)
return
new
ImageProviderAsyncAdaptor
(
static_cast
<
QQuickImageProvider
*>
(
provider
),
imageId
,
requestedSize
);
if
(
provider
->
imageType
()
==
QQmlImageProviderBase
::
ImageResponse
)
return
static_cast
<
QQuickAsyncImageProvider
*>
(
provider
)
->
requestImageResponse
(
imageId
,
requestedSize
);
return
nullptr
;
}
else
{
return
new
LocalImageResponse
(
url
,
requestedSize
);
}
}
}
RoundImage
::
RoundImage
(
QQuickItem
*
parent
)
:
QQuickItem
{
parent
}
...
...
@@ -127,6 +284,11 @@ RoundImage::RoundImage(QQuickItem *parent) : QQuickItem {parent}
connect
(
this
,
&
QQuickItem
::
widthChanged
,
this
,
&
RoundImage
::
regenerateRoundImage
);
}
RoundImage
::~
RoundImage
()
{
resetImageRequest
();
}
QSGNode
*
RoundImage
::
updatePaintNode
(
QSGNode
*
oldNode
,
UpdatePaintNodeData
*
)
{
auto
node
=
static_cast
<
QSGImageNode
*>
(
oldNode
);
...
...
@@ -229,25 +391,33 @@ void RoundImage::setDPR(const qreal value)
regenerateRoundImage
();
}
void
RoundImage
::
loa
d
()
void
RoundImage
::
handleImageRequestFinishe
d
()
{
m_enqueuedGeneration
=
false
;
assert
(
!
m_roundImageGenerator
);
const
QString
error
=
m_activeImageRequest
->
errorString
();
QImage
image
;
if
(
auto
textureFactory
=
m_activeImageRequest
->
textureFactory
())
{
image
=
textureFactory
->
image
();
delete
textureFactory
;
}
resetImageRequest
();
if
(
image
.
isNull
())
{
qDebug
()
<<
"failed to get image, error"
<<
error
<<
source
();
return
;
}
const
qreal
scaledWidth
=
this
->
width
()
*
m_dpr
;
const
qreal
scaledHeight
=
this
->
height
()
*
m_dpr
;
const
qreal
scaledRadius
=
this
->
radius
()
*
m_dpr
;
const
ImageCacheKey
key
{
source
(),
QSizeF
{
scaledWidth
,
scaledHeight
}.
toSize
(),
scaledRadius
};
if
(
auto
image
=
imageCache
.
object
(
key
))
// should only by called in mainthread
{
setRoundImage
(
*
image
);
return
;
}
// Image is generated in size factor of `m_dpr` to avoid scaling artefacts when
// generated image is set with device pixel ratio
m_roundImageGenerator
.
reset
(
new
RoundImageGenerator
(
m_sourc
e
,
scaledWidth
,
scaledHeight
,
scaledRadius
));
m_roundImageGenerator
.
reset
(
new
RoundImageGenerator
(
imag
e
,
scaledWidth
,
scaledHeight
,
scaledRadius
));
connect
(
m_roundImageGenerator
.
get
(),
&
BaseAsyncTask
::
result
,
this
,
[
this
,
key
]()
{
const
auto
image
=
new
QImage
(
m_roundImageGenerator
->
takeResult
());
...
...
@@ -270,6 +440,40 @@ void RoundImage::load()
m_roundImageGenerator
->
start
(
*
QThreadPool
::
globalInstance
());
}
void
RoundImage
::
resetImageRequest
()
{
if
(
!
m_activeImageRequest
)
return
;
m_activeImageRequest
->
disconnect
(
this
);
m_activeImageRequest
->
deleteLater
();
m_activeImageRequest
=
nullptr
;
}
void
RoundImage
::
load
()
{
m_enqueuedGeneration
=
false
;
assert
(
!
m_roundImageGenerator
);
auto
engine
=
qmlEngine
(
this
);
if
(
!
engine
||
m_source
.
isEmpty
()
||
!
size
().
isValid
()
||
size
().
isEmpty
())
return
;
const
qreal
scaledWidth
=
this
->
width
()
*
m_dpr
;
const
qreal
scaledHeight
=
this
->
height
()
*
m_dpr
;
const
qreal
scaledRadius
=
this
->
radius
()
*
m_dpr
;
const
ImageCacheKey
key
{
source
(),
QSizeF
{
scaledWidth
,
scaledHeight
}.
toSize
(),
scaledRadius
};
if
(
auto
image
=
imageCache
.
object
(
key
))
// should only by called in mainthread
{
setRoundImage
(
*
image
);
return
;
}
m_activeImageRequest
=
getAsyncImageResponse
(
source
(),
QSizeF
{
scaledWidth
,
scaledHeight
}.
toSize
(),
engine
);
connect
(
m_activeImageRequest
,
&
QQuickImageResponse
::
finished
,
this
,
&
RoundImage
::
handleImageRequestFinished
);
}
void
RoundImage
::
setRoundImage
(
QImage
image
)
{
m_dirty
=
true
;
...
...
@@ -292,6 +496,8 @@ void RoundImage::regenerateRoundImage()
// remove old contents
setRoundImage
({});
resetImageRequest
();
m_roundImageGenerator
.
reset
();
// use Qt::QueuedConnection to delay generation, so that dependent properties
...
...
@@ -301,8 +507,8 @@ void RoundImage::regenerateRoundImage()
QMetaObject
::
invokeMethod
(
this
,
&
RoundImage
::
load
,
Qt
::
QueuedConnection
);
}
RoundImage
::
RoundImageGenerator
::
RoundImageGenerator
(
const
Q
Url
&
source
,
qreal
width
,
qreal
height
,
qreal
radius
)
:
source
(
source
)
RoundImage
::
RoundImageGenerator
::
RoundImageGenerator
(
const
Q
Image
&
source
Image
,
qreal
width
,
qreal
height
,
qreal
radius
)
:
source
Image
(
source
Image
)
,
width
(
width
)
,
height
(
height
)
,
radius
(
radius
)
...
...
@@ -311,34 +517,9 @@ RoundImage::RoundImageGenerator::RoundImageGenerator(const QUrl &source, qreal w
QImage
RoundImage
::
RoundImageGenerator
::
execute
()
{
if
(
width
<=
0
||
height
<=
0
)
return
{};
if
(
source
.
isEmpty
())
return
{};
auto
file
=
getReadable
(
source
);
if
(
!
file
||
!
file
->
isOpen
())
if
(
width
<=
0
||
height
<=
0
||
sourceImage
.
isNull
())
return
{};
QImageReader
sourceReader
(
file
.
get
());
// do PreserveAspectCrop
const
QSizeF
size
{
width
,
height
};
QSizeF
defaultSize
=
sourceReader
.
size
();
if
(
!
defaultSize
.
isValid
())
defaultSize
=
size
;
const
qreal
ratio
=
std
::
max
(
size
.
width
()
/
defaultSize
.
width
(),
size
.
height
()
/
defaultSize
.
height
());
const
QSizeF
targetSize
=
defaultSize
*
ratio
;
const
QPointF
alignedCenteredTopLeft
{(
size
.
width
()
-
targetSize
.
width
())
/
2.
,
(
size
.
height
()
-
targetSize
.
height
())
/
2.
};
sourceReader
.
setScaledSize
(
targetSize
.
toSize
());
if
(
Q_UNLIKELY
(
radius
<=
0
))
{
return
sourceReader
.
read
();
}
QImage
target
(
width
,
height
,
QImage
::
Format_ARGB32_Premultiplied
);
if
(
target
.
isNull
())
return
target
;
...
...
@@ -354,7 +535,10 @@ QImage RoundImage::RoundImageGenerator::execute()
path
.
addRoundedRect
(
0
,
0
,
width
,
height
,
radius
,
radius
);
painter
.
setClipPath
(
path
);
painter
.
drawImage
({
alignedCenteredTopLeft
,
targetSize
},
sourceReader
.
read
());
// do PreserveAspectCrop
const
auto
imageSize
=
sourceImage
.
size
();
const
QPointF
alignedCenteredTopLeft
{(
width
-
imageSize
.
width
())
/
2.
,
(
height
-
imageSize
.
height
())
/
2.
};
painter
.
drawImage
(
QRectF
{
alignedCenteredTopLeft
,
imageSize
},
sourceImage
);
}
return
target
;
...
...
This diff is collapsed.
Click to expand it.
modules/gui/qt/widgets/native/roundimage.hpp
+
8
−
2
View file @
4082222d
...
...
@@ -31,6 +31,8 @@
#include
<QQuickItem>
#include
<QUrl>
class
QQuickImageResponse
;
class
RoundImage
:
public
QQuickItem
{
Q_OBJECT
...
...
@@ -42,6 +44,7 @@ class RoundImage : public QQuickItem
public:
RoundImage
(
QQuickItem
*
parent
=
nullptr
);
~
RoundImage
();
void
componentComplete
()
override
;
...
...
@@ -64,18 +67,20 @@ private:
class
RoundImageGenerator
:
public
AsyncTask
<
QImage
>
{
public:
RoundImageGenerator
(
const
Q
Url
&
source
,
qreal
width
,
qreal
height
,
qreal
radius
);
RoundImageGenerator
(
const
Q
Image
&
source
Image
,
qreal
width
,
qreal
height
,
qreal
radius
);
QImage
execute
();
private:
Q
Url
source
;
Q
Image
source
Image
;
qreal
width
;
qreal
height
;
qreal
radius
;
};
void
setDPR
(
qreal
value
);
void
handleImageRequestFinished
();
void
resetImageRequest
();
void
load
();
void
setRoundImage
(
QImage
image
);
void
regenerateRoundImage
();
...
...
@@ -88,6 +93,7 @@ private:
bool
m_dirty
=
false
;
TaskHandle
<
RoundImageGenerator
>
m_roundImageGenerator
{};
QQuickImageResponse
*
m_activeImageRequest
{};
bool
m_enqueuedGeneration
=
false
;
};
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment