diff --git a/libs/ui/tool/kis_resources_snapshot.cpp b/libs/ui/tool/kis_resources_snapshot.cpp index 7c556b7310..1e4f87d28e 100644 --- a/libs/ui/tool/kis_resources_snapshot.cpp +++ b/libs/ui/tool/kis_resources_snapshot.cpp @@ -1,399 +1,399 @@ /* * Copyright (c) 2011 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. */ #include "kis_resources_snapshot.h" #include #include #include #include #include #include #include #include #include "kis_canvas_resource_provider.h" #include "filter/kis_filter_configuration.h" #include "kis_image.h" #include "kis_paint_device.h" #include "kis_paint_layer.h" #include "recorder/kis_recorded_paint_action.h" #include "kis_selection.h" #include "kis_selection_mask.h" #include "kis_algebra_2d.h" struct KisResourcesSnapshot::Private { Private() : currentPattern(0) , currentGradient(0) , currentGenerator(0) , compositeOp(0) { } KisImageSP image; KisDefaultBoundsBaseSP bounds; KoColor currentFgColor; KoColor currentBgColor; - KoPattern *currentPattern; + KoPattern *currentPattern = 0; KoAbstractGradient *currentGradient; KisPaintOpPresetSP currentPaintOpPreset; KisNodeSP currentNode; qreal currentExposure; KisFilterConfigurationSP currentGenerator; QPointF axesCenter; - bool mirrorMaskHorizontal; - bool mirrorMaskVertical; + bool mirrorMaskHorizontal = false; + bool mirrorMaskVertical = false; - quint8 opacity; - QString compositeOpId; + quint8 opacity = OPACITY_OPAQUE_U8; + QString compositeOpId = COMPOSITE_OVER; const KoCompositeOp *compositeOp; - KisPainter::StrokeStyle strokeStyle; - KisPainter::FillStyle fillStyle; + KisPainter::StrokeStyle strokeStyle = KisPainter::StrokeStyleBrush; + KisPainter::FillStyle fillStyle = KisPainter::FillStyleForegroundColor; - bool globalAlphaLock; - qreal effectiveZoom; - bool presetAllowsLod; + bool globalAlphaLock = false; + qreal effectiveZoom = 1.0; + bool presetAllowsLod = false; KisSelectionSP selectionOverride; }; KisResourcesSnapshot::KisResourcesSnapshot(KisImageSP image, KisNodeSP currentNode, KoCanvasResourceManager *resourceManager, KisDefaultBoundsBaseSP bounds) : m_d(new Private()) { m_d->image = image; if (!bounds) { bounds = new KisDefaultBounds(m_d->image); } m_d->bounds = bounds; m_d->currentFgColor = resourceManager->resource(KoCanvasResourceManager::ForegroundColor).value(); m_d->currentBgColor = resourceManager->resource(KoCanvasResourceManager::BackgroundColor).value(); m_d->currentPattern = resourceManager->resource(KisCanvasResourceProvider::CurrentPattern).value(); m_d->currentGradient = resourceManager->resource(KisCanvasResourceProvider::CurrentGradient).value(); /** * We should deep-copy the preset, so that long-runnign actions * will have correct brush parameters. Theoretically this cloniong * can be expensive, but according to measurements, it takes * something like 0.1 ms for an average preset. */ m_d->currentPaintOpPreset = resourceManager->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value()->clone(); #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND KisPaintOpRegistry::instance()->preinitializePaintOpIfNeeded(m_d->currentPaintOpPreset); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ m_d->currentExposure = resourceManager->resource(KisCanvasResourceProvider::HdrExposure).toDouble(); m_d->currentGenerator = resourceManager->resource(KisCanvasResourceProvider::CurrentGeneratorConfiguration).value(); QPointF relativeAxesCenter(0.5, 0.5); if (m_d->image) { relativeAxesCenter = m_d->image->mirrorAxesCenter(); } m_d->axesCenter = KisAlgebra2D::relativeToAbsolute(relativeAxesCenter, m_d->bounds->bounds()); m_d->mirrorMaskHorizontal = resourceManager->resource(KisCanvasResourceProvider::MirrorHorizontal).toBool(); m_d->mirrorMaskVertical = resourceManager->resource(KisCanvasResourceProvider::MirrorVertical).toBool(); qreal normOpacity = resourceManager->resource(KisCanvasResourceProvider::Opacity).toDouble(); m_d->opacity = quint8(normOpacity * OPACITY_OPAQUE_U8); m_d->compositeOpId = resourceManager->resource(KisCanvasResourceProvider::CurrentEffectiveCompositeOp).toString(); setCurrentNode(currentNode); /** * Fill and Stroke styles are not a part of the resource manager * so the tools should set them manually * TODO: port stroke and fill styles to be a part * of the resource manager */ m_d->strokeStyle = KisPainter::StrokeStyleBrush; m_d->fillStyle = KisPainter::FillStyleNone; m_d->globalAlphaLock = resourceManager->resource(KisCanvasResourceProvider::GlobalAlphaLock).toBool(); m_d->effectiveZoom = resourceManager->resource(KisCanvasResourceProvider::EffectiveZoom).toDouble(); m_d->presetAllowsLod = resourceManager->resource(KisCanvasResourceProvider::PresetAllowsLod).toBool(); } KisResourcesSnapshot::KisResourcesSnapshot(KisImageSP image, KisNodeSP currentNode, KisDefaultBoundsBaseSP bounds) : m_d(new Private()) { m_d->image = image; if (!bounds) { bounds = new KisDefaultBounds(m_d->image); } m_d->bounds = bounds; #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND KisPaintOpRegistry::instance()->preinitializePaintOpIfNeeded(m_d->currentPaintOpPreset); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ QPointF relativeAxesCenter(0.5, 0.5); if (m_d->image) { relativeAxesCenter = m_d->image->mirrorAxesCenter(); } m_d->axesCenter = KisAlgebra2D::relativeToAbsolute(relativeAxesCenter, m_d->bounds->bounds()); m_d->opacity = OPACITY_OPAQUE_U8; setCurrentNode(currentNode); /** * Fill and Stroke styles are not a part of the resource manager * so the tools should set them manually * TODO: port stroke and fill styles to be a part * of the resource manager */ m_d->strokeStyle = KisPainter::StrokeStyleBrush; m_d->fillStyle = KisPainter::FillStyleNone; } KisResourcesSnapshot::~KisResourcesSnapshot() { delete m_d; } void KisResourcesSnapshot::setupPainter(KisPainter* painter) { painter->setPaintColor(m_d->currentFgColor); painter->setBackgroundColor(m_d->currentBgColor); painter->setGenerator(m_d->currentGenerator); painter->setPattern(m_d->currentPattern); painter->setGradient(m_d->currentGradient); QBitArray lockflags = channelLockFlags(); if (lockflags.size() > 0) { painter->setChannelFlags(lockflags); } painter->setOpacity(m_d->opacity); painter->setCompositeOp(m_d->compositeOp); painter->setMirrorInformation(m_d->axesCenter, m_d->mirrorMaskHorizontal, m_d->mirrorMaskVertical); painter->setStrokeStyle(m_d->strokeStyle); painter->setFillStyle(m_d->fillStyle); /** * The paintOp should be initialized the last, because it may * ask the painter for some options while initialization */ painter->setPaintOpPreset(m_d->currentPaintOpPreset, m_d->currentNode, m_d->image); } void KisResourcesSnapshot::setupPaintAction(KisRecordedPaintAction *action) { action->setPaintOpPreset(m_d->currentPaintOpPreset); action->setPaintIncremental(!needsIndirectPainting()); action->setPaintColor(m_d->currentFgColor); action->setBackgroundColor(m_d->currentBgColor); action->setGenerator(m_d->currentGenerator); action->setGradient(m_d->currentGradient); action->setPattern(m_d->currentPattern); action->setOpacity(m_d->opacity / qreal(OPACITY_OPAQUE_U8)); action->setCompositeOp(m_d->compositeOp->id()); action->setStrokeStyle(m_d->strokeStyle); action->setFillStyle(m_d->fillStyle); } KisPostExecutionUndoAdapter* KisResourcesSnapshot::postExecutionUndoAdapter() const { return m_d->image ? m_d->image->postExecutionUndoAdapter() : 0; } void KisResourcesSnapshot::setCurrentNode(KisNodeSP node) { m_d->currentNode = node; KisPaintDeviceSP device; if(m_d->currentNode && (device = m_d->currentNode->paintDevice())) { m_d->compositeOp = device->colorSpace()->compositeOp(m_d->compositeOpId); if(!m_d->compositeOp) { m_d->compositeOp = device->colorSpace()->compositeOp(COMPOSITE_OVER); } } } void KisResourcesSnapshot::setStrokeStyle(KisPainter::StrokeStyle strokeStyle) { m_d->strokeStyle = strokeStyle; } void KisResourcesSnapshot::setFillStyle(KisPainter::FillStyle fillStyle) { m_d->fillStyle = fillStyle; } KisNodeSP KisResourcesSnapshot::currentNode() const { return m_d->currentNode; } KisImageSP KisResourcesSnapshot::image() const { return m_d->image; } bool KisResourcesSnapshot::needsIndirectPainting() const { return !m_d->currentPaintOpPreset->settings()->paintIncremental(); } QString KisResourcesSnapshot::indirectPaintingCompositeOp() const { return m_d->currentPaintOpPreset->settings()->indirectPaintingCompositeOp(); } KisSelectionSP KisResourcesSnapshot::activeSelection() const { /** * It is possible to have/use the snapshot without the image. Such * usecase is present for example in the scratchpad. */ if (m_d->selectionOverride) { return m_d->selectionOverride; } KisSelectionSP selection = m_d->image ? m_d->image->globalSelection() : 0; KisLayerSP layer = qobject_cast(m_d->currentNode.data()); KisSelectionMaskSP mask; if((layer = qobject_cast(m_d->currentNode.data()))) { selection = layer->selection(); } else if ((mask = dynamic_cast(m_d->currentNode.data())) && mask->selection() == selection) { selection = 0; } return selection; } bool KisResourcesSnapshot::needsAirbrushing() const { return m_d->currentPaintOpPreset->settings()->isAirbrushing(); } qreal KisResourcesSnapshot::airbrushingInterval() const { return m_d->currentPaintOpPreset->settings()->airbrushInterval(); } bool KisResourcesSnapshot::needsSpacingUpdates() const { return m_d->currentPaintOpPreset->settings()->useSpacingUpdates(); } void KisResourcesSnapshot::setOpacity(qreal opacity) { m_d->opacity = opacity * OPACITY_OPAQUE_U8; } quint8 KisResourcesSnapshot::opacity() const { return m_d->opacity; } const KoCompositeOp* KisResourcesSnapshot::compositeOp() const { return m_d->compositeOp; } QString KisResourcesSnapshot::compositeOpId() const { return m_d->compositeOpId; } KoPattern* KisResourcesSnapshot::currentPattern() const { return m_d->currentPattern; } KoColor KisResourcesSnapshot::currentFgColor() const { return m_d->currentFgColor; } KoColor KisResourcesSnapshot::currentBgColor() const { return m_d->currentBgColor; } KisPaintOpPresetSP KisResourcesSnapshot::currentPaintOpPreset() const { return m_d->currentPaintOpPreset; } QBitArray KisResourcesSnapshot::channelLockFlags() const { QBitArray channelFlags; KisPaintLayer *paintLayer; if ((paintLayer = dynamic_cast(m_d->currentNode.data()))) { channelFlags = paintLayer->channelLockFlags(); if (m_d->globalAlphaLock) { if (channelFlags.isEmpty()) { channelFlags = paintLayer->colorSpace()->channelFlags(true, true); } channelFlags &= paintLayer->colorSpace()->channelFlags(true, false); } } return channelFlags; } qreal KisResourcesSnapshot::effectiveZoom() const { return m_d->effectiveZoom; } bool KisResourcesSnapshot::presetAllowsLod() const { return m_d->presetAllowsLod; } bool KisResourcesSnapshot::presetNeedsAsynchronousUpdates() const { return m_d->currentPaintOpPreset && m_d->currentPaintOpPreset->settings()->needsAsynchronousUpdates(); } void KisResourcesSnapshot::setFGColorOverride(const KoColor &color) { m_d->currentFgColor = color; } void KisResourcesSnapshot::setBGColorOverride(const KoColor &color) { m_d->currentBgColor = color; } void KisResourcesSnapshot::setSelectionOverride(KisSelectionSP selection) { m_d->selectionOverride = selection; } void KisResourcesSnapshot::setBrush(const KisPaintOpPresetSP &brush) { m_d->currentPaintOpPreset = brush; } diff --git a/libs/ui/widgets/kis_preset_live_preview_view.cpp b/libs/ui/widgets/kis_preset_live_preview_view.cpp index 35496f655a..7647655f8c 100644 --- a/libs/ui/widgets/kis_preset_live_preview_view.cpp +++ b/libs/ui/widgets/kis_preset_live_preview_view.cpp @@ -1,309 +1,328 @@ /* * Copyright (c) 2017 Scott Petrovic * * 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 #include #include #include "kis_paintop_settings.h" +#include KisPresetLivePreviewView::KisPresetLivePreviewView(QWidget *parent): QGraphicsView(parent) { } KisPresetLivePreviewView::~KisPresetLivePreviewView() { - delete m_brushPreviewPainter; delete m_noPreviewText; delete m_brushPreviewScene; } void KisPresetLivePreviewView::setup() { // initializing to 0 helps check later if they actually have something in them m_noPreviewText = 0; m_sceneImageItem = 0; setCursor(Qt::SizeAllCursor); setHorizontalScrollBarPolicy ( Qt::ScrollBarAlwaysOff ); setVerticalScrollBarPolicy ( Qt::ScrollBarAlwaysOff ); // layer image needs to be big enough to get an entire stroke of data m_canvasSize.setWidth(1200); m_canvasSize.setHeight(400); m_canvasCenterPoint.setX(m_canvasSize.width()*0.5); m_canvasCenterPoint.setY(m_canvasSize.height()*0.5); m_colorSpace = KoColorSpaceRegistry::instance()->rgb8(); m_image = new KisImage(0, m_canvasSize.width(), m_canvasSize.height(), m_colorSpace, "stroke sample image"); m_layer = new KisPaintLayer(m_image, "livePreviewStrokeSample", OPACITY_OPAQUE_U8, m_colorSpace); - m_brushPreviewPainter = new KisPainter(m_layer->paintDevice()); // set scene for the view m_brushPreviewScene = new QGraphicsScene(); setScene(m_brushPreviewScene); zoomToBrushSize(); } void KisPresetLivePreviewView::setCurrentPreset(KisPaintOpPresetSP preset) { m_currentPreset = preset; } void KisPresetLivePreviewView::updateStroke() { paintBackground(); // do not paint a stroke if we are any of these engines (they have some issue currently) if (m_currentPreset->paintOp().id() == "roundmarker" || m_currentPreset->paintOp().id() == "experimentbrush" || m_currentPreset->paintOp().id() == "duplicate") { return; } setupAndPaintStroke(); // crop the layer so a brush stroke won't go outside of the area m_layer->paintDevice()->crop(0,0, m_layer->image()->width(), m_layer->image()->height()); QImage m_temp_image; m_temp_image = m_layer->paintDevice()->convertToQImage(0); // only add the object once...then just update the pixmap so we can move the preview around if (!m_sceneImageItem) { m_sceneImageItem = m_brushPreviewScene->addPixmap(QPixmap::fromImage(m_temp_image)); m_sceneImageItem->setFlag(QGraphicsItem::ItemIsSelectable); m_sceneImageItem->setFlag(QGraphicsItem::ItemIsMovable); m_sceneImageItem->setFlag(QGraphicsItem::ItemSendsGeometryChanges); m_sceneImageItem->setPos(-m_canvasCenterPoint.x(), -m_canvasCenterPoint.y()); // center the object } else { m_sceneImageItem->setPixmap(QPixmap::fromImage(m_temp_image)); } } void KisPresetLivePreviewView::slotResetViewZoom() { zoomToBrushSize(); } void KisPresetLivePreviewView::slotZoomToOneHundredPercent() { m_scaleFactor = 1.0; resetMatrix(); this->scale(m_scaleFactor, m_scaleFactor); } void KisPresetLivePreviewView::paintBackground() { // clean up "no preview" text object if it exists. we will add it later if we need it if (m_noPreviewText) { this->scene()->removeItem(m_noPreviewText); m_noPreviewText = 0; } if (m_currentPreset->paintOp().id() == "colorsmudge" || m_currentPreset->paintOp().id() == "deformbrush" || m_currentPreset->paintOp().id() == "filter") { // easier to see deformations and smudging with alternating stripes in the background // paint the whole background with alternating stripes // filter engine may or may not show things depending on the filter...but it is better than nothing int grayStrips = 20; for (int i=0; i < grayStrips; i++ ) { float sectionPercent = 1.0 / (float)grayStrips; bool isAlternating = i % 2; KoColor fillColor; if (isAlternating) { fillColor.fromQColor(QColor(80,80,80)); } else { fillColor.fromQColor(QColor(140,140,140)); } - m_brushPreviewPainter->fill(m_layer->image()->width()*sectionPercent*i, - 0, - m_layer->image()->width()*(sectionPercent*i +sectionPercent), - m_layer->image()->height(), - fillColor); - + const QRect fillRect(m_layer->image()->width()*sectionPercent*i, + 0, + m_layer->image()->width()*(sectionPercent*i +sectionPercent), + m_layer->image()->height()); + m_layer->paintDevice()->fill(fillRect, fillColor); } - m_brushPreviewPainter->setPaintColor(KoColor(Qt::white, m_colorSpace)); + m_paintColor = KoColor(Qt::white, m_colorSpace); } else if (m_currentPreset->paintOp().id() == "roundmarker" || m_currentPreset->paintOp().id() == "experimentbrush" || m_currentPreset->paintOp().id() == "duplicate" ) { // cases where we will not show a preview for now // roundbrush (quick) -- this isn't showing anything, disable showing preview // experimentbrush -- this creates artifacts that carry over to other previews and messes up their display // duplicate (clone) brush doesn't have a preview as it doesn't show anything) if(m_sceneImageItem) { this->scene()->removeItem(m_sceneImageItem); m_sceneImageItem = 0; } QFont font; font.setPixelSize(14); font.setBold(false); slotZoomToOneHundredPercent(); // 100% zoom if we are showing the text m_noPreviewText = this->scene()->addText(i18n("No Preview for this engine"),font); m_noPreviewText->setPos(-this->width()/3, -this->height()/4); // this mostly centers the text in the viewport return; } else { // fill with gray first to clear out what existed from previous preview - m_brushPreviewPainter->fill(0,0, - m_layer->image()->width(), - m_layer->image()->height(), - KoColor(palette().color(QPalette::Background) , m_colorSpace)); - - m_brushPreviewPainter->setPaintColor(KoColor(palette().color(QPalette::Text), m_colorSpace)); + m_layer->paintDevice()->fill(m_image->bounds(), KoColor(palette().color(QPalette::Background) , m_colorSpace)); + m_paintColor = KoColor(palette().color(QPalette::Text), m_colorSpace); } } void KisPresetLivePreviewView::setupAndPaintStroke() { // limit the brush stroke size. larger brush strokes just don't look good and are CPU intensive // we are making a proxy preset and setting it to the painter...otherwise setting the brush size of the original preset // will fire off signals that make this run in an infinite loop qreal originalPresetSize = m_currentPreset->settings()->paintOpSize(); qreal previewSize = qBound(1.0, m_currentPreset->settings()->paintOpSize(), 150.0 ); // constrain live preview brush size KisPaintOpPresetSP proxy_preset = m_currentPreset->clone(); proxy_preset->settings()->setPaintOpSize(previewSize); - m_brushPreviewPainter->setPaintOpPreset(proxy_preset, m_layer, m_image); + + + KisResourcesSnapshotSP resources = + new KisResourcesSnapshot(m_image, + m_layer); + + resources->setBrush(proxy_preset); + resources->setFGColorOverride(m_paintColor); + FreehandStrokeStrategy::PainterInfo *painterInfo = new FreehandStrokeStrategy::PainterInfo(); + + KisStrokeStrategy *stroke = + new FreehandStrokeStrategy(resources->needsIndirectPainting(), + resources->indirectPaintingCompositeOp(), + resources, painterInfo, kundo2_noi18n("temp_stroke")); + + KisStrokeId strokeId = m_image->startStroke(stroke); + + + + //m_brushPreviewPainter->setPaintOpPreset(proxy_preset, m_layer, m_image); // slope-intercept is good for mapping two values. // find the slope of the line (slope-intercept form) float slope = (m_maxScale-m_maxStrokeScale) / (m_minScale-m_minStrokeScale); // y2-y1 / x2-x1 float yIntercept = m_maxStrokeScale - slope * m_minStrokeScale; // y1 − m * x1 float strokeScaleAmount = m_scaleFactor * slope + yIntercept; // y = mx + b strokeScaleAmount = qBound(m_minStrokeScale, strokeScaleAmount, m_maxStrokeScale); // we only need to change the zoom amount if we are changing the brush size if (m_currentBrushSize != m_currentPreset->settings()->paintOpSize()) { m_currentBrushSize = m_currentPreset->settings()->paintOpSize(); zoomToBrushSize(); } // points for drawing an S curve // we are going to paint the stroke right in the middle of the canvas to make sure // everything is captured for big brush strokes if (m_currentPreset->paintOp().id() == "sketchbrush") { slotZoomToOneHundredPercent(); // sketch brush is always scaled at 100% KisPaintInformation pointOne; pointOne.setPressure(0.0); pointOne.setPos(QPointF(m_canvasCenterPoint.x() - (this->width() * 0.4), m_canvasCenterPoint.y() - (this->height()*0.2) )); KisPaintInformation pointTwo; pointTwo.setPressure(1.0); pointTwo.setPos(QPointF(m_canvasCenterPoint.x() + (this->width() * 0.4), m_canvasCenterPoint.y() + (this->height()*0.2) )); - m_brushPreviewPainter->paintBezierCurve(pointOne, - QPointF(m_canvasCenterPoint.x() + this->width(), - m_canvasCenterPoint.y() - (this->height()*0.2) ), - QPointF(m_canvasCenterPoint.x() - this->width(), - m_canvasCenterPoint.y() + (this->height()*0.2) ), - pointTwo, &m_currentDistance); - + m_image->addJob(strokeId, + new FreehandStrokeStrategy::Data(0, + pointOne, + QPointF(m_canvasCenterPoint.x() + this->width(), + m_canvasCenterPoint.y() - (this->height()*0.2) ), + QPointF(m_canvasCenterPoint.x() - this->width(), + m_canvasCenterPoint.y() + (this->height()*0.2) ), + pointTwo)); } else { m_curvePointPI1.setPos(QPointF(m_canvasCenterPoint.x() - (this->width()*strokeScaleAmount), m_canvasCenterPoint.y() + (this->height()*strokeScaleAmount))); m_curvePointPI1.setPressure(0.0); m_curvePointPI2.setPos(QPointF(m_canvasCenterPoint.x() + (this->width()*strokeScaleAmount), m_canvasCenterPoint.y())); m_curvePointPI2.setPressure(1.0); - m_brushPreviewPainter->paintBezierCurve(m_curvePointPI1, - QPointF(m_canvasCenterPoint.x(), - m_canvasCenterPoint.y()-this->height()), - QPointF(m_canvasCenterPoint.x(), - m_canvasCenterPoint.y()+this->height()), - m_curvePointPI2, &m_currentDistance); + m_image->addJob(strokeId, + new FreehandStrokeStrategy::Data(0, + m_curvePointPI1, + QPointF(m_canvasCenterPoint.x(), + m_canvasCenterPoint.y()-this->height()), + QPointF(m_canvasCenterPoint.x(), + m_canvasCenterPoint.y()+this->height()), + m_curvePointPI2)); } + m_image->addJob(strokeId, new FreehandStrokeStrategy::UpdateData(true)); + m_image->endStroke(strokeId); + m_image->waitForDone(); // even though the brush is cloned, the proxy_preset still has some connection to the original preset which will mess brush sizing // we need to return brush size to normal.The normal brush sends out a lot of extra signals, so keeping the proxy for now proxy_preset->settings()->setPaintOpSize(originalPresetSize); } void KisPresetLivePreviewView::zoomToBrushSize() { // find the slope of the line (slope-intercept form) float slope = (m_maxScale-m_minScale) / (m_maxBrushVal-m_minBrushVal); // y2-y1 / x2-x1 float yIntercept = m_minScale - slope * m_minBrushVal; // y1 − m * x1 // finally calculate our zoom level float thresholdValue = qBound(m_minBrushVal, m_currentBrushSize, m_maxBrushVal); m_scaleFactor = thresholdValue * slope + yIntercept; // y = mx + b resetMatrix(); this->scale(m_scaleFactor,m_scaleFactor); // reset position of image preview in case we moved it if(m_sceneImageItem) { m_sceneImageItem->setPos(-m_canvasSize.width()/2, -m_canvasSize.height()/2 ); } } diff --git a/libs/ui/widgets/kis_preset_live_preview_view.h b/libs/ui/widgets/kis_preset_live_preview_view.h index f0a9b497f0..37986bb8c5 100644 --- a/libs/ui/widgets/kis_preset_live_preview_view.h +++ b/libs/ui/widgets/kis_preset_live_preview_view.h @@ -1,169 +1,170 @@ /* * Copyright (c) 2017 Scott Petrovic * * 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_PRESET_LIVE_PREVIEW_ #define _KIS_PRESET_LIVE_PREVIEW_ #include #include #include #include #include #include "kis_paintop_preset.h" #include "KoColorSpaceRegistry.h" #include "kis_paint_layer.h" #include "kis_painter.h" #include "kis_distance_information.h" #include "kis_painting_information_builder.h" #include #include +#include /** * Widget for displaying a live brush preview of your * selected brush. It listens for signalsetting changes * that the brush preset outputs and updates the preview * accordingly. This class can be added to a UI file * similar to how a QGraphicsView is added */ class KisPresetLivePreviewView : public QGraphicsView { Q_OBJECT public: KisPresetLivePreviewView(QWidget *parent); ~KisPresetLivePreviewView(); /** * @brief one time setup for initialization of many variables. * This live preview might be in a UI file, so make sure to * call this before starting to use it */ void setup(); /** * @brief set the current preset from resource manager for the live preview to use. * Good to call this every stroke update in case the preset has changed * @param the current preset from the resource manager */ void setCurrentPreset(KisPaintOpPresetSP preset); void updateStroke(); public Q_SLOTS: /** * @brief scales the view to fit the brush stroke and moves it back to the center position */ void slotResetViewZoom(); /** * @brief sets the zoom level to full size to get a close up of larger brushes */ void slotZoomToOneHundredPercent(); private: /// internally sets the image area for brush preview KisImageSP m_image; /// internally sets the layer area for brush preview KisLayerSP m_layer; /// internally sets the color space for brush preview const KoColorSpace *m_colorSpace; - /// painter that actually paints the stroke - KisPainter *m_brushPreviewPainter; + /// the color which is used for rendering the stroke + KoColor m_paintColor; /// the scene that can add items like text and the brush stroke image QGraphicsScene *m_brushPreviewScene; /// holds the preview brush stroke data QGraphicsPixmapItem *m_sceneImageItem; /// holds the 'no preview available' text object QGraphicsTextItem *m_noPreviewText; /// holds the width and height of the image of the brush preview /// Probably can later add set functions to make this customizable /// It is hard-coded to 1200 x 400 for right now for image size QRect m_canvasSize; /// convenience variable used internally when positioning the objects /// and points in the scene QPointF m_canvasCenterPoint; /// internal variables for constructing the stroke start and end shape /// there are two points that construct the "S" curve with this KisDistanceInformation m_currentDistance; QPainterPath m_curvedLine; KisPaintInformation m_curvePointPI1; KisPaintInformation m_curvePointPI2; /// internally stores the current preset. /// See setCurrentPreset(KisPaintOpPresetSP preset) /// for setting this externally KisPaintOpPresetSP m_currentPreset; /// holds the current zoom(scale) level of scene float m_scaleFactor; /// internal reference for internal brush size /// used to check if our brush size has changed /// do zooming and other things internall if it has changed float m_currentBrushSize = 1.0; /// the range of brush sizes that will control zooming in/out const float m_minBrushVal = 10.0; const float m_maxBrushVal = 100.0; /// range of scale values. 1.0 == 100% const qreal m_minScale = 1.0; const qreal m_maxScale = 0.3; /// multiplier that is used for lengthening the brush stroke points const float m_minStrokeScale = 0.4; // for smaller brush stroke const float m_maxStrokeScale = 1.0; // for larger brush stroke /** * @brief reads the brush size and scales the view out to fit it * used internally when resetting the views or changing brush sizes */ void zoomToBrushSize(); /** * @brief works as both clearing the previous stroke, providing * striped backgrounds for smudging brushes, and text if there is no preview */ void paintBackground(); /** * @brief creates and performs the actual stroke that goes on top of the background * this is internally and should always be called after the paintBackground() */ void setupAndPaintStroke(); }; #endif