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.
 
 
 
 
 

123 lines
4.0 KiB

/*
SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#pragma once
#include "opengl/glplatform.h"
#include "opengl/gltexture.h"
#include "opengl/glutils.h"
namespace KWin
{
// in-place vertical mirroring
static void mirrorVertically(uchar *data, int height, int stride)
{
const int halfHeight = height / 2;
std::vector<uchar> temp(stride);
for (int y = 0; y < halfHeight; ++y) {
auto cur = &data[y * stride], dest = &data[(height - y - 1) * stride];
memcpy(temp.data(), cur, stride);
memcpy(cur, dest, stride);
memcpy(dest, temp.data(), stride);
}
}
static GLenum closestGLType(QImage::Format format)
{
switch (format) {
case QImage::Format_ARGB32:
case QImage::Format_ARGB32_Premultiplied:
case QImage::Format_RGB32:
return GL_BGRA;
default:
qDebug() << "unknown format" << format;
return GL_RGBA;
}
}
static void doGrabTexture(GLTexture *texture, QImage *target)
{
if (texture->size() != target->size()) {
return;
}
const auto context = OpenGlContext::currentContext();
const QSize size = texture->size();
const bool invertNeeded = context->isOpenGLES() ^ (texture->contentTransform() != OutputTransform::FlipY);
const bool invertNeededAndSupported = invertNeeded && context->supportsPackInvert();
GLboolean prev;
if (invertNeededAndSupported) {
glGetBooleanv(GL_PACK_INVERT_MESA, &prev);
glPixelStorei(GL_PACK_INVERT_MESA, GL_TRUE);
}
texture->bind();
// BUG: The nvidia driver fails to glGetTexImage
// Drop driver() == DriverNVidia some time after that's fixed
if (context->isOpenGLES() || context->glPlatform()->driver() == Driver_NVidia) {
GLFramebuffer fbo(texture);
GLFramebuffer::pushFramebuffer(&fbo);
context->glReadnPixels(0, 0, size.width(), size.height(), closestGLType(target->format()), GL_UNSIGNED_BYTE, target->sizeInBytes(), target->bits());
GLFramebuffer::popFramebuffer();
} else {
context->glGetnTexImage(texture->target(), 0, closestGLType(target->format()), GL_UNSIGNED_BYTE, target->sizeInBytes(), target->bits());
}
if (invertNeededAndSupported) {
if (!prev) {
glPixelStorei(GL_PACK_INVERT_MESA, prev);
}
} else if (invertNeeded) {
mirrorVertically(static_cast<uchar *>(target->bits()), size.height(), target->bytesPerLine());
}
}
static void grabTexture(GLTexture *texture, QImage *target)
{
const OutputTransform contentTransform = texture->contentTransform();
if (contentTransform == OutputTransform::Normal || contentTransform == OutputTransform::FlipY) {
doGrabTexture(texture, target);
} else {
const QSize size = contentTransform.map(texture->size());
const auto backingTexture = GLTexture::allocate(GL_RGBA8, size);
if (!backingTexture) {
return;
}
backingTexture->setContentTransform(OutputTransform::FlipY);
ShaderBinder shaderBinder(ShaderTrait::MapTexture);
QMatrix4x4 projectionMatrix;
projectionMatrix.scale(1, -1);
projectionMatrix.ortho(QRect(QPoint(), size));
shaderBinder.shader()->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, projectionMatrix);
GLFramebuffer fbo(backingTexture.get());
GLFramebuffer::pushFramebuffer(&fbo);
texture->render(size);
GLFramebuffer::popFramebuffer();
doGrabTexture(backingTexture.get(), target);
}
}
static inline QRegion scaleRegion(const QRegion &_region, qreal scale)
{
if (scale == 1.) {
return _region;
}
QRegion region;
for (auto it = _region.begin(), itEnd = _region.end(); it != itEnd; ++it) {
region += QRect(std::floor(it->x() * scale),
std::floor(it->y() * scale),
std::ceil(it->width() * scale),
std::ceil(it->height() * scale));
}
return region;
}
} // namespace KWin