// 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 "qcanvasoffscreencanvas_p.h"
#include <rhi/qrhi.h>

QT_BEGIN_NAMESPACE

/*!
    \class QCanvasOffscreenCanvas
    \since 6.11
    \brief QCanvasOffscreenCanvas represents an offscreen canvas that QCanvasPainter can target.
    \inmodule QtCanvasPainter

    An offscreen canvas is essentially a color buffer (a QRhiTexture), and
    possibly other associated graphics resources. After content has been
    rendered into the canvas's color buffer, the canvas can be
    \l{QCanvasPainter::addImage()}{registered} to the painter to retrieve a QCanvasImage
    that can then be used in \l{QCanvasPainter::drawImage()}{drawImage()} calls or as
    an \l{QCanvasImagePattern}{image pattern}.

    To create an offscreen canvas, call \l QCanvasPainter::createCanvas(). To target
    an offscreen canvas with with draw commands, call the appropriate \l
    QCanvasRhiPaintDriver::beginPaint() overload when working with the lower level
    API, or \l QCanvasPainterWidget::beginCanvasPainting() or \l
    QCanvasPainterItemRenderer::beginCanvasPainting() when using the convenience
    widget or Qt Quick item classes.

    Similarly to QCanvasImage and QCanvasBrush, QCanvasOffscreenCanvas is explicitly shared.
    See \l{Implicit Data Sharing} and \l QSharedDataPointer for details.

    \note As with QCanvasImage, a QCanvasOffscreenCanvas can be seen as an object merely
    containing handles. Even when a detach occurs, the actual resources, e.g.
    the underlying texture and the image data in it, are never actually copied
    or duplicated. The actual owner of any real graphics resources (e.g., a
    QRhiTexture) is the QCanvasPainter that handed out the QCanvasOffscreenCanvas via
    \l{QCanvasPainter::}{createCanvas()}.

    A canvas always belongs to the QCanvasPainter that created it. Manually
    destroying canvases is done by calling \l{QCanvasPainter::}{destroyCanvas()}. In
    most cases this will not be necessary, however, since the painter will
    automatically destroy any canvases during its own destruction.
 */

 /*!
    \enum QCanvasOffscreenCanvas::Flag

    Specifies the flags for the canvas.

    \value PreserveContents Indicates that the contents of the canvas is
    preserved when painting to it. This can have a negative effect on
    performance, depending on the GPU architecture. See
    \l{QRhiTextureRenderTarget::PreserveColorContents} for details. Setting
    this flag is not supported when the sample count is greater than 1.

    \value MipMaps Indicates that the canvas' backing texture should have
    mipmapping enabled. Setting this flag is not supported when the sample count
    is greater than 1. Note that the mipmap sequence is generated when calling
    \l{QCanvasPainter::}{addImage()} with the
    \l{QCanvasPainter::ImageFlag::}{GenerateMipmaps} flag set. This implies that
    addImage() must be called every time after the canvas' content changes, so
    that the mipmap chain is regenerated. Frequent mipmap generation can have a
    negative effect on performance.
 */

/*!
    \internal
*/
QCanvasOffscreenCanvas::QCanvasOffscreenCanvas()
    : d(new QCanvasOffscreenCanvasPrivate)
{
}

/*!
    Copy constructor.
*/
QCanvasOffscreenCanvas::QCanvasOffscreenCanvas(const QCanvasOffscreenCanvas &canvas)
    : d(canvas.d)
{
}

/*!
    Destructor.
*/
QCanvasOffscreenCanvas::~QCanvasOffscreenCanvas() = default;

QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QCanvasOffscreenCanvasPrivate)

/*!
    Assigns a shallow copy of the given \a canvas to this canvas and returns a
    reference to this canvas. For more information about shallow copies, see the
    \l{Implicit Data Sharing}{implicit data sharing} documentation.

    \note The underlying resources, the graphics resources such as the
    QRhiTexture, are never copied or duplicated.
*/
QCanvasOffscreenCanvas &QCanvasOffscreenCanvas::operator=(const QCanvasOffscreenCanvas &canvas)
{
    QCanvasOffscreenCanvas(canvas).swap(*this);
    return *this;
}

/*!
    \fn bool QCanvasOffscreenCanvas::operator!=(const QCanvasOffscreenCanvas &lhs, const QCanvasOffscreenCanvas &rhs)

    \return \c true if the canvas handle \a lhs is different from \a rhs; \c false otherwise.

    \sa operator==()
*/

/*!
    \fn bool QCanvasOffscreenCanvas::operator==(const QCanvasOffscreenCanvas &lhs, const QCanvasOffscreenCanvas &rhs)

    \return \c true if the canvas handle \a lhs is equal to \a rhs; \c false otherwise.

    \note Equality means that the two canvas objects' fill colors are the same and they
    reference the same graphics resources. The contents (pixel data) is not
    compared.

    \sa operator!=()
*/
bool comparesEqual(const QCanvasOffscreenCanvas &lhs, const QCanvasOffscreenCanvas &rhs) noexcept
{
    auto *d = QCanvasOffscreenCanvasPrivate::get(&lhs);
    auto *pd = QCanvasOffscreenCanvasPrivate::get(&rhs);

    if (pd == d)
        return true;

    if (pd->rhiCanvas != d->rhiCanvas
        || pd->fillColor != d->fillColor)
    {
        return false;
    }

    return true;
}

/*!
    If multiple canvases share common data, this image makes a copy of the data
    and detaches itself from the sharing mechanism, making sure that this image
    is the only one referring to the data.

    Nothing is done if there is just a single reference.

    \note The canvas only contains handles to the underlying graphics resources,
    and data such as the fill color. The graphics resources themselves, such as
    the QRhiTexture, are never copied or duplicated.
 */
void QCanvasOffscreenCanvas::detach()
{
    if (d)
        d.detach();
    else
        d = new QCanvasOffscreenCanvasPrivate;
}

/*!
    \return true if this canvas has valid data.

    Normally this will always be true.

    \sa texture()
 */
bool QCanvasOffscreenCanvas::isNull() const
{
    return d->rhiCanvas.isNull();
}

/*!
    \return the width of this canvas in pixels.

    \sa height(), size()
 */
int QCanvasOffscreenCanvas::width() const
{
    return !d->rhiCanvas.isNull() ? d->rhiCanvas.tex->pixelSize().width() : 0;
}

/*!
    \return the height of this canvas in pixels.

    \sa width(), size()
 */
int QCanvasOffscreenCanvas::height() const
{
    return !d->rhiCanvas.isNull() ? d->rhiCanvas.tex->pixelSize().height() : 0;
}

/*!
    \return the size (width and height) of this canvas in pixels.

    \sa width(), height()
 */
QSize QCanvasOffscreenCanvas::size() const
{
    return !d->rhiCanvas.isNull() ? d->rhiCanvas.tex->pixelSize() : QSize();
}

/*!
    \return the flags with which the canvas was created.

    The flags are immutable and cannot be changed once the canvas has been
    created.
 */
QCanvasOffscreenCanvas::Flags QCanvasOffscreenCanvas::flags() const
{
    return d->rhiCanvas.flags;
}

/*!
    \return the current fill color.
 */
QColor QCanvasOffscreenCanvas::fillColor() const
{
    return d->fillColor;
}

/*!
    Sets the fill (clear) color to \a color.

    By default this is set to the value passed to
    \l{QCanvasPainter::}{createCanvas()}.
 */
void QCanvasOffscreenCanvas::setFillColor(const QColor &color)
{
    if (d->fillColor == color)
        return;

    detach();
    d->fillColor = color;
}

/*!
    \return the texture backing this offscreen canvas.

    \sa isNull()
 */
QRhiTexture *QCanvasOffscreenCanvas::texture() const
{
    return d->rhiCanvas.tex;
}

QT_END_NAMESPACE
