krita

kis_merge_visitor.h

00001 /*
00002  *  Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
00003  *  Copyright (c) 2005 Casper Boemann <cbr@boemann.dk>
00004  *  Copyright (c) 2006 Bart Coppens <kde@bartcoppens.be>
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  */
00020 #ifndef KIS_MERGE_H_
00021 #define KIS_MERGE_H_
00022 
00023 #include <qrect.h>
00024 
00025 #include "kis_types.h"
00026 #include "kis_paint_device.h"
00027 #include "kis_layer_visitor.h"
00028 #include "kis_painter.h"
00029 #include "kis_image.h"
00030 #include "kis_layer.h"
00031 #include "kis_group_layer.h"
00032 #include "kis_adjustment_layer.h"
00033 #include "kis_paint_layer.h"
00034 #include "kis_part_layer_iface.h"
00035 #include "kis_filter.h"
00036 #include "kis_filter_configuration.h"
00037 #include "kis_filter_registry.h"
00038 #include "kis_selection.h"
00039 #include "kis_transaction.h"
00040 #include "kis_iterators_pixel.h"
00041 
00042 class KisMergeVisitor : public KisLayerVisitor {
00043 public:
00048     KisMergeVisitor(KisPaintDeviceSP projection, const QRect& rc) :
00049         KisLayerVisitor()
00050     {
00051         Q_ASSERT(projection);
00052 
00053         m_projection = projection;
00054         m_rc = rc;
00055     }
00056 
00057 private:
00058     // Helper for the indirect painting (keep above to inhibit gcc-2.95 ICE)
00059     template<class Target>
00060     KSharedPtr<Target> paintIndirect(KisPaintDeviceSP source,
00061                                      KSharedPtr<Target> target,
00062                                      KisLayerSupportsIndirectPainting* layer,
00063                                      Q_INT32 sx, Q_INT32 sy, Q_INT32 dx, Q_INT32 dy,
00064                                      Q_INT32 w, Q_INT32 h) {
00065         KisPainter gc2(target.data());
00066         gc2.bitBlt(dx, dy, COMPOSITE_COPY, source,
00067                    OPACITY_OPAQUE, sx, sy, w, h);
00068         gc2.bitBlt(dx, dy, layer->temporaryCompositeOp(), layer->temporaryTarget(),
00069                    layer->temporaryOpacity(), sx, sy, w, h);
00070         gc2.end();
00071         return target;
00072     }
00073 
00074 public:
00075     virtual bool visit(KisPaintLayer *layer)
00076     {
00077         
00078         if (m_projection == 0) {
00079             return false;
00080         }
00081         
00082         kdDebug(41010) << "Visiting on paint layer " << layer->name() << ", visible: " << layer->visible()
00083                 << ", temporary: " << layer->temporary() << ", extent: "
00084                 << layer->extent() << ", dirty: " << layer->dirtyRect() << ", paint rect: " << m_rc << endl;
00085         if (!layer->visible())
00086             return true;
00087 
00088         Q_INT32 sx, sy, dx, dy, w, h;
00089 
00090         QRect rc = layer->paintDevice()->extent() & m_rc;
00091 
00092         // Indirect painting?
00093         KisPaintDeviceSP tempTarget = layer->temporaryTarget();
00094         if (tempTarget) {
00095             rc = (layer->paintDevice()->extent() | tempTarget->extent()) & m_rc;
00096         }
00097 
00098         sx = rc.left();
00099         sy = rc.top();
00100         w  = rc.width();
00101         h  = rc.height();
00102         dx = sx;
00103         dy = sy;
00104 
00105         KisPainter gc(m_projection);
00106         KisPaintDeviceSP source = layer->paintDevice();
00107 
00108         if (!layer->hasMask()) {
00109             if (tempTarget) {
00110                 KisPaintDeviceSP temp = new KisPaintDevice(source->colorSpace());
00111                 source = paintIndirect(source, temp, layer, sx, sy, dx, dy, w, h);
00112             }
00113 
00114             gc.bitBlt(dx, dy, layer->compositeOp(), source, layer->opacity(), sx, sy, w, h);
00115         } else {
00116             if (layer->renderMask()) {
00117                 // To display the mask, we don't do things with composite op and opacity
00118                 // This is like the gimp does it, I guess that's ok?
00119 
00120                 // Note that here we'll use m_rc, because even if the extent of the device is
00121                 // empty, we want a full mask to be drawn! (we don't change rc, since
00122                 // it'd mess with setClean). This is because KisPainter::bitBlt &'s with
00123                 // the source device's extent. This is ok in normal circumstances, but
00124                 // we changed the default tile. Fixing this properly would mean fixing it there.
00125                 sx = m_rc.left();
00126                 sy = m_rc.top();
00127                 w  = m_rc.width();
00128                 h  = m_rc.height();
00129                 dx = sx;
00130                 dy = sy;
00131 
00132                 // The problem is that the extent of the layer mask might not be extended
00133                 // enough. Check if that is the case
00134                 KisPaintDeviceSP mask = layer->getMask();
00135                 QRect mextent = mask->extent();
00136                 if ((mextent & m_rc) != m_rc) {
00137                     // Iterate over all pixels in the m_rc area. With just accessing the
00138                     // tiles in read-write mode, we ensure that the tiles get created if they
00139                     // do not exist. If they do, they'll remain untouched since we don't
00140                     // actually write data to it.
00141                     // XXX Admission: this is actually kind of a hack :-(
00142                     KisRectIteratorPixel it = mask->createRectIterator(sx, sy, w, h, true);
00143                     while (!it.isDone())
00144                         ++it;
00145                 }
00146                 if (tempTarget) {
00147                     KisPaintDeviceSP temp = new KisPaintDevice(source->colorSpace());
00148                     mask = paintIndirect(mask, temp, layer, sx, sy, dx, dy, w, h);
00149                 }
00150 
00151                 gc.bitBlt(dx, dy, COMPOSITE_OVER, mask, OPACITY_OPAQUE, sx, sy, w, h);
00152             } else {
00153                 KisSelectionSP mask = layer->getMaskAsSelection();
00154                 // The indirect painting happens on the mask
00155                 if (tempTarget && layer->editMask()) {
00156                     KisPaintDeviceSP maskSrc = layer->getMask();
00157                     KisPaintDeviceSP temp = new KisPaintDevice(maskSrc->colorSpace());
00158                     temp = paintIndirect(maskSrc, temp, layer, sx, sy, dx, dy, w, h);
00159                     // Blegh
00160                     KisRectIteratorPixel srcIt = temp->createRectIterator(sx, sy, w, h, false);
00161                     KisRectIteratorPixel dstIt = mask->createRectIterator(sx, sy, w, h, true);
00162 
00163                     while(!dstIt.isDone()) {
00164                         // Same as in convertMaskToSelection
00165                         *dstIt.rawData() = *srcIt.rawData();
00166                         ++srcIt;
00167                         ++dstIt;
00168                     }
00169                 } else if (tempTarget) {
00170                     // We have a mask, and paint indirect, but not on the mask
00171                     KisPaintDeviceSP temp = new KisPaintDevice(source->colorSpace());
00172                     source = paintIndirect(source, temp, layer, sx, sy, dx, dy, w, h);
00173                 }
00174 
00175                 gc.bltSelection(dx, dy,
00176                                 layer->compositeOp(),
00177                                 source,
00178                                 mask,
00179                                 layer->opacity(), sx, sy, w, h);
00180             }
00181         }
00182 
00183         layer->setClean( rc );
00184         return true;
00185     }
00186 
00187     virtual bool visit(KisGroupLayer *layer)
00188     {
00189         
00190         if (m_projection == 0) {
00191             return false;
00192         }
00193         
00194         kdDebug(41010) << "Visiting on group layer " << layer->name() << ", visible: " << layer->visible() << ", extent: "
00195                 << layer->extent() << ", dirty: " << layer->dirtyRect() << ", paint rect: " << m_rc << endl;
00196                 
00197         if (!layer->visible())
00198             return true;
00199         
00200         Q_INT32 sx, sy, dx, dy, w, h;
00201 
00202         // This automatically makes sure the projection is up-to-date for the specified rect.
00203         KisPaintDeviceSP dev = layer->projection(m_rc);
00204         QRect rc = dev->extent() & m_rc;
00205 
00206         sx = rc.left();
00207         sy = rc.top();
00208         w  = rc.width();
00209         h  = rc.height();
00210         dx = sx;
00211         dy = sy;
00212 
00213         KisPainter gc(m_projection);
00214         gc.bitBlt(dx, dy, layer->compositeOp(), dev, layer->opacity(), sx, sy, w, h);
00215 
00216         return true;
00217     }
00218 
00219     virtual bool visit(KisPartLayer* layer)
00220     {
00221 
00222         kdDebug(41010) << "Visiting on part layer " << layer->name() << ", visible: " << layer->visible() << ", extent: "
00223                 << layer->extent() << ", dirty: " << layer->dirtyRect() << ", paint rect: " << m_rc << endl;
00224         
00225         if (m_projection == 0) {
00226             return false;
00227         }
00228         if (!layer->visible())
00229             return true;
00230 
00231         KisPaintDeviceSP dev(layer->prepareProjection(m_projection, m_rc));
00232         if (!dev)
00233             return true;
00234 
00235         Q_INT32 sx, sy, dx, dy, w, h;
00236 
00237         QRect rc = dev->extent() & m_rc;
00238 
00239         sx= rc.left();
00240         sy = rc.top();
00241         w = rc.width();
00242         h = rc.height();
00243         dx = sx;
00244         dy = sy;
00245 
00246         KisPainter gc(m_projection);
00247         gc.bitBlt(dx, dy, layer->compositeOp() , dev, layer->opacity(), sx, sy, w, h);
00248 
00249         layer->setClean(rc);
00250         return true;
00251     }
00252 
00253     virtual bool visit(KisAdjustmentLayer* layer)
00254     {
00255         kdDebug(41010) << "Visiting on adjustment layer " << layer->name() << ", visible: " << layer->visible() << ", extent: "
00256                 << layer->extent() << ", dirty: " << layer->dirtyRect() << ", paint rect: " << m_rc << endl;
00257 
00258         if (m_projection == 0) {
00259             return true;
00260         }
00261 
00262         if (!layer->visible())
00263             return true;
00264 
00265         KisPaintDeviceSP tempTarget = layer->temporaryTarget();
00266         if (tempTarget) {
00267             m_rc = (layer->extent() | tempTarget->extent()) & m_rc;
00268         }
00269 
00270         if (m_rc.width() == 0 || m_rc.height() == 0) // Don't even try
00271             return true;
00272 
00273         KisFilterConfiguration * cfg = layer->filter();
00274         if (!cfg) return false;
00275 
00276 
00277         KisFilter * f = KisFilterRegistry::instance()->get( cfg->name() );
00278         if (!f) return false;
00279 
00280         // Possibly enlarge the rect that changed (like for convolution filters)
00281         // m_rc = f->enlargeRect(m_rc, cfg);
00282         KisSelectionSP selection = layer->selection();
00283 
00284         // Copy of the projection -- use the copy-on-write trick. XXX NO COPY ON WRITE YET =(
00285         //KisPaintDeviceSP tmp = new KisPaintDevice(*m_projection);
00286         KisPaintDeviceSP tmp = 0;
00287         KisSelectionSP sel = selection;
00288         // If there's a selection, only keep the selected bits
00289         if (selection != 0) {
00290             tmp = new KisPaintDevice(m_projection->colorSpace());
00291 
00292             KisPainter gc(tmp);
00293             QRect selectedRect = selection->selectedRect();
00294             selectedRect &= m_rc;
00295 
00296             if (selectedRect.width() == 0 || selectedRect.height() == 0) // Don't even try
00297                 return true;
00298 
00299             // Don't forget that we need to take into account the extended sourcing area as well
00300             //selectedRect = f->enlargeRect(selectedRect, cfg);
00301 
00302             //kdDebug() << k_funcinfo << selectedRect << endl;
00303             tmp->setX(selection->getX());
00304             tmp->setY(selection->getY());
00305 
00306             // Indirect painting
00307             if (tempTarget) {
00308                 sel = new KisSelection();
00309                 sel = paintIndirect(selection.data(), sel, layer, m_rc.left(), m_rc.top(),
00310                                     m_rc.left(), m_rc.top(), m_rc.width(), m_rc.height());
00311             }
00312 
00313             gc.bitBlt(selectedRect.x(), selectedRect.y(), COMPOSITE_COPY, m_projection,
00314                       selectedRect.x(), selectedRect.y(),
00315                       selectedRect.width(), selectedRect.height());
00316             gc.end();
00317         } else {
00318             tmp = new KisPaintDevice(*m_projection);
00319         }
00320 
00321         // Some filters will require usage of oldRawData, which is not available without
00322         // a transaction!
00323         KisTransaction* cmd = new KisTransaction("", tmp);
00324 
00325         // Filter the temporary paint device -- remember, these are only the selected bits,
00326         // if there was a selection.
00327         f->process(tmp, tmp, cfg, m_rc);
00328 
00329         delete cmd;
00330 
00331         // Copy the filtered bits onto the projection 
00332         KisPainter gc(m_projection);
00333         if (selection)
00334             gc.bltSelection(m_rc.left(), m_rc.top(),
00335                             COMPOSITE_OVER, tmp, sel, layer->opacity(),
00336                             m_rc.left(), m_rc.top(), m_rc.width(), m_rc.height());
00337         else
00338             gc.bitBlt(m_rc.left(), m_rc.top(),
00339                       COMPOSITE_OVER, tmp, layer->opacity(),
00340                       m_rc.left(), m_rc.top(), m_rc.width(), m_rc.height());
00341         gc.end();
00342 
00343         // Copy the finished projection onto the cache
00344         gc.begin(layer->cachedPaintDevice());
00345         gc.bitBlt(m_rc.left(), m_rc.top(),
00346                   COMPOSITE_COPY, m_projection, OPACITY_OPAQUE,
00347                   m_rc.left(), m_rc.top(), m_rc.width(), m_rc.height());
00348         layer->setClean(m_rc);
00349         return true;
00350     }
00351 
00352 private:
00353     KisPaintDeviceSP m_projection;
00354     QRect m_rc;
00355 };
00356 
00357 #endif // KIS_MERGE_H_
00358 
KDE Home | KDE Accessibility Home | Description of Access Keys