/** ****************************************************************************** * * @file cachedsvgitem.h * @author Dmytro Poplavskiy Copyright (C) 2011. * @{ * @brief OpenGL texture cached SVG item *****************************************************************************/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "cachedsvgitem.h" #include #ifndef GL_CLAMP_TO_EDGE #define GL_CLAMP_TO_EDGE 0x812F #endif CachedSvgItem::CachedSvgItem(QGraphicsItem *parent) : QGraphicsSvgItem(parent), m_context(0), m_texture(0), m_scale(1.0) { setCacheMode(NoCache); } CachedSvgItem::CachedSvgItem(const QString & fileName, QGraphicsItem *parent) : QGraphicsSvgItem(fileName, parent), m_context(0), m_texture(0), m_scale(1.0) { setCacheMode(NoCache); } CachedSvgItem::~CachedSvgItem() { if (m_context && m_texture) { m_context->makeCurrent(); glDeleteTextures(1, &m_texture); } } void CachedSvgItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { if (painter->paintEngine()->type() != QPaintEngine::OpenGL && painter->paintEngine()->type() != QPaintEngine::OpenGL2) { // Fallback to direct painting QGraphicsSvgItem::paint(painter, option, widget); return; } QRectF br = boundingRect(); QTransform transform = painter->worldTransform(); qreal sceneScale = transform.map(QLineF(0, 0, 1, 0)).length(); bool stencilTestEnabled = glIsEnabled(GL_STENCIL_TEST); bool scissorTestEnabled = glIsEnabled(GL_SCISSOR_TEST); painter->beginNativePainting(); if (stencilTestEnabled) { glEnable(GL_STENCIL_TEST); } if (scissorTestEnabled) { glEnable(GL_SCISSOR_TEST); } bool dirty = false; if (!m_texture) { glGenTextures(1, &m_texture); m_context = const_cast(QGLContext::currentContext()); dirty = true; } if (!qFuzzyCompare(sceneScale, m_scale)) { m_scale = sceneScale; dirty = true; } int textureWidth = (int(br.width() * m_scale) + 3) & ~3; int textureHeight = (int(br.height() * m_scale) + 3) & ~3; if (dirty) { // qDebug() << "re-render image"; QImage img(textureWidth, textureHeight, QImage::Format_ARGB32); { img.fill(Qt::transparent); QPainter p; p.begin(&img); p.setRenderHints(painter->renderHints()); p.translate(br.topLeft()); p.scale(m_scale, m_scale); QGraphicsSvgItem::paint(&p, option, 0); p.end(); img = img.rgbSwapped(); } glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, m_texture); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glDisable(GL_TEXTURE_2D); dirty = false; } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, m_texture); // texture may be slightly large than svn image, ensure only used area is rendered qreal tw = br.width() * m_scale / textureWidth; qreal th = br.height() * m_scale / textureHeight; glBegin(GL_QUADS); glTexCoord2d(0, 0); glVertex3d(br.left(), br.top(), -1); glTexCoord2d(tw, 0); glVertex3d(br.right(), br.top(), -1); glTexCoord2d(tw, th); glVertex3d(br.right(), br.bottom(), -1); glTexCoord2d(0, th); glVertex3d(br.left(), br.bottom(), -1); glEnd(); glDisable(GL_TEXTURE_2D); painter->endNativePainting(); }