int QOpenGLTextureGlyphCache::maxTextureHeight() const { QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); if (ctx == 0) return QImageTextureGlyphCache::maxTextureHeight(); if (ctx->d_func()->workaround_brokenTexSubImage) return qMin(1024, ctx->d_func()->maxTextureSize()); else return ctx->d_func()->maxTextureSize(); }
/*! Blits from the \a sourceRect rectangle in the \a source framebuffer object to the \a targetRect rectangle in the \a target framebuffer object. If \a source or \a target is 0, the default framebuffer will be used instead of a framebuffer object as source or target respectively. The \a buffers parameter should be a mask consisting of any combination of \c GL_COLOR_BUFFER_BIT, \c GL_DEPTH_BUFFER_BIT, and \c GL_STENCIL_BUFFER_BIT. Any buffer type that is not present both in the source and target buffers is ignored. The \a sourceRect and \a targetRect rectangles may have different sizes; in this case \a buffers should not contain \c GL_DEPTH_BUFFER_BIT or \c GL_STENCIL_BUFFER_BIT. The \a filter parameter should be set to \c GL_LINEAR or \c GL_NEAREST, and specifies whether linear or nearest interpolation should be used when scaling is performed. If \a source equals \a target a copy is performed within the same buffer. Results are undefined if the source and target rectangles overlap and have different sizes. The sizes must also be the same if any of the framebuffer objects are multisample framebuffers. Note that the scissor test will restrict the blit area if enabled. This function will have no effect unless hasOpenGLFramebufferBlit() returns true. \sa hasOpenGLFramebufferBlit() */ void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect, QOpenGLFramebufferObject *source, const QRect &sourceRect, GLbitfield buffers, GLenum filter) { QOpenGLContext *ctx = QOpenGLContext::currentContext(); if (!ctx) return; QOpenGLExtensions extensions(ctx); if (!extensions.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit)) return; const int sx0 = sourceRect.left(); const int sx1 = sourceRect.left() + sourceRect.width(); const int sy0 = sourceRect.top(); const int sy1 = sourceRect.top() + sourceRect.height(); const int tx0 = targetRect.left(); const int tx1 = targetRect.left() + targetRect.width(); const int ty0 = targetRect.top(); const int ty1 = targetRect.top() + targetRect.height(); extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, source ? source->handle() : 0); extensions.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target ? target->handle() : 0); extensions.glBlitFramebuffer(sx0, sy0, sx1, sy1, tx0, ty0, tx1, ty1, buffers, filter); extensions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo); }
/*! \fn bool QOpenGLFramebufferObject::bindDefault() Switches rendering back to the default, windowing system provided framebuffer. Returns \c true upon success, false otherwise. \sa bind(), release() */ bool QOpenGLFramebufferObject::bindDefault() { QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); QOpenGLFunctions functions(ctx); if (ctx) { ctx->d_func()->current_fbo = ctx->defaultFramebufferObject(); functions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo); #ifdef QT_DEBUG } else { qWarning("QOpenGLFramebufferObject::bindDefault() called without current context."); #endif } return ctx != 0; }
/*! Sets the attachments of the framebuffer object to \a attachment. This can be used to free or reattach the depth and stencil buffer attachments as needed. */ void QOpenGLFramebufferObject::setAttachment(QOpenGLFramebufferObject::Attachment attachment) { Q_D(QOpenGLFramebufferObject); if (attachment == d->fbo_attachment || !isValid()) return; QOpenGLContext *current = QOpenGLContext::currentContext(); if (!current) return; #ifdef QT_DEBUG if (current->shareGroup() != d->fbo_guard->group()) qWarning("QOpenGLFramebufferObject::setAttachment() called from incompatible context"); #endif d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo()); d->initAttachments(current, attachment); if (current->d_func()->current_fbo != d->fbo()) d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->d_func()->current_fbo); }
void QOpenGLTextureGlyphCache::createTextureData(int width, int height) { QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); if (ctx == 0) { 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 = 0; } if (!m_textureResource) m_textureResource = new QOpenGLGlyphTexture(ctx); glGenTextures(1, &m_textureResource->m_texture); glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); m_textureResource->m_width = width; m_textureResource->m_height = height; if (m_type == QFontEngineGlyphCache::Raster_RGBMask) { QVarLengthArray<uchar> data(width * height * 4); for (int i = 0; i < data.size(); ++i) data[i] = 0; 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; glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, &data[0]); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); m_filterMode = Nearest; }
/*! \fn bool QOpenGLFramebufferObject::release() Switches rendering back to the default, windowing system provided framebuffer. Returns \c true upon success, false otherwise. \sa bind() */ bool QOpenGLFramebufferObject::release() { if (!isValid()) return false; QOpenGLContext *current = QOpenGLContext::currentContext(); if (!current) return false; Q_D(QOpenGLFramebufferObject); #ifdef QT_DEBUG if (current->shareGroup() != d->fbo_guard->group()) qWarning("QOpenGLFramebufferObject::release() called from incompatible context"); #endif if (current) { current->d_func()->current_fbo = current->defaultFramebufferObject(); d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->d_func()->current_fbo); } return true; }
/*! \fn GLuint QOpenGLFramebufferObject::takeTexture() Returns the texture id for the texture attached to this framebuffer object. The ownership of the texture is transferred to the caller. If the framebuffer object is currently bound, an implicit release() will be done. During the next call to bind() a new texture will be created. If a multisample framebuffer object is used, then there is no texture and the return value from this function will be invalid. Similarly, incomplete framebuffer objects will also return 0. \since 5.3 \sa texture(), bind(), release() */ GLuint QOpenGLFramebufferObject::takeTexture() { Q_D(QOpenGLFramebufferObject); GLuint id = 0; if (isValid() && d->texture_guard) { QOpenGLContext *current = QOpenGLContext::currentContext(); if (current && current->shareGroup() == d->fbo_guard->group() && current->d_func()->current_fbo == d->fbo()) release(); id = d->texture_guard->id(); // Do not call free() on texture_guard, just null it out. // This way the texture will not be deleted when the guard is destroyed. d->texture_guard = 0; } return id; }
/*! \fn bool QOpenGLFramebufferObject::bind() Switches rendering from the default, windowing system provided framebuffer to this framebuffer object. Returns \c true upon success, false otherwise. \note If takeTexture() was called, a new texture is created and associated with the framebuffer object. This is potentially expensive and changes the context state (the currently bound texture). \sa release() */ bool QOpenGLFramebufferObject::bind() { if (!isValid()) return false; Q_D(QOpenGLFramebufferObject); QOpenGLContext *current = QOpenGLContext::currentContext(); if (!current) return false; #ifdef QT_DEBUG if (current->shareGroup() != d->fbo_guard->group()) qWarning("QOpenGLFramebufferObject::bind() called from incompatible context"); #endif d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo()); if (d->texture_guard || d->format.samples() != 0) d->valid = d->checkFramebufferStatus(current); else d->initTexture(d->format.textureTarget(), d->format.internalTextureFormat(), d->size, d->format.mipmap()); if (d->valid && current) current->d_func()->current_fbo = d->fbo(); return d->valid; }
/*! Copies the pbuffer contents into the texture specified with \a texture_id. The texture must be of the same size and format as the pbuffer. Example: \snippet code/src_opengl_qglpixelbuffer.cpp 1 An alternative on Windows and Mac OS X systems that support the \c render_texture extension is to use bindToDynamicTexture() to get dynamic updates of the texture. \sa generateDynamicTexture(), bindToDynamicTexture() */ void QGLPixelBuffer::updateDynamicTexture(GLuint texture_id) const { Q_D(const QGLPixelBuffer); if (d->invalid || !d->fbo) return; QOpenGLContext *ctx = QOpenGLContext::currentContext(); if (!ctx) return; #undef glBindFramebuffer #ifndef GL_READ_FRAMEBUFFER #define GL_READ_FRAMEBUFFER 0x8CA8 #endif #ifndef GL_DRAW_FRAMEBUFFER #define GL_DRAW_FRAMEBUFFER 0x8CA9 #endif QOpenGLExtensions extensions(ctx); if (d->blit_fbo) { QOpenGLFramebufferObject::blitFramebuffer(d->blit_fbo, d->fbo); extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, d->blit_fbo->handle()); } glBindTexture(GL_TEXTURE_2D, texture_id); #ifndef QT_OPENGL_ES glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, d->req_size.width(), d->req_size.height(), 0); #else glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, d->req_size.width(), d->req_size.height(), 0); #endif if (d->blit_fbo) extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, ctx->d_func()->current_fbo); }
void QOpenGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition) { QOpenGLContext *ctx = QOpenGLContext::currentContext(); if (ctx == 0) { qWarning("QOpenGLTextureGlyphCache::fillTexture: Called with no context"); return; } if (ctx->d_func()->workaround_brokenFBOReadBack) { QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition); glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); const QImage &texture = image(); const uchar *bits = texture.constBits(); bits += c.y * texture.bytesPerLine() + c.x; for (int i=0; i<c.h; ++i) { glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, c.w, 1, GL_ALPHA, GL_UNSIGNED_BYTE, bits); bits += texture.bytesPerLine(); } return; } QImage mask = textureMapForGlyph(glyph, subPixelPosition); const int maskWidth = mask.width(); const int maskHeight = mask.height(); if (mask.format() == QImage::Format_Mono) { mask = mask.convertToFormat(QImage::Format_Indexed8); for (int y = 0; y < maskHeight; ++y) { uchar *src = (uchar *) mask.scanLine(y); for (int x = 0; x < maskWidth; ++x) src[x] = -src[x]; // convert 0 and 1 into 0 and 255 } } else if (mask.format() == QImage::Format_RGB32) { // Make the alpha component equal to the average of the RGB values. // This is needed when drawing sub-pixel antialiased text on translucent targets. for (int y = 0; y < maskHeight; ++y) { quint32 *src = (quint32 *) mask.scanLine(y); for (int x = 0; x < maskWidth; ++x) { uchar r = src[x] >> 16; uchar g = src[x] >> 8; uchar b = src[x]; quint32 avg = (quint32(r) + quint32(g) + quint32(b) + 1) / 3; // "+1" for rounding. src[x] = (src[x] & 0x00ffffff) | (avg << 24); } } } glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); if (mask.format() == QImage::Format_RGB32) { #if defined(QT_OPENGL_ES_2) // ###TODO Ensure extension is actually present on ES2 glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_BGRA_EXT, GL_UNSIGNED_BYTE, mask.bits()); #else glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_BGRA, GL_UNSIGNED_BYTE, mask.bits()); #endif } else { // glTexSubImage2D() might cause some garbage to appear in the texture if the mask width is // not a multiple of four bytes. The bug appeared on a computer with 32-bit Windows Vista // and nVidia GeForce 8500GT. GL_UNPACK_ALIGNMENT is set to four bytes, 'mask' has a // multiple of four bytes per line, and most of the glyph shows up correctly in the // texture, which makes me think that this is a driver bug. // One workaround is to make sure the mask width is a multiple of four bytes, for instance // by converting it to a format with four bytes per pixel. Another is to copy one line at a // time. #if 0 if (!ctx->d_func()->workaround_brokenAlphaTexSubImage_init) { // don't know which driver versions exhibit this bug, so be conservative for now const QByteArray versionString(reinterpret_cast<const char*>(glGetString(GL_VERSION))); glctx->d_func()->workaround_brokenAlphaTexSubImage = versionString.indexOf("NVIDIA") >= 0; glctx->d_func()->workaround_brokenAlphaTexSubImage_init = true; } #endif #if 0 if (ctx->d_func()->workaround_brokenAlphaTexSubImage) { for (int i = 0; i < maskHeight; ++i) glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, maskWidth, 1, GL_ALPHA, GL_UNSIGNED_BYTE, mask.scanLine(i)); } else { #endif glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_ALPHA, GL_UNSIGNED_BYTE, mask.bits()); // } } } int QOpenGLTextureGlyphCache::glyphPadding() const { return 1; } int QOpenGLTextureGlyphCache::maxTextureWidth() const { QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); if (ctx == 0) return QImageTextureGlyphCache::maxTextureWidth(); else return ctx->d_func()->maxTextureSize(); }
void QOpenGLTextureGlyphCache::resizeTextureData(int width, int height) { QOpenGLContext *ctx = QOpenGLContext::currentContext(); if (ctx == 0) { qWarning("QOpenGLTextureGlyphCache::resizeTextureData: Called with no context"); return; } 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); Q_ASSERT(image().depth() == 8); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, oldHeight, GL_ALPHA, GL_UNSIGNED_BYTE, image().constBits()); glDeleteTextures(1, &oldTexture); return; } // ### the QTextureGlyphCache API needs to be reworked to allow // ### resizeTextureData to fail QOpenGLFunctions funcs(ctx); funcs.glBindFramebuffer(GL_FRAMEBUFFER, m_textureResource->m_fbo); GLuint tmp_texture; glGenTextures(1, &tmp_texture); glBindTexture(GL_TEXTURE_2D, tmp_texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, oldWidth, oldHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); m_filterMode = Nearest; 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); glBindTexture(GL_TEXTURE_2D, oldTexture); if (pex != 0) pex->transferMode(BrushDrawingMode); glDisable(GL_STENCIL_TEST); glDisable(GL_DEPTH_TEST); glDisable(GL_SCISSOR_TEST); glDisable(GL_BLEND); glViewport(0, 0, oldWidth, oldHeight); QOpenGLShaderProgram *blitProgram = 0; if (pex == 0) { if (m_blitProgram == 0) { m_blitProgram = new QOpenGLShaderProgram(ctx); { QString source; source.append(QLatin1String(qopenglslMainWithTexCoordsVertexShader)); source.append(QLatin1String(qopenglslUntransformedPositionVertexShader)); QOpenGLShader *vertexShader = new QOpenGLShader(QOpenGLShader::Vertex, m_blitProgram); vertexShader->compileSourceCode(source); m_blitProgram->addShader(vertexShader); } { QString source; source.append(QLatin1String(qopenglslMainFragmentShader)); source.append(QLatin1String(qopenglslImageSrcFragmentShader)); QOpenGLShader *fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment, m_blitProgram); fragmentShader->compileSourceCode(source); m_blitProgram->addShader(fragmentShader); } m_blitProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); m_blitProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); m_blitProgram->link(); } funcs.glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_vertexCoordinateArray); funcs.glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_textureCoordinateArray); m_blitProgram->bind(); m_blitProgram->enableAttributeArray(int(QT_VERTEX_COORDS_ATTR)); m_blitProgram->enableAttributeArray(int(QT_TEXTURE_COORDS_ATTR)); m_blitProgram->disableAttributeArray(int(QT_OPACITY_ATTR)); blitProgram = m_blitProgram; } else { pex->setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, m_vertexCoordinateArray); pex->setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, m_textureCoordinateArray); pex->shaderManager->useBlitProgram(); blitProgram = pex->shaderManager->blitProgram(); } blitProgram->setUniformValue("imageTexture", QT_IMAGE_TEXTURE_UNIT); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight); funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); glDeleteTextures(1, &tmp_texture); glDeleteTextures(1, &oldTexture); funcs.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo); if (pex != 0) { glViewport(0, 0, pex->width, pex->height); pex->updateClipScissorTest(); } else { m_blitProgram->disableAttributeArray(int(QT_VERTEX_COORDS_ATTR)); m_blitProgram->disableAttributeArray(int(QT_TEXTURE_COORDS_ATTR)); } }
void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSize &sz, QOpenGLFramebufferObject::Attachment attachment, GLenum texture_target, GLenum internal_format, GLint samples, bool mipmap) { QOpenGLContext *ctx = QOpenGLContext::currentContext(); funcs.initializeOpenGLFunctions(); if (!funcs.hasOpenGLFeature(QOpenGLFunctions::Framebuffers)) return; // Fall back to using a normal non-msaa FBO if we don't have support for MSAA if (!funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample) || !funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit)) { samples = 0; } #ifndef QT_OPENGL_ES_2 GLint maxSamples; funcs.glGetIntegerv(GL_MAX_SAMPLES, &maxSamples); samples = qBound(0, int(samples), int(maxSamples)); #endif size = sz; target = texture_target; // texture dimensions QT_RESET_GLERROR(); // reset error state GLuint fbo = 0; funcs.glGenFramebuffers(1, &fbo); funcs.glBindFramebuffer(GL_FRAMEBUFFER, fbo); GLuint color_buffer = 0; QT_CHECK_GLERROR(); // init texture if (samples == 0) { initTexture(texture_target, internal_format, size, mipmap); } else { mipmap = false; funcs.glGenRenderbuffers(1, &color_buffer); funcs.glBindRenderbuffer(GL_RENDERBUFFER, color_buffer); funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, internal_format, size.width(), size.height()); funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color_buffer); QT_CHECK_GLERROR(); valid = checkFramebufferStatus(ctx); if (valid) { funcs.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); color_buffer_guard = new QOpenGLSharedResourceGuard(ctx, color_buffer, freeRenderbufferFunc); } } format.setTextureTarget(target); format.setSamples(int(samples)); format.setInternalTextureFormat(internal_format); format.setMipmap(mipmap); initAttachments(ctx, attachment); funcs.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo); if (valid) { fbo_guard = new QOpenGLSharedResourceGuard(ctx, fbo, freeFramebufferFunc); } else { if (color_buffer_guard) { color_buffer_guard->free(); color_buffer_guard = 0; } else if (texture_guard) { texture_guard->free(); texture_guard = 0; } funcs.glDeleteFramebuffers(1, &fbo); } QT_CHECK_GLERROR(); }
bool QOpenGLFramebufferObject::isBound() const { Q_D(const QOpenGLFramebufferObject); QOpenGLContext *current = QOpenGLContext::currentContext(); return current ? current->d_func()->current_fbo == d->fbo() : false; }