📄 qpaintengine_opengl.cpp
字号:
qreal min_y; QDataBuffer<QPointF> tess_points; QVector<int> tess_points_stops; QImage pattern_image; GLdouble projection_matrix[4][4]; QList<QDrawQueueItem> drawQueue; GLuint drawable_texture; QSize drawable_texture_size; QGLPrivateCleanup ref_cleaner; friend class QGLMaskTextureCache;};void QGLPrivateCleanup::cleanupGLContextRefs(const QGLContext *context){ p->cleanupGLContextRefs(context);}static inline QPainterPath strokeForPath(const QPainterPath &path, const QPen &cpen) { QPainterPathStroker stroker; if (cpen.style() == Qt::CustomDashLine) stroker.setDashPattern(cpen.dashPattern()); else stroker.setDashPattern(cpen.style()); stroker.setCapStyle(cpen.capStyle()); stroker.setJoinStyle(cpen.joinStyle()); stroker.setMiterLimit(cpen.miterLimit()); qreal width = cpen.widthF(); if (width == 0) stroker.setWidth(1); else stroker.setWidth(width); QPainterPath stroke = stroker.createStroke(path); stroke.setFillRule(Qt::WindingFill); return stroke;}class QGLStrokeCache{ struct CacheInfo { inline CacheInfo(QPainterPath p, QPainterPath sp, QPen stroke_pen) : path(p), stroked_path(sp), pen(stroke_pen) {} QPainterPath path; QPainterPath stroked_path; QPen pen; }; typedef QMultiHash<quint64, CacheInfo> QGLStrokeTableHash;public: inline QPainterPath getStrokedPath(const QPainterPath &path, const QPen &pen) { quint64 hash_val = 0; for (int i = 0; i < path.elementCount() && i <= 2; i++) { hash_val += quint64(path.elementAt(i).x); hash_val += quint64(path.elementAt(i).y); } QGLStrokeTableHash::const_iterator it = cache.constFind(hash_val); if (it == cache.constEnd()) return addCacheElement(hash_val, path, pen); else { do { const CacheInfo &cache_info = it.value(); if (cache_info.path == path && cache_info.pen == pen) return cache_info.stroked_path; ++it; } while (it != cache.constEnd() && it.key() == hash_val); // an exact match for this path was not found, create new cache element return addCacheElement(hash_val, path, pen); } }protected: inline int maxCacheSize() const { return 500; } QPainterPath addCacheElement(quint64 hash_val, QPainterPath path, const QPen &pen) { if (cache.size() == maxCacheSize()) { int elem_to_remove = qrand() % maxCacheSize(); cache.remove(cache.keys()[elem_to_remove]); // may remove more than 1, but OK } QPainterPath stroke = strokeForPath(path, pen); CacheInfo cache_entry(path, stroke, pen); return cache.insert(hash_val, cache_entry).value().stroked_path; } QGLStrokeTableHash cache;};Q_GLOBAL_STATIC(QGLStrokeCache, qt_opengl_stroke_cache)class QGLGradientCache : public QObject{ Q_OBJECT struct CacheInfo { inline CacheInfo(QGradientStops s, qreal op) : stops(s), opacity(op) {} GLuint texId; QGradientStops stops; qreal opacity; }; typedef QMultiHash<quint64, CacheInfo> QGLGradientColorTableHash;public: QGLGradientCache() : QObject(), buffer_ctx(0) { connect(QGLProxy::signalProxy(), SIGNAL(aboutToDestroyContext(const QGLContext *)), SLOT(cleanupGLContextRefs(const QGLContext *))); } inline GLuint getBuffer(const QGradientStops &stops, qreal opacity, QGLContext *ctx) { if (buffer_ctx && !qgl_share_reg()->checkSharing(buffer_ctx, ctx)) cleanCache(); buffer_ctx = ctx; quint64 hash_val = 0; for (int i = 0; i < stops.size() && i <= 2; i++) hash_val += stops[i].second.rgba(); QGLGradientColorTableHash::const_iterator it = cache.constFind(hash_val); if (it == cache.constEnd()) return addCacheElement(hash_val, stops, opacity); else { do { const CacheInfo &cache_info = it.value(); if (cache_info.stops == stops && cache_info.opacity == opacity) { 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 cache return addCacheElement(hash_val, stops, opacity); } } inline int paletteSize() const { return 1024; }protected: inline int maxCacheSize() const { return 60; } inline void generateGradientColorTable(const QGradientStops& s, uint *colorTable, int size, qreal opacity) const; GLuint addCacheElement(quint64 hash_val, const QGradientStops &stops, qreal opacity) { if (cache.size() == maxCacheSize()) { int elem_to_remove = qrand() % maxCacheSize(); quint64 key = cache.keys()[elem_to_remove]; // need to call glDeleteTextures on each removed cache entry: QGLGradientColorTableHash::const_iterator it = cache.constFind(key); do { 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(stops, opacity); uint buffer[1024]; generateGradientColorTable(stops, buffer, paletteSize(), opacity); glGenTextures(1, &cache_entry.texId);#ifndef Q_WS_QWS glBindTexture(GL_TEXTURE_1D, cache_entry.texId); glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, paletteSize(), 0, GL_BGRA, GL_UNSIGNED_BYTE, buffer);#else // create 2D one-line texture instead. This requires an impl of manual GL_TEXGEN for all primitives#endif return cache.insert(hash_val, cache_entry).value().texId; } void cleanCache() { QGLGradientColorTableHash::const_iterator it = cache.constBegin(); for (; it != cache.constEnd(); ++it) { const CacheInfo &cache_info = it.value(); glDeleteTextures(1, &cache_info.texId); } cache.clear(); } QGLGradientColorTableHash cache; QGLContext *buffer_ctx;public Q_SLOTS: void cleanupGLContextRefs(const QGLContext *context) { if (context == buffer_ctx) { cleanCache(); buffer_ctx = 0; } }};void QGLGradientCache::generateGradientColorTable(const QGradientStops& s, uint *colorTable, int size, qreal opacity) const{ int pos = 0; qreal fpos = 0.0; qreal incr = 1.0 / qreal(size); QVector<uint> colors(s.size()); for (int i = 0; i < s.size(); ++i) colors[i] = s[i].second.rgba(); uint alpha = qRound(opacity * 256); while (fpos < s.first().first) { colorTable[pos] = PREMUL(ARGB_COMBINE_ALPHA(colors[0], alpha)); pos++; fpos += incr; } for (int i = 0; i < s.size() - 1; ++i) { qreal delta = 1/(s[i+1].first - s[i].first); while (fpos < s[i+1].first && pos < size) { int dist = int(256 * ((fpos - s[i].first) * delta)); int idist = 256 - dist; uint current_color = PREMUL(ARGB_COMBINE_ALPHA(colors[i], alpha)); uint next_color = PREMUL(ARGB_COMBINE_ALPHA(colors[i+1], alpha));#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);#else uint c = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist); colorTable[pos] = ( (c << 24) & 0xff000000) | ((c >> 24) & 0x000000ff) | ((c << 8) & 0x00ff0000) | ((c >> 8) & 0x0000ff00);#endif // Q_BYTE_ORDER ++pos; fpos += incr; } } for (;pos < size; ++pos) colorTable[pos] = colors[s.size() - 1];}#ifndef Q_WS_QWSQ_GLOBAL_STATIC(QGLGradientCache, qt_opengl_gradient_cache)#endifvoid QOpenGLPaintEnginePrivate::createGradientPaletteTexture(const QGradient& g){#ifdef Q_WS_QWS //### Q_UNUSED(g);#else GLuint texId = qt_opengl_gradient_cache()->getBuffer(g.stops(), opacity, drawable.context()); glBindTexture(GL_TEXTURE_1D, texId); grad_palette = texId; if (g.spread() == QGradient::RepeatSpread || g.type() == QGradient::ConicalGradient) glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); else if (g.spread() == QGradient::ReflectSpread) glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT_IBM); else glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);#endif}inline void QOpenGLPaintEnginePrivate::setGradientOps(const QBrush &brush, const QRectF &bounds){ current_style = brush.style(); if (current_style < Qt::LinearGradientPattern || current_style > Qt::ConicalGradientPattern) { setGLBrush(brush.color()); qt_glColor4ubv(brush_color); } updateGradient(brush, bounds);#ifndef Q_WS_QWS //### GLES does not have GL_TEXTURE_GEN_ so we are falling back for gradients glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_1D); if (current_style == Qt::LinearGradientPattern) { if (high_quality_antialiasing || !has_fast_composition_mode) { fragment_brush = FRAGMENT_PROGRAM_BRUSH_LINEAR; } else { glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_1D); } } else { if (use_fragment_programs) { if (current_style == Qt::RadialGradientPattern) fragment_brush = FRAGMENT_PROGRAM_BRUSH_RADIAL; else if (current_style == Qt::ConicalGradientPattern) fragment_brush = FRAGMENT_PROGRAM_BRUSH_CONICAL; else if (current_style == Qt::SolidPattern) fragment_brush = FRAGMENT_PROGRAM_BRUSH_SOLID; else if (current_style == Qt::TexturePattern) fragment_brush = FRAGMENT_PROGRAM_BRUSH_TEXTURE; else fragment_brush = FRAGMENT_PROGRAM_BRUSH_PATTERN; } }#endif}QOpenGLPaintEngine::QOpenGLPaintEngine() : QPaintEngine(*(new QOpenGLPaintEnginePrivate), PaintEngineFeatures(AllFeatures & ~(LinearGradientFill | RadialGradientFill | ConicalGradientFill | PatternBrush | BlendModes))){}QOpenGLPaintEngine::~QOpenGLPaintEngine(){}#ifndef Q_WS_QWSstatic bool qt_createFragmentProgram(QGLContext *ctx, GLuint &program, const char *shader_src){#ifndef Q_WS_WIN Q_UNUSED(ctx);#endif glGenProgramsARB(1, &program); glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, program); const GLbyte *gl_shader_src = reinterpret_cast<const GLbyte *>(shader_src); /* MSVC.NET 2002 */ glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, int(strlen(shader_src)), gl_shader_src); return glGetError() == GL_NO_ERROR;}#endif // !Q_WS_QWSbool QOpenGLPaintEngine::begin(QPaintDevice *pdev){ Q_D(QOpenGLPaintEngine); d->drawable.setDevice(pdev); d->offscreen.setDevice(pdev); d->has_clipping = false; d->has_fast_pen = false; d->inverseScale = 1; d->opacity = 1; d->drawable.makeCurrent(); d->matrix = QTransform(); bool has_frag_program = (QGLExtensions::glExtensions & QGLExtensions::FragmentProgram) && (pdev->devType() != QInternal::Pixmap); QGLContext *ctx = const_cast<QGLContext *>(d->drawable.context()); if (has_frag_program) has_frag_program = qt_resolve_frag_program_extensions(ctx); d->use_stencil_method = d->drawable.format().stencil() && QGLExtensions::glExtensions & QGLExtensions::StencilWrap; if (d->use_stencil_method && QGLExtensions::glExtensions & QGLExtensions::StencilTwoSide) d->has_stencil_face_ext = qt_resolve_stencil_face_extension(ctx); if (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_3) qt_resolve_version_1_3_functions(ctx);#ifndef Q_WS_QWS glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); glPushAttrib(GL_ALL_ATTRIB_BITS); glGetDoublev(GL_PROJECTION_MATRIX, &d->projection_matrix[0][0]);#endif glMatrixMode(GL_MODELVIEW); glPushMatrix(); glMatrixMode(GL_TEXTURE); glPushMatrix(); glLoadIdentity(); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_EDGE_FLAG_ARRAY); glDisableClientState(GL_INDEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); if (QGLExtensions::glExtensions & QGLExtensions::SampleBuffers) glDisable(GL_MULTISAMPLE);#ifndef Q_WS_QWS glDisable(GL_TEXTURE_1D);#endif glDisable(GL_TEXTURE_2D); if (QGLExtensions::glExtensions & QGLExtensions::TextureRectangle) glDisable(GL_TEXTURE_RECTANGLE_NV); glDisable(GL_DEPTH_TEST); glDisable(GL_STENCIL_TEST); glDisable(GL_CULL_FACE); glDisable(GL_LIGHTING); glShadeModel(GL_FLAT); if (d->use_stencil_method) { glStencilFunc(GL_ALWAYS, 0, ~0); glClearStencil(0); } glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); d->offscreen.begin(); const QColor &c = d->drawable.backgroundColor(); glClearColor(c.redF(), c.greenF(), c.blueF(), 1.0); if (d->drawable.context()->d_func()->clear_on_painter_begin && d->drawable.autoFillBackground()) { GLbitfield clearBits = GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;#ifndef Q_WS_QWS clearBits |= GL_ACCUM_BUFFER_BIT;#endif glClear(clearBits); } else if (d->use_stencil_method) { glClear(GL_STENCIL_BUFFER_BIT); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -