diff --git a/krita/ui/canvas/kis_canvas2.h b/krita/ui/canvas/kis_canvas2.h index 7538f93f77f..17603ff59b8 100644 --- a/krita/ui/canvas/kis_canvas2.h +++ b/krita/ui/canvas/kis_canvas2.h @@ -1,263 +1,263 @@ /* This file is part of the KDE project * Copyright (C) 2006, 2010 Boudewijn Rempt * Copyright (C) 2011 Silvio Heinrich * * 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. */ #ifndef KIS_CANVAS_H #define KIS_CANVAS_H #include #include #include #include #include #include #include #include #include #include "kis_ui_types.h" #include "kis_coordinates_converter.h" class KoToolProxy; class KoColorProfile; class KisCanvasDecoration; class KisViewManager; class KisFavoriteResourceManager; class KisDisplayFilter; class KisDisplayColorConverter; struct KisExposureGammaCorrectionInterface; class KisPaintingAssistantsDecoration; class KisView; class KisInputManager; enum KisCanvasType { QPAINTER, OPENGL }; class KisCoordinatesConverter; class KoViewConverter; /** * KisCanvas2 is not an actual widget class, but rather an adapter for * the widget it contains, which may be either a QPainter based * canvas, or an OpenGL based canvas: that are the real widgets. */ class KRITAUI_EXPORT KisCanvas2 : public QObject, public KoCanvasBase { Q_OBJECT public: /** * Create a new canvas. The canvas manages a widget that will do * the actual painting: the canvas itself is not a widget. * * @param viewConverter the viewconverter for converting between * window and document coordinates. */ KisCanvas2(KisCoordinatesConverter* coordConverter, KoCanvasResourceManager *resourceManager, QPointer viewManager, KoShapeBasedDocumentBase* sc); virtual ~KisCanvas2(); void setCanvasWidget(QWidget * widget); void notifyZoomChanged(); virtual void disconnectCanvasObserver(QObject *object); public: // KoCanvasBase implementation KoGuidesData *guidesData(); bool canvasIsOpenGL(); void gridSize(qreal *horizontal, qreal *vertical) const; bool snapToGrid() const; // XXX: Why? void addCommand(KUndo2Command *command); virtual QPoint documentOrigin() const; QPoint documentOffset() const; /** * Return the right shape manager for the current layer. That is * to say, if the current layer is a vector layer, return the shape * layer's canvas' shapemanager, else the shapemanager associated * with the global krita canvas. */ KoShapeManager * shapeManager() const; /** * Return the shape manager associated with this canvas */ KoShapeManager * globalShapeManager() const; void updateCanvas(const QRectF& rc); virtual void updateInputMethodInfo(); const KisCoordinatesConverter* coordinatesConverter() const; virtual KoViewConverter *viewConverter() const; virtual QWidget* canvasWidget(); virtual const QWidget* canvasWidget() const; virtual KoUnit unit() const; virtual KoToolProxy* toolProxy() const; const KoColorProfile* monitorProfile(); /** * Prescale the canvas represention of the image (if necessary, it * is for QPainter, not for OpenGL). */ void preScale(); // FIXME: // Temporary! Either get the current layer and image from the // resource provider, or use this, which gets them from the // current shape selection. KisImageWSP currentImage() const; /** * Filters events and sends them to canvas actions. Shared * among all the views/canvases * * NOTE: May be null while initialization! */ KisInputManager* globalInputManager() const; KisPaintingAssistantsDecoration* paintingAssistantsDecoration(); public: // KisCanvas2 methods KisImageWSP image() const; KisViewManager* viewManager() const; QPointer imageView() const; /// @return true if the canvas image should be displayed in vertically mirrored mode void addDecoration(KisCanvasDecoration* deco); KisCanvasDecoration* decoration(const QString& id) const; void setDisplayFilter(KisDisplayFilter *displayFilter); KisDisplayFilter *displayFilter() const; KisDisplayColorConverter* displayColorConverter() const; KisExposureGammaCorrectionInterface* exposureGammaCorrectionInterface() const; void setCursor(const QCursor &cursor); Q_SIGNALS: void imageChanged(KisImageWSP image); void sigCanvasCacheUpdated(); void sigContinueResizeImage(qint32 w, qint32 h); void documentOffsetUpdateFinished(); // emitted whenever the canvas widget thinks sketch should update void updateCanvasRequested(const QRect &rc); public Q_SLOTS: /// Update the entire canvas area void updateCanvas(); void startResizingImage(); void finishResizingImage(qint32 w, qint32 h); /// canvas rotation in degrees qreal rotationAngle() const; /// Bools indicating canvasmirroring. bool xAxisMirrored() const; bool yAxisMirrored() const; void channelSelectionChanged(); /** * Called whenever the display monitor profile resource changes */ void slotSetDisplayProfile(const KoColorProfile *profile); + void startUpdateInPatches(QRect imageRect); private Q_SLOTS: /// The image projection has changed, now start an update /// of the canvas representation. void startUpdateCanvasProjection(const QRect & rc); void updateCanvasProjection(); - void startUpdateInPatches(QRect imageRect); /** * Called whenever the view widget needs to show a different part of * the document * * @param documentOffset the offset in widget pixels */ void documentOffsetMoved(const QPoint &documentOffset); /** * Called whenever the configuration settings change. */ void slotConfigChanged(); void slotSelectionChanged(); void slotDoCanvasUpdate(); public: bool isPopupPaletteVisible(); void slotShowPopupPalette(const QPoint& = QPoint(0,0)); // interface for KisCanvasController only void setWrapAroundViewingMode(bool value); bool wrapAroundViewingMode() const; void initializeImage(); // interface for KisViewManager only void resetCanvas(bool useOpenGL); void setFavoriteResourceManager(KisFavoriteResourceManager* favoriteResourceManager); private: Q_DISABLE_COPY(KisCanvas2) void connectCurrentCanvas(); void disconnectCurrentCanvas(); void pan(QPoint shift); void createCanvas(bool useOpenGL); void createQPainterCanvas(); void createOpenGLCanvas(); void updateCanvasWidgetImpl(const QRect &rc = QRect()); private: class KisCanvas2Private; KisCanvas2Private * const m_d; }; #endif diff --git a/krita/ui/canvas/kis_qpainter_canvas.cpp b/krita/ui/canvas/kis_qpainter_canvas.cpp index b8966d94e90..9e6e23a0b04 100644 --- a/krita/ui/canvas/kis_qpainter_canvas.cpp +++ b/krita/ui/canvas/kis_qpainter_canvas.cpp @@ -1,254 +1,254 @@ /* * Copyright (C) Boudewijn Rempt , (C) 2006 * Copyright (C) Lukas Tvrdy , (C) 2009 * * 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. */ #include "kis_qpainter_canvas.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_coordinates_converter.h" #include #include #include #include #include #include "KisViewManager.h" #include "kis_canvas2.h" #include "kis_prescaled_projection.h" #include "kis_config.h" #include "kis_canvas_resource_provider.h" #include "KisDocument.h" #include "kis_selection_manager.h" #include "kis_selection.h" #include "kis_perspective_grid_manager.h" #include "kis_canvas_updates_compressor.h" #include "kis_config_notifier.h" #include "kis_group_layer.h" #include "canvas/kis_display_color_converter.h" //#define DEBUG_REPAINT #include class KisQPainterCanvas::Private { public: KisPrescaledProjectionSP prescaledProjection; QBrush checkBrush; }; KisQPainterCanvas::KisQPainterCanvas(KisCanvas2 *canvas, KisCoordinatesConverter *coordinatesConverter, QWidget * parent) : QWidget(parent) , KisCanvasWidgetBase(canvas, coordinatesConverter) , m_d(new Private()) { setAutoFillBackground(true); setAcceptDrops(true); setFocusPolicy(Qt::StrongFocus); setAttribute(Qt::WA_InputMethodEnabled, true); setAttribute(Qt::WA_StaticContents); setAttribute(Qt::WA_OpaquePaintEvent); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); slotConfigChanged(); } KisQPainterCanvas::~KisQPainterCanvas() { delete m_d; } void KisQPainterCanvas::setPrescaledProjection(KisPrescaledProjectionSP prescaledProjection) { m_d->prescaledProjection = prescaledProjection; } void KisQPainterCanvas::paintEvent(QPaintEvent * ev) { KisImageWSP image = canvas()->image(); if (image == 0) return; setAutoFillBackground(false); if (m_buffer.size() != size()) { m_buffer = QImage(size(), QImage::Format_ARGB32_Premultiplied); } QPainter gc(&m_buffer); // we double buffer, so we paint on an image first, then from the image onto the canvas, // so copy the clip region since otherwise we're filling the whole buffer every time with // the background color _and_ the transparent squares. gc.setClipRegion(ev->region()); KisCoordinatesConverter *converter = coordinatesConverter(); QTransform imageTransform = converter->viewportToWidgetTransform(); gc.save(); gc.setCompositionMode(QPainter::CompositionMode_Source); gc.fillRect(QRect(QPoint(0, 0), size()), borderColor()); QTransform checkersTransform; QPointF brushOrigin; QPolygonF polygon; converter->getQPainterCheckersInfo(&checkersTransform, &brushOrigin, &polygon); gc.setPen(Qt::NoPen); gc.setBrush(m_d->checkBrush); gc.setBrushOrigin(brushOrigin); gc.setTransform(checkersTransform); gc.drawPolygon(polygon); gc.setTransform(imageTransform); gc.setRenderHint(QPainter::SmoothPixmapTransform, true); QRectF viewportRect = converter->widgetToViewport(ev->rect()); gc.setCompositionMode(QPainter::CompositionMode_SourceOver); gc.drawImage(viewportRect, m_d->prescaledProjection->prescaledQImage(), viewportRect); gc.restore(); #ifdef DEBUG_REPAINT QColor color = QColor(random() % 255, random() % 255, random() % 255, 150); gc.fillRect(ev->rect(), color); #endif drawDecorations(gc, ev->rect()); gc.end(); QPainter painter(this); painter.drawImage(ev->rect(), m_buffer, ev->rect()); } QVariant KisQPainterCanvas::inputMethodQuery(Qt::InputMethodQuery query) const { return processInputMethodQuery(query); } void KisQPainterCanvas::inputMethodEvent(QInputMethodEvent *event) { processInputMethodEvent(event); } void KisQPainterCanvas::channelSelectionChanged(QBitArray channelFlags) { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->setChannelFlags(channelFlags); } void KisQPainterCanvas::setDisplayProfile(KisDisplayColorConverter *colorConverter) { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->setMonitorProfile(colorConverter->monitorProfile(), colorConverter->renderingIntent(), colorConverter->conversionFlags()); } void KisQPainterCanvas::setDisplayFilter(KisDisplayFilter* displayFilter) { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->setDisplayFilter(displayFilter); - startUpdateInPatches(image->bounds()); + canvas()->startUpdateInPatches(canvas()->image()->bounds()); } void KisQPainterCanvas::setWrapAroundViewingMode(bool value) { kDebug() << "Wrap around viewing mode not implemented in QPainter Canvas."; return; } void KisQPainterCanvas::disconnectCurrentCanvas() { } void KisQPainterCanvas::finishResizingImage(qint32 w, qint32 h) { m_d->prescaledProjection->slotImageSizeChanged(w, h); } KisUpdateInfoSP KisQPainterCanvas::startUpdateCanvasProjection(const QRect & rc, QBitArray channelFlags) { Q_UNUSED(channelFlags); return m_d->prescaledProjection->updateCache(rc); } QRect KisQPainterCanvas::updateCanvasProjection(KisUpdateInfoSP info) { /** * It might happen that the canvas type is switched while the * update info is being stuck in the Qt's signals queue. Than a wrong * type of the info may come. So just check it here. */ bool isPPUpdateInfo = dynamic_cast(info.data()); if (isPPUpdateInfo) { m_d->prescaledProjection->recalculateCache(info); return info->dirtyViewportRect(); } else { return QRect(); } } void KisQPainterCanvas::resizeEvent(QResizeEvent *e) { QSize size(e->size()); if (size.width() <= 0) { size.setWidth(1); } if (size.height() <= 0) { size.setHeight(1); } coordinatesConverter()->setCanvasWidgetSize(size); m_d->prescaledProjection->notifyCanvasSizeChanged(size); } void KisQPainterCanvas::slotConfigChanged() { m_d->checkBrush = QBrush(createCheckersImage()); notifyConfigChanged(); } bool KisQPainterCanvas::callFocusNextPrevChild(bool next) { return focusNextPrevChild(next); } diff --git a/krita/ui/opengl/kis_opengl_canvas2.cpp b/krita/ui/opengl/kis_opengl_canvas2.cpp index 6c49ac2db19..9d7b00804bf 100644 --- a/krita/ui/opengl/kis_opengl_canvas2.cpp +++ b/krita/ui/opengl/kis_opengl_canvas2.cpp @@ -1,779 +1,779 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2006-2013 * Copyright (C) 2015 Michael Abrahams * * 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. */ #define GL_GLEXT_PROTOTYPES #include "opengl/kis_opengl_canvas2.h" #ifdef HAVE_OPENGL #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoToolProxy.h" #include "kis_config.h" #include "kis_types.h" #include #include "canvas/kis_canvas2.h" #include "kis_coordinates_converter.h" #include "kis_image.h" #include "opengl/kis_opengl_image_textures.h" #include "kis_canvas_resource_provider.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_debug.h" #include "opengl/kis_opengl_canvas2_p.h" #include "kis_coordinates_converter.h" #include "canvas/kis_display_filter.h" #include "canvas/kis_display_color_converter.h" #define NEAR_VAL -1000.0 #define FAR_VAL 1000.0 #ifndef GL_CLAMP_TO_EDGE #define GL_CLAMP_TO_EDGE 0x812F #endif #define PROGRAM_VERTEX_ATTRIBUTE 0 #define PROGRAM_TEXCOORD_ATTRIBUTE 1 static bool OPENGL_SUCCESS = false; namespace { const GLuint NO_PROGRAM = 0; } struct KisOpenGLCanvas2::Private { public: Private() : canvasInitialized(false) , displayShader(0) , checkerShader(0) , glSyncObject(0) , wrapAroundMode(false) { vertices = new QVector3D[6]; texCoords = new QVector2D[6]; } ~Private() { delete displayShader; delete checkerShader; delete[] vertices; delete[] texCoords; Sync::deleteSync(glSyncObject); } bool canvasInitialized; QVector3D *vertices; QVector2D *texCoords; KisOpenGLImageTexturesSP openGLImageTextures; QOpenGLShaderProgram *displayShader; int displayUniformLocationModelViewProjection; int displayUniformLocationTextureMatrix; int displayUniformLocationViewPortScale; int displayUniformLocationTexelSize; int displayUniformLocationTexture0; int displayUniformLocationTexture1; QOpenGLShaderProgram *checkerShader; int checkerUniformLocationModelViewProjection; int checkerUniformLocationTextureMatrix; KisDisplayFilter* displayFilter; KisTextureTile::FilterMode filterMode; GLsync glSyncObject; bool firstDrawImage; qreal scaleX, scaleY; bool wrapAroundMode; int xToColWithWrapCompensation(int x, const QRect &imageRect) { int firstImageColumn = openGLImageTextures->xToCol(imageRect.left()); int lastImageColumn = openGLImageTextures->xToCol(imageRect.right()); int colsPerImage = lastImageColumn - firstImageColumn + 1; int numWraps = floor(qreal(x) / imageRect.width()); int remainder = x - imageRect.width() * numWraps; return colsPerImage * numWraps + openGLImageTextures->xToCol(remainder); } int yToRowWithWrapCompensation(int y, const QRect &imageRect) { int firstImageRow = openGLImageTextures->yToRow(imageRect.top()); int lastImageRow = openGLImageTextures->yToRow(imageRect.bottom()); int rowsPerImage = lastImageRow - firstImageRow + 1; int numWraps = floor(qreal(y) / imageRect.height()); int remainder = y - imageRect.height() * numWraps; return rowsPerImage * numWraps + openGLImageTextures->yToRow(remainder); } }; KisOpenGLCanvas2::KisOpenGLCanvas2(KisCanvas2 *canvas, KisCoordinatesConverter *coordinatesConverter, QWidget *parent, KisImageWSP image, KisDisplayColorConverter *colorConverter) : QOpenGLWidget(parent) , KisCanvasWidgetBase(canvas, coordinatesConverter) , d(new Private()) { QSurfaceFormat format; format.setDepthBufferSize(24); setFormat(format); KisConfig cfg; cfg.writeEntry("canvasState", "OPENGL_STARTED"); d->openGLImageTextures = KisOpenGLImageTextures::getImageTextures(image, colorConverter->monitorProfile(), colorConverter->renderingIntent(), colorConverter->conversionFlags()); setAcceptDrops(true); setFocusPolicy(Qt::StrongFocus); setAttribute(Qt::WA_NoSystemBackground); setAttribute(Qt::WA_AcceptTouchEvents); setAutoFillBackground(false); setAttribute(Qt::WA_InputMethodEnabled, true); setAttribute(Qt::WA_DontCreateNativeAncestors, true); setDisplayFilter(colorConverter->displayFilter()); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); slotConfigChanged(); cfg.writeEntry("canvasState", "OPENGL_SUCCESS"); } KisOpenGLCanvas2::~KisOpenGLCanvas2() { delete d; } void KisOpenGLCanvas2::setDisplayFilter(KisDisplayFilter* displayFilter) { bool needsInternalColorManagement = !displayFilter || displayFilter->useInternalColorManagement(); bool needsFullRefresh = d->openGLImageTextures-> setInternalColorManagementActive(needsInternalColorManagement); d->displayFilter = displayFilter; if (d->canvasInitialized) { d->canvasInitialized = false; initializeDisplayShader(); initializeCheckerShader(); d->canvasInitialized = true; } if (needsFullRefresh) { - startUpdateInPatches(image->bounds()); + canvas()->startUpdateInPatches(canvas()->image()->bounds()); } else { - updateCanvas(); + canvas()->updateCanvas(); } } void KisOpenGLCanvas2::disconnectCurrentCanvas() { Q_ASSERT(d->openGLImageTextures); d->openGLImageTextures->disconnect(canvas()); d->openGLImageTextures->disconnect(canvas()->image()); } void KisOpenGLCanvas2::setWrapAroundViewingMode(bool value) { d->wrapAroundMode = value; update(); } void KisOpenGLCanvas2::initializeGL() { // KisConfig cfg; // if (cfg.disableVSync()) { // if (!VSyncWorkaround::tryDisableVSync(this)) { // qWarning(); // qWarning() << "WARNING: We didn't manage to switch off VSync on your graphics adapter."; // qWarning() << "WARNING: It means either your hardware or driver doesn't support it,"; // qWarning() << "WARNING: or we just don't know about this hardware. Please report us a bug"; // qWarning() << "WARNING: with the output of \'glxinfo\' for your card."; // qWarning(); // qWarning() << "WARNING: Trying to workaround it by disabling Double Buffering."; // qWarning() << "WARNING: You may see some flickering when painting with some tools. It doesn't"; // qWarning() << "WARNING: affect the quality of the final image, though."; // qWarning(); // if (cfg.disableDoubleBuffering() && QOpenGLContext::currentContext()->format().swapBehavior() == QSurfaceFormat::DoubleBuffer) { // qCritical() << "CRITICAL: Failed to disable Double Buffering. Lines may look \"bended\" on your image."; // qCritical() << "CRITICAL: Your graphics card or driver does not fully support Krita's OpenGL canvas."; // qCritical() << "CRITICAL: For an optimal experience, please disable OpenGL"; // qCritical(); // } // } // } KisConfig cfg; qDebug() << "OpenGL: Preparing to initialize OpenGL for KisCanvas"; int glVersion = KisOpenGL::initializeContext(context()); qDebug() << "OpenGL: Version found" << glVersion; initializeOpenGLFunctions(); VSyncWorkaround::tryDisableVSync(context()); d->openGLImageTextures->initGL((QOpenGLFunctions *)this); d->openGLImageTextures->generateCheckerTexture(createCheckersImage(cfg.checkSize())); initializeCheckerShader(); initializeDisplayShader(); Sync::init(); /** * Warn about Intel's broken video drivers */ #if defined HAVE_OPENGL && defined Q_OS_WIN #ifndef GL_RENDERER # define GL_RENDERER 0x1F01 #endif QString renderer = QString((const char*)glGetString(GL_RENDERER)); if (cfg.useOpenGL() && renderer.startsWith("Intel") && !cfg.readEntry("WarnedAboutIntel", false)) { QMessageBox::information(0, i18nc("@title:window", "Krita: Warning"), i18n("You have an Intel(R) HD Graphics video adapter.\n" "If you experience problems like a black or blank screen," "please update your display driver to the latest version.\n\n" "You can also disable OpenGL rendering in Krita's Settings.\n")); cfg.writeEntry("WarnedAboutIntel", true); } #endif d->canvasInitialized = true; } void KisOpenGLCanvas2::resizeGL(int width, int height) { coordinatesConverter()->setCanvasWidgetSize(QSize(width, height)); paintGL(); } void KisOpenGLCanvas2::paintGL() { if (!OPENGL_SUCCESS) { KisConfig cfg; cfg.writeEntry("canvasState", "OPENGL_PAINT_STARTED"); } QPainter gc(this); gc.beginNativePainting(); renderCanvasGL(); if (d->glSyncObject) { Sync::deleteSync(d->glSyncObject); } d->glSyncObject = Sync::getSync(); gc.endNativePainting(); renderDecorations(&gc); gc.end(); if (!OPENGL_SUCCESS) { KisConfig cfg; cfg.writeEntry("canvasState", "OPENGL_SUCCESS"); OPENGL_SUCCESS = true; } } bool KisOpenGLCanvas2::isBusy() const { return Sync::syncStatus(d->glSyncObject) == Sync::Unsignaled; } inline void rectToVertices(QVector3D* vertices, const QRectF &rc) { vertices[0] = QVector3D(rc.left(), rc.bottom(), 0.f); vertices[1] = QVector3D(rc.left(), rc.top(), 0.f); vertices[2] = QVector3D(rc.right(), rc.bottom(), 0.f); vertices[3] = QVector3D(rc.left(), rc.top(), 0.f); vertices[4] = QVector3D(rc.right(), rc.top(), 0.f); vertices[5] = QVector3D(rc.right(), rc.bottom(), 0.f); } inline void rectToTexCoords(QVector2D* texCoords, const QRectF &rc) { texCoords[0] = QVector2D(rc.left(), rc.bottom()); texCoords[1] = QVector2D(rc.left(), rc.top()); texCoords[2] = QVector2D(rc.right(), rc.bottom()); texCoords[3] = QVector2D(rc.left(), rc.top()); texCoords[4] = QVector2D(rc.right(), rc.top()); texCoords[5] = QVector2D(rc.right(), rc.bottom()); } void KisOpenGLCanvas2::drawCheckers() { if (!d->checkerShader) { return; } KisCoordinatesConverter *converter = coordinatesConverter(); QTransform textureTransform; QTransform modelTransform; QRectF textureRect; QRectF modelRect; QRectF viewportRect = !d->wrapAroundMode ? converter->imageRectInViewportPixels() : converter->widgetToViewport(this->rect()); converter->getOpenGLCheckersInfo(viewportRect, &textureTransform, &modelTransform, &textureRect, &modelRect); // XXX: getting a config object every time we draw the checkers is bad for performance! KisConfig cfg; GLfloat checkSizeScale = KisOpenGLImageTextures::BACKGROUND_TEXTURE_CHECK_SIZE / static_cast(cfg.checkSize()); textureTransform *= QTransform::fromScale(checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE, checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE); d->checkerShader->bind(); QMatrix4x4 projectionMatrix; projectionMatrix.setToIdentity(); projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL); // Set view/projection matrices QMatrix4x4 modelMatrix(modelTransform); modelMatrix.optimize(); modelMatrix = projectionMatrix * modelMatrix; d->checkerShader->setUniformValue(d->checkerUniformLocationModelViewProjection, modelMatrix); QMatrix4x4 textureMatrix(textureTransform); d->checkerShader->setUniformValue(d->checkerUniformLocationTextureMatrix, textureMatrix); //Setup the geometry for rendering rectToVertices(d->vertices, modelRect); d->checkerShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); d->checkerShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, d->vertices); rectToTexCoords(d->texCoords, textureRect); d->checkerShader->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE); d->checkerShader->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, d->texCoords); // render checkers glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, d->openGLImageTextures->checkerTexture()); glDrawArrays(GL_TRIANGLES, 0, 6); glBindTexture(GL_TEXTURE_2D, 0); d->checkerShader->release(); } void KisOpenGLCanvas2::drawImage() { if (!d->displayShader) { return; } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); KisCoordinatesConverter *converter = coordinatesConverter(); d->displayShader->bind(); QMatrix4x4 projectionMatrix; projectionMatrix.setToIdentity(); projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL); // Set view/projection matrices QMatrix4x4 modelMatrix(coordinatesConverter()->imageToWidgetTransform()); modelMatrix.optimize(); modelMatrix = projectionMatrix * modelMatrix; d->displayShader->setUniformValue(d->displayUniformLocationModelViewProjection, modelMatrix); QMatrix4x4 textureMatrix; textureMatrix.setToIdentity(); d->displayShader->setUniformValue(d->displayUniformLocationTextureMatrix, textureMatrix); QRectF widgetRect(0,0, width(), height()); QRectF widgetRectInImagePixels = converter->documentToImage(converter->widgetToDocument(widgetRect)); qreal scaleX, scaleY; converter->imageScale(&scaleX, &scaleY); d->displayShader->setUniformValue(d->displayUniformLocationViewPortScale, (GLfloat) scaleX); d->displayShader->setUniformValue(d->displayUniformLocationTexelSize, (GLfloat) d->openGLImageTextures->texelSize()); QRect ir = d->openGLImageTextures->storedImageBounds(); QRect wr = widgetRectInImagePixels.toAlignedRect(); if (!d->wrapAroundMode) { // if we don't want to paint wrapping images, just limit the // processing area, and the code will handle all the rest wr &= ir; } int firstColumn = d->xToColWithWrapCompensation(wr.left(), ir); int lastColumn = d->xToColWithWrapCompensation(wr.right(), ir); int firstRow = d->yToRowWithWrapCompensation(wr.top(), ir); int lastRow = d->yToRowWithWrapCompensation(wr.bottom(), ir); int minColumn = d->openGLImageTextures->xToCol(ir.left()); int maxColumn = d->openGLImageTextures->xToCol(ir.right()); int minRow = d->openGLImageTextures->yToRow(ir.top()); int maxRow = d->openGLImageTextures->yToRow(ir.bottom()); int imageColumns = maxColumn - minColumn + 1; int imageRows = maxRow - minRow + 1; for (int col = firstColumn; col <= lastColumn; col++) { for (int row = firstRow; row <= lastRow; row++) { int effectiveCol = col; int effectiveRow = row; QPointF tileWrappingTranslation; if (effectiveCol > maxColumn || effectiveCol < minColumn) { int translationStep = floor(qreal(col) / imageColumns); int originCol = translationStep * imageColumns; effectiveCol = col - originCol; tileWrappingTranslation.rx() = translationStep * ir.width(); } if (effectiveRow > maxRow || effectiveRow < minRow) { int translationStep = floor(qreal(row) / imageRows); int originRow = translationStep * imageRows; effectiveRow = row - originRow; tileWrappingTranslation.ry() = translationStep * ir.height(); } KisTextureTile *tile = d->openGLImageTextures->getTextureTileCR(effectiveCol, effectiveRow); if (!tile) { qWarning() << "OpenGL: Trying to paint texture tile but it has not been created yet."; continue; } /* * We create a float rect here to workaround Qt's * "history reasons" in calculation of right() * and bottom() coordinates of integer rects. */ QRectF textureRect(tile->tileRectInTexturePixels()); QRectF modelRect(tile->tileRectInImagePixels().translated(tileWrappingTranslation.x(), tileWrappingTranslation.y())); //Setup the geometry for rendering rectToVertices(d->vertices, modelRect); d->displayShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); d->displayShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, d->vertices); rectToTexCoords(d->texCoords, textureRect); d->displayShader->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE); d->displayShader->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, d->texCoords); if (d->displayFilter) { glActiveTexture(GL_TEXTURE0 + 1); glBindTexture(GL_TEXTURE_3D, d->displayFilter->lutTexture()); d->displayShader->setUniformValue(d->displayUniformLocationTexture1, 1); } glActiveTexture(GL_TEXTURE0); tile->bindToActiveTexture(); if (SCALE_MORE_OR_EQUAL_TO(scaleX, scaleY, 2.0)) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); switch(d->filterMode) { case KisTextureTile::NearestFilterMode: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); break; case KisTextureTile::BilinearFilterMode: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); break; case KisTextureTile::TrilinearFilterMode: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); break; case KisTextureTile::HighQualityFiltering: if (SCALE_LESS_THAN(scaleX, scaleY, 0.5)) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } break; } } glDrawArrays(GL_TRIANGLES, 0, 6); } } glBindTexture(GL_TEXTURE_2D, 0); d->displayShader->release(); } void KisOpenGLCanvas2::reportShaderLinkFailedAndExit(bool result, const QString &context, const QString &log) { KisConfig cfg; if (cfg.useVerboseOpenGLDebugOutput()) { qDebug() << "GL-log:" << context << log; } if (result) return; QMessageBox::critical(this, i18nc("@title:window", "Krita"), QString(i18n("Krita could not initialize the OpenGL canvas:\n\n%1\n\n%2\n\n Krita will disable OpenGL and close now.")).arg(context).arg(log), QMessageBox::Close); cfg.setUseOpenGL(false); cfg.setCanvasState("OPENGL_FAILED"); } void KisOpenGLCanvas2::initializeCheckerShader() { if (d->canvasInitialized) return; delete d->checkerShader; d->checkerShader = new QOpenGLShaderProgram(); QString vertexShaderName; QString fragmentShaderName; if (KisOpenGL::supportsGLSL13()) { vertexShaderName = KGlobal::dirs()->findResource("data", "krita/shaders/matrix_transform.vert"); fragmentShaderName = KGlobal::dirs()->findResource("data", "krita/shaders/simple_texture.frag"); } else { vertexShaderName = KGlobal::dirs()->findResource("data", "krita/shaders/matrix_transform_legacy.vert"); fragmentShaderName = KGlobal::dirs()->findResource("data", "krita/shaders/simple_texture_legacy.frag"); } bool result; result = d->checkerShader->addShaderFromSourceFile(QOpenGLShader::Vertex, vertexShaderName); reportShaderLinkFailedAndExit(result, "Checker vertex shader", d->checkerShader->log()); result = d->checkerShader->addShaderFromSourceFile(QOpenGLShader::Fragment, fragmentShaderName); reportShaderLinkFailedAndExit(result, "Checker fragment shader", d->checkerShader->log()); d->checkerShader->bindAttributeLocation("a_vertexPosition", PROGRAM_VERTEX_ATTRIBUTE); d->checkerShader->bindAttributeLocation("a_textureCoordinate", PROGRAM_TEXCOORD_ATTRIBUTE); result = d->checkerShader->link(); reportShaderLinkFailedAndExit(result, "Checker shader (link)", d->checkerShader->log()); Q_ASSERT(d->checkerShader->isLinked()); d->checkerUniformLocationModelViewProjection = d->checkerShader->uniformLocation("modelViewProjection"); d->checkerUniformLocationTextureMatrix = d->checkerShader->uniformLocation("textureMatrix"); } QByteArray KisOpenGLCanvas2::buildFragmentShader() { QByteArray shaderText; bool haveDisplayFilter = d->displayFilter && !d->displayFilter->program().isEmpty(); bool useHiQualityFiltering = d->filterMode == KisTextureTile::HighQualityFiltering; bool haveGLSL13 = KisOpenGL::supportsGLSL13(); QString filename = haveGLSL13 && useHiQualityFiltering ? "highq_downscale" : "simple_texture"; QString legacyPostfix = !haveGLSL13 ? "_legacy" : ""; QString filterPostfix = haveDisplayFilter ? "_ocio" : ""; QString prefaceKey = QString("krita/shaders/%1%2_preface.frag.inc") .arg(filename) .arg(legacyPostfix); QString mainKey = QString("krita/shaders/%1%2_main%3.frag.inc") .arg(filename) .arg(legacyPostfix) .arg(filterPostfix); { QFile prefaceFile(KGlobal::dirs()->findResource("data", prefaceKey)); prefaceFile.open(QIODevice::ReadOnly); shaderText.append(prefaceFile.readAll()); } if (haveDisplayFilter) { shaderText.append(d->displayFilter->program().toLatin1()); } { QFile mainFile(KGlobal::dirs()->findResource("data", mainKey)); mainFile.open(QIODevice::ReadOnly); shaderText.append(mainFile.readAll()); } return shaderText; } void KisOpenGLCanvas2::initializeDisplayShader() { if (d->canvasInitialized) return; delete d->displayShader; d->displayShader = new QOpenGLShaderProgram(); bool result = d->displayShader->addShaderFromSourceCode(QOpenGLShader::Fragment, buildFragmentShader()); reportShaderLinkFailedAndExit(result, "Display fragment shader", d->displayShader->log()); if (KisOpenGL::supportsGLSL13()) { result = d->displayShader->addShaderFromSourceFile(QOpenGLShader::Vertex, KGlobal::dirs()->findResource("data", "krita/shaders/matrix_transform.vert")); } else { result = d->displayShader->addShaderFromSourceFile(QOpenGLShader::Vertex, KGlobal::dirs()->findResource("data", "krita/shaders/matrix_transform_legacy.vert")); } reportShaderLinkFailedAndExit(result, "Display vertex shader", d->displayShader->log()); d->displayShader->bindAttributeLocation("a_vertexPosition", PROGRAM_VERTEX_ATTRIBUTE); d->displayShader->bindAttributeLocation("a_textureCoordinate", PROGRAM_TEXCOORD_ATTRIBUTE); result = d->displayShader->link(); reportShaderLinkFailedAndExit(result, "Display shader (link)", d->displayShader->log()); Q_ASSERT(d->displayShader->isLinked()); d->displayUniformLocationModelViewProjection = d->displayShader->uniformLocation("modelViewProjection"); d->displayUniformLocationTextureMatrix = d->displayShader->uniformLocation("textureMatrix"); d->displayUniformLocationViewPortScale = d->displayShader->uniformLocation("viewportScale"); d->displayUniformLocationTexelSize = d->displayShader->uniformLocation("texelSize"); d->displayUniformLocationTexture0 = d->displayShader->uniformLocation("texture0"); d->displayUniformLocationTexture1 = d->displayShader->uniformLocation("texture1"); } void KisOpenGLCanvas2::slotConfigChanged() { KisConfig cfg; d->openGLImageTextures->generateCheckerTexture(createCheckersImage(cfg.checkSize())); d->openGLImageTextures->updateConfig(cfg.useOpenGLTextureBuffer(), cfg.numMipmapLevels()); d->filterMode = (KisTextureTile::FilterMode) cfg.openGLFilteringMode(); notifyConfigChanged(); } QVariant KisOpenGLCanvas2::inputMethodQuery(Qt::InputMethodQuery query) const { return processInputMethodQuery(query); } void KisOpenGLCanvas2::inputMethodEvent(QInputMethodEvent *event) { processInputMethodEvent(event); } void KisOpenGLCanvas2::renderCanvasGL() { // Draw the border (that is, clear the whole widget to the border color) QColor widgetBackgroundColor = borderColor(); glClearColor(widgetBackgroundColor.redF(), widgetBackgroundColor.greenF(), widgetBackgroundColor.blueF(), 1.0); glClear(GL_COLOR_BUFFER_BIT); drawCheckers(); drawImage(); } void KisOpenGLCanvas2::renderDecorations(QPainter *painter) { QRect boundingRect = coordinatesConverter()->imageRectInWidgetPixels().toAlignedRect(); drawDecorations(*painter, boundingRect); } void KisOpenGLCanvas2::setDisplayProfile(KisDisplayColorConverter *colorConverter) { d->openGLImageTextures->setMonitorProfile(colorConverter->monitorProfile(), colorConverter->renderingIntent(), colorConverter->conversionFlags()); } void KisOpenGLCanvas2::channelSelectionChanged(QBitArray channelFlags) { d->openGLImageTextures->setChannelFlags(channelFlags); } void KisOpenGLCanvas2::finishResizingImage(qint32 w, qint32 h) { d->openGLImageTextures->slotImageSizeChanged(w, h); } KisUpdateInfoSP KisOpenGLCanvas2::startUpdateCanvasProjection(const QRect & rc, QBitArray channelFlags) { d->openGLImageTextures->setChannelFlags(channelFlags); return d->openGLImageTextures->updateCache(rc); } QRect KisOpenGLCanvas2::updateCanvasProjection(KisUpdateInfoSP info) { // See KisQPainterCanvas::updateCanvasProjection for more info bool isOpenGLUpdateInfo = dynamic_cast(info.data()); if (isOpenGLUpdateInfo) { d->openGLImageTextures->recalculateCache(info); } return QRect(); // FIXME: Implement dirty rect for OpenGL } bool KisOpenGLCanvas2::callFocusNextPrevChild(bool next) { return focusNextPrevChild(next); } #endif // HAVE_OPENGL