/* * Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com> * * 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 2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include <config.h> #endif #ifdef HAVE_GL #include <kdebug.h> #include <ksharedptr.h> #include "kis_global.h" #include "kis_meta_registry.h" #include "kis_colorspace_factory_registry.h" #include "kis_image.h" #include "kis_layer.h" #include "kis_selection.h" #include "kis_background.h" #include "kis_opengl_canvas.h" #include "kis_opengl_image_context.h" using namespace std; TQGLWidget *KisOpenGLImageContext::SharedContextWidget = 0; int KisOpenGLImageContext::SharedContextWidgetRefCount = 0; KisOpenGLImageContext::ImageContextMap KisOpenGLImageContext::imageContextMap; KisOpenGLImageContext::KisOpenGLImageContext() { m_image = 0; m_monitorProfile = 0; m_exposure = 0; } KisOpenGLImageContext::~KisOpenGLImageContext() { kdDebug(41001) << "Destroyed KisOpenGLImageContext\n"; --SharedContextWidgetRefCount; kdDebug(41001) << "Shared context widget ref count now " << SharedContextWidgetRefCount << endl; if (SharedContextWidgetRefCount == 0) { kdDebug(41001) << "Deleting shared context widget\n"; delete SharedContextWidget; SharedContextWidget = 0; } imageContextMap.erase(m_image); } KisOpenGLImageContext::KisOpenGLImageContext(KisImageSP image, KisProfile *monitorProfile) { kdDebug(41001) << "Created KisOpenGLImageContext\n"; m_image = image; m_monitorProfile = monitorProfile; m_exposure = 0; m_displaySelection = true; if (SharedContextWidget == 0) { kdDebug(41001) << "Creating shared context widget\n"; SharedContextWidget = new TQGLWidget(KisOpenGLCanvasFormat); } ++SharedContextWidgetRefCount; kdDebug(41001) << "Shared context widget ref count now " << SharedContextWidgetRefCount << endl; SharedContextWidget->makeCurrent(); glGenTextures(1, &m_backgroundTexture); generateBackgroundTexture(); GLint max_texture_size; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); m_imageTextureTileWidth = TQMIN(PREFERRED_IMAGE_TEXTURE_WIDTH, max_texture_size); m_imageTextureTileHeight = TQMIN(PREFERRED_IMAGE_TEXTURE_HEIGHT, max_texture_size); createImageTextureTiles(); connect(m_image, TQT_SIGNAL(sigImageUpdated(TQRect)), TQT_SLOT(slotImageUpdated(TQRect))); connect(m_image, TQT_SIGNAL(sigSizeChanged(TQ_INT32, TQ_INT32)), TQT_SLOT(slotImageSizeChanged(TQ_INT32, TQ_INT32))); updateImageTextureTiles(m_image->bounds()); } KisOpenGLImageContextSP KisOpenGLImageContext::getImageContext(KisImageSP image, KisProfile *monitorProfile) { if (imageCanShareImageContext(image)) { ImageContextMap::iterator it = imageContextMap.find(image); if (it != imageContextMap.end()) { kdDebug(41001) << "Sharing image context from map\n"; KisOpenGLImageContextSP context = (*it).second; context->setMonitorProfile(monitorProfile); return context; } else { KisOpenGLImageContext *imageContext = new KisOpenGLImageContext(image, monitorProfile); imageContextMap[image] = imageContext; kdDebug(41001) << "Added shareable context to map\n"; return imageContext; } } else { kdDebug(41001) << "Creating non-shareable image context\n"; return new KisOpenGLImageContext(image, monitorProfile); } } bool KisOpenGLImageContext::imageCanShareImageContext(KisImageSP image) { if (image->colorSpace()->hasHighDynamicRange()) { //XXX: and we don't have shaders... return false; } else { return true; } } TQGLWidget *KisOpenGLImageContext::sharedContextWidget() const { return SharedContextWidget; } void KisOpenGLImageContext::updateImageTextureTiles(const TQRect& rect) { //kdDebug() << "updateImageTextureTiles " << rect << endl; TQRect updateRect = rect & m_image->bounds(); if (!updateRect.isEmpty()) { SharedContextWidget->makeCurrent(); int firstColumn = updateRect.left() / m_imageTextureTileWidth; int lastColumn = updateRect.right() / m_imageTextureTileWidth; int firstRow = updateRect.top() / m_imageTextureTileHeight; int lastRow = updateRect.bottom() / m_imageTextureTileHeight; for (int column = firstColumn; column <= lastColumn; column++) { for (int row = firstRow; row <= lastRow; row++) { TQRect tileRect(column * m_imageTextureTileWidth, row * m_imageTextureTileHeight, m_imageTextureTileWidth, m_imageTextureTileHeight); TQRect tileUpdateRect = tileRect & updateRect; glBindTexture(GL_TEXTURE_2D, imageTextureTile(tileRect.x(), tileRect.y())); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);//GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); TQImage tileUpdateImage = m_image->convertToTQImage(tileUpdateRect.left(), tileUpdateRect.top(), tileUpdateRect.right(), tileUpdateRect.bottom(), m_monitorProfile, m_exposure); if (m_displaySelection) { if (m_image->activeLayer() != 0) { m_image->activeLayer()->paintSelection(tileUpdateImage, tileUpdateRect.x(), tileUpdateRect.y(), tileUpdateRect.width(), tileUpdateRect.height()); } } if (tileUpdateRect.width() == m_imageTextureTileWidth && tileUpdateRect.height() == m_imageTextureTileHeight) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_imageTextureTileWidth, m_imageTextureTileHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, tileUpdateImage.bits()); } else { int xOffset = tileUpdateRect.x() - tileRect.x(); int yOffset = tileUpdateRect.y() - tileRect.y(); glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, tileUpdateRect.width(), tileUpdateRect.height(), GL_BGRA, GL_UNSIGNED_BYTE, tileUpdateImage.bits()); } GLenum error = glGetError (); if (error != GL_NO_ERROR) { kdDebug(41001) << "Error loading texture: " << endl; } } } } } KisColorSpace* KisOpenGLImageContext::textureColorSpaceForImageColorSpace(KisColorSpace */*imageColorSpace*/) { return KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("RGBA", ""), ""); } void KisOpenGLImageContext::setMonitorProfile(KisProfile *monitorProfile) { if (monitorProfile != m_monitorProfile) { m_monitorProfile = monitorProfile; generateBackgroundTexture(); updateImageTextureTiles(m_image->bounds()); } } void KisOpenGLImageContext::setHDRExposure(float exposure) { if (exposure != m_exposure) { m_exposure = exposure; if (m_image->colorSpace()->hasHighDynamicRange()) { //XXX: and we are not using shaders... updateImageTextureTiles(m_image->bounds()); } } } void KisOpenGLImageContext::generateBackgroundTexture() { SharedContextWidget->makeCurrent(); glBindTexture(GL_TEXTURE_2D, m_backgroundTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); TQImage backgroundImage = m_image->background()->patternTile(); // XXX: temp. Q_ASSERT(backgroundImage.width() == BACKGROUND_TEXTURE_WIDTH); Q_ASSERT(backgroundImage.height() == BACKGROUND_TEXTURE_HEIGHT); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, BACKGROUND_TEXTURE_WIDTH, BACKGROUND_TEXTURE_HEIGHT, 0, GL_BGRA, GL_UNSIGNED_BYTE, backgroundImage.bits()); } GLuint KisOpenGLImageContext::backgroundTexture() const { return m_backgroundTexture; } int KisOpenGLImageContext::imageTextureTileIndex(int x, int y) const { int column = x / m_imageTextureTileWidth; int row = y / m_imageTextureTileHeight; return column + (row * m_numImageTextureTileColumns); } GLuint KisOpenGLImageContext::imageTextureTile(int pixelX, int pixelY) const { TQ_INT32 textureTileIndex = imageTextureTileIndex(pixelX, pixelY); textureTileIndex = CLAMP(textureTileIndex, 0, ((TQ_INT32)m_imageTextureTiles.count()) - 1); return m_imageTextureTiles[textureTileIndex]; } int KisOpenGLImageContext::imageTextureTileWidth() const { return m_imageTextureTileWidth; } int KisOpenGLImageContext::imageTextureTileHeight() const { return m_imageTextureTileHeight; } void KisOpenGLImageContext::createImageTextureTiles() { SharedContextWidget->makeCurrent(); destroyImageTextureTiles(); m_numImageTextureTileColumns = (m_image->width() + m_imageTextureTileWidth - 1) / m_imageTextureTileWidth; int numImageTextureTileRows = (m_image->height() + m_imageTextureTileHeight - 1) / m_imageTextureTileHeight; int numImageTextureTiles = m_numImageTextureTileColumns * numImageTextureTileRows; m_imageTextureTiles.resize(numImageTextureTiles); glGenTextures(numImageTextureTiles, &(m_imageTextureTiles[0])); //XXX: will be float/half with shaders #define RGBA_BYTES_PER_PIXEL 4 TQByteArray emptyTilePixelData(m_imageTextureTileWidth * m_imageTextureTileHeight * RGBA_BYTES_PER_PIXEL); emptyTilePixelData.fill(0); for (int tileIndex = 0; tileIndex < numImageTextureTiles; ++tileIndex) { glBindTexture(GL_TEXTURE_2D, m_imageTextureTiles[tileIndex]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_imageTextureTileWidth, m_imageTextureTileHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, &emptyTilePixelData[0]); } } void KisOpenGLImageContext::destroyImageTextureTiles() { if (!m_imageTextureTiles.empty()) { SharedContextWidget->makeCurrent(); glDeleteTextures(m_imageTextureTiles.count(), &(m_imageTextureTiles[0])); m_imageTextureTiles.clear(); } } void KisOpenGLImageContext::update(const TQRect& imageRect) { updateImageTextureTiles(imageRect); } void KisOpenGLImageContext::setSelectionDisplayEnabled(bool enable) { m_displaySelection = enable; } void KisOpenGLImageContext::slotImageUpdated(TQRect rc) { TQRect r = rc & m_image->bounds(); updateImageTextureTiles(r); emit sigImageUpdated(r); } void KisOpenGLImageContext::slotImageSizeChanged(TQ_INT32 w, TQ_INT32 h) { createImageTextureTiles(); updateImageTextureTiles(m_image->bounds()); emit sigSizeChanged(w, h); } #include "kis_opengl_image_context.moc" #endif // HAVE_GL