diff --git a/CMakeLists.txt b/CMakeLists.txt index 408c323..220528a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,8 @@ PRIVATE ui/effects/slide_animation.h ui/gl/gl_detection.cpp ui/gl/gl_detection.h + ui/gl/gl_image.cpp + ui/gl/gl_image.h ui/gl/gl_surface.cpp ui/gl/gl_surface.h ui/image/image_prepare.cpp diff --git a/ui/gl/gl_image.cpp b/ui/gl/gl_image.cpp new file mode 100644 index 0000000..c1e7c20 --- /dev/null +++ b/ui/gl/gl_image.cpp @@ -0,0 +1,85 @@ +// This file is part of Desktop App Toolkit, +// a set of libraries for developing nice desktop applications. +// +// For license and copyright information please follow this link: +// https://github.com/desktop-app/legal/blob/master/LEGAL +// +#include "ui/gl/gl_image.h" + +#include + +namespace Ui::GL { +namespace details { + +void GenerateTextures(QOpenGLFunctions &f, gsl::span values) { + Expects(!values.empty()); + + f.glGenTextures(values.size(), values.data()); + for (const auto texture : values) { + f.glBindTexture(GL_TEXTURE_2D, texture); + const auto clamp = GL_CLAMP_TO_EDGE; + f.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp); + f.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, clamp); + f.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + f.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } +} + +void DestroyTextures(QOpenGLFunctions &f, gsl::span values) { + Expects(!values.empty()); + + f.glDeleteTextures(values.size(), values.data()); + ranges::fill(values, 0); +} + +} // namespace details +void Image::setImage(QImage image) { + _image = std::move(image); +} + +const QImage &Image::image() const { + return _image; +} + +QImage Image::takeImage() { + return _image.isNull() ? base::take(_storage) : base::take(_image); +} + +void Image::invalidate() { + _storage = base::take(_image); +} + +void Image::bind(QOpenGLFunctions &f) { + Expects(!_image.isNull()); + + _textures.ensureCreated(f); + const auto cacheKey = _image.cacheKey(); + const auto upload = (_cacheKey != cacheKey); + if (upload) { + _cacheKey = cacheKey; + _index = 1 - _index; + } + _textures.bind(f, _index); + const auto error = f.glGetError(); + if (upload) { + f.glPixelStorei(GL_UNPACK_ROW_LENGTH, _image.bytesPerLine() / 4); + f.glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA, + _image.width(), + _image.height(), + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + _image.constBits()); + f.glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + } +} + +void Image::destroy(QOpenGLFunctions &f) { + _textures.destroy(f); + _cacheKey = 0; +} + +} // namespace Ui::GL diff --git a/ui/gl/gl_image.h b/ui/gl/gl_image.h new file mode 100644 index 0000000..3adb235 --- /dev/null +++ b/ui/gl/gl_image.h @@ -0,0 +1,73 @@ +// This file is part of Desktop App Toolkit, +// a set of libraries for developing nice desktop applications. +// +// For license and copyright information please follow this link: +// https://github.com/desktop-app/legal/blob/master/LEGAL +// +#pragma once + +#include + +namespace Ui::GL { +namespace details { + +void GenerateTextures(QOpenGLFunctions &f, gsl::span values); +void DestroyTextures(QOpenGLFunctions &f, gsl::span values); + +} // namespace details + +template +class Textures final { +public: + static_assert(Count > 0); + + void ensureCreated(QOpenGLFunctions &f) { + if (!created()) { + details::GenerateTextures(f, gsl::make_span(_values)); + } + } + void destroy(QOpenGLFunctions &f) { + if (created()) { + details::DestroyTextures(f, gsl::make_span(_values)); + } + } + + [[nodiscard]] void bind(QOpenGLFunctions &f, int index) const { + Expects(index >= 0 && index < Count); + + f.glBindTexture(GL_TEXTURE_2D, _values[index]); + } + + [[nodiscard]] bool created() const { + return (_values[0] != 0); + } + +private: + std::array _values = { { 0 } }; + +}; + +class Image final { +public: + void setImage(QImage image); + [[nodiscard]] const QImage &image() const; + [[nodiscard]] QImage takeImage(); + void invalidate(); + + void bind(QOpenGLFunctions &f); + void destroy(QOpenGLFunctions &f); + + explicit operator bool() const { + return !_image.isNull(); + } + +private: + QImage _image; + QImage _storage; + Textures<2> _textures; + qint64 _cacheKey = 0; + int _index = 0; + +}; + +} // namespace Ui::GL diff --git a/ui/gl/gl_surface.cpp b/ui/gl/gl_surface.cpp index 6710eb4..9e38fa9 100644 --- a/ui/gl/gl_surface.cpp +++ b/ui/gl/gl_surface.cpp @@ -71,15 +71,15 @@ void SurfaceOpenGL::initializeGL() { context, &QOpenGLContext::aboutToBeDestroyed, [=] { callDeInit(); }); - _renderer->init(this, context->functions()); + _renderer->init(this, *context->functions()); } void SurfaceOpenGL::resizeGL(int w, int h) { - _renderer->resize(this, context()->functions(), w, h); + _renderer->resize(this, *context()->functions(), w, h); } void SurfaceOpenGL::paintGL() { - _renderer->paint(this, context()->functions()); + _renderer->paint(this, *context()->functions()); } void SurfaceOpenGL::callDeInit() { @@ -91,7 +91,7 @@ void SurfaceOpenGL::callDeInit() { Assert(surface != nullptr); const auto context = this->context(); context->makeCurrent(surface); - _renderer->deinit(this, context->functions()); + _renderer->deinit(this, *context->functions()); } SurfaceRaster::SurfaceRaster( @@ -109,7 +109,7 @@ void SurfaceRaster::paintEvent(QPaintEvent *e) { void Renderer::paint( not_null widget, - not_null f) { + QOpenGLFunctions &f) { paintFallback(Painter(widget.get()), widget->rect(), Backend::OpenGL); } diff --git a/ui/gl/gl_surface.h b/ui/gl/gl_surface.h index 6af0fb5..9984c28 100644 --- a/ui/gl/gl_surface.h +++ b/ui/gl/gl_surface.h @@ -28,24 +28,24 @@ class Renderer { public: virtual void init( not_null widget, - not_null f) { + QOpenGLFunctions &f) { } virtual void deinit( not_null widget, - not_null f) { + QOpenGLFunctions &f) { } virtual void resize( not_null widget, - not_null f, + QOpenGLFunctions &f, int w, int h) { } virtual void paint( not_null widget, - not_null f); + QOpenGLFunctions &f); virtual void paintFallback( Painter &&p,