krita

kis_opengl_image_context.cc

00001 /*
00002  *  Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com>
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 #ifdef HAVE_CONFIG_H
00020 #include <config.h>
00021 #endif
00022 
00023 #ifdef HAVE_GL
00024 
00025 #include <kdebug.h>
00026 #include <ksharedptr.h>
00027 
00028 #include "kis_global.h"
00029 #include "kis_meta_registry.h"
00030 #include "kis_colorspace_factory_registry.h"
00031 #include "kis_image.h"
00032 #include "kis_layer.h"
00033 #include "kis_selection.h"
00034 #include "kis_background.h"
00035 #include "kis_opengl_canvas.h"
00036 #include "kis_opengl_image_context.h"
00037 
00038 using namespace std;
00039 
00040 QGLWidget *KisOpenGLImageContext::SharedContextWidget = 0;
00041 int KisOpenGLImageContext::SharedContextWidgetRefCount = 0;
00042 
00043 KisOpenGLImageContext::ImageContextMap KisOpenGLImageContext::imageContextMap;
00044 
00045 KisOpenGLImageContext::KisOpenGLImageContext()
00046 {
00047     m_image = 0;
00048     m_monitorProfile = 0;
00049     m_exposure = 0;
00050 }
00051 
00052 KisOpenGLImageContext::~KisOpenGLImageContext()
00053 {
00054     kdDebug(41001) << "Destroyed KisOpenGLImageContext\n";
00055 
00056     --SharedContextWidgetRefCount;
00057     kdDebug(41001) << "Shared context widget ref count now " << SharedContextWidgetRefCount << endl;
00058 
00059     if (SharedContextWidgetRefCount == 0) {
00060 
00061         kdDebug(41001) << "Deleting shared context widget\n";
00062         delete SharedContextWidget;
00063         SharedContextWidget = 0;
00064     }
00065 
00066     imageContextMap.erase(m_image);
00067 }
00068 
00069 KisOpenGLImageContext::KisOpenGLImageContext(KisImageSP image, KisProfile *monitorProfile)
00070 {
00071     kdDebug(41001) << "Created KisOpenGLImageContext\n";
00072 
00073     m_image = image;
00074     m_monitorProfile = monitorProfile;
00075     m_exposure = 0;
00076     m_displaySelection = true;
00077 
00078     if (SharedContextWidget == 0) {
00079         kdDebug(41001) << "Creating shared context widget\n";
00080 
00081         SharedContextWidget = new QGLWidget(KisOpenGLCanvasFormat);
00082     }
00083 
00084     ++SharedContextWidgetRefCount;
00085 
00086     kdDebug(41001) << "Shared context widget ref count now " << SharedContextWidgetRefCount << endl;
00087 
00088     SharedContextWidget->makeCurrent();
00089     glGenTextures(1, &m_backgroundTexture);
00090     generateBackgroundTexture();
00091 
00092     GLint max_texture_size;
00093 
00094     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
00095 
00096     m_imageTextureTileWidth = QMIN(PREFERRED_IMAGE_TEXTURE_WIDTH, max_texture_size);
00097     m_imageTextureTileHeight = QMIN(PREFERRED_IMAGE_TEXTURE_HEIGHT, max_texture_size);
00098 
00099     createImageTextureTiles();
00100 
00101     connect(m_image, SIGNAL(sigImageUpdated(QRect)),
00102             SLOT(slotImageUpdated(QRect)));
00103     connect(m_image, SIGNAL(sigSizeChanged(Q_INT32, Q_INT32)),
00104             SLOT(slotImageSizeChanged(Q_INT32, Q_INT32)));
00105 
00106     updateImageTextureTiles(m_image->bounds());
00107 }
00108 
00109 KisOpenGLImageContextSP KisOpenGLImageContext::getImageContext(KisImageSP image, KisProfile *monitorProfile)
00110 {
00111     if (imageCanShareImageContext(image)) {
00112         ImageContextMap::iterator it = imageContextMap.find(image);
00113 
00114         if (it != imageContextMap.end()) {
00115 
00116             kdDebug(41001) << "Sharing image context from map\n";
00117 
00118             KisOpenGLImageContextSP context = (*it).second;
00119             context->setMonitorProfile(monitorProfile);
00120 
00121             return context;
00122         } else {
00123             KisOpenGLImageContext *imageContext = new KisOpenGLImageContext(image, monitorProfile);
00124             imageContextMap[image] = imageContext;
00125 
00126             kdDebug(41001) << "Added shareable context to map\n";
00127 
00128             return imageContext;
00129         }
00130     } else {
00131         kdDebug(41001) << "Creating non-shareable image context\n";
00132 
00133         return new KisOpenGLImageContext(image, monitorProfile);
00134     }
00135 }
00136 
00137 bool KisOpenGLImageContext::imageCanShareImageContext(KisImageSP image)
00138 {
00139     if (image->colorSpace()->hasHighDynamicRange()) {
00140         //XXX: and we don't have shaders...
00141         return false;
00142     } else {
00143         return true;
00144     }
00145 }
00146 
00147 QGLWidget *KisOpenGLImageContext::sharedContextWidget() const
00148 {
00149     return SharedContextWidget;
00150 }
00151 
00152 void KisOpenGLImageContext::updateImageTextureTiles(const QRect& rect)
00153 {
00154     //kdDebug() << "updateImageTextureTiles " << rect << endl;
00155 
00156     QRect updateRect = rect & m_image->bounds();
00157 
00158     if (!updateRect.isEmpty()) {
00159 
00160         SharedContextWidget->makeCurrent();
00161 
00162         int firstColumn = updateRect.left() / m_imageTextureTileWidth;
00163         int lastColumn = updateRect.right() / m_imageTextureTileWidth;
00164         int firstRow = updateRect.top() / m_imageTextureTileHeight;
00165         int lastRow = updateRect.bottom() / m_imageTextureTileHeight;
00166 
00167         for (int column = firstColumn; column <= lastColumn; column++) {
00168             for (int row = firstRow; row <= lastRow; row++) {
00169 
00170                 QRect tileRect(column * m_imageTextureTileWidth, row * m_imageTextureTileHeight,
00171                                m_imageTextureTileWidth, m_imageTextureTileHeight);
00172 
00173                 QRect tileUpdateRect = tileRect & updateRect;
00174 
00175                 glBindTexture(GL_TEXTURE_2D, imageTextureTile(tileRect.x(), tileRect.y()));
00176                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
00177                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);//GL_LINEAR);
00178                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
00179                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
00180 
00181                 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
00182 
00183                 QImage tileUpdateImage = m_image->convertToQImage(tileUpdateRect.left(), tileUpdateRect.top(),
00184                                                                     tileUpdateRect.right(), tileUpdateRect.bottom(),
00185                                                                      m_monitorProfile, m_exposure);
00186 
00187                 if (m_displaySelection) {
00188                     if (m_image->activeLayer() != 0) {
00189                         m_image->activeLayer()->paintSelection(tileUpdateImage,
00190                                                                    tileUpdateRect.x(), tileUpdateRect.y(),
00191                                                                    tileUpdateRect.width(), tileUpdateRect.height());
00192                     }
00193                 }
00194 
00195                 if (tileUpdateRect.width() == m_imageTextureTileWidth && tileUpdateRect.height() == m_imageTextureTileHeight) {
00196 
00197                     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_imageTextureTileWidth, m_imageTextureTileHeight, 0,
00198                           GL_BGRA, GL_UNSIGNED_BYTE, tileUpdateImage.bits());
00199                 } else {
00200                     int xOffset = tileUpdateRect.x() - tileRect.x();
00201                     int yOffset = tileUpdateRect.y() - tileRect.y();
00202 
00203                     glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, tileUpdateRect.width(), tileUpdateRect.height(),
00204                                     GL_BGRA, GL_UNSIGNED_BYTE, tileUpdateImage.bits());
00205                 }
00206 
00207                 GLenum error = glGetError ();
00208 
00209                 if (error != GL_NO_ERROR)
00210                 {
00211                     kdDebug(41001) << "Error loading texture: " << endl;
00212                 }
00213             }
00214         }
00215     }
00216 }
00217 
00218 KisColorSpace* KisOpenGLImageContext::textureColorSpaceForImageColorSpace(KisColorSpace */*imageColorSpace*/)
00219 {
00220     return KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("RGBA", ""), "");
00221 }
00222 
00223 void KisOpenGLImageContext::setMonitorProfile(KisProfile *monitorProfile)
00224 {
00225     if (monitorProfile != m_monitorProfile) {
00226         m_monitorProfile = monitorProfile;
00227         generateBackgroundTexture();
00228         updateImageTextureTiles(m_image->bounds());
00229     }
00230 }
00231 
00232 void KisOpenGLImageContext::setHDRExposure(float exposure)
00233 {
00234     if (exposure != m_exposure) {
00235         m_exposure = exposure;
00236 
00237         if (m_image->colorSpace()->hasHighDynamicRange()) {
00238             //XXX: and we are not using shaders...
00239             updateImageTextureTiles(m_image->bounds());
00240         }
00241     }
00242 }
00243 
00244 void KisOpenGLImageContext::generateBackgroundTexture()
00245 {
00246     SharedContextWidget->makeCurrent();
00247 
00248     glBindTexture(GL_TEXTURE_2D, m_backgroundTexture);
00249 
00250     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
00251     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
00252     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
00253     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
00254 
00255     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
00256 
00257     QImage backgroundImage = m_image->background()->patternTile();
00258 
00259     // XXX: temp.
00260     Q_ASSERT(backgroundImage.width() == BACKGROUND_TEXTURE_WIDTH);
00261     Q_ASSERT(backgroundImage.height() == BACKGROUND_TEXTURE_HEIGHT);
00262 
00263     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, BACKGROUND_TEXTURE_WIDTH, BACKGROUND_TEXTURE_HEIGHT, 0,
00264           GL_BGRA, GL_UNSIGNED_BYTE, backgroundImage.bits());
00265 }
00266 
00267 GLuint KisOpenGLImageContext::backgroundTexture() const
00268 {
00269     return m_backgroundTexture;
00270 }
00271 
00272 int KisOpenGLImageContext::imageTextureTileIndex(int x, int y) const
00273 {
00274     int column = x / m_imageTextureTileWidth;
00275     int row = y / m_imageTextureTileHeight;
00276 
00277     return column + (row * m_numImageTextureTileColumns);
00278 }
00279 
00280 GLuint KisOpenGLImageContext::imageTextureTile(int pixelX, int pixelY) const
00281 {
00282     Q_INT32 textureTileIndex = imageTextureTileIndex(pixelX, pixelY);
00283 
00284     textureTileIndex = CLAMP(textureTileIndex, 0, ((Q_INT32)m_imageTextureTiles.count()) - 1);
00285 
00286     return m_imageTextureTiles[textureTileIndex];
00287 }
00288 
00289 int KisOpenGLImageContext::imageTextureTileWidth() const
00290 {
00291     return m_imageTextureTileWidth;
00292 }
00293 
00294 int KisOpenGLImageContext::imageTextureTileHeight() const
00295 {
00296     return m_imageTextureTileHeight;
00297 }
00298 
00299 void KisOpenGLImageContext::createImageTextureTiles()
00300 {
00301     SharedContextWidget->makeCurrent();
00302 
00303     destroyImageTextureTiles();
00304 
00305     m_numImageTextureTileColumns = (m_image->width() + m_imageTextureTileWidth - 1) / m_imageTextureTileWidth;
00306     int numImageTextureTileRows = (m_image->height() + m_imageTextureTileHeight - 1) / m_imageTextureTileHeight;
00307     int numImageTextureTiles = m_numImageTextureTileColumns * numImageTextureTileRows;
00308 
00309     m_imageTextureTiles.resize(numImageTextureTiles);
00310     glGenTextures(numImageTextureTiles, &(m_imageTextureTiles[0]));
00311 
00312     //XXX: will be float/half with shaders
00313     #define RGBA_BYTES_PER_PIXEL 4
00314 
00315     QByteArray emptyTilePixelData(m_imageTextureTileWidth * m_imageTextureTileHeight * RGBA_BYTES_PER_PIXEL);
00316     emptyTilePixelData.fill(0);
00317 
00318     for (int tileIndex = 0; tileIndex < numImageTextureTiles; ++tileIndex) {
00319 
00320         glBindTexture(GL_TEXTURE_2D, m_imageTextureTiles[tileIndex]);
00321         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
00322         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
00323         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
00324         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
00325 
00326         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
00327 
00328         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_imageTextureTileWidth, m_imageTextureTileHeight, 0,
00329               GL_BGRA, GL_UNSIGNED_BYTE, &emptyTilePixelData[0]);
00330     }
00331 }
00332 
00333 void KisOpenGLImageContext::destroyImageTextureTiles()
00334 {
00335     if (!m_imageTextureTiles.empty()) {
00336         SharedContextWidget->makeCurrent();
00337         glDeleteTextures(m_imageTextureTiles.count(), &(m_imageTextureTiles[0]));
00338         m_imageTextureTiles.clear();
00339     }
00340 }
00341 
00342 void KisOpenGLImageContext::update(const QRect& imageRect)
00343 {
00344     updateImageTextureTiles(imageRect);
00345 }
00346 
00347 void KisOpenGLImageContext::setSelectionDisplayEnabled(bool enable)
00348 {
00349     m_displaySelection = enable;
00350 }
00351 
00352 void KisOpenGLImageContext::slotImageUpdated(QRect rc)
00353 {
00354     QRect r = rc & m_image->bounds();
00355 
00356     updateImageTextureTiles(r);
00357     emit sigImageUpdated(r);
00358 }
00359 
00360 void KisOpenGLImageContext::slotImageSizeChanged(Q_INT32 w, Q_INT32 h)
00361 {
00362     createImageTextureTiles();
00363     updateImageTextureTiles(m_image->bounds());
00364 
00365     emit sigSizeChanged(w, h);
00366 }
00367 
00368 #include "kis_opengl_image_context.moc"
00369 
00370 #endif // HAVE_GL
00371 
KDE Home | KDE Accessibility Home | Description of Access Keys