/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc ** All rights reserved. ** For any questions to Digia, please use contact form at http://qt.digia.com ** ** This file is part of the QtDataVisualization module. ** ** Licensees holding valid Qt Enterprise licenses may use this file in ** accordance with the Qt Enterprise License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. ** ** If you have questions regarding the use of this file, please use ** contact form at http://qt.digia.com ** ****************************************************************************/ #include "utils_p.h" #include "qutils.h" #include #include #include QT_BEGIN_NAMESPACE_DATAVISUALIZATION #define NUM_IN_POWER(y, x) for (;y> 1; int diffToFit = (valueStrWidth + prePadding) - testWidth; int maxSqueeze = int((valueStrWidth + prePadding) * 0.25f); if (diffToFit < maxSqueeze && maxTextureSize > GLint(testWidth)) targetWidth = testWidth; } bool sizeOk = false; int currentFontSize = textureFontSize; do { if (Utils::isOpenGLES()) { // ES2 can't handle textures with dimensions not in power of 2. Resize labels accordingly. // Add some padding before converting to power of two to avoid too tight fit labelSize = QSize(valueStrWidth + prePadding, valueStrHeight + prePadding); labelSize.setWidth(getNearestPowerOfTwo(labelSize.width())); labelSize.setHeight(getNearestPowerOfTwo(labelSize.height())); } else { if (!labelBackground) labelSize = QSize(valueStrWidth, valueStrHeight); else labelSize = QSize(valueStrWidth + paddingWidth * 2, valueStrHeight + paddingHeight * 2); } if (!maxTextureSize || (labelSize.width() <= maxTextureSize && (labelSize.width() <= targetWidth || !Utils::isOpenGLES()))) { // Make sure the label is not too wide sizeOk = true; } else if (--currentFontSize == 4) { qCritical() << "Label" << text << "is too long to be generated."; return QImage(); } else { fontRatio = (qreal)currentFontSize / (qreal)textureFontSize; // Reduce font size and try again valueFont.setPointSize(currentFontSize); QFontMetrics currentValueFM(valueFont); if (maxLabelWidth && (labelBackground || Utils::isOpenGLES())) valueStrWidth = maxLabelWidth * fontRatio; else valueStrWidth = currentValueFM.width(text); valueStrHeight = currentValueFM.height(); valueStrWidth += paddingWidth / 2; } } while (!sizeOk); // Create image QImage image = QImage(labelSize, QImage::Format_ARGB32); image.fill(Qt::transparent); // Init painter QPainter painter(&image); // Paint text painter.setRenderHint(QPainter::Antialiasing, true); painter.setCompositionMode(QPainter::CompositionMode_Source); painter.setFont(valueFont); if (!labelBackground) { painter.setPen(txtColor); if (Utils::isOpenGLES()) { painter.drawText((labelSize.width() - valueStrWidth) / 2.0f, (labelSize.height() - valueStrHeight) / 2.0f, valueStrWidth, valueStrHeight, Qt::AlignCenter | Qt::AlignVCenter, text); } else { painter.drawText(0, 0, valueStrWidth, valueStrHeight, Qt::AlignCenter | Qt::AlignVCenter, text); } } else { painter.setBrush(QBrush(bgrColor)); qreal radius = 10.0 * fontRatio; if (borders) { painter.setPen(QPen(QBrush(txtColor), 5.0 * fontRatio, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin)); painter.drawRoundedRect(5, 5, labelSize.width() - 10, labelSize.height() - 10, radius, radius); } else { painter.setPen(bgrColor); painter.drawRoundedRect(0, 0, labelSize.width(), labelSize.height(), radius, radius); } painter.setPen(txtColor); painter.drawText((labelSize.width() - valueStrWidth) / 2.0f, (labelSize.height() - valueStrHeight) / 2.0f, valueStrWidth, valueStrHeight, Qt::AlignCenter | Qt::AlignVCenter, text); } return image; } QVector4D Utils::getSelection(QPoint mousepos, int height) { // This is the only one that works with OpenGL ES 2.0, so we're forced to use it // Item count will be limited to 256*256*256 GLubyte pixel[4] = {255, 255, 255, 255}; QOpenGLContext::currentContext()->functions()->glReadPixels(mousepos.x(), height - mousepos.y(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void *)pixel); QVector4D selectedColor(pixel[0], pixel[1], pixel[2], pixel[3]); return selectedColor; } QImage Utils::getGradientImage(QLinearGradient &gradient) { QImage image(QSize(gradientTextureWidth, gradientTextureHeight), QImage::Format_RGB32); gradient.setFinalStop(qreal(gradientTextureWidth), qreal(gradientTextureHeight)); gradient.setStart(0.0, 0.0); QPainter pmp(&image); pmp.setBrush(QBrush(gradient)); pmp.setPen(Qt::NoPen); pmp.drawRect(0, 0, int(gradientTextureWidth), int(gradientTextureHeight)); return image; } Utils::ParamType Utils::preParseFormat(const QString &format, QString &preStr, QString &postStr, int &precision, char &formatSpec) { static QRegExp formatMatcher(QStringLiteral("^([^%]*)%([\\-\\+#\\s\\d\\.lhjztL]*)([dicuoxfegXFEG])(.*)$")); static QRegExp precisionMatcher(QStringLiteral("\\.(\\d+)")); Utils::ParamType retVal; if (formatMatcher.indexIn(format, 0) != -1) { preStr = formatMatcher.cap(1); // Six and 'g' are defaults in Qt API precision = 6; if (!formatMatcher.cap(2).isEmpty()) { if (precisionMatcher.indexIn(formatMatcher.cap(2), 0) != -1) precision = precisionMatcher.cap(1).toInt(); } if (formatMatcher.cap(3).isEmpty()) formatSpec = 'g'; else formatSpec = formatMatcher.cap(3).at(0).toLatin1(); postStr = formatMatcher.cap(4); retVal = mapFormatCharToParamType(formatSpec); } else { retVal = ParamTypeUnknown; // The out parameters are irrelevant in unknown case } return retVal; } Utils::ParamType Utils::mapFormatCharToParamType(char formatSpec) { ParamType retVal = ParamTypeUnknown; if (formatSpec == 'd' || formatSpec == 'i' || formatSpec == 'c') { retVal = ParamTypeInt; } else if (formatSpec == 'u' || formatSpec == 'o' || formatSpec == 'x'|| formatSpec == 'X') { retVal = ParamTypeUInt; } else if (formatSpec == 'f' || formatSpec == 'F' || formatSpec == 'e' || formatSpec == 'E' || formatSpec == 'g' || formatSpec == 'G') { retVal = ParamTypeReal; } return retVal; } QString Utils::formatLabelSprintf(const QByteArray &format, Utils::ParamType paramType, qreal value) { switch (paramType) { case ParamTypeInt: return QString().sprintf(format, (qint64)value); case ParamTypeUInt: return QString().sprintf(format, (quint64)value); case ParamTypeReal: return QString().sprintf(format, value); default: // Return format string to detect errors. Bars selection label logic also depends on this. return QString::fromUtf8(format); } } QString Utils::formatLabelLocalized(Utils::ParamType paramType, qreal value, const QLocale &locale, const QString &preStr, const QString &postStr, int precision, char formatSpec, const QByteArray &format) { switch (paramType) { case ParamTypeInt: case ParamTypeUInt: return preStr + locale.toString(qint64(value)) + postStr; case ParamTypeReal: return preStr + locale.toString(value, formatSpec, precision) + postStr; default: // Return format string to detect errors. Bars selection label logic also depends on this. return QString::fromUtf8(format); } } QString Utils::defaultLabelFormat() { static const QString defaultFormat(QStringLiteral("%.2f")); return defaultFormat; } float Utils::wrapValue(float value, float min, float max) { if (value > max) { value = min + (value - max); // In case single wrap fails, jump to opposite end. if (value > max) value = min; } if (value < min) { value = max + (value - min); // In case single wrap fails, jump to opposite end. if (value < min) value = max; } return value; } QQuaternion Utils::calculateRotation(const QVector3D &xyzRotations) { QQuaternion rotQuatX = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, xyzRotations.x()); QQuaternion rotQuatY = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, xyzRotations.y()); QQuaternion rotQuatZ = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, xyzRotations.z()); QQuaternion totalRotation = rotQuatY * rotQuatZ * rotQuatX; return totalRotation; } bool Utils::isOpenGLES() { if (!staticsResolved) resolveStatics(); return isES; } void Utils::resolveStatics() { QOpenGLContext *ctx = QOpenGLContext::currentContext(); QOffscreenSurface *dummySurface = 0; if (!ctx) { QSurfaceFormat surfaceFormat; dummySurface = new QOffscreenSurface(); dummySurface->setFormat(surfaceFormat); dummySurface->create(); ctx = new QOpenGLContext; ctx->setFormat(surfaceFormat); ctx->create(); ctx->makeCurrent(dummySurface); } #if defined(QT_OPENGL_ES_2) isES = true; #elif (QT_VERSION < QT_VERSION_CHECK(5, 3, 0)) isES = false; #else isES = ctx->isOpenGLES(); #endif ctx->functions()->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); if (dummySurface) { ctx->doneCurrent(); delete ctx; delete dummySurface; } #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) // We support only ES2 emulation with software renderer for now if (QCoreApplication::testAttribute(Qt::AA_UseSoftwareOpenGL)) { qWarning("Only OpenGL ES2 emulation is available for software rendering."); isES = true; } #endif staticsResolved = true; } QT_END_NAMESPACE_DATAVISUALIZATION