File indexing completed on 2024-11-10 04:57:07

0001 /*
0002     SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #include "opengl/glplatform.h"
0010 #include "opengl/gltexture.h"
0011 #include "opengl/glutils.h"
0012 #include <spa/buffer/buffer.h>
0013 #include <spa/param/video/raw.h>
0014 
0015 namespace KWin
0016 {
0017 
0018 // in-place vertical mirroring
0019 static void mirrorVertically(uchar *data, int height, int stride)
0020 {
0021     const int halfHeight = height / 2;
0022     std::vector<uchar> temp(stride);
0023     for (int y = 0; y < halfHeight; ++y) {
0024         auto cur = &data[y * stride], dest = &data[(height - y - 1) * stride];
0025         memcpy(temp.data(), cur, stride);
0026         memcpy(cur, dest, stride);
0027         memcpy(dest, temp.data(), stride);
0028     }
0029 }
0030 
0031 static GLenum closestGLType(spa_video_format format)
0032 {
0033     switch (format) {
0034     case SPA_VIDEO_FORMAT_RGB:
0035         return GL_RGB;
0036     case SPA_VIDEO_FORMAT_BGR:
0037         return GL_BGR;
0038     case SPA_VIDEO_FORMAT_RGBx:
0039     case SPA_VIDEO_FORMAT_RGBA:
0040         return GL_RGBA;
0041     case SPA_VIDEO_FORMAT_BGRA:
0042     case SPA_VIDEO_FORMAT_BGRx:
0043         return GL_BGRA;
0044     default:
0045         qDebug() << "unknown format" << format;
0046         return GL_RGBA;
0047     }
0048 }
0049 
0050 static void doGrabTexture(GLTexture *texture, spa_data *spa, spa_video_format format)
0051 {
0052     const QSize size = texture->size();
0053     const bool invertNeeded = GLPlatform::instance()->isGLES() ^ (texture->contentTransform() != OutputTransform::FlipY);
0054     const bool invertNeededAndSupported = invertNeeded && GLPlatform::instance()->supports(GLFeature::PackInvert);
0055     GLboolean prev;
0056     if (invertNeededAndSupported) {
0057         glGetBooleanv(GL_PACK_INVERT_MESA, &prev);
0058         glPixelStorei(GL_PACK_INVERT_MESA, GL_TRUE);
0059     }
0060 
0061     texture->bind();
0062     // BUG: The nvidia driver fails to glGetTexImage
0063     // Drop driver() == DriverNVidia some time after that's fixed
0064     if (GLPlatform::instance()->isGLES() || GLPlatform::instance()->driver() == Driver_NVidia) {
0065         GLFramebuffer fbo(texture);
0066         GLFramebuffer::pushFramebuffer(&fbo);
0067         glReadPixels(0, 0, size.width(), size.height(), closestGLType(format), GL_UNSIGNED_BYTE, spa->data);
0068         GLFramebuffer::popFramebuffer();
0069     } else if (GLPlatform::instance()->glVersion() >= Version(4, 5)) {
0070         glGetTextureImage(texture->texture(), 0, closestGLType(format), GL_UNSIGNED_BYTE, spa->chunk->size, spa->data);
0071     } else {
0072         glGetTexImage(texture->target(), 0, closestGLType(format), GL_UNSIGNED_BYTE, spa->data);
0073     }
0074 
0075     if (invertNeededAndSupported) {
0076         if (!prev) {
0077             glPixelStorei(GL_PACK_INVERT_MESA, prev);
0078         }
0079     } else if (invertNeeded) {
0080         mirrorVertically(static_cast<uchar *>(spa->data), size.height(), spa->chunk->stride);
0081     }
0082 }
0083 
0084 static void grabTexture(GLTexture *texture, spa_data *spa, spa_video_format format)
0085 {
0086     // transform to correct orientation with the GPU first
0087     const QSize size = texture->contentTransform().map(texture->size());
0088     if (texture->contentTransform() == OutputTransform::FlipY) {
0089         doGrabTexture(texture, spa, format);
0090     } else {
0091         // need to transform the texture to a usable transformation first
0092         const auto backingTexture = GLTexture::allocate(GL_RGBA8, size);
0093         if (!backingTexture) {
0094             return;
0095         }
0096         GLFramebuffer fbo(backingTexture.get());
0097 
0098         ShaderBinder shaderBinder(ShaderTrait::MapTexture);
0099         QMatrix4x4 projectionMatrix;
0100         projectionMatrix.ortho(QRect(QPoint(), size));
0101         shaderBinder.shader()->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, projectionMatrix);
0102 
0103         GLFramebuffer::pushFramebuffer(&fbo);
0104         texture->render(size);
0105         GLFramebuffer::popFramebuffer();
0106         doGrabTexture(backingTexture.get(), spa, format);
0107     }
0108 }
0109 
0110 } // namespace KWin