krita/ui

kis_shape_selection.cpp

Go to the documentation of this file.
00001 /*
00002  *  Copyright (c) 2007 Sven Langkamp <sven.langkamp@gmail.com>
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 #include "kis_shape_selection.h"
00020 
00021 
00022 #include <QPainter>
00023 #include <QTimer>
00024 
00025 #include <ktemporaryfile.h>
00026 
00027 #include <KoLineBorder.h>
00028 #include <KoPathShape.h>
00029 #include <KoCompositeOp.h>
00030 #include <KoColorSpaceRegistry.h>
00031 #include <KoShapeManager.h>
00032 #include <KoDocument.h>
00033 #include <KoEmbeddedDocumentSaver.h>
00034 #include <KoGenStyle.h>
00035 #include <KoOdfLoadingContext.h>
00036 #include <KoOdfReadStore.h>
00037 #include <KoOdfStylesReader.h>
00038 #include <KoOdfWriteStore.h>
00039 #include <KoXmlNS.h>
00040 #include <KoShapeRegistry.h>
00041 #include <KoShapeLoadingContext.h>
00042 #include <KoXmlWriter.h>
00043 #include <KoStore.h>
00044 #include <KoShapeSavingContext.h>
00045 #include <KoStoreDevice.h>
00046 
00047 
00048 #include "kis_painter.h"
00049 #include "kis_paint_device.h"
00050 #include "kis_shape_selection_model.h"
00051 #include "kis_image.h"
00052 #include "kis_selection.h"
00053 #include "kis_shape_selection_canvas.h"
00054 
00055 #include <kis_debug.h>
00056 
00057 KisShapeSelection::KisShapeSelection(KisImageWSP image, KisSelectionSP selection)
00058         : KoShapeLayer(new KisShapeSelectionModel(image, selection, this))
00059         , m_image(image)
00060 {
00061     Q_ASSERT(m_image);
00062     setShapeId("KisShapeSelection");
00063     setSelectable(false);
00064     m_dirty = false;
00065     m_canvas = new KisShapeSelectionCanvas();
00066     m_canvas->shapeManager()->add(this);
00067 
00068 }
00069 
00070 KisShapeSelection::~KisShapeSelection()
00071 {
00072     delete m_canvas;
00073 }
00074 
00075 KisShapeSelection::KisShapeSelection(const KisShapeSelection& rhs)
00076         : KoShapeLayer(rhs)
00077 {
00078     m_dirty = rhs.m_dirty;
00079     m_image = rhs.m_image;
00080     m_canvas = new KisShapeSelectionCanvas();
00081     m_canvas->shapeManager()->add(this);
00082 }
00083 
00084 KisSelectionComponent* KisShapeSelection::clone()
00085 {
00086     return new KisShapeSelection(*this);
00087 }
00088 
00089 bool KisShapeSelection::saveSelection(KoStore * store) const
00090 {
00091     store->disallowNameExpansion();
00092     KoOdfWriteStore odfStore(store);
00093     KoXmlWriter* manifestWriter = odfStore.manifestWriter("application/vnd.oasis.opendocument.graphics");
00094     KoEmbeddedDocumentSaver embeddedSaver;
00095     KoDocument::SavingContext documentContext(odfStore, embeddedSaver);
00096 
00097     if (!store->open("content.xml"))
00098         return false;
00099 
00100     KoStoreDevice storeDev(store);
00101     KoXmlWriter * docWriter = KoOdfWriteStore::createOasisXmlWriter(&storeDev, "office:document-content");
00102 
00103     // for office:master-styles
00104     KTemporaryFile masterStyles;
00105     masterStyles.open();
00106     KoXmlWriter masterStylesTmpWriter(&masterStyles, 1);
00107 
00108     KoPageLayout page;
00109     page.format = KoPageFormat::defaultFormat();
00110     QRectF rc = boundingRect();
00111     page.width = rc.width();
00112     page.height = rc.height();
00113     if (page.width > page.height) {
00114         page.orientation = KoPageFormat::Landscape;
00115     } else {
00116         page.orientation = KoPageFormat::Portrait;
00117     }
00118 
00119     KoGenStyles mainStyles;
00120     KoGenStyle pageLayout = page.saveOdf();
00121     QString layoutName = mainStyles.lookup(pageLayout, "PL");
00122     KoGenStyle masterPage(KoGenStyle::StyleMaster);
00123     masterPage.addAttribute("style:page-layout-name", layoutName);
00124     mainStyles.lookup(masterPage, "Default", KoGenStyles::DontForceNumbering);
00125 
00126     KTemporaryFile contentTmpFile;
00127     contentTmpFile.open();
00128     KoXmlWriter contentTmpWriter(&contentTmpFile, 1);
00129 
00130     contentTmpWriter.startElement("office:body");
00131     contentTmpWriter.startElement("office:drawing");
00132 
00133     KoShapeSavingContext shapeContext(contentTmpWriter, mainStyles, documentContext.embeddedSaver);
00134 
00135     shapeContext.xmlWriter().startElement("draw:page");
00136     shapeContext.xmlWriter().addAttribute("draw:name", "");
00137     shapeContext.xmlWriter().addAttribute("draw:id", "page1");
00138     shapeContext.xmlWriter().addAttribute("draw:master-page-name", "Default");
00139 
00140     saveOdf(shapeContext);
00141 
00142     shapeContext.xmlWriter().endElement(); // draw:page
00143 
00144     contentTmpWriter.endElement(); // office:drawing
00145     contentTmpWriter.endElement(); // office:body
00146 
00147     mainStyles.saveOdfAutomaticStyles(docWriter, false);
00148 
00149     // And now we can copy over the contents from the tempfile to the real one
00150     contentTmpFile.seek(0);
00151     docWriter->addCompleteElement(&contentTmpFile);
00152 
00153     docWriter->endElement(); // Root element
00154     docWriter->endDocument();
00155     delete docWriter;
00156 
00157     if (!store->close())
00158         return false;
00159 
00160     manifestWriter->addManifestEntry("content.xml", "text/xml");
00161 
00162     if (! mainStyles.saveOdfStylesDotXml(store, manifestWriter)) {
00163         return false;
00164     }
00165 
00166     manifestWriter->addManifestEntry("settings.xml", "text/xml");
00167 
00168     if (! shapeContext.saveDataCenter(documentContext.odfStore.store(), documentContext.odfStore.manifestWriter()))
00169         return false;
00170 
00171     // Write out manifest file
00172     if (!odfStore.closeManifestWriter()) {
00173         dbgImage << "closing manifestWriter failed";
00174         return false;
00175     }
00176 
00177     return true;
00178 }
00179 
00180 bool KisShapeSelection::loadSelection(KoStore* store)
00181 {
00182     KoOdfReadStore odfStore(store);
00183     QString errorMessage;
00184 
00185     odfStore.loadAndParse(errorMessage);
00186 
00187     if (!errorMessage.isEmpty()) {
00188         qDebug() << errorMessage;
00189         return false;
00190     }
00191 
00192     KoXmlElement contents = odfStore.contentDoc().documentElement();
00193 
00194 //    qDebug() <<"Start loading OASIS document..." << contents.text();
00195 //    qDebug() <<"Start loading OASIS contents..." << contents.lastChild().localName();
00196 //    qDebug() <<"Start loading OASIS contents..." << contents.lastChild().namespaceURI();
00197 //    qDebug() <<"Start loading OASIS contents..." << contents.lastChild().isElement();
00198 
00199     KoXmlElement body(KoXml::namedItemNS(contents, KoXmlNS::office, "body"));
00200 
00201     if (body.isNull()) {
00202         qDebug() << "No office:body found!";
00203         //setErrorMessage( i18n( "Invalid OASIS document. No office:body tag found." ) );
00204         return false;
00205     }
00206 
00207     body = KoXml::namedItemNS(body, KoXmlNS::office, "drawing");
00208     if (body.isNull()) {
00209         qDebug() << "No office:drawing found!";
00210         //setErrorMessage( i18n( "Invalid OASIS document. No office:drawing tag found." ) );
00211         return false;
00212     }
00213 
00214     KoXmlElement page(KoXml::namedItemNS(body, KoXmlNS::draw, "page"));
00215     if (page.isNull()) {
00216         qDebug() << "No office:drawing found!";
00217         //setErrorMessage( i18n( "Invalid OASIS document. No draw:page tag found." ) );
00218         return false;
00219     }
00220 
00221     KoXmlElement * master = 0;
00222     if (odfStore.styles().masterPages().contains("Standard"))
00223         master = odfStore.styles().masterPages().value("Standard");
00224     else if (odfStore.styles().masterPages().contains("Default"))
00225         master = odfStore.styles().masterPages().value("Default");
00226     else if (! odfStore.styles().masterPages().empty())
00227         master = odfStore.styles().masterPages().begin().value();
00228 
00229     if (master) {
00230         const KoXmlElement *style = odfStore.styles().findStyle(
00231                                         master->attributeNS(KoXmlNS::style, "page-layout-name", QString()));
00232         KoPageLayout pageLayout;
00233         pageLayout.loadOdf(*style);
00234         setSize(QSizeF(pageLayout.width, pageLayout.height));
00235     } else {
00236         kWarning() << "No master page found!";
00237         return false;
00238     }
00239 
00240     QMap<QString, KoDataCenter*> dataCenterMap;
00241     KoOdfLoadingContext context(odfStore.styles(), odfStore.store());
00242     KoShapeLoadingContext shapeContext(context, dataCenterMap);
00243 
00244 
00245     KoXmlElement layerElement;
00246     forEachElement(layerElement, context.stylesReader().layerSet()) {
00247         if (!loadOdf(layerElement, shapeContext)) {
00248             kWarning() << "Could not load shape layer!";
00249             return false;
00250         }
00251     }
00252 
00253     KoXmlElement child;
00254     forEachElement(child, page) {
00255         KoShape * shape = KoShapeRegistry::instance()->createShapeFromOdf(child, shapeContext);
00256         if (shape) {
00257             addChild(shape);
00258         }
00259     }
00260 
00261     return true;
00262 
00263 }
00264 
00265 
00266 void KisShapeSelection::addChild(KoShape *object)
00267 {
00268     KoShapeLayer::addChild(object);
00269     m_canvas->shapeManager()->add(object);
00270 }
00271 
00272 void KisShapeSelection::removeChild(KoShape *object)
00273 {
00274     m_canvas->shapeManager()->remove(object);
00275     KoShapeLayer::removeChild(object);
00276 }
00277 
00278 QPainterPath KisShapeSelection::selectionOutline()
00279 {
00280     if (m_dirty) {
00281         QList<KoShape*> shapesList = childShapes();
00282 
00283         QPainterPath outline;
00284         foreach(KoShape * shape, shapesList) {
00285             QMatrix shapeMatrix = shape->absoluteTransformation(0);
00286             outline = outline.united(shapeMatrix.map(shape->outline()));
00287         }
00288         m_outline = outline;
00289         m_dirty = false;
00290     }
00291     return m_outline;
00292 }
00293 
00294 void KisShapeSelection::paintComponent(QPainter& painter, const KoViewConverter& converter)
00295 {
00296     Q_UNUSED(painter);
00297     Q_UNUSED(converter);
00298 }
00299 
00300 void KisShapeSelection::renderToProjection(KisSelection* projection)
00301 {
00302     Q_ASSERT(projection);
00303     Q_ASSERT(m_image);
00304     QMatrix resolutionMatrix;
00305     resolutionMatrix.scale(m_image->xRes(), m_image->yRes());
00306 
00307     QRectF boundingRect = resolutionMatrix.mapRect(selectionOutline().boundingRect());
00308     renderSelection(projection, boundingRect.toAlignedRect());
00309 }
00310 
00311 void KisShapeSelection::renderToProjection(KisSelection* projection, const QRect& r)
00312 {
00313     Q_ASSERT(projection);
00314     renderSelection(projection, r);
00315 }
00316 
00317 void KisShapeSelection::renderSelection(KisSelection* projection, const QRect& r)
00318 {
00319     Q_ASSERT(projection);
00320     Q_ASSERT(m_image);
00321 
00322     QMatrix resolutionMatrix;
00323     resolutionMatrix.scale(m_image->xRes(), m_image->yRes());
00324 
00325     QTime t;
00326     t.start();
00327 
00328     KisPaintDeviceSP tmpMask = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
00329 
00330     const qint32 MASK_IMAGE_WIDTH = 256;
00331     const qint32 MASK_IMAGE_HEIGHT = 256;
00332 
00333     QImage polygonMaskImage(MASK_IMAGE_WIDTH, MASK_IMAGE_HEIGHT, QImage::Format_ARGB32);
00334     QPainter maskPainter(&polygonMaskImage);
00335     maskPainter.setRenderHint(QPainter::Antialiasing, true);
00336 
00337     // Break the mask up into chunks so we don't have to allocate a potentially very large QImage.
00338 
00339     for (qint32 x = r.x(); x < r.x() + r.width(); x += MASK_IMAGE_WIDTH) {
00340         for (qint32 y = r.y(); y < r.y() + r.height(); y += MASK_IMAGE_HEIGHT) {
00341 
00342             maskPainter.fillRect(polygonMaskImage.rect(), QColor(OPACITY_TRANSPARENT, OPACITY_TRANSPARENT, OPACITY_TRANSPARENT, 255));
00343             maskPainter.translate(-x, -y);
00344             maskPainter.fillPath(resolutionMatrix.map(selectionOutline()), QColor(OPACITY_OPAQUE, OPACITY_OPAQUE, OPACITY_OPAQUE, 255));
00345             maskPainter.translate(x, y);
00346 
00347             qint32 rectWidth = qMin(r.x() + r.width() - x, MASK_IMAGE_WIDTH);
00348             qint32 rectHeight = qMin(r.y() + r.height() - y, MASK_IMAGE_HEIGHT);
00349 
00350             KisRectIterator rectIt = tmpMask->createRectIterator(x, y, rectWidth, rectHeight);
00351 
00352             while (!rectIt.isDone()) {
00353                 (*rectIt.rawData()) = qRed(polygonMaskImage.pixel(rectIt.x() - x, rectIt.y() - y));
00354                 ++rectIt;
00355             }
00356         }
00357     }
00358     KisPainter painter(projection);
00359     painter.bitBlt(r.x(), r.y(), tmpMask, r.x(), r.y(), r.width(), r.height());
00360     painter.end();
00361     dbgRender << "Shape selection rendering: " << t.elapsed();
00362 }
00363 
00364 void KisShapeSelection::setDirty()
00365 {
00366     m_dirty = true;
00367 }
00368 
00369 KoShapeManager* KisShapeSelection::shapeManager() const
00370 {
00371     return m_canvas->shapeManager();
00372 }
00373 
00374 KisShapeSelectionFactory::KisShapeSelectionFactory(QObject* parent)
00375         : KoShapeFactory(parent, "KisShapeSelection", "selection shape container")
00376 {
00377 }
00378 
00379 KoShape* KisShapeSelectionFactory::createDefaultShape() const
00380 {
00381     return 0;
00382 }
00383 
00384 KoShape* KisShapeSelectionFactory::createShape(const KoProperties* params) const
00385 {
00386     Q_UNUSED(params);
00387     return 0;
00388 }
00389 
00390 #include "kis_shape_selection.moc"