krita

kis_painter.cc

00001 /*
00002  *  Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
00003  *  Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
00004  *  Copyright (c) 2004 Clarence Dang <dang@kde.org>
00005  *  Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
00006  *  Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
00007  *
00008  *  This program is free software; you can redistribute it and/or modify
00009  *  it under the terms of the GNU General Public License as published by
00010  *  the Free Software Foundation; either version 2 of the License, or
00011  *  (at your option) any later version.
00012  *
00013  *  This program is distributed in the hope that it will be useful,
00014  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  *  GNU General Public License for more details.
00017  *
00018  *  You should have received a copy of the GNU General Public License
00019  *  along with this program; if not, write to the Free Software
00020  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00021  */
00022 #include <stdlib.h>
00023 #include <string.h>
00024 #include <cfloat>
00025 #include <cmath>
00026 #include <climits>
00027 #include <strings.h>
00028 
00029 #include "qbrush.h"
00030 #include "qfontinfo.h"
00031 #include "qfontmetrics.h"
00032 #include "qpen.h"
00033 #include "qregion.h"
00034 #include "qwmatrix.h"
00035 #include <qimage.h>
00036 #include <qmap.h>
00037 #include <qpainter.h>
00038 #include <qpixmap.h>
00039 #include <qpointarray.h>
00040 #include <qrect.h>
00041 #include <qstring.h>
00042 
00043 #include <kdebug.h>
00044 #include <kcommand.h>
00045 #include <klocale.h>
00046 
00047 #include "kis_brush.h"
00048 #include "kis_debug_areas.h"
00049 #include "kis_image.h"
00050 #include "kis_layer.h"
00051 #include "kis_paint_device.h"
00052 #include "kis_painter.h"
00053 #include "kis_pattern.h"
00054 #include "kis_rect.h"
00055 #include "kis_colorspace.h"
00056 #include "kis_transaction.h"
00057 #include "kis_types.h"
00058 #include "kis_vec.h"
00059 #include "kis_iterators_pixel.h"
00060 #include "kis_paintop.h"
00061 #include "kis_selection.h"
00062 #include "kis_fill_painter.h"
00063 #include "kis_color.h"
00064 
00065 // Maximum distance from a Bezier control point to the line through the start
00066 // and end points for the curve to be considered flat.
00067 #define BEZIER_FLATNESS_THRESHOLD 0.5
00068 
00069 KisPainter::KisPainter()
00070 {
00071     init();
00072 }
00073 
00074 KisPainter::KisPainter(KisPaintDeviceSP device)
00075 {
00076     init();
00077     Q_ASSERT(device);
00078         begin(device);
00079 }
00080 
00081 void KisPainter::init()
00082 {
00083     m_transaction = 0;
00084     m_paintOp = 0;
00085     m_filter = 0;
00086     m_brush = 0;
00087     m_pattern= 0;
00088     m_opacity = OPACITY_OPAQUE;
00089     m_compositeOp = COMPOSITE_OVER;
00090     m_dab = 0;
00091     m_fillStyle = FillStyleNone;
00092     m_strokeStyle = StrokeStyleBrush;
00093     m_pressure = PRESSURE_MIN;
00094     m_duplicateHealing = false;
00095     m_duplicateHealingRadius = 10;
00096     m_duplicatePerspectiveCorrection = false;
00097 }
00098 
00099 KisPainter::~KisPainter()
00100 {
00101     m_brush = 0;
00102     delete m_paintOp;
00103     end();
00104 }
00105 
00106 void KisPainter::begin(KisPaintDeviceSP device)
00107 {
00108     if (!device) return;
00109 
00110     if (m_transaction)
00111         delete m_transaction;
00112 
00113     m_device = device;
00114     m_colorSpace = device->colorSpace();
00115     m_pixelSize = device->pixelSize();
00116 }
00117 
00118 KCommand *KisPainter::end()
00119 {
00120     return endTransaction();
00121 }
00122 
00123 void KisPainter::beginTransaction(const QString& customName)
00124 {
00125     if (m_transaction)
00126         delete m_transaction;
00127     m_transaction = new KisTransaction(customName, m_device);
00128     Q_CHECK_PTR(m_transaction);
00129 }
00130 
00131 void KisPainter::beginTransaction( KisTransaction* command)
00132 {
00133     if (m_transaction)
00134         delete m_transaction;
00135     m_transaction = command;
00136 }
00137 
00138 
00139 KCommand *KisPainter::endTransaction()
00140 {
00141     KCommand *command = m_transaction;
00142         m_transaction = 0;
00143         return command;
00144 }
00145 
00146 
00147 QRect KisPainter::dirtyRect() {
00148     QRect r = m_dirtyRect;
00149     m_dirtyRect = QRect();
00150     return r;
00151 }
00152 
00153 void KisPainter::bitBlt(Q_INT32 dx, Q_INT32 dy,
00154                         const KisCompositeOp& op,
00155                         KisPaintDeviceSP srcdev,
00156                         Q_UINT8 opacity,
00157                         Q_INT32 sx, Q_INT32 sy,
00158                         Q_INT32 sw, Q_INT32 sh)
00159 {
00160     if (srcdev == 0) {
00161         return;
00162     }
00163 
00164     QRect srcRect = QRect(sx, sy, sw, sh);
00165 
00166     if (srcdev->extentIsValid() && op != COMPOSITE_COPY) {
00167         srcRect &= srcdev->extent();
00168     }
00169 
00170     if (srcRect.isEmpty()) {
00171         return;
00172     }
00173 
00174     dx += srcRect.x() - sx;
00175     dy += srcRect.y() - sy;
00176 
00177     sx = srcRect.x();
00178     sy = srcRect.y();
00179     sw = srcRect.width();
00180     sh = srcRect.height();
00181 
00182     addDirtyRect(QRect(dx, dy, sw, sh));
00183 
00184     KisColorSpace * srcCs = srcdev->colorSpace();
00185 
00186     Q_INT32 dstY = dy;
00187     Q_INT32 srcY = sy;
00188     Q_INT32 rowsRemaining = sh;
00189 
00190     while (rowsRemaining > 0) {
00191 
00192         Q_INT32 dstX = dx;
00193         Q_INT32 srcX = sx;
00194         Q_INT32 columnsRemaining = sw;
00195         Q_INT32 numContiguousDstRows = m_device->numContiguousRows(dstY, dstX, dstX + sw - 1);
00196         Q_INT32 numContiguousSrcRows = srcdev->numContiguousRows(srcY, srcX, srcX + sw - 1);
00197 
00198         Q_INT32 rows = QMIN(numContiguousDstRows, numContiguousSrcRows);
00199         rows = QMIN(rows, rowsRemaining);
00200 
00201         while (columnsRemaining > 0) {
00202 
00203             Q_INT32 numContiguousDstColumns = m_device->numContiguousColumns(dstX, dstY, dstY + rows - 1);
00204             Q_INT32 numContiguousSrcColumns = srcdev->numContiguousColumns(srcX, srcY, srcY + rows - 1);
00205 
00206             Q_INT32 columns = QMIN(numContiguousDstColumns, numContiguousSrcColumns);
00207             columns = QMIN(columns, columnsRemaining);
00208 
00209             Q_INT32 srcRowStride = srcdev->rowStride(srcX, srcY);
00210             //const Q_UINT8 *srcData = srcdev->pixel(srcX, srcY);
00211             KisHLineIteratorPixel srcIt = srcdev->createHLineIterator(srcX, srcY, columns, false);
00212             const Q_UINT8 *srcData = srcIt.rawData();
00213 
00214             //Q_UINT8 *dstData = m_device->writablePixel(dstX, dstY);
00215             Q_INT32 dstRowStride = m_device->rowStride(dstX, dstY);
00216             KisHLineIteratorPixel dstIt = m_device->createHLineIterator(dstX, dstY, columns, true);
00217             Q_UINT8 *dstData = dstIt.rawData();
00218 
00219 
00220             m_colorSpace->bitBlt(dstData,
00221                           dstRowStride,
00222                           srcCs,
00223                           srcData,
00224                           srcRowStride,
00225                           0,
00226                           0,
00227                           opacity,
00228                           rows,
00229                           columns,
00230                           op);
00231 
00232             srcX += columns;
00233             dstX += columns;
00234             columnsRemaining -= columns;
00235         }
00236 
00237         srcY += rows;
00238         dstY += rows;
00239         rowsRemaining -= rows;
00240     }
00241 }
00242 
00243 void KisPainter::bltSelection(Q_INT32 dx, Q_INT32 dy,
00244                   const KisCompositeOp &op,
00245                   KisPaintDeviceSP srcdev,
00246                   KisSelectionSP seldev,
00247                   Q_UINT8 opacity,
00248                   Q_INT32 sx, Q_INT32 sy,
00249                   Q_INT32 sw, Q_INT32 sh)
00250 {
00251     // Better use a probablistic method than a too slow one
00252     if (seldev->isProbablyTotallyUnselected(QRect(dx, dy, sw, sh))) {
00253 /*
00254         kdDebug() << "Blitting outside selection rect\n";
00255 
00256         kdDebug() << "srcdev: " << srcdev << " (" << srcdev->name() << ")"
00257         << ", seldev: " << seldev << " (" << seldev->name() << ")"
00258         << ". dx, dy " << dx << "," << dy
00259         << ". sx, sy : sw, sy " << sx << "," << sy << " : " << sw << "," << sh << endl;
00260 */
00261         return;
00262     }
00263     bltMask(dx,dy,op,srcdev,seldev.data(),opacity,sx,sy,sw,sh);
00264 }
00265 
00266 void KisPainter::bltMask(Q_INT32 dx, Q_INT32 dy,
00267                      const KisCompositeOp &op,
00268                      KisPaintDeviceSP srcdev,
00269                      KisPaintDeviceSP seldev,
00270                      Q_UINT8 opacity,
00271                      Q_INT32 sx, Q_INT32 sy,
00272                      Q_INT32 sw, Q_INT32 sh)
00273 
00274 {
00275     if (srcdev == 0) return;
00276 
00277     if (seldev == 0) return;
00278 
00279     if (m_device == 0) return;
00280 
00281 
00282     QRect srcRect = QRect(sx, sy, sw, sh);
00283 
00284     if (srcdev->extentIsValid() && op != COMPOSITE_COPY) {
00285         srcRect &= srcdev->extent();
00286     }
00287 
00288     if (srcRect.isEmpty()) {
00289         return;
00290     }
00291 
00292     dx += srcRect.x() - sx;
00293     dy += srcRect.y() - sy;
00294 
00295     sx = srcRect.x();
00296     sy = srcRect.y();
00297     sw = srcRect.width();
00298     sh = srcRect.height();
00299 
00300     addDirtyRect(QRect(dx, dy, sw, sh));
00301 
00302     KisColorSpace * srcCs = srcdev->colorSpace();
00303 
00304     Q_INT32 dstY = dy;
00305     Q_INT32 srcY = sy;
00306     Q_INT32 rowsRemaining = sh;
00307 
00308     while (rowsRemaining > 0) {
00309 
00310         Q_INT32 dstX = dx;
00311         Q_INT32 srcX = sx;
00312         Q_INT32 columnsRemaining = sw;
00313         Q_INT32 numContiguousDstRows = m_device->numContiguousRows(dstY, dstX, dstX + sw - 1);
00314         Q_INT32 numContiguousSrcRows = srcdev->numContiguousRows(srcY, srcX, srcX + sw - 1);
00315         Q_INT32 numContiguousSelRows = seldev->numContiguousRows(dstY, dstX, dstX + sw - 1);
00316 
00317         Q_INT32 rows = QMIN(numContiguousDstRows, numContiguousSrcRows);
00318         rows = QMIN(numContiguousSelRows, rows);
00319         rows = QMIN(rows, rowsRemaining);
00320 
00321         while (columnsRemaining > 0) {
00322 
00323             Q_INT32 numContiguousDstColumns = m_device->numContiguousColumns(dstX, dstY, dstY + rows - 1);
00324             Q_INT32 numContiguousSrcColumns = srcdev->numContiguousColumns(srcX, srcY, srcY + rows - 1);
00325             Q_INT32 numContiguousSelColumns = seldev->numContiguousColumns(dstX, dstY, dstY + rows - 1);
00326 
00327             Q_INT32 columns = QMIN(numContiguousDstColumns, numContiguousSrcColumns);
00328             columns = QMIN(numContiguousSelColumns, columns);
00329             columns = QMIN(columns, columnsRemaining);
00330 
00331             //Q_UINT8 *dstData = m_device->writablePixel(dstX, dstY);
00332             Q_INT32 dstRowStride = m_device->rowStride(dstX, dstY);
00333             KisHLineIteratorPixel dstIt = m_device->createHLineIterator(dstX, dstY, columns, true);
00334             Q_UINT8 *dstData = dstIt.rawData();
00335 
00336             //const Q_UINT8 *srcData = srcdev->pixel(srcX, srcY);
00337             Q_INT32 srcRowStride = srcdev->rowStride(srcX, srcY);
00338             KisHLineIteratorPixel srcIt = srcdev->createHLineIterator(srcX, srcY, columns, false);
00339             const Q_UINT8 *srcData = srcIt.rawData();
00340 
00341             //const Q_UINT8 *selData = seldev->pixel(dstX, dstY);
00342             Q_INT32 selRowStride = seldev->rowStride(dstX, dstY);
00343             KisHLineIteratorPixel selIt = seldev->createHLineIterator(dstX, dstY, columns, false);
00344             const Q_UINT8 *selData = selIt.rawData();
00345 
00346             m_colorSpace->bitBlt(dstData,
00347                                    dstRowStride,
00348                                    srcCs,
00349                                    srcData,
00350                                    srcRowStride,
00351                                    selData,
00352                                    selRowStride,
00353                                    opacity,
00354                                    rows,
00355                                    columns,
00356                                    op);
00357 
00358             srcX += columns;
00359             dstX += columns;
00360             columnsRemaining -= columns;
00361         }
00362 
00363         srcY += rows;
00364         dstY += rows;
00365         rowsRemaining -= rows;
00366     }
00367 }
00368 
00369 
00370 void KisPainter::bltSelection(Q_INT32 dx, Q_INT32 dy,
00371                   const KisCompositeOp& op,
00372                   KisPaintDeviceSP srcdev,
00373                   Q_UINT8 opacity,
00374                   Q_INT32 sx, Q_INT32 sy,
00375                   Q_INT32 sw, Q_INT32 sh)
00376 {
00377     if (m_device == 0) return;
00378     if (!m_device->hasSelection()) {
00379         bitBlt(dx, dy, op, srcdev, opacity, sx, sy, sw, sh);
00380     }
00381     else
00382         bltSelection(dx,dy,op,srcdev, m_device->selection(),opacity,sx,sy,sw,sh);
00383 }
00384 
00385 double KisPainter::paintLine(const KisPoint & pos1,
00386                  const double pressure1,
00387                  const double xTilt1,
00388                  const double yTilt1,
00389                  const KisPoint & pos2,
00390                  const double pressure2,
00391                  const double xTilt2,
00392                  const double yTilt2,
00393                  const double inSavedDist)
00394 {
00395     if (!m_device) return 0;
00396     if (!m_paintOp) return 0;
00397     if (!m_brush) return 0;
00398 
00399     double savedDist = inSavedDist;
00400     KisVector2D end(pos2);
00401     KisVector2D start(pos1);
00402 
00403     KisVector2D dragVec = end - start;
00404     KisVector2D movement = dragVec;
00405 
00406     if (savedDist < 0) {
00407         m_paintOp->paintAt(pos1, KisPaintInformation(pressure1, xTilt1, yTilt1, movement));
00408         savedDist = 0;
00409     }
00410 
00411     // XXX: The spacing should vary as the pressure changes along the line.
00412     // This is a quick simplification.
00413     double xSpacing = m_brush->xSpacing((pressure1 + pressure2) / 2);
00414     double ySpacing = m_brush->ySpacing((pressure1 + pressure2) / 2);
00415 
00416     if (xSpacing < 0.5) {
00417         xSpacing = 0.5;
00418     }
00419     if (ySpacing < 0.5) {
00420         ySpacing = 0.5;
00421     }
00422 
00423     double xScale = 1;
00424     double yScale = 1;
00425     double spacing;
00426     // Scale x or y so that we effectively have a square brush
00427     // and calculate distance in that coordinate space. We reverse this scaling
00428     // before drawing the brush. This produces the correct spacing in both
00429     // x and y directions, even if the brush's aspect ratio is not 1:1.
00430     if (xSpacing > ySpacing) {
00431         yScale = xSpacing / ySpacing;
00432         spacing = xSpacing;
00433     }
00434     else {
00435         xScale = ySpacing / xSpacing;
00436         spacing = ySpacing;
00437     }
00438 
00439     dragVec.setX(dragVec.x() * xScale);
00440     dragVec.setY(dragVec.y() * yScale);
00441 
00442     double newDist = dragVec.length();
00443     double dist = savedDist + newDist;
00444     double l_savedDist = savedDist;
00445 
00446     if (dist < spacing) {
00447         return dist;
00448     }
00449 
00450     dragVec.normalize();
00451     KisVector2D step(0, 0);
00452 
00453     while (dist >= spacing) {
00454         if (l_savedDist > 0) {
00455             step += dragVec * (spacing - l_savedDist);
00456             l_savedDist -= spacing;
00457         }
00458         else {
00459             step += dragVec * spacing;
00460         }
00461 
00462         KisPoint p(start.x() + (step.x() / xScale), start.y() + (step.y() / yScale));
00463 
00464         double distanceMoved = step.length();
00465         double t = 0;
00466 
00467         if (newDist > DBL_EPSILON) {
00468             t = distanceMoved / newDist;
00469         }
00470 
00471         double pressure = (1 - t) * pressure1 + t * pressure2;
00472         double xTilt = (1 - t) * xTilt1 + t * xTilt2;
00473         double yTilt = (1 - t) * yTilt1 + t * yTilt2;
00474 
00475         m_paintOp->paintAt(p, KisPaintInformation(pressure, xTilt, yTilt, movement));
00476         dist -= spacing;
00477     }
00478 
00479     if (dist > 0)
00480         return dist;
00481     else
00482         return 0;
00483 }
00484 
00485 void KisPainter::paintPolyline (const vKisPoint &points,
00486                                 int index, int numPoints)
00487 {
00488     if (index >= (int) points.count ())
00489         return;
00490 
00491     if (numPoints < 0)
00492         numPoints = points.count ();
00493 
00494     if (index + numPoints > (int) points.count ())
00495         numPoints = points.count () - index;
00496 
00497 
00498     for (int i = index; i < index + numPoints - 1; i++)
00499     {
00500         paintLine (points [index], 0/*pressure*/, 0, 0, points [index + 1],
00501                0/*pressure*/, 0, 0);
00502     }
00503 }
00504 
00505 void KisPainter::getBezierCurvePoints(const KisPoint &pos1,
00506                       const KisPoint &control1,
00507                       const KisPoint &control2,
00508                       const KisPoint &pos2,
00509                       vKisPoint& points)
00510 {
00511     double d1 = pointToLineDistance(control1, pos1, pos2);
00512     double d2 = pointToLineDistance(control2, pos1, pos2);
00513 
00514     if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) {
00515         points.push_back(pos1);
00516     } else {
00517         // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508
00518         KisVector2D p1 = pos1;
00519         KisVector2D p2 = control1;
00520         KisVector2D p3 = control2;
00521         KisVector2D p4 = pos2;
00522 
00523         KisVector2D l2 = (p1 + p2) / 2;
00524         KisVector2D h = (p2 + p3) / 2;
00525         KisVector2D l3 = (l2 + h) / 2;
00526         KisVector2D r3 = (p3 + p4) / 2;
00527         KisVector2D r2 = (h + r3) / 2;
00528         KisVector2D l4 = (l3 + r2) / 2;
00529         KisVector2D r1 = l4;
00530         KisVector2D l1 = p1;
00531         KisVector2D r4 = p4;
00532 
00533         getBezierCurvePoints(l1.toKisPoint(), l2.toKisPoint(), l3.toKisPoint(), l4.toKisPoint(), points);
00534         getBezierCurvePoints(r1.toKisPoint(), r2.toKisPoint(), r3.toKisPoint(), r4.toKisPoint(), points);
00535     }
00536 }
00537 
00538 double KisPainter::paintBezierCurve(const KisPoint &pos1,
00539                     const double pressure1,
00540                     const double xTilt1,
00541                     const double yTilt1,
00542                     const KisPoint &control1,
00543                     const KisPoint &control2,
00544                     const KisPoint &pos2,
00545                     const double pressure2,
00546                     const double xTilt2,
00547                     const double yTilt2,
00548                     const double savedDist)
00549 {
00550     double newDistance;
00551     double d1 = pointToLineDistance(control1, pos1, pos2);
00552     double d2 = pointToLineDistance(control2, pos1, pos2);
00553 
00554     if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) {
00555         newDistance = paintLine(pos1, pressure1, xTilt1, yTilt1, pos2, pressure2, xTilt2, yTilt2, savedDist);
00556     } else {
00557         // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508
00558         KisVector2D p1 = pos1;
00559         KisVector2D p2 = control1;
00560         KisVector2D p3 = control2;
00561         KisVector2D p4 = pos2;
00562 
00563         KisVector2D l2 = (p1 + p2) / 2;
00564         KisVector2D h = (p2 + p3) / 2;
00565         KisVector2D l3 = (l2 + h) / 2;
00566         KisVector2D r3 = (p3 + p4) / 2;
00567         KisVector2D r2 = (h + r3) / 2;
00568         KisVector2D l4 = (l3 + r2) / 2;
00569         KisVector2D r1 = l4;
00570         KisVector2D l1 = p1;
00571         KisVector2D r4 = p4;
00572 
00573         double midPressure = (pressure1 + pressure2) / 2;
00574         double midXTilt = (xTilt1 + xTilt2) / 2;
00575         double midYTilt = (yTilt1 + yTilt2) / 2;
00576 
00577         newDistance = paintBezierCurve(l1.toKisPoint(), pressure1, xTilt1, yTilt1,
00578                            l2.toKisPoint(), l3.toKisPoint(),
00579                            l4.toKisPoint(), midPressure, midXTilt, midYTilt,
00580                            savedDist);
00581         newDistance = paintBezierCurve(r1.toKisPoint(), midPressure, midXTilt, midYTilt,
00582                            r2.toKisPoint(),
00583                            r3.toKisPoint(),
00584                            r4.toKisPoint(), pressure2, xTilt2, yTilt2, newDistance);
00585     }
00586 
00587     return newDistance;
00588 }
00589 
00590 void KisPainter::paintRect (const KisPoint &startPoint,
00591                             const KisPoint &endPoint,
00592                             const double /*pressure*/,
00593                 const double /*xTilt*/,
00594                 const double /*yTilt*/)
00595 {
00596     KoRect normalizedRect = KisRect (startPoint, endPoint).normalize ();
00597 
00598     vKisPoint points;
00599 
00600     points.push_back(normalizedRect.topLeft());
00601     points.push_back(normalizedRect.bottomLeft());
00602     points.push_back(normalizedRect.bottomRight());
00603     points.push_back(normalizedRect.topRight());
00604 
00605     paintPolygon(points);
00606 }
00607 
00608 void KisPainter::paintEllipse (const KisPoint &startPoint,
00609                                const KisPoint &endPoint,
00610                                const double /*pressure*/,
00611                    const double /*xTilt*/,
00612                    const double /*yTilt*/)
00613 {
00614     KisRect r = KisRect(startPoint, endPoint).normalize();
00615 
00616     // See http://www.whizkidtech.redprince.net/bezier/circle/ for explanation.
00617     // kappa = (4/3*(sqrt(2)-1))
00618     const double kappa = 0.5522847498;
00619     const double lx = (r.width() / 2) * kappa;
00620     const double ly = (r.height() / 2) * kappa;
00621 
00622     KisPoint center = r.center();
00623 
00624     KisPoint p0(r.left(), center.y());
00625     KisPoint p1(r.left(), center.y() - ly);
00626     KisPoint p2(center.x() - lx, r.top());
00627     KisPoint p3(center.x(), r.top());
00628 
00629     vKisPoint points;
00630 
00631     getBezierCurvePoints(p0, p1, p2, p3, points);
00632 
00633     KisPoint p4(center.x() + lx, r.top());
00634     KisPoint p5(r.right(), center.y() - ly);
00635     KisPoint p6(r.right(), center.y());
00636 
00637     getBezierCurvePoints(p3, p4, p5, p6, points);
00638 
00639     KisPoint p7(r.right(), center.y() + ly);
00640     KisPoint p8(center.x() + lx, r.bottom());
00641     KisPoint p9(center.x(), r.bottom());
00642 
00643     getBezierCurvePoints(p6, p7, p8, p9, points);
00644 
00645     KisPoint p10(center.x() - lx, r.bottom());
00646     KisPoint p11(r.left(), center.y() + ly);
00647 
00648     getBezierCurvePoints(p9, p10, p11, p0, points);
00649 
00650     paintPolygon(points);
00651 }
00652 
00653 void KisPainter::paintAt(const KisPoint & pos,
00654                          const double pressure,
00655                          const double xTilt,
00656                          const double yTilt)
00657 {
00658     if (!m_paintOp) return;
00659     m_paintOp->paintAt(pos, KisPaintInformation(pressure, xTilt, yTilt, KisVector2D()));
00660 }
00661 
00662 double KisPainter::pointToLineDistance(const KisPoint& p, const KisPoint& l0, const KisPoint& l1)
00663 {
00664     double lineLength = sqrt((l1.x() - l0.x()) * (l1.x() - l0.x()) + (l1.y() - l0.y()) * (l1.y() - l0.y()));
00665     double distance = 0;
00666 
00667     if (lineLength > DBL_EPSILON) {
00668         distance = ((l0.y() - l1.y()) * p.x() + (l1.x() - l0.x()) * p.y() + l0.x() * l1.y() - l1.x() * l0.y()) / lineLength;
00669         distance = fabs(distance);
00670     }
00671 
00672     return distance;
00673 }
00674 
00675 /*
00676  * Concave Polygon Scan Conversion
00677  * by Paul Heckbert
00678  * from "Graphics Gems", Academic Press, 1990
00679  */
00680 
00681 /*
00682  * concave: scan convert nvert-sided concave non-simple polygon with vertices at
00683  * (point[i].x, point[i].y) for i in [0..nvert-1] within the window win by
00684  * calling spanproc for each visible span of pixels.
00685  * Polygon can be clockwise or counterclockwise.
00686  * Algorithm does uniform point sampling at pixel centers.
00687  * Inside-outside test done by Jordan's rule: a point is considered inside if
00688  * an emanating ray intersects the polygon an odd number of times.
00689  * drawproc should fill in pixels from xl to xr inclusive on scanline y,
00690  * e.g:
00691  *    drawproc(y, xl, xr)
00692  *    int y, xl, xr;
00693  *    {
00694  *        int x;
00695  *        for (x=xl; x<=xr; x++)
00696  *        pixel_write(x, y, pixelvalue);
00697  *    }
00698  *
00699  *  Paul Heckbert    30 June 81, 18 Dec 89
00700  */
00701 
00702 typedef struct {    /* a polygon edge */
00703     double x;       /* x coordinate of edge's intersection with current scanline */
00704     double dx;      /* change in x with respect to y */
00705     int i;            /* edge number: edge i goes from pt[i] to pt[i+1] */
00706 } Edge;
00707 
00708 static int n;            /* number of vertices */
00709 static const KisPoint *pt;    /* vertices */
00710 
00711 static int nact;        /* number of active edges */
00712 static Edge *active;        /* active edge list:edges crossing scanline y */
00713 
00714 /* comparison routines for qsort */
00715 static int compare_ind(const void *pu, const void *pv)
00716 {
00717     const int *u = static_cast<const int *>(pu);
00718     const int *v = static_cast<const int *>(pv);
00719 
00720     return pt[*u].y() <= pt[*v].y() ? -1 : 1;
00721 }
00722 
00723 static int compare_active(const void *pu, const void *pv)
00724 {
00725     const Edge *u = static_cast<const Edge *>(pu);
00726     const Edge *v = static_cast<const Edge *>(pv);
00727 
00728     return u->x <= v->x ? -1 : 1;
00729 }
00730 
00731 static void cdelete(int i)        /* remove edge i from active list */
00732 {
00733     int j;
00734 
00735     for (j=0; j<nact && active[j].i!=i; j++);
00736     if (j>=nact) return;        /* edge not in active list; happens at win->y0*/
00737     nact--;
00738     bcopy(&active[j+1], &active[j], (nact-j)*sizeof active[0]);
00739 }
00740 
00741 static void cinsert(int i, int y)        /* append edge i to end of active list */
00742 {
00743     int j;
00744     double dx;
00745     const KisPoint *p, *q;
00746 
00747     j = i<n-1 ? i+1 : 0;
00748     if (pt[i].y() < pt[j].y()) {
00749         p = &pt[i]; q = &pt[j];
00750     } else {
00751         p = &pt[j]; q = &pt[i];
00752     }
00753     /* initialize x position at intersection of edge with scanline y */
00754     active[nact].dx = dx = (q->x()-p->x())/(q->y()-p->y());
00755     active[nact].x = dx*(y+.5-p->y())+p->x();
00756     active[nact].i = i;
00757     nact++;
00758 }
00759 
00760 void KisPainter::fillPolygon(const vKisPoint& points, FillStyle fillStyle)
00761 {
00762     int nvert = points.count();
00763     int k, y0, y1, y, i, j, xl, xr;
00764     int *ind;        /* list of vertex indices, sorted by pt[ind[j]].y */
00765 
00766     n = nvert;
00767     pt = &(points[0]);
00768     if (n<3) return;
00769     if (fillStyle == FillStyleNone) {
00770         return;
00771     }
00772 
00773     ind = new int[n];
00774     Q_CHECK_PTR(ind);
00775     active = new Edge[n];
00776     Q_CHECK_PTR(active);
00777 
00778     /* create y-sorted array of indices ind[k] into vertex list */
00779     for (k=0; k<n; k++)
00780         ind[k] = k;
00781     qsort(ind, n, sizeof ind[0], compare_ind);  /* sort ind by pt[ind[k]].y */
00782 
00783     nact = 0;                /* start with empty active list */
00784     k = 0;                    /* ind[k] is next vertex to process */
00785     y0 = static_cast<int>(ceil(pt[ind[0]].y()-.5));            /* ymin of polygon */
00786     y1 = static_cast<int>(floor(pt[ind[n-1]].y()-.5));        /* ymax of polygon */
00787 
00788     int x0 = INT_MAX;
00789     int x1 = INT_MIN;
00790 
00791     for (int i = 0; i < nvert; i++) {
00792         int pointHighX = static_cast<int>(ceil(points[i].x() - 0.5));
00793         int pointLowX = static_cast<int>(floor(points[i].x() - 0.5));
00794 
00795         if (pointLowX < x0) {
00796             x0 = pointLowX;
00797         }
00798         if (pointHighX > x1) {
00799             x1 = pointHighX;
00800         }
00801     }
00802 
00803     // Fill the polygon bounding rectangle with the required contents then we'll
00804     // create a mask for the actual polygon coverage.
00805 
00806     KisPaintDeviceSP polygon = new KisPaintDevice(m_device->colorSpace(), "polygon");
00807     Q_CHECK_PTR(polygon);
00808 
00809     KisFillPainter fillPainter(polygon);
00810     QRect boundingRectangle(x0, y0, x1 - x0 + 1, y1 - y0 + 1);
00811 
00812     // Clip to the image bounds.
00813     if (m_device->image()) {
00814         boundingRectangle &= m_device->image()->bounds();
00815     }
00816 
00817     switch (fillStyle) {
00818     default:
00819         // Fall through
00820     case FillStyleGradient:
00821         // Currently unsupported, fall through
00822     case FillStyleStrokes:
00823         // Currently unsupported, fall through
00824         kdWarning(DBG_AREA_CORE) << "Unknown or unsupported fill style in fillPolygon\n";
00825     case FillStyleForegroundColor:
00826         fillPainter.fillRect(boundingRectangle, paintColor(), OPACITY_OPAQUE);
00827         break;
00828     case FillStyleBackgroundColor:
00829         fillPainter.fillRect(boundingRectangle, backgroundColor(), OPACITY_OPAQUE);
00830         break;
00831     case FillStylePattern:
00832         Q_ASSERT(m_pattern != 0);
00833         fillPainter.fillRect(boundingRectangle, m_pattern);
00834         break;
00835     }
00836 
00837     KisSelectionSP polygonMask = new KisSelection(polygon);
00838 
00839     for (y=y0; y<=y1; y++) {        /* step through scanlines */
00840         /* scanline y is at y+.5 in continuous coordinates */
00841 
00842         /* check vertices between previous scanline and current one, if any */
00843         for (; k<n && pt[ind[k]].y()<=y+.5; k++) {
00844             /* to simplify, if pt.y=y+.5, pretend it's above */
00845             /* invariant: y-.5 < pt[i].y <= y+.5 */
00846             i = ind[k];
00847             /*
00848              * insert or delete edges before and after vertex i (i-1 to i,
00849              * and i to i+1) from active list if they cross scanline y
00850              */
00851             j = i>0 ? i-1 : n-1;        /* vertex previous to i */
00852             if (pt[j].y() <= y-.5)        /* old edge, remove from active list */
00853                 cdelete(j);
00854             else if (pt[j].y() > y+.5)    /* new edge, add to active list */
00855                 cinsert(j, y);
00856             j = i<n-1 ? i+1 : 0;        /* vertex next after i */
00857             if (pt[j].y() <= y-.5)        /* old edge, remove from active list */
00858                 cdelete(i);
00859             else if (pt[j].y() > y+.5)    /* new edge, add to active list */
00860                 cinsert(i, y);
00861         }
00862 
00863         /* sort active edge list by active[j].x */
00864         qsort(active, nact, sizeof active[0], compare_active);
00865 
00866         /* draw horizontal segments for scanline y */
00867         for (j=0; j<nact; j+=2) {    /* draw horizontal segments */
00868             /* span 'tween j & j+1 is inside, span tween j+1 & j+2 is outside */
00869             xl = static_cast<int>(ceil(active[j].x-.5));        /* left end of span */
00870             xr = static_cast<int>(floor(active[j+1].x-.5));        /* right end of span */
00871 
00872             if (xl<=xr) {
00873                 KisHLineIterator it = polygonMask->createHLineIterator(xl, y, xr - xl + 1, true);
00874 
00875                 while (!it.isDone()) {
00876                     // We're using a selection here, that means alpha colorspace, that means one byte.
00877                     it.rawData()[0] = MAX_SELECTED;
00878                     ++it;
00879                 }
00880             }
00881 
00882             active[j].x += active[j].dx;        /* increment edge coords */
00883             active[j+1].x += active[j+1].dx;
00884         }
00885     }
00886     delete [] ind;
00887     delete [] active;
00888 
00889     polygon->applySelectionMask(polygonMask);
00890 
00891     QRect r = polygon->extent();
00892 
00893     // The strokes for the outline may have already added updated the dirtyrect, but it can't hurt,
00894     // and if we're painting without outlines, then there will be no dirty rect. Let's do it ourselves...
00895     // addDirtyRect( r ); // XXX the bltSelection will add to the dirtyrect
00896 
00897     bltSelection(r.x(), r.y(), compositeOp(), polygon, opacity(), r.x(), r.y(), r.width(), r.height());
00898 }
00899 
00900 void KisPainter::paintPolygon(const vKisPoint& points)
00901 {
00902     if (m_fillStyle != FillStyleNone) {
00903         fillPolygon(points, m_fillStyle);
00904     }
00905 
00906     if (m_strokeStyle != StrokeStyleNone) {
00907         if (points.count() > 1) {
00908             double distance = -1;
00909 
00910             for (uint i = 0; i < points.count() - 1; i++) {
00911                 distance = paintLine(points[i], PRESSURE_DEFAULT, 0, 0, points[i + 1], PRESSURE_DEFAULT, 0, 0, distance);
00912             }
00913             paintLine(points[points.count() - 1], PRESSURE_DEFAULT, 0, 0, points[0], PRESSURE_DEFAULT, 0, 0, distance);
00914         }
00915     }
00916 }
00917 
KDE Home | KDE Accessibility Home | Description of Access Keys