diff --git a/krita/ui/canvas/kis_canvas2.cpp b/krita/ui/canvas/kis_canvas2.cpp index 40a9e38a88e..529e4352c21 100644 --- a/krita/ui/canvas/kis_canvas2.cpp +++ b/krita/ui/canvas/kis_canvas2.cpp @@ -1,931 +1,933 @@ /* This file is part of the KDE project * * Copyright (C) 2006, 2010 Boudewijn Rempt * Copyright (C) Lukáš Tvrdý , (C) 2010 * 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.. */ #include "kis_canvas2.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_tool_proxy.h" #include "kis_coordinates_converter.h" #include "kis_prescaled_projection.h" #include "kis_image.h" #include "KisDocument.h" #include "flake/kis_shape_layer.h" #include "kis_canvas_resource_provider.h" #include "KisViewManager.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_abstract_canvas_widget.h" #include "kis_qpainter_canvas.h" #include "kis_group_layer.h" #include "flake/kis_shape_controller.h" #include "kis_node_manager.h" #include "kis_selection.h" #include "kis_selection_component.h" #include "flake/kis_shape_selection.h" #include "kis_image_config.h" #include "kis_infinity_manager.h" #include "kis_signal_compressor.h" #include "kis_display_color_converter.h" #include "kis_exposure_gamma_correction_interface.h" #include "KisView.h" #include "kis_canvas_controller.h" #ifdef HAVE_OPENGL #include "opengl/kis_opengl_canvas2.h" #include "opengl/kis_opengl_image_textures.h" -#include #endif +#include "opengl/kis_opengl.h" + + #include #include #include "input/kis_input_manager.h" #include "kis_painting_assistants_decoration.h" #include "kis_canvas_updates_compressor.h" class KisCanvas2::KisCanvas2Private { public: KisCanvas2Private(KoCanvasBase *parent, KisCoordinatesConverter* coordConverter, QPointer view, KoCanvasResourceManager* resourceManager) : coordinatesConverter(coordConverter) , view(view) , canvasWidget(0) , shapeManager(new KoShapeManager(parent)) , currentCanvasIsOpenGL(false) , toolProxy(new KisToolProxy(parent)) , vastScrolling(true) , popupPalette(0) , displayColorConverter(new KisDisplayColorConverter(resourceManager, view)) { } ~KisCanvas2Private() { delete shapeManager; delete toolProxy; } KisCoordinatesConverter *coordinatesConverter; QPointerview; KisAbstractCanvasWidget *canvasWidget; KoShapeManager *shapeManager; bool currentCanvasIsOpenGL; #ifdef HAVE_OPENGL int openGLFilterMode; #endif KisToolProxy *toolProxy; #ifdef HAVE_OPENGL KisOpenGLImageTexturesSP openGLImageTextures; #endif KisPrescaledProjectionSP prescaledProjection; bool vastScrolling; KisSignalCompressor *updateSignalCompressor; QRect savedUpdateRect; QBitArray channelFlags; KisPopupPalette *popupPalette; KisDisplayColorConverter *displayColorConverter; KisCanvasUpdatesCompressor projectionUpdatesCompressor; }; KisCanvas2::KisCanvas2(KisCoordinatesConverter *coordConverter, KoCanvasResourceManager *resourceManager, QPointerview, KoShapeBasedDocumentBase *sc) : KoCanvasBase(sc, resourceManager) , m_d(new KisCanvas2Private(this, coordConverter, view, resourceManager)) { // a bit of duplication from slotConfigChanged() KisConfig cfg; m_d->vastScrolling = cfg.vastScrolling(); createCanvas(cfg.useOpenGL()); connect(view->canvasController()->proxyObject, SIGNAL(moveDocumentOffset(QPoint)), SLOT(documentOffsetMoved(QPoint))); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); /** * We switch the shape manager every time vector layer or * shape selection is activated. Flake does not expect this * and connects all the signals of the global shape manager * to the clients in the constructor. To workaround this we * forward the signals of local shape managers stored in the * vector layers to the signals of global shape manager. So the * sequence of signal deliveries is the following: * * shapeLayer.m_d.canvas.m_shapeManager.selection() -> * shapeLayer -> * shapeController -> * globalShapeManager.selection() */ KisShapeController *kritaShapeController = dynamic_cast(sc); connect(kritaShapeController, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged())); connect(kritaShapeController, SIGNAL(selectionContentChanged()), globalShapeManager(), SIGNAL(selectionContentChanged())); connect(kritaShapeController, SIGNAL(currentLayerChanged(const KoShapeLayer*)), globalShapeManager()->selection(), SIGNAL(currentLayerChanged(const KoShapeLayer*))); m_d->updateSignalCompressor = new KisSignalCompressor(10 /*ms*/, KisSignalCompressor::FIRST_ACTIVE, this); connect(m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDoCanvasUpdate())); } KisCanvas2::~KisCanvas2() { delete m_d; } void KisCanvas2::setCanvasWidget(QWidget * widget) { KisAbstractCanvasWidget *tmp = dynamic_cast(widget); Q_ASSERT_X(tmp, "setCanvasWidget", "Cannot cast the widget to a KisAbstractCanvasWidget"); if (m_d->popupPalette) { m_d->popupPalette->setParent(widget); } if(m_d->canvasWidget != 0) { tmp->setDecorations(m_d->canvasWidget->decorations()); // Redundant check for the constructor case, see below if(viewManager() != 0) viewManager()->inputManager()->removeTrackedCanvas(this); } m_d->canvasWidget = tmp; // Either tmp was null or we are being called by KisCanvas2 constructor that is called by KisView // constructor, so the view manager still doesn't exists. if(m_d->canvasWidget != 0 && viewManager() != 0) viewManager()->inputManager()->addTrackedCanvas(this); if (!m_d->canvasWidget->decoration(INFINITY_DECORATION_ID)) { KisInfinityManager *manager = new KisInfinityManager(m_d->view, this); manager->setVisible(true); m_d->canvasWidget->addDecoration(manager); } widget->setAutoFillBackground(false); widget->setAttribute(Qt::WA_OpaquePaintEvent); widget->setMouseTracking(true); widget->setAcceptDrops(true); KoCanvasControllerWidget *controller = dynamic_cast(canvasController()); if (controller) { Q_ASSERT(controller->canvas() == this); controller->changeCanvasWidget(widget); } } bool KisCanvas2::canvasIsOpenGL() { return m_d->currentCanvasIsOpenGL; } void KisCanvas2::gridSize(qreal *horizontal, qreal *vertical) const { Q_ASSERT(horizontal); Q_ASSERT(vertical); QTransform transform = coordinatesConverter()->imageToDocumentTransform(); QPointF size = transform.map(QPointF(m_d->view->document()->gridData().gridX(), m_d->view->document()->gridData().gridY())); *horizontal = size.x(); *vertical = size.y(); } bool KisCanvas2::snapToGrid() const { return m_d->view->document()->gridData().snapToGrid(); } qreal KisCanvas2::rotationAngle() const { return m_d->coordinatesConverter->rotationAngle(); } void KisCanvas2::channelSelectionChanged() { KisImageWSP image = this->image(); m_d->channelFlags = image->rootLayer()->channelFlags(); image->barrierLock(); if (m_d->currentCanvasIsOpenGL) { #ifdef HAVE_OPENGL Q_ASSERT(m_d->openGLImageTextures); m_d->openGLImageTextures->setChannelFlags(m_d->channelFlags); #else Q_ASSERT_X(0, "KisCanvas2::setChannelFlags", "Bad use of setChannelFlags(). It shouldn't have happened =("); #endif } else { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->setChannelFlags(m_d->channelFlags); } startUpdateInPatches(image->bounds()); image->unlock(); } void KisCanvas2::addCommand(KUndo2Command *command) { // This method exists to support flake-related operations m_d->view->document()->addCommand(command); } KoShapeManager* KisCanvas2::shapeManager() const { if (!viewManager()) return m_d->shapeManager; if (!viewManager()->nodeManager()) return m_d->shapeManager; KisLayerSP activeLayer = viewManager()->nodeManager()->activeLayer(); if (activeLayer && activeLayer->isEditable()) { KisShapeLayer * shapeLayer = dynamic_cast(activeLayer.data()); if (shapeLayer) { return shapeLayer->shapeManager(); } KisSelectionSP selection = activeLayer->selection(); if (selection && !selection.isNull()) { if (selection->hasShapeSelection()) { KoShapeManager* m = dynamic_cast(selection->shapeSelection())->shapeManager(); return m; } } } return m_d->shapeManager; } KoShapeManager * KisCanvas2::globalShapeManager() const { return m_d->shapeManager; } void KisCanvas2::updateInputMethodInfo() { // TODO call (the protected) QWidget::updateMicroFocus() on the proper canvas widget... } const KisCoordinatesConverter* KisCanvas2::coordinatesConverter() const { return m_d->coordinatesConverter; } KoViewConverter* KisCanvas2::viewConverter() const { return m_d->coordinatesConverter; } KisInputManager* KisCanvas2::globalInputManager() const { return m_d->view->globalInputManager(); } QWidget* KisCanvas2::canvasWidget() { return m_d->canvasWidget->widget(); } const QWidget* KisCanvas2::canvasWidget() const { return m_d->canvasWidget->widget(); } KoUnit KisCanvas2::unit() const { KoUnit unit(KoUnit::Pixel); KisImageWSP image = m_d->view->image(); if (image) { if (!qFuzzyCompare(image->xRes(), image->yRes())) { qWarning() << "WARNING: resolution of the image is anisotropic" << ppVar(image->xRes()) << ppVar(image->yRes()); } const qreal resolution = image->xRes(); unit.setFactor(resolution); } return unit; } KoToolProxy * KisCanvas2::toolProxy() const { return m_d->toolProxy; } void KisCanvas2::createQPainterCanvas() { m_d->currentCanvasIsOpenGL = false; KisQPainterCanvas * canvasWidget = new KisQPainterCanvas(this, m_d->coordinatesConverter, m_d->view); m_d->prescaledProjection = new KisPrescaledProjection(); m_d->prescaledProjection->setCoordinatesConverter(m_d->coordinatesConverter); m_d->prescaledProjection->setMonitorProfile(m_d->displayColorConverter->monitorProfile(), m_d->displayColorConverter->renderingIntent(), m_d->displayColorConverter->conversionFlags()); m_d->prescaledProjection->setDisplayFilter(m_d->displayColorConverter->displayFilter()); canvasWidget->setPrescaledProjection(m_d->prescaledProjection); setCanvasWidget(canvasWidget); } void KisCanvas2::createOpenGLCanvas() { #ifdef HAVE_OPENGL KisConfig cfg; m_d->openGLFilterMode = cfg.openGLFilteringMode(); m_d->currentCanvasIsOpenGL = true; m_d->openGLImageTextures = KisOpenGLImageTextures::getImageTextures(m_d->view->image(), m_d->displayColorConverter->monitorProfile(), m_d->displayColorConverter->renderingIntent(), m_d->displayColorConverter->conversionFlags()); KisOpenGLCanvas2 *canvasWidget = new KisOpenGLCanvas2(this, m_d->coordinatesConverter, 0, m_d->openGLImageTextures); canvasWidget->setDisplayFilter(m_d->displayColorConverter->displayFilter()); setCanvasWidget(canvasWidget); #else qFatal("Bad use of createOpenGLCanvas(). It shouldn't have happened =("); #endif } void KisCanvas2::createCanvas(bool useOpenGL) { KisConfig cfg; QDesktopWidget dw; const KoColorProfile *profile = cfg.displayProfile(dw.screenNumber(imageView())); m_d->displayColorConverter->setMonitorProfile(profile); if (useOpenGL) { #ifdef HAVE_OPENGL - if (QGLFormat::hasOpenGL()) { + if (KisOpenGL::hasOpenGL()) { createOpenGLCanvas(); if (cfg.canvasState() == "OPENGL_FAILED") { // Creating the opengl canvas failed, fall back createQPainterCanvas(); } } else { warnKrita << "Tried to create OpenGL widget when system doesn't have OpenGL\n"; createQPainterCanvas(); } #else warnKrita << "OpenGL requested while it's not available, starting qpainter canvas"; createQPainterCanvas(); #endif } else { #ifdef HAVE_OPENGL // Free shared pointer m_d->openGLImageTextures = 0; #endif createQPainterCanvas(); } if (m_d->popupPalette) { m_d->popupPalette->setParent(m_d->canvasWidget->widget()); } } void KisCanvas2::initializeImage() { KisImageWSP image = m_d->view->image(); m_d->coordinatesConverter->setImage(image); connect(image, SIGNAL(sigImageUpdated(QRect)), SLOT(startUpdateCanvasProjection(QRect)), Qt::DirectConnection); connect(this, SIGNAL(sigCanvasCacheUpdated()), SLOT(updateCanvasProjection())); connect(image, SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), SLOT(startResizingImage()), Qt::DirectConnection); connect(this, SIGNAL(sigContinueResizeImage(qint32,qint32)), SLOT(finishResizingImage(qint32,qint32))); connectCurrentCanvas(); } void KisCanvas2::connectCurrentCanvas() { KisImageWSP image = m_d->view->image(); if (!m_d->currentCanvasIsOpenGL) { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->setImage(image); } startResizingImage(); emit imageChanged(image); } void KisCanvas2::disconnectCurrentCanvas() { if (m_d->currentCanvasIsOpenGL) { #ifdef HAVE_OPENGL Q_ASSERT(m_d->openGLImageTextures); m_d->openGLImageTextures->disconnect(this); m_d->openGLImageTextures->disconnect(m_d->view->image()); #else qFatal("Bad use of disconnectCurrentCanvas(). It shouldn't have happened =("); #endif } } void KisCanvas2::resetCanvas(bool useOpenGL) { // we cannot reset the canvas before it's created, but this method might be called, // for instance when setting the monitor profile. if (!m_d->canvasWidget) { return; } #ifdef HAVE_OPENGL KisConfig cfg; bool needReset = (m_d->currentCanvasIsOpenGL != useOpenGL) || (m_d->currentCanvasIsOpenGL && m_d->openGLFilterMode != cfg.openGLFilteringMode()); if (needReset) { disconnectCurrentCanvas(); createCanvas(useOpenGL); connectCurrentCanvas(); notifyZoomChanged(); } #else Q_UNUSED(useOpenGL) #endif updateCanvasWidgetImpl(); } void KisCanvas2::startUpdateInPatches(QRect imageRect) { if (m_d->currentCanvasIsOpenGL) { startUpdateCanvasProjection(imageRect); } else { KisImageConfig imageConfig; int patchWidth = imageConfig.updatePatchWidth(); int patchHeight = imageConfig.updatePatchHeight(); for (int y = 0; y < imageRect.height(); y += patchHeight) { for (int x = 0; x < imageRect.width(); x += patchWidth) { QRect patchRect(x, y, patchWidth, patchHeight); startUpdateCanvasProjection(patchRect); } } } } void KisCanvas2::setDisplayFilter(KisDisplayFilter *displayFilter) { m_d->displayColorConverter->setDisplayFilter(displayFilter); if (m_d->currentCanvasIsOpenGL) { #ifdef HAVE_OPENGL KisImageWSP image = this->image(); image->barrierLock(); bool needsInternalColorManagement = !displayFilter || displayFilter->useInternalColorManagement(); bool needsFullRefresh = m_d->openGLImageTextures-> setInternalColorManagementActive(needsInternalColorManagement); m_d->canvasWidget->setDisplayFilter(displayFilter); if (needsFullRefresh) { startUpdateInPatches(image->bounds()); } else { updateCanvas(); } image->unlock(); #endif } else { KisImageWSP image = this->image(); image->barrierLock(); Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->setDisplayFilter(displayFilter); startUpdateInPatches(image->bounds()); image->unlock(); } } KisDisplayFilter *KisCanvas2::displayFilter() const { return m_d->displayColorConverter->displayFilter(); } KisDisplayColorConverter* KisCanvas2::displayColorConverter() const { return m_d->displayColorConverter; } KisExposureGammaCorrectionInterface* KisCanvas2::exposureGammaCorrectionInterface() const { KisDisplayFilter *displayFilter = m_d->displayColorConverter->displayFilter(); return displayFilter ? displayFilter->correctionInterface() : KisDumbExposureGammaCorrectionInterface::instance(); } void KisCanvas2::startResizingImage() { KisImageWSP image = this->image(); qint32 w = image->width(); qint32 h = image->height(); emit sigContinueResizeImage(w, h); QRect imageBounds(0, 0, w, h); startUpdateInPatches(imageBounds); } void KisCanvas2::finishResizingImage(qint32 w, qint32 h) { if (m_d->currentCanvasIsOpenGL) { #ifdef HAVE_OPENGL Q_ASSERT(m_d->openGLImageTextures); m_d->openGLImageTextures->slotImageSizeChanged(w, h); #else Q_ASSERT_X(0, "finishResizingImage()", "Bad use of finishResizingImage(). It shouldn't have happened =("); #endif } else { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->slotImageSizeChanged(w, h); } } void KisCanvas2::startUpdateCanvasProjection(const QRect & rc) { KisUpdateInfoSP info; if (m_d->currentCanvasIsOpenGL) { #ifdef HAVE_OPENGL Q_ASSERT(m_d->openGLImageTextures); m_d->openGLImageTextures->setChannelFlags(m_d->channelFlags); info = m_d->openGLImageTextures->updateCache(rc); #else Q_ASSERT_X(0, "startUpdateCanvasProjection()", "Bad use of startUpdateCanvasProjection(). It shouldn't have happened =("); #endif } else { Q_ASSERT(m_d->prescaledProjection); info = m_d->prescaledProjection->updateCache(rc); } if (m_d->projectionUpdatesCompressor.putUpdateInfo(info)) { emit sigCanvasCacheUpdated(); } } void KisCanvas2::updateCanvasProjection() { KisUpdateInfoSP info; while (info = m_d->projectionUpdatesCompressor.takeUpdateInfo()) { /** * 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. */ #ifdef HAVE_OPENGL bool isOpenGLUpdateInfo = dynamic_cast(info.data()); if (isOpenGLUpdateInfo != m_d->currentCanvasIsOpenGL) continue; #endif if (m_d->currentCanvasIsOpenGL) { #ifdef HAVE_OPENGL Q_ASSERT(m_d->openGLImageTextures); m_d->openGLImageTextures->recalculateCache(info); /** * FIXME: Please not update entire canvas * Implement info->dirtyViewportRect() */ updateCanvasWidgetImpl(); #else Q_ASSERT_X(0, "updateCanvasProjection()", "Bad use of updateCanvasProjection(). It shouldn't have happened =("); #endif } else { // See comment in startUpdateCanvasProjection() Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->recalculateCache(info); QRect vRect = m_d->coordinatesConverter-> viewportToWidget(info->dirtyViewportRect()).toAlignedRect(); if (!vRect.isEmpty()) { updateCanvasWidgetImpl(vRect); } } } } void KisCanvas2::slotDoCanvasUpdate() { if (m_d->canvasWidget->isBusy()) { // just restarting the timer updateCanvasWidgetImpl(m_d->savedUpdateRect); return; } if (m_d->savedUpdateRect.isEmpty()) { m_d->canvasWidget->widget()->update(); emit updateCanvasRequested(m_d->canvasWidget->widget()->rect()); } else { emit updateCanvasRequested(m_d->savedUpdateRect); m_d->canvasWidget->widget()->update(m_d->savedUpdateRect); } m_d->savedUpdateRect = QRect(); } void KisCanvas2::updateCanvasWidgetImpl(const QRect &rc) { if (!m_d->updateSignalCompressor->isActive() || !m_d->savedUpdateRect.isEmpty()) { m_d->savedUpdateRect |= rc; } m_d->updateSignalCompressor->start(); } void KisCanvas2::updateCanvas() { updateCanvasWidgetImpl(); } void KisCanvas2::updateCanvas(const QRectF& documentRect) { if (m_d->currentCanvasIsOpenGL && m_d->canvasWidget->decorations().size() > 0) { updateCanvasWidgetImpl(); } else { // updateCanvas is called from tools, never from the projection // updates, so no need to prescale! QRect widgetRect = m_d->coordinatesConverter->documentToWidget(documentRect).toAlignedRect(); widgetRect.adjust(-2, -2, 2, 2); if (!widgetRect.isEmpty()) { updateCanvasWidgetImpl(widgetRect); } } } void KisCanvas2::disconnectCanvasObserver(QObject *object) { KoCanvasBase::disconnectCanvasObserver(object); m_d->view->disconnect(object); } void KisCanvas2::notifyZoomChanged() { if (!m_d->currentCanvasIsOpenGL) { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->notifyZoomChanged(); } updateCanvas(); // update the canvas, because that isn't done when zooming using KoZoomAction } void KisCanvas2::preScale() { if (!m_d->currentCanvasIsOpenGL) { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->preScale(); } } const KoColorProfile * KisCanvas2::monitorProfile() { return m_d->displayColorConverter->monitorProfile(); } KisViewManager* KisCanvas2::viewManager() const { if (m_d->view) { return m_d->view->viewManager(); } return 0; } QPointerKisCanvas2::imageView() const { return m_d->view; } KisImageWSP KisCanvas2::image() const { return m_d->view->image(); } KisImageWSP KisCanvas2::currentImage() const { return m_d->view->image(); } void KisCanvas2::documentOffsetMoved(const QPoint &documentOffset) { QPointF offsetBefore = m_d->coordinatesConverter->imageRectInViewportPixels().topLeft(); m_d->coordinatesConverter->setDocumentOffset(documentOffset); QPointF offsetAfter = m_d->coordinatesConverter->imageRectInViewportPixels().topLeft(); QPointF moveOffset = offsetAfter - offsetBefore; if (!m_d->currentCanvasIsOpenGL) m_d->prescaledProjection->viewportMoved(moveOffset); emit documentOffsetUpdateFinished(); updateCanvas(); } void KisCanvas2::slotConfigChanged() { KisConfig cfg; m_d->vastScrolling = cfg.vastScrolling(); resetCanvas(cfg.useOpenGL()); } void KisCanvas2::slotSetDisplayProfile(const KoColorProfile * monitorProfile) { if (m_d->displayColorConverter->monitorProfile() == monitorProfile) return; m_d->displayColorConverter->setMonitorProfile(monitorProfile); KisImageWSP image = this->image(); image->barrierLock(); if (m_d->currentCanvasIsOpenGL) { #ifdef HAVE_OPENGL Q_ASSERT(m_d->openGLImageTextures); m_d->openGLImageTextures->setMonitorProfile(m_d->displayColorConverter->monitorProfile(), m_d->displayColorConverter->renderingIntent(), m_d->displayColorConverter->conversionFlags()); #else Q_ASSERT_X(0, "KisCanvas2::setMonitorProfile", "Bad use of setMonitorProfile(). It shouldn't have happened =("); #endif } else { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->setMonitorProfile(m_d->displayColorConverter->monitorProfile(), m_d->displayColorConverter->renderingIntent(), m_d->displayColorConverter->conversionFlags()); } startUpdateInPatches(image->bounds()); image->unlock(); } void KisCanvas2::addDecoration(KisCanvasDecoration* deco) { m_d->canvasWidget->addDecoration(deco); } KisCanvasDecoration* KisCanvas2::decoration(const QString& id) const { return m_d->canvasWidget->decoration(id); } QPoint KisCanvas2::documentOrigin() const { /** * In Krita we don't use document origin anymore. * All the centering when needed (vastScrolling < 0.5) is done * automatically by the KisCoordinatesConverter. */ return QPoint(); } QPoint KisCanvas2::documentOffset() const { return m_d->coordinatesConverter->documentOffset(); } void KisCanvas2::setFavoriteResourceManager(KisFavoriteResourceManager* favoriteResourceManager) { m_d->popupPalette = new KisPopupPalette(favoriteResourceManager, displayColorConverter()->displayRendererInterface(), m_d->canvasWidget->widget()); m_d->popupPalette->showPopupPalette(false); } void KisCanvas2::setCursor(const QCursor &cursor) { canvasWidget()->setCursor(cursor); } void KisCanvas2::slotSelectionChanged() { KisShapeLayer* shapeLayer = dynamic_cast(viewManager()->activeLayer().data()); if (!shapeLayer) { return; } m_d->shapeManager->selection()->deselectAll(); foreach(KoShape* shape, shapeLayer->shapeManager()->selection()->selectedShapes()) { m_d->shapeManager->selection()->select(shape); } } bool KisCanvas2::isPopupPaletteVisible() { if (!m_d->popupPalette) { return false; } return m_d->popupPalette->isVisible(); } void KisCanvas2::setWrapAroundViewingMode(bool value) { KisCanvasDecoration *infinityDecoration = m_d->canvasWidget->decoration(INFINITY_DECORATION_ID); if (infinityDecoration) { infinityDecoration->setVisible(!value); } m_d->canvasWidget->setWrapAroundViewingMode(value); } bool KisCanvas2::wrapAroundViewingMode() const { KisCanvasDecoration *infinityDecoration = m_d->canvasWidget->decoration(INFINITY_DECORATION_ID); if (infinityDecoration) { return !(infinityDecoration->visible()); } return false; } KoGuidesData *KisCanvas2::guidesData() { return &m_d->view->document()->guidesData(); } void KisCanvas2::slotShowPopupPalette(const QPoint &p) { if (!m_d->popupPalette) { return; } m_d->popupPalette->showPopupPalette(p); } KisPaintingAssistantsDecoration* KisCanvas2::paintingAssistantsDecoration() { KisCanvasDecoration* deco = decoration("paintingAssistantsDecoration"); return qobject_cast(deco); } diff --git a/krita/ui/dialogs/kis_dlg_preferences.cc b/krita/ui/dialogs/kis_dlg_preferences.cc index cd379e51a73..7c8f5312a42 100644 --- a/krita/ui/dialogs/kis_dlg_preferences.cc +++ b/krita/ui/dialogs/kis_dlg_preferences.cc @@ -1,1128 +1,1122 @@ /* * preferencesdlg.cc - part of KImageShop * * Copyright (c) 1999 Michael Koch * Copyright (c) 2003-2011 Boudewijn Rempt * * 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_dlg_preferences.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include - -#ifdef HAVE_OPENGL -#include -#endif - #include #include #include #include #include #include #include #include #include "KoID.h" #include #include #include #include #include - #include "widgets/squeezedcombobox.h" #include "kis_clipboard.h" #include "widgets/kis_cmb_idlist.h" #include "KoColorSpace.h" #include "KoColorSpaceRegistry.h" #include "kis_cursor.h" #include "kis_config.h" #include "kis_canvas_resource_provider.h" #include "kis_preference_set_registry.h" #include "kis_factory2.h" #include "kis_color_manager.h" #include "slider_and_spin_box_sync.h" // for the performance update #include #include "input/config/kis_input_configuration_page.h" GeneralTab::GeneralTab(QWidget *_parent, const char *_name) : WdgGeneralSettings(_parent, _name) { KisConfig cfg; m_cmbCursorShape->addItem(i18n("No Cursor")); m_cmbCursorShape->addItem(i18n("Tool Icon")); m_cmbCursorShape->addItem(i18n("Arrow")); m_cmbCursorShape->addItem(i18n("Small Circle")); m_cmbCursorShape->addItem(i18n("Crosshair")); m_cmbCursorShape->addItem(i18n("Triangle Righthanded")); m_cmbCursorShape->addItem(i18n("Triangle Lefthanded")); m_cmbOutlineShape->addItem(i18n("No Outline")); m_cmbOutlineShape->addItem(i18n("Circle Outline")); m_cmbOutlineShape->addItem(i18n("Preview Outline")); m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle()); m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle()); chkShowRootLayer->setChecked(cfg.showRootLayer()); int autosaveInterval = cfg.autoSaveInterval(); //convert to minutes m_autosaveSpinBox->setValue(autosaveInterval / 60); m_autosaveCheckBox->setChecked(autosaveInterval > 0); m_undoStackSize->setValue(cfg.undoStackLimit()); m_backupFileCheckBox->setChecked(cfg.backupFile()); m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting()); m_hideSplashScreen->setChecked(cfg.hideSplashScreen()); m_cmbMDIType->setCurrentIndex(cfg.readEntry("mdi_viewmode", (int)QMdiArea::TabbedView)); m_chkRubberBand->setChecked(cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); m_favoritePresetsSpinBox->setValue(cfg.favoritePresets()); m_mdiColor->setColor(cfg.getMDIBackgroundColor()); m_backgroundimage->setText(cfg.getMDIBackgroundImage()); m_chkCanvasMessages->setChecked(cfg.showCanvasMessages()); m_chkCompressKra->setChecked(cfg.compressKra()); m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker()); connect(m_bnFileName, SIGNAL(clicked()), SLOT(getBackgroundImage())); connect(clearBgImageButton, SIGNAL(clicked()), SLOT(clearBackgroundImage())); } void GeneralTab::setDefault() { KisConfig cfg; m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle(true)); m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle(true)); chkShowRootLayer->setChecked(cfg.showRootLayer(true)); m_autosaveCheckBox->setChecked(cfg.autoSaveInterval(true) > 0); //convert to minutes m_autosaveSpinBox->setValue(cfg.autoSaveInterval(true) / 60); m_undoStackSize->setValue(cfg.undoStackLimit(true)); m_backupFileCheckBox->setChecked(cfg.backupFile(true)); m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting(true)); m_hideSplashScreen->setChecked(cfg.hideSplashScreen(true)); m_cmbMDIType->setCurrentIndex((int)QMdiArea::TabbedView); m_chkRubberBand->setChecked(cfg.useOpenGL(true)); m_favoritePresetsSpinBox->setValue(cfg.favoritePresets(true)); m_mdiColor->setColor(cfg.getMDIBackgroundColor(true)); m_backgroundimage->setText(cfg.getMDIBackgroundImage(true)); m_chkCanvasMessages->setChecked(cfg.showCanvasMessages(true)); m_chkCompressKra->setChecked(cfg.compressKra(true)); m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker(true)); } CursorStyle GeneralTab::cursorStyle() { return (CursorStyle)m_cmbCursorShape->currentIndex(); } OutlineStyle GeneralTab::outlineStyle() { return (OutlineStyle)m_cmbOutlineShape->currentIndex(); } bool GeneralTab::showRootLayer() { return chkShowRootLayer->isChecked(); } int GeneralTab::autoSaveInterval() { //convert to seconds return m_autosaveCheckBox->isChecked() ? m_autosaveSpinBox->value()*60 : 0; } int GeneralTab::undoStackSize() { return m_undoStackSize->value(); } bool GeneralTab::showOutlineWhilePainting() { return m_showOutlinePainting->isChecked(); } bool GeneralTab::hideSplashScreen() { return m_hideSplashScreen->isChecked(); } int GeneralTab::mdiMode() { return m_cmbMDIType->currentIndex(); } int GeneralTab::favoritePresets() { return m_favoritePresetsSpinBox->value(); } bool GeneralTab::showCanvasMessages() { return m_chkCanvasMessages->isChecked(); } bool GeneralTab::compressKra() { return m_chkCompressKra->isChecked(); } bool GeneralTab::toolOptionsInDocker() { return m_radioToolOptionsInDocker->isChecked(); } void GeneralTab::getBackgroundImage() { KoFileDialog dialog(this, KoFileDialog::OpenFile, "BackgroundImages"); dialog.setCaption(i18n("Select a Background Image")); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); dialog.setImageFilters(); QString fn = dialog.url(); // dialog box was canceled or somehow no file was selected if (fn.isEmpty()) { return; } QImage image(fn); if (image.isNull()) { QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("%1 is not a valid image file!", fn)); } else { m_backgroundimage->setText(fn); } } void GeneralTab::clearBackgroundImage() { // clearing the background image text will implicitly make the background color be used m_backgroundimage->setText(""); } ColorSettingsTab::ColorSettingsTab(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); // XXX: Make sure only profiles that fit the specified color model // are shown in the profile combos QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgColorSettings(this); l->addWidget(m_page, 0, 0); KisConfig cfg; m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile()); connect(m_page->chkUseSystemMonitorProfile, SIGNAL(toggled(bool)), this, SLOT(toggleAllowMonitorProfileSelection(bool))); // XXX: no color management integration on Windows or OSX yet #ifndef HAVE_X11 m_page->chkUseSystemMonitorProfile->setVisible(false); #endif m_page->cmbWorkingColorSpace->setIDList(KoColorSpaceRegistry::instance()->listKeys()); m_page->cmbWorkingColorSpace->setCurrent(cfg.workingColorSpace()); m_page->cmbPrintingColorSpace->setIDList(KoColorSpaceRegistry::instance()->listKeys()); m_page->cmbPrintingColorSpace->setCurrent(cfg.printerColorSpace()); m_page->bnAddColorProfile->setIcon(koIcon("document-open")); m_page->bnAddColorProfile->setToolTip( i18n("Open Color Profile") ); connect(m_page->bnAddColorProfile, SIGNAL(clicked()), SLOT(installProfile())); refillPrintProfiles(KoID(cfg.printerColorSpace(), "")); //hide printing settings m_page->groupBox2->hide(); QGridLayout *monitorProfileGrid = new QGridLayout(m_page->monitorprofileholder); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { QLabel *lbl = new QLabel(i18nc("The number of the screen", "Screen: %1", i + 1)); monitorProfileGrid->addWidget(lbl, i, 0); m_monitorProfileLabels << lbl; SqueezedComboBox *cmb = new SqueezedComboBox(); monitorProfileGrid->addWidget(cmb, i, 1); m_monitorProfileWidgets << cmb; } refillMonitorProfiles(KoID("RGBA", "")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } if (m_page->cmbPrintProfile->contains(cfg.printerProfile())) m_page->cmbPrintProfile->setCurrentIndex(m_page->cmbPrintProfile->findText(cfg.printerProfile())); m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation()); m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization()); m_pasteBehaviourGroup.addButton(m_page->radioPasteWeb, PASTE_ASSUME_WEB); m_pasteBehaviourGroup.addButton(m_page->radioPasteMonitor, PASTE_ASSUME_MONITOR); m_pasteBehaviourGroup.addButton(m_page->radioPasteAsk, PASTE_ASK); QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour()); Q_ASSERT(button); if (button) { button->setChecked(true); } m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent()); toggleAllowMonitorProfileSelection(cfg.useSystemMonitorProfile()); connect(m_page->cmbPrintingColorSpace, SIGNAL(activated(const KoID &)), this, SLOT(refillPrintProfiles(const KoID &))); } void ColorSettingsTab::installProfile() { QStringList mime; mime << "ICM Profile (*.icm)" << "ICC Profile (*.icc)"; KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC"); dialog.setCaption(i18n("Install Color Profiles")); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); dialog.setNameFilters(mime); QStringList profileNames = dialog.urls(); KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); Q_ASSERT(iccEngine); QString saveLocation = KGlobal::dirs()->saveLocation("icc_profiles"); foreach (const QString &profileName, profileNames) { KUrl file(profileName); if (!QFile::copy(profileName, saveLocation + file.fileName())) { kWarning() << "Could not install profile!"; return; } iccEngine->addProfile(saveLocation + file.fileName()); } KisConfig cfg; refillMonitorProfiles(KoID("RGBA", "")); refillPrintProfiles(KoID(cfg.printerColorSpace(), "")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } if (m_page->cmbPrintProfile->contains(cfg.printerProfile())) m_page->cmbPrintProfile->setCurrentIndex(m_page->cmbPrintProfile->findText(cfg.printerProfile())); } void ColorSettingsTab::toggleAllowMonitorProfileSelection(bool useSystemProfile) { if (useSystemProfile) { KisConfig cfg; QStringList devices = KisColorManager::instance()->devices(); if (devices.size() == QApplication::desktop()->screenCount()) { for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->clear(); QString monitorForScreen = cfg.monitorForScreen(i, devices[i]); foreach(const QString &device, devices) { m_monitorProfileLabels[i]->setText(i18nc("The display/screen we got from Qt", "Screen %1:", i + 1)); m_monitorProfileWidgets[i]->addSqueezedItem(KisColorManager::instance()->deviceName(device), device); if (devices[i] == monitorForScreen) { m_monitorProfileWidgets[i]->setCurrentIndex(i); } } } } } else { KisConfig cfg; refillMonitorProfiles(KoID("RGBA", "")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } } } void ColorSettingsTab::setDefault() { m_page->cmbWorkingColorSpace->setCurrent("RGBA"); m_page->cmbPrintingColorSpace->setCurrent("CMYK"); refillPrintProfiles(KoID("CMYK", "")); refillMonitorProfiles(KoID("RGBA", "")); KisConfig cfg; m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation(true)); m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization(true)); m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent(true)); m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile(true)); QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour(true)); Q_ASSERT(button); if (button) { button->setChecked(true); } } void ColorSettingsTab::refillMonitorProfiles(const KoID & s) { const KoColorSpaceFactory * csf = KoColorSpaceRegistry::instance()->colorSpaceFactory(s.id()); for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->clear(); } if (!csf) return; QList profileList = KoColorSpaceRegistry::instance()->profilesFor(csf); foreach (const KoColorProfile *profile, profileList) { // qDebug() << "Profile" << profile->name() << profile->isSuitableForDisplay() << csf->defaultProfile(); if (profile->isSuitableForDisplay()) { for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->addSqueezedItem(profile->name()); } } } for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileLabels[i]->setText(i18nc("The number of the screen", "Screen: %1", i + 1)); m_monitorProfileWidgets[i]->setCurrent(csf->defaultProfile()); } } void ColorSettingsTab::refillPrintProfiles(const KoID & s) { const KoColorSpaceFactory * csf = KoColorSpaceRegistry::instance()->colorSpaceFactory(s.id()); m_page->cmbPrintProfile->clear(); if (!csf) return; QList profileList = KoColorSpaceRegistry::instance()->profilesFor(csf); foreach(const KoColorProfile *profile, profileList) { if (profile->isSuitableForPrinting()) m_page->cmbPrintProfile->addSqueezedItem(profile->name()); } m_page->cmbPrintProfile->setCurrent(csf->defaultProfile()); } //--------------------------------------------------------------------------------------------------- void TabletSettingsTab::setDefault() { KisCubicCurve curve; curve.fromString(DEFAULT_CURVE_STRING); m_page->pressureCurve->setCurve(curve); } TabletSettingsTab::TabletSettingsTab(QWidget* parent, const char* name): QWidget(parent) { setObjectName(name); QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgTabletSettings(this); l->addWidget(m_page, 0, 0); KisConfig cfg; KisCubicCurve curve; curve.fromString( cfg.pressureTabletCurve() ); m_page->pressureCurve->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); m_page->pressureCurve->setCurve(curve); } //--------------------------------------------------------------------------------------------------- #include "kis_image_config.h" #include "kis_acyclic_signal_connector.h" int getTotalRAM() { KisImageConfig cfg; return cfg.totalRAM(); } int PerformanceTab::realTilesRAM() { return intMemoryLimit->value() - intPoolLimit->value(); } PerformanceTab::PerformanceTab(QWidget *parent, const char *name) : WdgPerformanceSettings(parent, name) { KisImageConfig cfg; const int totalRAM = cfg.totalRAM(); lblTotalMemory->setText(i18n("%1 MiB", totalRAM)); sliderMemoryLimit->setSuffix(" %"); sliderMemoryLimit->setRange(1, 100, 2); sliderMemoryLimit->setSingleStep(0.01); sliderPoolLimit->setSuffix(" %"); sliderPoolLimit->setRange(0, 20, 2); sliderMemoryLimit->setSingleStep(0.01); sliderUndoLimit->setSuffix(" %"); sliderUndoLimit->setRange(0, 50, 2); sliderMemoryLimit->setSingleStep(0.01); intMemoryLimit->setMinimumWidth(80); intPoolLimit->setMinimumWidth(80); intUndoLimit->setMinimumWidth(80); SliderAndSpinBoxSync *sync1 = new SliderAndSpinBoxSync(sliderMemoryLimit, intMemoryLimit, getTotalRAM); sync1->slotParentValueChanged(); m_syncs << sync1; SliderAndSpinBoxSync *sync2 = new SliderAndSpinBoxSync(sliderPoolLimit, intPoolLimit, boost::bind(&QSpinBox::value, intMemoryLimit)); connect(intMemoryLimit, SIGNAL(valueChanged(int)), sync2, SLOT(slotParentValueChanged())); sync2->slotParentValueChanged(); m_syncs << sync2; SliderAndSpinBoxSync *sync3 = new SliderAndSpinBoxSync(sliderUndoLimit, intUndoLimit, boost::bind(&PerformanceTab::realTilesRAM, this)); connect(intPoolLimit, SIGNAL(valueChanged(int)), sync3, SLOT(slotParentValueChanged())); sync3->slotParentValueChanged(); m_syncs << sync3; sliderSwapSize->setSuffix(i18n(" GiB")); sliderSwapSize->setRange(1, 64); intSwapSize->setRange(1, 64); KisAcyclicSignalConnector *swapSizeConnector = new KisAcyclicSignalConnector(this); swapSizeConnector->connectForwardInt(sliderSwapSize, SIGNAL(valueChanged(int)), intSwapSize, SLOT(setValue(int))); swapSizeConnector->connectBackwardInt(intSwapSize, SIGNAL(valueChanged(int)), sliderSwapSize, SLOT(setValue(int))); lblSwapFileLocation->setText(cfg.swapDir()); connect(bnSwapFile, SIGNAL(clicked()), SLOT(selectSwapDir())); load(false); } PerformanceTab::~PerformanceTab() { qDeleteAll(m_syncs); } void PerformanceTab::load(bool requestDefault) { KisImageConfig cfg; sliderMemoryLimit->setValue(cfg.memoryHardLimitPercent(requestDefault)); sliderPoolLimit->setValue(cfg.memoryPoolLimitPercent(requestDefault)); sliderUndoLimit->setValue(cfg.memorySoftLimitPercent(requestDefault)); chkPerformanceLogging->setChecked(cfg.enablePerfLog(requestDefault)); sliderSwapSize->setValue(cfg.maxSwapSize(requestDefault) / 1024); lblSwapFileLocation->setText(cfg.swapDir(requestDefault)); } void PerformanceTab::save() { KisImageConfig cfg; cfg.setMemoryHardLimitPercent(sliderMemoryLimit->value()); cfg.setMemorySoftLimitPercent(sliderUndoLimit->value()); cfg.setMemoryPoolLimitPercent(sliderPoolLimit->value()); cfg.setEnablePerfLog(chkPerformanceLogging->isChecked()); cfg.setMaxSwapSize(sliderSwapSize->value() * 1024); cfg.setSwapDir(lblSwapFileLocation->text()); } void PerformanceTab::selectSwapDir() { KisImageConfig cfg; QString swapDir = cfg.swapDir(); swapDir = QFileDialog::getExistingDirectory(0, i18nc("@title:window", "Select a swap directory"), swapDir); lblSwapFileLocation->setText(swapDir); } //--------------------------------------------------------------------------------------------------- #include "KoColor.h" #include "KoColorPopupAction.h" DisplaySettingsTab::DisplaySettingsTab(QWidget *parent, const char *name) : WdgDisplaySettings(parent, name) { KisConfig cfg; #ifdef HAVE_OPENGL - if (!QGLFormat::hasOpenGL()) { + if (!KisOpenGL::hasOpenGL()) { grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); chkUseTextureBuffer->setEnabled(false); chkDisableDoubleBuffering->setEnabled(false); chkDisableVsync->setEnabled(false); cmbFilterMode->setEnabled(false); } else { grpOpenGL->setEnabled(true); grpOpenGL->setChecked(cfg.useOpenGL()); chkUseTextureBuffer->setEnabled(cfg.useOpenGL()); chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer()); chkDisableDoubleBuffering->setVisible(cfg.showAdvancedOpenGLSettings()); chkDisableDoubleBuffering->setEnabled(cfg.useOpenGL()); chkDisableDoubleBuffering->setChecked(cfg.disableDoubleBuffering()); chkDisableVsync->setVisible(cfg.showAdvancedOpenGLSettings()); chkDisableVsync->setEnabled(cfg.useOpenGL()); chkDisableVsync->setChecked(cfg.disableVSync()); cmbFilterMode->setEnabled(cfg.useOpenGL()); cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode()); // Don't show the high quality filtering mode if it's not available if (!KisOpenGL::supportsGLSL13()) { cmbFilterMode->removeItem(3); } } if (qApp->applicationName() == "kritasketch" || qApp->applicationName() == "kritagemini") { grpOpenGL->setVisible(false); grpOpenGL->setMaximumHeight(0); } #else grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); #endif KoColor c; c.fromQColor(cfg.selectionOverlayMaskColor()); m_selectionOverlayColorAction = new KoColorPopupAction(this); m_selectionOverlayColorAction->setCurrentColor(c); m_selectionOverlayColorAction->setIcon(koIcon("format-stroke-color")); m_selectionOverlayColorAction->setToolTip(i18n("Change the background color of the image")); btnSelectionOverlayColor->setDefaultAction(m_selectionOverlayColorAction); intCheckSize->setValue(cfg.checkSize()); chkMoving->setChecked(cfg.scrollCheckers()); colorChecks1->setColor(cfg.checkersColor1()); colorChecks2->setColor(cfg.checkersColor2()); canvasBorder->setColor(cfg.canvasBorderColor()); hideScrollbars->setChecked(cfg.hideScrollbars()); chkCurveAntialiasing->setChecked(cfg.antialiasCurves()); chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline()); chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor()); chkHidePopups->setChecked(cfg.hidePopups()); connect(grpOpenGL, SIGNAL(toggled(bool)), SLOT(slotUseOpenGLToggled(bool))); } void DisplaySettingsTab::setDefault() { KisConfig cfg; #ifdef HAVE_OPENGL - if (!QGLFormat::hasOpenGL()) { + if (!KisOpenGL::hasOpenGL()) { grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); chkUseTextureBuffer->setEnabled(false); chkDisableDoubleBuffering->setEnabled(false); chkDisableVsync->setEnabled(false); cmbFilterMode->setEnabled(false); } else { grpOpenGL->setEnabled(true); grpOpenGL->setChecked(cfg.useOpenGL(true)); chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer(true)); chkUseTextureBuffer->setEnabled(true); chkDisableDoubleBuffering->setEnabled(true); chkDisableDoubleBuffering->setChecked(cfg.disableDoubleBuffering(true)); chkDisableVsync->setEnabled(true); chkDisableVsync->setChecked(cfg.disableVSync(true)); cmbFilterMode->setEnabled(true); cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode(true)); } #else grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); #endif chkMoving->setChecked(cfg.scrollCheckers(true)); intCheckSize->setValue(cfg.checkSize(true)); colorChecks1->setColor(cfg.checkersColor1(true)); colorChecks2->setColor(cfg.checkersColor2(true)); canvasBorder->setColor(cfg.canvasBorderColor(true)); hideScrollbars->setChecked(cfg.hideScrollbars(true)); chkCurveAntialiasing->setChecked(cfg.antialiasCurves(true)); chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline(true)); chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor(true)); chkHidePopups->setChecked(cfg.hidePopups(true)); } void DisplaySettingsTab::slotUseOpenGLToggled(bool isChecked) { #ifdef HAVE_OPENGL chkUseTextureBuffer->setEnabled(isChecked); chkDisableDoubleBuffering->setEnabled(isChecked); chkDisableVsync->setEnabled(isChecked); cmbFilterMode->setEnabled(isChecked); #else Q_UNUSED(isChecked); #endif } //--------------------------------------------------------------------------------------------------- GridSettingsTab::GridSettingsTab(QWidget* parent) : WdgGridSettingsBase(parent) { KisConfig cfg; selectMainStyle->setCurrentIndex(cfg.getGridMainStyle()); selectSubdivisionStyle->setCurrentIndex(cfg.getGridSubdivisionStyle()); colorMain->setColor(cfg.getGridMainColor()); colorSubdivision->setColor(cfg.getGridSubdivisionColor()); intHSpacing->setValue(cfg.getGridHSpacing()); intVSpacing->setValue(cfg.getGridVSpacing()); spacingAspectButton->setKeepAspectRatio(cfg.getGridSpacingAspect()); linkSpacingToggled(cfg.getGridSpacingAspect()); intSubdivision->setValue(cfg.getGridSubdivisions()); intXOffset->setValue(cfg.getGridOffsetX()); intYOffset->setValue(cfg.getGridOffsetY()); offsetAspectButton->setKeepAspectRatio(cfg.getGridOffsetAspect()); linkOffsetToggled(cfg.getGridOffsetAspect()); connect(spacingAspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(linkSpacingToggled(bool))); connect(offsetAspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(linkOffsetToggled(bool))); connect(intHSpacing, SIGNAL(valueChanged(int)), this, SLOT(spinBoxHSpacingChanged(int))); connect(intVSpacing, SIGNAL(valueChanged(int)), this, SLOT(spinBoxVSpacingChanged(int))); connect(intXOffset, SIGNAL(valueChanged(int)), this, SLOT(spinBoxXOffsetChanged(int))); connect(intYOffset, SIGNAL(valueChanged(int)), this, SLOT(spinBoxYOffsetChanged(int))); } void GridSettingsTab::setDefault() { KisConfig cfg; selectMainStyle->setCurrentIndex(cfg.getGridMainStyle(true)); selectSubdivisionStyle->setCurrentIndex(cfg.getGridSubdivisionStyle(true)); colorMain->setColor(cfg.getGridMainColor(true)); colorSubdivision->setColor(cfg.getGridSubdivisionColor(true)); intHSpacing->setValue(cfg.getGridHSpacing(true)); intVSpacing->setValue(cfg.getGridVSpacing(true)); linkSpacingToggled(cfg.getGridSpacingAspect(true)); intSubdivision->setValue(cfg.getGridSubdivisions(true)); intXOffset->setValue(cfg.getGridOffsetX(true)); intYOffset->setValue(cfg.getGridOffsetY()); linkOffsetToggled(cfg.getGridOffsetAspect(true)); } void GridSettingsTab::spinBoxHSpacingChanged(int v) { if (m_linkSpacing) { intVSpacing->setValue(v); } } void GridSettingsTab::spinBoxVSpacingChanged(int v) { if (m_linkSpacing) { intHSpacing->setValue(v); } } void GridSettingsTab::linkSpacingToggled(bool b) { m_linkSpacing = b; if (m_linkSpacing) { intVSpacing->setValue(intHSpacing->value()); } } void GridSettingsTab::spinBoxXOffsetChanged(int v) { if (m_linkOffset) { intYOffset->setValue(v); } } void GridSettingsTab::spinBoxYOffsetChanged(int v) { if (m_linkOffset) { intXOffset->setValue(v); } } void GridSettingsTab::linkOffsetToggled(bool b) { m_linkOffset = b; if (m_linkOffset) { intYOffset->setValue(intXOffset->value()); } } //--------------------------------------------------------------------------------------------------- FullscreenSettingsTab::FullscreenSettingsTab(QWidget* parent) : WdgFullscreenSettingsBase(parent) { KisConfig cfg; chkDockers->setChecked(cfg.hideDockersFullscreen()); chkMenu->setChecked(cfg.hideMenuFullscreen()); chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen()); chkStatusbar->setChecked(cfg.hideStatusbarFullscreen()); chkTitlebar->setChecked(cfg.hideTitlebarFullscreen()); chkToolbar->setChecked(cfg.hideToolbarFullscreen()); } void FullscreenSettingsTab::setDefault() { KisConfig cfg; chkDockers->setChecked(cfg.hideDockersFullscreen(true)); chkMenu->setChecked(cfg.hideMenuFullscreen(true)); chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen(true)); chkStatusbar->setChecked(cfg.hideStatusbarFullscreen(true)); chkTitlebar->setChecked(cfg.hideTitlebarFullscreen(true)); chkToolbar->setChecked(cfg.hideToolbarFullscreen(true)); } //--------------------------------------------------------------------------------------------------- KisDlgPreferences::KisDlgPreferences(QWidget* parent, const char* name) : KPageDialog(parent) { Q_UNUSED(name); setWindowTitle(i18n("Preferences")); // QT5TODO: help button needs custom wiring up to whatever help should be shown setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::RestoreDefaults); button(QDialogButtonBox::Ok)->setDefault(true); setFaceType(KPageDialog::List); // General KVBox *vbox = new KVBox(); KPageWidgetItem *page = new KPageWidgetItem(vbox, i18n("General")); page->setObjectName("general"); page->setHeader(i18n("General")); page->setIcon(koIcon("configure")); addPage(page); m_general = new GeneralTab(vbox); // Display vbox = new KVBox(); page = new KPageWidgetItem(vbox, i18n("Display")); page->setObjectName("display"); page->setHeader(i18n("Display")); page->setIcon(koIcon("preferences-desktop-display")); addPage(page); m_displaySettings = new DisplaySettingsTab(vbox); // Color vbox = new KVBox(); page = new KPageWidgetItem(vbox, i18n("Color Management")); page->setObjectName("colormanagement"); page->setHeader(i18n("Color")); page->setIcon(koIcon("preferences-desktop-color")); addPage(page); m_colorSettings = new ColorSettingsTab(vbox); // Performance vbox = new KVBox(); page = new KPageWidgetItem(vbox, i18n("Performance")); page->setObjectName("performance"); page->setHeader(i18n("Performance")); page->setIcon(koIcon("preferences-system-performance")); addPage(page); m_performanceSettings = new PerformanceTab(vbox); // Grid vbox = new KVBox(); page = new KPageWidgetItem(vbox, i18n("Grid")); page->setObjectName("grid"); page->setHeader(i18n("Grid")); page->setIcon(koIcon("grid")); addPage(page); m_gridSettings = new GridSettingsTab(vbox); // Tablet vbox = new KVBox(); page = new KPageWidgetItem(vbox, i18n("Tablet settings")); page->setObjectName("tablet"); page->setHeader(i18n("Tablet")); page->setIcon(koIcon("input-tablet")); addPage(page); m_tabletSettings = new TabletSettingsTab(vbox); // full-screen mode vbox = new KVBox(); page = new KPageWidgetItem(vbox, i18n("Canvas-only settings")); page->setObjectName("canvasonly"); page->setHeader(i18n("Canvas-only")); page->setIcon(koIcon("preferences-system-performance")); addPage(page); m_fullscreenSettings = new FullscreenSettingsTab(vbox); // Author profiles m_authorPage = new KoConfigAuthorPage(); page = addPage(m_authorPage, i18nc("@title:tab Author page", "Author" )); page->setObjectName("author"); page->setHeader(i18n("Author")); page->setIcon(koIcon("user-identity")); // input settings m_inputConfiguration = new KisInputConfigurationPage(); page = addPage(m_inputConfiguration, i18n("Canvas Input Settings")); page->setHeader(i18n("Canvas Input")); page->setObjectName("canvasinput"); page->setIcon(koIcon("input-tablet")); QPushButton *restoreDefaultsButton = button(QDialogButtonBox::RestoreDefaults); connect(this, SIGNAL(accepted()), m_inputConfiguration, SLOT(saveChanges())); connect(this, SIGNAL(rejected()), m_inputConfiguration, SLOT(revertChanges())); connect(restoreDefaultsButton, SIGNAL(clicked(bool)), m_inputConfiguration, SLOT(setDefaults())); KisPreferenceSetRegistry *preferenceSetRegistry = KisPreferenceSetRegistry::instance(); foreach (KisAbstractPreferenceSetFactory *preferenceSetFactory, preferenceSetRegistry->values()) { KisPreferenceSet* preferenceSet = preferenceSetFactory->createPreferenceSet(); vbox = new KVBox(); page = new KPageWidgetItem(vbox, preferenceSet->name()); page->setHeader(preferenceSet->header()); page->setIcon(preferenceSet->icon()); addPage(page); preferenceSet->setParent(vbox); preferenceSet->loadPreferences(); connect(restoreDefaultsButton, SIGNAL(clicked(bool)), preferenceSet, SLOT(loadDefaultPreferences()), Qt::UniqueConnection); connect(this, SIGNAL(accepted()), preferenceSet, SLOT(savePreferences()), Qt::UniqueConnection); } connect(restoreDefaultsButton, SIGNAL(clicked(bool)), this, SLOT(slotDefault())); } KisDlgPreferences::~KisDlgPreferences() { } void KisDlgPreferences::slotDefault() { if (currentPage()->objectName() == "default") { m_general->setDefault(); } else if (currentPage()->objectName() == "display") { m_displaySettings->setDefault(); } else if (currentPage()->objectName() == "colormanagement") { m_colorSettings->setDefault(); } else if (currentPage()->objectName() == "performance") { m_performanceSettings->load(true); } else if (currentPage()->objectName() == "grid") { m_gridSettings->setDefault(); } else if (currentPage()->objectName() == "tablet") { m_tabletSettings->setDefault(); } else if (currentPage()->objectName() == "canvasonly") { m_fullscreenSettings->setDefault(); } else if (currentPage()->objectName() == "canvasinput") { m_inputConfiguration->setDefaults(); } } bool KisDlgPreferences::editPreferences() { KisDlgPreferences* dialog; dialog = new KisDlgPreferences(); bool baccept = (dialog->exec() == Accepted); if (baccept) { // General settings KisConfig cfg; cfg.setNewCursorStyle(dialog->m_general->cursorStyle()); cfg.setNewOutlineStyle(dialog->m_general->outlineStyle()); cfg.setShowRootLayer(dialog->m_general->showRootLayer()); cfg.setShowOutlineWhilePainting(dialog->m_general->showOutlineWhilePainting()); cfg.setHideSplashScreen(dialog->m_general->hideSplashScreen()); cfg.writeEntry("mdi_viewmode", dialog->m_general->mdiMode()); cfg.setMDIBackgroundColor(dialog->m_general->m_mdiColor->color()); cfg.setMDIBackgroundImage(dialog->m_general->m_backgroundimage->text()); cfg.setAutoSaveInterval(dialog->m_general->autoSaveInterval()); cfg.setBackupFile(dialog->m_general->m_backupFileCheckBox->isChecked()); cfg.setShowCanvasMessages(dialog->m_general->showCanvasMessages()); cfg.setCompressKra(dialog->m_general->compressKra()); cfg.setToolOptionsInDocker(dialog->m_general->toolOptionsInDocker()); KisPart *part = KisPart::instance(); if (part) { foreach(QPointer doc, part->documents()) { if (doc) { doc->setAutoSave(dialog->m_general->autoSaveInterval()); doc->setBackupFile(dialog->m_general->m_backupFileCheckBox->isChecked()); doc->undoStack()->setUndoLimit(dialog->m_general->undoStackSize()); } } } cfg.setUndoStackLimit(dialog->m_general->undoStackSize()); cfg.setFavoritePresets(dialog->m_general->favoritePresets()); // Color settings cfg.setUseSystemMonitorProfile(dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()); for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()) { int currentIndex = dialog->m_colorSettings->m_monitorProfileWidgets[i]->currentIndex(); QString monitorid = dialog->m_colorSettings->m_monitorProfileWidgets[i]->itemData(currentIndex).toString(); cfg.setMonitorForScreen(i, monitorid); } else { cfg.setMonitorProfile(i, dialog->m_colorSettings->m_monitorProfileWidgets[i]->itemHighlighted(), dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()); } } cfg.setWorkingColorSpace(dialog->m_colorSettings->m_page->cmbWorkingColorSpace->currentItem().id()); cfg.setPrinterColorSpace(dialog->m_colorSettings->m_page->cmbPrintingColorSpace->currentItem().id()); cfg.setPrinterProfile(dialog->m_colorSettings->m_page->cmbPrintProfile->itemHighlighted()); cfg.setUseBlackPointCompensation(dialog->m_colorSettings->m_page->chkBlackpoint->isChecked()); cfg.setAllowLCMSOptimization(dialog->m_colorSettings->m_page->chkAllowLCMSOptimization->isChecked()); cfg.setPasteBehaviour(dialog->m_colorSettings->m_pasteBehaviourGroup.checkedId()); cfg.setRenderIntent(dialog->m_colorSettings->m_page->cmbMonitorIntent->currentIndex()); // Tablet settings cfg.setPressureTabletCurve( dialog->m_tabletSettings->m_page->pressureCurve->curve().toString() ); dialog->m_performanceSettings->save(); #ifdef HAVE_OPENGL if (!cfg.useOpenGL() && dialog->m_displaySettings->grpOpenGL->isChecked()) cfg.setCanvasState("TRY_OPENGL"); cfg.setUseOpenGL(dialog->m_displaySettings->grpOpenGL->isChecked()); cfg.setUseOpenGLTextureBuffer(dialog->m_displaySettings->chkUseTextureBuffer->isChecked()); cfg.setOpenGLFilteringMode(dialog->m_displaySettings->cmbFilterMode->currentIndex()); cfg.setDisableDoubleBuffering(dialog->m_displaySettings->chkDisableDoubleBuffering->isChecked()); cfg.setDisableVSync(dialog->m_displaySettings->chkDisableVsync->isChecked()); #endif cfg.setCheckSize(dialog->m_displaySettings->intCheckSize->value()); cfg.setScrollingCheckers(dialog->m_displaySettings->chkMoving->isChecked()); cfg.setCheckersColor1(dialog->m_displaySettings->colorChecks1->color()); cfg.setCheckersColor2(dialog->m_displaySettings->colorChecks2->color()); cfg.setCanvasBorderColor(dialog->m_displaySettings->canvasBorder->color()); cfg.setHideScrollbars(dialog->m_displaySettings->hideScrollbars->isChecked()); cfg.setSelectionOverlayMaskColor(dialog->m_displaySettings->m_selectionOverlayColorAction->currentKoColor().toQColor()); cfg.setAntialiasCurves(dialog->m_displaySettings->chkCurveAntialiasing->isChecked()); cfg.setAntialiasSelectionOutline(dialog->m_displaySettings->chkSelectionOutlineAntialiasing->isChecked()); cfg.setShowSingleChannelAsColor(dialog->m_displaySettings->chkChannelsAsColor->isChecked()); cfg.setHidePopups(dialog->m_displaySettings->chkHidePopups->isChecked()); // Grid settings cfg.setGridMainStyle(dialog->m_gridSettings->selectMainStyle->currentIndex()); cfg.setGridSubdivisionStyle(dialog->m_gridSettings->selectSubdivisionStyle->currentIndex()); cfg.setGridMainColor(dialog->m_gridSettings->colorMain->color()); cfg.setGridSubdivisionColor(dialog->m_gridSettings->colorSubdivision->color()); cfg.setGridHSpacing(dialog->m_gridSettings->intHSpacing->value()); cfg.setGridVSpacing(dialog->m_gridSettings->intVSpacing->value()); cfg.setGridSpacingAspect(dialog->m_gridSettings->spacingAspectButton->keepAspectRatio()); cfg.setGridSubdivisions(dialog->m_gridSettings->intSubdivision->value()); cfg.setGridOffsetX(dialog->m_gridSettings->intXOffset->value()); cfg.setGridOffsetY(dialog->m_gridSettings->intYOffset->value()); cfg.setGridOffsetAspect(dialog->m_gridSettings->offsetAspectButton->keepAspectRatio()); cfg.setHideDockersFullscreen(dialog->m_fullscreenSettings->chkDockers->checkState()); cfg.setHideMenuFullscreen(dialog->m_fullscreenSettings->chkMenu->checkState()); cfg.setHideScrollbarsFullscreen(dialog->m_fullscreenSettings->chkScrollbars->checkState()); cfg.setHideStatusbarFullscreen(dialog->m_fullscreenSettings->chkStatusbar->checkState()); cfg.setHideTitlebarFullscreen(dialog->m_fullscreenSettings->chkTitlebar->checkState()); cfg.setHideToolbarFullscreen(dialog->m_fullscreenSettings->chkToolbar->checkState()); dialog->m_authorPage->apply(); } delete dialog; return baccept; } diff --git a/krita/ui/opengl/kis_opengl.cpp b/krita/ui/opengl/kis_opengl.cpp index dcf3b4ac1fe..bf071e5a1de 100644 --- a/krita/ui/opengl/kis_opengl.cpp +++ b/krita/ui/opengl/kis_opengl.cpp @@ -1,111 +1,122 @@ /* * Copyright (c) 2007 Adrian Page * * 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 "opengl/kis_opengl.h" #ifdef HAVE_OPENGL - -#include -#include +#include #include #include +#endif + +#include #include #include #include #include #include #include #include #include namespace { +#ifdef HAVE_OPENGL QOffscreenSurface *SharedSurface = 0; QOpenGLContext *SharedContext = 0; +#endif bool NeedsFenceWorkaround = false; } + void KisOpenGL::initialize() { +#ifdef HAVE_OPENGL dbgUI << "OpenGL: initializing"; KisConfig cfg;; QSurfaceFormat format; format.setProfile(QSurfaceFormat::CoreProfile); format.setDepthBufferSize(24); format.setStencilBufferSize(8); format.setVersion(3, 2); if (cfg.disableDoubleBuffering()) { format.setSwapBehavior(QSurfaceFormat::SingleBuffer); } format.setSwapInterval(0); // Disable vertical refresh syncing QSurfaceFormat::setDefaultFormat(format); if (!SharedSurface) { SharedSurface = new QOffscreenSurface(); SharedSurface->setFormat(format); SharedSurface->create(); } if (!SharedContext) { SharedContext = new QOpenGLContext(qApp); SharedContext->setFormat(format); SharedContext->create(); SharedContext->makeCurrent(SharedSurface); } - QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); QFile log(QDesktopServices::storageLocation(QDesktopServices::TempLocation) + "/krita-opengl.txt"); log.open(QFile::WriteOnly); QString vendor((const char*)f->glGetString(GL_VENDOR)); log.write(vendor.toLatin1()); log.write(", "); QString renderer((const char*)f->glGetString(GL_RENDERER)); log.write(renderer.toLatin1()); log.write(", "); QString version((const char*)f->glGetString(GL_VERSION)); log.write(version.toLatin1()); // Check if we have a bugged driver that needs fence workaround bool isOnX11 = false; #ifdef HAVE_X11 isOnX11 = true; #endif if ((isOnX11 && renderer.startsWith("AMD")) || cfg.forceOpenGLFenceWorkaround()) { NeedsFenceWorkaround = true; } +#else + NeedsFenceWorkaround = false; +#endif } bool KisOpenGL::supportsGLSL13() { return true; // QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_3_0; -- we force 3.2 for now, since that has glFenceSync } bool KisOpenGL::needsFenceWorkaround() { return NeedsFenceWorkaround; } -#endif // HAVE_OPENGL - +bool KisOpenGL::hasOpenGL() +{ + // QT5TODO: figure out runtime whether we have opengl... + return true; +} diff --git a/krita/ui/opengl/kis_opengl.h b/krita/ui/opengl/kis_opengl.h index 39a227f3250..c1c95db4b31 100644 --- a/krita/ui/opengl/kis_opengl.h +++ b/krita/ui/opengl/kis_opengl.h @@ -1,68 +1,65 @@ /* * Copyright (c) 2007 Adrian Page * * 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_OPENGL_H_ #define KIS_OPENGL_H_ /** @file */ #include -#ifdef HAVE_OPENGL - #if defined(_WIN32) || defined(_WIN64) #include #endif #include #include "krita_export.h" -class QOpenGLWidget; /** * This class manages a shared OpenGL context and provides utility * functions for checking capabilities and error reporting. */ class KRITAUI_EXPORT KisOpenGL { public: static void initialize(); + static bool hasOpenGL(); + /** * @brief supportsGLSL13 * @return true if we have a modern opengl capable of high-quality filtering */ static bool supportsGLSL13(); /** * Returns true if we have a driver that has bugged support to sync objects (a fence) * and false otherwise. */ static bool needsFenceWorkaround(); private: KisOpenGL(); }; -#endif // HAVE_OPENGL - #endif // KIS_OPENGL_H_ diff --git a/krita/ui/opengl/kis_opengl_canvas2.cpp b/krita/ui/opengl/kis_opengl_canvas2.cpp index 1477b7d2699..2b264b7af66 100644 --- a/krita/ui/opengl/kis_opengl_canvas2.cpp +++ b/krita/ui/opengl/kis_opengl_canvas2.cpp @@ -1,687 +1,687 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2006-2013 * * 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 "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" #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; - QGLShaderProgram *displayShader; + QOpenGLShaderProgram *displayShader; int displayUniformLocationModelViewProjection; int displayUniformLocationTextureMatrix; int displayUniformLocationViewPortScale; int displayUniformLocationTexelSize; int displayUniformLocationTexture0; int displayUniformLocationTexture1; - QGLShaderProgram *checkerShader; + 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, KisOpenGLImageTexturesSP imageTextures) : QOpenGLWidget(parent) , KisCanvasWidgetBase(canvas, coordinatesConverter) , d(new Private()) { initializeOpenGLFunctions(); KisConfig cfg; cfg.writeEntry("canvasState", "OPENGL_STARTED"); d->openGLImageTextures = imageTextures; 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); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); slotConfigChanged(); d->openGLImageTextures->generateCheckerTexture(createCheckersImage(cfg.checkSize())); cfg.writeEntry("canvasState", "OPENGL_SUCCESS"); } KisOpenGLCanvas2::~KisOpenGLCanvas2() { delete d; } void KisOpenGLCanvas2::setDisplayFilter(KisDisplayFilter* displayFilter) { d->displayFilter = displayFilter; d->canvasInitialized = false; - initializeDisplayShader(); +// initializeDisplayShader(); initializeCheckerShader(); d->canvasInitialized = true; } 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; +// 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(); +// } +// } +// } initializeCheckerShader(); - initializeDisplayShader(); +// initializeDisplayShader(); - Sync::init(); +// Sync::init(); d->canvasInitialized = true; } void KisOpenGLCanvas2::resizeGL(int width, int height) { - glViewport(0, 0, (GLint)width, (GLint)height); coordinatesConverter()->setCanvasWidgetSize(QSize(width, height)); + paintGL(); } void KisOpenGLCanvas2::paintGL() { if (!OPENGL_SUCCESS) { KisConfig cfg; cfg.writeEntry("canvasState", "OPENGL_PAINT_STARTED"); } renderCanvasGL(); QPainter gc(this); renderDecorations(&gc); gc.end(); - if (d->glSyncObject) { - Sync::deleteSync(d->glSyncObject); - } +// if (d->glSyncObject) { +// Sync::deleteSync(d->glSyncObject); +// } - d->glSyncObject = Sync::getSync(); +// d->glSyncObject = Sync::getSync(); if (!OPENGL_SUCCESS) { KisConfig cfg; cfg.writeEntry("canvasState", "OPENGL_SUCCESS"); OPENGL_SUCCESS = true; } } bool KisOpenGLCanvas2::isBusy() const { - return Sync::syncStatus(d->glSyncObject) == Sync::Unsignaled; +// return Sync::syncStatus(d->glSyncObject) == Sync::Unsignaled; + return false; } 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) + 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) + 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); KIS_ASSERT_RECOVER_BREAK(tile); /* * 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 QGLShaderProgram(); + 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(QGLShader::Vertex, vertexShaderName); + result = d->checkerShader->addShaderFromSourceFile(QOpenGLShader::Vertex, vertexShaderName); reportShaderLinkFailedAndExit(result, "Checker vertex shader", d->checkerShader->log()); - result = d->checkerShader->addShaderFromSourceFile(QGLShader::Fragment, fragmentShaderName); + 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 QGLShaderProgram(); + d->displayShader = new QOpenGLShaderProgram(); - bool result = d->displayShader->addShaderFromSourceCode(QGLShader::Fragment, buildFragmentShader()); + bool result = d->displayShader->addShaderFromSourceCode(QOpenGLShader::Fragment, buildFragmentShader()); reportShaderLinkFailedAndExit(result, "Display fragment shader", d->displayShader->log()); if (KisOpenGL::supportsGLSL13()) { - result = d->displayShader->addShaderFromSourceFile(QGLShader::Vertex, KGlobal::dirs()->findResource("data", "krita/shaders/matrix_transform.vert")); + result = d->displayShader->addShaderFromSourceFile(QOpenGLShader::Vertex, KGlobal::dirs()->findResource("data", "krita/shaders/matrix_transform.vert")); } else { - result = d->displayShader->addShaderFromSourceFile(QGLShader::Vertex, KGlobal::dirs()->findResource("data", "krita/shaders/matrix_transform_legacy.vert")); + 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); } bool KisOpenGLCanvas2::callFocusNextPrevChild(bool next) { return focusNextPrevChild(next); } -void KisOpenGLCanvas2::paintEvent(QPaintEvent* event) -{ - // Workaround for bug 322808, paint events with only a partial rect cause flickering - // Drop those event and trigger a new full update - if (event->rect().width() == width() && event->rect().height() == height()) { - QOpenGLWidget::paintEvent(event); - } else { - update(); - } -} +//void KisOpenGLCanvas2::paintEvent(QPaintEvent* event) +//{ +// // Workaround for bug 322808, paint events with only a partial rect cause flickering +// // Drop those event and trigger a new full update +// if (event->rect().width() == width() && event->rect().height() == height()) { +// QOpenGLWidget::paintEvent(event); +// } else { +// update(); +// } +//} #endif // HAVE_OPENGL diff --git a/krita/ui/opengl/kis_opengl_canvas2.h b/krita/ui/opengl/kis_opengl_canvas2.h index 5efa590d958..f492189c355 100644 --- a/krita/ui/opengl/kis_opengl_canvas2.h +++ b/krita/ui/opengl/kis_opengl_canvas2.h @@ -1,114 +1,114 @@ /* * Copyright (C) Boudewijn Rempt , (C) 2006 * * 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_OPENGL_CANVAS_2_H #define KIS_OPENGL_CANVAS_2_H #include #ifdef HAVE_OPENGL #include #include #include #include "canvas/kis_canvas_widget_base.h" #include "opengl/kis_opengl_image_textures.h" #include "krita_export.h" class QWidget; class QPaintEvent; class KisCanvas2; /** * KisOpenGLCanvas is the widget that shows the actual image using OpenGL * * NOTE: if you change something in the event handling here, also change it * in the qpainter canvas. * */ class KRITAUI_EXPORT KisOpenGLCanvas2 : public QOpenGLWidget, public QOpenGLFunctions, public KisCanvasWidgetBase { Q_OBJECT public: KisOpenGLCanvas2(KisCanvas2 * canvas, KisCoordinatesConverter *coordinatesConverter, QWidget * parent, KisOpenGLImageTexturesSP imageTextures); virtual ~KisOpenGLCanvas2(); void setDisplayFilter(KisDisplayFilter* displayFilter); void setWrapAroundViewingMode(bool value); public: // QWidget virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; virtual void inputMethodEvent(QInputMethodEvent *event); - virtual void paintEvent(QPaintEvent* event); + //virtual void paintEvent(QPaintEvent* event); public: bool isBusy() const; void initializeCheckerShader(); void initializeDisplayShader(); void renderCanvasGL(); void renderDecorations(QPainter *painter); private Q_SLOTS: void slotConfigChanged(); public: void resizeGL(int width, int height); void initializeGL(); void paintGL(); public: QWidget *widget() { return this; } protected: // KisCanvasWidgetBase virtual bool callFocusNextPrevChild(bool next); private: void reportShaderLinkFailedAndExit(bool result, const QString &context, const QString &log); private: struct Private; Private * const d; void drawImage(); void drawCheckers(); QByteArray buildFragmentShader(); }; #endif // HAVE_OPENGL #endif // KIS_OPENGL_CANVAS_2_H diff --git a/krita/ui/opengl/kis_opengl_canvas2_p.h b/krita/ui/opengl/kis_opengl_canvas2_p.h index 29eedb0d511..dc41aefa114 100644 --- a/krita/ui/opengl/kis_opengl_canvas2_p.h +++ b/krita/ui/opengl/kis_opengl_canvas2_p.h @@ -1,385 +1,385 @@ /* * Copyright (C) Boudewijn Rempt , (C) 2006 * * 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_OPENGL_CANVAS_2_P_H #define KIS_OPENGL_CANVAS_2_P_H #include #ifdef HAVE_OPENGL /** * This is a workaround for a very slow updates in OpenGL canvas (~6ms). * The delay happens because of VSync in the swapBuffers() call. At first * we try to disable VSync. If it fails we just disable Double Buffer * completely. * * This file is effectively a bit of copy-paste from qgl_x11.cpp */ #if defined Q_OS_LINUX #include #include #include #include #include #include #ifndef GL_NUM_EXTENSIONS #define GL_NUM_EXTENSIONS 0x821D #endif QString gl_library_name() { #if defined (QT_OPENGL_ES_2) return QLatin1String("GLESv2"); #else return QLatin1String("GL"); #endif } namespace VSyncWorkaround { class QGLExtensionMatcher { public: QGLExtensionMatcher(const char *str); QGLExtensionMatcher(); bool match(const char *str) const { int str_length = qstrlen(str); Q_ASSERT(str); Q_ASSERT(str_length > 0); Q_ASSERT(str[str_length-1] != ' '); for (int i = 0; i < m_offsets.size(); ++i) { const char *extension = m_extensions.constData() + m_offsets.at(i); if (qstrncmp(extension, str, str_length) == 0 && extension[str_length] == ' ') return true; } return false; } private: void init(const char *str); QByteArray m_extensions; QVector m_offsets; }; typedef const GLubyte * (*qt_glGetStringi)(GLenum, GLuint); QGLExtensionMatcher::QGLExtensionMatcher(const char *str) { init(str); } QGLExtensionMatcher::QGLExtensionMatcher() { const char *extensionStr = reinterpret_cast(glGetString(GL_EXTENSIONS)); if (extensionStr) { init(extensionStr); } else { // clear error state while (glGetError()) {} - const QGLContext *ctx = QGLContext::currentContext(); - if (ctx) { - qt_glGetStringi glGetStringi = (qt_glGetStringi)ctx->getProcAddress(QLatin1String("glGetStringi")); + const QOpenGLContext *ctx = QOpenGLContext::currentContext(); +// if (ctx) { +// qt_glGetStringi glGetStringi = (qt_glGetStringi)ctx->getProcAddress(QLatin1String("glGetStringi")); - GLint numExtensions; - glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); +// GLint numExtensions; +// glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); - for (int i = 0; i < numExtensions; ++i) { - const char *str = reinterpret_cast(glGetStringi(GL_EXTENSIONS, i)); +// for (int i = 0; i < numExtensions; ++i) { +// const char *str = reinterpret_cast(glGetStringi(GL_EXTENSIONS, i)); - m_offsets << m_extensions.size(); +// m_offsets << m_extensions.size(); - while (*str != 0) - m_extensions.append(*str++); - m_extensions.append(' '); - } - } +// while (*str != 0) +// m_extensions.append(*str++); +// m_extensions.append(' '); +// } +// } } } void QGLExtensionMatcher::init(const char *str) { m_extensions = str; // make sure extension string ends with a space if (!m_extensions.endsWith(' ')) m_extensions.append(' '); int index = 0; int next = 0; while ((next = m_extensions.indexOf(' ', index)) >= 0) { m_offsets << index; index = next + 1; } } void* qglx_getProcAddress(const char* procName) { // On systems where the GL driver is pluggable (like Mesa), we have to use // the glXGetProcAddressARB extension to resolve other function pointers as // the symbols wont be in the GL library, but rather in a plugin loaded by // the GL library. typedef void* (*qt_glXGetProcAddressARB)(const char *); static qt_glXGetProcAddressARB glXGetProcAddressARB = 0; static bool triedResolvingGlxGetProcAddress = false; if (!triedResolvingGlxGetProcAddress) { triedResolvingGlxGetProcAddress = true; QGLExtensionMatcher extensions(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS)); if (extensions.match("GLX_ARB_get_proc_address")) { void *handle = dlopen(NULL, RTLD_LAZY); if (handle) { glXGetProcAddressARB = (qt_glXGetProcAddressARB) dlsym(handle, "glXGetProcAddressARB"); dlclose(handle); } if (!glXGetProcAddressARB) { QLibrary lib(gl_library_name()); //lib.setLoadHints(QLibrary::ImprovedSearchHeuristics); glXGetProcAddressARB = (qt_glXGetProcAddressARB)(void*)lib.resolve("glXGetProcAddressARB"); } } } void *procAddress = 0; if (glXGetProcAddressARB) { procAddress = glXGetProcAddressARB(procName); } // If glXGetProcAddress didn't work, try looking the symbol up in the GL library if (!procAddress) { void *handle = dlopen(NULL, RTLD_LAZY); if (handle) { procAddress = dlsym(handle, procName); dlclose(handle); } } if (!procAddress) { QLibrary lib(gl_library_name()); //lib.setLoadHints(QLibrary::ImprovedSearchHeuristics); procAddress = (void*)lib.resolve(procName); } return procAddress; } bool tryDisableVSync(QWidget *widget) { bool result = false; bool triedDisable = false; Display *dpy = QX11Info::display(); WId wid = widget->winId(); QGLExtensionMatcher extensions(glXQueryExtensionsString(dpy, QX11Info::appScreen())); if (extensions.match("GLX_EXT_swap_control")) { typedef void (*kis_glXSwapIntervalEXT)(Display*, WId, int); kis_glXSwapIntervalEXT glXSwapIntervalEXT = (kis_glXSwapIntervalEXT)qglx_getProcAddress("glXSwapIntervalEXT"); if (glXSwapIntervalEXT) { glXSwapIntervalEXT(dpy, wid, 0); triedDisable = true; unsigned int swap = 1; #ifdef GLX_SWAP_INTERVAL_EXT glXQueryDrawable(dpy, wid, GLX_SWAP_INTERVAL_EXT, &swap); #endif result = !swap; } else { qDebug() << "Couldn't load glXSwapIntervalEXT extension function"; } } else if (extensions.match("GLX_MESA_swap_control")) { typedef int (*kis_glXSwapIntervalMESA)(unsigned int); typedef int (*kis_glXGetSwapIntervalMESA)(void); kis_glXSwapIntervalMESA glXSwapIntervalMESA = (kis_glXSwapIntervalMESA)qglx_getProcAddress("glXSwapIntervalMESA"); kis_glXGetSwapIntervalMESA glXGetSwapIntervalMESA = (kis_glXGetSwapIntervalMESA)qglx_getProcAddress("glXGetSwapIntervalMESA"); if (glXSwapIntervalMESA) { int retval = glXSwapIntervalMESA(0); triedDisable = true; int swap = 1; if (glXGetSwapIntervalMESA) { swap = glXGetSwapIntervalMESA(); } else { qDebug() << "Couldn't load glXGetSwapIntervalMESA extension function"; } result = !retval && !swap; } else { qDebug() << "Couldn't load glXSwapIntervalMESA extension function"; } } else { qDebug() << "There is neither GLX_EXT_swap_control or GLX_MESA_swap_control extension supported"; } if (triedDisable && !result) { qCritical(); qCritical() << "CRITICAL: Your video driver forbids disabling VSync!"; qCritical() << "CRITICAL: Try toggling some VSync- or VBlank-related options in your driver configuration dialog."; qCritical() << "CRITICAL: NVIDIA users can do:"; qCritical() << "CRITICAL: sudo nvidia-settings > (tab) OpenGL settings > Sync to VBlank ( unchecked )"; qCritical(); } return result; } } #elif defined Q_OS_WIN // QT5TODO: T360 //#include namespace VSyncWorkaround { bool tryDisableVSync(QWidget *) { bool retval = false; #ifdef WGL_EXT_swap_control if (WGLEW_EXT_swap_control) { wglSwapIntervalEXT(0); int interval = wglGetSwapIntervalEXT(); if (interval) { qWarning() << "Failed to disable VSync with WGLEW_EXT_swap_control"; } retval = !interval; } else { qWarning() << "WGL_EXT_swap_control extension is not available"; } #else qWarning() << "GLEW WGL_EXT_swap_control extension is not compiled in"; #endif return retval; } } #else // !defined Q_OS_LINUX && !defined Q_OS_WIN namespace VSyncWorkaround { bool tryDisableVSync(QWidget *) { return false; } } #endif // defined Q_OS_LINUX #include namespace Sync { //For checking sync status enum SyncStatus { Signaled, Unsignaled }; #ifndef GL_SYNC_GPU_COMMANDS_COMPLETE #define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 #endif #ifndef GL_UNSIGNALED #define GL_UNSIGNALED 0x9118 #endif #ifndef GL_SIGNALED #define GL_SIGNALED 0x9119 #endif #ifndef GL_SYNC_STATUS #define GL_SYNC_STATUS 0x9114 #endif //Function pointers for glFenceSync and glGetSynciv typedef GLsync (*kis_glFenceSync)(GLenum, GLbitfield); static kis_glFenceSync k_glFenceSync = 0; typedef void (*kis_glGetSynciv)(GLsync, GLenum, GLsizei, GLsizei*, GLint*); static kis_glGetSynciv k_glGetSynciv = 0; typedef void (*kis_glDeleteSync)(GLsync); static kis_glDeleteSync k_glDeleteSync = 0; //Initialise the function pointers for glFenceSync and glGetSynciv //Note: Assumes a current OpenGL context. void init() { #if defined Q_OS_WIN if (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_3_2) { #ifdef ENV64BIT k_glFenceSync = (kis_glFenceSync)wglGetProcAddress("glFenceSync"); k_glGetSynciv = (kis_glGetSynciv)wglGetProcAddress("glGetSynciv"); k_glDeleteSync = (kis_glDeleteSync)wglGetProcAddress("glDeleteSync"); #else k_glFenceSync = (kis_glFenceSync)wglGetProcAddress("glFenceSyncARB"); k_glGetSynciv = (kis_glGetSynciv)wglGetProcAddress("glGetSyncivARB"); k_glDeleteSync = (kis_glDeleteSync)wglGetProcAddress("glDeleteSyncARB"); #endif } #elif defined Q_OS_LINUX #ifdef HAVE_GLEW if ((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_3_2) || GLEW_ARB_sync) { #else if ((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_3_2)) { #endif k_glFenceSync = (kis_glFenceSync)VSyncWorkaround::qglx_getProcAddress("glFenceSync"); k_glGetSynciv = (kis_glGetSynciv)VSyncWorkaround::qglx_getProcAddress("glGetSynciv"); k_glDeleteSync = (kis_glDeleteSync)VSyncWorkaround::qglx_getProcAddress("glDeleteSync"); } #endif if (k_glFenceSync == 0 || k_glGetSynciv == 0 || k_glDeleteSync == 0) { qWarning("Could not find sync functions, disabling sync notification."); } } //Get a fence sync object from OpenGL GLsync getSync() { if(k_glFenceSync) { GLsync sync = k_glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); if (KisOpenGL::needsFenceWorkaround()) { glClientWaitSync(sync, 0, 1); } return sync; } return 0; } //Check the status of a sync object SyncStatus syncStatus(GLsync syncObject) { if(syncObject && k_glGetSynciv) { GLint status = -1; k_glGetSynciv(syncObject, GL_SYNC_STATUS, 1, 0, &status); return status == GL_SIGNALED ? Sync::Signaled : Sync::Unsignaled; } return Sync::Signaled; } void deleteSync(GLsync syncObject) { if(syncObject && k_glDeleteSync) { k_glDeleteSync(syncObject); } } } #endif // HAVE_OPENGL #endif // KIS_OPENGL_CANVAS_2_P_H diff --git a/krita/ui/opengl/kis_texture_tile.cpp b/krita/ui/opengl/kis_texture_tile.cpp index 29bbed32756..a2fe96a04bd 100644 --- a/krita/ui/opengl/kis_texture_tile.cpp +++ b/krita/ui/opengl/kis_texture_tile.cpp @@ -1,327 +1,327 @@ /* * Copyright (c) 2010 Dmitry Kazakov * * 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 "kis_texture_tile.h" #ifdef HAVE_OPENGL #ifndef GL_BGRA #define GL_BGRA 0x814F #endif inline QRectF relativeRect(const QRect &br /* baseRect */, const QRect &cr /* childRect */, const KisGLTexturesInfo *texturesInfo) { const qreal x = qreal(cr.x() - br.x()) / texturesInfo->width; const qreal y = qreal(cr.y() - br.y()) / texturesInfo->height; const qreal w = qreal(cr.width()) / texturesInfo->width; const qreal h = qreal(cr.height()) / texturesInfo->height; return QRectF(x, y, w, h); } KisTextureTile::KisTextureTile(QRect imageRect, const KisGLTexturesInfo *texturesInfo, const QByteArray &fillData, FilterMode filter, bool useBuffer, int numMipmapLevels) : m_textureId(0) #ifdef USE_PIXEL_BUFFERS , m_glBuffer(0) #endif , m_tileRectInImagePixels(imageRect) , m_filter(filter) , m_texturesInfo(texturesInfo) , m_needsMipmapRegeneration(false) , m_useBuffer(useBuffer) , m_numMipmapLevels(numMipmapLevels) { const GLvoid *fd = fillData.constData(); m_textureRectInImagePixels = stretchRect(m_tileRectInImagePixels, texturesInfo->border); m_tileRectInTexturePixels = relativeRect(m_textureRectInImagePixels, m_tileRectInImagePixels, m_texturesInfo); glGenTextures(1, &m_textureId); glBindTexture(GL_TEXTURE_2D, m_textureId); setTextureParameters(); #ifdef USE_PIXEL_BUFFERS createTextureBuffer(fillData.constData(), fillData.size()); // we set fill data to 0 so the next glTexImage2D call uses our buffer fd = 0; #endif glTexImage2D(GL_TEXTURE_2D, 0, m_texturesInfo->internalFormat, m_texturesInfo->width, m_texturesInfo->height, 0, m_texturesInfo->format, m_texturesInfo->type, fd); #ifdef USE_PIXEL_BUFFERS if (m_useBuffer) { m_glBuffer->release(); } #endif setNeedsMipmapRegeneration(); } KisTextureTile::~KisTextureTile() { #ifdef USE_PIXEL_BUFFERS if (m_useBuffer) { delete m_glBuffer; } #endif glDeleteTextures(1, &m_textureId); } void KisTextureTile::bindToActiveTexture() { glBindTexture(GL_TEXTURE_2D, textureId()); if (m_needsMipmapRegeneration) { glGenerateMipmap(GL_TEXTURE_2D); m_needsMipmapRegeneration = false; } } void KisTextureTile::setNeedsMipmapRegeneration() { if (m_filter == TrilinearFilterMode || m_filter == HighQualityFiltering) { m_needsMipmapRegeneration = true; } } void KisTextureTile::update(const KisTextureTileUpdateInfo &updateInfo) { glBindTexture(GL_TEXTURE_2D, m_textureId); setTextureParameters(); const GLvoid *fd = updateInfo.data(); #ifdef USE_PIXEL_BUFFERS if (!m_glBuffer) { createTextureBuffer((const char*)updateInfo.data(), updateInfo.patchPixelsLength()); } #endif if (updateInfo.isEntireTileUpdated()) { #ifdef USE_PIXEL_BUFFERS if (m_useBuffer) { m_glBuffer->bind(); m_glBuffer->allocate(updateInfo.patchPixelsLength()); - void *vid = m_glBuffer->map(QGLBuffer::WriteOnly); + void *vid = m_glBuffer->map(QOpenGLBuffer::WriteOnly); memcpy(vid, fd, updateInfo.patchPixelsLength()); m_glBuffer->unmap(); // we set fill data to 0 so the next glTexImage2D call uses our buffer fd = 0; } #endif glTexImage2D(GL_TEXTURE_2D, 0, m_texturesInfo->internalFormat, m_texturesInfo->width, m_texturesInfo->height, 0, m_texturesInfo->format, m_texturesInfo->type, fd); #ifdef USE_PIXEL_BUFFERS if (m_useBuffer) { m_glBuffer->release(); } #endif } else { QPoint patchOffset = updateInfo.patchOffset(); QSize patchSize = updateInfo.patchSize(); #ifdef USE_PIXEL_BUFFERS if (m_useBuffer) { m_glBuffer->bind(); quint32 size = patchSize.width() * patchSize.height() * updateInfo.pixelSize(); m_glBuffer->allocate(size); - void *vid = m_glBuffer->map(QGLBuffer::WriteOnly); + void *vid = m_glBuffer->map(QOpenGLBuffer::WriteOnly); memcpy(vid, fd, size); m_glBuffer->unmap(); // we set fill data to 0 so the next glTexImage2D call uses our buffer fd = 0; } #endif glTexSubImage2D(GL_TEXTURE_2D, 0, patchOffset.x(), patchOffset.y(), patchSize.width(), patchSize.height(), m_texturesInfo->format, m_texturesInfo->type, fd); #ifdef USE_PIXEL_BUFFERS if (m_useBuffer) { m_glBuffer->release(); } #endif } /** * On the boundaries of KisImage, there is a border-effect as well. * So we just repeat the bounding pixels of the image to make * bilinear interpolator happy. */ /** * WARN: The width of the stripes will be equal to the broder * width of the tiles. */ const int pixelSize = updateInfo.pixelSize(); const QRect imageRect = updateInfo.imageRect(); const QPoint patchOffset = updateInfo.patchOffset(); const QSize patchSize = updateInfo.patchSize(); const QRect patchRect = QRect(m_textureRectInImagePixels.topLeft() + patchOffset, patchSize); const QSize tileSize = updateInfo.tileRect().size(); if(imageRect.top() == patchRect.top()) { int start = 0; int end = patchOffset.y() - 1; for (int i = start; i <= end; i++) { glTexSubImage2D(GL_TEXTURE_2D, 0, patchOffset.x(), i, patchSize.width(), 1, m_texturesInfo->format, m_texturesInfo->type, updateInfo.data()); } } if (imageRect.bottom() == patchRect.bottom()) { int shift = patchSize.width() * (patchSize.height() - 1) * pixelSize; int start = patchOffset.y() + patchSize.height(); int end = tileSize.height() - 1; for (int i = start; i < end; i++) { glTexSubImage2D(GL_TEXTURE_2D, 0, patchOffset.x(), i, patchSize.width(), 1, m_texturesInfo->format, m_texturesInfo->type, updateInfo.data() + shift); } } if (imageRect.left() == patchRect.left()) { QByteArray columnBuffer(patchSize.height() * pixelSize, 0); quint8 *srcPtr = updateInfo.data(); quint8 *dstPtr = (quint8*) columnBuffer.data(); for(int i = 0; i < patchSize.height(); i++) { memcpy(dstPtr, srcPtr, pixelSize); srcPtr += patchSize.width() * pixelSize; dstPtr += pixelSize; } int start = 0; int end = patchOffset.x() - 1; for (int i = start; i <= end; i++) { glTexSubImage2D(GL_TEXTURE_2D, 0, i, patchOffset.y(), 1, patchSize.height(), m_texturesInfo->format, m_texturesInfo->type, columnBuffer.constData()); } } if (imageRect.right() == patchRect.right()) { QByteArray columnBuffer(patchSize.height() * pixelSize, 0); quint8 *srcPtr = updateInfo.data() + (patchSize.width() - 1) * pixelSize; quint8 *dstPtr = (quint8*) columnBuffer.data(); for(int i = 0; i < patchSize.height(); i++) { memcpy(dstPtr, srcPtr, pixelSize); srcPtr += patchSize.width() * pixelSize; dstPtr += pixelSize; } int start = patchOffset.x() + patchSize.width(); int end = tileSize.width() - 1; for (int i = start; i <= end; i++) { glTexSubImage2D(GL_TEXTURE_2D, 0, i, patchOffset.y(), 1, patchSize.height(), m_texturesInfo->format, m_texturesInfo->type, columnBuffer.constData()); } } setNeedsMipmapRegeneration(); } #ifdef USE_PIXEL_BUFFERS void KisTextureTile::createTextureBuffer(const char *data, int size) { if (m_useBuffer) { if (!m_glBuffer) { - m_glBuffer = new QGLBuffer(QGLBuffer::PixelUnpackBuffer); - m_glBuffer->setUsagePattern(QGLBuffer::DynamicDraw); + m_glBuffer = new QOpenGLBuffer(QOpenGLBuffer::PixelUnpackBuffer); + m_glBuffer->setUsagePattern(QOpenGLBuffer::DynamicDraw); m_glBuffer->create(); m_glBuffer->bind(); m_glBuffer->allocate(size); } - void *vid = m_glBuffer->map(QGLBuffer::WriteOnly); + void *vid = m_glBuffer->map(QOpenGLBuffer::WriteOnly); memcpy(vid, data, size); m_glBuffer->unmap(); } else { m_glBuffer = 0; } } #endif #endif /* HAVE_OPENGL */ diff --git a/krita/ui/opengl/kis_texture_tile.h b/krita/ui/opengl/kis_texture_tile.h index e05844c86ab..51f4e1f6bbc 100644 --- a/krita/ui/opengl/kis_texture_tile.h +++ b/krita/ui/opengl/kis_texture_tile.h @@ -1,141 +1,141 @@ /* * Copyright (c) 2010 Dmitry Kazakov * * 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_TEXTURE_TILE_H_ #define KIS_TEXTURE_TILE_H_ #include #ifdef HAVE_OPENGL #include "kis_texture_tile_update_info.h" #include #include #if QT_VERSION >= 0x040700 && !defined(QT_OPENGL_ES) #define USE_PIXEL_BUFFERS -#include +#include #endif struct KisGLTexturesInfo { // real width and height int width; int height; // width and height minus border padding? int effectiveWidth; int effectiveHeight; // size of the border padding int border; GLint internalFormat; GLint format; GLint type; }; inline QRect stretchRect(const QRect &rc, int delta) { return rc.adjusted(-delta, -delta, delta, delta); } class KisTextureTile { public: enum FilterMode { NearestFilterMode, // nearest BilinearFilterMode, // linear, no mipmap TrilinearFilterMode, // LINEAR_MIPMAP_LINEAR HighQualityFiltering // Mipmaps + custom shader }; KisTextureTile(QRect imageRect, const KisGLTexturesInfo *texturesInfo, const QByteArray &fillData, FilterMode mode, bool useBuffer, int numMipmapLevels); ~KisTextureTile(); void setUseBuffer(bool useBuffer) { m_useBuffer = useBuffer; } void setNumMipmapLevels(int num) { m_numMipmapLevels = num; } void update(const KisTextureTileUpdateInfo &updateInfo); inline QRect tileRectInImagePixels() { return m_tileRectInImagePixels; } inline GLuint textureId() { return m_textureId; } inline QRect textureRectInImagePixels() { return m_textureRectInImagePixels; } inline QRectF tileRectInTexturePixels() { return m_tileRectInTexturePixels; } inline void setTextureParameters() { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, m_numMipmapLevels); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, m_numMipmapLevels); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); } void bindToActiveTexture(); private: void setNeedsMipmapRegeneration(); GLuint m_textureId; #ifdef USE_PIXEL_BUFFERS void createTextureBuffer(const char*data, int size); - QGLBuffer *m_glBuffer; + QOpenGLBuffer *m_glBuffer; #endif QRect m_tileRectInImagePixels; QRectF m_tileRectInTexturePixels; QRect m_textureRectInImagePixels; FilterMode m_filter; const KisGLTexturesInfo *m_texturesInfo; bool m_needsMipmapRegeneration; bool m_useBuffer; int m_numMipmapLevels; Q_DISABLE_COPY(KisTextureTile) }; #endif /* HAVE_OPENGL */ #endif /* KIS_TEXTURE_TILE_H_ */ diff --git a/krita/ui/tool/kis_tool.cc b/krita/ui/tool/kis_tool.cc index 61714f28f13..15f6bf9daa0 100644 --- a/krita/ui/tool/kis_tool.cc +++ b/krita/ui/tool/kis_tool.cc @@ -1,803 +1,802 @@ /* * Copyright (c) 2006, 2010 Boudewijn Rempt * * 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_tool.h" #include "opengl/kis_opengl.h" #include #include #include #include #include #ifdef HAVE_OPENGL -#include -#include -#include +#include +#include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "opengl/kis_opengl_canvas2.h" #include "kis_canvas_resource_provider.h" #include "canvas/kis_canvas2.h" #include "kis_coordinates_converter.h" #include "filter/kis_filter_configuration.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_cursor.h" #include #include #include "kis_resources_snapshot.h" #include struct KisTool::Private { Private() : currentPattern(0), currentGradient(0), currentExposure(1.0), currentGenerator(0), optionWidget(0), #ifdef HAVE_OPENGL cursorShader(0), #endif useGLToolOutlineWorkaround(false) { } QCursor cursor; // the cursor that should be shown on tool activation. // From the canvas resources KoPattern* currentPattern; KoAbstractGradient* currentGradient; KoColor currentFgColor; KoColor currentBgColor; float currentExposure; KisFilterConfiguration* currentGenerator; QWidget* optionWidget; #ifdef HAVE_OPENGL - QGLShaderProgram *cursorShader; // Make static instead of creating for all tools? + QOpenGLShaderProgram *cursorShader; // Make static instead of creating for all tools? int cursorShaderModelViewProjectionUniform; #endif bool useGLToolOutlineWorkaround; }; KisTool::KisTool(KoCanvasBase * canvas, const QCursor & cursor) : KoToolBase(canvas) , d(new Private) { d->cursor = cursor; m_isActive = false; connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(resetCursorStyle())); connect(this, SIGNAL(isActiveChanged()), SLOT(resetCursorStyle())); KActionCollection *collection = this->canvas()->canvasController()->actionCollection(); if (!collection->action("toggle_fg_bg")) { KAction *toggleFgBg = new KAction(i18n("Swap Foreground and Background Color"), collection); toggleFgBg->setShortcut(QKeySequence(Qt::Key_X)); collection->addAction("toggle_fg_bg", toggleFgBg); } if (!collection->action("reset_fg_bg")) { KAction *resetFgBg = new KAction(i18n("Reset Foreground and Background Color"), collection); resetFgBg->setShortcut(QKeySequence(Qt::Key_D)); collection->addAction("reset_fg_bg", resetFgBg); } addAction("toggle_fg_bg", dynamic_cast(collection->action("toggle_fg_bg"))); addAction("reset_fg_bg", dynamic_cast(collection->action("reset_fg_bg"))); setMode(HOVER_MODE); QStringList qtVersion = QString(qVersion()).split('.'); int major = qtVersion.at(0).toInt(); int minor = qtVersion.at(1).toInt(); if (major == 4 && minor <= 6) { d->useGLToolOutlineWorkaround = true; } else { d->useGLToolOutlineWorkaround = false; } } KisTool::~KisTool() { #ifdef HAVE_OPENGL delete d->cursorShader; #endif delete d; } void KisTool::activate(ToolActivation toolActivation, const QSet &shapes) { Q_UNUSED(toolActivation); Q_UNUSED(shapes); resetCursorStyle(); if (!canvas()) return; if (!canvas()->resourceManager()) return; d->currentFgColor = canvas()->resourceManager()->resource(KoCanvasResourceManager::ForegroundColor).value(); d->currentBgColor = canvas()->resourceManager()->resource(KoCanvasResourceManager::BackgroundColor).value(); if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentPattern)) { d->currentPattern = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPattern).value(); } if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentGradient)) { d->currentGradient = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentGradient).value(); } KisPaintOpPresetSP preset = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); if (preset && preset->settings()) { preset->settings()->activate(); } if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::HdrExposure)) { d->currentExposure = static_cast(canvas()->resourceManager()->resource(KisCanvasResourceProvider::HdrExposure).toDouble()); } if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentGeneratorConfiguration)) { d->currentGenerator = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentGeneratorConfiguration).value(); } connect(actions().value("toggle_fg_bg"), SIGNAL(triggered()), SLOT(slotToggleFgBg()), Qt::UniqueConnection); connect(actions().value("reset_fg_bg"), SIGNAL(triggered()), SLOT(slotResetFgBg()), Qt::UniqueConnection); connect(image(), SIGNAL(sigUndoDuringStrokeRequested()), SLOT(requestUndoDuringStroke()), Qt::UniqueConnection); connect(image(), SIGNAL(sigStrokeCancellationRequested()), SLOT(requestStrokeCancellation()), Qt::UniqueConnection); connect(image(), SIGNAL(sigStrokeEndRequested()), SLOT(requestStrokeEnd()), Qt::UniqueConnection); m_isActive = true; emit isActiveChanged(); } void KisTool::deactivate() { bool result = true; result &= disconnect(image().data(), SIGNAL(sigUndoDuringStrokeRequested()), this, 0); result &= disconnect(image().data(), SIGNAL(sigStrokeCancellationRequested()), this, 0); result &= disconnect(image().data(), SIGNAL(sigStrokeEndRequested()), this, 0); result &= disconnect(actions().value("toggle_fg_bg"), 0, this, 0); result &= disconnect(actions().value("reset_fg_bg"), 0, this, 0); if (!result) { qWarning() << "WARNING: KisTool::deactivate() failed to disconnect" << "some signal connections. Your actions might be executed twice!"; } m_isActive = false; emit isActiveChanged(); } void KisTool::requestUndoDuringStroke() { /** * Default implementation just cancells the stroke */ requestStrokeCancellation(); } void KisTool::requestStrokeCancellation() { } void KisTool::requestStrokeEnd() { } void KisTool::canvasResourceChanged(int key, const QVariant & v) { switch (key) { case(KoCanvasResourceManager::ForegroundColor): d->currentFgColor = v.value(); break; case(KoCanvasResourceManager::BackgroundColor): d->currentBgColor = v.value(); break; case(KisCanvasResourceProvider::CurrentPattern): d->currentPattern = static_cast(v.value()); break; case(KisCanvasResourceProvider::CurrentGradient): d->currentGradient = static_cast(v.value()); break; case(KisCanvasResourceProvider::HdrExposure): d->currentExposure = static_cast(v.toDouble()); break; case(KisCanvasResourceProvider::CurrentGeneratorConfiguration): d->currentGenerator = static_cast(v.value()); break; case(KisCanvasResourceProvider::CurrentPaintOpPreset): emit statusTextChanged(v.value()->name()); break; case(KisCanvasResourceProvider::CurrentKritaNode): resetCursorStyle(); break; default: break; // Do nothing }; } void KisTool::updateSettingsViews() { } QPointF KisTool::widgetCenterInWidgetPixels() { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); const KisCoordinatesConverter *converter = kritaCanvas->coordinatesConverter(); return converter->flakeToWidget(converter->flakeCenterPoint()); } QPointF KisTool::convertDocumentToWidget(const QPointF& pt) { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); return kritaCanvas->coordinatesConverter()->documentToWidget(pt); } QPointF KisTool::convertToPixelCoord(KoPointerEvent *e) { if (!image()) return e->point; return image()->documentToPixel(e->point); } QPointF KisTool::convertToPixelCoord(const QPointF& pt) { if (!image()) return pt; return image()->documentToPixel(pt); } QPoint KisTool::convertToIntPixelCoord(KoPointerEvent *e) { if (!image()) return e->point.toPoint(); return image()->documentToIntPixel(e->point); } QPointF KisTool::viewToPixel(const QPointF &viewCoord) const { if (!image()) return viewCoord; return image()->documentToPixel(canvas()->viewConverter()->viewToDocument(viewCoord)); } QRectF KisTool::convertToPt(const QRectF &rect) { if (!image()) return rect; QRectF r; //We add 1 in the following to the extreme coords because a pixel always has size r.setCoords(int(rect.left()) / image()->xRes(), int(rect.top()) / image()->yRes(), int(1 + rect.right()) / image()->xRes(), int(1 + rect.bottom()) / image()->yRes()); return r; } QPointF KisTool::pixelToView(const QPoint &pixelCoord) const { if (!image()) return pixelCoord; QPointF documentCoord = image()->pixelToDocument(pixelCoord); return canvas()->viewConverter()->documentToView(documentCoord); } QPointF KisTool::pixelToView(const QPointF &pixelCoord) const { if (!image()) return pixelCoord; QPointF documentCoord = image()->pixelToDocument(pixelCoord); return canvas()->viewConverter()->documentToView(documentCoord); } QRectF KisTool::pixelToView(const QRectF &pixelRect) const { if (!image()) return pixelRect; QPointF topLeft = pixelToView(pixelRect.topLeft()); QPointF bottomRight = pixelToView(pixelRect.bottomRight()); return QRectF(topLeft, bottomRight); } QPainterPath KisTool::pixelToView(const QPainterPath &pixelPolygon) const { QTransform matrix; qreal zoomX, zoomY; canvas()->viewConverter()->zoom(&zoomX, &zoomY); matrix.scale(zoomX/image()->xRes(), zoomY/ image()->yRes()); return matrix.map(pixelPolygon); } QPolygonF KisTool::pixelToView(const QPolygonF &pixelPath) const { QTransform matrix; qreal zoomX, zoomY; canvas()->viewConverter()->zoom(&zoomX, &zoomY); matrix.scale(zoomX/image()->xRes(), zoomY/ image()->yRes()); return matrix.map(pixelPath); } void KisTool::updateCanvasPixelRect(const QRectF &pixelRect) { canvas()->updateCanvas(convertToPt(pixelRect)); } void KisTool::updateCanvasViewRect(const QRectF &viewRect) { canvas()->updateCanvas(canvas()->viewConverter()->viewToDocument(viewRect)); } KisImageWSP KisTool::image() const { // For now, krita tools only work in krita, not for a krita shape. Krita shapes are for 2.1 KisCanvas2 * kisCanvas = dynamic_cast(canvas()); if (kisCanvas) { return kisCanvas->currentImage(); } return 0; } QCursor KisTool::cursor() const { return d->cursor; } void KisTool::notifyModified() const { if (image()) { image()->setModified(); } } KoPattern * KisTool::currentPattern() { return d->currentPattern; } KoAbstractGradient * KisTool::currentGradient() { return d->currentGradient; } KisPaintOpPresetSP KisTool::currentPaintOpPreset() { return canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); } KisNodeSP KisTool::currentNode() { KisNodeSP node = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value(); return node; } KoColor KisTool::currentFgColor() { return d->currentFgColor; } KoColor KisTool::currentBgColor() { return d->currentBgColor; } KisImageWSP KisTool::currentImage() { return image(); } KisFilterConfiguration * KisTool::currentGenerator() { return d->currentGenerator; } void KisTool::setMode(ToolMode mode) { m_mode = mode; } KisTool::ToolMode KisTool::mode() const { return m_mode; } KisTool::AlternateAction KisTool::actionToAlternateAction(ToolAction action) { KIS_ASSERT_RECOVER_RETURN_VALUE(action != Primary, Secondary); return (AlternateAction)action; } void KisTool::activatePrimaryAction() { resetCursorStyle(); } void KisTool::deactivatePrimaryAction() { resetCursorStyle(); } void KisTool::beginPrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::beginPrimaryDoubleClickAction(KoPointerEvent *event) { beginPrimaryAction(event); } void KisTool::continuePrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::endPrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); } bool KisTool::primaryActionSupportsHiResEvents() const { return false; } void KisTool::activateAlternateAction(AlternateAction action) { Q_UNUSED(action); } void KisTool::deactivateAlternateAction(AlternateAction action) { Q_UNUSED(action); } void KisTool::beginAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(event); Q_UNUSED(action); } void KisTool::beginAlternateDoubleClickAction(KoPointerEvent *event, AlternateAction action) { beginAlternateAction(event, action); } void KisTool::continueAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(event); Q_UNUSED(action); } void KisTool::endAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(event); Q_UNUSED(action); } void KisTool::mouseDoubleClickEvent(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::mouseTripleClickEvent(KoPointerEvent *event) { mouseDoubleClickEvent(event); } void KisTool::mousePressEvent(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::mouseReleaseEvent(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::mouseMoveEvent(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::deleteSelection() { KisResourcesSnapshotSP resources = new KisResourcesSnapshot(image(), currentNode(), 0, this->canvas()->resourceManager()); KisSelectionSP selection = resources->activeSelection(); KisNodeSP node = resources->currentNode(); if(node && node->hasEditablePaintDevice()) { KisPaintDeviceSP device = node->paintDevice(); image()->barrierLock(); KisTransaction transaction(kundo2_i18n("Clear"), device); QRect dirtyRect; if (selection) { dirtyRect = selection->selectedRect(); device->clearSelection(selection); } else { dirtyRect = device->extent(); device->clear(); } transaction.commit(image()->undoAdapter()); device->setDirty(dirtyRect); image()->unlock(); } else { KoToolBase::deleteSelection(); } } void KisTool::setupPaintAction(KisRecordedPaintAction* action) { action->setPaintColor(currentFgColor()); action->setBackgroundColor(currentBgColor()); } QWidget* KisTool::createOptionWidget() { d->optionWidget = new QLabel(i18n("No options")); d->optionWidget->setObjectName("SpecialSpacer"); return d->optionWidget; } #define NEAR_VAL -1000.0 #define FAR_VAL 1000.0 #define PROGRAM_VERTEX_ATTRIBUTE 0 void KisTool::paintToolOutline(QPainter* painter, const QPainterPath &path) { #ifdef HAVE_OPENGL KisOpenGLCanvas2 *canvasWidget = dynamic_cast(canvas()->canvasWidget()); if (canvasWidget && !d->useGLToolOutlineWorkaround) { painter->beginNativePainting(); if (d->cursorShader == 0) { - d->cursorShader = new QGLShaderProgram(); - d->cursorShader->addShaderFromSourceFile(QGLShader::Vertex, KGlobal::dirs()->findResource("data", "krita/shaders/cursor.vert")); - d->cursorShader->addShaderFromSourceFile(QGLShader::Fragment, KGlobal::dirs()->findResource("data", "krita/shaders/cursor.frag")); + d->cursorShader = new QOpenGLShaderProgram(); + d->cursorShader->addShaderFromSourceFile(QOpenGLShader::Vertex, KGlobal::dirs()->findResource("data", "krita/shaders/cursor.vert")); + d->cursorShader->addShaderFromSourceFile(QOpenGLShader::Fragment, KGlobal::dirs()->findResource("data", "krita/shaders/cursor.frag")); d->cursorShader->bindAttributeLocation("a_vertexPosition", PROGRAM_VERTEX_ATTRIBUTE); if (! d->cursorShader->link()) { qDebug() << "OpenGL error" << glGetError(); qFatal("Failed linking cursor shader"); } Q_ASSERT(d->cursorShader->isLinked()); d->cursorShaderModelViewProjectionUniform = d->cursorShader->uniformLocation("modelViewProjection"); } d->cursorShader->bind(); // setup the mvp transformation KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); const KisCoordinatesConverter *converter = kritaCanvas->coordinatesConverter(); QMatrix4x4 projectionMatrix; projectionMatrix.setToIdentity(); projectionMatrix.ortho(0, canvasWidget->width(), canvasWidget->height(), 0, NEAR_VAL, FAR_VAL); // Set view/projection matrices QMatrix4x4 modelMatrix(converter->flakeToWidgetTransform()); modelMatrix.optimize(); modelMatrix = projectionMatrix * modelMatrix; d->cursorShader->setUniformValue(d->cursorShaderModelViewProjectionUniform, modelMatrix); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // XXX: not in ES 2.0 -- in that case, we should not go here. it seems to be in 3.1 core profile, but my book is very unclear glEnable(GL_COLOR_LOGIC_OP); glLogicOp(GL_XOR); // setup the array of vertices QVector vertices; QList subPathPolygons = path.toSubpathPolygons(); for (int i=0; icursorShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); d->cursorShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, vertices.constData()); glDrawArrays(GL_LINE_STRIP, 0, vertices.size()); vertices.clear(); } glDisable(GL_COLOR_LOGIC_OP); d->cursorShader->release(); painter->endNativePainting(); } else if (canvasWidget && d->useGLToolOutlineWorkaround) { #else if (d->useGLToolOutlineWorkaround) { #endif // HAVE_OPENGL // the workaround option is enabled for Qt 4.6 < 4.6.3... Only relevant on CentOS. painter->setCompositionMode(QPainter::CompositionMode_SourceOver); QPen pen = painter->pen(); pen.setStyle(Qt::SolidLine); pen.setWidth(1); pen.setColor(QColor(255, 255, 255)); painter->setPen(pen); painter->drawPath(path); pen.setStyle(Qt::DotLine); pen.setWidth(1); pen.setColor(QColor(0, 0, 0)); painter->setPen(pen); painter->drawPath(path); } else { painter->setCompositionMode(QPainter::RasterOp_SourceXorDestination); painter->setPen(QColor(128, 255, 128)); painter->drawPath(path); } } void KisTool::resetCursorStyle() { useCursor(d->cursor); } bool KisTool::overrideCursorIfNotEditable() { // override cursor for canvas iff this tool is active // and we can't paint on the active layer if (isActive()) { KisNodeSP node = currentNode(); if (node && !node->isEditable()) { canvas()->setCursor(Qt::ForbiddenCursor); return true; } } return false; } bool KisTool::isActive() const { return m_isActive; } void KisTool::slotToggleFgBg() { KoCanvasResourceManager* resourceManager = canvas()->resourceManager(); KoColor newFg = resourceManager->backgroundColor(); KoColor newBg = resourceManager->foregroundColor(); /** * NOTE: Some of color selectors do not differentiate foreground * and background colors, so if one wants them to end up * being set up to foreground color, it should be set the * last. */ resourceManager->setBackgroundColor(newBg); resourceManager->setForegroundColor(newFg); } void KisTool::slotResetFgBg() { KoCanvasResourceManager* resourceManager = canvas()->resourceManager(); // see a comment in slotToggleFgBg() resourceManager->setBackgroundColor(KoColor(Qt::white, KoColorSpaceRegistry::instance()->rgb8())); resourceManager->setForegroundColor(KoColor(Qt::black, KoColorSpaceRegistry::instance()->rgb8())); } void KisTool::setCurrentNodeLocked(bool locked) { if (currentNode()) { currentNode()->setSystemLocked(locked, false); } } bool KisTool::nodeEditable() { KisNodeSP node = currentNode(); if (!node) { return false; } if (!node->isEditable()) { KisCanvas2 * kiscanvas = static_cast(canvas()); QString message; if (!node->visible() && node->userLocked()) { message = i18n("Layer is locked and invisible."); } else if (node->userLocked()) { message = i18n("Layer is locked."); } else if(!node->visible()) { message = i18n("Layer is invisible."); } else { message = i18n("Group not editable."); } kiscanvas->viewManager()->showFloatingMessage(message, koIcon("object-locked")); } return node->isEditable(); } bool KisTool::selectionEditable() { KisCanvas2 * kisCanvas = static_cast(canvas()); KisViewManager * view = kisCanvas->viewManager(); bool editable = view->selectionEditable(); if (!editable) { KisCanvas2 * kiscanvas = static_cast(canvas()); kiscanvas->viewManager()->showFloatingMessage(i18n("Local selection is locked."), koIcon("object-locked")); } return editable; } void KisTool::listenToModifiers(bool listen) { Q_UNUSED(listen); } bool KisTool::listeningToModifiers() { return false; } diff --git a/libs/flake/KoCanvasController.h b/libs/flake/KoCanvasController.h index 362533c1e21..719cde533da 100644 --- a/libs/flake/KoCanvasController.h +++ b/libs/flake/KoCanvasController.h @@ -1,471 +1,471 @@ /* This file is part of the KDE project * Copyright (C) 2006, 2008 Thomas Zander * Copyright (C) 2007-2010 Boudewijn Rempt * Copyright (C) 2007-2008 C. Boemann * Copyright (C) 2006-2007 Jan Hambrecht * Copyright (C) 2009 Thorsten Zachmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOCANVASCONTROLLER_H #define KOCANVASCONTROLLER_H #include "flake_export.h" #include #include #include #include class KActionCollection; class QRect; class QRectF; class KoShape; class KoCanvasBase; class KoCanvasControllerProxyObject; /** * KoCanvasController is the base class for wrappers around your canvas * that provides scrolling and zooming for your canvas. * * Flake does not provide a canvas, the application will have to * implement a canvas themselves. You canvas can be QWidget-based, QGraphicsItem-based * or something we haven't invented yet -- as long the class that holds the canvas * imlements KoCanvasController, tools, scrolling and zooming will work. * * A KoCanvasController implementation acts as a decorator around the canvas widget or * graphics item and provides a way to scroll the canvas, allows the canvas to be centered * in the viewArea and manages tool activation. * *

The using application can instantiate this class and add its * canvas using the setCanvas() call. Which is designed so it can be * called multiple times if you need to exchange one canvas - * widget for another, for instance, switching between a plain QWidget or a QGLWidget. + * widget for another, for instance, switching between a plain QWidget or a QOpenGLWidget. * *

There is _one_ KoCanvasController per canvas in your * application. * *

The canvas widget is at most as big as the viewport of the scroll * area, and when the view on the document is near its edges, smaller. * In your canvas widget code, you can find the right place in your * document in view coordinates (pixels) by adding the documentOffset */ class FLAKE_EXPORT KoCanvasController { public: /// An enum to alter the positioning and size of the canvas inside the canvas controller enum CanvasMode { AlignTop, ///< canvas is top aligned if smaller than the viewport Centered, ///< canvas is centered if smaller than the viewport Infinite, ///< canvas is never smaller than the viewport Spreadsheet ///< same as Infinite, but supports right-to-left layouts }; // proxy QObject: use this to connect to slots and signals. KoCanvasControllerProxyObject *proxyObject; /** * Constructor. * @param actionCollection the action collection for this canvas */ explicit KoCanvasController(KActionCollection* actionCollection); virtual ~KoCanvasController(); public: /** * Returns the current margin that is used to pad the canvas with. * This value is read from the KConfig property "canvasmargin" */ virtual int margin() const; /** * Set the new margin to pad the canvas with. */ virtual void setMargin(int margin); /** * Sets the how the canvas behaves if the zoomed document becomes smaller than the viewport. * @param mode the new canvas mode, CanvasMode::Centered is the default value */ virtual void setCanvasMode(KoCanvasController::CanvasMode mode); /// Returns the current canvas mode virtual KoCanvasController::CanvasMode canvasMode() const; /** * compatibility with QAbstractScrollArea */ virtual void scrollContentsBy(int dx, int dy) = 0; /** * @return the size of the viewport */ virtual QSize viewportSize() const = 0; /** * Set the shadow option -- by default the canvas controller draws * a black shadow around the canvas widget, which you may or may * not want. * * @param drawShadow if true, the shadow is drawn, if false, not */ virtual void setDrawShadow(bool drawShadow) = 0; /** * Set the new canvas to be shown as a child * Calling this will emit canvasRemoved() if there was a canvas before, and will emit * canvasSet() with the new canvas. * @param canvas the new canvas. The KoCanvasBase::canvas() will be called to retrieve the * actual widget which will then be added as child of this one. */ virtual void setCanvas(KoCanvasBase *canvas) = 0; /** * Return the currently set canvas. The default implementation will return Null * @return the currently set canvas */ virtual KoCanvasBase *canvas() const; /** * return the amount of pixels vertically visible of the child canvas. * @return the amount of pixels vertically visible of the child canvas. */ virtual int visibleHeight() const = 0; /** * return the amount of pixels horizontally visible of the child canvas. * @return the amount of pixels horizontally visible of the child canvas. */ virtual int visibleWidth() const = 0; /** * return the amount of pixels that are not visible on the left side of the canvas. * The leftmost pixel that is shown is returned. */ virtual int canvasOffsetX() const = 0; /** * return the amount of pixels that are not visible on the top side of the canvas. * The topmost pixel that is shown is returned. */ virtual int canvasOffsetY() const = 0; /** * @brief Scrolls the content of the canvas so that the given rect is visible. * * The rect is to be specified in view coordinates (pixels). The scrollbar positions * are changed so that the centerpoint of the rectangle is centered if possible. * * @param rect the rectangle to make visible * @param smooth if true the viewport translation will make be just enough to ensure visibility, no more. * @see KoViewConverter::documentToView() */ virtual void ensureVisible(const QRectF &rect, bool smooth = false) = 0; /** * @brief Scrolls the content of the canvas so that the given shape is visible. * * This is just a wrapper function of the above function. * * @param shape the shape to make visible */ virtual void ensureVisible(KoShape *shape) = 0; /** * @brief zooms in around the center. * * The center must be specified in view coordinates (pixels). The scrollbar positions * are changed so that the center becomes center if possible. * * @param center the position to zoom in on */ virtual void zoomIn(const QPoint ¢er) = 0; /** * @brief zooms out around the center. * * The center must be specified in view coordinates (pixels). The scrollbar positions * are changed so that the center becomes center if possible. * * @param center the position to zoom out around */ virtual void zoomOut(const QPoint ¢er) = 0; /** * @brief zooms around the center. * * The center must be specified in view coordinates (pixels). The scrollbar positions * are changed so that the center becomes center if possible. * * @param center the position to zoom around * @param zoom the zoom to apply */ virtual void zoomBy(const QPoint ¢er, qreal zoom) = 0; /** * @brief zoom so that rect is exactly visible (as close as possible) * * The rect must be specified in view coordinates (pixels). The scrollbar positions * are changed so that the center of the rect becomes center if possible. * * @param rect the rect in view coordinates (pixels) that should fit the view afterwards */ virtual void zoomTo(const QRect &rect) = 0; /** * @brief repositions the scrollbars so previous center is once again center * * The previous center is cached from when the user uses the scrollbars or zoomTo * are called. zoomTo is mostly used when a zoom tool of sorts have marked an area * to zoom in on * * The success of this method is limited by the size of thing. But we try our best. */ virtual void recenterPreferred() = 0; /** * Sets the preferred center point in view coordinates (pixels). * @param viewPoint the new preferred center */ virtual void setPreferredCenter(const QPointF &viewPoint) = 0; /// Returns the currently set preferred center point in view coordinates (pixels) virtual QPointF preferredCenter() const = 0; /** * Move the canvas over the x and y distance of the parameter distance * @param distance the distance in view coordinates (pixels). A positive distance means moving the canvas up/left. */ virtual void pan(const QPoint &distance) = 0; /** * Get the position of the scrollbar */ virtual QPoint scrollBarValue() const = 0; /** * Set the position of the scrollbar * @param value the new values of the scroll bars */ virtual void setScrollBarValue(const QPoint &value) = 0; /** * Called when the size of your document in view coordinates (pixels) changes, for instance when zooming. * * @param newSize the new size, in view coordinates (pixels), of the document. * @param recalculateCenter if true the offset in the document we center on after calling * recenterPreferred() will be recalculated for the new document size so the visual offset stays the same. */ virtual void updateDocumentSize(const QSize &sz, bool recalculateCenter) = 0; /** * Set mouse wheel to zoom behaviour * @param zoom if true wheel will zoom instead of scroll, control modifier will scroll */ virtual void setZoomWithWheel(bool zoom) = 0; /** * Set scroll area to be bigger than actual document. * It allows the user to move the corner of the document * to e.g. the center of the screen * * @param factor the coefficient, defining how much we can scroll out, * measured in parts of the widget size. Null value means vast * scrolling is disabled. */ virtual void setVastScrolling(qreal factor) = 0; /** * Returns the action collection for the canvas * @returns action collection for this canvas, can be 0 */ virtual KActionCollection* actionCollection() const; QPoint documentOffset() const; protected: void setDocumentSize(const QSize &sz); QSize documentSize() const; void setPreferredCenterFractionX(qreal); qreal preferredCenterFractionX() const; void setPreferredCenterFractionY(qreal); qreal preferredCenterFractionY() const; void setDocumentOffset( QPoint &offset); private: class Private; Private * const d; }; /** * Workaround class for the problem that Qt does not allow two QObject base classes. * KoCanvasController can be implemented by for instance QWidgets, so it cannot be * a QObject directly. The interface of this class should be considered public interface * for KoCanvasController. */ class FLAKE_EXPORT KoCanvasControllerProxyObject : public QObject { Q_OBJECT Q_DISABLE_COPY(KoCanvasControllerProxyObject) public: explicit KoCanvasControllerProxyObject(KoCanvasController *canvasController, QObject *parent = 0); public: // Convenience methods to invoke the signals from subclasses void emitCanvasRemoved(KoCanvasController *canvasController) { emit canvasRemoved(canvasController); } void emitCanvasSet(KoCanvasController *canvasController) { emit canvasSet(canvasController); } void emitCanvasOffsetXChanged(int offset) { emit canvasOffsetXChanged(offset); } void emitCanvasOffsetYChanged(int offset) { emit canvasOffsetYChanged(offset); } void emitCanvasMousePositionChanged(const QPoint &position) { emit canvasMousePositionChanged(position); } void emitDocumentMousePositionChanged(const QPointF &position) { emit documentMousePositionChanged(position); } void emitSizeChanged(const QSize &size) { emit sizeChanged(size); } void emitMoveDocumentOffset(const QPoint &point) { emit moveDocumentOffset(point); } void emitZoomRelative(const qreal factor, const QPointF &stillPoint) { emit zoomRelative(factor, stillPoint); } // Convenience method to retrieve the canvas controller for who needs to use QPointer KoCanvasController *canvasController() const { return m_canvasController; } Q_SIGNALS: /** * Emitted when a previously added canvas is about to be removed. * @param canvasController this object */ void canvasRemoved(KoCanvasController *canvasController); /** * Emitted when a canvas is set on this widget * @param canvasController this object */ void canvasSet(KoCanvasController *canvasController); /** * Emitted when canvasOffsetX() changes * @param offset the new canvas offset */ void canvasOffsetXChanged(int offset); /** * Emitted when canvasOffsetY() changes * @param offset the new canvas offset */ void canvasOffsetYChanged(int offset); /** * Emitted when the cursor is moved over the canvas widget. * @param position the position in view coordinates (pixels). */ void canvasMousePositionChanged(const QPoint &position); /** * Emitted when the cursor is moved over the canvas widget. * @param position the position in document coordinates. * * Use \ref canvasMousePositionChanged to get the position * in view coordinates. */ void documentMousePositionChanged(const QPointF &position); /** * Emitted when the entire controller size changes * @param size the size in widget pixels. */ void sizeChanged(const QSize &size); /** * Emitted whenever the document is scrolled. * * @param point the new top-left point from which the document should * be drawn. */ void moveDocumentOffset(const QPoint &point); /** * Emitted when zoomRelativeToPoint have calculated a factor by which * the zoom should change and the point which should stand still * on screen. * Someone needs to connect to this and take action * * @param factor by how much the zoom needs to change. * @param stillPoint the point which will not change its position * in widget during the zooming. It is measured in * view coordinate system *before* zoom. */ void zoomRelative(const qreal factor, const QPointF &stillPoint); public Q_SLOTS: /** * Call this slot whenever the size of your document in view coordinates (pixels) * changes, for instance when zooming. * @param newSize the new size, in view coordinates (pixels), of the document. * @param recalculateCenter if true the offset in the document we center on after calling * recenterPreferred() will be recalculated for the new document size so the visual offset stays the same. */ void updateDocumentSize(const QSize &newSize, bool recalculateCenter = true); private: KoCanvasController *m_canvasController; }; class FLAKE_EXPORT KoDummyCanvasController : public KoCanvasController { public: explicit KoDummyCanvasController(KActionCollection* actionCollection) : KoCanvasController(actionCollection) {} virtual ~KoDummyCanvasController() {} virtual void scrollContentsBy(int /*dx*/, int /*dy*/) {} virtual QSize viewportSize() const { return QSize(); } virtual void setDrawShadow(bool /*drawShadow*/) {} virtual void setCanvas(KoCanvasBase *canvas) {Q_UNUSED(canvas)} virtual KoCanvasBase *canvas() const {return 0;} virtual int visibleHeight() const {return 0;} virtual int visibleWidth() const {return 0;} virtual int canvasOffsetX() const {return 0;} virtual int canvasOffsetY() const {return 0;} virtual void ensureVisible(const QRectF &/*rect*/, bool /*smooth */ = false) {} virtual void ensureVisible(KoShape *shape) {Q_UNUSED(shape)} virtual void zoomIn(const QPoint &/*center*/) {} virtual void zoomOut(const QPoint &/*center*/) {} virtual void zoomBy(const QPoint &/*center*/, qreal /*zoom*/) {} virtual void zoomTo(const QRect &/*rect*/) {} virtual void recenterPreferred() {} virtual void setPreferredCenter(const QPointF &/*viewPoint*/) {} virtual QPointF preferredCenter() const {return QPointF();} virtual void pan(const QPoint &/*distance*/) {} virtual QPoint scrollBarValue() const {return QPoint();} virtual void setScrollBarValue(const QPoint &/*value*/) {} virtual void updateDocumentSize(const QSize &/*sz*/, bool /*recalculateCenter*/) {} virtual void setZoomWithWheel(bool /*zoom*/) {} virtual void setVastScrolling(qreal /*factor*/) {} }; #endif diff --git a/libs/flake/KoCanvasControllerWidget.cpp b/libs/flake/KoCanvasControllerWidget.cpp index 4288e7f955f..5e055c91a5e 100644 --- a/libs/flake/KoCanvasControllerWidget.cpp +++ b/libs/flake/KoCanvasControllerWidget.cpp @@ -1,624 +1,624 @@ /* This file is part of the KDE project * * Copyright (C) 2006, 2008-2009 Thomas Zander * Copyright (C) 2006 Peter Simonsson * Copyright (C) 2006, 2009 Thorsten Zachmann * Copyright (C) 2007-2010 Boudewijn Rempt * Copyright (C) 2007 C. Boemann * Copyright (C) 2006-2008 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoCanvasControllerWidget.h" #include "KoCanvasControllerWidget_p.h" #include "KoCanvasControllerWidgetViewport_p.h" #include "KoShape.h" #include "KoViewConverter.h" #include "KoCanvasBase.h" #include "KoCanvasObserverBase.h" #include "KoCanvasSupervisor.h" #include "KoToolManager_p.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_OPENGL -#include +#include #endif #include void KoCanvasControllerWidget::Private::setDocumentOffset() { // The margins scroll the canvas widget inside the viewport, not // the document. The documentOffset is meant to be the value that // the canvas must add to the update rect in its paint event, to // compensate. QPoint pt(q->horizontalScrollBar()->value(), q->verticalScrollBar()->value()); q->proxyObject->emitMoveDocumentOffset(pt); QWidget *canvasWidget = canvas->canvasWidget(); if (canvasWidget) { bool isCanvasOpenGL = false; QWidget *canvasWidget = canvas->canvasWidget(); if (canvasWidget) { #ifdef HAVE_OPENGL - if (qobject_cast(canvasWidget) != 0) { + if (qobject_cast(canvasWidget) != 0) { isCanvasOpenGL = true; } #endif } if (!isCanvasOpenGL) { QPoint diff = q->documentOffset() - pt; if (q->canvasMode() == Spreadsheet && canvasWidget->layoutDirection() == Qt::RightToLeft) { canvasWidget->scroll(-diff.x(), diff.y()); } else { canvasWidget->scroll(diff.x(), diff.y()); } } } q->setDocumentOffset(pt); } void KoCanvasControllerWidget::Private::resetScrollBars() { // The scrollbar value always points at the top-left corner of the // bit of image we paint. int docH = q->documentSize().height() + q->margin(); int docW = q->documentSize().width() + q->margin(); int drawH = viewportWidget->height(); int drawW = viewportWidget->width(); QScrollBar *hScroll = q->horizontalScrollBar(); QScrollBar *vScroll = q->verticalScrollBar(); int horizontalReserve = vastScrollingFactor * drawW; int verticalReserve = vastScrollingFactor * drawH; int xMin = -horizontalReserve; int yMin = -verticalReserve; int xMax = docW - drawW + horizontalReserve; int yMax = docH - drawH + verticalReserve; hScroll->setRange(xMin, xMax); vScroll->setRange(yMin, yMax); int fontheight = QFontMetrics(q->font()).height(); vScroll->setPageStep(drawH); vScroll->setSingleStep(fontheight); hScroll->setPageStep(drawW); hScroll->setSingleStep(fontheight); } void KoCanvasControllerWidget::Private::emitPointerPositionChangedSignals(QEvent *event) { if (!canvas) return; if (!canvas->viewConverter()) return; QPoint pointerPos; QMouseEvent *mouseEvent = dynamic_cast(event); if (mouseEvent) { pointerPos = mouseEvent->pos(); } else { QTabletEvent *tabletEvent = dynamic_cast(event); if (tabletEvent) { pointerPos = tabletEvent->pos(); } } QPoint pixelPos = (pointerPos - canvas->documentOrigin()) + q->documentOffset(); QPointF documentPos = canvas->viewConverter()->viewToDocument(pixelPos); q->proxyObject->emitDocumentMousePositionChanged(documentPos); q->proxyObject->emitCanvasMousePositionChanged(pointerPos); } #include void KoCanvasControllerWidget::Private::activate() { QWidget *parent = q; while (parent->parentWidget()) { parent = parent->parentWidget(); } KoCanvasSupervisor *observerProvider = dynamic_cast(parent); if (!observerProvider) { return; } foreach(KoCanvasObserverBase *docker, observerProvider->canvasObservers()) { KoCanvasObserverBase *observer = dynamic_cast(docker); if (observer) { observer->setObservedCanvas(q->canvas()); } } } void KoCanvasControllerWidget::Private::unsetCanvas() { QWidget *parent = q; while (parent->parentWidget()) { parent = parent->parentWidget(); } KoCanvasSupervisor *observerProvider = dynamic_cast(parent); if (!observerProvider) { return; } foreach(KoCanvasObserverBase *docker, observerProvider->canvasObservers()) { KoCanvasObserverBase *observer = dynamic_cast(docker); if (observer && observer->observedCanvas() == q->canvas()) { observer->unsetObservedCanvas(); } } } //////////// KoCanvasControllerWidget::KoCanvasControllerWidget(KActionCollection * actionCollection, QWidget *parent) : QAbstractScrollArea(parent) , KoCanvasController(actionCollection) , d(new Private(this)) { // We need to set this as QDeclarativeView sets them a bit differnt from QAbstractScrollArea setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // And then our own Viewport d->viewportWidget = new Viewport(this); setViewport(d->viewportWidget); d->viewportWidget->setFocusPolicy(Qt::NoFocus); setFocusPolicy(Qt::NoFocus); setFrameStyle(0); //setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); setAutoFillBackground(false); /* Fixes: apps starting at zero zoom. Details: Since the document is set on the mainwindow before loading commences the inial show/layout can choose to set the document to be very small, even to be zero pixels tall. Setting a sane minimum size on the widget means we no loger get rounding errors in zooming and we no longer end up with zero-zoom. Note: KoPage apps should probably startup with a sane document size; for Krita that's impossible */ setMinimumSize(QSize(50, 50)); setMouseTracking(true); connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(updateCanvasOffsetX())); connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(updateCanvasOffsetY())); connect(d->viewportWidget, SIGNAL(sizeChanged()), this, SLOT(updateCanvasOffsetX())); connect(proxyObject, SIGNAL(moveDocumentOffset(const QPoint&)), d->viewportWidget, SLOT(documentOffsetMoved(const QPoint&))); } KoCanvasControllerWidget::~KoCanvasControllerWidget() { d->unsetCanvas(); delete d; } void KoCanvasControllerWidget::activate() { d->activate(); } void KoCanvasControllerWidget::scrollContentsBy(int dx, int dy) { Q_UNUSED(dx); Q_UNUSED(dy); d->setDocumentOffset(); } QSize KoCanvasControllerWidget::viewportSize() const { return viewport()->size(); } void KoCanvasControllerWidget::setDrawShadow(bool drawShadow) { d->viewportWidget->setDrawShadow(drawShadow); } void KoCanvasControllerWidget::resizeEvent(QResizeEvent *resizeEvent) { proxyObject->emitSizeChanged(resizeEvent->size()); // XXX: When resizing, keep the area we're looking at now in the // center of the resized view. d->resetScrollBars(); d->setDocumentOffset(); } void KoCanvasControllerWidget::setCanvas(KoCanvasBase *canvas) { Q_ASSERT(canvas); // param is not null if (d->canvas) { d->unsetCanvas(); proxyObject->emitCanvasRemoved(this); canvas->setCanvasController(0); d->canvas->canvasWidget()->removeEventFilter(this); } canvas->setCanvasController(this); d->canvas = canvas; changeCanvasWidget(canvas->canvasWidget()); proxyObject->emitCanvasSet(this); QTimer::singleShot(0, this, SLOT(activate())); setPreferredCenterFractionX(0); setPreferredCenterFractionY(0); } KoCanvasBase* KoCanvasControllerWidget::canvas() const { return d->canvas; } void KoCanvasControllerWidget::changeCanvasWidget(QWidget *widget) { if (d->viewportWidget->canvas()) { widget->setCursor(d->viewportWidget->canvas()->cursor()); d->viewportWidget->canvas()->removeEventFilter(this); } d->viewportWidget->setCanvas(widget); setFocusProxy(d->canvas->canvasWidget()); widget->installEventFilter(this); widget->setMouseTracking(true); } int KoCanvasControllerWidget::visibleHeight() const { if (d->canvas == 0) return 0; QWidget *canvasWidget = canvas()->canvasWidget(); int height1; if (canvasWidget == 0) height1 = viewport()->height(); else height1 = qMin(viewport()->height(), canvasWidget->height()); int height2 = height(); return qMin(height1, height2); } int KoCanvasControllerWidget::visibleWidth() const { if (d->canvas == 0) return 0; QWidget *canvasWidget = canvas()->canvasWidget(); int width1; if (canvasWidget == 0) width1 = viewport()->width(); else width1 = qMin(viewport()->width(), canvasWidget->width()); int width2 = width(); return qMin(width1, width2); } int KoCanvasControllerWidget::canvasOffsetX() const { int offset = -horizontalScrollBar()->value(); if (d->canvas) { offset += d->canvas->canvasWidget()->x() + frameWidth(); } return offset; } int KoCanvasControllerWidget::canvasOffsetY() const { int offset = -verticalScrollBar()->value(); if (d->canvas) { offset += d->canvas->canvasWidget()->y() + frameWidth(); } return offset; } void KoCanvasControllerWidget::updateCanvasOffsetX() { proxyObject->emitCanvasOffsetXChanged(canvasOffsetX()); if (d->ignoreScrollSignals) return; setPreferredCenterFractionX((horizontalScrollBar()->value() + viewport()->width() / 2.0) / documentSize().width()); } void KoCanvasControllerWidget::updateCanvasOffsetY() { proxyObject->emitCanvasOffsetYChanged(canvasOffsetY()); if (d->ignoreScrollSignals) return; setPreferredCenterFractionY((verticalScrollBar()->value() + verticalScrollBar()->pageStep() / 2.0) / documentSize().height()); } bool KoCanvasControllerWidget::eventFilter(QObject *watched, QEvent *event) { if (d->canvas && d->canvas->canvasWidget() && (watched == d->canvas->canvasWidget())) { if (event->type() == QEvent::MouseMove || event->type() == QEvent::TabletMove) { d->emitPointerPositionChangedSignals(event); } } return false; } void KoCanvasControllerWidget::ensureVisible(KoShape *shape) { Q_ASSERT(shape); ensureVisible(d->canvas->viewConverter()->documentToView(shape->boundingRect())); } void KoCanvasControllerWidget::ensureVisible(const QRectF &rect, bool smooth) { QRect currentVisible(-canvasOffsetX(), -canvasOffsetY(), visibleWidth(), visibleHeight()); QRect viewRect = rect.toRect(); viewRect.translate(d->canvas->documentOrigin()); if (!viewRect.isValid() || currentVisible.contains(viewRect)) return; // its visible. Nothing to do. // if we move, we move a little more so the amount of times we have to move is less. int jumpWidth = smooth ? 0 : currentVisible.width() / 5; int jumpHeight = smooth ? 0 : currentVisible.height() / 5; if (!smooth && viewRect.width() + jumpWidth > currentVisible.width()) jumpWidth = 0; if (!smooth && viewRect.height() + jumpHeight > currentVisible.height()) jumpHeight = 0; int horizontalMove = 0; if (currentVisible.width() <= viewRect.width()) // center view horizontalMove = viewRect.center().x() - currentVisible.center().x(); else if (currentVisible.x() > viewRect.x()) // move left horizontalMove = viewRect.x() - currentVisible.x() - jumpWidth; else if (currentVisible.right() < viewRect.right()) // move right horizontalMove = viewRect.right() - qMax(0, currentVisible.right() - jumpWidth); int verticalMove = 0; if (currentVisible.height() <= viewRect.height()) // center view verticalMove = viewRect.center().y() - currentVisible.center().y(); if (currentVisible.y() > viewRect.y()) // move up verticalMove = viewRect.y() - currentVisible.y() - jumpHeight; else if (currentVisible.bottom() < viewRect.bottom()) // move down verticalMove = viewRect.bottom() - qMax(0, currentVisible.bottom() - jumpHeight); pan(QPoint(horizontalMove, verticalMove)); } void KoCanvasControllerWidget::recenterPreferred() { const bool oldIgnoreScrollSignals = d->ignoreScrollSignals; d->ignoreScrollSignals = true; QPointF center = preferredCenter(); // convert into a viewport based point center.rx() += d->canvas->canvasWidget()->x() + frameWidth(); center.ry() += d->canvas->canvasWidget()->y() + frameWidth(); // scroll to a new center point QPointF topLeft = center - 0.5 * QPointF(viewport()->width(), viewport()->height()); setScrollBarValue(topLeft.toPoint()); d->ignoreScrollSignals = oldIgnoreScrollSignals; } void KoCanvasControllerWidget::zoomIn(const QPoint ¢er) { zoomBy(center, sqrt(2.0)); } void KoCanvasControllerWidget::zoomOut(const QPoint ¢er) { zoomBy(center, sqrt(0.5)); } void KoCanvasControllerWidget::zoomBy(const QPoint ¢er, qreal zoom) { setPreferredCenterFractionX(1.0 * center.x() / documentSize().width()); setPreferredCenterFractionY(1.0 * center.y() / documentSize().height()); const bool oldIgnoreScrollSignals = d->ignoreScrollSignals; d->ignoreScrollSignals = true; proxyObject->emitZoomRelative(zoom, preferredCenter()); d->ignoreScrollSignals = oldIgnoreScrollSignals; } void KoCanvasControllerWidget::zoomTo(const QRect &viewRect) { qreal scale; if (1.0 * viewport()->width() / viewRect.width() > 1.0 * viewport()->height() / viewRect.height()) scale = 1.0 * viewport()->height() / viewRect.height(); else scale = 1.0 * viewport()->width() / viewRect.width(); zoomBy(viewRect.center(), scale); } void KoCanvasControllerWidget::setToolOptionWidgets(const QList >&widgetMap) { emit toolOptionWidgetsChanged(widgetMap); } void KoCanvasControllerWidget::updateDocumentSize(const QSize &sz, bool recalculateCenter) { // Don't update if the document-size didn't changed to prevent infinite loops and unneeded updates. if (KoCanvasController::documentSize() == sz) return; if (!recalculateCenter) { // assume the distance from the top stays equal and recalculate the center. setPreferredCenterFractionX(documentSize().width() * preferredCenterFractionX() / sz.width()); setPreferredCenterFractionY(documentSize().height() * preferredCenterFractionY() / sz.height()); } const bool oldIgnoreScrollSignals = d->ignoreScrollSignals; d->ignoreScrollSignals = true; KoCanvasController::setDocumentSize(sz); d->viewportWidget->setDocumentSize(sz); d->resetScrollBars(); // Always emit the new offset. updateCanvasOffsetX(); updateCanvasOffsetY(); d->ignoreScrollSignals = oldIgnoreScrollSignals; } void KoCanvasControllerWidget::setZoomWithWheel(bool zoom) { d->zoomWithWheel = zoom; } void KoCanvasControllerWidget::setVastScrolling(qreal factor) { d->vastScrollingFactor = factor; } void KoCanvasControllerWidget::pan(const QPoint &distance) { QPoint sourcePoint = scrollBarValue(); setScrollBarValue(sourcePoint + distance); } void KoCanvasControllerWidget::setPreferredCenter(const QPointF &viewPoint) { setPreferredCenterFractionX(viewPoint.x() / documentSize().width()); setPreferredCenterFractionY(viewPoint.y() / documentSize().height()); recenterPreferred(); } QPointF KoCanvasControllerWidget::preferredCenter() const { QPointF center; center.setX(preferredCenterFractionX() * documentSize().width()); center.setY(preferredCenterFractionY() * documentSize().height()); return center; } void KoCanvasControllerWidget::paintEvent(QPaintEvent *event) { QPainter gc(viewport()); d->viewportWidget->handlePaintEvent(gc, event); } void KoCanvasControllerWidget::dragEnterEvent(QDragEnterEvent *event) { d->viewportWidget->handleDragEnterEvent(event); } void KoCanvasControllerWidget::dropEvent(QDropEvent *event) { d->viewportWidget->handleDropEvent(event); } void KoCanvasControllerWidget::dragMoveEvent(QDragMoveEvent *event) { d->viewportWidget->handleDragMoveEvent(event); } void KoCanvasControllerWidget::dragLeaveEvent(QDragLeaveEvent *event) { d->viewportWidget->handleDragLeaveEvent(event); } void KoCanvasControllerWidget::keyPressEvent(QKeyEvent *event) { KoToolManager::instance()->priv()->switchToolByShortcut(event); } void KoCanvasControllerWidget::wheelEvent(QWheelEvent *event) { if (d->zoomWithWheel != ((event->modifiers() & Qt::ControlModifier) == Qt::ControlModifier)) { const qreal zoomCoeff = event->delta() > 0 ? sqrt(2.0) : sqrt(0.5); zoomRelativeToPoint(event->pos(), zoomCoeff); event->accept(); } else QAbstractScrollArea::wheelEvent(event); } void KoCanvasControllerWidget::zoomRelativeToPoint(const QPoint &widgetPoint, qreal zoomCoeff) { const QPoint offset = scrollBarValue(); const QPoint mousePos(widgetPoint + offset); const bool oldIgnoreScrollSignals = d->ignoreScrollSignals; d->ignoreScrollSignals = true; proxyObject->emitZoomRelative(zoomCoeff, mousePos); d->ignoreScrollSignals = oldIgnoreScrollSignals; } bool KoCanvasControllerWidget::focusNextPrevChild(bool) { // we always return false meaning the canvas takes keyboard focus, but never gives it away. return false; } void KoCanvasControllerWidget::setMargin(int margin) { KoCanvasController::setMargin(margin); Q_ASSERT(d->viewportWidget); d->viewportWidget->setMargin(margin); } QPoint KoCanvasControllerWidget::scrollBarValue() const { QScrollBar * hBar = horizontalScrollBar(); QScrollBar * vBar = verticalScrollBar(); return QPoint(hBar->value(), vBar->value()); } void KoCanvasControllerWidget::setScrollBarValue(const QPoint &value) { QScrollBar * hBar = horizontalScrollBar(); QScrollBar * vBar = verticalScrollBar(); hBar->setValue(value.x()); vBar->setValue(value.y()); } KoCanvasControllerWidget::Private *KoCanvasControllerWidget::priv() { return d; } //have to include this because of Q_PRIVATE_SLOT #include "moc_KoCanvasControllerWidget.cpp" diff --git a/libs/flake/KoCanvasControllerWidget.h b/libs/flake/KoCanvasControllerWidget.h index 12abde4f556..3cf459fbe0b 100644 --- a/libs/flake/KoCanvasControllerWidget.h +++ b/libs/flake/KoCanvasControllerWidget.h @@ -1,194 +1,194 @@ /* This file is part of the KDE project * Copyright (C) 2006, 2008 Thomas Zander * Copyright (C) 2007-2010 Boudewijn Rempt * Copyright (C) 2007-2008 C. Boemann * Copyright (C) 2006-2007 Jan Hambrecht * Copyright (C) 2009 Thorsten Zachmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOCANVASCONTROLLERWIDGET_H #define KOCANVASCONTROLLERWIDGET_H #include "flake_export.h" #include #include #include "KoCanvasController.h" class KoShape; class KoCanvasBase; /** * KoCanvasController implementation for QWidget based canvases */ class FLAKE_EXPORT KoCanvasControllerWidget : public QAbstractScrollArea, public KoCanvasController { Q_OBJECT public: /** * Constructor. * @param parent the parent this widget will belong to */ explicit KoCanvasControllerWidget(KActionCollection * actionCollection, QWidget *parent = 0); virtual ~KoCanvasControllerWidget(); /// Reimplemented from QObject virtual bool eventFilter(QObject *watched, QEvent *event); /** * Reimplemented from QAbstractScrollArea. */ void scrollContentsBy(int dx, int dy); virtual QSize viewportSize() const; /// Reimplemented from KoCanvasController /** * Activate this canvascontroller */ virtual void activate(); virtual void setDrawShadow(bool drawShadow); virtual void setCanvas(KoCanvasBase *canvas); virtual KoCanvasBase *canvas() const; /** * Change the actual canvas widget used by the current canvas. This allows the canvas widget * to be changed while keeping the current KoCanvasBase canvas and its associated resources as - * they are. This might be used, for example, to switch from a QWidget to a QGLWidget canvas. + * they are. This might be used, for example, to switch from a QWidget to a QOpenGLWidget canvas. * @param widget the new canvas widget. */ virtual void changeCanvasWidget(QWidget *widget); virtual int visibleHeight() const; virtual int visibleWidth() const; virtual int canvasOffsetX() const; virtual int canvasOffsetY() const; virtual void ensureVisible(const QRectF &rect, bool smooth = false); virtual void ensureVisible(KoShape *shape); /** * will cause the toolOptionWidgetsChanged to be emitted and all * listeners to be updated to the new widget. * * FIXME: This doesn't belong her and it does an * inherits("KoView") so it too much tied to komain * * @param widgets the map of widgets */ void setToolOptionWidgets(const QList > &widgets); virtual void zoomIn(const QPoint ¢er); virtual void zoomOut(const QPoint ¢er); virtual void zoomBy(const QPoint ¢er, qreal zoom); virtual void zoomTo(const QRect &rect); /** * Zoom document keeping point \p widgetPoint unchanged * \param widgetPoint sticky point in widget pixels */ virtual void zoomRelativeToPoint(const QPoint &widgetPoint, qreal zoomCoeff); virtual void recenterPreferred(); virtual void setPreferredCenter(const QPointF &viewPoint); /// Returns the currently set preferred center point in view coordinates (pixels) virtual QPointF preferredCenter() const; virtual void pan(const QPoint &distance); virtual void setMargin(int margin); virtual QPoint scrollBarValue() const; /** * Used by KisCanvasController to correct the scrollbars position * after the rotation. */ virtual void setScrollBarValue(const QPoint &value); virtual void updateDocumentSize(const QSize &sz, bool recalculateCenter = true); /** * Set mouse wheel to zoom behaviour * @param zoom if true wheel will zoom instead of scroll, control modifier will scroll */ void setZoomWithWheel(bool zoom); virtual void setVastScrolling(qreal factor); /** * \internal */ class Private; KoCanvasControllerWidget::Private *priv(); Q_SIGNALS: /** * Emit the new tool option widgets to be used with this canvas. */ void toolOptionWidgetsChanged(const QList > &widgets); private Q_SLOTS: /// Called by the horizontal scrollbar when its value changes void updateCanvasOffsetX(); /// Called by the vertical scrollbar when its value changes void updateCanvasOffsetY(); protected: friend class KisZoomAndPanTest; /// reimplemented from QWidget virtual void paintEvent(QPaintEvent *event); /// reimplemented from QWidget virtual void resizeEvent(QResizeEvent *resizeEvent); /// reimplemented from QWidget virtual void dragEnterEvent(QDragEnterEvent *event); /// reimplemented from QWidget virtual void dropEvent(QDropEvent *event); /// reimplemented from QWidget virtual void dragMoveEvent(QDragMoveEvent *event); /// reimplemented from QWidget virtual void dragLeaveEvent(QDragLeaveEvent *event); /// reimplemented from QWidget virtual void wheelEvent(QWheelEvent *event); /// reimplemented from QWidget virtual void keyPressEvent(QKeyEvent *event); /// reimplemented from QWidget virtual bool focusNextPrevChild(bool next); private: Q_PRIVATE_SLOT(d, void activate()) Private * const d; }; #endif