void FontOverlay::render() { QPainter fontPainter(&image); //draw font fontPainter.setRenderHint(QPainter::Antialiasing, true); fontPainter.setRenderHint(QPainter::TextAntialiasing, true); fontPainter.setRenderHint(QPainter::SmoothPixmapTransform, true); fontPainter.setFont(QFont("Ubuntu", 24)); fontPainter.setLayoutDirection(Qt::RightToLeft); fontPainter.setPen(Qt::white); string text = ""; foreach(string line, textLines) { text += line + "\n"; }
void StelQGLRenderer::drawText(const TextParams& params) { statistics[TEXT_DRAWS] += 1.0; StelQGLTextureBackend* currentTexture = currentlyBoundTextures[0]; if(params.string_.length() == 0) { return; } viewport.enablePainting(); if(currentFontSet) { viewport.setFont(currentFont); } QPainter* painter = viewport.getPainter(); Q_ASSERT_X(NULL != painter, Q_FUNC_INFO, "Trying to draw text but painting is disabled"); QFontMetrics fontMetrics = painter->fontMetrics(); StelProjectorP projector = NULL == params.projector_ ? StelApp::getInstance().getCore()->getProjection2d() : params.projector_; Vec3f win; if(params.doNotProject_) { win = params.position_; } else if(!projector->project(params.position_, win)) { viewport.disablePainting(); return; } const int x = win[0]; const int y = win[1]; // Avoid drawing if outside viewport. // We do a worst-case approximation as getting exact text dimensions is expensive. // We also account for rotation by assuming the worst case in bot X and Y // (culling with a rotating rectangle would be expensive) const int cullDistance = std::max(fontMetrics.height(), params.string_.size() * fontMetrics.maxWidth()); const Vec4i viewXywh = projector->getViewportXywh(); const int viewMinX = viewXywh[0]; const int viewMinY = viewXywh[1]; const int viewMaxX = viewMinX + viewXywh[2]; const int viewMaxY = viewMinY + viewXywh[3]; if(y + cullDistance < viewMinY || y - cullDistance > viewMaxY || x + cullDistance < viewMinX || x - cullDistance > viewMaxX) { viewport.disablePainting(); return; } if(projector->useGravityLabels() && !params.noGravity_) { drawTextGravityHelper(params, *painter, x, y, projector); return; } const int pixelSize = painter->font().pixelSize(); // Strings drawn by drawText() can differ by text, font size, or the font itself. const QByteArray hash = params.string_.toUtf8() + QByteArray::number(pixelSize) + painter->font().family().toUtf8(); StelQGLTextureBackend* textTexture = textTextureCache.object(hash); // No texture in cache for this string, need to draw it. if (NULL == textTexture) { const QRect extents = fontMetrics.boundingRect(params.string_); // Width and height of the text. // Texture width/height is required to be at least equal to this. // // Both X and Y need to be at least 1 so we don't create an empty image // (doesn't work with textures) const int requiredWidth = std::max(1, extents.width() + 1 + static_cast<int>(0.02f * extents.width())); const int requiredHeight = std::max(1, extents.height()); // Create temporary image and render text into it // QImage is used solely to reuse existing QGLTextureBackend constructor // function. QPixmap could be used as well (not sure which is faster, // needs profiling) QImage image = areNonPowerOfTwoTexturesSupported() ? QImage(requiredWidth, requiredHeight, QImage::Format_ARGB32_Premultiplied) : QImage(StelUtils::smallestPowerOfTwoGreaterOrEqualTo(requiredWidth), StelUtils::smallestPowerOfTwoGreaterOrEqualTo(requiredHeight), QImage::Format_ARGB32); image.fill(Qt::transparent); QPainter fontPainter(&image); fontPainter.setFont(painter->font()); fontPainter.setRenderHints(QPainter::TextAntialiasing, true); fontPainter.setPen(Qt::white); // The second argument ensures the text is positioned correctly even if // the image is enlarged to power-of-two. fontPainter.drawText(-extents.x(), image.height() - requiredHeight - extents.y(), params.string_); textTexture = StelQGLTextureBackend::constructFromImage (this, QString(), TextureParams().filtering(TextureFiltering_Linear), image); const QSize size = textTexture->getDimensions(); if(!textTexture->getStatus() == TextureStatus_Loaded) { qWarning() << "Texture error: " << textTexture->getErrorMessage(); Q_ASSERT_X(false, Q_FUNC_INFO, "Failed to construct a text texture"); } textTextureCache.insert(hash, textTexture, 4 * size.width() * size.height()); } // Even if NPOT textures are not supported, we always draw the full rectangle // of the texture. The extra space is fully transparent, so it's not an issue. // Shortcut variables to calculate the rectangle. const QSize size = textTexture->getDimensions(); const float w = size.width(); const float h = size.height(); const float xShift = params.xShift_; const float yShift = params.yShift_; const float angleDegrees = params.angleDegrees_ + (params.noGravity_ ? 0.0f : projector->getDefaultAngleForGravityText()); // Zero out very small angles. // // (this could also be used to optimize the case with zero angled // to avoid sin/cos if needed) const bool angled = std::fabs(angleDegrees) >= 1.0f * M_PI / 180.f; const float cosr = angled ? std::cos(angleDegrees * M_PI / 180.0) : 1.0f; const float sinr = angled ? std::sin(angleDegrees * M_PI / 180.0) : 0.0f; // Corners of the (possibly rotated) texture rectangle. const Vec2f ne(round(x + cosr * xShift - sinr * yShift), round(y + sinr * xShift + cosr * yShift)); const Vec2f nw(round(x + cosr * (w + xShift) - sinr * yShift), round(y + sinr * (w + xShift) + cosr * yShift)); const Vec2f se(round(x + cosr * xShift - sinr * (h + yShift)), round(y + sinr * xShift + cosr * (h + yShift))); const Vec2f sw(round(x + cosr * (w + xShift) - sinr * (h + yShift)), round(y + sinr * (w + xShift) + cosr * (h + yShift))); // Construct the text vertex buffer if it doesn't exist yet, otherwise clear it. if(NULL == textBuffer) { textBuffer = createVertexBuffer<TexturedVertex>(PrimitiveType_TriangleStrip); } else { textBuffer->unlock(); textBuffer->clear(); } textBuffer->addVertex(TexturedVertex(ne, Vec2f(0.0f, 0.0f))); textBuffer->addVertex(TexturedVertex(nw, Vec2f(1.0f, 0.0f))); textBuffer->addVertex(TexturedVertex(se, Vec2f(0.0f, 1.0f))); textBuffer->addVertex(TexturedVertex(sw, Vec2f(1.0f, 1.0f))); textBuffer->lock(); // Draw. const BlendMode oldBlendMode = blendMode; setBlendMode(BlendMode_Alpha); textTexture->bind(0); drawVertexBuffer(textBuffer); setBlendMode(oldBlendMode); // Reset user-bound texture. if(NULL != currentTexture) { currentTexture->bind(0); } viewport.disablePainting(); }