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