[PATCH 02/10] Some cleanup and documentation and added more composite modes to KisPressureCompositeOptionWidget.
Silvio Heinrich
plassy at web.de
Fri Dec 31 21:28:09 CET 2010
---
.../defaultpaintops/smudge/kis_smudgeop.cpp | 163 ++++----------------
.../paintops/defaultpaintops/smudge/kis_smudgeop.h | 8 +-
.../libpaintop/kis_pressure_composite_option.cpp | 7 +-
.../libpaintop/kis_pressure_composite_option.h | 7 +-
.../kis_pressure_composite_option_widget.cpp | 6 +
5 files changed, 42 insertions(+), 149 deletions(-)
diff --git a/krita/plugins/paintops/defaultpaintops/smudge/kis_smudgeop.cpp b/krita/plugins/paintops/defaultpaintops/smudge/kis_smudgeop.cpp
index c8dde6d..372a1b7 100644
--- a/krita/plugins/paintops/defaultpaintops/smudge/kis_smudgeop.cpp
+++ b/krita/plugins/paintops/defaultpaintops/smudge/kis_smudgeop.cpp
@@ -40,19 +40,8 @@
#include <kis_selection.h>
#include <kis_brush_based_paintop_settings.h>
-
-// Both limits defined to be 15 units away from the min (0) or max (255) to allow actual mixing of colors
-const quint8 MIXABLE_UPPER_LIMIT = 240;
-const quint8 MIXABLE_LOWER_LIMIT = 15;
-
-// All pieces of color extracted from the canvas will be centered around ANCHOR_POINT
-const QPoint ANCHOR_POINT = QPoint(0, 0);
-
-
KisSmudgeOp::KisSmudgeOp(const KisBrushBasedPaintOpSettings *settings, KisPainter *painter, KisImageWSP image)
- : KisBrushBasedPaintOp(settings, painter)
- , m_firstRun(true)
- , m_tempDev(0)
+ : KisBrushBasedPaintOp(settings, painter), m_tempDev(0)
{
Q_UNUSED(image);
Q_ASSERT(settings);
@@ -65,11 +54,6 @@ KisSmudgeOp::KisSmudgeOp(const KisBrushBasedPaintOpSettings *settings, KisPainte
m_compositeOption.sensor()->reset();
m_tempDev = new KisPaintDevice(painter->device()->colorSpace());
-
- // Initializing to a valid value to avoid weird errors during modifications
- m_wholeTempData = QRect(0, 0, 0, 0);
-
- m_color = painter->paintColor();
}
KisSmudgeOp::~KisSmudgeOp()
@@ -89,8 +73,10 @@ qreal KisSmudgeOp::paintAt(const KisPaintInformation& info)
if(!brush || !brush->canPaintFor(info))
return 1.0;
+ // get the scaling factor calculated by the size option
double scale = m_sizeOption.apply(info);
+ // don't paint anything if the brush is too samll
if((scale*brush->width()) <= 0.01 || (scale*brush->height()) <= 0.01)
return 1.0;
@@ -105,7 +91,7 @@ qreal KisSmudgeOp::paintAt(const KisPaintInformation& info)
KisFixedPaintDeviceSP maskDab = cachedDab(painter()->device()->colorSpace());
- // Extract the brush mask (maskDab) from brush, and turn it into a transparency mask (alpha8).
+ // Extract the brush mask (maskDab) from brush with the correct scaled size
if(brush->brushType() == IMAGE || brush->brushType() == PIPE_IMAGE) {
// This is for bitmap brushes
maskDab = brush->paintDevice(painter()->device()->colorSpace(), scale, 0.0, info, xFraction, yFraction);
@@ -115,135 +101,46 @@ qreal KisSmudgeOp::paintAt(const KisPaintInformation& info)
brush->mask(maskDab, painter()->paintColor(), scale, scale, 0.0, info, xFraction, yFraction);
}
+ // transforms the fixed paint device with the current brush to alpha color space (to use it as alpha/transparency mask)
maskDab->convertTo(KoColorSpaceRegistry::instance()->alpha8());
- KisPainter copyPainter(m_tempDev);
-
- if(m_compositeOption.isChecked()) {
- m_compositeOption.apply(©Painter, OPACITY_OPAQUE_U8, info);
-
- copyPainter.setFillStyle(KisPainter::FillStyleForegroundColor);
- copyPainter.setPaintColor(painter()->paintColor());
- copyPainter.paintRect(maskDab->bounds());
- }
-
+ // GET the opacy calculated by the rate option (apply is misleading because the opacy will not be applied)
quint8 newOpacity = m_rateOption.apply(OPACITY_OPAQUE_U8, info);
+ // set opacity calculated by the rate option
+ // then blit the temporary painting device on the canvas at the current brush position
+ // the alpha mask (maskDab) will be used here to only blit the pixels that lie in the area (shape) of the brush
painter()->setOpacity(newOpacity);
painter()->bitBltWithFixedSelection(x, y, m_tempDev, maskDab, maskDab->bounds().width(), maskDab->bounds().height());
- //painter()->bitBlt(QPoint(x,y), m_tempDev, dab->bounds());
+ // IMPORTANT: clear the temporary painting device to color black with zero opacity
+ // it will only clear the extents of the brush
m_tempDev->clear(maskDab->bounds());
+ KisPainter copyPainter(m_tempDev);
+
+ // reset composite mode and opacity
+ // then cut out the area from the canvas under the brush
+ // and blit it to the temporary painting device
copyPainter.setCompositeOp(COMPOSITE_OVER);
copyPainter.setOpacity(OPACITY_OPAQUE_U8);
copyPainter.bitBlt(0, 0, painter()->device(), x, y, maskDab->bounds().width(), maskDab->bounds().height());
- copyPainter.end();
-
- return spacing(scale);
-}
-
-
-/* To smudge, one does the following:
- *
- 1 *.- First step: initialize a temporary paint device (m_tempDev) with a copy of the colors below the mouse pointer.
- All other times:
- 2.- Vanishing step: Reduce the transparency of the temporary paint device so as to let it mix gradually.
- 3.- Combine: Combine the temporary device with the piece the brush currently is 'painting', according to a ratio:
- in this case, opacity. (This is what in the first step does the copying of the data).
- 4.- Blit to screen: This combination is then composited upon the actual image.
- 5.- Special case: If the size of the dab (brush mask) changes during the stroke (for example, when
- using a stylus sensitive to pressure), align the colors extracted to the center of the previously absorbed colors,
- and in the vanishing step, ensure that all the colors have their opacity slowly reduced, not just the ones below
- the current brush mask.
-
- For the sake of speed optimization, the extent of the largest area of color contained in the
- temporary device is cached such that only the colored areas are considered.
- TODO: Make this cached value dump colors that have faded nearly completely and lie outside of the rectangle (dab)
- of the current iteration.
- *
-qreal KisSmudgeOp::paintAt(const KisPaintInformation& info)
-{
- KisBrushSP brush = m_brush;
- // Simple error catching
- if (!painter()->device()) return 1.0;
- if (!brush) return 1.0;
- if (!brush->canPaintFor(info)) return 1.0;
-
- // Grow the brush (this includes the mask) according to pressure or other parameters
- double scale = m_sizeOption.apply(info);
- if ((scale * brush->width()) <= 0.01 || (scale * brush->height()) <= 0.01) return 1.0;
- setCurrentScale(scale);
-
- // Align a point that represents the top-left corner of the brush-stroke-rendering
- // with the mouse pointer and take into account the brush mask size
- QPointF hotSpot = brush->hotSpot(scale, scale);
- QPointF pt = info.pos() - hotSpot;
-
- // Split the coordinates into integer plus fractional parts. The integer
- //is where the dab will be positioned and the fractional part determines
- // the sub-pixel positioning.
- qint32 x, y;
- qreal xFraction, yFraction;
-
- splitCoordinate(pt.x(), &x, &xFraction);
- splitCoordinate(pt.y(), &y, &yFraction);
-
- KisFixedPaintDeviceSP maskDab = 0;
-
- // Extract the brush mask (maskDab) from brush, and turn it into a transparency mask (alpha8).
- if (brush->brushType() == IMAGE || brush->brushType() == PIPE_IMAGE) {
- // This is for bitmap brushes
- maskDab = brush->paintDevice(painter()->device()->colorSpace(), scale, 0.0, info, xFraction, yFraction);
- maskDab->convertTo(KoColorSpaceRegistry::instance()->alpha8());
- } else {
- // This is for parametric brushes, those created in the Autobrush popup config dialogue
- maskDab = cachedDab();
- brush->mask(maskDab, m_color, scale, scale, 0.0, info, xFraction, yFraction);
- maskDab->convertTo(KoColorSpaceRegistry::instance()->alpha8());
- }
-
- // Convenient renaming for the limits of the maskDab
- qint32 sw = maskDab->bounds().width();
- qint32 sh = maskDab->bounds().height();
-
- // Prepare the top left corner of the temporary paint device where the extracted color will be drawn
- QPoint extractionTopLeft = QPoint(ANCHOR_POINT.x() - sw / 2,
- ANCHOR_POINT.y() - sh / 2);
-
- // In the block below, the opacity of the colors stored in m_tempDev
- // is reduced in opacity. Nothing of the color present inside it is left out
- quint8 opacity = OPACITY_OPAQUE_U8;
- if (!m_firstRun) {
- opacity = m_rateOption.apply(opacity, info);
- // Without those limits, the smudge brush doesn't smudge anymore, it either makes a single
- // dropplet of color, or drags a frame indefinitely over the canvas.
- opacity = qBound(MIXABLE_LOWER_LIMIT, opacity, MIXABLE_UPPER_LIMIT);
-
- // Invert the opacity value for color absorption in the next lines (copyPainter)
- opacity = OPACITY_OPAQUE_U8 - opacity;
- m_wholeTempData |= QRect(extractionTopLeft, maskDab->bounds().size());
- }
- else {
- m_firstRun = false;
- m_wholeTempData = QRect(extractionTopLeft, maskDab->bounds().size());
+ // if the user selected the color smudge option
+ // we will mix some color into the temorary painting device (m_tempDev)
+ if(m_compositeOption.isChecked()) {
+ // this will apply the composite mode and the opacy (selected by the user)
+ // to copyPainter
+ m_compositeOption.apply(©Painter, OPACITY_OPAQUE_U8, info);
+
+ // paint a rectangle with the current color (foreground color)
+ // into the temporary painting device
+ copyPainter.setFillStyle(KisPainter::FillStyleForegroundColor);
+ copyPainter.setPaintColor(painter()->paintColor());
+ copyPainter.paintRect(maskDab->bounds());
}
- // copyPainter will extract the piece of color (image) to be duplicated to generate the smudge effect,
- // it extracts a simple unmasked rectangle and adds it to what was extracted before in this same block of code,
- // this sometimes shows artifacts when the brush is used with stylus and high spacing
- KisPainter copyPainter(m_tempDev);
- copyPainter.setCompositeOp(COMPOSITE_COPY);
- copyPainter.setOpacity(opacity);
- copyPainter.bitBlt(m_wholeTempData.x(), m_wholeTempData.y(), painter()->device(),
- x - m_wholeTempData.x() + extractionTopLeft.x(),
- y - m_wholeTempData.y() + extractionTopLeft.y(),
- m_wholeTempData.width(), m_wholeTempData.height());
- copyPainter.end();
- // This is the line that renders the extracted colors to the screen, with maskDab giving it the brush shape
- painter()->bitBltWithFixedSelection(x, y, m_tempDev, maskDab, 0, 0, extractionTopLeft.x(), extractionTopLeft.y(), sw, sh);
- renderMirrorMask(QRect(QPoint(x,y),QSize(sw,sh)),m_tempDev,extractionTopLeft.x(), extractionTopLeft.y(),maskDab);
+ copyPainter.end();
return spacing(scale);
-}//*/
+}
diff --git a/krita/plugins/paintops/defaultpaintops/smudge/kis_smudgeop.h b/krita/plugins/paintops/defaultpaintops/smudge/kis_smudgeop.h
index 35a5b64..5948e04 100644
--- a/krita/plugins/paintops/defaultpaintops/smudge/kis_smudgeop.h
+++ b/krita/plugins/paintops/defaultpaintops/smudge/kis_smudgeop.h
@@ -49,13 +49,7 @@ public:
qreal paintAt(const KisPaintInformation& info);
private:
- bool m_firstRun;
- // The "temporary paint device"
- KisPaintDeviceSP m_tempDev;
- // The size of the rectangle encompassing the whole data in the temporary device needs to be cached for speed
- QRect m_wholeTempData;
- KoColor m_color;
-
+ KisPaintDeviceSP m_tempDev; // The temporary paint device
KisPressureSizeOption m_sizeOption;
KisPressureRateOption m_rateOption;
KisPressureCompositeOption m_compositeOption;
diff --git a/krita/plugins/paintops/libpaintop/kis_pressure_composite_option.cpp b/krita/plugins/paintops/libpaintop/kis_pressure_composite_option.cpp
index 18a9882..ca09eb6 100644
--- a/krita/plugins/paintops/libpaintop/kis_pressure_composite_option.cpp
+++ b/krita/plugins/paintops/libpaintop/kis_pressure_composite_option.cpp
@@ -30,7 +30,7 @@
#include <KoCompositeOp.h>
KisPressureCompositeOption::KisPressureCompositeOption()
- : KisCurveOption(i18n("Color"), "Color", KisPaintOpOption::brushCategory(), false)
+ : KisCurveOption(i18n("Color"), "Color", KisPaintOpOption::brushCategory(), false)
{
setMinimumLabel(i18n("Full Color"));
setMaximumLabel(i18n("No Color"));
@@ -53,7 +53,7 @@ void KisPressureCompositeOption::readOptionSetting(const KisPropertiesConfigurat
m_compositeOp = COMPOSITE_OVER;
}
-QString KisPressureCompositeOption::apply(KisPainter* painter, qint8 opacity, const KisPaintInformation& info) const
+void KisPressureCompositeOption::apply(KisPainter* painter, qint8 opacity, const KisPaintInformation& info) const
{
if(!isChecked())
return painter->compositeOp()->id();
@@ -65,9 +65,6 @@ QString KisPressureCompositeOption::apply(KisPainter* painter, qint8 opacity, co
(qint32)(double(opacity) * computeValue(info) / PRESSURE_DEFAULT),
(qint32)OPACITY_OPAQUE_U8);
- //qreal opacity1 = (qreal)(painter->opacity() * computeValue(info));
- //quint8 opacity2 = (quint8)qRound(qBound<qreal>(OPACITY_TRANSPARENT_U8, opacity1, OPACITY_OPAQUE_U8));
-
painter->setCompositeOp(m_compositeOp);
painter->setOpacity(opacity);
diff --git a/krita/plugins/paintops/libpaintop/kis_pressure_composite_option.h b/krita/plugins/paintops/libpaintop/kis_pressure_composite_option.h
index f05642a..2ba3d8b 100644
--- a/krita/plugins/paintops/libpaintop/kis_pressure_composite_option.h
+++ b/krita/plugins/paintops/libpaintop/kis_pressure_composite_option.h
@@ -35,11 +35,10 @@ public:
KisPressureCompositeOption();
/**
- * Set the composite mode and opacity of the painter based on the pressure
- * and the curve (if checked) and return the old composite mode
- * of the painter.
+ * Set the composite mode and opacity of the painter based on the user selection
+ * and the pressure curve (if checked)
*/
- QString apply(KisPainter* painter, qint8 opacity, const KisPaintInformation& info) const;
+ void apply(KisPainter* painter, qint8 opacity, const KisPaintInformation& info) const;
void writeOptionSetting(KisPropertiesConfiguration* setting) const;
void readOptionSetting(const KisPropertiesConfiguration* setting);
diff --git a/krita/plugins/paintops/libpaintop/kis_pressure_composite_option_widget.cpp b/krita/plugins/paintops/libpaintop/kis_pressure_composite_option_widget.cpp
index 140c628..6f17544 100644
--- a/krita/plugins/paintops/libpaintop/kis_pressure_composite_option_widget.cpp
+++ b/krita/plugins/paintops/libpaintop/kis_pressure_composite_option_widget.cpp
@@ -46,6 +46,12 @@ KisPressureCompositeOptionWidget::KisPressureCompositeOptionWidget()
m_compositeOpBox->addItem(COMPOSITE_SCREEN);
m_compositeOpBox->addItem(COMPOSITE_ADD);
m_compositeOpBox->addItem(COMPOSITE_SUBTRACT);
+ m_compositeOpBox->addItem(COMPOSITE_DIVIDE);
+ m_compositeOpBox->addItem(COMPOSITE_BURN);
+ m_compositeOpBox->addItem(COMPOSITE_DODGE);
+ m_compositeOpBox->addItem(COMPOSITE_COLOR);
+ m_compositeOpBox->addItem(COMPOSITE_HARD_LIGHT);
+ m_compositeOpBox->addItem(COMPOSITE_SOFT_LIGHT);
m_rateSlider = new QSlider();
m_rateSlider->setMinimum(0);
--
1.7.1
More information about the kimageshop
mailing list