diff --git a/autotests/client/test_drag_drop.cpp b/autotests/client/test_drag_drop.cpp --- a/autotests/client/test_drag_drop.cpp +++ b/autotests/client/test_drag_drop.cpp @@ -187,7 +187,7 @@ using namespace KWayland::Client; auto s = m_compositor->createSurface(); - QImage img(QSize(100, 200), QImage::Format_ARGB32); + QImage img(QSize(100, 200), QImage::Format_RGB32); img.fill(Qt::red); s->attachBuffer(m_shm->createBuffer(img)); s->damage(QRect(0, 0, 100, 200)); diff --git a/autotests/client/test_shadow.cpp b/autotests/client/test_shadow.cpp --- a/autotests/client/test_shadow.cpp +++ b/autotests/client/test_shadow.cpp @@ -204,28 +204,28 @@ // now create the shadow QScopedPointer shadow(m_shadow->createShadow(surface.data())); - QImage topLeftImage(QSize(10, 10), QImage::Format_ARGB32); + QImage topLeftImage(QSize(10, 10), QImage::Format_ARGB32_Premultiplied); topLeftImage.fill(Qt::white); shadow->attachTopLeft(m_shm->createBuffer(topLeftImage)); - QImage topImage(QSize(11, 11), QImage::Format_ARGB32); + QImage topImage(QSize(11, 11), QImage::Format_ARGB32_Premultiplied); topImage.fill(Qt::black); shadow->attachTop(m_shm->createBuffer(topImage)); - QImage topRightImage(QSize(12, 12), QImage::Format_ARGB32); + QImage topRightImage(QSize(12, 12), QImage::Format_ARGB32_Premultiplied); topRightImage.fill(Qt::red); shadow->attachTopRight(m_shm->createBuffer(topRightImage)); - QImage rightImage(QSize(13, 13), QImage::Format_ARGB32); + QImage rightImage(QSize(13, 13), QImage::Format_ARGB32_Premultiplied); rightImage.fill(Qt::darkRed); shadow->attachRight(m_shm->createBuffer(rightImage)); - QImage bottomRightImage(QSize(14, 14), QImage::Format_ARGB32); + QImage bottomRightImage(QSize(14, 14), QImage::Format_ARGB32_Premultiplied); bottomRightImage.fill(Qt::green); shadow->attachBottomRight(m_shm->createBuffer(bottomRightImage)); - QImage bottomImage(QSize(15, 15), QImage::Format_ARGB32); + QImage bottomImage(QSize(15, 15), QImage::Format_ARGB32_Premultiplied); bottomImage.fill(Qt::darkGreen); shadow->attachBottom(m_shm->createBuffer(bottomImage)); - QImage bottomLeftImage(QSize(16, 16), QImage::Format_ARGB32); + QImage bottomLeftImage(QSize(16, 16), QImage::Format_ARGB32_Premultiplied); bottomLeftImage.fill(Qt::blue); shadow->attachBottomLeft(m_shm->createBuffer(bottomLeftImage)); - QImage leftImage(QSize(17, 17), QImage::Format_ARGB32); + QImage leftImage(QSize(17, 17), QImage::Format_ARGB32_Premultiplied); leftImage.fill(Qt::darkBlue); shadow->attachLeft(m_shm->createBuffer(leftImage)); shadow->setOffsets(QMarginsF(1, 2, 3, 4)); diff --git a/autotests/client/test_shm_pool.cpp b/autotests/client/test_shm_pool.cpp --- a/autotests/client/test_shm_pool.cpp +++ b/autotests/client/test_shm_pool.cpp @@ -44,6 +44,7 @@ void testCreateBufferNullSize(); void testCreateBufferInvalidSize(); void testCreateBufferFromImage(); + void testCreateBufferFromImageWithAlpha(); void testCreateBufferFromData(); void testReuseBuffer(); void testDestroy(); @@ -154,33 +155,46 @@ void TestShmPool::testCreateBufferFromImage() { QVERIFY(m_shmPool->isValid()); - QImage img(24, 24, QImage::Format_ARGB32); + QImage img(24, 24, QImage::Format_RGB32); img.fill(Qt::black); QVERIFY(!img.isNull()); auto buffer = m_shmPool->createBuffer(img).toStrongRef(); QVERIFY(buffer); QCOMPARE(buffer->size(), img.size()); - QImage img2(buffer->address(), img.width(), img.height(), QImage::Format_ARGB32); + QImage img2(buffer->address(), img.width(), img.height(), QImage::Format_RGB32); + QCOMPARE(img2, img); +} + +void TestShmPool::testCreateBufferFromImageWithAlpha() +{ + QVERIFY(m_shmPool->isValid()); + QImage img(24, 24, QImage::Format_ARGB32_Premultiplied); + img.fill(QColor(255,0,0,100)); //red with alpha + QVERIFY(!img.isNull()); + auto buffer = m_shmPool->createBuffer(img).toStrongRef(); + QVERIFY(buffer); + QCOMPARE(buffer->size(), img.size()); + QImage img2(buffer->address(), img.width(), img.height(), QImage::Format_ARGB32_Premultiplied); QCOMPARE(img2, img); } void TestShmPool::testCreateBufferFromData() { QVERIFY(m_shmPool->isValid()); - QImage img(24, 24, QImage::Format_ARGB32); + QImage img(24, 24, QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); QVERIFY(!img.isNull()); auto buffer = m_shmPool->createBuffer(img.size(), img.bytesPerLine(), img.constBits()).toStrongRef(); QVERIFY(buffer); QCOMPARE(buffer->size(), img.size()); - QImage img2(buffer->address(), img.width(), img.height(), QImage::Format_ARGB32); + QImage img2(buffer->address(), img.width(), img.height(), QImage::Format_ARGB32_Premultiplied); QCOMPARE(img2, img); } void TestShmPool::testReuseBuffer() { QVERIFY(m_shmPool->isValid()); - QImage img(24, 24, QImage::Format_ARGB32); + QImage img(24, 24, QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); QVERIFY(!img.isNull()); auto buffer = m_shmPool->createBuffer(img).toStrongRef(); diff --git a/autotests/client/test_wayland_seat.cpp b/autotests/client/test_wayland_seat.cpp --- a/autotests/client/test_wayland_seat.cpp +++ b/autotests/client/test_wayland_seat.cpp @@ -830,7 +830,7 @@ // let's map the surfaces auto render = [this] (Surface *s, const QSize &size) { - QImage image(size, QImage::Format_ARGB32); + QImage image(size, QImage::Format_ARGB32_Premultiplied); image.fill(Qt::black); s->attachBuffer(m_shm->createBuffer(image)); s->damage(QRect(QPoint(0, 0), size)); @@ -1171,7 +1171,7 @@ // let's map the surfaces auto render = [this] (Surface *s, const QSize &size) { - QImage image(size, QImage::Format_ARGB32); + QImage image(size, QImage::Format_ARGB32_Premultiplied); image.fill(Qt::black); s->attachBuffer(m_shm->createBuffer(image)); s->damage(QRect(QPoint(0, 0), size)); @@ -1328,7 +1328,7 @@ QCOMPARE(cursor->surface()->buffer()->data(), img); // and add another image to the surface - QImage blue(QSize(10, 20), QImage::Format_ARGB32); + QImage blue(QSize(10, 20), QImage::Format_ARGB32_Premultiplied); blue.fill(Qt::blue); cursorSurface->attachBuffer(m_shm->createBuffer(blue)); cursorSurface->damage(QRect(0, 0, 10, 20)); @@ -1382,7 +1382,7 @@ // now let's set the cursor Surface *cursorSurface = m_compositor->createSurface(m_compositor); QVERIFY(cursorSurface); - QImage red(QSize(10, 10), QImage::Format_ARGB32); + QImage red(QSize(10, 10), QImage::Format_ARGB32_Premultiplied); red.fill(Qt::red); cursorSurface->attachBuffer(m_shm->createBuffer(red)); cursorSurface->damage(QRect(0, 0, 10, 10)); @@ -1392,7 +1392,7 @@ QCOMPARE(pointer->cursor()->surface()->buffer()->data(), red); // and damage the surface - QImage blue(QSize(10, 10), QImage::Format_ARGB32); + QImage blue(QSize(10, 10), QImage::Format_ARGB32_Premultiplied); blue.fill(Qt::blue); cursorSurface->attachBuffer(m_shm->createBuffer(blue)); cursorSurface->damage(QRect(0, 0, 10, 10)); diff --git a/autotests/client/test_wayland_subsurface.cpp b/autotests/client/test_wayland_subsurface.cpp --- a/autotests/client/test_wayland_subsurface.cpp +++ b/autotests/client/test_wayland_subsurface.cpp @@ -619,7 +619,7 @@ QSignalSpy childDamagedSpy(childSurface, &SurfaceInterface::damaged); QVERIFY(childDamagedSpy.isValid()); - QImage image(QSize(200, 200), QImage::Format_ARGB32); + QImage image(QSize(200, 200), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::black); surface->attachBuffer(m_shm->createBuffer(image)); surface->damage(QRect(0, 0, 200, 200)); @@ -632,7 +632,7 @@ QVERIFY(!childSurface->isMapped()); QVERIFY(!parentSurface->isMapped()); - QImage image2(QSize(400, 400), QImage::Format_ARGB32); + QImage image2(QSize(400, 400), QImage::Format_ARGB32_Premultiplied); image2.fill(Qt::red); parent->attachBuffer(m_shm->createBuffer(image2)); parent->damage(QRect(0, 0, 400, 400)); @@ -681,7 +681,7 @@ QSignalSpy childDamagedSpy(childSurface, &SurfaceInterface::damaged); QVERIFY(childDamagedSpy.isValid()); - QImage image(QSize(200, 200), QImage::Format_ARGB32); + QImage image(QSize(200, 200), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::black); surface->attachBuffer(m_shm->createBuffer(image)); surface->damage(QRect(0, 0, 200, 200)); @@ -853,7 +853,7 @@ // first map the child, should not map it QSignalSpy child3DamageSpy(child3->surface().data(), &SurfaceInterface::damaged); QVERIFY(child3DamageSpy.isValid()); - QImage image(QSize(200, 200), QImage::Format_ARGB32); + QImage image(QSize(200, 200), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::black); childLevel3Surface->attachBuffer(m_shm->createBuffer(image)); childLevel3Surface->damage(QRect(0, 0, 200, 200)); @@ -1025,7 +1025,7 @@ m_subCompositor->createSubSurface(child.data(), parent.data()); // let's damage this surface, will be in sub-surface pending state - QImage image(QSize(100, 100), QImage::Format_ARGB32); + QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::red); child->attachBuffer(m_shm->createBuffer(image)); child->damage(QRect(0, 0, 100, 100)); diff --git a/autotests/client/test_wayland_surface.cpp b/autotests/client/test_wayland_surface.cpp --- a/autotests/client/test_wayland_surface.cpp +++ b/autotests/client/test_wayland_surface.cpp @@ -261,7 +261,7 @@ QVERIFY(damageSpy.isEmpty()); QVERIFY(!serverSurface->isMapped()); - QImage img(QSize(10, 10), QImage::Format_ARGB32); + QImage img(QSize(10, 10), QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); auto b = m_shm->createBuffer(img); s->attachBuffer(b); @@ -275,7 +275,7 @@ // damage multiple times QRegion testRegion(5, 8, 3, 6); testRegion = testRegion.united(QRect(10, 20, 30, 15)); - img = QImage(QSize(40, 35), QImage::Format_ARGB32); + img = QImage(QSize(40, 35), QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); b = m_shm->createBuffer(img); s->attachBuffer(b); @@ -302,7 +302,7 @@ QSignalSpy frameRenderedSpy(s, SIGNAL(frameRendered())); QVERIFY(frameRenderedSpy.isValid()); - QImage img(QSize(10, 10), QImage::Format_ARGB32); + QImage img(QSize(10, 10), QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); auto b = m_shm->createBuffer(img); s->attachBuffer(b); @@ -325,10 +325,10 @@ KWayland::Server::SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface); - // create two images + // create three images QImage black(24, 24, QImage::Format_RGB32); black.fill(Qt::black); - QImage red(24, 24, QImage::Format_ARGB32); + QImage red(24, 24, QImage::Format_ARGB32); //Note - deliberately not premultiplied red.fill(QColor(255, 0, 0, 128)); QImage blue(24, 24, QImage::Format_ARGB32_Premultiplied); blue.fill(QColor(0, 0, 255, 128)); @@ -371,8 +371,15 @@ KWayland::Server::BufferInterface *buffer2 = serverSurface->buffer(); buffer2->ref(); QVERIFY(buffer2->shmBuffer()); - QCOMPARE(buffer2->data(), red); - QCOMPARE(buffer2->data().format(), QImage::Format_ARGB32); + QCOMPARE(buffer2->data().format(), QImage::Format_ARGB32_Premultiplied); + QCOMPARE(buffer2->data().width(), 24); + QCOMPARE(buffer2->data().height(), 24); + for (int i = 0; i < 24; ++i) { + for (int j = 0; j < 24; ++j) { + // it's premultiplied in the format + QCOMPARE(buffer2->data().pixel(i, j), qRgba(128, 0, 0, 128)); + } + } buffer2->unref(); QVERIFY(buffer2->isReferenced()); QVERIFY(!redBuffer.data()->isReleased()); @@ -400,7 +407,7 @@ KWayland::Server::BufferInterface *buffer3 = serverSurface->buffer(); buffer3->ref(); QVERIFY(buffer3->shmBuffer()); - QCOMPARE(buffer3->data().format(), QImage::Format_ARGB32); + QCOMPARE(buffer3->data().format(), QImage::Format_ARGB32_Premultiplied); QCOMPARE(buffer3->data().width(), 24); QCOMPARE(buffer3->data().height(), 24); for (int i = 0; i < 24; ++i) { @@ -482,7 +489,7 @@ // create two images QImage black(24, 24, QImage::Format_RGB32); black.fill(Qt::black); - QImage red(24, 24, QImage::Format_ARGB32); + QImage red(24, 24, QImage::Format_ARGB32_Premultiplied); red.fill(QColor(255, 0, 0, 128)); auto blackBuffer = pool1.createBuffer(black); @@ -707,7 +714,7 @@ scaleChangedSpy.clear(); //attach a buffer of 100x100, our scale is 4, so this should be a size of 25x25 - QImage red(100, 100, QImage::Format_ARGB32); + QImage red(100, 100, QImage::Format_ARGB32_Premultiplied); red.fill(QColor(255, 0, 0, 128)); auto redBuffer = m_shm->createBuffer(red); s->attachBuffer(redBuffer.data()); @@ -731,7 +738,7 @@ scaleChangedSpy.clear(); //set scale and size in one commit, buffer is 50x50 at scale 2 so size should be 25x25 - QImage blue(50, 50, QImage::Format_ARGB32); + QImage blue(50, 50, QImage::Format_ARGB32_Premultiplied); red.fill(QColor(255, 0, 0, 128)); auto blueBuffer = m_shm->createBuffer(blue); s->attachBuffer(blueBuffer.data()); @@ -907,7 +914,7 @@ // let's damage this surface QSignalSpy damagedSpy(serverSurface, &SurfaceInterface::damaged); QVERIFY(damagedSpy.isValid()); - QImage image(QSize(100, 100), QImage::Format_ARGB32); + QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::red); s->attachBuffer(m_shm->createBuffer(image)); s->damage(QRect(0, 0, 100, 100)); @@ -948,7 +955,7 @@ QVERIFY(serverSurface); // now render to it - QImage img(QSize(10, 10), QImage::Format_ARGB32); + QImage img(QSize(10, 10), QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); auto b = m_shm->createBuffer(img); s->attachBuffer(b); diff --git a/src/client/buffer.h b/src/client/buffer.h --- a/src/client/buffer.h +++ b/src/client/buffer.h @@ -52,6 +52,7 @@ ARGB32, ///< 32-bit ARGB format, can be used for QImage::Format_ARGB32 and QImage::Format_ARGB32_Premultiplied RGB32 ///< 32-bit RGB format, can be used for QImage::Format_RGB32 }; + ~Buffer(); /** * Copies the data from @p src into the Buffer. diff --git a/src/client/shm_pool.cpp b/src/client/shm_pool.cpp --- a/src/client/shm_pool.cpp +++ b/src/client/shm_pool.cpp @@ -170,13 +170,15 @@ static Buffer::Format toBufferFormat(const QImage &image) { switch (image.format()) { - case QImage::Format_ARGB32: case QImage::Format_ARGB32_Premultiplied: return Buffer::Format::ARGB32; case QImage::Format_RGB32: return Buffer::Format::RGB32; + case QImage::Format_ARGB32: + qCWarning(KWAYLAND_CLIENT) << "Unsupported image format: " << image.format() << ". expect slow performance. Use QImage::Format_ARGB32_Premultiplied"; + return Buffer::Format::ARGB32; default: - qCWarning(KWAYLAND_CLIENT) << "Unsupported image format: " << image.format() << "going to use ARGB32, expect rendering errors"; + qCWarning(KWAYLAND_CLIENT) << "Unsupported image format: " << image.format() << ". expect slow performance."; return Buffer::Format::ARGB32; } } @@ -187,11 +189,17 @@ if (image.isNull() || !d->valid) { return QWeakPointer(); } - auto it = d->getBuffer(image.size(), image.bytesPerLine(), toBufferFormat(image)); + auto format = toBufferFormat(image); + auto it = d->getBuffer(image.size(), image.bytesPerLine(), format); if (it == d->buffers.end()) { return QWeakPointer(); } - (*it)->copy(image.bits()); + if (format == Buffer::Format::ARGB32 && image.format() != QImage::Format_ARGB32_Premultiplied) { + auto imageCopy = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + (*it)->copy(imageCopy.bits()); + } else { + (*it)->copy(image.bits()); + } return QWeakPointer(*it); } diff --git a/src/server/buffer_interface.cpp b/src/server/buffer_interface.cpp --- a/src/server/buffer_interface.cpp +++ b/src/server/buffer_interface.cpp @@ -224,7 +224,7 @@ } switch (wl_shm_buffer_get_format(shmBuffer)) { case WL_SHM_FORMAT_ARGB8888: - return QImage::Format_ARGB32; + return QImage::Format_ARGB32_Premultiplied; case WL_SHM_FORMAT_XRGB8888: return QImage::Format_RGB32; default: