Source code

Revision control

Copy as Markdown

// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include "qcanvasimage.h"
#include "qcanvasimage_p.h"
#include "qvariant.h"
QT_BEGIN_NAMESPACE
/*!
\class QCanvasImage
\since 6.11
\brief QCanvasImage is the image class for QCanvasPainter.
\inmodule QtCanvasPainter
QCanvasImage is the image class used by QCanvasPainter.
To be able to paint images, they fist need to be made available
with e.g. \l QCanvasPainter::addImage(). Then images can be painted
as-is using \l QCanvasPainter::drawImage() or used with
\l QCanvasImagePattern brush to fill / stroke.
Here is a simple example:
\code
static QImage logoImage(":/qtlogo.png");
// Paint an image pattern.
QCanvasImage bg = painter.addImage(logoImage,
QCanvasPainter::ImageFlag::Repeat |
QCanvasPainter::ImageFlag::GenerateMipmaps);
QCanvasImagePattern ip(bg, 0, 0, 44, 32);
painter.setFillStyle(ip);
painter.fillRect(50, 50, 320, 230);
// Paint a single image, with tint color.
QCanvasImage logo = painter.addImage(logoImage);
logo.setTintColor("#2cde85");
painter.drawImage(logo, 100, 80);
\endcode
\image image_example_1.png
{Green-tinted Qt logo drawn over a tiled background of small Qt
logos}
In the above example the QImage is static and addImage() is called
on every repaint. This is not a problem as when the image and flags
remain the same, addImage() fetches the image from cache instead of
uploads it again as a texture. But a more common approac is having
QCanvasImage variables as class members and calling \l QCanvasPainter::addImage()
e.g. in \l QCanvasPainterItemRenderer::initializeResources().
Similarly to QCanvasBrush and QCanvasOffscreenCanvas, QCanvasImage is explicitly shared.
See \l{Implicit Data Sharing} and \l QSharedDataPointer for details.
\note A QCanvasImage object contains only a handle to a graphics resource, such
as a texture. Even when a detach occurs, the actual resource, i.e. the
underlying texture and the image data in it, is never actually copied or
duplicated. The actual owner of the real graphics resource (e.g., a
QRhiTexture) is the QCanvasPainter that handed out the QCanvasImage via
\l{QCanvasPainter::}{addImage()}.
A QCanvasImage always belongs to the QCanvasPainter that created it. Manually
removing images is done by calling \l{QCanvasPainter::}{removeImage()}. In
most cases this will not be necessary, however, since the painter will
automatically destroy any images during its own destruction.
*/
/*!
Constructs a default image.
*/
QCanvasImage::QCanvasImage()
: d(new QCanvasImagePrivate())
{
}
/*!
Constructs an image that is a copy of the given \a image.
*/
QCanvasImage::QCanvasImage(const QCanvasImage &image)
: d(image.d)
{
}
/*!
Destroys the image.
*/
QCanvasImage::~QCanvasImage() = default;
QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QCanvasImagePrivate)
/*!
Assigns the given \a image to this image and returns a reference to
this image.
*/
QCanvasImage &QCanvasImage::operator=(const QCanvasImage &image)
{
QCanvasImage(image).swap(*this);
return *this;
}
/*!
\fn QCanvasImage::QCanvasImage(QCanvasImage &&other) noexcept
Move-constructs a new QCanvasImage from \a other.
*/
/*!
\fn QCanvasImage &QCanvasImage::operator=(QCanvasImage &&other)
Move-assigns \a other to this QCanvasImage instance.
*/
/*!
\fn void QCanvasImage::swap(QCanvasImage &other)
\memberswap{image}
*/
/*!
Returns the image as a QVariant.
*/
QCanvasImage::operator QVariant() const
{
return QVariant::fromValue(*this);
}
/*!
\fn bool QCanvasImage::operator!=(const QCanvasImage &lhs, const QCanvasImage &rhs)
\return \c true if the image handle \a lhs is different from \a rhs; \c false otherwise.
\sa operator==()
*/
/*!
\fn bool QCanvasImage::operator==(const QCanvasImage &lhs, const QCanvasImage &rhs)
\return \c true if the image handle \a lhs is equal to \a rhs; \c false otherwise.
\note Equality means that the two image objects reference the same graphics
resources and the tint colors are the same. The contents (pixel data) is not
compared.
\sa operator!=()
*/
bool comparesEqual(const QCanvasImage &lhs, const QCanvasImage &rhs) noexcept
{
auto *d = QCanvasImagePrivate::get(&lhs);
auto *pd = QCanvasImagePrivate::get(&rhs);
if (pd == d)
return true;
if (pd->id != d->id ||
pd->width != d->width ||
pd->height != d->height ||
pd->sizeInBytes != d->sizeInBytes ||
pd->type != d->type ||
pd->tintColor != d->tintColor)
return false;
return true;
}
#ifndef QT_NO_DEBUG_STREAM
/*!
\internal
*/
QDebug operator<<(QDebug dbg, const QCanvasImage &i)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "QCanvasImage(" << i.id() << ": " << i.width() << " X " << i.height() << ')';
return dbg;
}
#endif // QT_NO_DEBUG_STREAM
/*!
Returns the texture id for this image.
When the image has not been assigned yet, this returns 0.
\sa isNull()
*/
int QCanvasImage::id() const
{
return d->id;
}
/*!
Returns the width of this image in pixels.
*/
int QCanvasImage::width() const
{
return d->width;
}
/*!
Returns the height of this image in pixels;
*/
int QCanvasImage::height() const
{
return d->height;
}
/*!
\fn QSize QCanvasImage::size() const
\return the size (width and height) of this image in pixels.
*/
/*!
Returns the size of this image in bytes.
QCanvasPainter does not keep copies of the CPU-side QImage data once
\l{QCanvasPainter::}{addImage()} has returned. If the source is a
\l{QCanvasOffscreenCanvas}{offscreen canvas}, then there is no CPU-side
image data in the first place. Therefore, the result of this function is an
approximation of the GPU memory that is used for the underlying texture.
\note The value is only an estimate based on the image format and
dimensions. Qt has no knowledge of how the data for textures is stored and
laid out in memory on the GPU side.
\note This function does not take mipmap or multisample data into
consideration.
*/
qsizetype QCanvasImage::sizeInBytes() const
{
return d->sizeInBytes;
}
/*!
Returns the device pixel ratio for the image. This is the
ratio between \e{device pixels} and \e{device independent pixels}.
A QCanvasImage with a greater than 1 device pixel ratio applies that scale
factor when using the image. For example, \c{drawImage(image, 0, 0)} is then
equivalent to \c{drawImage(image, 0, 0, image.width() /
image.devicePixelRatio(), image.height() / image.devicePixelRatio())}.
The default value is 1.0.
The value is carried over from the QImage the QCanvasImage is created from.
A QCanvasImage created from a texture or
\l{QCanvasOffscreenCanvas}{offscreen canvas} always has a device pixel ratio
of 1.
\sa QImage
*/
qreal QCanvasImage::devicePixelRatio() const
{
return d->devicePixelRatio;
}
/*!
Returns true if the image has not been assigned yet.
See \l QCanvasPainter::addImage() for details.
*/
bool QCanvasImage::isNull() const
{
return d->id == 0;
}
/*!
Returns the tint color of the image.
The default value is Qt::white.
*/
QColor QCanvasImage::tintColor() const
{
return d->tintColor;
}
/*!
Set the tint \a color of the image.
This color will be multiplied with the image color to
colorize the image. This can be used for example to highlight
icons or to adjust images becase on the theme.
*/
void QCanvasImage::setTintColor(QColor color)
{
detach();
d->tintColor = color;
}
/*!
\internal
*/
void QCanvasImage::detach()
{
if (d)
d.detach();
else
d = new QCanvasImagePrivate();
}
// ***** Private *****
QCanvasImagePrivate::QCanvasImagePrivate()
: id(0)
, width(0)
, height(0)
, devicePixelRatio(1.0)
, type(DataType::Unknown)
, sizeInBytes(0)
, tintColor(QColorConstants::White)
{
}
QT_END_NAMESPACE