[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-kimageshop
Subject:    Real (not as buggy) indirect painting
From:       Bart Coppens <kde () bartcoppens ! be>
Date:       2006-08-26 17:26:00
Message-ID: 200608261926.00793.kde () bartcoppens ! be
[Download RAW message or body]

Thanks to some discussions with Ilmari Heikkinen (kig) on IRC, I got 
interested in making the indirect painting work more like people expect it, 
and less buggy.

Basically, the most complained about problem with the current implementation 
is (other than being hacky) that it still does not handle pressure-varying 
opacity correctly. Another problem is that it, in fact, didn't handle 
paintops other than OVER correctly. I even think that it didn't work at all 
with layer masks and adjustment layers.
The first was partly solved by Ilmari by introducing a 'darken alpha' paintop, 
and implementing it for rgba8. So I decided to see if I could make that work 
better, and make the rest work, by getting rid of the temporary layer in the 
layer stack, and replacing it with a temporary paint device inside the paint 
layer itself.

Attached is a patch (combined with Ilmari's alpha darken) that fixes it for 
regular paint layers (so no adjustment layers, masks or selections yet). I 
think it's a bit hacky as well, but replacing something buggy and hacky with 
something working and hacky might be worth considering for 1.6, Cyrille?

If this would get greenlighted for 1.6 I'll see what I can do about making 
this work with masks and adjustment layers (and other colorspaces).

Bart

["paintindirect.diff" (text/x-diff)]

Index: core/kis_paint_layer.h
===================================================================
--- core/kis_paint_layer.h	(revision 577360)
+++ core/kis_paint_layer.h	(working copy)
@@ -132,6 +132,13 @@
     /// Same as above
     virtual void setDirty(const QRect & rect, bool propagate = true);
 
+    // Indirect painting
+    void setTemporaryTarget(KisPaintDeviceSP t) { m_temporaryTarget = t; }
+    void setTemporaryCompositeOp(const KisCompositeOp& c) { m_compositeOp = c; }
+    void setTemporaryOpacity(Q_UINT8 o) { m_compositeOpacity = o; }
+    KisPaintDeviceSP temporaryTarget() { return m_temporaryTarget; }
+    KisCompositeOp temporaryCompositeOp() const { return m_compositeOp; }
+    Q_UINT8 temporaryOpacity() const { return m_compositeOpacity; }
 signals:
     /// When the mask is created/destroyed or the editmask or rendermask is changed
     void sigMaskInfoChanged();
@@ -146,6 +153,11 @@
     KisSelectionSP m_maskAsSelection; // The mask as selection, to apply and render \
easily  bool m_renderMask;
     bool m_editMask;
+
+    // To simulate the indirect painting
+    KisPaintDeviceSP m_temporaryTarget;
+    KisCompositeOp m_compositeOp;
+    Q_UINT8 m_compositeOpacity;
 };
 
 typedef KSharedPtr<KisPaintLayer> KisPaintLayerSP;
Index: core/kis_group_layer.cc
===================================================================
--- core/kis_group_layer.cc	(revision 577360)
+++ core/kis_group_layer.cc	(working copy)
@@ -86,7 +86,8 @@
     // paint device as the projection if the child is visible and 100% opaque
     if (parent() == 0 && childCount() == 1) {
         KisPaintLayerSP l = dynamic_cast<KisPaintLayer*>(firstChild().data());
-        if (l && l->paintDevice()->colorSpace() == m_image->colorSpace() && \
l->visible() && l->opacity() == OPACITY_OPAQUE) { +        if (l && \
l->paintDevice()->colorSpace() == m_image->colorSpace() && l->visible() +            \
&& l->opacity() == OPACITY_OPAQUE && !l->temporaryTarget()) {  l->setClean(rect);
             setClean(rect);
             return l->paintDevice();
Index: core/kis_merge_visitor.h
===================================================================
--- core/kis_merge_visitor.h	(revision 577360)
+++ core/kis_merge_visitor.h	(working copy)
@@ -72,6 +72,12 @@
 
         QRect rc = layer->paintDevice()->extent() & m_rc;
 
+        // Indirect painting?
+        KisPaintDeviceSP tempTarget = layer->temporaryTarget();
+        if (tempTarget) {
+            rc = (layer->paintDevice()->extent() | tempTarget->extent()) & m_rc;
+        }
+
         sx = rc.left();
         sy = rc.top();
         w  = rc.width();
@@ -80,9 +86,19 @@
         dy = sy;
 
         KisPainter gc(m_projection);
+        KisPaintDeviceSP source = layer->paintDevice();
 
+        if (tempTarget) {
+            source = new KisPaintDevice(source->colorSpace());
+            KisPainter gc2(source);
+            gc2.bitBlt(dx, dy, COMPOSITE_COPY, layer->paintDevice(),
+                       OPACITY_OPAQUE, sx, sy, w, h);
+            gc2.bitBlt(dx, dy, layer->temporaryCompositeOp(), tempTarget,
+                       layer->temporaryOpacity(), sx, sy, w, h);
+            gc2.end();
+        }
         if (!layer->hasMask()) {
-            gc.bitBlt(dx, dy, layer->compositeOp(), layer->paintDevice(), \
layer->opacity(), sx, sy, w, h); +            gc.bitBlt(dx, dy, layer->compositeOp(), \
source, layer->opacity(), sx, sy, w, h);  } else {
             if (layer->renderMask()) {
                 // To display the mask, we don't do things with composite op and \
opacity @@ -119,7 +135,7 @@
             } else {
                 gc.bltSelection(dx, dy,
                                 layer->compositeOp(),
-                                layer->paintDevice(),
+                                source,
                                 layer->getMaskAsSelection(),
                                 layer->opacity(), sx, sy, w, h);
             }
Index: kritacolor/kis_composite_op.h
===================================================================
--- kritacolor/kis_composite_op.h	(revision 577360)
+++ kritacolor/kis_composite_op.h	(working copy)
@@ -26,6 +26,7 @@
 
 enum CompositeOp {
     COMPOSITE_OVER,
+    COMPOSITE_ALPHA_DARKEN,
     COMPOSITE_IN,
     COMPOSITE_OUT,
     COMPOSITE_ATOP,
Index: kritacolor/kis_composite_op.cc
===================================================================
--- kritacolor/kis_composite_op.cc	(revision 577360)
+++ kritacolor/kis_composite_op.cc	(working copy)
@@ -90,48 +90,49 @@
 void KisCompositeOp::fillMap()
 {
     s_idOpMap[KisID("normal",     i18n("Normal"))] =        COMPOSITE_OVER;
-    s_idOpMap[KisID("in",         i18n("In"))] =                   COMPOSITE_IN;     \
                
-    s_idOpMap[KisID("out",         i18n("Out"))] =            COMPOSITE_OUT;         \
                
-    s_idOpMap[KisID("atop",     i18n("Atop"))] =        COMPOSITE_ATOP;              \
                
-    s_idOpMap[KisID("xor",         i18n("Xor"))] =            COMPOSITE_XOR;         \
                
-    s_idOpMap[KisID("plus",        i18n("Plus"))] =        COMPOSITE_PLUS;           \
                
-    s_idOpMap[KisID("minus",    i18n("Minus"))] =        COMPOSITE_MINUS;            \
                
-    s_idOpMap[KisID("add",         i18n("Add"))] =            COMPOSITE_ADD;         \
                
-    s_idOpMap[KisID("subtract",    i18n("Subtract"))] =        COMPOSITE_SUBTRACT;   \
                
-    s_idOpMap[KisID("diff",     i18n("Diff"))] =        COMPOSITE_DIFF;              \
                
-    s_idOpMap[KisID("multiply",    i18n("Multiply"))] =        COMPOSITE_MULT;       \
                
-    s_idOpMap[KisID("divide",    i18n("Divide"))] =        COMPOSITE_DIVIDE;         \
                
-    s_idOpMap[KisID("dodge",     i18n("Dodge"))] =        COMPOSITE_DODGE;           \
                
-    s_idOpMap[KisID("burn",     i18n("Burn"))] =        COMPOSITE_BURN;              \
                
-    s_idOpMap[KisID("bumpmap",     i18n("Bumpmap"))] =        COMPOSITE_BUMPMAP;     \
                
-    s_idOpMap[KisID("copy",     i18n("Copy"))] =        COMPOSITE_COPY;              \
                
-    s_idOpMap[KisID("copyred",     i18n("Copy Red"))] =        COMPOSITE_COPY_RED;   \
                
-    s_idOpMap[KisID("copygreen",     i18n("Copy Green"))] =        \
                COMPOSITE_COPY_GREEN;         
-    s_idOpMap[KisID("copyblue",     i18n("Copy Blue"))] =        \
                COMPOSITE_COPY_BLUE;          
-    s_idOpMap[KisID("copyopacity",     i18n("Copy Opacity"))] =    \
                COMPOSITE_COPY_OPACITY;       
-    s_idOpMap[KisID("clear",     i18n("Clear"))] =        COMPOSITE_CLEAR;           \
                
-    s_idOpMap[KisID("dissolve",     i18n("Dissolve"))] =        COMPOSITE_DISSOLVE;  \
                
-    s_idOpMap[KisID("displace",     i18n("Displace"))] =        COMPOSITE_DISPLACE;  \
                
-#if 0                                                                                \
                
-    s_idOpMap[KisID("modulate",     i18n("Modulate"))] =        COMPOSITE_MODULATE;  \
                
-    s_idOpMap[KisID("threshold",     i18n("Threshold"))] =        \
                COMPOSITE_THRESHOLD;          
-#endif                                                                               \
                
-    s_idOpMap[KisID("nocomposition",i18n("No Composition"))] =    COMPOSITE_NO;      \
                
-    s_idOpMap[KisID("darken",     i18n("Darken"))] =        COMPOSITE_DARKEN;        \
                
-    s_idOpMap[KisID("lighten",     i18n("Lighten"))] =        COMPOSITE_LIGHTEN;     \
                
-    s_idOpMap[KisID("hue",         i18n("Hue"))] =            COMPOSITE_HUE;         \
                
-    s_idOpMap[KisID("saturation",     i18n("Saturation"))] =        \
                COMPOSITE_SATURATION;         
-    s_idOpMap[KisID("value",     i18n("Value"))] =        COMPOSITE_VALUE;           \
                
-    s_idOpMap[KisID("color",     i18n("Color"))] =        COMPOSITE_COLOR;           \
                
-    s_idOpMap[KisID("colorize",     i18n("Colorize"))] =        COMPOSITE_COLORIZE;  \
                
-    s_idOpMap[KisID("luminize",     i18n("Luminize"))] =        COMPOSITE_LUMINIZE;  \
                
-    s_idOpMap[KisID("screen",     i18n("Screen"))] =        COMPOSITE_SCREEN;        \
                
-    s_idOpMap[KisID("overlay",     i18n("Overlay"))] =        COMPOSITE_OVERLAY;     \
                
-    s_idOpMap[KisID("copycyan",     i18n("Copy Cyan"))] =        \
                COMPOSITE_COPY_CYAN;          
-    s_idOpMap[KisID("copymagenta",     i18n("Copy Magenta"))] =    \
                COMPOSITE_COPY_MAGENTA;       
-    s_idOpMap[KisID("copyyellow",     i18n("Copy Yellow"))] =        \
                COMPOSITE_COPY_YELLOW;        
-    s_idOpMap[KisID("copyblack",     i18n("Copy Black"))] =        \
                COMPOSITE_COPY_BLACK;         
-    s_idOpMap[KisID("erase",     i18n("Erase"))] =        COMPOSITE_ERASE;           \
                
-    s_idOpMap[KisID("undefined",     i18n("Undefined"))] =            \
COMPOSITE_UNDEF;               +    s_idOpMap[KisID("alphadarken",     i18n("Alpha \
Darken"))] =        COMPOSITE_ALPHA_DARKEN; +    s_idOpMap[KisID("in",         \
i18n("In"))] =                   COMPOSITE_IN; +    s_idOpMap[KisID("out",         \
i18n("Out"))] =            COMPOSITE_OUT; +    s_idOpMap[KisID("atop",     \
i18n("Atop"))] =        COMPOSITE_ATOP; +    s_idOpMap[KisID("xor",         \
i18n("Xor"))] =            COMPOSITE_XOR; +    s_idOpMap[KisID("plus",        \
i18n("Plus"))] =        COMPOSITE_PLUS; +    s_idOpMap[KisID("minus",    \
i18n("Minus"))] =        COMPOSITE_MINUS; +    s_idOpMap[KisID("add",         \
i18n("Add"))] =            COMPOSITE_ADD; +    s_idOpMap[KisID("subtract",    \
i18n("Subtract"))] =        COMPOSITE_SUBTRACT; +    s_idOpMap[KisID("diff",     \
i18n("Diff"))] =        COMPOSITE_DIFF; +    s_idOpMap[KisID("multiply",    \
i18n("Multiply"))] =        COMPOSITE_MULT; +    s_idOpMap[KisID("divide",    \
i18n("Divide"))] =        COMPOSITE_DIVIDE; +    s_idOpMap[KisID("dodge",     \
i18n("Dodge"))] =        COMPOSITE_DODGE; +    s_idOpMap[KisID("burn",     \
i18n("Burn"))] =        COMPOSITE_BURN; +    s_idOpMap[KisID("bumpmap",     \
i18n("Bumpmap"))] =        COMPOSITE_BUMPMAP; +    s_idOpMap[KisID("copy",     \
i18n("Copy"))] =        COMPOSITE_COPY; +    s_idOpMap[KisID("copyred",     \
i18n("Copy Red"))] =        COMPOSITE_COPY_RED; +    s_idOpMap[KisID("copygreen",     \
i18n("Copy Green"))] =        COMPOSITE_COPY_GREEN; +    s_idOpMap[KisID("copyblue",  \
i18n("Copy Blue"))] =        COMPOSITE_COPY_BLUE; +    s_idOpMap[KisID("copyopacity", \
i18n("Copy Opacity"))] =    COMPOSITE_COPY_OPACITY; +    s_idOpMap[KisID("clear",     \
i18n("Clear"))] =        COMPOSITE_CLEAR; +    s_idOpMap[KisID("dissolve",     \
i18n("Dissolve"))] =        COMPOSITE_DISSOLVE; +    s_idOpMap[KisID("displace",     \
i18n("Displace"))] =        COMPOSITE_DISPLACE; +#if 0
+    s_idOpMap[KisID("modulate",     i18n("Modulate"))] =        COMPOSITE_MODULATE;
+    s_idOpMap[KisID("threshold",     i18n("Threshold"))] =        \
COMPOSITE_THRESHOLD; +#endif
+    s_idOpMap[KisID("nocomposition",i18n("No Composition"))] =    COMPOSITE_NO;
+    s_idOpMap[KisID("darken",     i18n("Darken"))] =        COMPOSITE_DARKEN;
+    s_idOpMap[KisID("lighten",     i18n("Lighten"))] =        COMPOSITE_LIGHTEN;
+    s_idOpMap[KisID("hue",         i18n("Hue"))] =            COMPOSITE_HUE;
+    s_idOpMap[KisID("saturation",     i18n("Saturation"))] =        \
COMPOSITE_SATURATION; +    s_idOpMap[KisID("value",     i18n("Value"))] =        \
COMPOSITE_VALUE; +    s_idOpMap[KisID("color",     i18n("Color"))] =        \
COMPOSITE_COLOR; +    s_idOpMap[KisID("colorize",     i18n("Colorize"))] =        \
COMPOSITE_COLORIZE; +    s_idOpMap[KisID("luminize",     i18n("Luminize"))] =        \
COMPOSITE_LUMINIZE; +    s_idOpMap[KisID("screen",     i18n("Screen"))] =        \
COMPOSITE_SCREEN; +    s_idOpMap[KisID("overlay",     i18n("Overlay"))] =        \
COMPOSITE_OVERLAY; +    s_idOpMap[KisID("copycyan",     i18n("Copy Cyan"))] =        \
COMPOSITE_COPY_CYAN; +    s_idOpMap[KisID("copymagenta",     i18n("Copy Magenta"))] = \
COMPOSITE_COPY_MAGENTA; +    s_idOpMap[KisID("copyyellow",     i18n("Copy Yellow"))] \
=        COMPOSITE_COPY_YELLOW; +    s_idOpMap[KisID("copyblack",     i18n("Copy \
Black"))] =        COMPOSITE_COPY_BLACK; +    s_idOpMap[KisID("erase",     \
i18n("Erase"))] =        COMPOSITE_ERASE; +    s_idOpMap[KisID("undefined",     \
i18n("Undefined"))] =            COMPOSITE_UNDEF;  }
-                 
+
Index: ui/kis_tool_freehand.cc
===================================================================
--- ui/kis_tool_freehand.cc	(revision 577360)
+++ ui/kis_tool_freehand.cc	(working copy)
@@ -100,18 +100,13 @@
 
             r = QRect(r.left()-1, r.top()-1, r.width()+2, r.height()+2); //needed to \
update selectionvisualization  if (!m_paintOnSelection) {
-                if (!m_paintIncremental) {
-                    m_tempLayer->setDirty(r);
-                }
-                else {
                     m_currentImage->activeLayer()->setDirty(r);
-                }
             }
             else {
                 m_target->setDirty(r);
                 // Just update the canvas. XXX: After 1.5, find a better way to make \
sure tools don't set dirty what they didn't touch.  \
                m_subject->canvasController()->updateCanvas( r );
-            } 
+            }
         }
     }
 }
@@ -129,7 +124,7 @@
     if (m_mode == PAINT) {
 
         paintLine(m_prevPos, m_prevPressure, m_prevXTilt, m_prevYTilt, e->pos(), \
                e->pressure(), e->xTilt(), e->yTilt());
-    
+
         m_prevPos = e->pos();
         m_prevPressure = e->pressure();
         m_prevXTilt = e->xTilt();
@@ -141,19 +136,14 @@
             m_dirtyRect |= r;
 
             if (!m_paintOnSelection) {
-                if (!m_paintIncremental) {
-                    m_tempLayer->setDirty(r);
-                }
-                else {
                     m_currentImage->activeLayer()->setDirty(r);
-                }
             }
             else {
                 // Just update the canvas
                 r = QRect(r.left()-1, r.top()-1, r.width()+2, r.height()+2); \
//needed to update selectionvisualization  m_target->setDirty(r);
                 m_subject->canvasController()->updateCanvas( r );
-            } 
+            }
         }
     }
 }
@@ -168,30 +158,21 @@
     // Create painter
     KisPaintDeviceSP device;
     if (m_currentImage && (device = m_currentImage->activeDevice())) {
-        
+
         if (m_painter)
             delete m_painter;
-        
+
         if (!m_paintIncremental) {
             if (m_currentImage->undo())
                 m_currentImage->undoAdapter()->beginMacro(m_transactionText);
 
-            //if (m_tempLayer == 0) {
-                // XXX ugly! hacky! We'd like to cache the templayer, but that makes \
                sure
-                // the layer is never really removed from its parent group because \
                of shared pointers
-                m_tempLayer = new KisPaintLayer(m_currentImage, "temp", \
                OPACITY_OPAQUE);
-                m_tempLayer->setTemporary(true);
-                // Yuck, what an ugly cast!
-                m_target = \
                (dynamic_cast<KisPaintLayer*>(m_tempLayer.data()))->paintDevice();
-            //}
-
-            m_tempLayer->setCompositeOp( m_compositeOp );
-            m_tempLayer->setOpacity( m_opacity );
-
-            m_tempLayer->setVisible(true);
-
-            currentImage()->addLayer(m_tempLayer, \
                m_currentImage->activeLayer()->parent().data(), \
                m_currentImage->activeLayer());
-
+            KisPaintLayer* layer;
+            if ((layer = \
dynamic_cast<KisPaintLayer*>(m_currentImage->activeLayer().data()))) { +              \
m_target = new KisPaintDevice(layer, device->colorSpace()); +                \
layer->setTemporaryTarget(m_target); +                \
layer->setTemporaryCompositeOp(m_compositeOp); +                \
layer->setTemporaryOpacity(m_opacity); +            }
         } else {
             m_target = device;
         }
@@ -212,7 +193,7 @@
         m_painter->setCompositeOp(m_compositeOp);
         m_painter->setOpacity(m_opacity);
     } else {
-        m_painter->setCompositeOp(COMPOSITE_OVER);
+        m_painter->setCompositeOp(COMPOSITE_ALPHA_DARKEN);
         m_painter->setOpacity( OPACITY_OPAQUE );
 
     }
@@ -231,29 +212,33 @@
 {
     m_mode = HOVER;
     if (m_currentImage) {
-        
+
         if (m_painter) {
             // If painting in mouse release, make sure painter
             // is destructed or end()ed
             if (!m_paintIncremental) {
-                if (m_currentImage->undo()) m_painter->endTransaction();
+                if (m_currentImage->undo())
+                    m_painter->endTransaction();
                 KisPainter painter( m_source );
                 painter.setCompositeOp(m_compositeOp);
-                if (m_currentImage->undo()) \
                painter.beginTransaction(m_transactionText);
-                painter.bitBlt(m_dirtyRect.x(), m_dirtyRect.y(), m_compositeOp, \
                m_target, m_opacity,
-                               m_dirtyRect.x(), m_dirtyRect.y(), \
m_dirtyRect.width(), m_dirtyRect.height()); +                if \
(m_currentImage->undo()) +                    \
painter.beginTransaction(m_transactionText); +                \
painter.bitBlt(m_dirtyRect.x(), m_dirtyRect.y(), m_compositeOp, m_target, +           \
m_opacity, +                               m_dirtyRect.x(), m_dirtyRect.y(),
+                               m_dirtyRect.width(), m_dirtyRect.height());
 
-                if (m_currentImage->undo()) \
                m_currentImage->undoAdapter()->addCommand(painter.endTransaction());
-                m_currentImage->removeLayer(m_tempLayer);
-                // The shared ptr layer vector in the group keeps the layer from \
                being
-                // being removed if it isn't removed here. It would be much faster \
                to
-                // keep the layer and clear it, but that isn't possible. Replacing \
                the
-                // old templayer with a new one at the end of paint prevents at \
                least
-                // the pause when painting again.
-                //m_target->clear();
-                if (m_currentImage->undo()) \
m_currentImage->undoAdapter()->endMacro(); +                KisPaintLayer* layer = \
dynamic_cast<KisPaintLayer*>(m_source->parentLayer()); +                \
layer->setTemporaryTarget(0); +                layer->setDirty(m_dirtyRect);
+
+                if (m_currentImage->undo()) {
+                    \
m_currentImage->undoAdapter()->addCommand(painter.endTransaction()); +                \
m_currentImage->undoAdapter()->endMacro(); +                }
             } else {
-                if (m_currentImage->undo()) \
m_currentImage->undoAdapter()->addCommand(m_painter->endTransaction()); +             \
if (m_currentImage->undo()) +                    \
m_currentImage->undoAdapter()->addCommand(m_painter->endTransaction());  }
         }
         delete m_painter;



_______________________________________________
kimageshop mailing list
kimageshop@kde.org
https://mail.kde.org/mailman/listinfo/kimageshop


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic