You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
298 lines
6.8 KiB
298 lines
6.8 KiB
/* |
|
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org> |
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later |
|
*/ |
|
|
|
#include "scene/surfaceitem.h" |
|
#include "scene/scene.h" |
|
|
|
namespace KWin |
|
{ |
|
|
|
SurfaceItem::SurfaceItem(Scene *scene, Item *parent) |
|
: Item(scene, parent) |
|
{ |
|
} |
|
|
|
QMatrix4x4 SurfaceItem::surfaceToBufferMatrix() const |
|
{ |
|
return m_surfaceToBufferMatrix; |
|
} |
|
|
|
void SurfaceItem::setSurfaceToBufferMatrix(const QMatrix4x4 &matrix) |
|
{ |
|
m_surfaceToBufferMatrix = matrix; |
|
m_bufferToSurfaceMatrix = matrix.inverted(); |
|
} |
|
|
|
QRectF SurfaceItem::bufferSourceBox() const |
|
{ |
|
return m_bufferSourceBox; |
|
} |
|
|
|
void SurfaceItem::setBufferSourceBox(const QRectF &box) |
|
{ |
|
m_bufferSourceBox = box; |
|
} |
|
|
|
OutputTransform SurfaceItem::bufferTransform() const |
|
{ |
|
return m_bufferTransform; |
|
} |
|
|
|
void SurfaceItem::setBufferTransform(OutputTransform transform) |
|
{ |
|
m_bufferTransform = transform; |
|
} |
|
|
|
QSize SurfaceItem::bufferSize() const |
|
{ |
|
return m_bufferSize; |
|
} |
|
|
|
void SurfaceItem::setBufferSize(const QSize &size) |
|
{ |
|
m_bufferSize = size; |
|
} |
|
|
|
QRegion SurfaceItem::mapFromBuffer(const QRegion ®ion) const |
|
{ |
|
QRegion result; |
|
for (const QRect &rect : region) { |
|
result += m_bufferToSurfaceMatrix.mapRect(QRectF(rect)).toAlignedRect(); |
|
} |
|
return result; |
|
} |
|
|
|
static QRegion expandRegion(const QRegion ®ion, const QMargins &padding) |
|
{ |
|
if (region.isEmpty()) { |
|
return QRegion(); |
|
} |
|
|
|
QRegion ret; |
|
for (const QRect &rect : region) { |
|
ret += rect.marginsAdded(padding); |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
void SurfaceItem::addDamage(const QRegion ®ion) |
|
{ |
|
m_damage += region; |
|
|
|
const QRectF sourceBox = m_bufferTransform.map(m_bufferSourceBox, m_bufferSize); |
|
const qreal xScale = sourceBox.width() / size().width(); |
|
const qreal yScale = sourceBox.height() / size().height(); |
|
const QRegion logicalDamage = mapFromBuffer(region); |
|
|
|
const auto delegates = scene()->delegates(); |
|
for (SceneDelegate *delegate : delegates) { |
|
QRegion delegateDamage = logicalDamage; |
|
const qreal delegateScale = delegate->scale(); |
|
if (xScale != delegateScale || yScale != delegateScale) { |
|
// Simplified version of ceil(ceil(0.5 * output_scale / surface_scale) / output_scale) |
|
const int xPadding = std::ceil(0.5 / xScale); |
|
const int yPadding = std::ceil(0.5 / yScale); |
|
delegateDamage = expandRegion(delegateDamage, QMargins(xPadding, yPadding, xPadding, yPadding)); |
|
} |
|
scheduleRepaint(delegate, delegateDamage); |
|
} |
|
|
|
Q_EMIT damaged(); |
|
} |
|
|
|
void SurfaceItem::resetDamage() |
|
{ |
|
m_damage = QRegion(); |
|
} |
|
|
|
QRegion SurfaceItem::damage() const |
|
{ |
|
return m_damage; |
|
} |
|
|
|
SurfacePixmap *SurfaceItem::pixmap() const |
|
{ |
|
if (m_pixmap && m_pixmap->isValid()) { |
|
return m_pixmap.get(); |
|
} |
|
if (m_previousPixmap && m_previousPixmap->isValid()) { |
|
return m_previousPixmap.get(); |
|
} |
|
return nullptr; |
|
} |
|
|
|
SurfacePixmap *SurfaceItem::previousPixmap() const |
|
{ |
|
return m_previousPixmap.get(); |
|
} |
|
|
|
void SurfaceItem::referencePreviousPixmap() |
|
{ |
|
if (m_previousPixmap && m_previousPixmap->isDiscarded()) { |
|
m_referencePixmapCounter++; |
|
} |
|
} |
|
|
|
void SurfaceItem::unreferencePreviousPixmap() |
|
{ |
|
if (!m_previousPixmap || !m_previousPixmap->isDiscarded()) { |
|
return; |
|
} |
|
m_referencePixmapCounter--; |
|
if (m_referencePixmapCounter == 0) { |
|
m_previousPixmap.reset(); |
|
} |
|
} |
|
|
|
void SurfaceItem::updatePixmap() |
|
{ |
|
if (!m_pixmap) { |
|
m_pixmap = createPixmap(); |
|
} |
|
if (m_pixmap->isValid()) { |
|
m_pixmap->update(); |
|
} else { |
|
m_pixmap->create(); |
|
if (m_pixmap->isValid()) { |
|
unreferencePreviousPixmap(); |
|
discardQuads(); |
|
} |
|
} |
|
} |
|
|
|
void SurfaceItem::discardPixmap() |
|
{ |
|
if (m_pixmap) { |
|
if (m_pixmap->isValid()) { |
|
m_previousPixmap = std::move(m_pixmap); |
|
m_previousPixmap->markAsDiscarded(); |
|
referencePreviousPixmap(); |
|
} else { |
|
m_pixmap.reset(); |
|
} |
|
} |
|
} |
|
|
|
void SurfaceItem::destroyPixmap() |
|
{ |
|
m_pixmap.reset(); |
|
} |
|
|
|
void SurfaceItem::preprocess() |
|
{ |
|
updatePixmap(); |
|
} |
|
|
|
WindowQuadList SurfaceItem::buildQuads() const |
|
{ |
|
if (!pixmap()) { |
|
return {}; |
|
} |
|
|
|
const QList<QRectF> region = shape(); |
|
const auto size = pixmap()->size(); |
|
|
|
WindowQuadList quads; |
|
quads.reserve(region.count()); |
|
|
|
for (const QRectF rect : region) { |
|
WindowQuad quad; |
|
|
|
// Use toPoint to round the device position to match what we eventually |
|
// do for the geometry, otherwise we end up with mismatched UV |
|
// coordinates as the texture size is going to be in (rounded) device |
|
// coordinates as well. |
|
const QPointF bufferTopLeft = m_surfaceToBufferMatrix.map(rect.topLeft()).toPoint(); |
|
const QPointF bufferTopRight = m_surfaceToBufferMatrix.map(rect.topRight()).toPoint(); |
|
const QPointF bufferBottomRight = m_surfaceToBufferMatrix.map(rect.bottomRight()).toPoint(); |
|
const QPointF bufferBottomLeft = m_surfaceToBufferMatrix.map(rect.bottomLeft()).toPoint(); |
|
|
|
quad[0] = WindowVertex(rect.topLeft(), QPointF{bufferTopLeft.x() / size.width(), bufferTopLeft.y() / size.height()}); |
|
quad[1] = WindowVertex(rect.topRight(), QPointF{bufferTopRight.x() / size.width(), bufferTopRight.y() / size.height()}); |
|
quad[2] = WindowVertex(rect.bottomRight(), QPointF{bufferBottomRight.x() / size.width(), bufferBottomRight.y() / size.height()}); |
|
quad[3] = WindowVertex(rect.bottomLeft(), QPointF{bufferBottomLeft.x() / size.width(), bufferBottomLeft.y() / size.height()}); |
|
|
|
quads << quad; |
|
} |
|
|
|
return quads; |
|
} |
|
|
|
ContentType SurfaceItem::contentType() const |
|
{ |
|
return ContentType::None; |
|
} |
|
|
|
SurfaceTexture::~SurfaceTexture() |
|
{ |
|
} |
|
|
|
SurfacePixmap::SurfacePixmap(std::unique_ptr<SurfaceTexture> &&texture, QObject *parent) |
|
: QObject(parent) |
|
, m_texture(std::move(texture)) |
|
{ |
|
} |
|
|
|
GraphicsBuffer *SurfacePixmap::buffer() const |
|
{ |
|
return m_bufferRef.buffer(); |
|
} |
|
|
|
void SurfacePixmap::setBuffer(GraphicsBuffer *buffer) |
|
{ |
|
if (m_bufferRef.buffer() == buffer) { |
|
return; |
|
} |
|
m_bufferRef = buffer; |
|
if (m_bufferRef) { |
|
m_hasAlphaChannel = m_bufferRef->hasAlphaChannel(); |
|
m_size = m_bufferRef->size(); |
|
} |
|
} |
|
|
|
GraphicsBufferOrigin SurfacePixmap::bufferOrigin() const |
|
{ |
|
return m_bufferOrigin; |
|
} |
|
|
|
void SurfacePixmap::setBufferOrigin(GraphicsBufferOrigin origin) |
|
{ |
|
m_bufferOrigin = origin; |
|
} |
|
|
|
void SurfacePixmap::update() |
|
{ |
|
} |
|
|
|
SurfaceTexture *SurfacePixmap::texture() const |
|
{ |
|
return m_texture.get(); |
|
} |
|
|
|
bool SurfacePixmap::hasAlphaChannel() const |
|
{ |
|
return m_hasAlphaChannel; |
|
} |
|
|
|
QSize SurfacePixmap::size() const |
|
{ |
|
return m_size; |
|
} |
|
|
|
bool SurfacePixmap::isDiscarded() const |
|
{ |
|
return m_isDiscarded; |
|
} |
|
|
|
void SurfacePixmap::markAsDiscarded() |
|
{ |
|
m_isDiscarded = true; |
|
} |
|
|
|
} // namespace KWin |
|
|
|
#include "moc_surfaceitem.cpp"
|
|
|