|
|
|
/*
|
|
|
|
* 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, TQ_SIGNAL(sigImageUpdated(TQRect)),
|
|
|
|
TQ_SLOT(slotImageUpdated(TQRect)));
|
|
|
|
connect(m_image, TQ_SIGNAL(sigSizeChanged(TQ_INT32, TQ_INT32)),
|
|
|
|
TQ_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
|
|
|
|
|