summaryrefslogtreecommitdiffstats
path: root/src/opengl/qopengltextureglyphcache.cpp
blob: 0bab710b1b6d1f1ed17229f573fc9c107484b4b7 (plain)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
// 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"qopengltextureglyphcache_p.h"#include <private/qopenglpaintengine_p.h>#include"private/qopenglengineshadersource_p.h"#include <private/qopenglextensions_p.h>#include <qrgb.h>#include <private/qdrawhelper_p.h> QT_BEGIN_NAMESPACE static intnext_qopengltextureglyphcache_serial_number(){ Q_CONSTINIT static QBasicAtomicInt serial =Q_BASIC_ATOMIC_INITIALIZER(0);return1+ serial.fetchAndAddRelaxed(1);}QOpenGLTextureGlyphCache::QOpenGLTextureGlyphCache(QFontEngine::GlyphFormat format,const QTransform &matrix,const QColor &color):QImageTextureGlyphCache(format, matrix, color),m_textureResource(nullptr),pex(nullptr),m_blitProgram(nullptr),m_filterMode(Nearest),m_serialNumber(next_qopengltextureglyphcache_serial_number()),m_buffer(QOpenGLBuffer::VertexBuffer){#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUGqDebug(" -> QOpenGLTextureGlyphCache() %p for context %p.",this,QOpenGLContext::currentContext());#endif m_vertexCoordinateArray[0] = -1.0f; m_vertexCoordinateArray[1] = -1.0f; m_vertexCoordinateArray[2] =1.0f; m_vertexCoordinateArray[3] = -1.0f; m_vertexCoordinateArray[4] =1.0f; m_vertexCoordinateArray[5] =1.0f; m_vertexCoordinateArray[6] = -1.0f; m_vertexCoordinateArray[7] =1.0f; m_textureCoordinateArray[0] =0.0f; m_textureCoordinateArray[1] =0.0f; m_textureCoordinateArray[2] =1.0f; m_textureCoordinateArray[3] =0.0f; m_textureCoordinateArray[4] =1.0f; m_textureCoordinateArray[5] =1.0f; m_textureCoordinateArray[6] =0.0f; m_textureCoordinateArray[7] =1.0f;}QOpenGLTextureGlyphCache::~QOpenGLTextureGlyphCache(){#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUGqDebug(" -> ~QOpenGLTextureGlyphCache() %p.",this);#endifclear();}#if !QT_CONFIG(opengles2)staticinlineboolisCoreProfile(){returnQOpenGLContext::currentContext()->format().profile() ==QSurfaceFormat::CoreProfile;}#endifvoidQOpenGLTextureGlyphCache::createTextureData(int width,int height){ QOpenGLContext *ctx =const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());if(ctx ==nullptr) {qWarning("QOpenGLTextureGlyphCache::createTextureData: Called with no context");return;}// create in QImageTextureGlyphCache baseclass is meant to be called// only to create the initial image and does not preserve the content,// so we don't call when this function is called from resize.if(ctx->d_func()->workaround_brokenFBOReadBack &&image().isNull())QImageTextureGlyphCache::createTextureData(width, height);// Make the lower glyph texture size 16 x 16.if(width <16) width =16;if(height <16) height =16;if(m_textureResource && !m_textureResource->m_texture) {delete m_textureResource; m_textureResource =nullptr;}if(!m_textureResource) m_textureResource =newQOpenGLGlyphTexture(ctx); QOpenGLFunctions *funcs = ctx->functions(); funcs->glGenTextures(1, &m_textureResource->m_texture); funcs->glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); m_textureResource->m_width = width; m_textureResource->m_height = height;if(m_format ==QFontEngine::Format_A32 || m_format ==QFontEngine::Format_ARGB) { QVarLengthArray<uchar>data(width * height *4);for(int i =0; i < data.size(); ++i) data[i] =0; funcs->glTexImage2D(GL_TEXTURE_2D,0, GL_RGBA, width, height,0, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]);}else{ QVarLengthArray<uchar>data(width * height);for(int i =0; i < data.size(); ++i) data[i] =0;#if !QT_CONFIG(opengles2)const GLint internalFormat =isCoreProfile() ? GL_R8 : GL_ALPHA;const GLenum format =isCoreProfile() ? GL_RED : GL_ALPHA;#elseconst GLint internalFormat = GL_ALPHA;const GLenum format = GL_ALPHA;#endif funcs->glTexImage2D(GL_TEXTURE_2D,0, internalFormat, width, height,0, format, GL_UNSIGNED_BYTE, &data[0]);} funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); m_filterMode = Nearest;if(!m_buffer.isCreated()) { m_buffer.create(); m_buffer.bind();static GLfloat buf[sizeof(m_vertexCoordinateArray) +sizeof(m_textureCoordinateArray)];memcpy(buf, m_vertexCoordinateArray,sizeof(m_vertexCoordinateArray));memcpy(buf + (sizeof(m_vertexCoordinateArray) /sizeof(GLfloat)), m_textureCoordinateArray,sizeof(m_textureCoordinateArray)); m_buffer.allocate(buf,sizeof(buf)); m_buffer.release();}if(!m_vao.isCreated()) m_vao.create();}voidQOpenGLTextureGlyphCache::setupVertexAttribs(){ m_buffer.bind(); m_blitProgram->setAttributeBuffer(int(QT_VERTEX_COORDS_ATTR), GL_FLOAT,0,2); m_blitProgram->setAttributeBuffer(int(QT_TEXTURE_COORDS_ATTR), GL_FLOAT,sizeof(m_vertexCoordinateArray),2); m_blitProgram->enableAttributeArray(int(QT_VERTEX_COORDS_ATTR)); m_blitProgram->enableAttributeArray(int(QT_TEXTURE_COORDS_ATTR)); m_buffer.release();}static voidload_glyph_image_to_texture(QOpenGLContext *ctx, QImage &img, GLuint texture,int tx,int ty){ QOpenGLFunctions *funcs = ctx->functions();const int imgWidth = img.width();const int imgHeight = img.height();if(img.format() ==QImage::Format_Mono) { img = img.convertToFormat(QImage::Format_Grayscale8);}else if(img.depth() ==32) {if(img.format() ==QImage::Format_RGB32 // We need to make the alpha component equal to the average of the RGB values.// This is needed when drawing sub-pixel antialiased text on translucent targets.#if Q_BYTE_ORDER == Q_BIG_ENDIAN|| img.format() ==QImage::Format_ARGB32_Premultiplied #else|| (img.format() ==QImage::Format_ARGB32_Premultiplied && ctx->isOpenGLES())#endif) {for(int y =0; y < imgHeight; ++y) { QRgb *src = (QRgb *) img.scanLine(y);for(int x =0; x < imgWidth; ++x) {int r =qRed(src[x]);int g =qGreen(src[x]);int b =qBlue(src[x]);int avg;if(img.format() ==QImage::Format_RGB32) avg = (r + g + b +1) /3;// "+1" for rounding.else// Format_ARGB_Premultiplied avg =qAlpha(src[x]); src[x] =qRgba(r, g, b, avg);// swizzle the bits to accommodate for the GL_RGBA upload.#if Q_BYTE_ORDER != Q_BIG_ENDIANif(ctx->isOpenGLES())#endif src[x] =ARGB2RGBA(src[x]);}}}} funcs->glBindTexture(GL_TEXTURE_2D, texture);if(img.depth() ==32) {#if QT_CONFIG(opengles2) GLenum fmt = GL_RGBA;#else GLenum fmt = ctx->isOpenGLES() ? GL_RGBA : GL_BGRA;#endif// QT_CONFIG(opengles2)#if Q_BYTE_ORDER == Q_BIG_ENDIAN fmt = GL_RGBA;#endif funcs->glTexSubImage2D(GL_TEXTURE_2D,0, tx, ty, imgWidth, imgHeight, fmt, GL_UNSIGNED_BYTE, img.constBits());}else{// The scanlines in image are 32-bit aligned, even for mono or 8-bit formats. This// is good because it matches the default of 4 bytes for GL_UNPACK_ALIGNMENT.#if !QT_CONFIG(opengles2)const GLenum format =isCoreProfile() ? GL_RED : GL_ALPHA;#elseconst GLenum format = GL_ALPHA;#endif funcs->glTexSubImage2D(GL_TEXTURE_2D,0, tx, ty, imgWidth, imgHeight, format, GL_UNSIGNED_BYTE, img.constBits());}}static voidload_glyph_image_region_to_texture(QOpenGLContext *ctx,const QImage &srcImg,int x,int y,int w,int h, GLuint texture,int tx,int ty){Q_ASSERT(x + w <= srcImg.width() && y + h <= srcImg.height()); QImage img;if(x !=0|| y !=0|| w != srcImg.width() || h != srcImg.height()) img = srcImg.copy(x, y, w, h);else img = srcImg;load_glyph_image_to_texture(ctx, img, texture, tx, ty);}voidQOpenGLTextureGlyphCache::resizeTextureData(int width,int height){ QOpenGLContext *ctx =QOpenGLContext::currentContext();if(ctx ==nullptr) {qWarning("QOpenGLTextureGlyphCache::resizeTextureData: Called with no context");return;} QOpenGLFunctions *funcs = ctx->functions(); GLint oldFbo; funcs->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFbo);int oldWidth = m_textureResource->m_width;int oldHeight = m_textureResource->m_height;// Make the lower glyph texture size 16 x 16.if(width <16) width =16;if(height <16) height =16; GLuint oldTexture = m_textureResource->m_texture;createTextureData(width, height);if(ctx->d_func()->workaround_brokenFBOReadBack) {QImageTextureGlyphCache::resizeTextureData(width, height);load_glyph_image_region_to_texture(ctx,image(),0,0,qMin(oldWidth, width),qMin(oldHeight, height), m_textureResource->m_texture,0,0);return;}// ### the QTextureGlyphCache API needs to be reworked to allow// ### resizeTextureData to fail funcs->glBindFramebuffer(GL_FRAMEBUFFER, m_textureResource->m_fbo); GLuint tmp_texture; funcs->glGenTextures(1, &tmp_texture); funcs->glBindTexture(GL_TEXTURE_2D, tmp_texture); funcs->glTexImage2D(GL_TEXTURE_2D,0, GL_RGBA, oldWidth, oldHeight,0, GL_RGBA, GL_UNSIGNED_BYTE,nullptr); funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); m_filterMode = Nearest; funcs->glBindTexture(GL_TEXTURE_2D,0); funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tmp_texture,0); funcs->glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); funcs->glBindTexture(GL_TEXTURE_2D, oldTexture);if(pex !=nullptr) pex->transferMode(BrushDrawingMode); funcs->glDisable(GL_STENCIL_TEST); funcs->glDisable(GL_DEPTH_TEST); funcs->glDisable(GL_SCISSOR_TEST); funcs->glDisable(GL_BLEND); funcs->glViewport(0,0, oldWidth, oldHeight); QOpenGLShaderProgram *blitProgram =nullptr;if(pex ==nullptr) {if(m_blitProgram ==nullptr) { m_blitProgram =new QOpenGLShaderProgram;const bool isCoreProfile = ctx->format().profile() ==QSurfaceFormat::CoreProfile;{ QString source;#ifdef Q_OS_WASM source.append(QLatin1StringView(isCoreProfile ? qopenglslUntransformedPositionVertexShader_core : qopenglslUntransformedPositionVertexShader)); source.append(QLatin1StringView(isCoreProfile ? qopenglslMainWithTexCoordsVertexShader_core : qopenglslMainWithTexCoordsVertexShader));#else source.append(QLatin1StringView(isCoreProfile ? qopenglslMainWithTexCoordsVertexShader_core : qopenglslMainWithTexCoordsVertexShader)); source.append(QLatin1StringView(isCoreProfile ? qopenglslUntransformedPositionVertexShader_core : qopenglslUntransformedPositionVertexShader));#endif m_blitProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, source);}{ QString source;#ifdef Q_OS_WASM source.append(QLatin1StringView(isCoreProfile ? qopenglslImageSrcFragmentShader_core : qopenglslImageSrcFragmentShader)); source.append(QLatin1StringView(isCoreProfile ? qopenglslMainFragmentShader_core : qopenglslMainFragmentShader));#else source.append(QLatin1StringView(isCoreProfile ? qopenglslMainFragmentShader_core : qopenglslMainFragmentShader)); source.append(QLatin1StringView(isCoreProfile ? qopenglslImageSrcFragmentShader_core : qopenglslImageSrcFragmentShader));#endif m_blitProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, source);} m_blitProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); m_blitProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); m_blitProgram->link();if(m_vao.isCreated()) { m_vao.bind();setupVertexAttribs();}}if(m_vao.isCreated()) m_vao.bind();elsesetupVertexAttribs(); m_blitProgram->bind(); blitProgram = m_blitProgram;}else{ pex->uploadData(QT_VERTEX_COORDS_ATTR, m_vertexCoordinateArray,8); pex->uploadData(QT_TEXTURE_COORDS_ATTR, m_textureCoordinateArray,8); pex->shaderManager->useBlitProgram(); blitProgram = pex->shaderManager->blitProgram();} blitProgram->setUniformValue("imageTexture", QT_IMAGE_TEXTURE_UNIT); funcs->glDrawArrays(GL_TRIANGLE_FAN,0,4); funcs->glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); funcs->glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0, oldWidth, oldHeight); funcs->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,0); funcs->glDeleteTextures(1, &tmp_texture); funcs->glDeleteTextures(1, &oldTexture); funcs->glBindFramebuffer(GL_FRAMEBUFFER, (GLuint)oldFbo);if(pex !=nullptr) { funcs->glViewport(0,0, pex->width, pex->height); pex->updateClipScissorTest();}else{if(m_vao.isCreated()) { m_vao.release();}else{ m_blitProgram->disableAttributeArray(int(QT_VERTEX_COORDS_ATTR)); m_blitProgram->disableAttributeArray(int(QT_TEXTURE_COORDS_ATTR));}}}voidQOpenGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph,const QFixedPoint &subPixelPosition){ QOpenGLContext *ctx =QOpenGLContext::currentContext();if(ctx ==nullptr) {qWarning("QOpenGLTextureGlyphCache::fillTexture: Called with no context");return;}if(ctx->d_func()->workaround_brokenFBOReadBack) {QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition);load_glyph_image_region_to_texture(ctx,image(), c.x, c.y, c.w, c.h, m_textureResource->m_texture, c.x, c.y);return;} QImage mask =textureMapForGlyph(glyph, subPixelPosition);load_glyph_image_to_texture(ctx, mask, m_textureResource->m_texture, c.x, c.y);}intQOpenGLTextureGlyphCache::glyphPadding()const{if(m_format ==QFontEngine::Format_Mono)return8;elsereturn1;}intQOpenGLTextureGlyphCache::maxTextureWidth()const{ QOpenGLContext *ctx =const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());if(ctx ==nullptr)returnQImageTextureGlyphCache::maxTextureWidth();elsereturn ctx->d_func()->maxTextureSize();}intQOpenGLTextureGlyphCache::maxTextureHeight()const{ QOpenGLContext *ctx =const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());if(ctx ==nullptr)returnQImageTextureGlyphCache::maxTextureHeight();if(ctx->d_func()->workaround_brokenTexSubImage)returnqMin(1024, ctx->d_func()->maxTextureSize());elsereturn ctx->d_func()->maxTextureSize();}voidQOpenGLTextureGlyphCache::clear(){if(m_textureResource) m_textureResource->free(); m_textureResource =nullptr;delete m_blitProgram; m_blitProgram =nullptr; m_w =0; m_h =0; m_cx =0; m_cy =0; m_currentRowHeight =0; coords.clear();} QT_END_NAMESPACE 
close