summaryrefslogtreecommitdiffstats
path: root/chalk/ui/kis_opengl_image_context.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chalk/ui/kis_opengl_image_context.cc')
-rw-r--r--chalk/ui/kis_opengl_image_context.cc371
1 files changed, 371 insertions, 0 deletions
diff --git a/chalk/ui/kis_opengl_image_context.cc b/chalk/ui/kis_opengl_image_context.cc
new file mode 100644
index 00000000..6a302192
--- /dev/null
+++ b/chalk/ui/kis_opengl_image_context.cc
@@ -0,0 +1,371 @@
+/*
+ * 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
+