summaryrefslogtreecommitdiffstats
path: root/src/opengl/qopenglgradientcache.cpp
blob: 24453655e216b01745d33823283ffa9ffdfad682 (plain)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
// Copyright (C) 2016 The Qt Company Ltd.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only#include"qopenglgradientcache_p.h"#include <private/qdrawhelper_p.h>#include <private/qopenglcontext_p.h>#include <private/qrgba64_p.h>#include <QtCore/qmutex.h>#include <QtCore/qrandom.h>#include"qopenglfunctions.h"#include <private/qopenglextensions_p.h>#ifndef GL_RGBA16#define GL_RGBA16 0x805B#endif QT_BEGIN_NAMESPACE class QOpenGL2GradientCacheWrapper {public: QOpenGL2GradientCache *cacheForContext(QOpenGLContext *context) { QMutexLocker lock(&m_mutex);return m_resource.value<QOpenGL2GradientCache>(context);}private: QOpenGLMultiGroupSharedResource m_resource; QMutex m_mutex;};Q_GLOBAL_STATIC(QOpenGL2GradientCacheWrapper, qt_gradient_caches)QOpenGL2GradientCache::QOpenGL2GradientCache(QOpenGLContext *ctx):QOpenGLSharedResource(ctx->shareGroup()){}QOpenGL2GradientCache::~QOpenGL2GradientCache(){ cache.clear();} QOpenGL2GradientCache *QOpenGL2GradientCache::cacheForContext(QOpenGLContext *context){returnqt_gradient_caches()->cacheForContext(context);}voidQOpenGL2GradientCache::invalidateResource(){ QMutexLocker lock(&m_mutex); cache.clear();}voidQOpenGL2GradientCache::freeResource(QOpenGLContext *){cleanCache();}voidQOpenGL2GradientCache::cleanCache(){ QMutexLocker lock(&m_mutex);QOpenGLGradientColorTableHash::const_iterator it = cache.constBegin(); QOpenGLFunctions *funcs =QOpenGLContext::currentContext()->functions();for(; it != cache.constEnd(); ++it) {const CacheInfo &cache_info = it.value(); funcs->glDeleteTextures(1, &cache_info.texId);} cache.clear();} GLuint QOpenGL2GradientCache::getBuffer(const QGradient &gradient, qreal opacity){ quint64 hash_val =0;const QGradientStops stops = gradient.stops();for(int i =0; i < stops.size() && i <=2; i++) hash_val += stops[i].second.rgba();const QMutexLocker lock(&m_mutex);QOpenGLGradientColorTableHash::const_iterator it = cache.constFind(hash_val);if(it == cache.constEnd())returnaddCacheElement(hash_val, gradient, opacity);else{do{const CacheInfo &cache_info = it.value();if(cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode()){return cache_info.texId;}++it;}while(it != cache.constEnd() && it.key() == hash_val);// an exact match for these stops and opacity was not found, create new cachereturnaddCacheElement(hash_val, gradient, opacity);}} GLuint QOpenGL2GradientCache::addCacheElement(quint64 hash_val,const QGradient &gradient, qreal opacity){ QOpenGLFunctions *funcs =QOpenGLContext::currentContext()->functions();if(cache.size() ==maxCacheSize()) {int elem_to_remove =QRandomGenerator::global()->bounded(maxCacheSize()); quint64 key = cache.keys()[elem_to_remove];// need to call glDeleteTextures on each removed cache entry:QOpenGLGradientColorTableHash::const_iterator it = cache.constFind(key);do{ funcs->glDeleteTextures(1, &it.value().texId);}while(++it != cache.constEnd() && it.key() == key); cache.remove(key);// may remove more than 1, but OK} CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode()); funcs->glGenTextures(1, &cache_entry.texId); funcs->glBindTexture(GL_TEXTURE_2D, cache_entry.texId);if(static_cast<QOpenGLExtensions *>(funcs)->hasOpenGLExtension(QOpenGLExtensions::Sized16Formats)) { QRgba64 buffer[1024];generateGradientColorTable(gradient, buffer,paletteSize(), opacity); funcs->glTexImage2D(GL_TEXTURE_2D,0, GL_RGBA16,paletteSize(),1,0, GL_RGBA, GL_UNSIGNED_SHORT, buffer);}else{ uint buffer[1024];generateGradientColorTable(gradient, buffer,paletteSize(), opacity); funcs->glTexImage2D(GL_TEXTURE_2D,0, GL_RGBA,paletteSize(),1,0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);}return cache.insert(hash_val, cache_entry).value().texId;}//TODO: Let GL generate the texture using an FBOvoidQOpenGL2GradientCache::generateGradientColorTable(const QGradient& gradient, QRgba64 *colorTable,int size, qreal opacity)const{int pos =0;const QGradientStops s = gradient.stops();bool colorInterpolation = (gradient.interpolationMode() ==QGradient::ColorInterpolation); uint alpha =qRound(opacity *256); QRgba64 current_color =combineAlpha256(s[0].second.rgba64(), alpha); qreal incr =1.0/qreal(size); qreal fpos =1.5* incr; colorTable[pos++] =qPremultiply(current_color);while(fpos <= s.first().first) { colorTable[pos] = colorTable[pos -1]; pos++; fpos += incr;}if(colorInterpolation) current_color =qPremultiply(current_color);const int sLast = s.size() -1;for(int i =0; i < sLast; ++i) { qreal delta =1/(s[i+1].first - s[i].first); QRgba64 next_color =combineAlpha256(s[i +1].second.rgba64(), alpha);if(colorInterpolation) next_color =qPremultiply(next_color);while(fpos < s[i+1].first && pos < size) {int dist =int(256* ((fpos - s[i].first) * delta));int idist =256- dist;if(colorInterpolation) colorTable[pos] =interpolate256(current_color, idist, next_color, dist);else colorTable[pos] =qPremultiply(interpolate256(current_color, idist, next_color, dist));++pos; fpos += incr;} current_color = next_color;}Q_ASSERT(s.size() >0); QRgba64 last_color =qPremultiply(combineAlpha256(s[sLast].second.rgba64(), alpha));for(;pos < size; ++pos) colorTable[pos] = last_color;// Make sure the last color stop is represented at the end of the table colorTable[size-1] = last_color;}voidQOpenGL2GradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable,int size, qreal opacity)const{int pos =0;const QGradientStops s = gradient.stops();bool colorInterpolation = (gradient.interpolationMode() ==QGradient::ColorInterpolation); uint alpha =qRound(opacity *256);// Qt LIES! It returns ARGB (on little-endian AND on big-endian) uint current_color =ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha); qreal incr =1.0/qreal(size); qreal fpos =1.5* incr; colorTable[pos++] =ARGB2RGBA(qPremultiply(current_color));while(fpos <= s.first().first) { colorTable[pos] = colorTable[pos -1]; pos++; fpos += incr;}if(colorInterpolation) current_color =qPremultiply(current_color);const int sLast = s.size() -1;for(int i =0; i < sLast; ++i) { qreal delta =1/(s[i+1].first - s[i].first); uint next_color =ARGB_COMBINE_ALPHA(s[i +1].second.rgba(), alpha);if(colorInterpolation) next_color =qPremultiply(next_color);while(fpos < s[i+1].first && pos < size) {int dist =int(256* ((fpos - s[i].first) * delta));int idist =256- dist;if(colorInterpolation) colorTable[pos] =ARGB2RGBA(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));else colorTable[pos] =ARGB2RGBA(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)));++pos; fpos += incr;} current_color = next_color;}Q_ASSERT(s.size() >0); uint last_color =ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha)));for(;pos < size; ++pos) colorTable[pos] = last_color;// Make sure the last color stop is represented at the end of the table colorTable[size-1] = last_color;} QT_END_NAMESPACE 
close